diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..4341e3a71dad646b30295dc450d3a1fdd4960a1a --- /dev/null +++ b/Android.bp @@ -0,0 +1,27 @@ +cc_binary_host { + name: "unifdef", + srcs: ["scripts/unifdef.c"], + sanitize: { + never: true, + } +} + +gensrcs { + name: "qseecom-kernel-includes", + + // move to out/ as root for header generation because of scripts/unifdef + // storage - at the expense of extra ../ references + cmd: "pushd out && mkdir -p scripts && rm -f scripts/unifdef && ln -s ../../$(location unifdef) scripts/unifdef && ../$(location scripts/headers_install.sh) `dirname ../$(out)` ../ $(in) && popd", + + tools: ["unifdef"], + tool_files: ["scripts/headers_install.sh"], + export_include_dirs: ["include/uapi"], + srcs: ["include/uapi/linux/qseecom.h"], + output_extension: "h", +} + +cc_library_headers { + name: "qseecom-kernel-headers", + generated_headers: ["qseecom-kernel-includes"], + export_generated_headers: ["qseecom-kernel-includes"], +} diff --git a/AndroidKernel.mk b/AndroidKernel.mk index c84f70958ba77a4ead7791b9baeb64aee6a72f4c..12ae066fcc21babb437ab5ecd1780c5146e3d5d8 100644 --- a/AndroidKernel.mk +++ b/AndroidKernel.mk @@ -47,6 +47,20 @@ else KERNEL_CROSS_COMPILE := $(TARGET_KERNEL_CROSS_COMPILE_PREFIX) endif +ifeq ($(KERNEL_LLVM_SUPPORT), true) + ifeq ($(KERNEL_SD_LLVM_SUPPORT), true) #Using sd-llvm compiler + ifeq ($(shell echo $(SDCLANG_PATH) | head -c 1),/) + KERNEL_LLVM_BIN := $(SDCLANG_PATH)/clang + else + KERNEL_LLVM_BIN := $(ANDROID_BUILD_TOP)/$(SDCLANG_PATH)/clang + endif + $(warning "Using sdllvm" $(KERNEL_LLVM_BIN)) + else + KERNEL_LLVM_BIN := $(ANDROID_BUILD_TOP)/$(CLANG) #Using aosp-llvm compiler + $(warning "Using aosp-llvm" $(KERNEL_LLVM_BIN)) + endif +endif + ifeq ($(TARGET_PREBUILT_KERNEL),) KERNEL_GCC_NOANDROID_CHK := $(shell (echo "int main() {return 0;}" | $(KERNEL_CROSS_COMPILE)gcc -E -mno-android - > /dev/null 2>&1 ; echo $$?)) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index d870b5514d15a23de66d34f226f870ed0d94dd67..540553c933b6197e51cd81367ffbc16fbd24273d 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -192,3 +192,14 @@ Date: November 2017 Contact: "Sheng Yong" Description: Controls readahead inode block in readdir. + +What: /sys/fs/f2fs//extension_list +Date: Feburary 2018 +Contact: "Chao Yu" +Description: + Used to control configure extension list: + - Query: cat /sys/fs/f2fs//extension_list + - Add: echo '[h/c]extension' > /sys/fs/f2fs//extension_list + - Del: echo '[h/c]!extension' > /sys/fs/f2fs//extension_list + - [h] means add/del hot file extension + - [c] means add/del cold file extension diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt index 8f8d97f65d7375a9fc87f4bad9f4a6c8033e8bba..7972942f8c192eb319297893d4c0b29ed720f709 100644 --- a/Documentation/DMA-attributes.txt +++ b/Documentation/DMA-attributes.txt @@ -156,3 +156,10 @@ accesses to DMA buffers in both privileged "supervisor" and unprivileged subsystem that the buffer is fully accessible at the elevated privilege level (and ideally inaccessible or at least read-only at the lesser-privileged levels). + +DMA_ATTR_IOMMU_USE_LLC_NWA +------------------------------------ + +DMA_ATTR_IOMMU_USE_LLC_NWA: Overrides the bus attributes to use +System Cache(LLC) with allocation policy as Inner Non-Cacheable, Outer Cacheable: +Write-Back, Read-Allocate, No Write-Allocate policy. \ No newline at end of file diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 3330e7ddcfc515dabc17f66696fc272f07b5ee0e..fa6c50abe702939ac48f9c1a64dfb478fc2132be 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2556,6 +2556,9 @@ noalign [KNL,ARM] + noaltinstr [S390] Disables alternative instructions patching + (CPU alternatives feature). + noapic [SMP,APIC] Tells the kernel to not make use of any IOAPICs that may be present in the system. diff --git a/Documentation/arm/msm/rpm.txt b/Documentation/arm/msm/rpm.txt new file mode 100644 index 0000000000000000000000000000000000000000..d5be6a7e28901c5e8e5f17167baf3b9cee36625f --- /dev/null +++ b/Documentation/arm/msm/rpm.txt @@ -0,0 +1,157 @@ +Introduction +============ + +Resource Power Manager (RPM) + +RPM is a dedicated hardware engine for managing shared SoC resources, +which includes buses, clocks, power rails, etc. The goal of RPM is +to achieve the maximum power savings while satisfying the SoC's +operational and performance requirements. RPM accepts resource +requests from multiple RPM masters. It arbitrates and aggregates the +requests, and configures the shared resources. The RPM masters are +the application processor, the modem processor, as well as some +hardware accelerators. + +The RPM driver provides an API for interacting with RPM. Kernel code +calls the RPM driver to request RPM-managed, shared resources. +Kernel code can also register with the driver for RPM notifications, +which are sent when the status of shared resources change. + +Hardware description +==================== + +RPM exposes a separate region of registers to each of the RPM masters. +In general, each register represents some shared resource(s). At a +very basic level, a master requests resources by writing to the +registers, then generating an interrupt to RPM. RPM processes the +request, writes acknowledgment to the registers, then generates an +interrupt to the master. + +In addition to the master-specific regions, RPM also exposes a shared +region that contains the current status of the shared resources. Only +RPM can write to the status region, but every master can read from it. + +RPM contains internal logics that aggregate and arbitrate among +requests from the various RPM masters. It interfaces with the PMIC, +the bus arbitration block, and the clock controller block in order to +configure the shared resources. + +Software description +==================== + +The RPM driver encapsulates the low level RPM interactions, which +rely on reading/writing registers and generating/processing +interrupts, and provides a higher level synchronuous set/clear/get +interface. Most functions take an array of id-value pairs. +The ids identify the RPM registers which would correspond to some +RPM resources, the values specify the new resource values. + +The RPM driver synchronizes accesses to RPM. It protects against +simultaneous accesses from multiple tasks, on SMP cores, in task +contexts, and in atomic contexts. + +Design +====== + +Design goals: +- Encapsulate low level RPM interactions. +- Provide a synchronuous set/clear/get interface. +- Synchronize simultaneous software accesses to RPM. + +Power Management +================ + +RPM is part of the power management architecture for MSM 8660. RPM +manages shared system resources to lower system power. + +SMP/multi-core +============== + +The RPM driver uses mutex to synchronize client accesses among tasks. +It uses spinlocks to synchronize accesses from atomic contexts and +SMP cores. + +Security +======== + +None. + +Performance +=========== + +None. + +Interface +========= + +msm_rpm_get_status(): +The function reads the shared status region and returns the current +resource values, which are the arbitrated/aggregated results across +all RPM masters. + +msm_rpm_set(): +The function makes a resource request to RPM. + +msm_rpm_set_noirq(): +The function is similar to msm_rpm_set() except that it must be +called with interrupts masked. If possible, use msm_rpm_set() +instead, to maximize CPU throughput. + +msm_rpm_clear(): +The function makes a resource request to RPM to clear resource values. +Once the values are cleared, the resources revert back to their default +values for this RPM master. RPM internally uses the default values as +the requests from this RPM master when arbitrating and aggregating with +requests from other RPM masters. + +msm_rpm_clear_noirq(): +The function is similar to msm_rpm_clear() except that it must be +called with interrupts masked. If possible, use msm_rpm_clear() +instead, to maximize CPU throughput. + +msm_rpm_register_notification(): +The function registers for RPM notification. When the specified +resources change their status on RPM, RPM sends out notifications +and the driver will "up" the semaphore in struct +msm_rpm_notification. + +msm_rpm_unregister_notification(): +The function unregisters a notification. + +msm_rpm_init(): +The function initializes the RPM driver with platform specific data. + +Driver parameters +================= + +None. + +Config options +============== + +MSM_RPM + +Dependencies +============ + +None. + +User space utilities +==================== + +None. + +Other +===== + +None. + +Known issues +============ + +None. + +To do +===== + +None. diff --git a/Documentation/device-mapper/thin-provisioning.txt b/Documentation/device-mapper/thin-provisioning.txt index 1699a55b7b709adddd18b97cd769cb8946662e48..ef639960b272d4f746cf23d987a07e4cadfe7a8f 100644 --- a/Documentation/device-mapper/thin-provisioning.txt +++ b/Documentation/device-mapper/thin-provisioning.txt @@ -112,9 +112,11 @@ $low_water_mark is expressed in blocks of size $data_block_size. If free space on the data device drops below this level then a dm event will be triggered which a userspace daemon should catch allowing it to extend the pool device. Only one such event will be sent. -Resuming a device with a new table itself triggers an event so the -userspace daemon can use this to detect a situation where a new table -already exceeds the threshold. + +No special event is triggered if a just resumed device's free space is below +the low water mark. However, resuming a device always triggers an +event; a userspace daemon should verify that free space exceeds the low +water mark when handling this event. A low water mark for the metadata device is maintained in the kernel and will trigger a dm event if free space on the metadata device drops below diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt index 89fd8f9a259f69b9c9423da9bb16771ed0596cad..b3d2e4a422559a571008a9f85c43f01c79e41aa1 100644 --- a/Documentation/device-mapper/verity.txt +++ b/Documentation/device-mapper/verity.txt @@ -109,6 +109,17 @@ fec_start This is the offset, in blocks, from the start of the FEC device to the beginning of the encoding data. +check_at_most_once + Verify data blocks only the first time they are read from the data device, + rather than every time. This reduces the overhead of dm-verity so that it + can be used on systems that are memory and/or CPU constrained. However, it + provides a reduced level of security because only offline tampering of the + data device's content will be detected, not online tampering. + + Hash blocks are still verified each time they are read from the hash device, + since verification of hash blocks is less performance critical than data + blocks, and a hash block will not be verified any more after all the data + blocks it covers have been verified anyway. Theory of operation =================== diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt index c7572331026a28052dc934660e30dc514472a12f..1a357b10418de6dd6e7fee49cbf8d9414e8afff0 100644 --- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt +++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt @@ -28,6 +28,14 @@ Required properties: mask of the cluster mode in the composite state ID used to define cluster low power modes in PSCI. +Optional properties: + - qcom,disable-prediction: This property is used to indicate the LPM + governor will not use LPM prediction for this cluster. + - qcom,clstr-tmr-add: This property is used as correction timer for + wrong prediction by lpm prediction algorithm for cluster predictions. + This value should be between 100 to 1500. Higher values would mean + longer time staying in shallower state before waking up to select a + deeper state in case of wrong prediction. qcom,pm-cluster contains qcom,pm-cluster-level nodes which identify the various low power modes that the cluster can enter. The qcom,pm-cluster node should also include another cluster node or a cpu @@ -78,6 +86,22 @@ that share the parameters.It contains the following properties. enter. The following section explains the required properties of this node. +Optional properties: + - qcom,disable-prediction: This property is used to indicate the + LPM governor is to disable sleep prediction to this cpu. + - qcom,ref-stddev: This property is used as reference standard deviation + in lpm prediction algorithm. This value should be between 100 to 1000. + Higher value would result in more predictions and thereby resulting in + shallower low power modes. + - qcom,tmr-add: This property is used as correction timer for wrong + prediction by lpm prediction algorithm. This value should be between + 100 to 1500. Higher values would mean longer time staying in shallower + state before waking up to select a deeper state in case of wrong prediction. + - qcom,ref-premature-cnt: This property is used as reference premature + count to predict next sleep state by the prediction algorithm. This value + should be between 1 to 5. Higher value for this parameter would result in + less predictions to disallow deeper low power modes. + [Node bindings for qcom,pm-cpu-levels] Required properties: - reg: The numeric cpu level id diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt index 19a77085ffabfb8dbe0475e9d764b86c80ea1a33..124e0b7b2083dc771a1ddd0a7991821a2e819998 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm.txt @@ -47,8 +47,8 @@ SoCs: - SDMSHRIKE compatible = "qcom,sdmshrike" -- SDM640 - compatible = "qcom,sdm640" +- SM6150 + compatible = "qcom,sm6150" - QCS405 compatible = "qcom,qcs405" @@ -132,13 +132,18 @@ compatible = "qcom,sm8150-rumi" compatible = "qcom,sm8150-mtp" compatible = "qcom,sm8150-cdp" compatible = "qcom,sm8150-qrd" +compatible = "qcom,sm8150p-cdp" +compatible = "qcom,sm8150p-mtp" +compatible = "qcom,sm8150p-qrd" compatible = "qcom,sdmshrike-rumi" compatible = "qcom,sdmshrike-mtp" compatible = "qcom,sdmshrike-cdp" -compatible = "qcom,sdm640-rumi" -compatible = "qcom,sdm640-mtp" -compatible = "qcom,sdm640-cdp" -compatible = "qcom,sdm640-qrd" +compatible = "qcom,sm6150-rumi" +compatible = "qcom,sm6150-mtp" +compatible = "qcom,sm6150-cdp" +compatible = "qcom,sm6150-qrd" compatible = "qcom,qcs405-rumi" compatible = "qcom,qcs405-mtp" compatible = "qcom,qcs405-cdp" +compatible = "qcom,sm8150-auto-adp-star" +compatible = "qcom,auto-adp-star" diff --git a/Documentation/devicetree/bindings/arm/msm/proxy-client.txt b/Documentation/devicetree/bindings/arm/msm/proxy-client.txt new file mode 100644 index 0000000000000000000000000000000000000000..29cfaf90016c9a70034701a498863b7b25c015d3 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/proxy-client.txt @@ -0,0 +1,34 @@ +Bus Proxy Client Bindings + +Bus proxy client provides means to cast proxy bandwidth votes during bootup +which is removed at the end of boot. This feature can be used in situations +where a shared resource can be scaled between several possible perfomance +levels and hardware requires that it be at a high level at the beginning of +boot before the client has probed and voted for required bandwidth. + +Required properties: +- compatible: Must be "qcom,bus-proxy-client". + +Optional properties: +- qcom,msm-bus,name: String representing the client-name. +- qcom,msm-bus,num-cases: Total number of usecases. +- qcom,msm-bus,active-only: Boolean context flag for requests in active or + dual (active & sleep) contex. +- qcom,msm-bus,num-paths: Total number of master-slave pairs. +- qcom,msm-bus,vectors-KBps: Arrays of unsigned integers representing: + master-id, slave-id, arbitrated bandwidth + in KBps, instantaneous bandwidth in KBps. + +Example: + + qcom,proxy-client { + compatible = "qcom,bus-proxy-client"; + qcom,msm-bus,name = "proxy_client"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,active-only; + qcom,msm-bus,vectors-KBps = + <22 512 0 0>, <23 512 0 0>, + <22 512 0 6400000>, <23 512 0 6400000>, + <22 512 0 6400000>, <23 512 0 6400000>; + }; diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt index f2d6cd9dfa64111adf1ceca7cf05b0e0e94c7542..57583eafb5b9c302b004beaefbb049668d67a136 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt +++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt @@ -93,8 +93,8 @@ compatible devices: qcom,sdm845-llcc, qcom,sdm670-llcc, qcom,sm8150-llcc, - qcom,sdmshrike-llcc - + qcom,sdmshrike-llcc, + qcom,sm6150-llcc Example: diff --git a/Documentation/devicetree/bindings/arm/msm/qsee_ipc_irq_bridge.txt b/Documentation/devicetree/bindings/arm/msm/qsee_ipc_irq_bridge.txt new file mode 100644 index 0000000000000000000000000000000000000000..442ad52b4bf6e471d2c310db2ce053012b1b8163 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/qsee_ipc_irq_bridge.txt @@ -0,0 +1,30 @@ +Qualcomm Technologies, Inc. Secure Execution Environment IPC Interrupt Bridge + +[Root level node] +Required properties: +-compatible : should be "qcom,qsee-ipc-irq-bridge"; + +[Second level nodes] +qcom,qsee-ipc-irq-subsystem +Required properties: +-qcom,dev-name: the bridge device name +-interrupt: IPC interrupt line from remote subsystem to QSEE +-label : The name of this subsystem. + +Required properties if interrupt type is IRQ_TYPE_LEVEL_HIGH[4]: +-qcom,rx-irq-clr : the register to clear the level triggered rx interrupt +-qcom,rx-irq-clr-mask : the bitmask to clear the rx interrupt + +Example: + + qcom,qsee_ipc_irq_bridge { + compatible = "qcom,qsee-ipc-irq-bridge"; + + qcom,qsee-ipc-irq-spss { + qcom,rx-irq-clr = <0x1d08008 0x4>; + qcom,rx-irq-clr-mask = <0x2>; + qcom,dev-name = "qsee_ipc_irq_spss"; + interrupts = <0 349 4>; + label = "spss"; + }; + }; diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt new file mode 100644 index 0000000000000000000000000000000000000000..4cba3ecaeb90bf244975b460dc1610dcf8b947fc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt @@ -0,0 +1,41 @@ +Resource Power Manager(RPM) + +RPM is a dedicated hardware engine for managing shared SoC resources, +which includes buses, clocks, power rails, etc. The goal of RPM is +to achieve the maximum power savings while satisfying the SoC's +operational and performance requirements. RPM accepts resource +requests from multiple RPM masters. It arbitrates and aggregates the +requests, and configures the shared resources. The RPM masters are +the application processor, the modem processor, as well as hardware +accelerators. The RPM driver communicates with the hardware engine using +SMD. + +The devicetree representation of the SPM block should be: + +Required properties + +- compatible: "qcom,rpm-smd" or "qcom,rpm-glink" +- rpm-channel-name: The string corresponding to the channel name of the + peripheral subsystem. Required for both smd and + glink transports. +- rpm-channel-type: The interal SMD edge for this subsystem found in + +- qcom,glink-edge: Logical name of the remote subsystem. This is a required + property when rpm-smd driver using glink as trasport. + +Optional properties +- rpm-standalone: Allow RPM driver to run in standalone mode irrespective of RPM + channel presence. +- reg: Contains the memory address at which rpm messaging format version is + stored. If this field is not present, the target only supports v0 format. + +Example: + + qcom,rpm-smd@68150 { + compatible = "qcom,rpm-smd", "qcom,rpm-glink"; + reg = <0x68150 0x3200>; + qcom,rpm-channel-name = "rpm_requests"; + qcom,rpm-channel-type = 15; /* SMD_APPS_RPM */ + qcom,glink-edge = "rpm"; + } +} diff --git a/Documentation/devicetree/bindings/arm/msm/rpmh-master-stat.txt b/Documentation/devicetree/bindings/arm/msm/rpmh-master-stat.txt new file mode 100644 index 0000000000000000000000000000000000000000..a53eba57f89dbd255c056cdb0a290f7d97108ec3 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/rpmh-master-stat.txt @@ -0,0 +1,31 @@ +* RPMH Master Stats + +Differet Subsystems maintains master data in SMEM. +It tells about the individual masters information at any given +time like "system sleep counts", "system sleep last entered at" +and "system sleep accumulated duration" etc. These stats can be +displayed using the sysfs interface. +To achieve this, device tree node has been added. + +Additionally, RPMH master stats also maintains application processor's +master stats. It uses profiling units to calculate power down and power +up stats. + +The required properties for rpmh-master-stats are: + +- compatible: + Usage: required + Value type: + Definition: Should be "qcom,rpmh-master-stats-v1". + +- reg: + Usage: required + Value type: + Definition: Specifies physical address of start of profiling unit. + +Example: + +qcom,rpmh-master-stats { + compatible = "qcom,rpmh-master-stats"; + reg = <0xb221200 0x60>; +}; diff --git a/Documentation/devicetree/bindings/arm/msm/sleepstate-smp2p.txt b/Documentation/devicetree/bindings/arm/msm/sleepstate-smp2p.txt new file mode 100644 index 0000000000000000000000000000000000000000..adfa94f9d1ff599699244a90ebb22db270f6c3f3 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/sleepstate-smp2p.txt @@ -0,0 +1,13 @@ +Qualcomm Technologies, Inc. SMSM Point-to-Point (SMP2P) Sleepstate driver + +Required properties: +-compatible : should be one of the following: +- "qcom,smp2pgpio_sleepstate_3_out" - for sensor processor on remote pid 3 +- "qcom,smp2pgpio-sleepstate-out" - for other cases +-gpios : the relevant gpio pins of the entry. + +Example: + qcom,smp2pgpio-sleepstate-3-out { + compatible = "qcom,smp2pgpio_sleepstate_3_out"; + gpios = <&smp2pgpio_sleepstate_3_out 0 0>; + }; diff --git a/Documentation/devicetree/bindings/arm/msm/spcom.txt b/Documentation/devicetree/bindings/arm/msm/spcom.txt new file mode 100644 index 0000000000000000000000000000000000000000..36a07ec686ade94a4dc95617f9b5e25d2632df2a --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/spcom.txt @@ -0,0 +1,11 @@ +Qualcomm Technologies, Inc. Secure Proccessor Communication (spcom) + +Required properties: +-compatible : should be "qcom,spcom" +-qcom,spcom-ch-names: predefined channels name string + +Example: + qcom,spcom { + compatible = "qcom,spcom"; + qcom,spcom-ch-names = "sp_kernel" , "sp_ssr"; + }; diff --git a/Documentation/devicetree/bindings/batterydata/batterydata.txt b/Documentation/devicetree/bindings/batterydata/batterydata.txt index 59113c39145e5b9389e25cf525c42592f1b15a34..9d80eb9d1859f8e5ec2ebbd8e0de6d96ebacc6b7 100644 --- a/Documentation/devicetree/bindings/batterydata/batterydata.txt +++ b/Documentation/devicetree/bindings/batterydata/batterydata.txt @@ -106,6 +106,12 @@ Profile data node optional properties: the low and high thresholds. The threshold values in range should be in ascending and shouldn't overlap. It support 8 ranges at max. +- qcom,jeita-soft-thresholds: A tuple entry to specify ADC code for battery's soft JEITA + threshold. + . +- qcom,jeita-hard-thresholds: A tuple entry to specify ADC code for battery's hard JEITA + threshold. + . Profile data node required subnodes: - qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes @@ -170,6 +176,8 @@ qcom,palladium-batterydata { qcom,v-cutoff-uv = <3400000>; qcom,chg-term-ua = <100000>; qcom,batt-id-kohm = <75>; + qcom,jeita-soft-thresholds = <0x3ecc 0x1bff>; + qcom,jeita-hard-thresholds = <0x4aff 0x15aa>; qcom,step-chg-ranges = <3600000 4000000 3000000 4001000 4200000 2800000 4201000 4400000 2000000>; diff --git a/Documentation/devicetree/bindings/bus/mhi.txt b/Documentation/devicetree/bindings/bus/mhi.txt index 69a351e84e5c217103667e85f5a11f53aeea6dc6..b74fe736a11c5be46a0f8270eb040af666d34bb5 100644 --- a/Documentation/devicetree/bindings/bus/mhi.txt +++ b/Documentation/devicetree/bindings/bus/mhi.txt @@ -22,8 +22,10 @@ Main node properties: 2nd element: Transfer ring length in elements 3rd element: Event ring associated with this channel 4th element: Channel direction as defined by enum dma_data_direction + 0 = Bidirectional data transfer 1 = UL data transfer 2 = DL data transfer + 3 = No direction, not a regular data transfer channel 5th element: Channel doorbell mode configuration as defined by enum MHI_BRSTMODE 2 = burst mode disabled @@ -52,6 +54,11 @@ Main node properties: the data pipe. Not involved in active data transfer. BIT(2) : Must switch to doorbell mode whenever MHI M0 state transition happens. + BIT(3) : MHI bus driver pre-allocate buffer for this channel. + If set, clients not allowed to queue buffers. Valid only for DL + direction. + BIT(4) : MHI host driver to automatically start channels once + mhi device driver probe is complete. - mhi,chan-names Usage: required diff --git a/Documentation/devicetree/bindings/clock/qcom,debugcc.txt b/Documentation/devicetree/bindings/clock/qcom,debugcc.txt index 710246f51417ed25be1a2c3d282a4279696b3776..1b558f0f83fe634de391c1795c945b022c48a70a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,debugcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,debugcc.txt @@ -2,12 +2,14 @@ Qualcomm Technologies, Inc. Debug Clock Controller Binding ---------------------------------------------------------- Required properties : -- compatible: Shall contain "qcom,debugcc-sm8150". +- compatible: Shall contain "qcom,debugcc-sm8150", + "qcom,debugcc-qcs405". - qcom,gcc: phandle to the GCC device node. - qcom,videocc: phandle to the Video CC device node. - qcom,camcc: phandle to the Camera CC device node. - qcom,dispcc: phandle to the Display CC device node. - qcom,npucc: phandle to the NPU CC device node. +- qcom,cpucc: phandle to the CPU CC debug device node. - clock-names: Shall contain "xo_clk_src" - clocks: phandle + clock reference to the CXO clock. - #clock-cells : Shall contain 1. @@ -20,6 +22,7 @@ Example: qcom,camcc = <&clock_camcc>; qcom,dispcc = <&clock_dispcc>; qcom,npucc = <&clock_npucc>; + qcom,cpucc = <&cpucc_debug>; clock-names = "xo_clk_src"; clocks = <&clock_rpmh RPMH_CXO_CLK>; #clock-cells = <1>; diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt index 3d99c632a4205f7fab3bd462a9547d906dbb89c6..4390120d11dbefb829d12edc21cfb78f85417180 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt @@ -20,6 +20,8 @@ Required properties : "qcom,gcc-mdm9615" "qcom,gcc-sm8150" "qcom,gcc-sdmshrike" + "qcom,gcc-qcs405" + "qcom,gcc-mdss-qcs405" - reg : shall contain base register location and length - #clock-cells : shall contain 1 diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt index ad9d190255a0ebd8c1349d955921fd1cae4bb256..e70109dd10983510fdd9f04ab3175b6ac4e3e7d5 100644 --- a/Documentation/devicetree/bindings/cnss/icnss.txt +++ b/Documentation/devicetree/bindings/cnss/icnss.txt @@ -30,6 +30,7 @@ Optional properties: - qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass - qcom,wlan-msa-fixed-region: phandle, specifier pairs to children of /reserved-memory - qcom,gpio-force-fatal-error: SMP2P bit triggered by WLAN FW to force error fatal. + - qcom,gpio-early-crash-ind: SMP2P bit triggered by WLAN FW to indicate FW is in assert. Example: @@ -61,4 +62,5 @@ Example: vdd-0.8-cx-mx-supply = <&pm8998_l5>; qcom,vdd-0.8-cx-mx-config = <800000 800000 2400 1000>; qcom,gpio-forced-fatal-error = <&smp2pgpio_wlan_1_in 0 0>; + qcom,gpio-early-crash-ind = <&smp2pgpio_wlan_1_in 1 0>; }; diff --git a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt index f7a81f1a13167ec36d0eb32e59aff815c6a1b116..7849afcaa2713323cfbff154c07bbbb9848d99a6 100644 --- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt +++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt @@ -1,5 +1,8 @@ * QCEDEV (QTI Crypto Engine Device) +[Root level node] +Crypto Engine +============ Required properties: - compatible : should be "qcom,qcedev" - reg : should contain crypto, BAM register map. @@ -24,6 +27,19 @@ Optional properties: - iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device. - qcom,no-clock-support : indicates clocks are not handled by hlos crypto driver. + +[Second level nodes] +Context banks +============= +Required properties: + - compatible : should be "qcom,qcedev,context-bank" + - iommus : A phandle parsed by smmu driver. Number of entries will vary across targets. + +Optional properties: + - label - string describing iommu domain usage. + - virtual-addr : start of virtual address pool. + - virtual-size : size of virtual address pool. + Example: qcom,qcedev@fd440000 { @@ -43,4 +59,15 @@ Example: <56 512 0 0>, <56 512 3936000 393600>, qcom,ce-opp-freq = <100000000>; + + qcom_cedev_ns_cb { + compatible = "qcom,qcedev,context-bank"; + label = "ns_context"; + iommus = <&anoc2_smmu 0x1878>, + <&anoc2_smmu 0x1879>, + <&anoc2_smmu 0x187c>, + <&anoc2_smmu 0x187f>; + virtual-addr = <0x60000000>; + virtual-size = <0x00200000>; + }; }; diff --git a/Documentation/devicetree/bindings/devfreq/devbw.txt b/Documentation/devicetree/bindings/devfreq/devbw.txt index ece0fa761e8380b1603b898ef5c65356d2d67074..217e46e35fa4b88c56ede4290318c0b5905504af 100644 --- a/Documentation/devicetree/bindings/devfreq/devbw.txt +++ b/Documentation/devicetree/bindings/devfreq/devbw.txt @@ -9,11 +9,11 @@ Required properties: - compatible: Must be "qcom,devbw" - qcom,src-dst-ports: A list of tuples where each tuple consists of a bus master port number and a bus slave port number. -- qcom,bw-tbl: A list of meaningful instantaneous bandwidth values - (in MB/s) that can be requested from the device - master port to the slave port. The list of values - depend on the supported bus/slave frequencies and the - bus width. +- operating-points-v2: A phandle to the OPP v2 table that holds meaningful + instantaneous bandwidth values (in MB/s) that can be + requested from the device master port to the slave port. + The list of values depend on the supported bus/slave + frequencies and the bus width. Optional properties: - qcom,active-only: Indicates that the bandwidth votes need to be @@ -23,17 +23,36 @@ Optional properties: Example: + bw_opp_table: bw-opp-table { + compatible = "operating-points-v2"; + opp-75 { + opp-hz = /bits/ 64 < 572 >; /* 75 MHz */ + }; + opp-150 { + opp-hz = /bits/ 64 < 1144 >; /* 150 MHz */ + }; + opp-200 { + opp-hz = /bits/ 64 < 1525 >; /* 200 MHz */ + }; + opp-307 { + opp-hz = /bits/ 64 < 2342 >; /* 307 MHz */ + }; + opp-460 { + opp-hz = /bits/ 64 < 3509 >; /* 460 MHz */ + }; + opp-614 { + opp-hz = /bits/ 64 < 4684 >; /* 614 MHz */ + }; + opp-800 { + opp-hz = /bits/ 64 < 6103 >; /* 800 MHz */ + }; + opp-931 { + opp-hz = /bits/ 64 < 7102 >; /* 931 MHz */ + }; + }; qcom,cpubw { compatible = "qcom,devbw"; qcom,src-dst-ports = <1 512>, <2 512>; qcom,active-only; - qcom,bw-tbl = - < 572 /* 75 MHz */ >, - < 1144 /* 150 MHz */ >, - < 1525 /* 200 MHz */ >, - < 2342 /* 307 MHz */ >, - < 3509 /* 460 MHz */ >, - < 4684 /* 614 MHz */ >, - < 6103 /* 800 MHz */ >, - < 7102 /* 931 MHz */ >; + operating-points-v2 = <&bw_opp_table>; }; diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt index 84f0e124618e14353115680621ef555b8b88b793..923d145ff49c32c73cb0a4fa0a04e47cf6739373 100644 --- a/Documentation/devicetree/bindings/display/msm/sde.txt +++ b/Documentation/devicetree/bindings/display/msm/sde.txt @@ -352,8 +352,8 @@ Optional properties: priority for realtime clients. - qcom,sde-vbif-qos-nrt-remap: This array is used to program vbif qos remapper register priority for non-realtime clients. -- qcom,sde-danger-lut: A 4 cell property, with a format of , +- qcom,sde-danger-lut: Array of 5 cell property, with a format of + , indicating the danger luts on sspp. - qcom,sde-safe-lut-linear: Array of 2 cell property, with a format of in ascending fill level @@ -363,6 +363,11 @@ Optional properties: in ascending fill level indicating the safe luts for macrotile format on sspp. Zero fill level on the last entry identifies the default lut. +- qcom,sde-safe-lut-macrotile-qseed: Array of 2 cell property, with a format of + in ascending fill level + indicating the safe luts for macrotile format + with qseed3 on sspp. + Zero fill level on the last entry identifies the default lut. - qcom,sde-safe-lut-nrt: Array of 2 cell property, with a format of in ascending fill level indicating the safe luts for nrt (e.g wfd) on sspp. @@ -379,6 +384,11 @@ Optional properties: in ascending fill level indicating the qos luts for macrotile format on sspp. Zero fill level on the last entry identifies the default lut. +- qcom,sde-qos-lut-macrotile-qseed: Array of 3 cell property, with a format of + in ascending fill level + indicating the qos luts for macrotile format + with qseed3 enabled on sspp. + Zero fill level on the last entry identifies the default lut. - qcom,sde-qos-lut-nrt: Array of 3 cell property, with a format of in ascending fill level indicating the qos luts for nrt (e.g wfd) on sspp. @@ -617,8 +627,13 @@ Example: qcom,sde-wb-clk-ctrl = <0x2bc 16>; qcom,sde-danger-lut = <0x0000000f 0x0000ffff 0x00000000 - 0x00000000>; - qcom,sde-safe-lut = <0xfffc 0xff00 0xffff 0xffff>; + 0x00000000 0x0000ffff>; + qcom,sde-safe-lut-linear = <0 0xfff8>; + qcom,sde-safe-lut-macrotile = <0 0xf000>; + qcom,sde-safe-lut-macrotile-qseed = <0 0xf000>; + qcom,sde-safe-lut-nrt = <0 0xffff>; + qcom,sde-safe-lut-cwb = <0 0xffff>; + qcom,sde-qos-lut-linear = <4 0x00000000 0x00000357>, <5 0x00000000 0x00003357>, @@ -639,6 +654,8 @@ Example: <13 0x00002233 0x44556677>, <14 0x00012233 0x44556677>, <0 0x00112233 0x44556677>; + qcom,sde-qos-lut-macrotile-qseed = + <0 0x00112233 0x66777777>; qcom,sde-qos-lut-nrt = <0 0x00000000 0x00000000>; qcom,sde-qos-lut-cwb = diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt index 152146613a7e58417306f93bb995b915cbe85dcf..a9b05550c7c4f6209df8dc8d357bbf80407f5474 100644 --- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt @@ -241,9 +241,9 @@ Optional properties: - qcom,mdss-dsi-lane-2-state: Boolean that specifies whether data lane 2 is enabled. - qcom,mdss-dsi-lane-3-state: Boolean that specifies whether data lane 3 is enabled. - qcom,mdss-dsi-t-clk-post: Specifies the byte clock cycles after mode switch. - 0x03 = default value. + 0x00 = default value. - qcom,mdss-dsi-t-clk-pre: Specifies the byte clock cycles before mode switch. - 0x24 = default value. + 0x00 = default value. - qcom,mdss-dsi-stream: Specifies the packet stream to be used. 0 = stream 0 (default) 1 = stream 1 diff --git a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt index 375d856ab73da79a245491a3fc8a351d6819c00b..0b660ce672d1f0833e9120caba33735d8a01b397 100644 --- a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt +++ b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt @@ -62,6 +62,10 @@ Optional properties: - qcom,dsi-display-active: Current active display - qcom,dsi-ctrl: handle to dsi controller device - qcom,dsi-phy: handle to dsi phy device +- qcom,dsi-ctrl-num: Specifies the DSI controllers to use +- qcom,dsi-phy-num: Specifies the DSI PHYs to use +- qcom,dsi-select-clocks: Specifies the required clocks to use +- qcom,dsi-display-list: Specifies the list of supported displays. - qcom,dsi-manager: Specifies dsi manager is present - qcom,dsi-display: Specifies dsi display is present - qcom,hdmi-display: Specifies hdmi is present diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt index dfc14f71e81fb33bc3fe07133e2e36544cc875aa..1e904ca94538d2f7e103866da62f9b3db04b4920 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt +++ b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt @@ -10,6 +10,9 @@ Either one of id-gpio or vbus-gpio must be present. Both can be present as well. - id-gpio: gpio for USB ID pin. See gpio binding. - vbus-gpio: gpio for USB VBUS pin. +Optional properties: +- vbus-out-gpio: gpio for enabling VBUS output (e.g. when entering host mode) + Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below: extcon_usb1 { compatible = "linux,extcon-usb-gpio"; diff --git a/Documentation/devicetree/bindings/fb/adv7533.txt b/Documentation/devicetree/bindings/fb/adv7533.txt new file mode 100644 index 0000000000000000000000000000000000000000..b198f37f8fc610ad8bb77c7110fed95008e85cdc --- /dev/null +++ b/Documentation/devicetree/bindings/fb/adv7533.txt @@ -0,0 +1,54 @@ +ADV7533 DSI to HDMI bridge + + +Required properties: +- compatible: Must be "adv7533" +- reg: Main I2C slave ID (for I2C host driver) +- adi,video-mode: Excepted a number and possible inputs are 0 to 3, while: + 3 = 1080p + 2 = 720p + 1 = 480p + 0 = 1080p pattern +- adi,main-addr: Main I2C slave ID +- adi,cec-dsi-addr: CEC DSI I2C slave ID + +Optional properties: +- adi,enable-audio: +- adi,disable-gpios: +- adi,irq-gpio: Main IRQ gpio mapping +- adi,hpd-irq-gpio: HPD IRQ gpio mapping +- adi,switch-gpio: DSI switch gpio mapping +- qcom,supply-names: Regulator names that supply 5v to bridge chip +- qcom,min-voltage-level Minimum voltage level to be supplied to bridge chip +- qcom,max-voltage-level Maximum voltage level to be supplied to bridge chip +- qcom,enable-load Load current to bridge chip when enabled +- qcom,disable-load Load current to bridge chip when disabled +- qcom,post-on-sleep Sleep time (ms) to indicate the sleep + time after the vreg is enabled + +Example: +&soc { + i2c@78b8000 { + adv7533@39 { + compatible = "adv7533"; + reg = <0x39>; + adi,video-mode = <3>; /* 3 = 1080p */ + adi,main-addr = <0x39>; + adi,cec-dsi-addr = <0x3C>; + adi,enable-audio; + pinctrl-names = "pmx_adv7533_active","pmx_adv7533_suspend"; + pinctrl-0 = <&adv7533_int_active &adv7533_hpd_int_active &adv7533_switch_active>; + pinctrl-1 = <&adv7533_int_suspend &adv7533_hpd_int_suspend &adv7533_switch_suspend>; + adi,irq-gpio = <&msm_gpio 31 0x2002>; + adi,hpd-irq-gpio = <&msm_gpio 20 0x2003>; + adi,switch-gpio = <&msm_gpio 32 0x0>; + hpd-5v-en-supply = <&adv_vreg>; + qcom,supply-names = "hpd-5v-en"; + qcom,min-voltage-level = <0>; + qcom,max-voltage-level = <0>; + qcom,enable-load = <0>; + qcom,disable-load = <0>; + qcom,post-on-sleep = <10>; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/fb/lt8912.txt b/Documentation/devicetree/bindings/fb/lt8912.txt new file mode 100644 index 0000000000000000000000000000000000000000..daeb15fe3ab555c2de51d733487b84ce98957391 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/lt8912.txt @@ -0,0 +1,20 @@ +LT8912 DSI to HDMI bridge + + +Required properties: +- compatible: Must be "lontium,lt8912" +- reg: Main I2C slave ID (for I2C host driver) + +Optional properties: +- qcom,hdmi-reset: Main reset gpio mapping + +Example: +&soc { + i2c@78b8000 { + lt8912@48 { + compatible = "lontium,lt8912"; + reg = <0x48>; + qcom,hdmi-reset = <&tlmm 64 0x0>; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 608b4260a0ab6182c568a3f2196c358ce9efef6d..78566c9b9b36883710f1ae32ac307c00a3dbd0ab 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -1,4 +1,4 @@ -Qualcomm mdss-dsi-panel +Qualcomm Technologies, Inc. mdss-dsi-panel mdss-dsi-panel is a dsi panel device which supports panels that are compatible with MIPI display serial interface specification. @@ -12,6 +12,7 @@ Required properties: This property specifies the version for DSI HW that this panel will work with "qcom,dsi-panel-v2" = DSI V2.0 + "qcom,msm-dsi-v2" = DSI V2.0 - status: This property applies to DSI V2 panels only. This property should not be added for panels that work based on version "V6.0" @@ -37,8 +38,10 @@ Required properties: "display_2" = DISPLAY_2 - qcom,mdss-dsi-panel-timings: An array of length 12 that specifies the PHY timing settings for the panel. -- qcom,mdss-dsi-panel-timings-8996: An array of length 40 char that specifies the 8996 PHY lane - timing settings for the panel. +- qcom,mdss-dsi-panel-timings-phy-v2: An array of length 40 char that specifies the PHY version 2 + lane timing settings for the panel. +- qcom,mdss-dsi-panel-timings-phy-12nm: An array of length 8 char that specifies the 12nm DSI PHY + lane timing settings for the panel. - qcom,mdss-dsi-on-command: A byte stream formed by multiple dcs packets base on qcom dsi controller protocol. byte 0: dcs data type @@ -61,9 +64,39 @@ Required properties: transmitted byte 5, 6: 16 bits length in network byte order byte 7 and beyond: number byte of payload +- qcom,mdss-dsi-lp-mode-on: This is used to enable display low persistence mode. + A byte stream formed by multiple dcs packets base on + qcom dsi controller protocol. + byte 0: dcs data type + byte 1: set to indicate this is an individual packet + (no chain) + byte 2: virtual channel number + byte 3: expect ack from client (dcs read command) + byte 4: wait number of specified ms after dcs command + transmitted + byte 5, 6: 16 bits length in network byte order + byte 7 and beyond: number byte of payload +- qcom,mdss-dsi-lp-mode-off: This is used to disable display low persistence mode. + A byte stream formed by multiple dcs packets base on + qcom dsi controller protocol. + byte 0: dcs data type + byte 1: set to indicate this is an individual packet + (no chain) + byte 2: virtual channel number + byte 3: expect ack from client (dcs read command) + byte 4: wait number of specified ms after dcs command + transmitted + byte 5, 6: 16 bits length in network byte order + byte 7 and beyond: number byte of payload - qcom,mdss-dsi-post-panel-on-command: same as "qcom,mdss-dsi-on-command" except commands are sent after displaying an image. +- qcom,mdss-dsi-idle-on-command: same as "qcom,mdss-dsi-on-command". Set of DCS command + used for idle mode entry. + +- qcom,mdss-dsi-idle-off-command: same as "qcom,mdss-dsi-on-command". Set of DCS command + used for idle mode exit. + Note, if a short DCS packet(i.e packet with Byte 0:dcs data type as 05) mentioned in qcom,mdss-dsi-on-command/qcom,mdss-dsi-off-command stream fails to transmit, then 3 options can be tried. @@ -248,6 +281,35 @@ Optional properties: 60 = 60 frames per second (default) - qcom,mdss-dsi-panel-clockrate: A 64 bit value specifies the panel clock speed in Hz. 0 = default value. +- qcom,mdss-mdp-kickoff-threshold: This property can be used to define a region + (in terms of scanlines) where the +hardware is allowed + to trigger a data transfer from MDP to DSI. + If this property is used, the region must be defined setting + two values, the low and the high thresholds: + + Where following condition must be met: + low_threshold < high_threshold + These values will be used by the driver in such way that if + the Driver receives a request to kickoff a transfer (MDP to DSI), + the transfer will be triggered only if the following condition + is satisfied: + low_threshold < scanline < high_threshold + If the condition is not met, then the driver will delay the + transfer by the time defined in the following property: + "qcom,mdss-mdp-kickoff-delay". + So in order to use this property, the delay property must + be defined as well and greater than 0. +- qcom,mdss-mdp-kickoff-delay: This property defines the delay in microseconds that + the driver will delay before triggering an MDP transfer if the + thresholds defined by the following property are not met: + "qcom,mdss-mdp-kickoff-threshold". + So in order to use this property, the threshold property must + be defined as well. Note that this delay cannot be zero + and also should not be greater than +the fps window. + i.e. For 60fps value should not exceed +16666 uS. - qcom,mdss-mdp-transfer-time-us: Specifies the dsi transfer time for command mode panels in microseconds. Driver uses this number to adjust the clock rate according to the expected transfer time. @@ -275,14 +337,10 @@ Optional properties: to the physical width in the framebuffer information. - qcom,mdss-pan-physical-height-dimension: Specifies panel physical height in mm which corresponds to the physical height in the framebuffer information. -- qcom,mdss-dsi-mode-sel-gpio-state: String that specifies the lcd mode for panel - (such as single-port/dual-port), if qcom,panel-mode-gpio - binding is defined in dsi controller. - "dual_port" = Set GPIO to LOW - "single_port" = Set GPIO to HIGH +- qcom,mdss-dsi-panel-mode-gpio-state: String that specifies the mode state for panel if it is defined + in dsi controller. "high" = Set GPIO to HIGH "low" = Set GPIO to LOW - The default value is "dual_port". - qcom,mdss-tear-check-disable: Boolean to disable mdp tear check. Tear check is enabled by default to avoid tearing. Other tear-check properties are ignored if this property is present. The below tear check configuration properties can be individually tuned if @@ -330,6 +388,28 @@ Optional properties: 2A/2B command. - qcom,dcs-cmd-by-left: Boolean to indicate that dcs command are sent through the left DSI controller only in a dual-dsi configuration +- qcom,mdss-dsi-panel-hdr-enabled: Boolean to indicate HDR support in panel. +- qcom,mdss-dsi-panel-hdr-color-primaries: + Array of 8 unsigned integers denoting chromaticity of panel.These + values are specified in nits units. The value range is 0 through 50000. + To obtain real chromacity, these values should be divided by factor of + 50000. The structure of array is defined in below order + value 1: x value of white chromaticity of display panel + value 2: y value of white chromaticity of display panel + value 3: x value of red chromaticity of display panel + value 4: y value of red chromaticity of display panel + value 5: x value of green chromaticity of display panel + value 6: y value of green chromaticity of display panel + value 7: x value of blue chromaticity of display panel + value 8: y value of blue chromaticity of display panel +- qcom,mdss-dsi-panel-peak-brightness: Maximum brightness supported by panel.In absence of maximum value + typical value becomes peak brightness. Value is specified in nits units. + To obtail real peak brightness, this value should be divided by factor of + 10000. +- qcom,mdss-dsi-panel-blackness-level: Blackness level supported by panel. Blackness level is defined as + ratio of peak brightness to contrast. Value is specified in nits units. + To obtail real blackness level, this value should be divided by factor of + 10000. - qcom,mdss-dsi-lp11-init: Boolean used to enable the DSI clocks and data lanes (low power 11) before issuing hardware reset line. - qcom,mdss-dsi-init-delay-us: Delay in microseconds(us) before performing any DSI activity in lp11 @@ -424,7 +504,11 @@ Optional properties: fields in the supply entry, refer to the qcom,ctrl-supply-entries binding above. - qcom,config-select: Optional property to select default configuration. - +- qcom,panel-allow-phy-poweroff: A boolean property indicates that panel allows to turn off the phy power + supply during idle screen. A panel should able to handle the dsi lanes + in floating state(not LP00 or LP11) to turn on this property. Software + turns off PHY pmic power supply, phy ldo and DSI Lane ldo during + idle screen (footswitch control off) when this property is enabled. [[Optional config sub-nodes]] These subnodes provide different configurations for a given same panel. Default configuration can be chosen by specifying phandle of the selected subnode in the qcom,config-select. @@ -471,6 +555,7 @@ Optional properites: to a non-DSI interface. - qcom,bridge-name: A string to indicate the name of the bridge chip connected to DSI. qcom,bridge-name is required if qcom,dba-panel is defined for the panel. +- qcom,hdmi-mode: Indicates where current panel is HDMI mode, otherwise, it will be DVI mode. - qcom,adjust-timer-wakeup-ms: An integer value to indicate the timer delay(in ms) to accommodate s/w delay while configuring the event timer wakeup logic. @@ -493,6 +578,8 @@ Additional properties added to the second level nodes that represent timings pro Note, if a given optional qcom,* binding is not present, then the driver will configure the default values specified. +Note, all the "qcom,supply-*" properties have their definitions in mdss-dsi-txt. + Example: &mdss_mdp { dsi_sim_vid: qcom,mdss_dsi_sim_video { @@ -538,7 +625,6 @@ Example: qcom,mdss-dsi-underflow-color = <0xff>; qcom,mdss-dsi-bl-min-level = <1>; qcom,mdss-dsi-bl-max-level = < 15>; - qcom,mdss-brightness-max-level = <255>; qcom,mdss-dsi-interleave-mode = <0>; qcom,mdss-dsi-panel-type = "dsi_video_mode"; qcom,mdss-dsi-te-check-enable; @@ -568,19 +654,26 @@ Example: qcom,mdss-dsi-dma-trigger = <0>; qcom,mdss-dsi-panel-framerate = <60>; qcom,mdss-dsi-panel-clockrate = <424000000>; + qcom,mdss-mdp-kickoff-threshold = <11 2430>; + qcom,mdss-mdp-kickoff-delay = <1000>; qcom,mdss-mdp-transfer-time-us = <12500>; qcom,mdss-dsi-panel-timings = [7d 25 1d 00 37 33 22 27 1e 03 04 00]; - qcom,mdss-dsi-panel-timings-8996 = [23 20 06 09 05 03 04 a0 + qcom,mdss-dsi-panel-timings-phy-v2 = [23 20 06 09 05 03 04 a0 23 20 06 09 05 03 04 a0 23 20 06 09 05 03 04 a0 23 20 06 09 05 03 04 a0 23 2e 06 08 05 03 04 a0]; + qcom,mdss-dsi-panel-timings-phy-12nm = + [a9 4e 56 0b 8a 4d 0b d6]; qcom,mdss-dsi-on-command = [32 01 00 00 00 00 02 00 00 29 01 00 00 10 00 02 FF 99]; qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command = [22 01 00 00 00 00 00]; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-lp-mode-on = [32 01 00 00 00 00 02 00 00 + 29 01 00 00 10 00 02 FF 99]; + qcom,mdss-dsi-lp-mode-off = [22 01 00 00 00 00 00]; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_suspend_resume_mode"; @@ -592,7 +685,7 @@ Example: qcom,5v-boost-gpio = <&pm8994_gpios 14 0>; qcom,mdss-pan-physical-width-dimension = <60>; qcom,mdss-pan-physical-height-dimension = <140>; - qcom,mdss-dsi-mode-sel-gpio-state = "dsc_mode"; + qcom,mdss-dsi-panel-mode-gpio-state = "low"; qcom,mdss-tear-check-sync-cfg-height = <0xfff0>; qcom,mdss-tear-check-sync-init-val = <1280>; qcom,mdss-tear-check-sync-threshold-start = <4>; @@ -611,6 +704,7 @@ Example: qcom,suspend-ulps-enabled; qcom,panel-roi-alignment = <4 4 2 2 20 20>; qcom,esd-check-enabled; + qcom,panel-allow-phy-poweroff; qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 02 0A 08]; qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode"; qcom,mdss-dsi-panel-status-check-mode = "reg_read"; @@ -682,6 +776,7 @@ Example: qcom,supply-max-voltage = <2800000>; qcom,supply-enable-load = <100000>; qcom,supply-disable-load = <100>; + qcom,supply-ulp-load = <100>; qcom,supply-pre-on-sleep = <0>; qcom,supply-post-on-sleep = <0>; qcom,supply-pre-off-sleep = <0>; @@ -695,6 +790,7 @@ Example: qcom,supply-max-voltage = <1800000>; qcom,supply-enable-load = <100000>; qcom,supply-disable-load = <100>; + qcom,supply-ulp-load = <100>; qcom,supply-pre-on-sleep = <0>; qcom,supply-post-on-sleep = <0>; qcom,supply-pre-off-sleep = <0>; diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi.txt b/Documentation/devicetree/bindings/fb/mdss-dsi.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b593a97ef0d80dc4fb9785bd96c89cc2f1dd8ba --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt @@ -0,0 +1,261 @@ +Qualcomm Technologies, Inc. mdss-dsi + +mdss-dsi is the master DSI device which supports multiple DSI host controllers that +are compatible with MIPI display serial interface specification. + +Required properties: +- compatible: Must be "qcom,mdss-dsi" +- hw-config: Specifies the DSI host setup configuration + "hw-config" = "single_dsi" + "hw-config" = "dual_dsi" + "hw-config" = "split_dsi" +- ranges: The standard property which specifies the child address + space, parent address space and the length. +- vdda-supply: Phandle for vreg regulator device node. + +Bus Scaling Data: +- qcom,msm-bus,name: String property describing MDSS client. +- qcom, msm-bus,num-cases: This is the number of bus scaling use cases + defined in the vectors property. This must be + set to <2> for MDSS DSI driver where use-case 0 + is used to remove BW votes from the system. Use + case 1 is used to generate bandwidth requestes + when sending command packets. +- qcom,msm-bus,num-paths: This represents number of paths in each bus + scaling usecase. This value depends on number of + AXI master ports dedicated to MDSS for + particular chipset. +- qcom,msm-bus,vectors-KBps: A series of 4 cell properties, with a format + of (src, dst, ab, ib) which is defined at + Documentation/devicetree/bindings/arm/msm/msm_bus.txt. + DSI driver should always set average bandwidth + (ab) to 0 and always use instantaneous + bandwidth(ib) values. + +Optional properties: +- vcca-supply: Phandle for vcca regulator device node. +- qcom,-supply-entries: A node that lists the elements of the supply used by the + a particular "type" of DSI modulee. The module "types" + can be "core", "ctrl", and "phy". Within the same type, + there can be more than one instance of this binding, + in which case the entry would be appended with the + supply entry index. + e.g. qcom,ctrl-supply-entry@0 + -- qcom,supply-name: name of the supply (vdd/vdda/vddio) + -- qcom,supply-min-voltage: minimum voltage level (uV) + -- qcom,supply-max-voltage: maximum voltage level (uV) + -- qcom,supply-enable-load: load drawn (uA) from enabled supply + -- qcom,supply-disable-load: load drawn (uA) from disabled supply + -- qcom,supply-ulp-load: load drawn (uA) from supply in ultra-low power mode + -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on + -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on + -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off + -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off +- pll-src-config Specified the source PLL for the DSI + link clocks: + "PLL0" - Clocks sourced out of DSI PLL0 + "PLL1" - Clocks sourced out of DSI PLL1 + This property is only valid for + certain DSI hardware configurations + mentioned in the "hw-config" binding above. + For example, in split_dsi config, the clocks can + only be sourced out of PLL0. For + dual_dsi, both PLL would be active. + For single DSI, it is possible to + select either PLL. If no value is specified, + the default value for single DSI is set as PLL0. +- qcom,mmss-ulp-clamp-ctrl-offset: Specifies the offset for dsi ulps clamp control register. +- qcom,mmss-phyreset-ctrl-offset: Specifies the offset for dsi phy reset control register. +- qcom,dsi-clk-ln-recovery: Boolean which enables the clk lane recovery + +mdss-dsi-ctrl is a dsi controller device which is treated as a subnode of the mdss-dsi device. + +Required properties: +- compatible: Must be "qcom,mdss-dsi-ctrl" +- cell-index: Specifies the controller used among the two controllers. +- reg: Base address and length of the different register + regions(s) required for DSI device functionality. +- reg-names: A list of strings that map in order to the list of regs. + "dsi_ctrl" - MDSS DSI controller register region + "dsi_phy" - MDSS DSI PHY register region + "dsi_phy_regulator" - MDSS DSI PHY REGULATOR region + "mmss_misc_phys" - Register region for MMSS DSI clamps +- vdd-supply: Phandle for vdd regulator device node. +- vddio-supply: Phandle for vdd-io regulator device node. +- qcom,mdss-fb-map-prim: pHandle that specifies the framebuffer to which the + primary interface is mapped. +- qcom,mdss-mdp: pHandle that specifies the mdss-mdp device. +- qcom,platform-regulator-settings: An array of length 7 or 5 that specifies the PHY + regulator settings. It use 5 bytes for 8996 pll. +- qcom,platform-strength-ctrl: An array of length 2 or 10 that specifies the PHY + strengthCtrl settings. It use 10 bytes for 8996 pll. +- qcom,platform-lane-config: An array of length 45 or 20 that specifies the PHY + lane configuration settings. It use 20 bytes for 8996 pll. +- qcom,platform-bist-ctrl: An array of length 6 that specifies the PHY + BIST ctrl settings. +- qcom,dsi-pref-prim-pan: phandle that specifies the primary panel to be used + with the controller. + +Optional properties: +- label: A string used to describe the controller used. +- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the + interface is mapped. +- qcom,mdss-fb-map-sec: pHandle that specifies the framebuffer to which the + secondary interface is mapped. +- qcom,platform-enable-gpio: Specifies the panel lcd/display enable gpio. +- qcom,platform-reset-gpio: Specifies the panel reset gpio. +- qcom,platform-te-gpio: Specifies the gpio used for TE. +- qcom,platform-bklight-en-gpio: Specifies the gpio used to enable display back-light +- qcom,platform-mode-gpio: Select video/command mode of panel through gpio when it supports + both modes. +- qcom,platform-intf-mux-gpio: Select dsi/external(hdmi) interface through gpio when it supports + either dsi or external interface. +- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node + Refer to pinctrl-bindings.txt +- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin + controller. These pin configurations are installed in the pinctrl + device node. Refer to pinctrl-bindings.txt +- qcom,regulator-ldo-mode: Boolean to enable ldo mode for the dsi phy regulator +- qcom,null-insertion-enabled: Boolean to enable NULL packet insertion + feature for DSI controller. +- qcom,dsi-irq-line: Boolean specifies if DSI has a different irq line than mdp. +- qcom,lane-map: Specifies the data lane swap configuration. + "lane_map_0123" = <0 1 2 3> (default value) + "lane_map_3012" = <3 0 1 2> + "lane_map_2301" = <2 3 0 1> + "lane_map_1230" = <1 2 3 0> + "lane_map_0321" = <0 3 2 1> + "lane_map_1032" = <1 0 3 2> + "lane_map_2103" = <2 1 0 3> + "lane_map_3210" = <3 2 1 0> +- qcom,pluggable Boolean to enable hotplug feature. +- qcom,timing-db-mode: Boolean specifies dsi timing mode registers are supported or not. +- qcom,display-id A string indicates the display ID for the controller. + The possible values are: + - "primary" + - "secondary" + - "tertiary" +- qcom,bridge-index: Instance id of the bridge chip connected to DSI. qcom,bridge-index is + required if a bridge chip panel is used. + +Example: + mdss_dsi: qcom,mdss_dsi@0 { + compatible = "qcom,mdss-dsi"; + hw-config = "single_dsi"; + pll-src-config = "PLL0"; + #address-cells = <1>; + #size-cells = <1>; + vdda-supply = <&pm8226_l4>; + vcca-supply = <&pm8226_l28>; + reg = <0x1a98000 0x1a98000 0x25c + 0x1a98500 0x1a98500 0x280 + 0x1a98780 0x1a98780 0x30 + 0x193e000 0x193e000 0x30>; + + qcom,dsi-clk-ln-recovery; + + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + qcom,supply-ulp-load = <0>; + qcom,supply-pre-on-sleep = <0>; + qcom,supply-post-on-sleep = <0>; + qcom,supply-pre-off-sleep = <0>; + qcom,supply-post-off-sleep = <0>; + }; + }; + + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-ulp-load = <100>; + qcom,supply-pre-on-sleep = <0>; + qcom,supply-post-on-sleep = <20>; + qcom,supply-pre-off-sleep = <0>; + qcom,supply-post-off-sleep = <0>; + }; + }; + + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-ulp-load = <1000>; + qcom,supply-pre-on-sleep = <0>; + qcom,supply-post-on-sleep = <20>; + qcom,supply-pre-off-sleep = <0>; + qcom,supply-post-off-sleep = <0>; + }; + }; + + mdss_dsi0: mdss_dsi_ctrl0@fd922800 { + compatible = "qcom,mdss-dsi-ctrl"; + label = "MDSS DSI CTRL->0"; + cell-index = <0>; + reg = <0xfd922800 0x1f8>, + <0xfd922b00 0x2b0>, + <0xfd998780 0x30>, + <0xfd828000 0x108>; + reg-names = "dsi_ctrl", "dsi_phy", + "dsi_phy_regulator", "mmss_misc_phys"; + + vdd-supply = <&pm8226_l15>; + vddio-supply = <&pm8226_l8>; + qcom,mdss-fb-map-prim = <&mdss_fb0>; + qcom,mdss-mdp = <&mdss_mdp>; + + qcom,dsi-pref-prim-pan = <&dsi_tosh_720_vid>; + + qcom,platform-strength-ctrl = [ff 06]; + qcom,platform-bist-ctrl = [00 00 b1 ff 00 00]; + qcom,platform-regulator-settings = [07 09 03 00 20 00 01]; + qcom,platform-lane-config = [00 00 00 00 00 00 00 01 97 + 00 00 00 00 05 00 00 01 97 + 00 00 00 00 0a 00 00 01 97 + 00 00 00 00 0f 00 00 01 97 + 00 c0 00 00 00 00 00 01 bb]; + + qcom,mmss-ulp-clamp-ctrl-offset = <0x20>; + qcom,mmss-phyreset-ctrl-offset = <0x24>; + qcom,regulator-ldo-mode; + qcom,null-insertion-enabled; + qcom,timing-db-mode; + + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active>; + pinctrl-1 = <&mdss_dsi_suspend>; + qcom,platform-reset-gpio = <&msmgpio 25 1>; + qcom,platform-te-gpio = <&msmgpio 24 0>; + qcom,platform-enable-gpio = <&msmgpio 58 1>; + qcom,platform-bklight-en-gpio = <&msmgpio 86 0>; + qcom,platform-mode-gpio = <&msmgpio 7 0>; + qcom,platform-intf-mux-gpio = <&tlmm 115 0>; + qcom,dsi-irq-line; + qcom,lane-map = "lane_map_3012"; + qcom,display-id = "primary"; + qcom,bridge-index = <00>; + }; + }; diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt new file mode 100644 index 0000000000000000000000000000000000000000..3d649e5a6a0eb4ee3e422ed900569b3e31520240 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt @@ -0,0 +1,52 @@ +Qualcomm Technologies, Inc. MDSS EDP + +MDSS EDP is a edp driver which supports panels that are compatible with +VESA EDP display interface specification. + +When configuring the optional properties for external backlight, one should also +configure the gpio that drives the pwm to it. + +Required properties +- compatible : Must be "qcom,mdss-edp". +- reg : Offset and length of the register set for the + device. +- reg-names : Names to refer to register sets related to this + device +- vdda-supply : Phandle for vdd regulator device node. +- gpio-panel-en : GPIO for supplying power to panel and backlight + driver. +- gpio-lvl-en : GPIO to enable HPD be received by host. +- status : A string that has to be set to "okay/ok" to enable + the driver. By default this property will be set to + "disable". Will be set to "ok/okay" status for + specific platforms. +- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the + interface is mapped. +- gpio-panel-hpd : gpio pin use for edp hpd + +Optional properties +- qcom,panel-lpg-channel : LPG channel for backlight. +- qcom,panel-pwm-period : PWM period in microseconds. + + +Optional properties: +- qcom,mdss-brightness-max-level: Specifies the max brightness level supported. + 255 = default value. + +Example: + mdss_edp: qcom,mdss_edp@fd923400 { + compatible = "qcom,mdss-edp"; + reg = <0xfd923400 0x700>, + <0xfd8c2000 0x1000>; + reg-names = "edp_base", "mmss_cc_base"; + vdda-supply = <&pm8941_l12>; + gpio-panel-en = <&msmgpio 58 0>; + gpio-lvl-en = <&msmgpio 91 0>; + qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */ + qcom,panel-pwm-period = <53>; + status = "disable"; + qcom,mdss-fb-map = <&mdss_fb0>; + gpio-panel-hpd = <&msmgpio 102 0>; + }; + + diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt new file mode 100644 index 0000000000000000000000000000000000000000..5624321b47d68d19866f0c50f240ac59ebbfc7b9 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt @@ -0,0 +1,898 @@ +Qualcomm Technologies, Inc. MDSS MDP + +MDSS is Mobile Display SubSystem which implements Linux framebuffer APIs to +drive user interface to different panel interfaces. MDP driver is the core of +MDSS which manage all data paths to different panel interfaces. + +Required properties +- compatible : Must be "qcom,mdss_mdp" + - "qcom,mdss_mdp3" for mdp3 +- reg : offset and length of the register set for the device. +- reg-names : names to refer to register sets related to this device +- interrupts : Interrupt associated with MDSS. +- interrupt-controller: Mark the device node as an interrupt controller. + This is an empty, boolean property. +- #interrupt-cells: Should be one. The first cell is interrupt number. +- vdd-supply : Phandle for vdd regulator device node. +- qcom,max-clk-rate: Specify maximum MDP core clock rate in hz that this + device supports. +- qcom,mdss-pipe-vig-off: Array of offset for MDP source surface pipes of + type VIG, the offsets are calculated from + register "mdp_phys" defined in reg property. + The number of offsets defined here should + reflect the amount of VIG pipes that can be + active in MDP for this configuration. +- qcom,mdss-pipe-vig-fetch-id: Array of shared memory pool fetch ids + corresponding to the VIG pipe offsets defined in + previous property, the amount of fetch ids + defined should match the number of offsets + defined in property: qcom,mdss-pipe-vig-off +- qcom,mdss-pipe-vig-xin-id: Array of VBIF clients ids (xins) corresponding + to the respective VIG pipes. Number of xin ids + defined should match the number of offsets + defined in property: qcom,mdss-pipe-vig-off +- qcom,mdss-pipe-vig-clk-ctrl-off: Array of offsets describing clk control + offsets for dynamic clock gating. 1st value + in the array represents offset of the control + register. 2nd value represents bit offset within + control register and 3rd value represents bit + offset within status register. Number of tuples + defined should match the number of offsets + defined in property: qcom,mdss-pipe-vig-off +- qcom,mdss-pipe-rgb-off: Array of offsets for MDP source surface pipes of + type RGB, the offsets are calculated from + register "mdp_phys" defined in reg property. + The number of offsets defined here should + reflect the amount of RGB pipes that can be + active in MDP for this configuration. +- qcom,mdss-pipe-rgb-fetch-id: Array of shared memory pool fetch ids + corresponding to the RGB pipe offsets defined in + previous property, the amount of fetch ids + defined should match the number of offsets + defined in property: qcom,mdss-pipe-rgb-off +- qcom,mdss-pipe-rgb-xin-id: Array of VBIF clients ids (xins) corresponding + to the respective RGB pipes. Number of xin ids + defined should match the number of offsets + defined in property: qcom,mdss-pipe-rgb-off +- qcom,mdss-pipe-rgb-clk-ctrl-off: Array of offsets describing clk control + offsets for dynamic clock gating. 1st value + in the array represents offset of the control + register. 2nd value represents bit offset within + control register and 3rd value represents bit + offset within status register. Number of tuples + defined should match the number of offsets + defined in property: qcom,mdss-pipe-rgb-off +- qcom,mdss-pipe-dma-off: Array of offsets for MDP source surface pipes of + type DMA, the offsets are calculated from + register "mdp_phys" defined in reg property. + The number of offsets defined here should + reflect the amount of DMA pipes that can be + active in MDP for this configuration. +- qcom,mdss-pipe-dma-fetch-id: Array of shared memory pool fetch ids + corresponding to the DMA pipe offsets defined in + previous property, the amount of fetch ids + defined should match the number of offsets + defined in property: qcom,mdss-pipe-dma-off +- qcom,mdss-pipe-dma-xin-id: Array of VBIF clients ids (xins) corresponding + to the respective DMA pipes. Number of xin ids + defined should match the number of offsets + defined in property: qcom,mdss-pipe-dma-off +- qcom,mdss-pipe-dma-clk-ctrl-off: Array of offsets describing clk control + offsets for dynamic clock gating. 1st value + in the array represents offset of the control + register. 2nd value represents bit offset within + control register and 3rd value represents bit + offset within status register. Number of tuples + defined should match the number of offsets + defined in property: qcom,mdss-pipe-dma-off +- qcom,mdss-pipe-cursor-off: Array of offsets for MDP source surface pipes of + type cursor, the offsets are calculated from + register "mdp_phys" defined in reg property. + The number of offsets defined here should + reflect the amount of cursor pipes that can be + active in MDP for this configuration. Meant for + hardware that has hw cursors support as a + source pipe. +- qcom,mdss-pipe-cursor-xin-id: Array of VBIF clients ids (xins) corresponding + to the respective cursor pipes. Number of xin ids + defined should match the number of offsets + defined in property: qcom,mdss-pipe-cursor-off +- qcom,mdss-pipe-cursor-clk-ctrl-off: Array of offsets describing clk control + offsets for dynamic clock gating. 1st value + in the array represents offset of the control + register. 2nd value represents bit offset within + control register and 3rd value represents bit + offset within status register. Number of tuples + defined should match the number of offsets + defined in property: qcom,mdss-pipe-cursor-off +- qcom,mdss-ctl-off: Array of offset addresses for the available ctl + hw blocks within MDP, these offsets are + calculated from register "mdp_phys" defined in + reg property. The number of ctl offsets defined + here should reflect the number of control paths + that can be configured concurrently on MDP for + this configuration. +- qcom,mdss-wb-off: Array of offset addresses for the progammable + writeback blocks within MDP. The number of + offsets defined should match the number of ctl + blocks defined in property: qcom,mdss-ctl-off +- qcom,mdss-mixer-intf-off: Array of offset addresses for the available + mixer blocks that can drive data to panel + interfaces. + These offsets are be calculated from register + "mdp_phys" defined in reg property. + The number of offsets defined should reflect the + amount of mixers that can drive data to a panel + interface. +- qcom,mdss-dspp-off: Array of offset addresses for the available dspp + blocks. These offsets are calculated from + register "mdp_phys" defined in reg property. + The number of dspp blocks should match the + number of mixers driving data to interface + defined in property: qcom,mdss-mixer-intf-off +- qcom,mdss-pingpong-off: Array of offset addresses for the available + pingpong blocks. These offsets are calculated + from register "mdp_phys" defined in reg property. + The number of pingpong blocks should match the + number of mixers driving data to interface + defined in property: qcom,mdss-mixer-intf-off +- qcom,mdss-mixer-wb-off: Array of offset addresses for the available + mixer blocks that can be drive data to writeback + block. These offsets will be calculated from + register "mdp_phys" defined in reg property. + The number of writeback mixer offsets defined + should reflect the number of mixers that can + drive data to a writeback block. +- qcom,mdss-intf-off: Array of offset addresses for the available MDP + video interface blocks that can drive data to a + panel controller through timing engine. + The offsets are calculated from "mdp_phys" + defined in reg property. The number of offsets + defiend should reflect the number of progammable + interface blocks available in hardware. +- qcom,mdss-pref-prim-intf: A string which indicates the configured hardware + interface between MDP and the primary panel. + Individual panel controller drivers initialize + hardware based on this property. + Based on the interfaces supported at present, + possible values are: + - "dsi" + - "edp" + - "hdmi" + +Bus Scaling Data: +- qcom,msm-bus,name: String property describing MDSS client. +- qcom,msm-bus,num-cases: This is the the number of Bus Scaling use cases + defined in the vectors property. This must be + set to <3> for MDSS driver where use-case 0 is + used to take off MDSS BW votes from the system. + And use-case 1 & 2 are used in ping-pong fashion + to generate run-time BW requests. +- qcom,msm-bus,active-only: A boolean flag indicating if it is active only. +- qcom,msm-bus,num-paths: This represents the number of paths in each + Bus Scaling Usecase. This value depends on + how many number of AXI master ports are + dedicated to MDSS for particular chipset. This + value represents the RT + NRT AXI master ports. +- qcom,msm-bus,vectors-KBps: * A series of 4 cell properties, with a format + of (src, dst, ab, ib) which is defined at + Documentation/devicetree/bindings/arm/msm/msm_bus.txt + * Current values of src & dst are defined at + include/linux/msm-bus-board.h + src values allowed for MDSS are: + 22 = MSM_BUS_MASTER_MDP_PORT0 + 23 = MSM_BUS_MASTER_MDP_PORT1 + 25 = MSM_BUS_MASTER_ROTATOR + dst values allowed for MDSS are: + 512 = MSM_BUS_SLAVE_EBI_CH0 + ab: Represents aggregated bandwidth. + ib: Represents instantaneous bandwidth. + * Total number of 4 cell properties will be + (number of use-cases * number of paths). + * These values will be overridden by the driver + based on the run-time requirements. So initial + ab and ib values defined here are random and + bare no logic except for the use-case 0 where ab + and ib values needs to be 0. + * Define realtime vector properties followed by + non-realtime vector properties. + +- qcom,mdss-prefill-outstanding-buffer-bytes: The size of mdp outstanding buffer + in bytes. The buffer is filled during prefill + time and the buffer size shall be included in + prefill bandwidth calculation. +- qcom,mdss-prefill-y-buffer-bytes: The size of mdp y plane buffer in bytes. The + buffer is filled during prefill time when format + is YUV and the buffer size shall be included in + prefill bandwidth calculation. +- qcom,mdss-prefill-scaler-buffer-lines-bilinear: The value indicates how many lines + of scaler line buffer need to be filled during + prefill time. If bilinear scalar is enabled, then this + number of lines is used to determine how many bytes + of scaler buffer to be included in prefill bandwidth + calculation. +- qcom,mdss-prefill-scaler-buffer-lines-caf: The value indicates how many lines of + of scaler line buffer need to be filled during + prefill time. If CAF mode filter is enabled, then + this number of lines is used to determine how many + bytes of scaler buffer to be included in prefill + bandwidth calculation. +- qcom,mdss-prefill-post-scaler-buffer: The size of post scaler buffer in bytes. + The buffer is used to smooth the output of the + scaler. If the buffer is present in h/w, it is + filled during prefill time and the number of bytes + shall be included in prefill bandwidth calculation. +- qcom,mdss-prefill-pingpong-buffer-pixels: The size of pingpong buffer in pixels. + The buffer is used to keep pixels flowing to the + panel interface. If the vertical start position of a + layer is in the beginning of the active area, pingpong + buffer must be filled during prefill time to generate + starting lines. The number of bytes to be filled is + determined by the line width, starting position, + byte per pixel and scaling ratio, this number shall be + included in prefill bandwidth calculation. +- qcom,mdss-prefill-fbc-lines: The value indicates how many lines are required to fill + fbc buffer during prefill time if FBC (Frame Buffer + Compressor) is enabled. The number of bytes to be filled + is determined by the line width, bytes per pixel and + scaling ratio, this number shall be included in prefill bandwidth + calculation. +- qcom,max-mixer-width: Specify maximum MDP mixer width that the device supports. + This is a mandatory property, if not specified then + mdp probe will fail. + +Optional properties: +- batfet-supply : Phandle for battery FET regulator device node. +- vdd-cx-supply : Phandle for vdd CX regulator device node. +- qcom,vbif-settings : Array with key-value pairs of constant VBIF register + settings used to setup MDSS QoS for optimum performance. + The key used should be offset from "vbif_phys" register + defined in reg property. +- qcom,vbif-nrt-settings : The key used should be offset from "vbif_nrt_phys" + register defined in reg property. Refer qcom,vbif-settings + for a detailed description of this binding. +- qcom,mdp-settings : Array with key-value pairs of constant MDP register + settings used to setup MDSS QoS for best performance. + The key used should be offset from "mdp_phys" register + defined in reg property. +- qcom,mdss-smp-data: Array of shared memory pool data for dynamic SMP. There + should be only two values in this property. The first + value corresponds to the number of smp blocks and the + second is the size of each block present in the mdss + hardware. This property is optional for MDP hardware + with fix pixel latency ram. +- qcom,mdss-rot-block-size: The size of a memory block (in pixels) to be used + by the rotator. If this property is not specified, + then a default value of 128 pixels would be used. +- qcom,mdss-has-bwc: Boolean property to indicate the presence of bandwidth + compression feature in the rotator. +- qcom,mdss-has-non-scalar-rgb: Boolean property to indicate the presence of RGB + pipes which have no scaling support. +- qcom,mdss-has-decimation: Boolean property to indicate the presence of + decimation feature in fetch. +- qcom,mdss-has-fixed-qos-arbiter-enabled: Boolean property to indicate the + presence of rt/nrt feature. This feature enables + increased performance by prioritizing the real time + (rt) traffic over non real time (nrt) traffic to + access the memory. +- qcom,mdss-num-nrt-paths: Integer property represents the number of non-realtime + paths in each Bus Scaling Usecase. This value depends on + number of AXI ports are dedicated to non-realtime VBIF for + particular chipset. This property is mandatory when + "qcom,mdss-has-fixed-qos-arbiter-enabled" is enabled. + These paths must be defined after rt-paths in + "qcom,msm-bus,vectors-KBps" vector request. +- qcom,mdss-has-source-split: Boolean property to indicate if source split + feature is available or not. +- qcom,mdss-has-rotator-downscale: Boolean property to indicate if rotator + downscale feature is available or not. +- qcom,mdss-rot-downscale-min: This integer value indicates the Minimum + downscale factor supported by rotator. +- qcom,mdss-rot-downscale-max: This integer value indicates the Maximum + downscale factor supported by rotator. +- qcom,mdss-ad-off: Array of offset addresses for the available + Assertive Display (AD) blocks. These offsets + are calculated from the register "mdp_phys" + defined in reg property. The number of AD + offsets should be less than or equal to the + number of mixers driving interfaces defined in + property: qcom,mdss-mixer-intf-off. Assumes + that AD blocks are aligned with the mixer + offsets as well (i.e. the first mixer offset + corresponds to the same pathway as the first + AD offset). +- qcom,mdss-has-wb-ad: Boolean property to indicate assertive display feature + support on write back framebuffer. +- qcom,mdss-no-lut-read: Boolean property to indicate reading of LUT is + not supported. +- qcom,mdss-no-hist-vote Boolean property to indicate histogram reads + and histogram LUT writes do not need additional + bandwidth voting. +- qcom,mdss-mdp-wfd-mode: A string that specifies what is the mode of + writeback wfd block. + "intf" = Writeback wfd block is + connected to the interface mixer. + "shared" = Writeback block shared + between wfd and rotator. + "dedicated" = Dedicated writeback + block for wfd using writeback mixer. +- 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. +- qcom,mdss-highest-bank-bit: Property to indicate tile format as opposed to usual + linear format. The value tells the GPU highest memory + bank bit used. +- qcom,mdss-pipe-rgb-fixed-mmb: Array of indexes describing fixed Memory Macro + Blocks (MMBs) for rgb pipes. First value denotes + total numbers of MMBs per pipe while values, if + any, following first one denotes indexes of MMBs + to that RGB pipe. +- qcom,mdss-pipe-vig-fixed-mmb: Array of indexes describing fixed Memory Macro + Blocks (MMBs) for vig pipes. First value denotes + total numbers of MMBs per pipe while values, if + any, following first one denotes indexes of MMBs + to that VIG pipe. +- qcom,mdss-pipe-sw-reset-off: Property to indicate offset to the register which + holds sw_reset bitmap for different MDSS + components. +- qcom,mdss-pipe-vig-sw-reset-map: Array of bit offsets for vig pipes within + sw_reset register bitmap. Number of offsets + defined should match the number of offsets + defined in property: qcom,mdss-pipe-vig-off +- qcom,mdss-pipe-rgb-sw-reset-map: Array of bit offsets for rgb pipes within + sw_reset register bitmap. Number of offsets + defined should match the number of offsets + defined in property: qcom,mdss-pipe-rgb-off +- qcom,mdss-pipe-dma-sw-reset-map: Array of bit offsets for dma pipes within + sw_reset register bitmap. Number of offsets + defined should match the number of offsets + defined in property: qcom,mdss-pipe-dma-off +- qcom,mdss-default-ot-wr-limit: This integer value indicates maximum number of pending + writes that can be allowed on each WR xin. + This value can be used to reduce the pending writes + limit and can be tuned to match performance + requirements depending upon system state. + Some platforms require a dynamic ot limiting in + some cases. Setting this default ot write limit + will enable this dynamic limiting for the write + operations in the platforms that require these + limits. +- qcom,mdss-default-ot-rd-limit: This integer value indicates the default number of pending + reads that can be allowed on each RD xin. + Some platforms require a dynamic ot limiting in + some cases. Setting this default ot read limit + will enable this dynamic limiting for the read + operations in the platforms that require these + limits. +- qcom,mdss-clk-levels: This array indicates the mdp core clock level selection + array. Core clock is calculated for each frame and + hence depending upon calculated value, clock rate + will be rounded up to the next level according to + this table. Order of entries need to be ordered in + ascending order. +- qcom,mdss-vbif-qos-rt-setting: This array is used to program vbif qos remapper register + priority for real time clients. +- qcom,mdss-vbif-qos-nrt-setting: This array is used to program vbif qos remapper register + priority for non real time clients. +- qcom,mdss-traffic-shaper-enabled: This boolean property enables traffic shaper functionality + for MDSS rotator which spread out rotator bandwidth request + so that rotator don't compete with other real time read + clients. +- qcom,mdss-dram-channels: This represents the number of channels in the + Bus memory controller. +- qcom,regs-dump-mdp: This array represents the registers offsets that + will be dumped from the mdp when the debug logging + is enabled; each entry in the table is an start and + end offset from the MDP address "mdp_phys", the + format of each entry is as follows: + + Ex: + <0x01000 0x01404> + Will dump the MDP registers + from the address: "mdp_phys + 0x01000" + to the address: "mdp_phys + 0x01404" +- qcom,regs-dump-names-mdp: This array represents the tag that will be used + for each of the entries defined within regs-dump. + Note that each tag matches with one of the + regs-dump entries in the same order as they + are defined. +- qcom,regs-dump-xin-id-mdp: Array of VBIF clients ids (xins) corresponding + to mdp block. Xin id property is not valid for mdp + internal blocks like ctl, lm, dspp. It should set + to 0xff for such blocks. + +Fudge Factors: Fudge factors are used to boost demand for + resources like bus bandswidth, clk rate etc. to + overcome system inefficiencies and avoid any + glitches. These fudge factors are expressed in + terms of numerator and denominator. First value + is numerator followed by denominator. They all + are optional but highly recommended. + Ex: + x = value to be fudged + a = numerator, default value is 1 + b = denominator, default value is 1 + FUDGE(x, a, b) = ((x * a) / b) +- qcom,mdss-ib-factor: This fudge factor is applied to calculated ib + values in default conditions. +- qcom,mdss-ib-factor-overlap: This fudge factor is applied to calculated ib + values when the overlap bandwidth is the + predominant value compared to prefill bandwidth + value. +- qcom,mdss-clk-factor: This fudge factor is applied to calculated mdp + clk rate in default conditions. + +- qcom,max-bandwidth-low-kbps: This value indicates the max bandwidth in KB + that can be supported without underflow. + This is a low bandwidth threshold which should + be applied in most scenarios to be safe from + underflows when unable to satisfy bandwidth + requirements. +- qcom,max-bandwidth-high-kbps: This value indicates the max bandwidth in KB + that can be supported without underflow. + This is a high bandwidth threshold which can be + applied in scenarios where panel interface can + be more tolerant to memory latency such as + command mode panels. +- qcom,max-bandwidth-per-pipe-kbps: A two dimensional array indicating the max + bandwidth in KB that a single pipe can support + without underflow for various usecases. The + first parameter indicates the usecase and the + second parameter gives the max bw allowed for + the usecase. Following are the enum values for + modes in different cases: + For default case, mode = 1 + camera usecase, mode = 2 + hflip usecase, mode = 4 + vflip usecase, mode = 8 + First parameter/mode value need to match enum, + mdss_mdp_max_bw_mode, present in + include/uapi/linux/msm_mdp.h. +- qcom,max-bw-settings: This two dimension array indicates the max bandwidth + in KB that has to be supported when particular + scenarios are involved such as camera, flip. + The first parameter indicate the + scenario/usecase and second parameter indicate + the maximum bandwidth for that usecase. + Following are the enum values for modes in different + cases: + For default case, mode = 1 + camera usecase, mode = 2 + hflip usecase, mode = 4 + vflip usecase, mode = 8 + First parameter/mode value need to match enum, + mdss_mdp_max_bw_mode, present in + include/uapi/linux/msm_mdp.h. + +- qcom,mdss-has-panic-ctrl: Boolean property to indicate if panic/robust signal + control feature is available or not. +- qcom,mdss-en-svs-high: Boolean property to indicate if this target needs to + enable the svs high voltage level for CX rail. +- qcom,mdss-pipe-vig-panic-ctrl-offsets: Array of panic/robust signal offsets + corresponding to the respective VIG pipes. + Number of signal offsets should match the + number of offsets defined in property: + qcom,mdss-pipe-vig-off +- qcom,mdss-pipe-rgb-panic-ctrl-offsets: Array of panic/robust signal offsets + corresponding to the respective RGB pipes. + Number of signal offsets should match the + number of offsets defined in property: + qcom,mdss-pipe-rgb-off +- qcom,mdss-pipe-dma-panic-ctrl-offsets: Array of panic/robust signal offsets + corresponding to the respective DMA pipes. + Number of signal offsets should match the + number of offsets defined in property: + qcom,mdss-pipe-dma-off +- qcom,mdss-per-pipe-panic-luts: Array to configure the panic/robust luts for + each rt and nrt clients. This property is + for the MDPv1.7 and above, which configures + the panic independently on each client. + Each element of the array corresponds to: + First element - panic for linear formats + Second element - panic for tile formats + Third element - robust for linear formats + Fourth element - robust for tile formats +- qcom,mdss-has-pingpong-split: Boolean property to indicate if destination + split feature is available or not in the target. +- qcom,mdss-slave-pingpong-off: Offset address for the extra TE block which needs + to be programmed when pingpong split feature is enabled. + Offset is calculated from the "mdp_phys" + register value. Mandatory when qcom,mdss-has-pingpong-split + is enabled. +- qcom,mdss-ppb-ctl-off: Array of offset addresses of ping pong buffer control registers. + The offsets are calculated from the "mdp_phys" base address + specified. The number of offsets should match the + number of ping pong buffers available in the hardware. + Mandatory when qcom,mdss-has-pingpong-split is enabled. +- qcom,mdss-ppb-cfg-off: Array of offset addresses of ping pong buffer config registers. + The offsets are calculated from the "mdp_phys" base address + specified. The number of offsets should match the + number of ping pong buffers available in the hardware. + Mandatory when qcom,mdss-has-pingpong-split is enabled. +- qcom,mdss-cdm-off: Array of offset addresses for the available + chroma down modules that can convert RGB data + to YUV before sending it to the interface + block. These offsets will be calculated from + register "mdp_phys" define in reg property. The + number of cdm offsets should reflect the number + of cdm blocks present in hardware. +- qcom,mdss-dsc-off: Array of offset addresses for the available + display stream compression module block. + These offsets will be calculated from + register "mdp_phys" define in reg property. The + number of dsc offsets should reflect the number + of dsc blocks present in hardware. +- qcom,max-pipe-width: This value specifies the maximum MDP SSPP width + the device supports. If not specified, a default value + of 2048 will be applied. +- qcom,mdss-reg-bus: Property to provide Bus scaling for register access for + MDP and DSI Blocks. + +- qcom,mdss-rot-reg-bus: Property to provide Bus scaling for register access for + Rotator Block. + +- qcom,mdss-hw-rt: Optional Property to request min vote on the bus. + Few Low tier targets expect min vote on the bus during SMMU + and TZ operations. use this handle to request the vote needed. + +Optional subnodes: +- mdss_fb: Child nodes representing the frame buffer virtual devices. + +Subnode properties: +- compatible : Must be "qcom,mdss-fb" +- cell-index : Index representing frame buffer +- qcom,mdss-mixer-swap: A boolean property that indicates if the mixer muxes + need to be swapped based on the target panel. + By default the property is not defined. +- qcom,memblock-reserve: Specifies the memory location and the size reserved + for the framebuffer used to display the splash screen. + This property is required whenever the continuous splash + screen feature is enabled for the corresponding + framebuffer device. It should be used for only 32bit + kernel. +- qcom,cont-splash-memory: Specifies the memory block region reserved for + continuous splash screen feature. This property should be + defined for corresponding framebuffer device if + "qcom,memblock-reserve" is not defined when continuous + splash screen feature is enabled. +- linux,contiguous-region: Phandle to the continuous memory region reserved for + frame-buffer or continuous splash screen. Size of this + region is dependent on the display panel resolution and + buffering scheme for frame-buffer node. Currently driver + uses double buffering. + + Example: Width = 1920, Height = 1080, BytesPerPixel = 4, + Number of frame-buffers reserved = 2. + Size = 1920*1080*4*2 = ROUND_1MB(15.8MB) = 16MB. +- qcom,mdss-fb-splash-logo-enabled: The boolean entry enables the framebuffer + driver to display the splash logo image. + It is independent of continuous splash + screen feature and has no relation with + qcom,cont-splash-enabled entry present in + panel configuration. +- qcom,mdss-idle-power-collapse-enabled: Boolean property that enables support + for mdss power collapse in idle + screen use cases with smart panels. +- qcom,boot-indication-enabled: Boolean property that enables turning on the blue + LED for notifying that the device is in boot + process. + +- qcom,mdss-pp-offets: A node that lists the offsets of post processing blocks + from base module. + -- qcom,mdss-mdss-sspp-igc-lut-off: This 32 bit value provides the + offset to the IGC lut rams from mdp_phys base. + -- qcom,mdss-sspp-vig-pcc-off: This 32 bit value provides the offset + to PCC block from the VIG pipe base address. + -- qcom,mdss-sspp-rgb-pcc-off: This 32 bit value provides the offset + to PCC block from the RGB pipe base address. + -- qcom,mdss-sspp-dma-pcc-off: This 32 bit value provides the offset + to PCC block from the DMA pipe base address. + -- qcom,mdss-dspp-pcc-off: This 32 bit value provides the offset + to PCC block from the DSPP pipe base address. + -- qcom,mdss-lm-pgc-off: This 32 bit value provides the offset + to PGC block from the layer mixer base address. + -- qcom,mdss-dspp-gamut-off: This 32 bit value provides the offset + to gamut block from DSPP base address. + -- qcom,mdss-dspp-pgc-off: This 32 bit value provides the offset to + PGC block from the DSPP base address. + +- qcom,mdss-scaler-offsets: A node that lists the offsets of scaler blocks + from base module. + -- qcom,mdss-vig-scaler-off: This 32 bit value provides the + offset to vig scaler from vig pipe base. + -- qcom,mdss-vig-scaler-lut-off: This 32 bit value provides the + offset to vig scaler lut from vig pipe base. + -- qcom,mdss-has-dest-scaler: Boolean property to indicate the + presence of destination scaler block. + -- qcom,mdss-dest-block-off: This 32 bit value provides the + offset from mdp base to destination scaler block. + -- qcom,mdss-dest-scaler-off: Array containing offsets of + destination scalar modules from the scaler block. + -- qcom,mdss-dest-scaler-lut-off: Array containing offsets of destination + scaler lut tables from scalar block. + +- qcom,mdss-has-separate-rotator: Boolean property to indicate support of + indpendent rotator. Indpendent rotator has + separate DMA pipe working in block mode only. + +- smmu_mdp_***: Child nodes representing the mdss smmu virtual devices. + Mandatory smmu v2 and not required for smmu v1. + +Subnode properties: +- compatible : Compatible name used in smmu v2. + smmu_v2 names should be: + "qcom,smmu_mdp_unsec" - smmu context bank device for + unsecure mdp domain. + "qcom,smmu_rot_unsec" - smmu context bank device for + unsecure rotation domain. + "qcom,smmu_mdp_sec" - smmu context bank device for + secure mdp domain. + "qcom,smmu_rot_sec" - smmu context bank device for + secure rotation domain. + "qcom,smmu_kms_unsec" - smmu context bank device for + unsecure mdp domain for KMS driver. + "qcom,smmu_nrt_unsec" - smmu context bank device for + unsecure rotation domain for KMS driver. + "qcom,smmu_kms_sec" - smmu context bank device for + secure mdp domain for KMS driver. + "qcom,smmu_nrt_sec" - smmu context bank device for + secure rotation domain for KMS driver. + "qcom,smmu_arm_mdp_unsec" - arm smmu context bank device for + unsecure mdp domain. + "qcom,smmu_arm_mdp_sec" - arm smmu context bank device for + secure mdp domain. +- gdsc-mmagic-mdss-supply: Phandle for mmagic mdss supply regulator device node. +- reg : offset and length of the register set for the device. +- reg-names : names to refer to register sets related to this device +- clocks: List of Phandles for clock device nodes + needed by the device. +- clock-names: List of clock names needed by the device. + +Subnode properties: +Required properties: +- compatible: Must be "qcom,mdss_wb" +- qcom,mdss_pan_res: Array containing two elements, width and height which + specifies size of writeback buffer. +- qcom,mdss_pan_bpp: Specifies bits per pixel for writeback buffer. +- qcom,mdss-fb-map: Specifies the handle for frame buffer. + +Example: + mdss_mdp: qcom,mdss_mdp@fd900000 { + compatible = "qcom,mdss_mdp"; + reg = <0xfd900000 0x22100>, + <0xfd924000 0x1000>, + <0xfd925000 0x1000>; + reg-names = "mdp_phys", "vbif_phys", "vbif_nrt_phys"; + interrupts = <0 72 0>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + vdd-supply = <&gdsc_mdss>; + batfet-supply = <&pm8941_chg_batif>; + vdd-cx-supply = <&pm8841_s2_corner>; + + /* Bus Scale Settings */ + qcom,msm-bus,name = "mdss_mdp"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <2>; + qcom,mdss-dram-channels = <2>; + qcom,mdss-num-nrt-paths = <1>; + qcom,msm-bus,vectors-KBps = + <22 512 0 0>, <23 512 0 0>, + <22 512 0 6400000>, <23 512 0 6400000>, + <22 512 0 6400000>, <23 512 0 6400000>; + + /* Fudge factors */ + qcom,mdss-ab-factor = <2 1>; /* 2 times */ + qcom,mdss-ib-factor = <3 2>; /* 1.5 times */ + qcom,mdss-high-ib-factor = <2 1>; /* 2 times */ + qcom,mdss-clk-factor = <5 4>; /* 1.25 times */ + + /* Clock levels */ + qcom,mdss-clk-levels = <92310000, 177780000, 200000000>; + + /* VBIF QoS remapper settings*/ + qcom,mdss-vbif-qos-rt-setting = <2 2 2 2>; + qcom,mdss-vbif-qos-nrt-setting = <1 1 1 1>; + + qcom,max-bandwidth-low-kbps = <2300000>; + qcom,max-bandwidth-high-kbps = <3000000>; + qcom,max-bandwidth-per-pipe-kbps = <4 2100000>, + <8 1800000>; + qcom,max-bw-settings = <1 2300000>, + <2 1700000>, + <4 2300000>, + <8 2000000>; + + qcom,max-mixer-width = <2048>; + qcom,max-pipe-width = <2048>; + qcom,max-clk-rate = <320000000>; + qcom,vbif-settings = <0x0004 0x00000001>, + <0x00D8 0x00000707>; + qcom,vbif-nrt-settings = <0x0004 0x00000001>, + <0x00D8 0x00000707>; + qcom,mdp-settings = <0x02E0 0x000000AA>, + <0x02E4 0x00000055>; + qcom,mdss-pipe-vig-off = <0x00001200 0x00001600 + 0x00001A00>; + qcom,mdss-pipe-rgb-off = <0x00001E00 0x00002200 + 0x00002600>; + qcom,mdss-pipe-dma-off = <0x00002A00 0x00002E00>; + qcom,mdss-pipe-cursor-off = <0x00035000 0x00037000>; + qcom,mdss-dsc-off = <0x00081000 0x00081400>; + qcom,mdss-pipe-vig-fetch-id = <1 4 7>; + qcom,mdss-pipe-rgb-fetch-id = <16 17 18>; + qcom,mdss-pipe-dma-fetch-id = <10 13>; + qcom,mdss-pipe-rgb-fixed-mmb = <2 0 1>, + <2 2 3>, + <2 4 5>, + <2 6 7>; + qcom,mdss-pipe-vig-fixed-mmb = <1 8>, + <1 9>, + <1 10>, + <1 11>; + qcom,mdss-smp-data = <22 4096>; + qcom,mdss-rot-block-size = <64>; + qcom,mdss-rotator-ot-limit = <2>; + qcom,mdss-smp-mb-per-pipe = <2>; + qcom,mdss-pref-prim-intf = "dsi"; + qcom,mdss-has-non-scalar-rgb; + qcom,mdss-has-bwc; + qcom,mdss-has-decimation; + qcom,mdss-has-fixed-qos-arbiter-enabled; + qcom,mdss-has-source-split; + qcom,mdss-wfd-mode = "intf"; + qcom,mdss-no-lut-read; + qcom,mdss-no-hist-vote; + qcom,mdss-traffic-shaper-enabled; + qcom,mdss-has-rotator-downscale; + qcom,mdss-rot-downscale-min = <2>; + qcom,mdss-rot-downscale-max = <16>; + + qcom,mdss-has-pingpong-split; + qcom,mdss-pipe-vig-xin-id = <0 4 8>; + qcom,mdss-pipe-rgb-xin-id = <1 5 9>; + qcom,mdss-pipe-dma-xin-id = <2 10>; + qcom,mdss-pipe-cursor-xin-id = <7 7>; + + qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>, + <0x3B4 0 0>, + <0x3BC 0 0>, + <0x3C4 0 0>; + + qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>, + <0x3B4 4 8>, + <0x3BC 4 8>, + <0x3C4 4 8>; + + qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>, + <0x3B4 8 12>; + + qcom,mdss-per-pipe-panic-luts = <0x000f>, + <0xffff>, + <0xfffc>, + <0xff00>; + + qcom,mdss-has-panic-ctrl; + qcom,mdss-pipe-vig-panic-ctrl-offsets = <0 1 2 3>; + qcom,mdss-pipe-rgb-panic-ctrl-offsets = <4 5 6 7>; + qcom,mdss-pipe-dma-panic-ctrl-offsets = <8 9>; + + qcom,mdss-pipe-sw-reset-off = <0x0128>; + qcom,mdss-pipe-vig-sw-reset-map = <5 6 7 8>; + qcom,mdss-pipe-rgb-sw-reset-map = <9 10 11 12>; + qcom,mdss-pipe-dma-sw-reset-map = <13 14>; + + qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800 + 0x00000900 0x0000A00>; + qcom,mdss-mixer-intf-off = <0x00003200 0x00003600 + 0x00003A00>; + qcom,mdss-mixer-wb-off = <0x00003E00 0x00004200>; + qcom,mdss-dspp-off = <0x00004600 0x00004A00 0x00004E00>; + qcom,mdss-pingpong-off = <0x00012D00 0x00012E00 0x00012F00>; + qcom,mdss-wb-off = <0x00011100 0x00013100 0x00015100 + 0x00017100 0x00019100>; + qcom,mdss-intf-off = <0x00021100 0x00021300 + 0x00021500 0x00021700>; + qcom,mdss-cdm-off = <0x0007A200>; + qcom,mdss-ppb-ctl-off = <0x0000420>; + qcom,mdss-ppb-cfg-off = <0x0000424>; + qcom,mdss-slave-pingpong-off = <0x00073000> + + /* buffer parameters to calculate prefill bandwidth */ + qcom,mdss-prefill-outstanding-buffer-bytes = <1024>; + qcom,mdss-prefill-y-buffer-bytes = <4096>; + qcom,mdss-prefill-scaler-buffer-lines-bilinear = <2>; + qcom,mdss-prefill-scaler-buffer-lines-caf = <4>; + qcom,mdss-prefill-post-scaler-buffer-pixels = <2048>; + qcom,mdss-prefill-pingpong-buffer-pixels = <5120>; + qcom,mdss-prefill-fbc-lines = <2>; + qcom,mdss-idle-power-collapse-enabled; + + qcom,regs-dump-xin-id-mdp = <0xff 0xff 0xff 0xff 0x0 0x0>; + mdss_fb0: qcom,mdss_fb_primary { + cell-index = <0>; + compatible = "qcom,mdss-fb"; + qcom,mdss-mixer-swap; + linux,contiguous-region = <&fb_mem>; + qcom,mdss-fb-splash-logo-enabled: + qcom,cont-splash-memory { + linux,contiguous-region = <&cont_splash_mem>; + }; + }; + + qcom,mdss-pp-offsets { + qcom,mdss-sspp-mdss-igc-lut-off = <0x3000>; + qcom,mdss-sspp-vig-pcc-off = <0x1580>; + qcom,mdss-sspp-rgb-pcc-off = <0x180>; + qcom,mdss-sspp-dma-pcc-off = <0x180>; + qcom,mdss-lm-pgc-off = <0x3C0>; + qcom,mdss-dspp-gamut-off = <0x1600>; + qcom,mdss-dspp-pcc-off = <0x1700>; + qcom,mdss-dspp-pgc-off = <0x17C0>; + }; + + qcom,mdss-scaler-offsets { + qcom,mdss-vig-scaler-off = <0xA00>; + qcom,mdss-vig-scaler-lut-off = <0xB00>; + qcom,mdss-has-dest-scaler; + qcom,mdss-dest-block-off = <0x00061000>; + qcom,mdss-dest-scaler-off = <0x800 0x1000>; + qcom,mdss-dest-scaler-lut-off = <0x900 0x1100>; + }; + + qcom,mdss-reg-bus { + /* Reg Bus Scale Settings */ + qcom,msm-bus,name = "mdss_reg"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,active-only; + qcom,msm-bus,vectors-KBps = + <1 590 0 0>, + <1 590 0 76800>, + <1 590 0 160000>, + <1 590 0 320000>; + }; + + qcom,mdss-hw-rt-bus { + /* hw-rt Bus Scale Settings */ + qcom,msm-bus,name = "mdss_hw_rt"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <22 512 0 0>, + <22 512 0 1000>; + }; + + smmu_mdp_sec: qcom,smmu_mdp_sec_cb { + compatible = "qcom,smmu_mdp_sec"; + iommus = <&mdp_smmu 1>; + reg = <0xd09000 0x000d00>, + reg-names = "mmu_cb"; + gdsc-mmagic-mdss-supply = <&gdsc_mmagic_mdss>; + clocks = <&clock_mmss clk_smmu_mdp_ahb_clk>, + <&clock_mmss clk_smmu_mdp_axi_clk>; + clock-names = "dummy_clk", "dummy_clk"; + }; + + qcom,mdss_wb_panel { + compatible = "qcom,mdss_wb"; + qcom,mdss_pan_res = <1280 720>; + qcom,mdss_pan_bpp = <24>; + qcom,mdss-fb-map = <&mdss_fb1>; + }; + + qcom,mdss-rot-reg-bus { + /* Reg Bus Scale Settings */ + qcom,msm-bus,name = "mdss_rot_reg"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,active-only; + qcom,msm-bus,vectors-KBps = + <1 590 0 0>, + <1 590 0 76800>; + }; + }; + diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt index e92e4deb0469432cb0b94f6a314974e1771cf438..6b9238c4bf302d0dc9824b79b23a5596741d02b9 100644 --- a/Documentation/devicetree/bindings/fb/mdss-pll.txt +++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt @@ -1,24 +1,20 @@ -Qualcomm Technologies MDSS pll for DSI/EDP/HDMI +Qualcomm Technologies, Inc. MDSS pll for DSI/EDP/HDMI -mdss-pll is a pll controller device which supports pll devices that -are compatible with MIPI display serial interface specification, -HDMI and edp. +mdss-pll is a pll controller device which supports pll devices that are +compatiable with MIPI display serial interface specification, HDMI and edp. Required properties: -- compatible: Compatible name used in the driver - "qcom,mdss_dsi_pll_8916", "qcom,mdss_dsi_pll_8939", - "qcom,mdss_dsi_pll_8974", "qcom,mdss_dsi_pll_8994", - "qcom,mdss_dsi_pll_8994", "qcom,mdss_dsi_pll_8909", - "qcom,mdss_hdmi_pll", "qcom,mdss_hdmi_pll_8994", - "qcom,mdss_dsi_pll_8992", "qcom,mdss_hdmi_pll_8992", - "qcom,mdss_dsi_pll_8996", "qcom,mdss_hdmi_pll_8996", - "qcom,mdss_hdmi_pll_8996_v2", "qcom,mdss_dsi_pll_8996_v2", - "qcom,mdss_hdmi_pll_8996_v3", "qcom,mdss_hdmi_pll_8996_v3_1p8", - "qcom,mdss_edp_pll_8996_v3", "qcom,mdss_edp_pll_8996_v3_1p8", - "qcom,mdss_dsi_pll_10nm", "qcom,mdss_dp_pll_8998", - "qcom,mdss_hdmi_pll_8998", "qcom,mdss_dp_pll_10nm", - "qcom,mdss_dsi_pll_7nm", - "qcom,mdss_dp_pll_7nm". +- compatible: Compatible name used in the driver. Should be one of: + "qcom,mdss_dsi_pll_8916", "qcom,mdss_dsi_pll_8939", + "qcom,mdss_dsi_pll_8974", "qcom,mdss_dsi_pll_8994", + "qcom,mdss_dsi_pll_8994", "qcom,mdss_dsi_pll_8909", + "qcom,mdss_hdmi_pll", "qcom,mdss_hdmi_pll_8994", + "qcom,mdss_dsi_pll_8992", "qcom,mdss_hdmi_pll_8992", + "qcom,mdss_dsi_pll_8996", "qcom,mdss_hdmi_pll_8996", + "qcom,mdss_hdmi_pll_8996_v2", "qcom,mdss_dsi_pll_8996_v2", + "qcom,mdss_hdmi_pll_8996_v3", "qcom,mdss_dsi_pll_8952", + "qcom,mdss_dsi_pll_8937", "qcom,mdss_hdmi_pll_8996_v3_1p8", + "qcom,mdss_dsi_pll_8953" - cell-index: Specifies the controller used - reg: offset and length of the register set for the device. - reg-names : names to refer to register sets related to this device diff --git a/Documentation/devicetree/bindings/fb/mdss-qpic-panel.txt b/Documentation/devicetree/bindings/fb/mdss-qpic-panel.txt new file mode 100644 index 0000000000000000000000000000000000000000..8c11a438f5d8f569ec77f77484f0388dc436b8e6 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-qpic-panel.txt @@ -0,0 +1,25 @@ +Qualcomm Technologies, Inc. mdss-qpic-panel + +mdss-qpic-panel is a panel device which can be driven by qpic. + +Required properties: +- compatible: Must be "qcom,mdss-qpic-panel" +- qcom,mdss-pan-res: A two dimensional array that specifies the panel + resolution. +- qcom,mdss-pan-bpp: Specifies the panel bits per pixel. +- qcom,refresh_rate: Panel refresh rate + +Optional properties: +- label: A string used as a descriptive name of the panel + + +Example: +/ { + qcom,mdss_lcdc_ili9341_qvga { + compatible = "qcom,mdss-qpic-panel"; + label = "ili qvga lcdc panel"; + qcom,mdss-pan-res = <240 320>; + qcom,mdss-pan-bpp = <18>; + qcom,refresh_rate = <60>; + }; +}; diff --git a/Documentation/devicetree/bindings/fb/mdss-qpic.txt b/Documentation/devicetree/bindings/fb/mdss-qpic.txt new file mode 100644 index 0000000000000000000000000000000000000000..16d5b3547bdc28725ca1f04922f27a2a530c939c --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-qpic.txt @@ -0,0 +1,49 @@ +Qualcomm Technolgies, Inc. mdss-qpic + +mdss-qpic is a qpic controller device which supports dma transmission to MIPI +and LCDC panel. + +Required properties: +- compatible: must be "qcom,mdss_qpic" +- reg: offset and length of the register set for the device. +- reg-names : names to refer to register sets related to this device +- interrupts: IRQ line +- vdd-supply: Phandle for vdd regulator device node. +- avdd-supply: Phandle for avdd regulator device node. +- qcom,cs-gpio: Phandle for cs gpio device node. +- qcom,te-gpio: Phandle for te gpio device node. +- qcom,rst-gpio: Phandle for rst gpio device node. +- qcom,ad8-gpio: Phandle for ad8 gpio device node. +- qcom,bl-gpio: Phandle for backlight gpio device node. + +Optional properties: +- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for +below Bus Scaling properties: + - qcom,msm-bus,name + - qcom,msm-bus,num-cases + - qcom,msm-bus,num-paths + - qcom,msm-bus,vectors-KBps + +Example: + qcom,msm_qpic@f9ac0000 { + compatible = "qcom,mdss_qpic"; + reg = <0xf9ac0000 0x24000>; + reg-names = "qpic_base"; + interrupts = <0 251 0>; + + qcom,msm-bus,name = "mdss_qpic"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + + qcom,msm-bus,vectors-KBps = + <91 512 0 0>, + <91 512 400000 800000>; + + vdd-supply = <&pm8019_l11>; + avdd-supply = <&pm8019_l14>; + qcom,cs-gpio = <&msmgpio 21 0>; + qcom,te-gpio = <&msmgpio 22 0>; + qcom,rst-gpio = <&msmgpio 23 0>; + qcom,ad8-gpio = <&msmgpio 20 0>; + qcom,bl-gpio = <&msmgpio 84 0>; + }; diff --git a/Documentation/devicetree/bindings/fb/mdss-rotator.txt b/Documentation/devicetree/bindings/fb/mdss-rotator.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e077ac23819adc6804913465beb5b17ee4ec53f --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-rotator.txt @@ -0,0 +1,78 @@ +QTI MDSS Rotator + +MDSS rotator is a rotator driver, which manages the rotator hw +block inside the Mobile Display Subsystem. + +Required properties +- compatible : Must be "qcom,mdss-rotator". +- qcom,mdss-wb-count: The number of writeback block + in the hardware +- -supply: Phandle for regulator device node. + +Bus Scaling Data: +- qcom,msm-bus,name: String property describing MDSS client. +- qcom,msm-bus,num-cases: This is the the number of Bus Scaling use cases + defined in the vectors property. This must be + set to <3> for MDSS driver where use-case 0 is + used to take off MDSS BW votes from the system. + And use-case 1 & 2 are used in ping-pong fashion + to generate run-time BW requests. +- qcom,msm-bus,num-paths: This represents the number of paths in each + Bus Scaling Usecase. This value depends on + how many number of AXI master ports are + dedicated to MDSS for particular chipset. +- qcom,msm-bus,vectors-KBps: * A series of 4 cell properties, with a format + of (src, dst, ab, ib) which is defined at + Documentation/devicetree/bindings/arm/msm/msm_bus.txt + * Current values of src & dst are defined at + include/linux/msm-bus-board.h + src values allowed for MDSS are: + 22 = MSM_BUS_MASTER_MDP_PORT0 + 23 = MSM_BUS_MASTER_MDP_PORT1 + 25 = MSM_BUS_MASTER_ROTATOR + dst values allowed for MDSS are: + 512 = MSM_BUS_SLAVE_EBI_CH0 + ab: Represents aggregated bandwidth. + ib: Represents instantaneous bandwidth. + * Total number of 4 cell properties will be + (number of use-cases * number of paths). + * These values will be overridden by the driver + based on the run-time requirements. So initial + ab and ib values defined here are random and + bare no logic except for the use-case 0 where ab + and ib values needs to be 0. + * Define realtime vector properties followed by + non-realtime vector properties. + +Optional properties +- qcom,mdss-has-reg-bus: Boolean property to indicate + if rotator needs to vote for register bus. This + property is needed starting 8996 +- qcom,mdss-has-ubwc: Boolean property to indicate + if the hw supports universal + bandwidth compression (ubwc) +- qcom,mdss-has-downscale Boolean property to indicate + if the hw supports downscale + +Example: + mdss_rotator: qcom,mdss_rotator { + compatible = "qcom,mdss_rotator"; + qcom,mdss-has-downscale; + qcom,mdss-has-ubwc; + qcom,mdss-wb-count = <2>; + + qcom,mdss-has-reg-bus; + /* Bus Scale Settings */ + qcom,msm-bus,name = "mdss_rotator"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,mdss-num-nrt-paths = <1>; + qcom,msm-bus,vectors-KBps = + <25 512 0 0>, + <25 512 0 6400000>, + <25 512 0 6400000>; + + vdd-supply = <&gdsc_mdss>; + gdsc-mmagic-mdss-supply = <&gdsc_mmagic_mdss>; + qcom,supply-names = "vdd", "gdsc-mmagic-mdss"; + }; diff --git a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt new file mode 100644 index 0000000000000000000000000000000000000000..285a14f7ff6918ecafec26df5a737f85a2a38f2f --- /dev/null +++ b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt @@ -0,0 +1,116 @@ +* Qualcomm Technologies, Inc. HDMI Tx + +Required properties: +- cell-index: hdmi tx controller index +- compatible: must be "qcom,hdmi-tx" +- reg: offset and length of the register regions(s) for the device. +- reg-names: a list of strings that map in order to the list of regs. + +- hpd-gdsc-supply: phandle to the mdss gdsc regulator device tree node. +- hpd-5v-supply: phandle to the 5V regulator device tree node. +- core-vdda-supply: phandle to the HDMI vdda regulator device tree node. +- core-vcc-supply: phandle to the HDMI vcc regulator device tree node. +- qcom,supply-names: a list of strings that map in order + to the list of supplies. +- qcom,min-voltage-level: specifies minimum voltage (uV) level + of supply(ies) mentioned above. +- qcom,max-voltage-level: specifies maximum voltage (uV) level + of supply(ies) mentioned above. +- qcom,enable-load: specifies the current (uA) that will be + drawn from the enabled supply(ies) mentioned above. +- qcom,disable-load: specifies the current (uA) that will be + drawn from the disabled supply(ies) mentioned above. + +- qcom,hdmi-tx-cec: gpio for Consumer Electronics Control (cec) line. +- qcom,hdmi-tx-ddc-clk: gpio for Display Data Channel (ddc) clock line. +- qcom,hdmi-tx-ddc-data: gpio for ddc data line. + +Optional properties: +- hpd-5v-en-supply: phandle to the 5V boost enable regulator device tree node. +- qcom,hdmi-tx-mux-sel: gpio required to toggle HDMI output between + docking station, type A, and liquid device, type D, ports. Required + property for liquid devices. +- qcom,hdmi-tx-ddc-mux-sel: gpio for ddc mux select. +- qcom,hdmi-tx-mux-en: gpio required to enable mux for HDMI output + on liquid devices. Required property for liquid devices. +- qcom,hdmi-tx-mux-lpm: gpio required for hdmi mux configuration + selection on liquid devices. Required property for liquid devices. +- qcom,conditional-power-on: Enables HPD conditionally on MTP targets. + Required property for MTP devices which are reworked to expose HDMI port. +- qcom,hdmi-tx-hpd: gpio required for HDMI hot-plug detect. Required on + platforms where companion chip is not used. +- pinctrl-names: a list of strings that map to the pinctrl states. +- pinctrl-0: list of phandles, each pointing at a pin configuration node. +... +- pinctrl-n: list of phandles, each pointing at a pin configuration node. +- qcom,conti-splash-enabled: Enables the hdmi continuous splash screen feature. + HDMI interface will remain powered on from LK to kernel with continuous + display of bootup logo. +- qcom,pluggable: boolean to enable hotplug feature. +- qcom,display-id: A string indicates the display ID for the controller. + The possible values are: + - "primary" + - "secondary" + - "tertiary" + +[Optional child nodes]: These nodes are for devices which are +dependent on HDMI Tx controller. If HDMI Tx controller is disabled then +these devices will be disabled as well. Ex. HDMI Audio Codec device. + +- qcom,msm-hdmi-audio-rx: Node for HDMI audio codec. +Required properties: +- compatible : "msm-hdmi-audio-codec-rx"; + +Example: + mdss_hdmi_tx: qcom,hdmi_tx@fd922100 { + cell-index = <0>; + compatible = "qcom,hdmi-tx"; + reg = <0xfd922100 0x35C>, + <0xfd922500 0x7C>, + <0xfc4b8000 0x60F0>, + <0xfe2a0000 0xFFF>; + reg-names = "core_physical", "phy_physical", "qfprom_physical", + "hdcp_physical"; + + hpd-gdsc-supply = <&gdsc_mdss>; + hpd-5v-supply = <&pm8941_mvs2>; + hpd-5v-en-supply = <&hdmi_vreg>; + core-vdda-supply = <&pm8941_l12>; + core-vcc-supply = <&pm8941_s3>; + qcom,supply-names = "hpd-gdsc", "hpd-5v", "hpd-5v-en", "core-vdda", "core-vcc"; + qcom,min-voltage-level = <0 0 0 1800000 1800000>; + qcom,max-voltage-level = <0 0 0 1800000 1800000>; + qcom,enable-load = <0 0 0 1800000 0>; + qcom,disable-load = <0 0 0 0 0>; + + qcom,hdmi-tx-ddc-mux-sel = <&pma8084_gpios 6 0>; + qcom,hdmi-tx-cec = <&msmgpio 31 0>; + qcom,hdmi-tx-ddc-clk = <&msmgpio 32 0>; + qcom,hdmi-tx-ddc-data = <&msmgpio 33 0>; + qcom,hdmi-tx-hpd = <&msmgpio 34 0>; + + qcom,hdmi-tx-mux-lpm = <&msmgpio 27 0>; + qcom,hdmi-tx-mux-en = <&msmgpio 83 0>; + qcom,hdmi-tx-mux-sel = <&msmgpio 85 0>; + + qcom,conditional-power-on; + qcom,pluggable; + qcom,display-id = "secondary"; + + qcom,msm-hdmi-audio-rx { + compatible = "qcom,msm-hdmi-audio-codec-rx"; + }; + pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active", + "hdmi_cec_active", "hdmi_active", + "hdmi_sleep"; + pinctrl-0 = <&mdss_hdmi_hpd_active &mdss_hdmi_ddc_suspend + &mdss_hdmi_cec_suspend>; + pinctrl-1 = <&mdss_hdmi_hpd_active &mdss_hdmi_ddc_active + &mdss_hdmi_cec_suspend>; + pinctrl-2 = <&mdss_hdmi_hpd_active &mdss_hdmi_cec_active + &mdss_hdmi_ddc_suspend>; + pinctrl-3 = <&mdss_hdmi_hpd_active &mdss_hdmi_ddc_active + &mdss_hdmi_cec_active>; + pinctrl-4 = <&mdss_hdmi_hpd_suspend &mdss_hdmi_ddc_suspend + &mdss_hdmi_cec_suspend>; + }; diff --git a/Documentation/devicetree/bindings/fb/mxsfb.txt b/Documentation/devicetree/bindings/fb/mxsfb.txt new file mode 100644 index 0000000000000000000000000000000000000000..96ec5179c8a00199e2056ee574c3c55aef37bae3 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mxsfb.txt @@ -0,0 +1,49 @@ +* Freescale MXS LCD Interface (LCDIF) + +Required properties: +- compatible: Should be "fsl,-lcdif". Supported chips include + imx23 and imx28. +- reg: Address and length of the register set for lcdif +- interrupts: Should contain lcdif interrupts +- display : phandle to display node (see below for details) + +* display node + +Required properties: +- bits-per-pixel : <16> for RGB565, <32> for RGB888/666. +- bus-width : number of data lines. Could be <8>, <16>, <18> or <24>. + +Required sub-node: +- display-timings : Refer to binding doc display-timing.txt for details. + +Examples: + +lcdif@80030000 { + compatible = "fsl,imx28-lcdif"; + reg = <0x80030000 2000>; + interrupts = <38 86>; + + display: display { + bits-per-pixel = <32>; + bus-width = <24>; + + display-timings { + native-mode = <&timing0>; + timing0: timing0 { + clock-frequency = <33500000>; + hactive = <800>; + vactive = <480>; + hfront-porch = <164>; + hback-porch = <89>; + hsync-len = <10>; + vback-porch = <23>; + vfront-porch = <10>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/fb/sm501fb.txt b/Documentation/devicetree/bindings/fb/sm501fb.txt new file mode 100644 index 0000000000000000000000000000000000000000..9d9f0098092b927b48dabbf198ee252e91d08a58 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/sm501fb.txt @@ -0,0 +1,34 @@ +* SM SM501 + +The SM SM501 is a LCD controller, with proper hardware, it can also +drive DVI monitors. + +Required properties: +- compatible : should be "smi,sm501". +- reg : contain two entries: + - First entry: System Configuration register + - Second entry: IO space (Display Controller register) +- interrupts : SMI interrupt to the cpu should be described here. +- interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + +Optional properties: +- mode : select a video mode: + x[-][@] +- edid : verbatim EDID data block describing attached display. + Data from the detailed timing descriptor will be used to + program the display controller. +- little-endian: available on big endian systems, to + set different foreign endian. +- big-endian: available on little endian systems, to + set different foreign endian. + +Example for MPC5200: + display@1,0 { + compatible = "smi,sm501"; + reg = <1 0x00000000 0x00800000 + 1 0x03e00000 0x00200000>; + interrupts = <1 1 3>; + mode = "640x480-32@60"; + edid = [edid-data]; + }; diff --git a/Documentation/devicetree/bindings/gpu/adreno-pwrlevels.txt b/Documentation/devicetree/bindings/gpu/adreno-pwrlevels.txt index aece6ac44b784b4b38728a9d303c1a76b90dcf99..747e0b601da22d1b21d7f57be5e883ef4df9158e 100644 --- a/Documentation/devicetree/bindings/gpu/adreno-pwrlevels.txt +++ b/Documentation/devicetree/bindings/gpu/adreno-pwrlevels.txt @@ -17,6 +17,10 @@ Properties: - qcom,gpu-pwrlevel: A single powerlevel +- qcom,ca-target-pwrlevel: + This value indicates which qcom,gpu-pwrlevel + to jump on in case of context aware power level + jump. Properties: - reg: Index of the powerlevel (0 = highest perf) - qcom,gpu-freq GPU frequency for the powerlevel (in Hz) diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index 375e929ab36743e6488d3398e41b42d767ab09d3..7976a8728b15a2e1676d8cf31c0796456f1a4d6c 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -167,6 +167,15 @@ Optional Properties: Specify the size of snapshot in bytes. This will override snapshot size defined in the driver code. +- qcom,enable-ca-jump: + Boolean. Enables use of context aware DCVS +- qcom,ca-busy-penalty: + This property represents the time in microseconds required to + initiate context aware power level jump. +- qcom,ca-target-pwrlevel: + This value indicates which qcom,gpu-pwrlevel to jump on in case + of context aware power level jump. + - qcom,gpu-qdss-stm: baseAddr - base address of the gpu channels in the qdss stm memory region @@ -323,6 +332,15 @@ Example of A330 GPU in MSM8916: coresight-child-list = <&funnel_in0>; coresight-child-ports = <5>; + /* Enable context aware freq. scaling */ + qcom,enable-ca-jump; + + /* Context aware jump busy penalty in us */ + qcom,ca-busy-penalty = <12000>; + + /* Context aware jump target power level */ + qcom,ca-target-pwrlevel = <1>; + qcom,soc-hw-revisions { #address-cells = <1>; #size-cells = <0>; @@ -389,6 +407,7 @@ Example of A330 GPU in MSM8916: #size-cells = <0>; qcom,speed-bin = <0>; + qcom,ca-target-pwrlevel = <1>; qcom,gpu-pwrlevel@0 { reg = <0>; diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc-sdm640.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc-sm6150.txt similarity index 98% rename from Documentation/devicetree/bindings/interrupt-controller/qti,pdc-sdm640.txt rename to Documentation/devicetree/bindings/interrupt-controller/qti,pdc-sm6150.txt index 059e3ccbd35fa5239a816ef067cfffdac69523d9..4ee8ae29f7ea388ce6aa114bc63a3e6c2ca11a77 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc-sdm640.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc-sm6150.txt @@ -55,7 +55,7 @@ Properties: Example: pdcgic: interrupt-controller@0xb220000{ - compatible = "qcom,pdc-sdm640"; + compatible = "qcom,pdc-sm6150"; reg = <0xb220000 0x30000>; #interrupt-cells = <3>; interrupt-parent = <&intc>; diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt index a014dacda7fbddc96b3a0224c4a8f906fe7a8ab8..93bd945277953c99b9afaa9bda702e01b797e9ae 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt @@ -101,6 +101,15 @@ conditions. Any sid X for which X&~mask==sid will be programmed with the given actlr-setting. +- qcom,enable-static-cb : Enables option to use pre-defined static context bank + allocation programmed by TZ. Global register including SMR and + S2CR registers are configured by TZ before kernel comes up and + this programming is not altered throughout the life of system. + We would be reading through these registers at run time to + identify CB allocated for a particular sid. SID masking isn't + supported as we are directly comparing client SID with ID bits + of SMR registers. + - qcom,deferred-regulator-disable-delay : The time delay for deferred regulator disable in ms. In case of unmap call, regulator is enabled/disabled. This may introduce additional delay. For diff --git a/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt b/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt index 87d313462a891305a3f255e4661736870de2af3f..22921230900be890953270a14550ec6ca802779c 100644 --- a/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt +++ b/Documentation/devicetree/bindings/leds/backlight/qcom-spmi-wled.txt @@ -7,7 +7,10 @@ platforms. The PMIC is connected to the host processor via SPMI bus. - compatible Usage: required Value type: - Definition: should be "qcom,pmi8998-spmi-wled". + Definition: should be one of the below. + "qcom,pmi8998-spmi-wled", + "qcom,pm855l-spmi-wled", + "qcom,pm6150l-spmi-wled" - reg Usage: required diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-vibrator-ldo.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-vibrator-ldo.txt new file mode 100644 index 0000000000000000000000000000000000000000..2865019b3fccf2187f44b30174fdc1bd3086101d --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-vibrator-ldo.txt @@ -0,0 +1,50 @@ +Qualcomm Technologies, Inc. Vibrator-LDO + +QPNP (Qualcomm Technologies, Inc. Plug N Play) Vibrator-LDO is a peripheral +on some QTI PMICs. It can be interfaced with the host processor via SPMI. + +Vibrator-LDO peripheral supports Eccentric Rotation Mass (ERM) vibrator. + +Properties: + +- compatible + Usage: required + Value type: + Definition: "qcom,qpnp-vibrator-ldo". + +- reg + Usage: required + Value type: + Definition: Base address of vibrator-ldo peripheral. + +- qcom,vib-ldo-volt-uv + Usage: required + Value type: + Definition: The optimal voltage requirement of the vibrator motor for + a normal vibration. Value is specified in microvolts. + +- qcom,disable-overdrive + Usage: optional + Value type: + Definition: Do not apply overdrive voltage. + +- qcom,vib-overdrive-volt-uv + Usage: optional and not required if qcom,disable-overdrive present + Value type: + Definition: The voltage in microvolts used as overdrive factor for + improving motor reactivity at the start of vibration. + If this property not specified, a default value of + 2 times the value specified in qcom,vib-ldo-volt-uv + property is used. + +======= +Example +======= + +pmi632_vib: qcom,vibrator@5700 { + compatible = "qcom,qpnp-vibrator-ldo"; + reg = <0x5700 0x100>; + qcom,vib-ldo-volt-uv = <1504000>; + qcom,disable-overdrive; + qcom,vib-overdrive-volt-uv = <3544000>; +}; diff --git a/Documentation/devicetree/bindings/media/msm-npu-pwrlevels.txt b/Documentation/devicetree/bindings/media/msm-npu-pwrlevels.txt new file mode 100644 index 0000000000000000000000000000000000000000..9a8a014c95d822b4c6f2128ebda7078411b03ce9 --- /dev/null +++ b/Documentation/devicetree/bindings/media/msm-npu-pwrlevels.txt @@ -0,0 +1,164 @@ +Qualcomm Technologies, Inc. NPU powerlevels + +Powerlevels are defined in sets by qcom,npu-pwrlevels. Each powerlevel defines +a series of clock frequencies. These frequencies are for the corresponding +clocks in the clocks property of the msm_npu device. + +qcom,npu-pwrlevels bindings: + +Required Properties: +- #address-cells: Should be set to 1 +- #size-cells: Should be set to 0 +- compatible: Must be qcom,npu-pwrlevels +- initial-pwrlevel: NPU initial wakeup power level, this is the index of the + child node. + +qcom,npu-pwrlevel: This is a child node defining power levels. +qcom,npu-pwrlevels must contain at least one power level node. Each child node +has the following properties: + +Required Properties: +- reg: Index of the powerlevel (0 = lowest performance) +- clk-freq: List of clock frequencies (in Hz) of each clock for the current + powerlevel. List of clocks and order described in: + Documentation/devicetree/bindings/media/msm-npu.txt + +Example: + qcom,npu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + compatible = "qcom,npu-pwrlevels"; + initial-pwrlevel = <4>; + qcom,npu-pwrlevel@0 { + reg = <0>; + clk-freq = <9600000 + 9600000 + 19200000 + 19200000 + 19200000 + 19200000 + 9600000 + 60000000 + 19200000 + 19200000 + 30000000 + 19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 9600000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@1 { + reg = <1>; + clk-freq = <300000000 + 300000000 + 19200000 + 100000000 + 19200000 + 19200000 + 300000000 + 150000000 + 19200000 + 19200000 + 60000000 + 100000000 + 100000000 + 37500000 + 100000000 + 19200000 + 300000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@2 { + reg = <2>; + clk-freq = <350000000 + 350000000 + 19200000 + 150000000 + 19200000 + 19200000 + 350000000 + 200000000 + 37500000 + 19200000 + 120000000 + 150000000 + 150000000 + 75000000 + 150000000 + 19200000 + 350000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@3 { + reg = <3>; + clk-freq = <400000000 + 400000000 + 19200000 + 200000000 + 19200000 + 19200000 + 400000000 + 300000000 + 37500000 + 19200000 + 120000000 + 200000000 + 200000000 + 75000000 + 200000000 + 19200000 + 400000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@4 { + reg = <4>; + clk-freq = <600000000 + 600000000 + 19200000 + 300000000 + 19200000 + 19200000 + 600000000 + 403000000 + 75000000 + 19200000 + 240000000 + 300000000 + 300000000 + 150000000 + 300000000 + 19200000 + 600000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@5 { + reg = <5>; + clk-freq = <715000000 + 715000000 + 19200000 + 350000000 + 19200000 + 19200000 + 715000000 + 533000000 + 75000000 + 19200000 + 240000000 + 350000000 + 350000000 + 150000000 + 350000000 + 19200000 + 715000000 + 19200000 + 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/msm-npu.txt b/Documentation/devicetree/bindings/media/msm-npu.txt index 77a276da306a5d05d5ba85abcd070948eed33698..80e6db79c214b3d33fae0d181a7bee1ffe6331b9 100644 --- a/Documentation/devicetree/bindings/media/msm-npu.txt +++ b/Documentation/devicetree/bindings/media/msm-npu.txt @@ -3,41 +3,226 @@ NPU (Neural Network Processing Unit) applies neural network processing Required properties: -- compatible: - - "qcom,msm-npu" +- compatible: Must be "qcom,msm-npu" - reg: Specify offset and length of the device register sets. - reg-names: Names corresponding to the defined register sets. - - "npu_base": npu base registers + - "npu_base": npu base registers - interrupts: Specify the npu interrupts. -- interrupt-names : should specify relevant names to each interrupts - property defined. -- clocks : clocks required for the device. -- clock-names : names of clocks required for the device. -- vdd-supply : Phandle for vdd regulator device node +- interrupt-names: should specify relevant names to each interrupts + property defined. +- cache-slice-names: A set of names that identify the usecase names of a + client that uses cache slice. These strings are used to look up the + cache slice entries by name +- cache-slices: The tuple has phandle to llcc device as the first argument + and the second argument is the usecase id of the client +- clocks: clocks required for the device. +- clock-names: names of clocks required for the device. +- vdd-supply: Phandle for vdd regulator device node - vdd_'reg'-supply: Reference to the regulator that supplies the corresponding - 'reg' domain, e.g. vdd_cx-supply. + 'reg' domain, e.g. vdd_cx-supply. - qcom,proxy-reg-names: Names of the regulators that need to be turned on/off - during proxy voting/unvoting. + during proxy voting/unvoting. - qcom,vdd_'reg'-uV-uA: Voltage and current values for the 'reg' regulator, - e.g. qcom,vdd_cx-uV-uA. - + e.g. qcom,vdd_cx-uV-uA. +- mboxes: Phandle array for mailbox controllers to be used for IPC +- mbox-names: names of each mailboxes +- #cooling-cells: Should be set to 2 +- qcom,npubw-dev: a phandle to a device representing bus bandwidth requirements + (see devbw.txt) +- qcom,npu-pwrlevels: Container for NPU power levels + (see msm-npu-pwrlevels.txt) Example: - msm_npu: qcom,msm_npu { + msm_npu: qcom,msm_npu@9800000 { compatible = "qcom,msm-npu"; + status = "ok"; reg = <0x9800000 0x800000>; reg-names = "npu_base"; - interrupts = <0 346 0>; - interrupt-names = "single"; - clocks = <&clock_npucc NPU_CC_XO_CLK>, - <&clock_npucc NPU_CC_NPU_CORE_CLK>, - <&clock_npucc NPU_CC_CAL_DP_CLK>, - <&clock_npucc NPU_CC_ARMWIC_CORE_CLK>, - <&clock_npucc NPU_CC_COMP_NOC_AXI_CLK>, - <&clock_npucc NPU_CC_CONF_NOC_AHB_CLK>; - clock-names = "xo", "core", "cal_dp", "armwic", - "axi", "ahb"; + interrupts = ; + iommus = <&apps_smmu 0x1461 0x0>, <&apps_smmu 0x2061 0x0>; + cache-slice-names = "npu"; + cache-slices = <&llcc 23>; + clocks = <&clock_npucc NPU_CC_CAL_DP_CLK>, + <&clock_npucc NPU_CC_CAL_DP_CLK_SRC>, + <&clock_npucc NPU_CC_XO_CLK>, + <&clock_npucc NPU_CC_ARMWIC_CORE_CLK>, + <&clock_npucc NPU_CC_BTO_CORE_CLK>, + <&clock_npucc NPU_CC_BWMON_CLK>, + <&clock_npucc NPU_CC_CAL_DP_CDC_CLK>, + <&clock_npucc NPU_CC_COMP_NOC_AXI_CLK>, + <&clock_npucc NPU_CC_CONF_NOC_AHB_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_APB_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_ATB_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_CLK_SRC>, + <&clock_npucc NPU_CC_NPU_CORE_CTI_CLK>, + <&clock_npucc NPU_CC_NPU_CPC_CLK>, + <&clock_npucc NPU_CC_NPU_CPC_TIMER_CLK>, + <&clock_npucc NPU_CC_PERF_CNT_CLK>, + <&clock_npucc NPU_CC_QTIMER_CORE_CLK>, + <&clock_npucc NPU_CC_SLEEP_CLK>; + clock-names = "cal_dp_clk", + "cal_dp_clk_src", + "xo_clk", + "armwic_core_clk", + "bto_core_clk", + "bwmon_clk", + "cal_dp_cdc_clk", + "comp_noc_axi_clk", + "conf_noc_ahb_clk", + "npu_core_apb_clk", + "npu_core_atb_clk", + "npu_core_clk", + "npu_core_clk_src", + "npu_core_cti_clk", + "npu_cpc_clk", + "npu_cpc_timer_clk", + "perf_cnt_clk", + "qtimer_core_clk", + "sleep_clk"; vdd-supply = <&npu_core_gdsc>; vdd_cx-supply = <&pm855l_s6_level>; qcom,proxy-reg-names ="vdd", "vdd_cx"; qcom,vdd_cx-uV-uA = ; + mboxes = <&qmp_npu0 0>, <&qmp_npu1 0>; + mbox-names = "npu_low", "npu_high"; + #cooling-cells = <2>; + qcom,npubw-dev = <&npu_npu_ddr_bw>; + qcom,npu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + compatible = "qcom,npu-pwrlevels"; + initial-pwrlevel = <4>; + qcom,npu-pwrlevel@0 { + reg = <0>; + clk-freq = <9600000 + 9600000 + 19200000 + 19200000 + 19200000 + 19200000 + 9600000 + 60000000 + 19200000 + 19200000 + 30000000 + 19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 9600000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@1 { + reg = <1>; + clk-freq = <300000000 + 300000000 + 19200000 + 100000000 + 19200000 + 19200000 + 300000000 + 150000000 + 19200000 + 19200000 + 60000000 + 100000000 + 100000000 + 37500000 + 100000000 + 19200000 + 300000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@2 { + reg = <2>; + clk-freq = <350000000 + 350000000 + 19200000 + 150000000 + 19200000 + 19200000 + 350000000 + 200000000 + 37500000 + 19200000 + 120000000 + 150000000 + 150000000 + 75000000 + 150000000 + 19200000 + 350000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@3 { + reg = <3>; + clk-freq = <400000000 + 400000000 + 19200000 + 200000000 + 19200000 + 19200000 + 400000000 + 300000000 + 37500000 + 19200000 + 120000000 + 200000000 + 200000000 + 75000000 + 200000000 + 19200000 + 400000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@4 { + reg = <4>; + clk-freq = <600000000 + 600000000 + 19200000 + 300000000 + 19200000 + 19200000 + 600000000 + 403000000 + 75000000 + 19200000 + 240000000 + 300000000 + 300000000 + 150000000 + 300000000 + 19200000 + 600000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@5 { + reg = <5>; + clk-freq = <715000000 + 715000000 + 19200000 + 350000000 + 19200000 + 19200000 + 715000000 + 533000000 + 75000000 + 19200000 + 240000000 + 350000000 + 350000000 + 150000000 + 350000000 + 19200000 + 715000000 + 19200000 + 0>; + }; + }; }; diff --git a/Documentation/devicetree/bindings/misc/qpnp-misc.txt b/Documentation/devicetree/bindings/misc/qpnp-misc.txt new file mode 100644 index 0000000000000000000000000000000000000000..a34cbde456e4d96ecb09fb92aab0db4bb3b32a23 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/qpnp-misc.txt @@ -0,0 +1,25 @@ +QPNP-MISC + +QPNP-MISC provides a way to read the PMIC part number and revision. + +Required properties: +- compatible : should be "qcom,qpnp-misc" +- reg : offset and length of the PMIC peripheral register map. + +Optional properties: +- qcom,pwm-sel: Select PWM source. Possible values: + 0: LOW + 1: PWM1_in + 2: PWM2_in + 3: PWM1_in & PWM2_in +- qcom,enable-gp-driver: Enable the GP driver. Should only be specified + if a non-zero PWM source is specified under + "qcom,pwm-sel" property. + +Example: + qcom,misc@900 { + compatible = "qcom,qpnp-misc"; + reg = <0x900 0x100>; + qcom,pwm-sel = <2>; + qcom,enable-gp-driver; + }; diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt index e4d8c1487ad1419b3374d94ab0567ee5b4c58908..a764fcdff19a890757d7c59c53a5a1ce844984e6 100644 --- a/Documentation/devicetree/bindings/pci/msm_pcie.txt +++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt @@ -74,6 +74,8 @@ Optional Properties: - qcom,max-link-speed: Max Gen speed Root complex supports. - qcom,n-fts: The number of fast training sequences sent when the link state is changed from L0s to L0. + - qcom,phy-power-down-offset: Offset from PCIe PHY base to control the power state + of the PHY. - qcom,pcie-phy-ver: version of PCIe PHY. - qcom,phy-sequence: The initialization sequence to bring up the PCIe PHY. Should be specified in groups (offset, value, delay). @@ -274,6 +276,7 @@ Example: qcom,wr-halt-size = <0xa>; /* 1KB */ qcom,slv-addr-space-size = <0x1000000>; /* 16MB */ qcom,phy-status-offset = <0x800>; + qcom,phy-power-down-offset = <0x840>; qcom,cpl-timeout = <0x2>; iommus = <&anoc0_smmu>; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdm640-pinctrl b/Documentation/devicetree/bindings/pinctrl/qcom,sm6150-pinctrl similarity index 97% rename from Documentation/devicetree/bindings/pinctrl/qcom,sdm640-pinctrl rename to Documentation/devicetree/bindings/pinctrl/qcom,sm6150-pinctrl index f585a341a455d8a51696795bac765cfc33aa65a2..9787c416f8d903bb61e832dd474d95bc06d54865 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,sdm640-pinctrl +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm6150-pinctrl @@ -1,12 +1,12 @@ -Qualcomm Technologies, Inc. SDM640 TLMM block +Qualcomm Technologies, Inc. SM6150 TLMM block This binding describes the Top Level Mode Multiplexer block found in the -SDM640 platform. +SM6150 platform. - compatible: Usage: required Value type: - Definition: must be "qcom,sdm640-pinctrl" + Definition: must be "qcom,sm6150-pinctrl" - reg: Usage: required @@ -176,7 +176,7 @@ to specify in a pin configuration subnode: Example: tlmm: pinctrl@03000000 { - compatible = "qcom,sdm640-pinctrl"; + compatible = "qcom,sm6150-pinctrl"; reg = <0x03000000 0xdc2000>; interrupts = <0 208 0>; gpio-controller; diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt index d272b7f3426a1063e354ec50fd6d212cd7deae3d..15611971426db68acc56224d077f0f1e6304f83d 100644 --- a/Documentation/devicetree/bindings/platform/msm/ipa.txt +++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt @@ -37,6 +37,8 @@ Optional: compatible "qcom,ipa-smmu-uc-cb" - qcom,use-a2-service: determine if A2 service will be used - qcom,use-ipa-tethering-bridge: determine if tethering bridge will be used +- qcom,use-ipa-in-mhi-mode: Boolean context flag to indicate whether + device booting in MHI config or not. - qcom,use-ipa-bamdma-a2-bridge: determine if a2/ipa hw bridge will be used - qcom,ee: which EE is assigned to (non-secure) APPS from IPA-BAM POV. This is a number @@ -128,6 +130,8 @@ IPA SMMU sub nodes - qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass. +- dma-coherent: Indicate using dma-coherent or not in SMMU block + - iommus : the phandle and stream IDs for the SMMU used by this root - qcom,iova-mapping: specifies the start address and size of iova space. @@ -137,14 +141,12 @@ IPA SMMU sub nodes IPA SMP2P sub nodes --compatible: "qcom,smp2pgpio-map-ipa-1-out" - represents the out gpio from +-compatible: "qcom,smp2p-map-ipa-1-out" - represents the out smp2p from ipa driver to modem. --compatible: "qcom,smp2pgpio-map-ipa-1-in" - represents the in gpio to +-compatible: "qcom,smp2p-map-ipa-1-in" - represents the in smp2p to ipa driver from modem. --gpios: Binding to the gpio defined in XXX-smp2p.dtsi - Example: @@ -197,15 +199,13 @@ qcom,ipa@fd4c0000 { qcom,descriptor-fifo-size = <0x300>; }; - /* smp2p gpio information */ - qcom,smp2pgpio_map_ipa_1_out { - compatible = "qcom,smp2pgpio-map-ipa-1-out"; - gpios = <&smp2pgpio_ipa_1_out 0 0>; + /* smp2p information */ + qcom,smp2p_map_ipa_1_out { + compatible = "qcom,smp2p-map-ipa-1-out"; }; - qcom,smp2pgpio_map_ipa_1_in { - compatible = "qcom,smp2pgpio-map-ipa-1-in"; - gpios = <&smp2pgpio_ipa_1_in 0 0>; + qcom,smp2p_map_ipa_1_in { + compatible = "qcom,smp2p-map-ipa-1-in"; }; ipa_smmu_ap: ipa_smmu_ap { diff --git a/Documentation/devicetree/bindings/platform/msm/ipa_mhi_proxy.txt b/Documentation/devicetree/bindings/platform/msm/ipa_mhi_proxy.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3483d81572c3052d86228259a8ca44a18928463 --- /dev/null +++ b/Documentation/devicetree/bindings/platform/msm/ipa_mhi_proxy.txt @@ -0,0 +1,27 @@ +* Qualcomm Technologies, Inc. IPA MHI proxy driver module + +This module enables modem to modem communication using IPA +and MHI. + +Required properties: +- compatible: Must be "qcom,ipa-mhi-proxy" +- qcom,mhi-chdb-base: MHI channel doorbell base address in MMIO space +- qcom,mhi-erdb-base: MHI event doorbell base address in MMIO space + +Optional: +- qcom,ctrl-iova: Pair of start address and size of the IOVA space + dedicated for MHI control structures + (such as transfer rings and event rings). + If not present, SMMU S1 is considered to be in bypass mode. +- qcom,data-iova: Pair of start address and size of the IOVA space + dedicated for MHI data buffers. + If not present, SMMU S1 is considered to be in bypass mode. + +Example: + imp: qcom,ipa-mhi-proxy { + compatible = "qcom,ipa-mhi-proxy"; + qcom,ctrl-iova = <0x00010000 0x0FFF0000>; + qcom,data-iova = <0x10000000 0x0FFFFFFF>; + qcom,mhi-chdb-base = <0x40300300>; + qcom,mhi-erdb-base = <0x40300700>; + }; diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt index 975d0ecb85c993c3142d32be5b2f17d2ac48ab18..18568d3863ebc2dddb150a28b59041f21df03e8a 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen4.txt @@ -158,6 +158,55 @@ First Level Node - FG Gen4 device loaded earlier by bootloader doesn't match with the profile available in the device tree. +- qcom,cl-start-capacity + Usage: optional + Value type: + Definition: Battery SOC threshold to start the capacity learning. + If this is not specified, then the default value used + will be 15. Unit is in percentage. + +- qcom,cl-min-temp + Usage: optional + Value type: + Definition: Lower limit of battery temperature to start the capacity + learning. If this is not specified, then the default value + used will be 150 (15 C). Unit is in decidegC. + +- qcom,cl-max-temp + Usage: optional + Value type: + Definition: Upper limit of battery temperature to start the capacity + learning. If this is not specified, then the default value + used will be 500 (50 C). Unit is in decidegC. + +- qcom,cl-max-increment + Usage: optional + Value type: + Definition: Maximum capacity increment allowed per capacity learning + cycle. If this is not specified, then the default value + used will be 5 (0.5%). Unit is in decipercentage. + +- qcom,cl-max-decrement + Usage: optional + Value type: + Definition: Maximum capacity decrement allowed per capacity learning + cycle. If this is not specified, then the default value + used will be 100 (10%). Unit is in decipercentage. + +- qcom,cl-min-limit + Usage: optional + Value type: + Definition: Minimum limit that the capacity cannot go below in a + capacity learning cycle. If this is not specified, then + the default value is 0. Unit is in decipercentage. + +- qcom,cl-max-limit + Usage: optional + Value type: + Definition: Maximum limit that the capacity cannot go above in a + capacity learning cycle. If this is not specified, then + the default value is 0. Unit is in decipercentage. + - qcom,hold-soc-while-full Usage: optional Value type: diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt index b7e6a31bf6c31c94fdf686416ff32516b57c7f74..e7986327979274cf24a0c13acfed240ba6567133 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt @@ -27,6 +27,13 @@ Charger specific properties: Definition: Should specify the phandle of PMI's revid module. This is used to identify the PMI subtype. +- io-channels +- io-channel-names + Usage: optional + Value type: + Definition: For details about IIO bindings see: + Documentation/devicetree/bindings/iio/iio-bindings.txt + - qcom,batteryless-platform Usage: optional Value type: @@ -167,10 +174,7 @@ Charger specific properties: Usage: optional Value type: Definition: Specifies the phandle of the node which contains the battery - profiles supported on the device. This is only specified - when step charging and sw-jeita configurations are desired - to be get from these properties defined in battery profile: - qcom,step-chg-ranges, qcom,jeita-fcc-ranges, qcom,jeita-fv-ranges. + profiles supported on the device. - qcom,flash-derating-soc Usage: optional @@ -223,13 +227,22 @@ Peripheral specific properties: Example ======= -pmi8998_charger: qcom,qpnp-smb5 { +pm855b_charger: qcom,qpnp-smb5 { compatible = "qcom,qpnp-smb5"; #address-cells = <1>; #size-cells = <1>; + qcom,pmic-revid = <&pm855b_revid>; + dpdm-supply = <&qusb_phy0>; + io-channels = <&pm855b_vadc ADC_USB_IN_V_16>, + <&pm855b_vadc ADC_USB_IN_I>, + <&pm855b_vadc ADC_CHG_TEMP>; + io-channel-names = "usb_in_voltage", + "usb_in_current", + "chg_temp"; + qcom,chgr@1000 { reg = <0x1000 0x100>; interrupts = <0x2 0x10 0x0 IRQ_TYPE_NONE>, diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb1390-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb1390-charger.txt new file mode 100644 index 0000000000000000000000000000000000000000..f40a7158c83c75460659d22e07e7cc8e415acaf5 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/qcom/smb1390-charger.txt @@ -0,0 +1,76 @@ +Qualcomm Technologies, Inc. SMB1390 Charger Specific Bindings + +SMB1390 charge pump is paired with QTI family of standalone chargers to +enable a high current, high efficiency Li+ battery charging system. + +======================= +Required Node Structure +======================= + +SMB1390 Charger must be described in two levels of device nodes. + +================================== +First Level Node - SMB1390 Charger +================================== + +Charger specific properties: +- compatible + Usage: required + Value type: + Definition: "qcom,smb1390-charger". + +- qcom,pmic-revid + Usage: required + Value type: phandle + Definition: Should specify the phandle of SMB's revid module. This is used + to identify the SMB subtype. + +- io-channels +- io-channel-names + Usage: required + Value type: + Definition: For details about IIO bindings see: + Documentation/devicetree/bindings/iio/iio-bindings.txt + +================================================ +Second Level Nodes - SMB1390 Charger Peripherals +================================================ + +Peripheral specific properties: +- interrupts + Usage: required + Value type: + Definition: Peripheral interrupt specifier. + +- interrupt-names + Usage: required + Value type: + Definition: Interrupt names. This list must match up 1-to-1 with the + interrupts specified in the 'interrupts' property. + +======= +Example +======= + +smb1390_charger: qcom,charge_pump { + compatible = "qcom,smb1390-charger"; + qcom,pmic-revid = <&smb1390_revid>; + interrupt-parent = <&smb1390>; + status = "disabled"; + + io-channels = <&pm855b_vadc ADC_AMUX_THM2>; + io-channel-names = "cp_die_temp"; + + qcom,core { + interrupts = <0x10 0x0 IRQ_TYPE_EDGE_RISING>, + <0x10 0x1 IRQ_TYPE_EDGE_RISING>, + <0x10 0x2 IRQ_TYPE_EDGE_RISING>, + <0x10 0x3 IRQ_TYPE_EDGE_RISING>, + <0x10 0x4 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "switcher-off-window", + "switcher-off-fault", + "vph-ov-soft", + "ilim", + "temp-alarm"; + }; +}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt index 3174ccb0fd2d0ce26e4d729e86b130a53b10114f..ddd90e134a1664c58822120fe3f74a50ec3b2534 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt @@ -11,26 +11,137 @@ device module in Qualcomm Technologies, Inc. PMIC chips. - reg: Usage: required Value type: - Definition: Register base and length for LPG modules. The length - varies based on the number of channels available in - the PMIC chips. + Definition: Register base and length for LPG and LUT modules. LPG size + or length available per channel varies depending on the + number of channels in PMIC. - reg-names: Usage: required Value type: Definition: The name of the register defined in the reg property. - It must be "lpg-base". + It must have "lpg-base", "lut-base" is optional but + it's required if any LPG channels support LUT mode. - #pwm-cells: Usage: required Value type: - Definition: See Documentation/devicetree/bindings/pwm/pwm.txt; + Definition: The number of cells in "pwms" property specified in + PWM user nodes. It should be 2. The first cell is + the PWM channel ID indexed from 0, and the second + cell is the PWM default period in nanoseconds. +- qcom,lut-patterns: + Usage: optional + Value type: + Definition: Duty ratios in percentages for LPG working at LUT mode. + These duty ratios will be translated into PWM values + and stored in LUT module. The LUT module has resource + to store 47 PWM values at max and shared for all LPG + channels. This property is required if any LPG channels + support LUT mode. + +Subnode is optional if LUT mode is not required, it's required if any LPG +channels expected to be supported in LUT mode. + +Subnode properties: +Subnodes for each LPG channel (lpg@X) can be defined if any of the following +parameters needs to be configured for that channel. + +- qcom,lpg-chan-id: + Usage: required + Value type: + Definition: The LPG channel's hardware ID indexed from 1. Allowed + range is 1 - 8. Maximum value depends on the number of + channels supported on PMIC. + +- qcom,ramp-step-ms: + Usage: required + Value type: + Definition: The step duration in milliseconds for LPG staying at each + duty specified in the LUT pattern. Allowed range is + 1 - 511. + +- qcom,ramp-high-index: + Usage: required + Value type: + Definition: The high index of the LUT pattern where LPG ends up + ramping to. Allowed range is 1 - 47. + +- qcom,ramp-low-index: + Usage: required + Value type: + Definition: The low index of the LUT pattern from where LPG begins + ramping from. Allowed range is 0 - 46. + +- qcom,ramp-from-low-to-high: + Usage: optional + Value type: + Definition: The flag to specify the LPG ramping direction. The ramping + direction is from low index to high index of the LUT + pattern if it's specified. + +- qcom,ramp-pattern-repeat: + Usage: optional + Value type: + Definition: The flag to specify if LPG would be ramping with the LUT + pattern repeatedly. + +- qcom,ramp-toggle: + Usage: optional + Value type: + Definition: The flag to specify if LPG would toggle the LUT pattern + in ramping. If toggling enabled, LPG would return to the + low index when high index is reached, or return to the high + index when low index is reached. + +- qcom,ramp-pause-hi-count: + Usage: optional + Value type: + Definition: The step count that LPG stop the output when it ramped up + to the high index of the LUT. + +- qcom,ramp-pause-lo-count: + Usage: optional + Value type: + Definition: The step count that LPG stop the output when it ramped up + to the low index of the LUT. Example: pmi8998_lpg: lpg@b100 { compatible = "qcom,pwm-lpg"; - reg = <0xb100 0x600>; - reg-names = "lpg-base"; + reg = <0xb100 0x600>, <0xb000 0x100>; + reg-names = "lpg-base", "lut-base"; #pwm-cells = <2>; + qcom,lut-patterns = <0 14 28 42 56 70 84 100 + 100 84 70 56 42 28 14 0>; + lpg@3 { + qcom,lpg-chan-id = <3>; + qcom,ramp-step-ms = <200>; + qcom,ramp-pause-hi-count = <10>; + qcom,ramp-pause-lo-count = <10>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <15>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + lpg@4 { + qcom,lpg-chan-id = <4>; + qcom,ramp-step-ms = <200>; + qcom,ramp-pause-hi-count = <10>; + qcom,ramp-pause-lo-count = <10>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <15>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + lpg@5 { + qcom,lpg-chan-id = <5>; + qcom,ramp-step-ms = <200>; + qcom,ramp-pause-hi-count = <10>; + qcom,ramp-pause-lo-count = <10>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <15>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; }; diff --git a/Documentation/devicetree/bindings/qbt1000/qbt1000.txt b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt new file mode 100644 index 0000000000000000000000000000000000000000..c9861e4948f9ca74c9cb203877fc56bcddd13ea2 --- /dev/null +++ b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt @@ -0,0 +1,54 @@ +Qualcomm Technologies, Inc. QBT1000 Specific Bindings + +QBT is a fingerprint sensor ASIC capable of performing fingerprint image scans +and detecting finger presence on the sensor using programmable firmware. + +======================= +Required Node Structure +======================= + +- compatible + Usage: required + Value type: + Definition: "qcom,qbt1000". + +- clock-names + Usage: required + Value type: + Definition: List of clock names that need to be voted on/off. + +- clocks + Usage: required + Value type: + Definition: Property pair that represents the clock controller and the clock + id. This in combination with the clock-name is used to obtain + the handle for the clock that needs to be voted on/off. + +- clock-frequency + Usage: required + Value type: + Definition: Frequency of clock in Hz. + +- qcom,ipc-gpio + Usage: required + Value type: + Definition: phandle for GPIO to be used for IPC. + +- qcom,finger-detect-gpio + Usage: required + Value type: + Definition: phandle for GPIO to be used for finger detect. + +======= +Example +======= + +qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clocks = <&clock_gcc clk_gcc_blsp2_qup6_spi_apps_clk>, + <&clock_gcc clk_gcc_blsp2_ahb_clk>; + clock-frequency = <15000000>; + qcom,ipc-gpio = <&tlmm 121 0>; + qcom,finger-detect-gpio = <&pmcobalt_gpios 2 0>; +}; diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt index c5b48aebb6f54f59cea4871569be66868069edd6..5e104f92b5f59a812525e8ccc9b2bec54319aab0 100644 --- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt +++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt @@ -21,10 +21,13 @@ Optional subnodes: - qcom,msm-fastrpc-rpmsg : Child node for rpmsg instead of glink for IPC Subnode Required properties: -- compatible : Must be "qcom,msm-fastrpc-compute-cb" -- label: Label describing the channel this context bank belongs to -- iommus : A list of phandle and IOMMU specifier pairs that describe the +- compatible : Must be "qcom,msm-fastrpc-compute-cb" +- label : Label describing the channel this context bank belongs to +- iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device +- dma-coherent : A flag marking a context bank as I/O coherent +- shared-cb : A value indicating how many fastrpc sessions can share a + context bank Example: qcom,msm_fastrpc { @@ -42,12 +45,14 @@ Example: qcom,msm_fastrpc_compute_cb_1 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "adsprpc-smd"; - iommus = <&apps_smmu 0x1401 0x0>, + iommus = <&apps_smmu 0x1401 0x0>; + dma-coherent; }; qcom,msm_fastrpc_compute_cb_2 { compatible = "qcom,msm-fastrpc-compute-cb"; - label = "adsprpc-smd"; - iommus = <&apps_smmu 0x1402 0x0>, + label = "sdsprpc-smd"; + iommus = <&apps_smmu 0x1402 0x0>; + shared-cb = <5>; }; }; diff --git a/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt index 0927eee292bffba9138b253ead64dd9b12f8f03d..5c93e5666634ebc8166ef3b9d0977cbbee5aa120 100644 --- a/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt @@ -50,16 +50,28 @@ Optional properties: to enable. - qcom,reset-aon-logic: If present, the GPU DEMET cells need to be reset while enabling the GX GDSC. - - qcom,vote-parent-supply-voltage: If present, need to vote for a minimum - operational voltage (LOW_SVS) on the GDSC parent - regulator prior to configuring it. The vote is removed - once the GDSC FSM has latched on to the new state. + - vdd_parent-supply: phandle to the regulator that this GDSC gates. If + present, need to vote for a minimum operational voltage + (LOW_SVS) on the GDSC parent regulator prior to + configuring it. The vote is removed once the GDSC FSM + has latched on to the new state. - resets: reset specifier pair consisting of phandle for the reset controller and reset lines used by this controller. These can be supplied only if we support qcom,skip-logic-collapse. - reset-names: reset signal name strings sorted in the same order as the resets property. These can be supplied only if we support qcom,skip-logic-collapse. + - qcom,msm-bus,name: Name to use for the bus client. See [1] for details. + - qcom,msm-bus,num-cases: Must be 2 if qcom,msm-bus,name is specified. The + first case corresponds to no bus request and the second + case corresponds to a minimum bus request. See [1] for + details. + - qcom,msm-bus,num-paths: Should be 1 if qcom,msm-bus,name is specified. See + [1] for details. + - qcom,msm-bus,vectors-KBps: Required if qcom,msm-bus,name is specified. See + [1] for an explanation of the data format. + +[1]: Documentation/devicetree/bindings/arm/msm/msm_bus.txt Example: gdsc_oxili_gx: qcom,gdsc@fd8c4024 { diff --git a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt index 9798ac60b4931f96d9ba76daab50541a7e05a7cd..72c4eaf17b5fc6699a3bd00c8a6fb5f24d03be41 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt @@ -212,8 +212,9 @@ Properties below are specific to BOOST subnode only. - qcom,bst-headroom-mv Usage: optional Value type: - Definition: Headroom of the boost (in mV). The minimum headroom is - 200mV and if not specified defaults to 200mV. + Definition: Headroom of the boost (in mV). If not specified, then the + default value is 200 mV (PM660L) or 150 mV (for PM855L or + PMI632). ======= Example diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt index dad3b2ec66d45e8b8af037b2ef61b3770bec1256..aeb6db4e35c3bb816d41da1345f35eed1b83b09c 100644 --- a/Documentation/devicetree/bindings/serial/8250.txt +++ b/Documentation/devicetree/bindings/serial/8250.txt @@ -24,6 +24,7 @@ Required properties: - "ti,da830-uart" - "aspeed,ast2400-vuart" - "aspeed,ast2500-vuart" + - "nuvoton,npcm750-uart" - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,glink-probe.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,glink-probe.txt index af7b3f9c1a15bd28b218c8eae9ce7ad2637cfbae..badb9f9bcd53cb5ebb093f58623a185201b8ab9e 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,glink-probe.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,glink-probe.txt @@ -13,26 +13,57 @@ The GLINK probe node must contain subnodes that describes the edge-pairs. See qcom,glink.txt for details on how to describe them. In addition to the properties in qcom,glink.txt, The GLINK Probe driver -requires the remote-pid and transport type to be specified in the subnodes. +requires the qcom,glink-label and transport type to be specified in the +subnodes. - transport : Usage: required Value type: Definition: must be "smem", "spss", or "spi" -- qcom,remote-pid : +- qcom,glink-label : Usage: required - Value type: + Value type: Definition: specifies the identifier of the remote proc of this edge. += GLINK_SSR +The GLINK probe driver also initializes the GLINK_SSR channel for the edges +that it brings up. The channel should be specified as a subnode to each edge. In +addition to the properties in qcom,glink.txt to specify a channel device node, +the qcom,notify-edges property must be defined. + +- qcom,notify-edges : + Usage: required + Value type: + Definition: list of phandles that specify the subsystems this glink edge + needs to receive ssr notifications about. + = EXAMPLE qcom,glink { compatible = "qcom,glink"; - modem { + glink_modem: modem { transport = "smem"; - qcom,remote-pid = <1>; + qcom,remote-pid = <0>; mboxes = <&apcs_glb 8>; mbox-names = "mpss_smem"; interrupts = ; + + qcom,modem_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_adsp>; + }; + }; + + glink_adsp: adsp { + transport = "smem"; + qcom,remote-pid = <2>; + mboxes = <&apcs_glb 4>; + mbox-names = "adsp_smem"; + interrupts = ; + + qcom,modem_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>; + }; }; }; diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb-debug.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb-debug.txt index ceac719878e7a32dd90a4f7e2227d8bd43d7c447..2131c33237f00953f10f92ac8337b41a63896a87 100644 --- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb-debug.txt +++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb-debug.txt @@ -35,6 +35,19 @@ Supported Properties: the corresponding addresses are specified in the reg property. +- clocks + Usage: optional + Value type: + Definition: Clock tuple consisting of a phandle to a clock controller + device and the clock ID number for the SPMI debug controller + clock. + +- clock-names + Usage: required if clocks property is specified + Value type: + Definition: Defines the name of the clock defined in the "clocks" + property. This must be "core_clk". + - #address-cells Usage: required Value type: @@ -57,6 +70,8 @@ qcom,spmi-debug@6b22000 { compatible = "qcom,spmi-pmic-arb-debug"; reg = <0x6b22000 0x60>, <0x7820A8 4>; reg-names = "core", "fuse"; + clocks = <&clock_aop QDSS_CLK>; + clock-names = "core_clk"; qcom,fuse-disable-bit = <12>; #address-cells = <2>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/thermal/qcom-adc-tm.txt b/Documentation/devicetree/bindings/thermal/qcom-adc-tm.txt new file mode 100644 index 0000000000000000000000000000000000000000..05cdd98d9e39e021b34ed05b776e83ee21b2581e --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/qcom-adc-tm.txt @@ -0,0 +1,145 @@ +Qualcomm Technologies, Inc. PMIC thermal monitor ADC driver (ADC_TM) + +PMIC thermal monitoring (TM) provides interface to thermal clients +to set temperature thresholds and receive notification when the thresholds +are crossed. A 15 bit ADC is used for measurements. The driver is part +of the sysfs thermal framework that provides support to read the trip +points, set threshold for the trip points and enable the trip points. + +ADC_TM node + +- compatible: + Usage: required + Value type: + Definition: Should contain "qcom,adc-tm5" for PMIC5 ADC TM driver. + +- reg: + Usage: required + Value type: + Definition: ADC_TM base address and length in the SPMI PMIC register map. + +- #address-cells: + Usage: required + Value type: + Definition: Must be one. Child node 'reg' property should define ADC + channel number. + +- #size-cells: + Usage: required + Value type: + Definition: Must be zero. + +- interrupts: + Usage: required + Value type: + Definition: End of conversion interrupt. + +- interrupt-names: + Usage: required + Value type: + Definition: Should contain "thr-int-en" for PMIC5 ADC TM driver. + +- qcom,decimation: + Usage: optional + Value type: + Definition: This parameter is used to decrease ADC sampling rate. + Quicker measurements can be made by reducing decimation ratio. + For PMIC5 ADC, combined two step decimation values are 250, 420 and 840. + If property is not found, default value of 840 will be used. + +- qcom,avg-samples: + Usage: optional + Value type: + Definition: Number of samples to be used for measurement. + Averaging provides the option to obtain a single measurement + from the ADC that is an average of multiple samples. The value + selected is 2^(value). + Valid values are: 1, 2, 4, 8, 16 + If property is not found, 1 sample will be used. + +- #thermal-sensor-cells: + Usage: optional + Value type: + Definition: Should be 1. See thermal.txt for a description. + +- io-channels: + Usage: Required + Value type: + Definition: The phandle of the iio provider. + +Channel node properties: + +- reg: + Usage: required + Value type: + Definition: ADC channel number. + See include/dt-bindings/iio/qcom,spmi-vadc.h + +- qcom,pre-scaling: + Usage: optional + Value type: + Definition: Used for scaling the channel input signal before the signal is + fed to VADC. The configuration for this node is to know the + pre-determined ratio and use it for post scaling. Select one from + the following options. + <1 1>, <1 3>, <1 4>, <1 6>, <1 20>, <1 8>, <10 81>, <1 10> + If property is not found default value depending on chip will be used. + +- qcom,ratiometric: + Usage: optional + Value type: + Definition: Channel calibration type. If this property is specified + VADC will use the VDD reference (1.875V) and GND for channel + calibration. If property is not found, channel will be + calibrated with 0V and 1.25V reference channels, also + known as absolute calibration. + +- qcom,hw-settle-time: + Usage: optional + Value type: + Definition: Time between AMUX getting configured and the ADC starting + conversion. + For PMIC5, delay = 15us for value 0, + 100us * (value) for values 0 < value < 11, and + 2ms * (value - 10) otherwise. + Valid values are: 15, 100, 200, 300, 400, 500, 600, 700, 1, + 2, 4, 8, 16, 32, 64, 128 ms + If property is not found, channel will use 15us. + +Example: + + /* ADC_TM node */ + pmic_adc_tm: adc_tm@3500 { + compatible = "qcom,adc-tm5"; + reg = <0x3500 0x100>; + interrupts = <0x0 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "thr-int-en"; + #address-cells = <1>; + #size-cells = <0>; + #thermal-sensor-cells = <1>; + io-channels = <&pmic_vadc ADC_AMUX_THM2_PU2>; + + /* Channel node */ + skin_msm_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + }; + + /* Adding thermal zone to register with of_thermal */ + &thermal_zones { + wp-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pmic_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt index 355ae18130bb173ccff8fb25d1aae730e1d2cf17..dcb4bda84f2bd4707049cf7727172a7ee3ae2ad2 100644 --- a/Documentation/devicetree/bindings/thermal/tsens.txt +++ b/Documentation/devicetree/bindings/thermal/tsens.txt @@ -21,14 +21,18 @@ Required properties: should be "qcom,tsens24xx" for 2.4 TSENS controller. should be "qcom,msm8937-tsens" for 8937 TSENS driver. should be "qcom,qcs405-tsens" for QCS405 TSENS driver. + should be "qcom,sm6150-tsens" for 6150 TSENS driver. + The compatible property is used to identify the respective controller to use for the corresponding SoC. - reg : offset and length of the TSENS registers with associated property in reg-names as "tsens_srot_physical" for TSENS SROT physical address region. TSENS TM - physical address region as "tsens_tm_physical". + physical address region as "tsens_tm_physical", and "tsens_eeprom_physical" for the + TSENS calibration fuse register region. - reg-names : resource names used for the physical address of the TSENS registers. Should be "tsens_srot_physical" for physical address of the TSENS - SROT region and "tsens_tm_physical" for physical address of the TM region. + SROT region, "tsens_tm_physical" for physical address of the TM region and + "tsens_eeprom_physical" for the TSENS calibration fuse register region. - interrupts : TSENS interrupt to notify Upper/Lower and Critical temperature threshold. - interrupt-names: Should be "tsens-upper-lower" for temperature threshold. Add "tsens-critical" for Critical temperature threshold notification @@ -42,7 +46,8 @@ tsens@fc4a8000 { reg = <0xfc4a8000 0x10>, <0xfc4b8000 0x1ff>; reg-names = "tsens_srot_physical", - "tsens_tm_physical"; + "tsens_tm_physical", + "tsens_eeprom_physical", interrupts = <0 184 0>; interrupt-names = "tsens-upper-lower"; }; diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 4c20233c5e98c5d52be1a4c79d5e44a09b452223..d3fbfe1f358b6a66bb58910db3f9c15f7bc6f1d4 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -48,6 +48,8 @@ Optional properties: from P0 to P1/P2/P3 without delay. - snps,dis-tx-ipgap-linecheck-quirk: when set, disable u2mac linestate check during HS transmit. + - snps,ssp-u3-u0-quirk: when set, core always changes PHY power state + to P2, before attempting a U3 exit handshake. - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal utmi_l1_suspend_n, false when asserts utmi_sleep_n - snps,hird-threshold: HIRD threshold @@ -62,6 +64,8 @@ Optional properties: - snps,bus-suspend-enable: If present then controller supports low power mode during bus suspend. - snps,usb3-u1u2-disable: If present, disable U1U2 low power modes in Superspeed mode + - snps,disable-clk-gating: If present, disable controller's internal clock gating. + Default it is enabled. - in addition all properties from usb-xhci.txt from the current directory are supported as well diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 13c2ff0343485f7595b66da41aa96475a4602bfd..4b6cf4c5e06168023db327035f149b8f5ca584a6 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -174,6 +174,23 @@ offgrpjquota Turn off group journelled quota. offprjjquota Turn off project journelled quota. quota Enable plain user disk quota accounting. noquota Disable all plain disk quota option. +whint_mode=%s Control which write hints are passed down to block + layer. This supports "off", "user-based", and + "fs-based". In "off" mode (default), f2fs does not pass + down hints. In "user-based" mode, f2fs tries to pass + down hints given by users. And in "fs-based" mode, f2fs + passes down hints with its policy. +alloc_mode=%s Adjust block allocation policy, which supports "reuse" + and "default". +fsync_mode=%s Control the policy of fsync. Currently supports "posix" + and "strict". In "posix" mode, which is default, fsync + will follow POSIX semantics and does a light operation + to improve the filesystem performance. In "strict" mode, + fsync will be heavy and behaves in line with xfs, ext4 + and btrfs, where xfstest generic/342 will pass, but the + performance will regress. +test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt + context. The fake fscrypt context is used by xfstests. ================================================================================ DEBUGFS ENTRIES diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index b244d6e23fc48733890efb0e94dc5454e5b23d11..579337f3fbf3455f24d74d4ff2a1397d0faeac88 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -857,6 +857,11 @@ ip_unprivileged_port_start - INTEGER Default: 1024 +reserved_port_bind - BOOLEAN + If set, allows explicit bind requests to applications requesting + any port within the range of ip_local_reserved_ports. + Default: 1 + ip_nonlocal_bind - BOOLEAN If set, allows processes to bind() to non-local IP addresses, which can be quite useful - but may break some applications. diff --git a/Documentation/networking/netdev-features.txt b/Documentation/networking/netdev-features.txt index 7413eb05223b104f4d8a00d9615dbd694b596cf4..dfb2707c7fa48939395a62c38eadf357bc96da91 100644 --- a/Documentation/networking/netdev-features.txt +++ b/Documentation/networking/netdev-features.txt @@ -113,6 +113,13 @@ whatever headers there might be. NETIF_F_TSO_ECN means that hardware can properly split packets with CWR bit set, be it TCPv4 (when NETIF_F_TSO is enabled) or TCPv6 (NETIF_F_TSO6). + * Transmit UDP segmentation offload + +NETIF_F_GSO_UDP_GSO_L4 accepts a single UDP header with a payload that exceeds +gso_size. On segmentation, it segments the payload on gso_size boundaries and +replicates the network and UDP headers (fixing up the last one if less than +gso_size). + * Transmit DMA from high memory On platforms where this is relevant, NETIF_F_HIGHDMA signals that diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index e63a35fafef0e153c30023e92622111006d1dd7c..88ad78c6f605fb5967888f30b38c4021e0efc8ce 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1837,6 +1837,7 @@ registers, find a list below: PPC | KVM_REG_PPC_DBSR | 32 PPC | KVM_REG_PPC_TIDR | 64 PPC | KVM_REG_PPC_PSSCR | 64 + PPC | KVM_REG_PPC_DEC_EXPIRY | 64 PPC | KVM_REG_PPC_TM_GPR0 | 64 ... PPC | KVM_REG_PPC_TM_GPR31 | 64 @@ -1939,6 +1940,9 @@ ARM 32-bit VFP control registers have the following id bit patterns: ARM 64-bit FP registers have the following id bit patterns: 0x4030 0000 0012 0 +ARM firmware pseudo-registers have the following bit pattern: + 0x4030 0000 0014 + arm64 registers are mapped using the lower 32 bits. The upper 16 of that is the register group type, or coprocessor number: @@ -1955,6 +1959,9 @@ arm64 CCSIDR registers are demultiplexed by CSSELR value: arm64 system registers have the following id bit patterns: 0x6030 0000 0013 +arm64 firmware pseudo-registers have the following bit pattern: + 0x6030 0000 0014 + MIPS registers are mapped using the lower 32 bits. The upper 16 of that is the register group type: @@ -2489,7 +2496,8 @@ Possible features: and execute guest code when KVM_RUN is called. - KVM_ARM_VCPU_EL1_32BIT: Starts the CPU in a 32bit mode. Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only). - - KVM_ARM_VCPU_PSCI_0_2: Emulate PSCI v0.2 for the CPU. + - KVM_ARM_VCPU_PSCI_0_2: Emulate PSCI v0.2 (or a future revision + backward compatible with v0.2) for the CPU. Depends on KVM_CAP_ARM_PSCI_0_2. - KVM_ARM_VCPU_PMU_V3: Emulate PMUv3 for the CPU. Depends on KVM_CAP_ARM_PMU_V3. diff --git a/Documentation/virtual/kvm/arm/psci.txt b/Documentation/virtual/kvm/arm/psci.txt new file mode 100644 index 0000000000000000000000000000000000000000..aafdab887b047b6f14c85f29660d838b2d3f4143 --- /dev/null +++ b/Documentation/virtual/kvm/arm/psci.txt @@ -0,0 +1,30 @@ +KVM implements the PSCI (Power State Coordination Interface) +specification in order to provide services such as CPU on/off, reset +and power-off to the guest. + +The PSCI specification is regularly updated to provide new features, +and KVM implements these updates if they make sense from a virtualization +point of view. + +This means that a guest booted on two different versions of KVM can +observe two different "firmware" revisions. This could cause issues if +a given guest is tied to a particular PSCI revision (unlikely), or if +a migration causes a different PSCI version to be exposed out of the +blue to an unsuspecting guest. + +In order to remedy this situation, KVM exposes a set of "firmware +pseudo-registers" that can be manipulated using the GET/SET_ONE_REG +interface. These registers can be saved/restored by userspace, and set +to a convenient value if required. + +The following register is defined: + +* KVM_REG_ARM_PSCI_VERSION: + + - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set + (and thus has already been initialized) + - Returns the current PSCI version on GET_ONE_REG (defaulting to the + highest PSCI version implemented by KVM and compatible with v0.2) + - Allows any PSCI version implemented by KVM and compatible with + v0.2 to be set with SET_ONE_REG + - Affects the whole VM (even if the register view is per-vcpu) diff --git a/Makefile b/Makefile index 0b60e4d377b233865d0a90535fbd0108bd08d647..b2810321a5eb57a7cff02692eac9f49dfd8b6ba1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 4 PATCHLEVEL = 14 -SUBLEVEL = 32 +SUBLEVEL = 39 EXTRAVERSION = NAME = Petit Gorille @@ -378,6 +378,7 @@ endif AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld REAL_CC = $(CROSS_COMPILE)gcc +LDGOLD = $(CROSS_COMPILE)ld.gold CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm @@ -640,6 +641,20 @@ CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disabl CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) export CFLAGS_GCOV CFLAGS_KCOV +# Make toolchain changes before including arch/$(SRCARCH)/Makefile to ensure +# ar/cc/ld-* macros return correct values. +ifdef CONFIG_LTO_CLANG +# use GNU gold with LLVMgold for LTO linking, and LD for vmlinux_link +LDFINAL_vmlinux := $(LD) +LD := $(LDGOLD) +LDFLAGS += -plugin LLVMgold.so +# use llvm-ar for building symbol tables from IR files, and llvm-dis instead +# of objdump for processing symbol versions and exports +LLVM_AR := llvm-ar +LLVM_DIS := llvm-dis +export LLVM_AR LLVM_DIS +endif + # The arch Makefile can set ARCH_{CPP,A,C}FLAGS to override the default # values of the respective KBUILD_* variables ARCH_CPPFLAGS := @@ -818,6 +833,53 @@ KBUILD_CFLAGS += $(call cc-option,-ffunction-sections,) KBUILD_CFLAGS += $(call cc-option,-fdata-sections,) endif +ifdef CONFIG_LTO_CLANG +lto-clang-flags := -flto -fvisibility=hidden + +# allow disabling only clang LTO where needed +DISABLE_LTO_CLANG := -fno-lto -fvisibility=default +export DISABLE_LTO_CLANG +endif + +ifdef CONFIG_LTO +lto-flags := $(lto-clang-flags) +KBUILD_CFLAGS += $(lto-flags) + +DISABLE_LTO := $(DISABLE_LTO_CLANG) +export DISABLE_LTO + +# LDFINAL_vmlinux and LDFLAGS_FINAL_vmlinux can be set to override +# the linker and flags for vmlinux_link. +export LDFINAL_vmlinux LDFLAGS_FINAL_vmlinux +endif + +ifdef CONFIG_CFI_CLANG +cfi-clang-flags += -fsanitize=cfi +DISABLE_CFI_CLANG := -fno-sanitize=cfi +ifdef CONFIG_MODULES +cfi-clang-flags += -fsanitize-cfi-cross-dso +DISABLE_CFI_CLANG += -fno-sanitize-cfi-cross-dso +endif +ifdef CONFIG_CFI_PERMISSIVE +cfi-clang-flags += -fsanitize-recover=cfi -fno-sanitize-trap=cfi +endif + +# also disable CFI when LTO is disabled +DISABLE_LTO_CLANG += $(DISABLE_CFI_CLANG) +# allow disabling only clang CFI where needed +export DISABLE_CFI_CLANG +endif + +ifdef CONFIG_CFI +# cfi-flags are re-tested in prepare-compiler-check +cfi-flags := $(cfi-clang-flags) +KBUILD_CFLAGS += $(cfi-flags) + +DISABLE_CFI := $(DISABLE_CFI_CLANG) +DISABLE_LTO += $(DISABLE_CFI) +export DISABLE_CFI +endif + # arch Makefile may override CC so keep this after arch Makefile is included NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) CHECKFLAGS += $(NOSTDINC_FLAGS) @@ -1135,6 +1197,22 @@ prepare-objtool: $(objtool_target) # CC_STACKPROTECTOR_STRONG! Why did it build with _REGULAR?!") PHONY += prepare-compiler-check prepare-compiler-check: FORCE +# Make sure we're using a supported toolchain with LTO_CLANG +ifdef CONFIG_LTO_CLANG + ifneq ($(call clang-ifversion, -ge, 0500, y), y) + @echo Cannot use CONFIG_LTO_CLANG: requires clang 5.0 or later >&2 && exit 1 + endif + ifneq ($(call gold-ifversion, -ge, 112000000, y), y) + @echo Cannot use CONFIG_LTO_CLANG: requires GNU gold 1.12 or later >&2 && exit 1 + endif +endif +# Make sure compiler supports LTO flags +ifdef lto-flags + ifeq ($(call cc-option, $(lto-flags)),) + @echo Cannot use CONFIG_LTO: $(lto-flags) not supported by compiler \ + >&2 && exit 1 + endif +endif # Make sure compiler supports requested stack protector flag. ifdef stackp-name ifeq ($(call cc-option, $(stackp-flag)),) @@ -1148,6 +1226,11 @@ ifdef stackp-check @echo Cannot use CONFIG_CC_STACKPROTECTOR_$(stackp-name): \ $(stackp-flag) available but compiler is broken >&2 && exit 1 endif +endif +ifdef cfi-flags + ifeq ($(call cc-option, $(cfi-flags)),) + @echo Cannot use CONFIG_CFI: $(cfi-flags) not supported by compiler >&2 && exit 1 + endif endif @: @@ -1608,7 +1691,8 @@ clean: $(clean-dirs) -o -name modules.builtin -o -name '.tmp_*.o.*' \ -o -name '*.c.[012]*.*' \ -o -name '*.ll' \ - -o -name '*.gcno' \) -type f -print | xargs rm -f + -o -name '*.gcno' \ + -o -name '*.*.symversions' \) -type f -print | xargs rm -f # Generate tags for editors # --------------------------------------------------------------------------- diff --git a/arch/Kconfig b/arch/Kconfig index 400b9e1b2f275d2f253a54087c137bedf0a491ea..6ba7bbb16c68ec99e2cc24cdc463f30d5257ef0a 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -605,6 +605,74 @@ config LD_DEAD_CODE_DATA_ELIMINATION sections (e.g., '.text.init'). Typically '.' in section names is used to distinguish them from label names / C identifiers. +config LTO + def_bool n + +config ARCH_SUPPORTS_LTO_CLANG + bool + help + An architecture should select this option it supports: + - compiling with clang, + - compiling inline assembly with clang's integrated assembler, + - and linking with either lld or GNU gold w/ LLVMgold. + +choice + prompt "Link-Time Optimization (LTO) (EXPERIMENTAL)" + default LTO_NONE + help + This option turns on Link-Time Optimization (LTO). + +config LTO_NONE + bool "None" + +config LTO_CLANG + bool "Use clang Link Time Optimization (LTO) (EXPERIMENTAL)" + depends on ARCH_SUPPORTS_LTO_CLANG + depends on !FTRACE_MCOUNT_RECORD || HAVE_C_RECORDMCOUNT + select LTO + select THIN_ARCHIVES + select LD_DEAD_CODE_DATA_ELIMINATION + help + This option enables clang's Link Time Optimization (LTO), which allows + the compiler to optimize the kernel globally at link time. If you + enable this option, the compiler generates LLVM IR instead of object + files, and the actual compilation from IR occurs at the LTO link step, + which may take several minutes. + + If you select this option, you must compile the kernel with clang >= + 5.0 (make CC=clang) and GNU gold from binutils >= 2.27, and have the + LLVMgold plug-in in LD_LIBRARY_PATH. + +endchoice + +config CFI + bool + +config CFI_PERMISSIVE + bool "Use CFI in permissive mode" + depends on CFI + help + When selected, Control Flow Integrity (CFI) violations result in a + warning instead of a kernel panic. This option is useful for finding + CFI violations in drivers during development. + +config CFI_CLANG + bool "Use clang Control Flow Integrity (CFI) (EXPERIMENTAL)" + depends on LTO_CLANG + depends on KALLSYMS + select CFI + help + This option enables clang Control Flow Integrity (CFI), which adds + runtime checking for indirect function calls. + +config CFI_CLANG_SHADOW + bool "Use CFI shadow to speed up cross-module checks" + default y + depends on CFI_CLANG + help + If you select this option, the kernel builds a fast look-up table of + CFI check functions in loaded modules to reduce overhead. + config HAVE_ARCH_WITHIN_STACK_FRAMES bool help @@ -959,4 +1027,13 @@ config REFCOUNT_FULL against various use-after-free conditions that can be used in security flaw exploits. +config PANIC_ON_REFCOUNT_ERROR + bool "Kernel panic on refcount error detection" + depends on REFCOUNT_FULL + help + If enabled, the kernel will panic when the refcount library + has detected any type of error (e.g. potential use-after-free + or potential memory-leaks) with an object associated with that + reference counter. + source "kernel/gcov/Kconfig" diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 12c6d93d1b25702a28efad9809162d0fcb183714..1f7950af5c692ca048a27fa522a6cc006af054d9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -19,6 +19,7 @@ config ARM select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF select ARCH_WANT_IPC_PARSE_VERSION + select ARM_PSCI_FW if PM select BUILDTIME_EXTABLE_SORT if MMU select CLONE_BACKWARDS select CPU_PM if (SUSPEND || CPU_IDLE) diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 7ab23ba86a0e2acddf2793cac20a0a3fa6491e2e..a6cebd5e9a6bdf2ebf1fc6d46a8a832c7c95d9c7 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -63,6 +63,21 @@ config DEBUG_USER 8 - SIGSEGV faults 16 - SIGBUS faults +config ARCH_SUPPORTS_DEBUG_PAGEALLOC + def_bool y + depends on FORCE_PAGES + +config FORCE_PAGES + bool "Force lowmem to be mapped with 4K pages" + help + There are some advanced debug features that can only be done when + memory is mapped with pages instead of sections. Enable this option + to always map lowmem pages with pages. This may have a performance + cost due to increased TLB pressure. + + If unsure say N. + + # These options are only for real kernel hackers who want to get their hands dirty. config DEBUG_LL bool "Kernel low-level debugging functions (read help!)" diff --git a/arch/arm/boot/dts/am335x-pepper.dts b/arch/arm/boot/dts/am335x-pepper.dts index 9fb7426070ce0dc9402520c3ee5210ae31e6e6da..03c7d77023c6a303213328ab6bde8de5f9f32288 100644 --- a/arch/arm/boot/dts/am335x-pepper.dts +++ b/arch/arm/boot/dts/am335x-pepper.dts @@ -139,7 +139,7 @@ &audio_codec { status = "okay"; - reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>; + gpio-reset = <&gpio1 16 GPIO_ACTIVE_LOW>; AVDD-supply = <&ldo3_reg>; IOVDD-supply = <&ldo3_reg>; DRVDD-supply = <&ldo3_reg>; diff --git a/arch/arm/boot/dts/at91sam9g25.dtsi b/arch/arm/boot/dts/at91sam9g25.dtsi index a7da0dd0c98fa0af8d05ad545660aba426962c30..0898213f3bb22e41df4612a51b71ac77034fede6 100644 --- a/arch/arm/boot/dts/at91sam9g25.dtsi +++ b/arch/arm/boot/dts/at91sam9g25.dtsi @@ -21,7 +21,7 @@ atmel,mux-mask = < /* A B C */ 0xffffffff 0xffe0399f 0xc000001c /* pioA */ - 0x0007ffff 0x8000fe3f 0x00000000 /* pioB */ + 0x0007ffff 0x00047e3f 0x00000000 /* pioB */ 0x80000000 0x07c0ffff 0xb83fffff /* pioC */ 0x003fffff 0x003f8000 0x00000000 /* pioD */ >; diff --git a/arch/arm/boot/dts/da850-lego-ev3.dts b/arch/arm/boot/dts/da850-lego-ev3.dts index 413dbd5d9f6442b8dae4b7f132b35e5f816cf798..81942ae83e1f9c9f717dd0fd565a28966c44c1da 100644 --- a/arch/arm/boot/dts/da850-lego-ev3.dts +++ b/arch/arm/boot/dts/da850-lego-ev3.dts @@ -178,7 +178,7 @@ */ battery { pinctrl-names = "default"; - pintctrl-0 = <&battery_pins>; + pinctrl-0 = <&battery_pins>; compatible = "lego,ev3-battery"; io-channels = <&adc 4>, <&adc 3>; io-channel-names = "voltage", "current"; @@ -392,7 +392,7 @@ batt_volt_en { gpio-hog; gpios = <6 GPIO_ACTIVE_HIGH>; - output-low; + output-high; }; }; diff --git a/arch/arm/boot/dts/dra76-evm.dts b/arch/arm/boot/dts/dra76-evm.dts index b024a65c6e2711abb4beeb288556860841ea22c0..f64aab450315d878be8f7f80ac65672a0ce58e9c 100644 --- a/arch/arm/boot/dts/dra76-evm.dts +++ b/arch/arm/boot/dts/dra76-evm.dts @@ -148,6 +148,7 @@ compatible = "ti,tps65917"; reg = <0x58>; ti,system-power-controller; + ti,palmas-override-powerhold; interrupt-controller; #interrupt-cells = <2>; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 8dbeb873e99ca8bac4badd0532c75d14699337b6..35b1949a3e3cf3ccda9688092f043781258625ac 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -643,7 +643,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL0>; clock-names = "gscl"; - iommu = <&sysmmu_gsc0>; + iommus = <&sysmmu_gsc0>; }; gsc_1: gsc@13e10000 { @@ -653,7 +653,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL1>; clock-names = "gscl"; - iommu = <&sysmmu_gsc1>; + iommus = <&sysmmu_gsc1>; }; gsc_2: gsc@13e20000 { @@ -663,7 +663,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL2>; clock-names = "gscl"; - iommu = <&sysmmu_gsc2>; + iommus = <&sysmmu_gsc2>; }; gsc_3: gsc@13e30000 { @@ -673,7 +673,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL3>; clock-names = "gscl"; - iommu = <&sysmmu_gsc3>; + iommus = <&sysmmu_gsc3>; }; hdmi: hdmi@14530000 { diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index 9319e1f0f1d8f8360ad11513b178bca7cc71d9fe..379b4a03cfe2f7b92b2c3bd630d204be12a337d9 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -155,7 +155,7 @@ }; esdhc: esdhc@1560000 { - compatible = "fsl,esdhc"; + compatible = "fsl,ls1021a-esdhc", "fsl,esdhc"; reg = <0x0 0x1560000 0x0 0x10000>; interrupts = ; clock-frequency = <0>; diff --git a/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts b/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts index 7bf5aa2237c9d1363b85517295b0ce4b53ab7b57..7de704575aeeb80bc912cf7e0760e5cb1479a965 100644 --- a/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts +++ b/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts @@ -39,6 +39,24 @@ }; }; + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_5v: regulator-5v { + compatible = "regulator-fixed"; + regulator-name = "fixed-5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + }; + gpio_keys { compatible = "gpio-keys"; pinctrl-names = "default"; @@ -468,12 +486,14 @@ }; &usb1 { - vusb33-supply = <&mt6323_vusb_reg>; + vusb33-supply = <®_3p3v>; + vbus-supply = <®_5v>; status = "okay"; }; &usb2 { - vusb33-supply = <&mt6323_vusb_reg>; + vusb33-supply = <®_3p3v>; + vbus-supply = <®_5v>; status = "okay"; }; diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index c66ee8432a85f38d335c39023a57aae8d43c181e..4acd32a1c4ef7c8801bbd516f2f317d50db21218 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -558,7 +558,7 @@ tlv320aic3x: tlv320aic3x@18 { compatible = "ti,tlv320aic3x"; reg = <0x18>; - reset-gpios = <&gpio2 28 GPIO_ACTIVE_LOW>; /* 60 */ + gpio-reset = <&gpio2 28 GPIO_ACTIVE_HIGH>; /* 60 */ ai3x-gpio-func = < 0 /* AIC3X_GPIO1_FUNC_DISABLED */ 5 /* AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT */ @@ -575,7 +575,7 @@ tlv320aic3x_aux: tlv320aic3x@19 { compatible = "ti,tlv320aic3x"; reg = <0x19>; - reset-gpios = <&gpio2 28 GPIO_ACTIVE_LOW>; /* 60 */ + gpio-reset = <&gpio2 28 GPIO_ACTIVE_HIGH>; /* 60 */ AVDD-supply = <&vmmc2>; DRVDD-supply = <&vmmc2>; diff --git a/arch/arm/boot/dts/sama5d4.dtsi b/arch/arm/boot/dts/sama5d4.dtsi index 2fa36c525957cc193000f2db84ef50d07aa1ca85..81b5260850979efcc454f5cf7bdc22419c15acdb 100644 --- a/arch/arm/boot/dts/sama5d4.dtsi +++ b/arch/arm/boot/dts/sama5d4.dtsi @@ -1365,7 +1365,7 @@ pinctrl@fc06a000 { #address-cells = <1>; #size-cells = <1>; - compatible = "atmel,at91sam9x5-pinctrl", "atmel,at91rm9200-pinctrl", "simple-bus"; + compatible = "atmel,sama5d3-pinctrl", "atmel,at91sam9x5-pinctrl", "simple-bus"; ranges = <0xfc068000 0xfc068000 0x100 0xfc06a000 0xfc06a000 0x4000>; /* WARNING: revisit as pin spec has changed */ diff --git a/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts b/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts index 51e6f1d21c32b745957fe82dfc6eeeafb9b67bb7..b2758dd8ce438abdfd53a0a347f496b6d8ddbe7d 100644 --- a/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts +++ b/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts @@ -42,7 +42,6 @@ /dts-v1/; #include "sun6i-a31s.dtsi" -#include "sunxi-common-regulators.dtsi" #include / { @@ -99,6 +98,7 @@ pinctrl-0 = <&gmac_pins_rgmii_a>, <&gmac_phy_reset_pin_bpi_m2>; phy = <&phy1>; phy-mode = "rgmii"; + phy-supply = <®_dldo1>; snps,reset-gpio = <&pio 0 21 GPIO_ACTIVE_HIGH>; /* PA21 */ snps,reset-active-low; snps,reset-delays-us = <0 10000 30000>; @@ -118,7 +118,7 @@ &mmc0 { pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_bpi_m2>; - vmmc-supply = <®_vcc3v0>; + vmmc-supply = <®_dcdc1>; bus-width = <4>; cd-gpios = <&pio 0 4 GPIO_ACTIVE_HIGH>; /* PA4 */ cd-inverted; @@ -132,7 +132,7 @@ &mmc2 { pinctrl-names = "default"; pinctrl-0 = <&mmc2_pins_a>; - vmmc-supply = <®_vcc3v0>; + vmmc-supply = <®_aldo1>; mmc-pwrseq = <&mmc2_pwrseq>; bus-width = <4>; non-removable; @@ -163,6 +163,8 @@ reg = <0x68>; interrupt-parent = <&nmi_intc>; interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + eldoin-supply = <®_dcdc1>; + x-powers,drive-vbus-en; }; }; @@ -193,7 +195,28 @@ #include "axp22x.dtsi" +®_aldo1 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-wifi"; +}; + +®_aldo2 { + regulator-always-on; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-name = "vcc-gmac"; +}; + +®_aldo3 { + regulator-always-on; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-name = "avcc"; +}; + ®_dc5ldo { + regulator-always-on; regulator-min-microvolt = <700000>; regulator-max-microvolt = <1320000>; regulator-name = "vdd-cpus"; @@ -233,6 +256,40 @@ regulator-name = "vcc-dram"; }; +®_dldo1 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-name = "vcc-mac"; +}; + +®_dldo2 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-name = "avdd-csi"; +}; + +®_dldo3 { + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-pb"; +}; + +®_eldo1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vdd-csi"; + status = "okay"; +}; + +®_ldo_io1 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-pm-cpus"; + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins_a>; diff --git a/arch/arm/configs/qcs405-perf_defconfig b/arch/arm/configs/qcs405-perf_defconfig index 4cc91738ca57624ed5ae427c1a54a8952bb45f66..82c5f5a9d2e7499c34dfb148167b4f2373593fdc 100644 --- a/arch/arm/configs/qcs405-perf_defconfig +++ b/arch/arm/configs/qcs405-perf_defconfig @@ -30,6 +30,7 @@ CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_QCS405=y # CONFIG_VDSO is not set +CONFIG_ARM_PSCI=y CONFIG_PREEMPT=y CONFIG_CLEANCACHE=y CONFIG_CMA=y @@ -37,7 +38,10 @@ CONFIG_ZSMALLOC=y CONFIG_SECCOMP=y CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_PM_AUTOSLEEP=y CONFIG_PM_WAKELOCKS=y @@ -166,6 +170,8 @@ CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y CONFIG_NET_CLS_ACT=y +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_BT=y @@ -246,6 +252,14 @@ CONFIG_SPI_SPIDEV=y CONFIG_PINCTRL_QCS405=y CONFIG_GPIOLIB=y CONFIG_THERMAL=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_FAN53555=y @@ -296,24 +310,43 @@ CONFIG_DUAL_ROLE_USB_INTF=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y CONFIG_MMC_TEST=m CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y CONFIG_RTC_CLASS=y CONFIG_DMADEVICES=y CONFIG_UIO=y CONFIG_STAGING=y CONFIG_ASHMEM=y CONFIG_ION=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_QCOM_CLK_SMD_RPM=y +CONFIG_MDM_GCC_QCS405=y CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y CONFIG_MAILBOX=y +CONFIG_QCOM_APCS_IPC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_SMD=y +CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y +CONFIG_QCOM_SMD_RPM=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_SMP2P=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_QCOM_GLINK=y +CONFIG_QCOM_GLINK_PKT=y CONFIG_PWM=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/arch/arm/configs/qcs405_defconfig b/arch/arm/configs/qcs405_defconfig index d03f31a38a592ac1690159c691ef9522f8bccc45..23a0718ad009b3b9da98337800a6bb296adcd192 100644 --- a/arch/arm/configs/qcs405_defconfig +++ b/arch/arm/configs/qcs405_defconfig @@ -33,6 +33,7 @@ CONFIG_ARCH_QCOM=y CONFIG_ARCH_QCS405=y # CONFIG_VDSO is not set CONFIG_SMP=y +CONFIG_ARM_PSCI=y CONFIG_PREEMPT=y CONFIG_CLEANCACHE=y CONFIG_CMA=y @@ -41,7 +42,10 @@ CONFIG_ZSMALLOC=y CONFIG_SECCOMP=y CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_PM_AUTOSLEEP=y CONFIG_PM_WAKELOCKS=y @@ -172,6 +176,8 @@ CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y CONFIG_NET_CLS_ACT=y +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y @@ -253,7 +259,16 @@ CONFIG_SPI_DEBUG=y CONFIG_SPI_SPIDEV=y CONFIG_PINCTRL_QCS405=y CONFIG_GPIOLIB=y +CONFIG_POWER_SUPPLY=y CONFIG_THERMAL=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_FAN53555=y @@ -297,36 +312,62 @@ CONFIG_USB_STORAGE_ALAUDA=y CONFIG_USB_STORAGE_KARMA=y CONFIG_USB_STORAGE_CYPRESS_ATACB=y CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y CONFIG_USB_SERIAL=y CONFIG_USB_EHSET_TEST_FIXTURE=y CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_QCOM_EMU_PHY=y CONFIG_DUAL_ROLE_USB_INTF=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_DEBUG_FILES=y CONFIG_USB_GADGET_DEBUG_FS=y CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_DIAG=y CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y CONFIG_MMC_TEST=m +CONFIG_MMC_RING_BUFFER=y CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y CONFIG_RTC_CLASS=y CONFIG_DMADEVICES=y CONFIG_UIO=y CONFIG_STAGING=y CONFIG_ASHMEM=y CONFIG_ION=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_QCOM_CLK_SMD_RPM=y +CONFIG_MDM_GCC_QCS405=y CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y CONFIG_MAILBOX=y +CONFIG_QCOM_APCS_IPC=y CONFIG_QCOM_LAZY_MAPPING=y CONFIG_IOMMU_DEBUG=y CONFIG_QCOM_IOMMU=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_SMD=y +CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y +CONFIG_QCOM_SMD_RPM=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_SMP2P=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_QCOM_GLINK=y +CONFIG_QCOM_GLINK_PKT=y +# CONFIG_MSM_JTAGV8 is not set CONFIG_PWM=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y @@ -378,11 +419,16 @@ CONFIG_IPC_LOGGING=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_LKDTM=y CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y CONFIG_CORESIGHT_STM=y CONFIG_CORESIGHT_CTI=y CONFIG_CORESIGHT_TPDA=y CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 CONFIG_CORESIGHT_EVENT=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 2620ce790db0afaa18ed4d51eca2d886a21b00a8..371fca4e1ab7deafe2d8aed06ac5fd4092c7a8e4 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -57,6 +57,7 @@ CONFIG_MTD_M25P80=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_DENALI_DT=y CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set CONFIG_SPI_CADENCE_QUADSPI=y CONFIG_OF_OVERLAY=y CONFIG_OF_CONFIGFS=y diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile index a758107c552573583677b87c4a3cb751c3239498..3304e671918d66f6308580eb51bc2cc07647ad94 100644 --- a/arch/arm/crypto/Makefile +++ b/arch/arm/crypto/Makefile @@ -56,6 +56,7 @@ crc32-arm-ce-y:= crc32-ce-core.o crc32-ce-glue.o chacha20-neon-y := chacha20-neon-core.o chacha20-neon-glue.o speck-neon-y := speck-neon-core.o speck-neon-glue.o +ifdef REGENERATE_ARM_CRYPTO quiet_cmd_perl = PERL $@ cmd_perl = $(PERL) $(<) > $(@) @@ -64,5 +65,6 @@ $(src)/sha256-core.S_shipped: $(src)/sha256-armv4.pl $(src)/sha512-core.S_shipped: $(src)/sha512-armv4.pl $(call cmd,perl) +endif .PRECIOUS: $(obj)/sha256-core.S $(obj)/sha512-core.S diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 74504b154256e36ff4897ed1c7df43eb4d91bdef..f7ba4389f0fa73a3a2f2b89b2b61ea2c51718653 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -94,6 +94,21 @@ * DMA Cache Coherency * =================== * + * dma_inv_range(start, end) + * + * Invalidate (discard) the specified virtual address range. + * May not write back any entries. If 'start' or 'end' + * are not cache line aligned, those lines must be written + * back. + * - start - virtual start address + * - end - virtual end address + * + * dma_clean_range(start, end) + * + * Clean (write back) the specified virtual address range. + * - start - virtual start address + * - end - virtual end address + * * dma_flush_range(start, end) * * Clean and invalidate the specified virtual address range. @@ -115,6 +130,8 @@ struct cpu_cache_fns { void (*dma_map_area)(const void *, size_t, int); void (*dma_unmap_area)(const void *, size_t, int); + void (*dma_inv_range)(const void *, const void *); + void (*dma_clean_range)(const void *, const void *); void (*dma_flush_range)(const void *, const void *); } __no_randomize_layout; @@ -140,6 +157,8 @@ extern struct cpu_cache_fns cpu_cache; * is visible to DMA, or data written by DMA to system memory is * visible to the CPU. */ +#define dmac_inv_range cpu_cache.dma_inv_range +#define dmac_clean_range cpu_cache.dma_clean_range #define dmac_flush_range cpu_cache.dma_flush_range #else @@ -159,6 +178,8 @@ extern void __cpuc_flush_dcache_area(void *, size_t); * is visible to DMA, or data written by DMA to system memory is * visible to the CPU. */ +extern void dmac_inv_range(const void *start, const void *end); +extern void dmac_clean_range(const void *start, const void *end); extern void dmac_flush_range(const void *, const void *); #endif diff --git a/arch/arm/include/asm/glue-cache.h b/arch/arm/include/asm/glue-cache.h index 01c3d92624e5ed3e0035c4305ca5b6797ba1888b..d14f31047a5c75442c69a238b21b4e714c15ddb2 100644 --- a/arch/arm/include/asm/glue-cache.h +++ b/arch/arm/include/asm/glue-cache.h @@ -155,6 +155,8 @@ static inline void nop_dma_unmap_area(const void *s, size_t l, int f) { } #define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area) #define dmac_flush_range __glue(_CACHE,_dma_flush_range) +#define dmac_inv_range __glue(_CACHE, _dma_inv_range) +#define dmac_clean_range __glue(_CACHE, _dma_clean_range) #endif #endif diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 31fbb9285f62a8504eb4393ca7ea66ba385783a2..8f973e3b7348e4aaac5bc15c9497d87e8d543790 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -75,6 +75,9 @@ struct kvm_arch { /* Interrupt controller */ struct vgic_dist vgic; int max_vcpus; + + /* Mandated version of PSCI */ + u32 psci_version; }; #define KVM_NR_MEM_OBJS 40 diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h index 1f57bbe82b6fb8582c2a3a1617345266c22e33e8..df24fc8da1bc22e1a437a64dcf36a91f842f9424 100644 --- a/arch/arm/include/uapi/asm/kvm.h +++ b/arch/arm/include/uapi/asm/kvm.h @@ -180,6 +180,12 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_VFP_FPINST 0x1009 #define KVM_REG_ARM_VFP_FPINST2 0x100A +/* KVM-as-firmware specific pseudo-registers */ +#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_FW | ((r) & 0xffff)) +#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) + /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 diff --git a/arch/arm/kernel/io.c b/arch/arm/kernel/io.c index 60b621295d6cbee114dedb1ce710086178d656ea..a20e48c50d85f33b06b01f92d40fd6abee8d1c29 100644 --- a/arch/arm/kernel/io.c +++ b/arch/arm/kernel/io.c @@ -4,6 +4,8 @@ #include #include +#define IO_CHECK_ALIGN(v, a) ((((unsigned long)(v)) & ((a) - 1)) == 0) + static DEFINE_RAW_SPINLOCK(__io_lock); /* @@ -40,46 +42,90 @@ EXPORT_SYMBOL(atomic_io_modify); /* * Copy data from IO memory space to "real" memory space. - * This needs to be optimized. */ void _memcpy_fromio(void *to, const volatile void __iomem *from, size_t count) { - unsigned char *t = to; - while (count) { + while (count && (!IO_CHECK_ALIGN(from, 8) || !IO_CHECK_ALIGN(to, 8))) { + *(u8 *)to = readb_relaxed_no_log(from); + from++; + to++; count--; - *t = readb(from); - t++; + } + + while (count >= 8) { + *(u64 *)to = readq_relaxed_no_log(from); + from += 8; + to += 8; + count -= 8; + } + + while (count) { + *(u8 *)to = readb_relaxed_no_log(from); from++; + to++; + count--; } } EXPORT_SYMBOL(_memcpy_fromio); /* * Copy data from "real" memory space to IO memory space. - * This needs to be optimized. */ void _memcpy_toio(volatile void __iomem *to, const void *from, size_t count) { - const unsigned char *f = from; + void *p = (void __force *)to; + + while (count && (!IO_CHECK_ALIGN(p, 8) || !IO_CHECK_ALIGN(from, 8))) { + writeb_relaxed_no_log(*(volatile u8 *)from, p); + from++; + p++; + count--; + } + + while (count >= 8) { + writeq_relaxed_no_log(*(volatile u64 *)from, p); + from += 8; + p += 8; + count -= 8; + } + while (count) { + writeb_relaxed_no_log(*(volatile u8 *)from, p); + from++; + p++; count--; - writeb(*f, to); - f++; - to++; } } EXPORT_SYMBOL(_memcpy_toio); /* * "memset" on IO memory space. - * This needs to be optimized. */ void _memset_io(volatile void __iomem *dst, int c, size_t count) { + void *p = (void __force *)dst; + u64 qc = c; + + qc |= qc << 8; + qc |= qc << 16; + qc |= qc << 32; + + while (count && !IO_CHECK_ALIGN(p, 8)) { + writeb_relaxed_no_log(c, p); + p++; + count--; + } + + while (count >= 8) { + writeq_relaxed_no_log(qc, p); + p += 8; + count -= 8; + } + while (count) { + writeb_relaxed_no_log(c, p); + p++; count--; - writeb(c, dst); - dst++; } } EXPORT_SYMBOL(_memset_io); diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index b07664f750eae8757a4186798d024f3995b6de81..02f9924de7f2e4aa4af675efdd0165504bcc6285 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -167,77 +167,6 @@ static void show_extra_register_data(struct pt_regs *regs, int nbytes) set_fs(fs); } -/* - * dump a block of kernel memory from around the given address - */ -static void show_data(unsigned long addr, int nbytes, const char *name) -{ - int i, j; - int nlines; - u32 *p; - - /* - * don't attempt to dump non-kernel addresses or - * values that are probably just small negative numbers - */ - if (addr < PAGE_OFFSET || addr > -256UL) - return; - - printk("\n%s: %#lx:\n", name, addr); - - /* - * round address down to a 32 bit boundary - * and always dump a multiple of 32 bytes - */ - p = (u32 *)(addr & ~(sizeof(u32) - 1)); - nbytes += (addr & (sizeof(u32) - 1)); - nlines = (nbytes + 31) / 32; - - - for (i = 0; i < nlines; i++) { - /* - * just display low 16 bits of address to keep - * each line of the dump < 80 characters - */ - printk("%04lx ", (unsigned long)p & 0xffff); - for (j = 0; j < 8; j++) { - u32 data; - if (probe_kernel_address(p, data)) { - printk(" ********"); - } else { - printk(" %08x", data); - } - ++p; - } - printk("\n"); - } -} - -static void show_extra_register_data(struct pt_regs *regs, int nbytes) -{ - mm_segment_t fs; - - fs = get_fs(); - set_fs(KERNEL_DS); - show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC"); - show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR"); - show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP"); - show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP"); - show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP"); - show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0"); - show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1"); - show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2"); - show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3"); - show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4"); - show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5"); - show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6"); - show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7"); - show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8"); - show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9"); - show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10"); - set_fs(fs); -} - void __show_regs(struct pt_regs *regs) { unsigned long flags; diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 6fdaf0c062228a40d02b225ba3234247b0225a47..f1409ecb0920e446c215315bb6722cd45c0460b5 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -673,6 +673,8 @@ void handle_IPI(int ipinr, struct pt_regs *regs) if ((unsigned)ipinr < NR_IPI) trace_ipi_exit_rcuidle(ipi_types[ipinr]); + + per_cpu(pending_ipi, cpu) = false; set_irq_regs(old_regs); } diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c index 1e0784ebbfd6db77595652961ad3c48c41fceabe..a18f33edc471a92fcf6194dbe6601bc4b94f1988 100644 --- a/arch/arm/kvm/guest.c +++ b/arch/arm/kvm/guest.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +177,7 @@ static unsigned long num_core_regs(void) unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu) { return num_core_regs() + kvm_arm_num_coproc_regs(vcpu) + + kvm_arm_get_fw_num_regs(vcpu) + NUM_TIMER_REGS; } @@ -196,6 +198,11 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) uindices++; } + ret = kvm_arm_copy_fw_reg_indices(vcpu, uindices); + if (ret) + return ret; + uindices += kvm_arm_get_fw_num_regs(vcpu); + ret = copy_timer_indices(vcpu, uindices); if (ret) return ret; @@ -214,6 +221,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return get_core_reg(vcpu, reg); + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW) + return kvm_arm_get_fw_reg(vcpu, reg); + if (is_timer_reg(reg->id)) return get_timer_reg(vcpu, reg); @@ -230,6 +240,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return set_core_reg(vcpu, reg); + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW) + return kvm_arm_set_fw_reg(vcpu, reg); + if (is_timer_reg(reg->id)) return set_timer_reg(vcpu, reg); diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 1a7e5b5d08d83234ff58a6a75d6e0c777f0be9c6..3dbbf1fffeaddc17e8f0a254671f7f44bfdd817c 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -276,11 +276,7 @@ static int exynos_cpu0_enter_aftr(void) goto fail; call_firmware_op(cpu_boot, 1); - - if (soc_is_exynos3250()) - dsb_sev(); - else - arch_send_wakeup_ipi_mask(cpumask_of(1)); + dsb_sev(); } } fail: diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index de78109d002db1a5e7c94a6c1bc8bb94161d07b8..4c850aa3af2b2439fced4e130441329a724fb370 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -349,7 +349,7 @@ ENDPROC(v7_flush_kern_dcache_area) * - start - virtual start address of region * - end - virtual end address of region */ -v7_dma_inv_range: +ENTRY(v7_dma_inv_range) dcache_line_size r2, r3 sub r3, r2, #1 tst r0, r3 @@ -377,7 +377,7 @@ ENDPROC(v7_dma_inv_range) * - start - virtual start address of region * - end - virtual end address of region */ -v7_dma_clean_range: +ENTRY(v7_dma_clean_range) dcache_line_size r2, r3 sub r3, r2, #1 bic r0, r0, r3 diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f002379d4837e9728c243e9cd3597004b76a7908..0b499a7773ca801717e80edf9cfe2ac1cbae8222 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -487,6 +487,15 @@ void __init dma_contiguous_remap(void) struct map_desc map; unsigned long addr; + /* + * Make start and end PMD_SIZE aligned, observing memory + * boundaries + */ + if (memblock_is_memory(start & PMD_MASK)) + start = start & PMD_MASK; + if (memblock_is_memory(ALIGN(end, PMD_SIZE))) + end = ALIGN(end, PMD_SIZE); + if (end > arm_lowmem_limit) end = arm_lowmem_limit; if (start >= end) @@ -507,8 +516,13 @@ void __init dma_contiguous_remap(void) * and ensures that this code is architecturally compliant. */ for (addr = __phys_to_virt(start); addr < __phys_to_virt(end); - addr += PMD_SIZE) - pmd_clear(pmd_off_k(addr)); + addr += PMD_SIZE) { + pmd_t *pmd; + + pmd = pmd_off_k(addr); + if (pmd_bad(*pmd)) + pmd_clear(pmd); + } flush_tlb_kernel_range(__phys_to_virt(start), __phys_to_virt(end)); @@ -648,9 +662,14 @@ static void __free_from_contiguous(struct device *dev, struct page *page, static inline pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot) { - prot = (attrs & DMA_ATTR_WRITE_COMBINE) ? - pgprot_writecombine(prot) : - pgprot_dmacoherent(prot); + if (attrs & DMA_ATTR_WRITE_COMBINE) + prot = pgprot_writecombine(prot); + else if (attrs & DMA_ATTR_STRONGLY_ORDERED) + prot = pgprot_stronglyordered(prot); + /* if non-consistent just pass back what was given */ + else if ((attrs & DMA_ATTR_NON_CONSISTENT) == 0) + prot = pgprot_dmacoherent(prot); + return prot; } @@ -1824,7 +1843,31 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg, int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs) { - return __iommu_map_sg(dev, sg, nents, dir, attrs, false); + struct scatterlist *s; + int i; + size_t ret; + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + unsigned int total_length = 0, current_offset = 0; + dma_addr_t iova; + int prot = __dma_info_to_prot(dir, attrs); + + for_each_sg(sg, s, nents, i) + total_length += s->length; + + iova = __alloc_iova(mapping, total_length); + ret = iommu_map_sg(mapping->domain, iova, sg, nents, prot); + if (ret != total_length) { + __free_iova(mapping, iova, total_length); + return 0; + } + + for_each_sg(sg, s, nents, i) { + s->dma_address = iova + current_offset; + s->dma_length = total_length - current_offset; + current_offset += s->length; + } + + return nents; } static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg, @@ -1875,7 +1918,15 @@ void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs) { - __iommu_unmap_sg(dev, sg, nents, dir, attrs, false); + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + unsigned int total_length = sg_dma_len(sg); + dma_addr_t iova = sg_dma_address(sg); + + total_length = PAGE_ALIGN((iova & ~PAGE_MASK) + total_length); + iova &= PAGE_MASK; + + iommu_unmap(mapping->domain, iova, total_length); + __free_iova(mapping, iova, total_length); } /** @@ -1985,9 +2036,6 @@ static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle, int offset = handle & ~PAGE_MASK; int len = PAGE_ALIGN(size + offset); - if (!iova) - return; - iommu_unmap(mapping->domain, iova, len); __free_iova(mapping, iova, len); } @@ -2085,9 +2133,6 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev, struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); unsigned int offset = handle & ~PAGE_MASK; - if (!iova) - return; - __dma_page_dev_to_cpu(page, offset, size, dir); } @@ -2099,9 +2144,6 @@ static void arm_iommu_sync_single_for_device(struct device *dev, struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); unsigned int offset = handle & ~PAGE_MASK; - if (!iova) - return; - __dma_page_cpu_to_dev(page, offset, size, dir); } @@ -2176,6 +2218,9 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size) if (!bitmap_size) return ERR_PTR(-EINVAL); + WARN(!IS_ALIGNED(size, SZ_128M), + "size is not aligned to 128M, alignment enforced"); + if (bitmap_size > PAGE_SIZE) { extensions = bitmap_size / PAGE_SIZE; bitmap_size = PAGE_SIZE; @@ -2290,12 +2335,16 @@ int arm_iommu_attach_device(struct device *dev, struct dma_iommu_mapping *mapping) { int err; + int s1_bypass = 0; err = __arm_iommu_attach_device(dev, mapping); if (err) return err; - set_dma_ops(dev, &iommu_ops); + iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS, + &s1_bypass); + if (!s1_bypass) + set_dma_ops(dev, &iommu_ops); return 0; } EXPORT_SYMBOL_GPL(arm_iommu_attach_device); @@ -2310,6 +2359,7 @@ EXPORT_SYMBOL_GPL(arm_iommu_attach_device); void arm_iommu_detach_device(struct device *dev) { struct dma_iommu_mapping *mapping; + int s1_bypass = 0; mapping = to_dma_iommu_mapping(dev); if (!mapping) { @@ -2320,7 +2370,8 @@ void arm_iommu_detach_device(struct device *dev) iommu_detach_device(mapping->domain, dev); kref_put(&mapping->kref, release_iommu_mapping); to_dma_iommu_mapping(dev) = NULL; - set_dma_ops(dev, NULL); + if (!s1_bypass) + set_dma_ops(dev, NULL); pr_debug("Detached IOMMU controller from %s device.\n", dev_name(dev)); } diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 0f6d1537f3301d33a53e8a53aab89dfe6f78982f..8902e2d3e217948f38f879589a6915ee4ecef6a9 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -600,6 +600,9 @@ struct section_perm { pmdval_t mask; pmdval_t prot; pmdval_t clear; + pteval_t ptemask; + pteval_t pteprot; + pteval_t pteclear; }; /* First section-aligned location at or after __start_rodata. */ @@ -613,6 +616,8 @@ static struct section_perm nx_perms[] = { .end = (unsigned long)_stext, .mask = ~PMD_SECT_XN, .prot = PMD_SECT_XN, + .ptemask = ~L_PTE_XN, + .pteprot = L_PTE_XN, }, /* Make init RW (set NX). */ { @@ -621,6 +626,8 @@ static struct section_perm nx_perms[] = { .end = (unsigned long)_sdata, .mask = ~PMD_SECT_XN, .prot = PMD_SECT_XN, + .ptemask = ~L_PTE_XN, + .pteprot = L_PTE_XN, }, /* Make rodata NX (set RO in ro_perms below). */ { @@ -629,6 +636,8 @@ static struct section_perm nx_perms[] = { .end = (unsigned long)__init_begin, .mask = ~PMD_SECT_XN, .prot = PMD_SECT_XN, + .ptemask = ~L_PTE_XN, + .pteprot = L_PTE_XN, }, }; @@ -646,6 +655,8 @@ static struct section_perm ro_perms[] = { .prot = PMD_SECT_APX | PMD_SECT_AP_WRITE, .clear = PMD_SECT_AP_WRITE, #endif + .ptemask = ~L_PTE_RDONLY, + .pteprot = L_PTE_RDONLY, }, }; @@ -654,6 +665,35 @@ static struct section_perm ro_perms[] = { * copied into each mm). During startup, this is the init_mm. Is only * safe to be called with preemption disabled, as under stop_machine(). */ +struct pte_data { + pteval_t mask; + pteval_t val; +}; + +static int __pte_update(pte_t *ptep, pgtable_t token, unsigned long addr, + void *d) +{ + struct pte_data *data = d; + pte_t pte = *ptep; + + pte = __pte((pte_val(*ptep) & data->mask) | data->val); + set_pte_ext(ptep, pte, 0); + + return 0; +} + +static inline void pte_update(unsigned long addr, pteval_t mask, + pteval_t prot, struct mm_struct *mm) +{ + struct pte_data data; + + data.mask = mask; + data.val = prot; + + apply_to_page_range(mm, addr, SECTION_SIZE, __pte_update, &data); + flush_tlb_kernel_range(addr, addr + SECTION_SIZE); +} + static inline void section_update(unsigned long addr, pmdval_t mask, pmdval_t prot, struct mm_struct *mm) { @@ -702,11 +742,21 @@ void set_section_perms(struct section_perm *perms, int n, bool set, for (addr = perms[i].start; addr < perms[i].end; - addr += SECTION_SIZE) - section_update(addr, perms[i].mask, - set ? perms[i].prot : perms[i].clear, mm); + addr += SECTION_SIZE) { + pmd_t *pmd; + + pmd = pmd_offset(pud_offset(pgd_offset(mm, addr), + addr), addr); + if (pmd_bad(*pmd)) + section_update(addr, perms[i].mask, + set ? perms[i].prot : perms[i].clear, + mm); + else + pte_update(addr, perms[i].ptemask, + set ? perms[i].pteprot : perms[i].pteclear, + mm); + } } - } /** diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e46a6a446cdd27126869bb97574d5bf51075e9e4..e9597cfa03cd95e3caf93105b135757a04453bdd 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -1586,6 +1586,119 @@ static void __init early_paging_init(const struct machine_desc *mdesc) #endif +#ifdef CONFIG_FORCE_PAGES +/* + * remap a PMD into pages + * We split a single pmd here none of this two pmd nonsense + */ +static noinline void __init split_pmd(pmd_t *pmd, unsigned long addr, + unsigned long end, unsigned long pfn, + const struct mem_type *type) +{ + pte_t *pte, *start_pte; + pmd_t *base_pmd; + + base_pmd = pmd_offset( + pud_offset(pgd_offset(&init_mm, addr), addr), addr); + + if (pmd_none(*base_pmd) || pmd_bad(*base_pmd)) { + start_pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE); +#ifndef CONFIG_ARM_LPAE + /* + * Following is needed when new pte is allocated for pmd[1] + * cases, which may happen when base (start) address falls + * under pmd[1]. + */ + if (addr & SECTION_SIZE) + start_pte += pte_index(addr); +#endif + } else { + start_pte = pte_offset_kernel(base_pmd, addr); + } + + pte = start_pte; + + do { + set_pte_ext(pte, pfn_pte(pfn, type->prot_pte), 0); + pfn++; + } while (pte++, addr += PAGE_SIZE, addr != end); + + *pmd = __pmd((__pa(start_pte) + PTE_HWTABLE_OFF) | type->prot_l1); + mb(); /* let pmd be programmed */ + flush_pmd_entry(pmd); + flush_tlb_all(); +} + +/* + * It's significantly easier to remap as pages later after all memory is + * mapped. Everything is sections so all we have to do is split + */ +static void __init remap_pages(void) +{ + struct memblock_region *reg; + + for_each_memblock(memory, reg) { + phys_addr_t phys_start = reg->base; + phys_addr_t phys_end = reg->base + reg->size; + unsigned long addr = (unsigned long)__va(phys_start); + unsigned long end = (unsigned long)__va(phys_end); + pmd_t *pmd = NULL; + unsigned long next; + unsigned long pfn = __phys_to_pfn(phys_start); + bool fixup = false; + unsigned long saved_start = addr; + + if (phys_start > arm_lowmem_limit) + break; + if (phys_end > arm_lowmem_limit) + end = (unsigned long)__va(arm_lowmem_limit); + if (phys_start >= phys_end) + break; + + pmd = pmd_offset( + pud_offset(pgd_offset(&init_mm, addr), addr), addr); + +#ifndef CONFIG_ARM_LPAE + if (addr & SECTION_SIZE) { + fixup = true; + pmd_empty_section_gap((addr - SECTION_SIZE) & PMD_MASK); + pmd++; + } + + if (end & SECTION_SIZE) + pmd_empty_section_gap(end); +#endif + + do { + next = addr + SECTION_SIZE; + + if (pmd_none(*pmd) || pmd_bad(*pmd)) + split_pmd(pmd, addr, next, pfn, + &mem_types[MT_MEMORY_RWX]); + pmd++; + pfn += SECTION_SIZE >> PAGE_SHIFT; + + } while (addr = next, addr < end); + + if (fixup) { + /* + * Put a faulting page table here to avoid detecting no + * pmd when accessing an odd section boundary. This + * needs to be faulting to help catch errors and avoid + * speculation + */ + pmd = pmd_off_k(saved_start); + pmd[0] = pmd[1] & ~1; + } + } +} +#else +static void __init remap_pages(void) +{ + +} +#endif + static void __init early_fixmap_shutdown(void) { int i; @@ -1595,11 +1708,13 @@ static void __init early_fixmap_shutdown(void) pmd_clear(fixmap_pmd(va)); local_flush_tlb_kernel_page(va); + BUILD_BUG_ON(__end_of_permanent_fixed_addresses > + __end_of_fixed_addresses); for (i = 0; i < __end_of_permanent_fixed_addresses; i++) { pte_t *pte; struct map_desc map; - map.virtual = fix_to_virt(i); + map.virtual = __fix_to_virt(i); pte = pte_offset_early_fixmap(pmd_off_k(map.virtual), map.virtual); /* Only i/o device mappings are supported ATM */ @@ -1628,6 +1743,7 @@ void __init paging_init(const struct machine_desc *mdesc) memblock_set_current_limit(arm_lowmem_limit); dma_contiguous_remap(); early_fixmap_shutdown(); + remap_pages(); devicemaps_init(mdesc); kmap_init(); tcm_init(); diff --git a/arch/arm/mm/pageattr.c b/arch/arm/mm/pageattr.c index 1403cb4a0c3da8c3be996722739ae6c0c9e9c3a1..365dc9d7756cf8ec67e5618474822c76f2e542f4 100644 --- a/arch/arm/mm/pageattr.c +++ b/arch/arm/mm/pageattr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014,2017-2018 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,7 +56,8 @@ static int change_memory_common(unsigned long addr, int numpages, if (!size) return 0; - if (!in_range(start, size, MODULES_VADDR, MODULES_END) && + if (!IS_ENABLED(CONFIG_FORCE_PAGES) && + !in_range(start, size, MODULES_VADDR, MODULES_END) && !in_range(start, size, VMALLOC_START, VMALLOC_END)) return -EINVAL; @@ -97,3 +98,19 @@ int set_memory_x(unsigned long addr, int numpages) __pgprot(0), __pgprot(L_PTE_XN)); } + +#ifdef CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC +void __kernel_map_pages(struct page *page, int numpages, int enable) +{ + unsigned long addr; + + if (PageHighMem(page)) + return; + + addr = (unsigned long) page_address(page); + if (enable) + set_memory_rw(addr, numpages); + else + set_memory_ro(addr, numpages); +} +#endif diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S index f10e31d0730afa572cb6604d65813eb6df1dcfa9..841614a834313da599d6c32ad69204e6f6f08a40 100644 --- a/arch/arm/mm/proc-macros.S +++ b/arch/arm/mm/proc-macros.S @@ -324,6 +324,8 @@ ENTRY(\name\()_cache_fns) .long \name\()_flush_kern_dcache_area .long \name\()_dma_map_area .long \name\()_dma_unmap_area + .long \name\()_dma_inv_range + .long \name\()_dma_clean_range .long \name\()_dma_flush_range .size \name\()_cache_fns, . - \name\()_cache_fns .endm diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c index 054b491ff7649ca067ff821770aec80a4da42102..70e8b7d3443467ae9595924f1a9d043b984e2c93 100644 --- a/arch/arm/mm/proc-syms.c +++ b/arch/arm/mm/proc-syms.c @@ -30,6 +30,9 @@ EXPORT_SYMBOL(__cpuc_flush_user_all); EXPORT_SYMBOL(__cpuc_flush_user_range); EXPORT_SYMBOL(__cpuc_coherent_kern_range); EXPORT_SYMBOL(__cpuc_flush_dcache_area); +EXPORT_SYMBOL(dmac_inv_range); +EXPORT_SYMBOL(dmac_clean_range); +EXPORT_SYMBOL(dmac_flush_range); #else EXPORT_SYMBOL(cpu_cache); #endif diff --git a/arch/arm/plat-omap/include/plat/sram.h b/arch/arm/plat-omap/include/plat/sram.h index fb061cf0d736d3203cf670d366cab227776e8264..30a07730807a86b2f5440702d17094b2f6a4bb26 100644 --- a/arch/arm/plat-omap/include/plat/sram.h +++ b/arch/arm/plat-omap/include/plat/sram.h @@ -5,13 +5,4 @@ void omap_map_sram(unsigned long start, unsigned long size, unsigned long skip, int cached); void omap_sram_reset(void); -extern void *omap_sram_push_address(unsigned long size); - -/* Macro to push a function to the internal SRAM, using the fncpy API */ -#define omap_sram_push(funcp, size) ({ \ - typeof(&(funcp)) _res = NULL; \ - void *_sram_address = omap_sram_push_address(size); \ - if (_sram_address) \ - _res = fncpy(_sram_address, &(funcp), size); \ - _res; \ -}) +extern void *omap_sram_push(void *funcp, unsigned long size); diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index a5bc92d7e4765b81315379c41618fff7f84cbec2..921840acf65c83a5ac785ac5e514591a404a8a46 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -42,7 +43,7 @@ static void __iomem *omap_sram_ceil; * Note that fncpy requires the returned address to be aligned * to an 8-byte boundary. */ -void *omap_sram_push_address(unsigned long size) +static void *omap_sram_push_address(unsigned long size) { unsigned long available, new_ceil = (unsigned long)omap_sram_ceil; @@ -60,6 +61,30 @@ void *omap_sram_push_address(unsigned long size) return (void *)omap_sram_ceil; } +void *omap_sram_push(void *funcp, unsigned long size) +{ + void *sram; + unsigned long base; + int pages; + void *dst = NULL; + + sram = omap_sram_push_address(size); + if (!sram) + return NULL; + + base = (unsigned long)sram & PAGE_MASK; + pages = PAGE_ALIGN(size) / PAGE_SIZE; + + set_memory_rw(base, pages); + + dst = fncpy(sram, funcp, size); + + set_memory_ro(base, pages); + set_memory_x(base, pages); + + return dst; +} + /* * The SRAM context is lost during off-idle and stack * needs to be reset. @@ -75,6 +100,9 @@ void omap_sram_reset(void) void __init omap_map_sram(unsigned long start, unsigned long size, unsigned long skip, int cached) { + unsigned long base; + int pages; + if (size == 0) return; @@ -95,4 +123,10 @@ void __init omap_map_sram(unsigned long start, unsigned long size, */ memset_io(omap_sram_base + omap_sram_skip, 0, omap_sram_size - omap_sram_skip); + + base = (unsigned long)omap_sram_base; + pages = PAGE_ALIGN(omap_sram_size) / PAGE_SIZE; + + set_memory_ro(base, pages); + set_memory_x(base, pages); } diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index a71a48e71fffa8626fe90106815376c44bbe679b..aa7496be311dfcd09f955ffd05b3297adde123e1 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -648,7 +648,7 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp, */ static int vfp_dying_cpu(unsigned int cpu) { - vfp_force_reload(cpu, current_thread_info()); + vfp_current_hw_state[cpu] = NULL; return 0; } diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4bd2419ed06ab92f630d345b93262e672939d471..57eeeaff9a6f2046e197dbc3fce1ddda7d1b177b 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -24,6 +24,7 @@ config ARM64 select ARCH_HAVE_NMI_SAFE_CMPXCHG if ACPI_APEI_SEA select ARCH_USE_CMPXCHG_LOCKREF select ARCH_SUPPORTS_MEMORY_FAILURE + select ARCH_SUPPORTS_LTO_CLANG select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_NUMA_BALANCING select ARCH_WANT_COMPAT_IPC_PARSE_VERSION @@ -433,7 +434,7 @@ config ARM64_ERRATUM_845719 config ARM64_ERRATUM_843419 bool "Cortex-A53: 843419: A load or store might access an incorrect address" - default y + default y if !LTO_CLANG select ARM64_MODULE_CMODEL_LARGE if MODULES help This option links the kernel with '--fix-cortex-a53-843419' and @@ -644,6 +645,13 @@ config HOTPLUG_CPU Say Y here to experiment with turning CPUs off and on. CPUs can be controlled through /sys/devices/system/cpu. +config ARCH_ENABLE_MEMORY_HOTPLUG + depends on !NUMA + def_bool y + +config ARCH_ENABLE_MEMORY_HOTREMOVE + def_bool y + # The GPIO number here must be sorted by descending number. In case of # a multiplatform kernel, we just want the highest value required by the # selected platforms. @@ -761,6 +769,10 @@ config ARM64_DMA_IOMMU_ALIGNMENT endif +config ARCH_MEMORY_PROBE + def_bool y + depends on MEMORY_HOTPLUG + config SECCOMP bool "Enable seccomp to safely compute untrusted bytecode" ---help--- @@ -878,6 +890,15 @@ config HARDEN_BRANCH_PREDICTOR If unsure, say Y. +config PRINT_VMEMLAYOUT + bool "Enable debug option to print kernel virtual memory layout" + help + Enable support for printing kernel virtual memory layout for debugging + purpose. If disabled kernel would not show any information about + virtual memory layout during boot up. + + If unsure, say N. + menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" depends on COMPAT @@ -1090,7 +1111,7 @@ config RANDOMIZE_BASE config RANDOMIZE_MODULE_REGION_FULL bool "Randomize the module region independently from the core kernel" - depends on RANDOMIZE_BASE + depends on RANDOMIZE_BASE && !LTO_CLANG default y help Randomizes the location of the module region without considering the diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index e393bbcd8b917fee2929ef0da6c29a5aa3db069d..b5f07163814826c11c389a079469ddff8bbe1526 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -159,12 +159,12 @@ config ARCH_SDMSHRIKE If you do not wish to build a kernel that runs on this chipset, say 'N' here. -config ARCH_SDM640 - bool "Enable Support for Qualcomm Technologies, Inc. SDM640" +config ARCH_SM6150 + bool "Enable Support for Qualcomm Technologies, Inc. SM6150" depends on ARCH_QCOM select COMMON_CLK_QCOM help - This enables support for the SDM640 chipset. If you do not + This enables support for the SM6150 chipset. If you do not wish to build a kernel that runs on this chipset, say 'N' here. config ARCH_QCS405 diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 5547eb5a5239370d16690061d6b5ffed08c78c1f..5f0ebc02e257c88497db68c928ecd14d2b0fb5d3 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -26,8 +26,17 @@ ifeq ($(CONFIG_ARM64_ERRATUM_843419),y) ifeq ($(call ld-option, --fix-cortex-a53-843419),) $(warning ld does not support --fix-cortex-a53-843419; kernel may be susceptible to erratum) else + ifeq ($(call gold-ifversion, -lt, 114000000, y), y) +$(warning This version of GNU gold may generate incorrect code with --fix-cortex-a53-843419;\ + see https://sourceware.org/bugzilla/show_bug.cgi?id=21491) + endif LDFLAGS_vmlinux += --fix-cortex-a53-843419 endif +else + ifeq ($(ld-name),gold) +# Pass --no-fix-cortex-a53-843419 to ensure the erratum fix is disabled +LDFLAGS += --no-fix-cortex-a53-843419 + endif endif KBUILD_DEFCONFIG := defconfig @@ -70,14 +79,22 @@ KBUILD_CPPFLAGS += -mbig-endian CHECKFLAGS += -D__AARCH64EB__ AS += -EB LD += -EB +ifeq ($(ld-name),gold) +LDFLAGS += -maarch64_elf64_be_vec +else LDFLAGS += -maarch64linuxb +endif UTS_MACHINE := aarch64_be else KBUILD_CPPFLAGS += -mlittle-endian CHECKFLAGS += -D__AARCH64EL__ AS += -EL LD += -EL +ifeq ($(ld-name),gold) +LDFLAGS += -maarch64_elf64_le_vec +else LDFLAGS += -maarch64linux +endif UTS_MACHINE := aarch64 endif @@ -85,6 +102,10 @@ CHECKFLAGS += -D__aarch64__ -m64 ifeq ($(CONFIG_ARM64_MODULE_CMODEL_LARGE), y) KBUILD_CFLAGS_MODULE += -mcmodel=large +ifeq ($(CONFIG_LTO_CLANG), y) +# Code model is not stored in LLVM IR, so we need to pass it also to LLVMgold +LDFLAGS += -plugin-opt=-code-model=large +endif endif ifeq ($(CONFIG_ARM64_MODULE_PLTS),y) diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index 83d3577b0b69d0b7be60d1a25af75d87cbc78a8b..831c7042fb4e6609bfe730707fc866d1d010a2f2 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -28,6 +28,17 @@ DTB_LIST := $(dtb-y) endif DTB_OBJS := $(shell find $(obj)/dts/ -name \*.dtb) +# Add RTIC DTB to the DTB list if RTIC MPGen is enabled +ifdef RTIC_MPGEN +DTB_OBJS += rtic_mp.dtb +endif + +rtic_mp.dtb: vmlinux FORCE + $(RTIC_MPGEN) --objcopy="${OBJCOPY}" --objdump="${OBJDUMP}" \ + --binpath="" --vmlinux="vmlinux" --config=${KCONFIG_CONFIG} \ + --cc="${CC}" --dts=rtic_mp.dts && \ + $(DTC) -O dtb -o rtic_mp.dtb -b 0 $(DTC_FLAGS) rtic_mp.dts + $(obj)/Image: vmlinux FORCE $(call if_changed,objcopy) diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts index 08b7bb7f5b74960b6801e94b91c7c827dccb5b44..c3c65b06ba76b738ace26a3d5949c874a91e6dd9 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts @@ -276,7 +276,7 @@ pinctrl-names = "default", "clk-gate"; bus-width = <8>; - max-frequency = <200000000>; + max-frequency = <100000000>; non-removable; disable-wp; cap-mmc-highspeed; diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 72bc792e22c7979b1b0807d970766c19d6f0d007..08947733d11e67651d389be2cb26c14d6c101d57 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -16,6 +16,10 @@ ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y) sm8150-mtp-overlay.dtbo \ sm8150-rumi-overlay.dtbo \ sm8150-qrd-overlay.dtbo \ + sm8150-auto-adp-star-overlay.dtbo \ + sm8150p-cdp-overlay.dtbo \ + sm8150p-mtp-overlay.dtbo \ + sm8150p-qrd-overlay.dtbo \ sm8150-sdx50m-cdp-overlay.dtbo \ sm8150-sdx50m-mtp-overlay.dtbo @@ -23,27 +27,59 @@ sm8150-cdp-overlay.dtbo-base := sm8150.dtb sm8150-v2.dtb sm8150-mtp-overlay.dtbo-base := sm8150.dtb sm8150-v2.dtb sm8150-rumi-overlay.dtbo-base := sm8150.dtb sm8150-v2.dtb sm8150-qrd-overlay.dtbo-base := sm8150.dtb sm8150-v2.dtb +sm8150-auto-adp-star-overlay.dtbo-base := sm8150-auto.dtb sm8150-sdx50m-cdp-overlay.dtbo-base := sm8150.dtb sm8150-v2.dtb sm8150-sdx50m-mtp-overlay.dtbo-base := sm8150.dtb sm8150-v2.dtb +sm8150p-mtp-overlay.dtbo-base := sm8150p.dtb sm8150p-v2.dtb +sm8150p-qrd-overlay.dtbo-base := sm8150p.dtb sm8150p-v2.dtb else dtb-$(CONFIG_ARCH_SM8150) += sm8150-rumi.dtb \ sm8150-mtp.dtb \ sm8150-cdp.dtb \ sm8150-qrd.dtb \ + sm8150-auto-adp-star.dtb \ sm8150-v2-rumi.dtb \ sm8150-v2-mtp.dtb \ sm8150-v2-cdp.dtb \ - sm8150-v2-qrd.dtb + sm8150-v2-qrd.dtb \ + sm8150p-mtp.dtb \ + sm8150p-cdp.dtb \ + sm8150p-qrd.dtb \ + sm8150p-v2-mtp.dtb \ + sm8150p-v2-cdp.dtb \ + sm8150p-v2-qrd.dtb endif +ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y) + dtbo-$(CONFIG_ARCH_SDMSHRIKE) += \ + sdmshrike-cdp-overlay.dtbo \ + sdmshrike-mtp-overlay.dtbo + +sdmshrike-cdp-overlay.dtbo-base := sdmshrike.dtb +sdmshrike-mtp-overlay.dtbo-base := sdmshrike.dtb +else dtb-$(CONFIG_ARCH_SDMSHRIKE) += sdmshrike-rumi.dtb \ sdmshrike-mtp.dtb \ sdmshrike-cdp.dtb +endif -dtb-$(CONFIG_ARCH_SDM640) += sdm640-rumi.dtb \ - sdm640-mtp.dtb \ - sdm640-cdp.dtb \ - sdm640-qrd.dtb +ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y) + dtbo-$(CONFIG_ARCH_SM6150) += \ + sm6150-cdp-overlay.dtbo \ + sm6150-mtp-overlay.dtbo \ + sm6150-rumi-overlay.dtbo \ + sm6150-qrd-overlay.dtbo \ + +sm6150-cdp-overlay.dtbo-base := sm6150.dtb +sm6150-mtp-overlay.dtbo-base := sm6150.dtb +sm6150-rumi-overlay.dtbo-base := sm6150.dtb +sm6150-qrd-overlay.dtbo-base := sm6150.dtb +else +dtb-$(CONFIG_ARCH_SM6150) += sm6150-rumi.dtb \ + sm6150-mtp.dtb \ + sm6150-cdp.dtb \ + sm6150-qrd.dtb +endif ifeq ($(CONFIG_ARM64),y) always := $(dtb-y) diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi index ce849c62e6cdfe3dca915d161605819fc8ec1715..92897aee929e341f131aab5463f08eb9a976e41b 100644 --- a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-cmd.dtsi @@ -39,6 +39,11 @@ qcom,dcs-cmd-by-left; qcom,mdss-dsi-tx-eot-append; qcom,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; qcom,mdss-dsi-display-timings { timing@0{ diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi index d3411c8ac65c41549c506a8ec1082aa5bad728f8..9995fb3e79bcde70b2ed1a9915155e01366c9597 100644 --- a/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sharp-dsc-4k-video.dtsi @@ -33,6 +33,11 @@ qcom,mdss-dsi-tx-eot-append; qcom,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000 + 17000 15500 30000 8000 3000>; + qcom,mdss-dsi-panel-peak-brightness = <4200000>; + qcom,mdss-dsi-panel-blackness-level = <3230>; qcom,mdss-dsi-display-timings { timing@0{ diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi index 50da1bf4ab2fa045ad3ea8b233da55ccedee0eb8..c49583e0d92a40b774c93f972830b8ceba2f9109 100644 --- a/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017-2018, 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 @@ -49,26 +49,22 @@ qcom,mdss-dsi-display-timings { timing@0{ - qcom,mdss-dsi-panel-width = <640>; - qcom,mdss-dsi-panel-height = <480>; - qcom,mdss-dsi-h-front-porch = <20>; - qcom,mdss-dsi-h-back-porch = <20>; - qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-panel-width = <1440>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <40>; qcom,mdss-dsi-h-sync-skew = <0>; - qcom,mdss-dsi-v-back-porch = <16>; - qcom,mdss-dsi-v-front-porch = <4>; - qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-v-back-porch = <100>; + qcom,mdss-dsi-v-front-porch = <100>; + qcom,mdss-dsi-v-pulse-width = <40>; qcom,mdss-dsi-h-left-border = <0>; qcom,mdss-dsi-h-right-border = <0>; qcom,mdss-dsi-v-top-border = <0>; qcom,mdss-dsi-v-bottom-border = <0>; - qcom,mdss-dsi-h-sync-pulse = <0>; qcom,mdss-dsi-panel-framerate = <60>; - qcom,mdss-dsi-hor-line-idle = <0 40 256>, - <40 120 128>, - <120 240 64>; qcom,mdss-dsi-panel-timings = - [cd 32 22 00 60 64 26 34 29 03 04 00]; + [00 21 09 09 24 23 08 08 08 03 04 00]; qcom,mdss-dsi-on-command = [29 01 00 00 00 00 02 b0 03 05 01 00 00 0a 00 01 00 @@ -98,6 +94,126 @@ [05 01 00 00 32 00 02 28 00 05 01 00 00 78 00 02 10 00]; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <40>; + qcom,mdss-dsc-slice-width = <720>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + + timing@1{ + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <460>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <100>; + qcom,mdss-dsi-v-front-porch = <740>; + qcom,mdss-dsi-v-pulse-width = <40>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = + [00 21 09 09 24 23 08 08 08 03 04 00]; + qcom,mdss-dsi-on-command = + [29 01 00 00 00 00 02 b0 03 + 05 01 00 00 0a 00 01 00 + /* Soft reset, wait 10ms */ + 15 01 00 00 0a 00 02 3a 77 + /* Set Pixel format (24 bpp) */ + 39 01 00 00 0a 00 05 2a 00 00 04 ff + /* Set Column address */ + 39 01 00 00 0a 00 05 2b 00 00 05 9f + /* Set page address */ + 15 01 00 00 0a 00 02 35 00 + /* Set tear on */ + 39 01 00 00 0a 00 03 44 00 00 + /* Set tear scan line */ + 15 01 00 00 0a 00 02 51 ff + /* write display brightness */ + 15 01 00 00 0a 00 02 53 24 + /* write control brightness */ + 15 01 00 00 0a 00 02 55 00 + /* CABC brightness */ + 05 01 00 00 78 00 01 11 + /* exit sleep mode, wait 120ms */ + 05 01 00 00 10 00 01 29]; + /* Set display on, wait 16ms */ + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 32 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <40>; + qcom,mdss-dsc-slice-width = <540>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + + timing@2{ + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1280>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <840>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <100>; + qcom,mdss-dsi-v-front-porch = <1380>; + qcom,mdss-dsi-v-pulse-width = <40>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = + [00 21 09 09 24 23 08 08 08 03 04 00]; + qcom,mdss-dsi-on-command = + [29 01 00 00 00 00 02 b0 03 + 05 01 00 00 0a 00 01 00 + /* Soft reset, wait 10ms */ + 15 01 00 00 0a 00 02 3a 77 + /* Set Pixel format (24 bpp) */ + 39 01 00 00 0a 00 05 2a 00 00 04 ff + /* Set Column address */ + 39 01 00 00 0a 00 05 2b 00 00 05 9f + /* Set page address */ + 15 01 00 00 0a 00 02 35 00 + /* Set tear on */ + 39 01 00 00 0a 00 03 44 00 00 + /* Set tear scan line */ + 15 01 00 00 0a 00 02 51 ff + /* write display brightness */ + 15 01 00 00 0a 00 02 53 24 + /* write control brightness */ + 15 01 00 00 0a 00 02 55 00 + /* CABC brightness */ + 05 01 00 00 78 00 01 11 + /* exit sleep mode, wait 120ms */ + 05 01 00 00 10 00 01 29]; + /* Set display on, wait 16ms */ + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 32 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + + qcom,compression-mode = "dsc"; + qcom,mdss-dsc-slice-height = <40>; + qcom,mdss-dsc-slice-width = <360>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; }; }; }; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi index a93deb55ff2f43f85c6ad7a30f906f77f9934b0c..870e2b3d80720b50e07fd4dbc83aec9b75d266c0 100644 --- a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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 @@ -46,6 +46,31 @@ qcom,mdss-dsi-display-timings { timing@0{ + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <28>; + qcom,mdss-dsi-h-back-porch = <4>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <12>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-panel-framerate = <120>; + qcom,mdss-dsi-on-command = + [/* exit sleep mode, wait 0ms */ + 05 01 00 00 00 00 01 29]; + /* Set display on, wait 16ms */ + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 00 00 02 28 00 + 05 01 00 00 00 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + timing@1{ qcom,mdss-dsi-panel-width = <1280>; qcom,mdss-dsi-panel-height = <1440>; qcom,mdss-dsi-h-front-porch = <120>; @@ -62,33 +87,33 @@ qcom,mdss-dsi-h-sync-pulse = <0>; qcom,mdss-dsi-panel-framerate = <60>; qcom,mdss-dsi-on-command = - [29 01 00 00 00 00 02 b0 03 - 05 01 00 00 0a 00 01 00 - /* Soft reset, wait 10ms */ - 15 01 00 00 0a 00 02 3a 77 - /* Set Pixel format (24 bpp) */ - 39 01 00 00 0a 00 05 2a 00 00 04 ff - /* Set Column address */ - 39 01 00 00 0a 00 05 2b 00 00 05 9f - /* Set page address */ - 15 01 00 00 0a 00 02 35 00 - /* Set tear on */ - 39 01 00 00 0a 00 03 44 00 00 - /* Set tear scan line */ - 15 01 00 00 0a 00 02 51 ff - /* write display brightness */ - 15 01 00 00 0a 00 02 53 24 - /* write control brightness */ - 15 01 00 00 0a 00 02 55 00 - /* CABC brightness */ - 05 01 00 00 78 00 01 11 - /* exit sleep mode, wait 120ms */ - 05 01 00 00 10 00 01 29]; - /* Set display on, wait 16ms */ + [/* exit sleep mode, wait 0ms */ + 05 01 00 00 00 00 01 29]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command = + [05 01 00 00 00 00 02 28 00 + 05 01 00 00 00 00 02 10 00]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + }; + timing@2{ + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-front-porch = <30>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <7>; + qcom,mdss-dsi-v-front-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-panel-framerate = <40>; + qcom,mdss-dsi-on-command = + [/* exit sleep mode, wait 0ms */ + 05 01 00 00 00 00 01 29]; qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command = - [05 01 00 00 32 00 02 28 00 - 05 01 00 00 78 00 02 10 00]; + [05 01 00 00 00 00 02 28 00 + 05 01 00 00 00 00 02 10 00]; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; }; }; diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sw43404-amoled-dsc-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sw43404-amoled-dsc-wqhd-cmd.dtsi index f536c227f161806480b808127b03c8ef5acaaccf..c7e17b531b90cd40b5880b3c99e90e16982b7fed 100644 --- a/arch/arm64/boot/dts/qcom/dsi-panel-sw43404-amoled-dsc-wqhd-cmd.dtsi +++ b/arch/arm64/boot/dts/qcom/dsi-panel-sw43404-amoled-dsc-wqhd-cmd.dtsi @@ -33,7 +33,7 @@ qcom,mdss-dsi-dma-trigger = "trigger_sw"; qcom,mdss-dsi-mdp-trigger = "none"; qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; - qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-dsi-bl-max-level = <1023>; qcom,mdss-dsi-te-pin-select = <1>; qcom,mdss-dsi-wr-mem-start = <0x2c>; qcom,mdss-dsi-wr-mem-continue = <0x3c>; @@ -80,8 +80,7 @@ 05 01 00 00 1e 00 02 11 00 39 01 00 00 00 00 03 b0 a5 00 05 01 00 00 78 00 02 35 00 - 05 01 00 00 00 00 02 29 00 - 39 01 00 00 3c 00 03 51 80 03 + 05 01 00 00 3c 00 02 29 00 ]; qcom,mdss-dsi-off-command = [ diff --git a/arch/arm64/boot/dts/qcom/fg-gen4-batterydata-mlp466076-3250mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen4-batterydata-mlp466076-3250mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..3a31cbdf659218d089a59f326d7e74f05e4baca8 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/fg-gen4-batterydata-mlp466076-3250mah.dtsi @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2018, 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,mlp466076_3250mah_averaged_masterslave_mar27th2018 { + /* #mlp466076_3250mAh_averaged_MasterSlave_Mar27th2018 */ + qcom,max-voltage-uv = <4400000>; + qcom,fastchg-current-ma = <6000000>; + qcom,jeita-fcc-ranges = <0 150 650000 + 151 450 4875000 + 451 550 1625000>; + qcom,jeita-fv-ranges = <0 150 4150000 + 151 450 4400000 + 451 550 4150000>; + qcom,batt-id-kohm = <133>; + qcom,battery-beta = <4250>; + qcom,therm-room-temp = <100000>; + qcom,fg-cc-cv-threshold-mv = <4390>; + qcom,battery-type = "mlp466076_3250mah_mar27th2018"; + qcom,therm-coefficients = <0x2318 0xd0c 0xdaf7 0xc556 0x848d>; + qcom,therm-center-offset = <0x70>; + qcom,checksum = <0xC5FF>; + qcom,gui-version = "PM855GUI - 0.0.0.32"; + qcom,fg-profile-data = [ + 09 00 31 EA + 85 C4 5A BA + 33 AA 00 00 + EE BC 66 8B + F6 87 AC 95 + 78 9A D2 87 + 2F 00 6C 0C + EF 02 B0 04 + 41 02 CE 07 + 00 00 A6 00 + 70 07 CD 06 + 64 14 66 25 + 55 1C 06 0A + E8 3A C0 43 + 40 00 37 00 + 40 00 4D 00 + 41 00 33 00 + 38 00 3B 00 + 43 00 4C 00 + 40 00 40 00 + 3E 00 3B 00 + 34 00 32 00 + 2F 00 56 00 + 4A 64 42 00 + 49 00 40 08 + 40 00 36 00 + 36 00 40 10 + 3C 10 36 00 + 64 28 4A 48 + 3E 60 39 0C + 40 00 D8 00 + 66 20 C1 0C + C7 02 61 FE + 2A 1C EA 0B + 5A 0D A2 22 + F6 17 3A 42 + 2E 55 7F 02 + 71 13 47 20 + B9 04 2E 0B + 9E 05 D4 1C + D5 03 F7 05 + 4D 02 8C 18 + D4 22 B5 45 + 90 52 7F 14 + AE 20 60 04 + 68 CB 66 AD + DC 1C 7C D1 + B1 05 D3 BA + 78 18 AA 8A + FD 85 18 92 + 8F A0 09 80 + 43 00 82 FC + 33 03 25 02 + 00 F8 FE EC + F4 DB F1 F7 + 68 0B EA 14 + 71 20 20 18 + A6 1E BD 03 + C8 05 54 01 + CE 07 32 00 + 15 03 4D 02 + 90 02 35 04 + 01 03 1D 03 + 47 03 DB 02 + 52 05 40 00 + 3B 00 3E 00 + 3F 64 40 00 + 41 00 3E 08 + 43 F8 40 00 + 40 08 40 10 + 40 10 36 00 + 3F 28 42 48 + 42 60 47 0C + 38 00 39 00 + 3F 08 40 00 + 40 00 40 00 + 2F 10 3D 10 + 3B 00 42 20 + 53 40 37 58 + 3D 0E 41 00 + 40 00 40 08 + D8 00 28 20 + 94 05 44 0A + 23 0D 8F 1C + 08 22 3D 45 + C4 52 4F 18 + 41 02 26 05 + 86 02 6A 11 + 3F 0A EA 1F + 59 05 BC 02 + FA 05 A2 1C + 77 03 92 05 + A5 02 84 18 + A0 03 34 04 + C6 02 67 00 + E9 1F 2B 05 + FE 02 A8 05 + C4 1C 83 02 + F1 04 66 03 + A6 18 FC 02 + 65 05 28 03 + 71 00 61 01 + C0 00 FA 00 + 38 0D 00 00 + ]; +}; diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdmshrike.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdmshrike.dtsi index 478012739507dffdc5f98432d7ac55c4ea8c9dc3..5b773252cf2c0ed59ac1e3939a08751edd4710e7 100644 --- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdmshrike.dtsi +++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdmshrike.dtsi @@ -322,8 +322,6 @@ <0x15182220 0x8>; reg-names = "base", "status-reg"; qcom,stream-id-range = <0x1000 0x400>; - qcom,regulator-names = "vdd"; - vdd-supply = <&hlos1_vote_turing_mmu_tbu1_gdsc>; qcom,msm-bus,name = "apps_smmu"; qcom,msm-bus,num-cases = <2>; qcom,msm-bus,active-only; @@ -343,8 +341,6 @@ <0x15182228 0x8>; reg-names = "base", "status-reg"; qcom,stream-id-range = <0x1400 0x400>; - qcom,regulator-names = "vdd"; - vdd-supply = <&hlos1_vote_turing_mmu_tbu0_gdsc>; qcom,msm-bus,name = "apps_smmu"; qcom,msm-bus,num-cases = <2>; qcom,msm-bus,active-only; diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sm6150.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sm6150.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..d24312ad8bfced7aee691af68fc13f32b68865ba --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sm6150.dtsi @@ -0,0 +1,214 @@ +/* Copyright (c) 2018, 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 + +&soc { + kgsl_smmu: kgsl-smmu@0x50a0000 { + compatible = "qcom,qsmmu-v500"; + reg = <0x50a0000 0x10000>, + <0x50c2000 0x20>; + reg-names = "base", "tcu-base"; + #iommu-cells = <1>; + qcom,skip-init; + qcom,use-3-lvl-tables; + #global-interrupts = <1>; + qcom,regulator-names = "vdd"; + vdd-supply = <&gpu_cx_gdsc>; + #size-cells = <1>; + #address-cells = <1>; + ranges; + interrupts = , + , + , + , + , + , + , + , + ; + + gfx_0_tbu: gfx_0_tbu@0x50c5000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x50c5000 0x1000>, + <0x50c2200 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x0 0x400>; + }; + + gfx_1_tbu: gfx_1_tbu@0x50c9000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x50c9000 0x1000>, + <0x50c2208 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x400 0x400>; + }; + }; + + apps_smmu: apps-smmu@0x15000000 { + compatible = "qcom,qsmmu-v500"; + reg = <0x15000000 0x80000>, + <0x150c2000 0x20>; + reg-names = "base", "tcu-base"; + #iommu-cells = <2>; + qcom,skip-init; + qcom,use-3-lvl-tables; + #global-interrupts = <1>; + #size-cells = <1>; + #address-cells = <1>; + ranges; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + + anoc_1_tbu: anoc_1_tbu@0x150c5000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150c5000 0x1000>, + <0x150c2200 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x0 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_aggre_noc_mmu_tbu1_gdsc>; + }; + + anoc_2_tbu: anoc_2_tbu@0x150c9000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150c9000 0x1000>, + <0x150c2208 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x400 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_aggre_noc_mmu_tbu2_gdsc>; + }; + + mnoc_hf_0_tbu: mnoc_hf_0_tbu@0x150cd000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150cd000 0x1000>, + <0x150c2210 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x800 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc>; + }; + + mnoc_sf_0_tbu: mnoc_sf_0_tbu@0x150d1000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150d1000 0x1000>, + <0x150c2218 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0xc00 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_sf_gdsc>; + }; + + compute_dsp_tbu: compute_dsp_tbu@0x150d5000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150d5000 0x1000>, + <0x150c2220 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x1000 0x400>; + /* No GDSC */ + }; + + adsp_tbu: adsp_tbu@0x150d9000 { + compatible = "qcom,qsmmuv500-tbu"; + reg = <0x150d9000 0x1000>, + <0x150c2228 0x8>; + reg-names = "base", "status-reg"; + qcom,stream-id-range = <0x1400 0x400>; + qcom,regulator-names = "vdd"; + vdd-supply = <&hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc>; + }; + }; + + kgsl_iommu_test_device { + compatible = "iommu-debug-test"; + /* + * 0x7 isn't a valid sid, but should pass the sid sanity check. + * We just need _something_ here to get this node recognized by + * the SMMU driver. Our test uses ATOS, which doesn't use SIDs + * anyways, so using a dummy value is ok. + */ + iommus = <&kgsl_smmu 0x7>; + }; + + apps_iommu_test_device { + compatible = "iommu-debug-test"; + /* + * This SID belongs to TSIF. We can't use a fake SID for + * the apps_smmu device. + */ + iommus = <&apps_smmu 0x20 0>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sm8150.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sm8150.dtsi index be74bea8614a9c80a078e5a2293ef395ece27484..279c141e33a6a7f13e1667de0943d2c6220f97d8 100644 --- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sm8150.dtsi @@ -20,6 +20,7 @@ <0x2CC2000 0x20>; reg-names = "base", "tcu-base"; #iommu-cells = <2>; + qcom,dynamic; qcom,skip-init; qcom,use-3-lvl-tables; qcom,no-asid-retention; @@ -360,26 +361,28 @@ kgsl_iommu_test_device { compatible = "iommu-debug-test"; - /* - * 0x7 isn't a valid sid, but should pass the sid sanity check. - * We just need _something_ here to get this node recognized by - * the SMMU driver. Our test uses ATOS, which doesn't use SIDs - * anyways, so using a dummy value is ok. - */ iommus = <&kgsl_smmu 0x7 0>; }; + kgsl_iommu_coherent_test_device { + compatible = "iommu-debug-test"; + iommus = <&kgsl_smmu 0x9 0>; + dma-coherent; + }; + apps_iommu_test_device { compatible = "iommu-debug-test"; - /* - * This SID belongs to TSIF. We can't use a fake SID for - * the apps_smmu device. - */ - iommus = <&apps_smmu 0x20 0>; + iommus = <&apps_smmu 0x21 0>; + }; + + apps_iommu_coherent_test_device { + compatible = "iommu-debug-test"; + iommus = <&apps_smmu 0x23 0>; + dma-coherent; }; }; &kgsl_smmu { - qcom,actlr = <0x0 0x407 0x1>; + qcom,actlr = <0x0 0x407 0x303>; }; diff --git a/arch/arm64/boot/dts/qcom/pm6150.dtsi b/arch/arm64/boot/dts/qcom/pm6150.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..ff70579dd561160a261ec49feee34edace70310f --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pm6150.dtsi @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018, 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 +#include +#include +#include + +&spmi_bus { + qcom,pm6150@0 { + compatible = "qcom,spmi-pmic"; + reg = <0x0 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + pm6150_revid: 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 IRQ_TYPE_NONE>, + <0x0 0x8 0x1 IRQ_TYPE_NONE>; + interrupt-names = "kpdpwr", "resin"; + qcom,pon-dbc-delay = <15625>; + qcom,kpdpwr-sw-debounce; + qcom,system-reset; + qcom,store-hard-reset-reason; + + qcom,pon_1 { + qcom,pon-type = ; + qcom,pull-up = <1>; + linux,code = ; + }; + + qcom,pon_2 { + qcom,pon-type = ; + qcom,pull-up = <1>; + linux,code = ; + }; + }; + + pm6150_misc: qcom,misc@900 { + compatible = "qcom,qpnp-misc"; + reg = <0x900 0x100>; + }; + + pm6150_tz: qcom,temp-alarm@2400 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0x2400 0x100>; + interrupts = <0x0 0x24 0x0 IRQ_TYPE_EDGE_RISING>; + #thermal-sensor-cells = <0>; + qcom,temperature-threshold-set = <1>; + }; + + pm6150_clkdiv: clock-controller@5b00 { + compatible = "qcom,spmi-clkdiv"; + reg = <0x5b00 0x100>; + #clock-cells = <1>; + qcom,num-clkdivs = <1>; + clock-output-names = "pm6150_div_clk1"; + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + }; + + pm6150_gpios: pinctrl@c000 { + compatible = "qcom,spmi-gpio"; + reg = <0xc000 0xa00>; + interrupts = <0x0 0xc0 0 IRQ_TYPE_NONE>, + <0x0 0xc1 0 IRQ_TYPE_NONE>, + <0x0 0xc2 0 IRQ_TYPE_NONE>, + <0x0 0xc3 0 IRQ_TYPE_NONE>, + <0x0 0xc6 0 IRQ_TYPE_NONE>, + <0x0 0xc7 0 IRQ_TYPE_NONE>; + interrupt-names = "pm6150_gpio1", "pm6150_gpio2", + "pm6150_gpio3", "pm6150_gpio4", + "pm6150_gpio7", "pm6150_gpio8"; + gpio-controller; + #gpio-cells = <2>; + qcom,gpios-disallowed = <5 6 9 10>; + }; + }; + + qcom,pm6150@1 { + compatible ="qcom,spmi-pmic"; + reg = <0x1 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + pm6150_vib: qcom,vibrator@5300 { + compatible = "qcom,qpnp-vibrator-ldo"; + reg = <0x5300 0x100>; + qcom,vib-ldo-volt-uv = <3000000>; + qcom,vib-overdrive-volt-uv = <3544000>; + }; + }; +}; + diff --git a/arch/arm64/boot/dts/qcom/pm6150l.dtsi b/arch/arm64/boot/dts/qcom/pm6150l.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..1bfc77bd3a0d01520ee31dd7c42ef25fc19a093c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pm6150l.dtsi @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017-2018, 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 +#include + +&spmi_bus { + qcom,pm6150l@4 { + compatible = "qcom,spmi-pmic"; + reg = <0x4 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + pm6150l_revid: qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + }; + + qcom,power-on@800 { + compatible = "qcom,qpnp-power-on"; + reg = <0x800 0x100>; + }; + + pm6150l_tz: qcom,temp-alarm@2400 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0x2400 0x100>; + interrupts = <0x4 0x24 0x0 IRQ_TYPE_EDGE_RISING>; + #thermal-sensor-cells = <0>; + qcom,temperature-threshold-set = <1>; + }; + + pm6150l_clkdiv: clock-controller@5b00 { + compatible = "qcom,spmi-clkdiv"; + reg = <0x5b00 0x100>; + #clock-cells = <1>; + qcom,num-clkdivs = <1>; + clock-output-names = "pm6150l_div_clk1"; + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + }; + + pm6150l_gpios: pinctrl@c000 { + compatible = "qcom,spmi-gpio"; + reg = <0xc000 0xc00>; + interrupts = <0x4 0xc0 0 IRQ_TYPE_NONE>, + <0x4 0xc1 0 IRQ_TYPE_NONE>, + <0x4 0xc2 0 IRQ_TYPE_NONE>, + <0x4 0xc3 0 IRQ_TYPE_NONE>, + <0x4 0xc4 0 IRQ_TYPE_NONE>, + <0x4 0xc5 0 IRQ_TYPE_NONE>, + <0x4 0xc7 0 IRQ_TYPE_NONE>, + <0x4 0xc8 0 IRQ_TYPE_NONE>, + <0x4 0xc9 0 IRQ_TYPE_NONE>, + <0x4 0xca 0 IRQ_TYPE_NONE>, + <0x4 0xcb 0 IRQ_TYPE_NONE>; + interrupt-names = "pm6150l_gpio1", "pm6150l_gpio2", + "pm6150l_gpio3", "pm6150l_gpio4", + "pm6150l_gpio5", "pm6150l_gpio6", + "pm6150l_gpio8", "pm6150l_gpio9", + "pm6150l_gpio10", "pm6150l_gpio11", + "pm6150l_gpio12"; + gpio-controller; + #gpio-cells = <2>; + qcom,gpios-disallowed = <7>; + }; + }; + + qcom,pm6150l@5 { + compatible ="qcom,spmi-pmic"; + reg = <0x5 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + pm6150l_lcdb: qcom,lcdb@ec00 { + compatible = "qcom,qpnp-lcdb-regulator"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xec00 0x100>; + interrupts = <0x5 0xec 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "sc-irq"; + qcom,pmic-revid = <&pm6150l_revid>; + status = "disabled"; + + lcdb_ldo_vreg: ldo { + label = "ldo"; + regulator-name = "lcdb_ldo"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + + lcdb_ncp_vreg: ncp { + label = "ncp"; + regulator-name = "lcdb_ncp"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + }; + + flash_led: qcom,leds@d300 { + compatible = "qcom,qpnp-flash-led-v2"; + status = "okay"; + reg = <0xd300 0x100>; + label = "flash"; + interrupts = <0x5 0xd3 0x0 IRQ_TYPE_EDGE_RISING>, + <0x5 0xd3 0x3 IRQ_TYPE_EDGE_RISING>, + <0x5 0xd3 0x4 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "led-fault-irq", + "all-ramp-down-done-irq", + "all-ramp-up-done-irq"; + qcom,hdrm-auto-mode; + qcom,short-circuit-det; + qcom,open-circuit-det; + qcom,vph-droop-det; + qcom,thermal-derate-en; + qcom,thermal-derate-current = <200 500 1000>; + qcom,isc-delay = <192>; + qcom,pmic-revid = <&pm6150l_revid>; + + pm6150l_flash0: qcom,flash_0 { + label = "flash"; + qcom,led-name = "led:flash_0"; + qcom,max-current = <1500>; + qcom,default-led-trigger = "flash0_trigger"; + qcom,id = <0>; + qcom,current-ma = <1000>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pm6150l_flash1: qcom,flash_1 { + label = "flash"; + qcom,led-name = "led:flash_1"; + qcom,max-current = <1500>; + qcom,default-led-trigger = "flash1_trigger"; + qcom,id = <1>; + qcom,current-ma = <1000>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pm6150l_flash2: qcom,flash_2 { + label = "flash"; + qcom,led-name = "led:flash_2"; + qcom,max-current = <750>; + qcom,default-led-trigger = "flash2_trigger"; + qcom,id = <2>; + qcom,current-ma = <500>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + status = "disabled"; + }; + + pm6150l_torch0: qcom,torch_0 { + label = "torch"; + qcom,led-name = "led:torch_0"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch0_trigger"; + qcom,id = <0>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pm6150l_torch1: qcom,torch_1 { + label = "torch"; + qcom,led-name = "led:torch_1"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch1_trigger"; + qcom,id = <1>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pm6150l_torch2: qcom,torch_2 { + label = "torch"; + qcom,led-name = "led:torch_2"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch2_trigger"; + qcom,id = <2>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + status = "disabled"; + }; + + pm6150l_switch0: qcom,led_switch_0 { + label = "switch"; + qcom,led-name = "led:switch_0"; + qcom,led-mask = <1>; + qcom,default-led-trigger = "switch0_trigger"; + }; + + pm6150l_switch1: qcom,led_switch_1 { + label = "switch"; + qcom,led-name = "led:switch_1"; + qcom,led-mask = <2>; + qcom,default-led-trigger = "switch1_trigger"; + }; + + pm6150l_switch2: qcom,led_switch_2 { + label = "switch"; + qcom,led-name = "led:switch_2"; + qcom,led-mask = <3>; + qcom,default-led-trigger = "switch2_trigger"; + }; + }; + + pm6150l_wled: qcom,wled@d800 { + compatible = "qcom,pm6150l-spmi-wled"; + reg = <0xd800 0x100>, <0xd900 0x100>; + reg-names = "wled-ctrl-base", "wled-sink-base"; + label = "backlight"; + interrupts = <0x5 0xd8 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "ovp-irq"; + qcom,pmic-revid = <&pm6150l_revid>; + qcom,auto-calibration; + status = "disabled"; + }; + + pm6150l_lpg: qcom,pwms@b100 { + compatible = "qcom,pwm-lpg"; + reg = <0xb100 0x300>; + reg-names = "lpg-base"; + #pwm-cells = <2>; + }; + + pm6150l_rgb_led: qcom,leds@d000 { + compatible = "qcom,tri-led"; + reg = <0xd000 0x100>; + red { + label = "red"; + pwms = <&pm6150l_lpg 0 1000000>; + led-sources = <0>; + }; + green { + label = "green"; + pwms = <&pm6150l_lpg 1 1000000>; + led-sources = <1>; + }; + blue { + label = "blue"; + pwms = <&pm6150l_lpg 2 1000000>; + led-sources = <2>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pm855.dtsi b/arch/arm64/boot/dts/qcom/pm855.dtsi index 1dda03621edd4c32c73ce351c595495bd4d8098e..1797ef3ac5e4478dbfd6aba9f447b51063fe5df4 100644 --- a/arch/arm64/boot/dts/qcom/pm855.dtsi +++ b/arch/arm64/boot/dts/qcom/pm855.dtsi @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -134,6 +135,17 @@ qcom,pre-scaling = <1 1>; }; }; + + pm855_adc_tm: adc_tm@3500 { + compatible = "qcom,adc-tm5"; + reg = <0x3500 0x100>; + interrupts = <0x0 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "thr-int-en"; + #address-cells = <1>; + #size-cells = <0>; + #thermal-sensor-cells = <1>; + io-channels = <&pm855_vadc ADC_XO_THERM_PU2>; + }; }; qcom,pm855@1 { diff --git a/arch/arm64/boot/dts/qcom/pm855b.dtsi b/arch/arm64/boot/dts/qcom/pm855b.dtsi index c9b469aa6e19a2c6fa72665ebbf0c344e8742b80..93ec56783e5e681eaf90467c3257c34be218b5f1 100644 --- a/arch/arm64/boot/dts/qcom/pm855b.dtsi +++ b/arch/arm64/boot/dts/qcom/pm855b.dtsi @@ -125,6 +125,33 @@ label = "v_i_parallel_vbat_vdata"; qcom,pre-scaling = <1 1>; }; + + smb1390_therm { + reg = ; + label = "smb1390_therm"; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; + + smb1355_therm { + reg = ; + label = "smb1355_therm"; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + }; + }; + + pm855b_adc_tm: adc_tm@3500 { + compatible = "qcom,adc-tm5"; + reg = <0x3500 0x100>; + interrupts = <0x2 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "thr-int-en"; + #address-cells = <1>; + #size-cells = <0>; + #thermal-sensor-cells = <1>; + io-channels = <&pm855b_vadc ADC_AMUX_THM2_PU2>; + status = "disabled"; }; pm855b_charger: qcom,qpnp-smb5 { @@ -457,6 +484,20 @@ }; &thermal_zones { + pm855b-wp-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + pm855b_temp_alarm: pm855b_tz { polling-delay-passive = <0>; polling-delay = <0>; @@ -483,7 +524,7 @@ }; pm855b-ibat-lvl0 { - polling-delay-passive = <0>; + polling-delay-passive = <100>; polling-delay = <0>; thermal-governor = "step_wise"; thermal-sensors = <&pm855b_bcl 0>; @@ -513,14 +554,15 @@ }; pm855b-vbat-lvl0 { - polling-delay-passive = <0>; + polling-delay-passive = <100>; polling-delay = <0>; - thermal-governor = "step_wise"; + thermal-governor = "low_limits_cap"; thermal-sensors = <&pm855b_bcl 2>; + tracks-low; trips { vbat_lvl0: vbat-lvl0 { - temperature = <3500>; + temperature = <3000>; hysteresis = <200>; type = "passive"; }; @@ -530,12 +572,13 @@ pm855b-vbat-lvl1 { polling-delay-passive = <0>; polling-delay = <0>; - thermal-governor = "step_wise"; + thermal-governor = "low_limits_cap"; thermal-sensors = <&pm855b_bcl 3>; + tracks-low; trips { vbat_lvl1:vbat-lvl1 { - temperature = <3000>; + temperature = <2800>; hysteresis = <200>; type = "passive"; }; @@ -545,12 +588,13 @@ pm855b-vbat-lvl2 { polling-delay-passive = <0>; polling-delay = <0>; - thermal-governor = "step_wise"; + thermal-governor = "low_limits_cap"; thermal-sensors = <&pm855b_bcl 4>; + tracks-low; trips { vbat_lvl2:vbat-lvl2 { - temperature = <2800>; + temperature = <2600>; hysteresis = <200>; type = "passive"; }; @@ -558,10 +602,11 @@ }; soc { - polling-delay-passive = <0>; + polling-delay-passive = <100>; polling-delay = <0>; - thermal-governor = "step_wise"; + thermal-governor = "low_limits_cap"; thermal-sensors = <&bcl_soc>; + tracks-low; trips { soc_trip:soc-trip { diff --git a/arch/arm64/boot/dts/qcom/pm855l.dtsi b/arch/arm64/boot/dts/qcom/pm855l.dtsi index 14786207d4de09995170df4b5e38c30603e677af..39317f5d237ef6504e795ce03d834a0e79254527 100644 --- a/arch/arm64/boot/dts/qcom/pm855l.dtsi +++ b/arch/arm64/boot/dts/qcom/pm855l.dtsi @@ -117,6 +117,19 @@ "bcl-vbat-lvl2"; #thermal-sensor-cells = <1>; }; + + pm855l_adc_tm: adc_tm@3500 { + compatible = "qcom,adc-tm5"; + reg = <0x3500 0x100>; + interrupts = <0x4 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "thr-int-en"; + #address-cells = <1>; + #size-cells = <0>; + #thermal-sensor-cells = <1>; + io-channels = <&pm855l_vadc ADC_AMUX_THM1_PU2>, + <&pm855l_vadc ADC_AMUX_THM2_PU2>, + <&pm855l_vadc ADC_AMUX_THM3_PU2>; + }; }; qcom,pm855l@5 { @@ -283,9 +296,43 @@ pm855l_lpg: qcom,pwms@b100 { compatible = "qcom,pwm-lpg"; - reg = <0xb100 0x300>; - reg-names = "lpg-base"; + reg = <0xb100 0x300>, <0xb000 0x100>; + reg-names = "lpg-base", "lut-base"; #pwm-cells = <2>; + qcom,lut-patterns = <0 10 20 30 40 50 60 70 80 90 100 + 90 80 70 60 50 40 30 20 10 0>; + lpg1 { + qcom,lpg-chan-id = <1>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + + lpg2 { + qcom,lpg-chan-id = <2>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; + + lpg3 { + qcom,lpg-chan-id = <3>; + qcom,ramp-step-ms = <100>; + qcom,ramp-pause-hi-count = <2>; + qcom,ramp-pause-lo-count = <2>; + qcom,ramp-low-index = <0>; + qcom,ramp-high-index = <20>; + qcom,ramp-from-low-to-high; + qcom,ramp-pattern-repeat; + }; }; pm855l_rgb_led: qcom,leds@d000 { @@ -295,16 +342,19 @@ label = "red"; pwms = <&pm855l_lpg 0 1000000>; led-sources = <0>; + linux,default-trigger = "timer"; }; green { label = "green"; pwms = <&pm855l_lpg 1 1000000>; led-sources = <1>; + linux,default-trigger = "timer"; }; blue { label = "blue"; pwms = <&pm855l_lpg 2 1000000>; led-sources = <2>; + linux,default-trigger = "timer"; }; }; }; @@ -337,7 +387,7 @@ }; pm855l-vph-lvl0 { - polling-delay-passive = <0>; + polling-delay-passive = <100>; polling-delay = <0>; thermal-governor = "low_limits_cap"; thermal-sensors = <&pm855l_bcl 2>; @@ -345,7 +395,7 @@ trips { vph_lvl0: vph-lvl0 { - temperature = <3500>; + temperature = <3000>; hysteresis = <200>; type = "passive"; }; @@ -361,7 +411,7 @@ trips { vph_lvl1:vph-lvl1 { - temperature = <3000>; + temperature = <2750>; hysteresis = <200>; type = "passive"; }; @@ -377,7 +427,7 @@ trips { vph_lvl2:vph-lvl2 { - temperature = <2800>; + temperature = <2500>; hysteresis = <200>; type = "passive"; }; diff --git a/arch/arm64/boot/dts/qcom/pms405-rpm-regulator.dtsi b/arch/arm64/boot/dts/qcom/pms405-rpm-regulator.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..dd0200ef50276b0eaa4995883b6b5a8e4df297eb --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pms405-rpm-regulator.dtsi @@ -0,0 +1,304 @@ +/* Copyright (c) 2018 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 { + /* VDD_MX/CX supply */ + rpm-regulator-smpa1 { + compatible = "qcom,rpm-smd-regulator-resource"; + qcom,resource-name = "rwmx"; + qcom,resource-id = <1>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + status = "disabled"; + + regulator-s1 { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_s1"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + /* VDD_LPI_CX supply */ + rpm-regulator-smpa2 { + compatible = "qcom,rpm-smd-regulator-resource"; + qcom,resource-name = "rwlc"; + qcom,resource-id = <2>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + status = "disabled"; + + regulator-s2 { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_s2"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-smpa3 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_s3"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-smpa4 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_s4"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-smpa5 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_s5"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa1 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l1"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa2 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l2"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa3 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l3"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa4 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l4"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa5 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l5"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa6 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l6"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa7 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l7"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa8 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l8"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + /* VDD_LPI_MX supply */ + rpm-regulator-ldoa9 { + compatible = "qcom,rpm-smd-regulator-resource"; + qcom,resource-name = "rwlm"; + qcom,resource-id = <0>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + status = "disabled"; + + regulator-l9 { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_l9"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa10 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l10"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa11 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l11"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa12 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l12"; + qcom,set = <3>; + status = "disabled"; + }; + }; + + rpm-regulator-ldoa13 { + compatible = "qcom,rpm-smd-regulator-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-smd-regulator"; + regulator-name = "pms405_l13"; + qcom,set = <3>; + status = "disabled"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pms405.dtsi b/arch/arm64/boot/dts/qcom/pms405.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..969812d894685c708448edc536161e47004fbad8 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pms405.dtsi @@ -0,0 +1,103 @@ +/* Copyright (c) 2018, 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 +#include + +&spmi_bus { + qcom,pms405@0 { + compatible ="qcom,spmi-pmic"; + reg = <0x0 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + qcom,revid@100 { + compatible = "qcom,qpnp-revid"; + reg = <0x100 0x100>; + }; + + pms405_pon: qcom,power-on@800 { + compatible = "qcom,qpnp-power-on"; + reg = <0x800 0x100>; + interrupts = <0x0 0x8 0x0>; + interrupt-names = "kpdpwr"; + qcom,pon-dbc-delay = <15625>; + + qcom,pon_1 { + qcom,pon-type = <0>; + qcom,pull-up = <1>; + linux,code = ; + }; + }; + + pms405_misc: qcom,misc@900 { + compatible = "qcom,qpnp-misc"; + reg = <0x900 0x100>; + }; + + /* QCS405 + PMS405 GPIO configuration */ + pms405_gpios: pinctrl@c000 { + compatible = "qcom,spmi-gpio"; + reg = <0xc000 0xc00>; + interrupts = <0x0 0xc1 0 IRQ_TYPE_NONE>, + <0x0 0xc2 0 IRQ_TYPE_NONE>, + <0x0 0xc3 0 IRQ_TYPE_NONE>, + <0x0 0xc4 0 IRQ_TYPE_NONE>, + <0x0 0xc5 0 IRQ_TYPE_NONE>, + <0x0 0xc6 0 IRQ_TYPE_NONE>, + <0x0 0xc7 0 IRQ_TYPE_NONE>, + <0x0 0xca 0 IRQ_TYPE_NONE>, + <0x0 0xcb 0 IRQ_TYPE_NONE>; + interrupt-names = "pms405_gpio2", "pms405_gpio3", + "pms405_gpio4", "pms405_gpio5", + "pms405_gpio6", "pms405_gpio7", + "pms405_gpio8", "pms405_gpio11", + "pms405_gpio12"; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <1>; + qcom,gpios-disallowed = <1 9 10>; + }; + + qcom,pms405_rtc { + compatible = "qcom,qpnp-rtc"; + #address-cells = <1>; + #size-cells = <1>; + qcom,qpnp-rtc-write = <0>; + qcom,qpnp-rtc-alarm-pwrup = <0>; + + qcom,pms405_rtc_rw@6000 { + reg = <0x6000 0x100>; + }; + + qcom,pms405_rtc_alarm@6100 { + reg = <0x6100 0x100>; + interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>; + }; + }; + }; + + qcom,pms405@1 { + compatible = "qcom,spmi-pmic"; + reg = <0x1 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + pms405_pwm: qcom,pwms@bc00 { + compatible = "qcom,pwm-lpg"; + reg = <0xbc00 0x200>; + reg-names = "lpg-base"; + #pwm-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405-blsp.dtsi b/arch/arm64/boot/dts/qcom/qcs405-blsp.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..a65e323ccfdd7268fab24fa61bbb3bad719a3c41 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs405-blsp.dtsi @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2018, 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 "qcs405-pinctrl.dtsi" + +/ { + aliases { + spi1 = &spi_1; + spi2 = &spi_2; + spi3 = &spi_3; + spi4 = &spi_4; + spi5 = &spi_5; + spi6 = &spi_6; + i2c1 = &i2c_1; + i2c2 = &i2c_2; + i2c3 = &i2c_3; + i2c4 = &i2c_4; + i2c5 = &i2c_5; + i2c6 = &i2c_6; + }; +}; + + +&soc { + + dma_blsp1: qcom,sps-dma@7884000{ /* BLSP1 */ + #dma-cells = <4>; + compatible = "qcom,sps-dma"; + reg = <0x7884000 0x25000>; + interrupts = <0 238 0>; + qcom,summing-threshold = <0x10>; + }; + + dma_blsp2: qcom,sps-dma@7ac4000{ /* BLSP2 */ + #dma-cells = <4>; + compatible = "qcom,sps-dma"; + reg = <0x7ac4000 0x17000>; + interrupts = <0 239 0>; + qcom,summing-threshold = <0x10>; + }; + + i2c_1: i2c@78b5000 { /* BLSP1 QUP1 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x78b5000 0x600>; + reg-names = "qup_phys_addr"; + interrupt-names = "qup_irq"; + interrupts = <0 95 0>; + dmas = <&dma_blsp1 8 64 0x20000020 0x20>, + <&dma_blsp1 9 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP0_I2C_APPS_CLK>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_1_active>; + pinctrl-1 = <&i2c_1_sleep>; + status = "disabled"; + }; + + i2c_2: i2c@78b6000 { /* BLSP1 QUP2 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x78b6000 0x600>; + reg-names = "qup_phys_addr"; + interrupt-names = "qup_irq"; + interrupts = <0 96 0>; + dmas = <&dma_blsp1 10 64 0x20000020 0x20>, + <&dma_blsp1 11 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_2_active>; + pinctrl-1 = <&i2c_2_sleep>; + status = "disabled"; + }; + + i2c_3: i2c@78b7000 { /* BLSP1 QUP3 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x78b7000 0x600>; + reg-names = "qup_phys_addr"; + interrupt-names = "qup_irq"; + interrupts = <0 97 0>; + dmas = <&dma_blsp1 12 64 0x20000020 0x20>, + <&dma_blsp1 13 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_3_sda_active>, <&i2c_3_scl_active>; + pinctrl-1 = <&i2c_3_sleep>; + status = "disabled"; + }; + + i2c_4: i2c@78b8000 { /* BLSP1 QUP4 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x78b8000 0x600>; + reg-names = "qup_phys_addr"; + interrupt-names = "qup_irq"; + interrupts = <0 98 0>; + dmas = <&dma_blsp1 14 64 0x20000020 0x20>, + <&dma_blsp1 15 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_4_active>; + pinctrl-1 = <&i2c_4_sleep>; + status = "disabled"; + }; + + i2c_5: i2c@78b9000 { /* BLSP2 QUP1 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x78b9000 0x600>; + reg-names = "qup_phys_addr"; + interrupt-names = "qup_irq"; + interrupts = <0 99 0>; + dmas = <&dma_blsp1 16 64 0x20000020 0x20>, + <&dma_blsp1 17 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP4_I2C_APPS_CLK>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_5_active>; + pinctrl-1 = <&i2c_5_sleep>; + status = "disabled"; + }; + + i2c_6: i2c@7af5000 { /* BLSP2 QUP1 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x7af5000 0x600>; + reg-names = "qup_phys_addr"; + interrupt-names = "qup_irq"; + interrupts = <0 299 0>; + dmas = <&dma_blsp2 2 64 0x20000020 0x20>, + <&dma_blsp2 3 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <84>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP2_AHB_CLK>, + <&clock_gcc GCC_BLSP2_QUP0_I2C_APPS_CLK>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_6_active>; + pinctrl-1 = <&i2c_6_sleep>; + status = "disabled"; + }; + + spi_1: spi@78b5000 { /* BLSP1 QUP1 */ + compatible = "qcom,spi-qup-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "spi_physical", "spi_bam_physical"; + reg = <0x78b5000 0x600>, + <0x7884000 0x25000>; + interrupt-names = "spi_irq", "spi_bam_irq"; + interrupts = <0 95 0>, <0 238 0>; + spi-max-frequency = <50000000>; + qcom,use-bam; + qcom,ver-reg-exists; + qcom,bam-consumer-pipe-index = <8>; + qcom,bam-producer-pipe-index = <9>; + qcom,master-id = <86>; + qcom,use-pinctrl; + pinctrl-names = "spi_default", "spi_sleep"; + pinctrl-0 = <&spi_1_active>; + pinctrl-1 = <&spi_1_sleep>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP0_SPI_APPS_CLK>; + status = "disabled"; + }; + + spi_2: spi@78b6000 { /* BLSP1 QUP2 */ + compatible = "qcom,spi-qup-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "spi_physical", "spi_bam_physical"; + reg = <0x78b6000 0x600>, + <0x7884000 0x25000>; + interrupt-names = "spi_irq", "spi_bam_irq"; + interrupts = <0 96 0>, <0 238 0>; + spi-max-frequency = <50000000>; + qcom,use-bam; + qcom,ver-reg-exists; + qcom,bam-consumer-pipe-index = <10>; + qcom,bam-producer-pipe-index = <11>; + qcom,master-id = <86>; + qcom,use-pinctrl; + pinctrl-names = "spi_default", "spi_sleep"; + pinctrl-0 = <&spi_2_active>; + pinctrl-1 = <&spi_2_sleep>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>; + status = "disabled"; + }; + + spi_3: spi@78b7000 { /* BLSP1 QUP3 */ + compatible = "qcom,spi-qup-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "spi_physical", "spi_bam_physical"; + reg = <0x78b7000 0x600>, + <0x7884000 0x25000>; + interrupt-names = "spi_irq", "spi_bam_irq"; + interrupts = <0 97 0>, <0 238 0>; + spi-max-frequency = <50000000>; + qcom,use-bam; + qcom,ver-reg-exists; + qcom,bam-consumer-pipe-index = <12>; + qcom,bam-producer-pipe-index = <13>; + qcom,master-id = <86>; + qcom,use-pinctrl; + pinctrl-names = "spi_default", "spi_sleep"; + pinctrl-0 = <&spi_3_active>; + pinctrl-1 = <&spi_3_sleep>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP2_SPI_APPS_CLK>; + status = "disabled"; + }; + + spi_4: spi@78b8000 { /* BLSP1 QUP4 */ + compatible = "qcom,spi-qup-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "spi_physical", "spi_bam_physical"; + reg = <0x78b8000 0x600>, + <0x7884000 0x25000>; + interrupt-names = "spi_irq", "spi_bam_irq"; + interrupts = <0 98 0>, <0 238 0>; + spi-max-frequency = <50000000>; + qcom,use-bam; + qcom,ver-reg-exists; + qcom,bam-consumer-pipe-index = <14>; + qcom,bam-producer-pipe-index = <15>; + qcom,master-id = <86>; + qcom,use-pinctrl; + pinctrl-names = "spi_default", "spi_sleep"; + pinctrl-0 = <&spi_4_active>; + pinctrl-1 = <&spi_4_sleep>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP3_SPI_APPS_CLK>; + status = "disabled"; + }; + + spi_5: spi@78b9000 { /* BLSP1 QUP2 */ + compatible = "qcom,spi-qup-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "spi_physical", "spi_bam_physical"; + reg = <0x78b9000 0x600>, + <0x7884000 0x25000>; + interrupt-names = "spi_irq", "spi_bam_irq"; + interrupts = <0 99 0>, <0 238 0>; + spi-max-frequency = <50000000>; + qcom,use-bam; + qcom,ver-reg-exists; + qcom,bam-consumer-pipe-index = <16>; + qcom,bam-producer-pipe-index = <17>; + qcom,master-id = <86>; + qcom,use-pinctrl; + pinctrl-names = "spi_default", "spi_sleep"; + pinctrl-0 = <&spi_5_active>; + pinctrl-1 = <&spi_5_sleep>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>, + <&clock_gcc GCC_BLSP1_QUP4_SPI_APPS_CLK>; + status = "disabled"; + }; + + spi_6: spi@7af5000 { /* BLSP2 QUP1 */ + compatible = "qcom,spi-qup-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "spi_physical", "spi_bam_physical"; + reg = <0x7af5000 0x600>, + <0x7ac4000 0x17000>; + interrupt-names = "spi_irq", "spi_bam_irq"; + interrupts = <0 299 0>, <0 239 0>; + spi-max-frequency = <50000000>; + qcom,use-bam; + qcom,ver-reg-exists; + qcom,bam-consumer-pipe-index = <2>; + qcom,bam-producer-pipe-index = <3>; + qcom,master-id = <84>; + qcom,use-pinctrl; + pinctrl-names = "spi_default", "spi_sleep"; + pinctrl-0 = <&spi_6_active>; + pinctrl-1 = <&spi_6_sleep>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc GCC_BLSP2_AHB_CLK>, + <&clock_gcc GCC_BLSP2_QUP0_SPI_APPS_CLK>; + status = "disabled"; + }; + + + blsp1_uart1_hs: uart@78af000 { /* BLSP1 UART1 */ + compatible = "qcom,msm-hsuart-v14"; + reg = <0x78af000 0x200>, + <0x7884000 0x25000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp1_uart1_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 0 107 0 + 1 &intc 0 0 238 0 + 2 &tlmm 31 0>; + + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xfd>; + + qcom,bam-tx-ep-pipe-index = <0>; + qcom,bam-rx-ep-pipe-index = <1>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc GCC_BLSP1_UART0_APPS_CLK>, + <&clock_gcc GCC_BLSP1_AHB_CLK>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&blsp1_uart1_sleep>; + pinctrl-1 = <&blsp1_uart1_active>; + + qcom,msm-bus,name = "buart1"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + status = "disabled"; + }; + + blsp1_uart2_hs: uart@78b0000{ /* BLSP1 UART2 */ + compatible = "qcom,msm-hsuart-v14"; + reg = <0x78b0000 0x200>, + <0x7884000 0x25000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp1_uart2_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 0 108 0 + 1 &intc 0 0 238 0 + 2 &tlmm 23 0>; + + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xfd>; + + qcom,bam-tx-ep-pipe-index = <2>; + qcom,bam-rx-ep-pipe-index = <3>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc GCC_BLSP1_UART1_APPS_CLK>, + <&clock_gcc GCC_BLSP1_AHB_CLK>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&blsp1_uart2_sleep>; + pinctrl-1 = <&blsp1_uart2_active>; + + qcom,msm-bus,name = "buart2"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + status = "disabled"; + }; + + blsp1_uart3_hs: uart@78b1000 { /* BLSP1 UART3 */ + compatible = "qcom,msm-hsuart-v14"; + reg = <0x78b1000 0x200>, + <0x7884000 0x25000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp1_uart3_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 0 118 0 + 1 &intc 0 0 238 0 + 2 &tlmm 18 0>; + + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xfd>; + + qcom,bam-tx-ep-pipe-index = <4>; + qcom,bam-rx-ep-pipe-index = <5>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>, + <&clock_gcc GCC_BLSP1_AHB_CLK>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&blsp1_uart3_sleep>; + pinctrl-1 = <&blsp1_uart3_active>; + + qcom,msm-bus,name = "buart3"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + status = "disabled"; + }; + + blsp1_uart4_hs: uart@78b2000 { /* BLSP1 UART4 */ + compatible = "qcom,msm-hsuart-v14"; + reg = <0x78b2000 0x200>, + <0x7884000 0x25000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp1_uart4_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 0 119 0 + 1 &intc 0 0 238 0 + 2 &tlmm 83 0>; + + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xfd>; + + qcom,bam-tx-ep-pipe-index = <6>; + qcom,bam-rx-ep-pipe-index = <7>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc GCC_BLSP1_UART3_APPS_CLK>, + <&clock_gcc GCC_BLSP1_AHB_CLK>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&blsp1_uart4_tx_sleep>, + <&blsp1_uart4_rxcts_sleep>, <&blsp1_uart4_rfr_sleep>; + pinctrl-1 = <&blsp1_uart4_tx_active>, + <&blsp1_uart4_rxcts_active>, <&blsp1_uart4_rfr_active>; + + qcom,msm-bus,name = "buart4"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + status = "disabled"; + }; + + blsp2_uart1_hs: uart@7aef000 { /* BLSP1 UART4 */ + compatible = "qcom,msm-hsuart-v14"; + reg = <0x7aef000 0x200>, + <0x7ac4000 0x17000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp2_uart1_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 0 297 0 + 1 &intc 0 0 239 0 + 2 &tlmm 27 0>; + + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xfd>; + + qcom,bam-tx-ep-pipe-index = <0>; + qcom,bam-rx-ep-pipe-index = <1>; + qcom,master-id = <84>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc GCC_BLSP2_UART0_APPS_CLK>, + <&clock_gcc GCC_BLSP2_AHB_CLK>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&blsp2_uart1_sleep>; + pinctrl-1 = <&blsp2_uart1_active>; + + qcom,msm-bus,name = "buart5"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <84 512 0 0>, + <84 512 500 800>; + status = "disabled"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405-coresight.dtsi b/arch/arm64/boot/dts/qcom/qcs405-coresight.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..28574e12ec143e6520e933da2e154f864c1ab791 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs405-coresight.dtsi @@ -0,0 +1,1070 @@ +/* Copyright (c) 2018, 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 { + + replicator_qdss: replicator@6046000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b909>; + + reg = <0x6046000 0x1000>; + reg-names = "replicator-base"; + + coresight-name = "coresight-replicator-qdss"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + replicator_out_tmc_etr: endpoint { + remote-endpoint= + <&tmc_etr_in_replicator>; + }; + }; + + port@1 { + reg = <0>; + replicator_in_tmc_etf: endpoint { + slave-mode; + remote-endpoint= + <&tmc_etf_out_replicator>; + }; + }; + }; + }; + + tmc_etr: tmc@6048000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b961>; + + reg = <0x6048000 0x1000>, + <0x6064000 0x15000>; + reg-names = "tmc-base", "bam-base"; + + arm,buffer-size = <0x400000>; + + coresight-name = "coresight-tmc-etr"; + coresight-ctis = <&cti0>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + interrupts = ; + interrupt-names = "byte-cntr-irq"; + + port { + tmc_etr_in_replicator: endpoint { + slave-mode; + remote-endpoint = <&replicator_out_tmc_etr>; + }; + }; + }; + + tmc_etf: tmc@6047000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b961>; + + reg = <0x6047000 0x1000>; + reg-names = "tmc-base"; + + coresight-name = "coresight-tmc-etf"; + coresight-ctis = <&cti0>; + arm,default-sink; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + tmc_etf_out_replicator: endpoint { + remote-endpoint = + <&replicator_in_tmc_etf>; + }; + }; + + port@1 { + reg = <0>; + tmc_etf_in_funnel_merg: endpoint { + slave-mode; + remote-endpoint = + <&funnel_merg_out_tmc_etf>; + }; + }; + }; + + }; + + funnel_merg: funnel@6045000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6045000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-merg"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_merg_out_tmc_etf: endpoint { + remote-endpoint = + <&tmc_etf_in_funnel_merg>; + }; + }; + + port@1 { + reg = <0>; + funnel_merg_in_funnel_in0: endpoint { + slave-mode; + remote-endpoint = + <&funnel_in0_out_funnel_merg>; + }; + }; + + port@2 { + reg = <1>; + funnel_merg_in_funnel_in1: endpoint { + slave-mode; + remote-endpoint = + <&funnel_in1_out_funnel_merg>; + }; + }; + + port@3 { + reg = <2>; + funnel_merg_in_funnel_in2: endpoint { + slave-mode; + remote-endpoint = + <&funnel_in2_out_funnel_merg>; + }; + }; + }; + }; + + funnel_in0: funnel@6041000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6041000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-in0"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_in0_out_funnel_merg: endpoint { + remote-endpoint = + <&funnel_merg_in_funnel_in0>; + }; + }; + + port@1 { + reg = <0>; + funnel_in0_in_rpm_etm0: endpoint { + slave-mode; + remote-endpoint = + <&rpm_etm0_out_funnel_in0>; + }; + }; + + port@2 { + reg = <6>; + funnel_in0_in_funnel_qatb: endpoint { + slave-mode; + remote-endpoint = + <&funnel_qatb_out_funnel_in0>; + }; + }; + + port@3 { + reg = <7>; + funnel_in0_in_stm: endpoint { + slave-mode; + remote-endpoint = + <&stm_out_funnel_in0>; + }; + }; + }; + }; + + funnel_in1: funnel@6042000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6042000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-in1"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_in1_out_funnel_merg: endpoint { + remote-endpoint = + <&funnel_merg_in_funnel_in1>; + }; + }; + port@1 { + reg = <3>; + funnel_in1_in_audio_etm0: endpoint { + slave-mode; + remote-endpoint = + <&audio_etm0_out_funnel_in1>; + }; + }; + port@2 { + reg = <4>; + funnel_in1_in_modem_etm0: endpoint { + slave-mode; + remote-endpoint = + <&modem_etm0_out_funnel_in1>; + }; + }; + }; + }; + + funnel_in2: funnel@6043000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6043000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-in2"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_in2_out_funnel_merg: endpoint { + remote-endpoint = + <&funnel_merg_in_funnel_in2>; + }; + }; + + port@1 { + reg = <3>; + funnel_in2_in_turing_etm0: endpoint { + slave-mode; + remote-endpoint = + <&turing_etm0_out_funnel_in2>; + }; + }; + + port@2 { + reg = <7>; + funnel_in2_in_funnel_apss: endpoint { + slave-mode; + remote-endpoint = + <&funnel_apss_out_funnel_in2>; + }; + }; + }; + }; + + stm: stm@6002000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b962>; + + reg = <0x6002000 0x1000>, + <0x09000000 0x1000000>; + reg-names = "stm-base", "stm-stimulus-base"; + + coresight-name = "coresight-stm"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + stm_out_funnel_in0: endpoint { + remote-endpoint = <&funnel_in0_in_stm>; + }; + }; + }; + + tpda: tpda@6004000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x6004000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda"; + + qcom,tpda-atid = <65>; + qcom,bc-elem-size = <10 32>, + <13 32>; + qcom,tc-elem-size = <13 32>; + qcom,dsb-elem-size = <0 32>, + <2 32>, + <3 32>, + <5 32>, + <6 32>, + <10 32>, + <11 32>, + <13 32>; + qcom,cmb-elem-size = <3 64>, + <7 64>, + <13 64>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_out_funnel_qatb: endpoint { + remote-endpoint = + <&funnel_qatb_in_tpda>; + }; + }; + + port@1 { + reg = <0>; + tpda_in_tpdm_wcss: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_wcss_out_tpda>; + }; + }; + + port@2 { + reg = <7>; + tpda_in_tpdm_dcc: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_dcc_out_tpda>; + }; + }; + + port@3 { + reg = <9>; + tpda_in_tpdm_0_north: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_0_north_out_tpda>; + }; + }; + port@4 { + reg = <10>; + tpda_in_tpdm_1_south: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_1_south_out_tpda>; + }; + }; + + port@5 { + reg = <11>; + tpda_in_tpdm_2_center: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_2_center_out_tpda>; + }; + }; + + + port@6 { + reg = <12>; + tpda_in_tpdm_3_center: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_3_center_out_tpda>; + }; + }; + }; + }; + + tpdm_0_north: tpdm@6114000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6114000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-0-north"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + qcom,msr-fix-req; + + port { + tpdm_0_north_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_0_north>; + }; + }; + }; + + tpdm_1_south: tpdm@6115000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6115000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-1-south"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + qcom,msr-fix-req; + + port { + tpdm_1_south_out_tpda: endpoint { + remote-endpoint = + <&tpda_in_tpdm_1_south>; + }; + }; + }; + + + tpdm_2_center: tpdm@6116000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6116000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-2-center"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + tpdm_2_center_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_2_center>; + }; + }; + }; + + + tpdm_3_center: tpdm@6117000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6117000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-3-center"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + tpdm_3_center_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_3_center>; + }; + }; + }; + + tpdm_dcc: tpdm@6178000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6178000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-dcc"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + qcom,msr-fix-req; + + port { + tpdm_dcc_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_dcc>; + }; + }; + }; + + tpdm_wcss: tpdm@1440000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x1440000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-wcss"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + tpdm_wcss_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_wcss>; + }; + }; + }; + + funnel_qatb: funnel@6005000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6005000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-qatb"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_qatb_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_funnel_qatb>; + }; + }; + + port@1 { + reg = <1>; + funnel_qatb_in_tpda: endpoint { + slave-mode; + remote-endpoint = + <&tpda_out_funnel_qatb>; + }; + }; + }; + }; + + cti_cpu0: cti@61b8000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x61b8000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu0"; + cpu = <&CPU0>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti_cpu1: cti@61b9000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x61b9000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu1"; + cpu = <&CPU1>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti_cpu2: cti@61ba000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x61ba000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu2"; + cpu = <&CPU2>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti_cpu3: cti@61bb000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x61bb000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu3"; + cpu = <&CPU3>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti0: cti@6010000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6010000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti0"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti1: cti@6011000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6011000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti1"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti2: cti@6012000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6012000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti2"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti3: cti@6013000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6013000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti3"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti4: cti@6014000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6014000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti4"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti5: cti@6015000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6015000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti5"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti6: cti@6016000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6016000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti6"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti7: cti@6017000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6017000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti7"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti8: cti@6018000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6018000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti8"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti9: cti@6019000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x6019000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti9"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti10: cti@601a000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601a000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti10"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti11: cti@601b000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601b000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti11"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti12: cti@601c000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601c000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti12"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti13: cti@601d000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601d000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti13"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti14: cti@601e000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601e000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti14"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + cti15: cti@601f000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + reg = <0x601f000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti15"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + }; + + rpm_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-rpm-etm0"; + qcom,inst-id = <4>; + + port{ + rpm_etm0_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_rpm_etm0>; + }; + }; + }; + + turing_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-turing-etm0"; + qcom,inst-id = <13>; + + port{ + turing_etm0_out_funnel_in2: endpoint { + remote-endpoint = + <&funnel_in2_in_turing_etm0>; + }; + }; + }; + + modem_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-modem-etm0"; + qcom,inst-id = <2>; + + port { + modem_etm0_out_funnel_in1: endpoint { + remote-endpoint = + <&funnel_in1_in_modem_etm0>; + }; + }; + }; + + audio_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-audio-etm0"; + qcom,inst-id = <5>; + + port { + audio_etm0_out_funnel_in1: endpoint { + remote-endpoint = + <&funnel_in1_in_audio_etm0>; + }; + }; + }; + + etm0: etm@61bc000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x61bc000 0x1000>; + cpu = <&CPU0>; + + coresight-name = "coresight-etm0"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + etm0_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm0>; + }; + }; + }; + + etm1: etm@61bd000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x61bd000 0x1000>; + cpu = <&CPU1>; + + coresight-name = "coresight-etm1"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + etm1_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm1>; + }; + }; + }; + + etm2: etm@61be000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x61be000 0x1000>; + cpu = <&CPU2>; + + coresight-name = "coresight-etm2"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + etm2_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm2>; + }; + }; + }; + + etm3: etm@61bf000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x000bb95d>; + + reg = <0x61bf000 0x1000>; + cpu = <&CPU3>; + + coresight-name = "coresight-etm3"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port { + etm3_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm3>; + }; + }; + }; + + funnel_apss: funnel@61a1000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x61a1000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-apss"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_apss_out_funnel_in2: endpoint { + remote-endpoint = + <&funnel_in2_in_funnel_apss>; + }; + }; + port@1 { + reg = <0>; + funnel_apss_in_etm0: endpoint { + slave-mode; + remote-endpoint = + <&etm0_out_funnel_apss>; + }; + }; + + port@2 { + reg = <1>; + funnel_apss_in_etm1: endpoint { + slave-mode; + remote-endpoint = + <&etm1_out_funnel_apss>; + }; + }; + + port@3 { + reg = <2>; + funnel_apss_in_etm2: endpoint { + slave-mode; + remote-endpoint = + <&etm2_out_funnel_apss>; + }; + }; + + port@4 { + reg = <3>; + funnel_apss_in_etm3: endpoint { + slave-mode; + remote-endpoint = + <&etm3_out_funnel_apss>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405-cpu.dtsi b/arch/arm64/boot/dts/qcom/qcs405-cpu.dtsi index b34ce592856cfbfc7897a879dfae6f438b710299..3f83fd04b6de1712608b59de3d98f47f599c0938 100644 --- a/arch/arm64/boot/dts/qcom/qcs405-cpu.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs405-cpu.dtsi @@ -43,6 +43,7 @@ reg = <0x100>; enable-method = "psci"; next-level-cache = <&L2_1>; + #cooling-cells = <2>; L2_1: l2-cache { compatible = "arm,arch-cache"; cache-level = <2>; @@ -65,6 +66,7 @@ reg = <0x101>; enable-method = "psci"; next-level-cache = <&L2_1>; + #cooling-cells = <2>; L1_I_101: l1-icache { compatible = "arm,arch-cache"; qcom,dump-size = <0x8800>; @@ -81,6 +83,7 @@ reg = <0x102>; enable-method = "psci"; next-level-cache = <&L2_1>; + #cooling-cells = <2>; L1_I_102: l1-icache { compatible = "arm,arch-cache"; qcom,dump-size = <0x8800>; @@ -97,6 +100,7 @@ reg = <0x103>; enable-method = "psci"; next-level-cache = <&L2_1>; + #cooling-cells = <2>; L1_I_103: l1-icache { compatible = "arm,arch-cache"; qcom,dump-size = <0x8800>; diff --git a/arch/arm64/boot/dts/qcom/qcs405-gdsc.dtsi b/arch/arm64/boot/dts/qcom/qcs405-gdsc.dtsi index 7dac7132fd177b1077d23c0b1b9b1c54d31b9540..52d3ffb8a78a14c711ed8867ff365cb6550c14cf 100644 --- a/arch/arm64/boot/dts/qcom/qcs405-gdsc.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs405-gdsc.dtsi @@ -14,14 +14,14 @@ &soc { /* GDSCs in Global CC */ gdsc_mdss: qcom,gdsc@184d078 { - compatible = "regulator-fixed"; + compatible = "qcom,gdsc"; regulator-name = "gdsc_mdss"; reg = <0x184d078 0x4>; status = "disabled"; }; gdsc_oxili_gx: qcom,gdsc@185901c { - compatible = "regulator-fixed"; + compatible = "qcom,gdsc"; regulator-name = "gdsc_oxili_gx"; reg = <0x185901c 0x4>; status = "disabled"; diff --git a/arch/arm64/boot/dts/qcom/qcs405-ion.dtsi b/arch/arm64/boot/dts/qcom/qcs405-ion.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..0fc6ca98c1b7b97a0e3bad2f11e743717b6a26bb --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs405-ion.dtsi @@ -0,0 +1,30 @@ +/* Copyright (c) 2018, 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,ion { + compatible = "qcom,msm-ion"; + #address-cells = <1>; + #size-cells = <0>; + + system_heap: qcom,ion-heap@25 { + reg = <25>; + qcom,ion-heap-type = "SYSTEM"; + }; + + qcom,ion-heap@27 { /* QSEECOM HEAP */ + reg = <27>; + memory-region = <&qseecom_mem>; + qcom,ion-heap-type = "DMA"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/qcs405-pinctrl.dtsi index ce806af6f4e9ce569c175ad05e261744575a50db..0463c5c6372043cfba24a3591e0d4c3147261551 100644 --- a/arch/arm64/boot/dts/qcom/qcs405-pinctrl.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs405-pinctrl.dtsi @@ -48,5 +48,738 @@ }; }; }; + + blsp1_uart1 { + blsp1_uart1_active: blsp1_uart1_active { + mux { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + function = "blsp_uart0"; + }; + + config { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart1_sleep: blsp1_uart1_sleep { + mux { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + function = "gpio"; + }; + + config { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + blsp1_uart2 { + blsp1_uart2_active: blsp1_uart2_active { + mux { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + function = "blsp_uart1"; + }; + + config { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart2_sleep: blsp1_uart2_sleep { + mux { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + function = "gpio"; + }; + + config { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + blsp1_uart3 { + blsp1_uart3_active: blsp1_uart3_active { + mux { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + function = "blsp_uart2"; + }; + + config { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart3_sleep: blsp1_uart3_sleep { + mux { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + function = "gpio"; + }; + + config { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + blsp1_uart4: blsp1_uart4 { + blsp1_uart4_tx_active: blsp1_uart4_tx_active { + mux { + pins = "gpio82"; + function = "blsp_uart3"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart4_tx_sleep: blsp1_uart4_tx_sleep { + mux { + pins = "gpio82"; + function = "gpio"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + blsp1_uart4_rxcts_active: blsp1_uart4_rxcts_active { + mux { + pins = "gpio83", "gpio84"; + function = "blsp_uart3"; + }; + + config { + pins = "gpio83", "gpio84"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart4_rxcts_sleep: blsp1_uart4_rxcts_sleep { + mux { + pins = "gpio83", "gpio84"; + function = "gpio"; + }; + + config { + pins = "gpio83", "gpio84"; + drive-strength = <2>; + bias-no-pull; + }; + }; + + blsp1_uart4_rfr_active: blsp1_uart4_rfr_active { + mux { + pins = "gpio85"; + function = "blsp_uart3"; + }; + + config { + pins = "gpio85"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart4_rfr_sleep: blsp1_uart4_rfr_sleep { + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + drive-strength = <2>; + bias-no-pull; + }; + }; + }; + + blsp2_uart1 { + blsp2_uart1_active: blsp2_uart1_active { + mux { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + function = "blsp_uart5"; + }; + + config { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp2_uart1_sleep: blsp2_uart1_sleep { + mux { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + /* SPI CONFIGURATION */ + spi_1 { + spi_1_active: spi_1_active { + mux { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + function = "blsp_spi0"; + }; + + config { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_1_sleep: spi_1_sleep { + mux { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + function = "blsp_spi0"; + }; + + config { + pins = "gpio30", "gpio31", + "gpio32", "gpio33"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_2 { + spi_2_active: spi_2_active { + mux { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + function = "blsp_spi1"; + }; + + config { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_2_sleep: spi_2_sleep { + mux { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + function = "blsp_spi1"; + }; + + config { + pins = "gpio22", "gpio23", + "gpio24", "gpio25"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_3 { + spi_3_active: spi_3_active { + mux { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + function = "blsp_spi2"; + }; + + config { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_3_sleep: spi_3_sleep { + mux { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + function = "blsp_spi2"; + }; + + config { + pins = "gpio17", "gpio18", + "gpio19", "gpio20"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_4 { + spi_4_active: spi_4_active { + mux { + pins = "gpio82", "gpio83", + "gpio84", "gpio85"; + function = "blsp_spi3"; + }; + + config { + pins = "gpio82", "gpio83", + "gpio84", "gpio85"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_4_sleep: spi_4_sleep { + mux { + pins = "gpio82", "gpio83", + "gpio84", "gpio85"; + function = "blsp_spi3"; + }; + + config { + pins = "gpio82", "gpio83", + "gpio84", "gpio85"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_5 { + spi_5_active: spi_5_active { + mux { + pins = "gpio37", "gpio38", + "gpio117", "gpio118"; + function = "blsp_spi4"; + }; + + config { + pins = "gpio37", "gpio38", + "gpio117", "gpio118"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_5_sleep: spi_5_sleep { + mux { + pins = "gpio37", "gpio38", + "gpio117", "gpio118"; + function = "blsp_spi4"; + }; + + config { + pins = "gpio37", "gpio38", + "gpio117", "gpio118"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + spi_6 { + spi_6_active: spi_6_active { + mux { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + function = "blsp_spi5"; + }; + + config { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + drive-strength = <6>; + bias-disable; + }; + }; + + spi_6_sleep: spi_6_sleep { + mux { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + function = "blsp_spi5"; + }; + + config { + pins = "gpio26", "gpio27", + "gpio28", "gpio29"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + i2c_1 { + i2c_1_active: i2c_1_active { + /* active state */ + mux { + pins = "gpio32", "gpio33"; + function = "blsp_i2c0"; + }; + + config { + pins = "gpio32", "gpio33"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_1_sleep: i2c_1_sleep { + /* suspended state */ + mux { + pins = "gpio32", "gpio33"; + function = "gpio"; + }; + + config { + pins = "gpio32", "gpio33"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + i2c_2 { + i2c_2_active: i2c_2_active { + /* active state */ + mux { + pins = "gpio24", "gpio25"; + function = "blsp_i2c1"; + }; + + config { + pins = "gpio24", "gpio25"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_2_sleep: i2c_2_sleep { + /* suspended state */ + mux { + pins = "gpio24", "gpio25"; + function = "gpio"; + }; + + config { + pins = "gpio24", "gpio25"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + i2c_3 { + i2c_3_sda_active: i2c_3_sda_active { + /* active state */ + mux { + pins = "gpio19"; + function = "blsp_i2c_sda_a2"; + }; + + config { + pins = "gpio19"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_3_scl_active: i2c_3_scl_active { + /* active state */ + mux { + pins = "gpio20"; + function = "blsp_i2c_scl_a2"; + }; + + config { + pins = "gpio20"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_3_sleep: i2c_3_sleep { + /* suspended state */ + mux { + pins = "gpio19", "gpio20"; + function = "gpio"; + }; + + config { + pins = "gpio19", "gpio20"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + i2c_4 { + i2c_4_active: i2c_4_active { + /* active state */ + mux { + pins = "gpio84", "gpio85"; + function = "blsp_i2c3"; + }; + + config { + pins = "gpio84", "gpio85"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_4_sleep: i2c_4_sleep { + /* suspended state */ + mux { + pins = "gpio84", "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio84", "gpio85"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + i2c_5 { + i2c_5_active: i2c_5_active { + /* active state */ + mux { + pins = "gpio117", "gpio118"; + function = "blsp_i2c4"; + }; + + config { + pins = "gpio117", "gpio118"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_5_sleep: i2c_5_sleep { + /* suspended state */ + mux { + pins = "gpio117", "gpio118"; + function = "gpio"; + }; + + config { + pins = "gpio117", "gpio118"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + i2c_6 { + i2c_6_active: i2c_6_active { + /* active state */ + mux { + pins = "gpio28", "gpio29"; + function = "blsp_i2c5"; + }; + + config { + pins = "gpio28", "gpio29"; + drive-strength = <2>; + bias-disable; + }; + }; + + i2c_6_sleep: i2c_6_sleep { + /* suspended state */ + mux { + pins = "gpio28", "gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio29"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + /* SDC pin type */ + sdc1_clk_on: sdc1_clk_on { + config { + pins = "sdc1_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc1_clk_off: sdc1_clk_off { + config { + pins = "sdc1_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc1_cmd_on: sdc1_cmd_on { + config { + pins = "sdc1_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc1_cmd_off: sdc1_cmd_off { + config { + pins = "sdc1_cmd"; + num-grp-pins = <1>; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc1_data_on: sdc1_data_on { + config { + pins = "sdc1_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc1_data_off: sdc1_data_off { + config { + pins = "sdc1_data"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc1_rclk_on: sdc1_rclk_on { + config { + pins = "sdc1_rclk"; + bias-pull-down; /* pull down */ + }; + }; + + sdc1_rclk_off: sdc1_rclk_off { + config { + pins = "sdc1_rclk"; + bias-pull-down; /* pull down */ + }; + }; + + sdc2_clk_on: sdc2_clk_on { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_clk_off: sdc2_clk_off { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cmd_on: sdc2_cmd_on { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_cmd_off: sdc2_cmd_off { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_data_on: sdc2_data_on { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_off: sdc2_data_off { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cd_on: cd_on { + mux { + pins = "gpio21"; /* sdcard_det */ + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + sdc2_cd_off: cd_off { + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive-strength = <2>; + bias-disable; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/qcom/qcs405-regulator.dtsi b/arch/arm64/boot/dts/qcom/qcs405-regulator.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..5d2842fb2ad2c6b90b43f54f33e750d67a3283b7 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs405-regulator.dtsi @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2018, 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 + +&rpm_bus { + /* PMS405 S1 - VDD_MX/CX supply */ + rpm-regulator-smpa1 { + status = "okay"; + pms405_s1_level: regulator-s1-level { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_s1_level"; + qcom,set = <3>; + regulator-min-microvolt = + ; + regulator-max-microvolt = + ; + qcom,use-voltage-level; + }; + + pms405_s1_floor_level: regulator-s1-floor-level { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_s1_floor_level"; + qcom,set = <3>; + regulator-min-microvolt = + ; + regulator-max-microvolt = + ; + qcom,use-voltage-floor-level; + qcom,always-send-voltage; + }; + + pms405_s1_level_ao: regulator-s1-level-ao { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_s1_level_ao"; + qcom,set = <1>; + regulator-min-microvolt = + ; + regulator-max-microvolt = + ; + qcom,use-voltage-level; + }; + + cx_cdev: cx-cdev-lvl { + compatible = "qcom,regulator-cooling-device"; + regulator-cdev-supply = <&pms405_s1_floor_level>; + regulator-levels = ; + #cooling-cells = <2>; + }; + }; + + /* PMS405 S2 - VDD_LPI_CX supply */ + rpm-regulator-smpa2 { + status = "okay"; + pms405_s2_level: regulator-s2-level { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_s2_level"; + qcom,set = <3>; + regulator-min-microvolt = + ; + regulator-max-microvolt = + ; + qcom,use-voltage-level; + }; + + pms405_s2_floor_level: regulator-s2-floor-level { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_s2_floor_level"; + qcom,set = <3>; + regulator-min-microvolt = + ; + regulator-max-microvolt = + ; + qcom,use-voltage-floor-level; + qcom,always-send-voltage; + }; + }; + + rpm-regulator-smpa4 { + status = "okay"; + pms405_s4: regulator-s4 { + regulator-min-microvolt = <1728000>; + regulator-max-microvolt = <1920000>; + qcom,init-voltage = <1728000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa1 { + status = "okay"; + pms405_l1: regulator-l1 { + regulator-min-microvolt = <1240000>; + regulator-max-microvolt = <1352000>; + qcom,init-voltage = <1240000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa2 { + status = "okay"; + pms405_l2: regulator-l2 { + regulator-min-microvolt = <1048000>; + regulator-max-microvolt = <1280000>; + qcom,init-voltage = <1048000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa3 { + status = "okay"; + pms405_l3: regulator-l3 { + regulator-min-microvolt = <976000>; + regulator-max-microvolt = <1160000>; + qcom,init-voltage = <976000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa4 { + status = "okay"; + pms405_l4: regulator-l4 { + regulator-min-microvolt = <1144000>; + regulator-max-microvolt = <1256000>; + qcom,init-voltage = <1144000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa5 { + status = "okay"; + pms405_l5: regulator-l5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + status = "okay"; + }; + + pms405_l5_ao: regulator-l5-ao { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_l5_ao"; + qcom,set = <1>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa6 { + status = "okay"; + pms405_l6: regulator-l6 { + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <1896000>; + qcom,init-voltage = <1704000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa7 { + status = "okay"; + pms405_l7: regulator-l7 { + regulator-min-microvolt = <1616000>; + regulator-max-microvolt = <3000000>; + qcom,init-voltage = <1616000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa8 { + status = "okay"; + pms405_l8: regulator-l8 { + regulator-min-microvolt = <1136000>; + regulator-max-microvolt = <1352000>; + qcom,init-voltage = <1136000>; + status = "okay"; + }; + }; + + /* PMS405 L9 - VDD_LPI_MX supply */ + rpm-regulator-ldoa9 { + status = "okay"; + pms405_l9_level: regulator-l9-level { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_l9_level"; + qcom,set = <3>; + regulator-min-microvolt = + ; + regulator-max-microvolt = + ; + qcom,use-voltage-level; + }; + + pms405_l9_floor_level: regulator-l9-floor-level { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pms405_l9_floor_level"; + qcom,set = <3>; + regulator-min-microvolt = + ; + regulator-max-microvolt = + ; + qcom,use-voltage-floor-level; + qcom,always-send-voltage; + }; + }; + + rpm-regulator-ldoa10 { + status = "okay"; + pms405_l10: regulator-l10 { + regulator-min-microvolt = <2936000>; + regulator-max-microvolt = <3088000>; + qcom,init-voltage = <2936000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa11 { + status = "okay"; + pms405_l11: regulator-l11 { + regulator-min-microvolt = <2696000>; + regulator-max-microvolt = <3304000>; + qcom,init-voltage = <2696000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa12 { + status = "okay"; + pms405_l12: regulator-l12 { + regulator-min-microvolt = <2968000>; + regulator-max-microvolt = <3300000>; + qcom,init-voltage = <2968000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa13 { + status = "okay"; + pms405_l13: regulator-l13 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + qcom,init-voltage = <3000000>; + status = "okay"; + }; + }; +}; + +/* Stub regulators */ +/ { + /* VDD_APC supply */ + apc_vreg_corner: regulator-apc-corner { + compatible = "qcom,stub-regulator"; + regulator-name = "apc_corner"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <3>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405-rumi.dtsi b/arch/arm64/boot/dts/qcom/qcs405-rumi.dtsi index 4c27aa9f1ea2f1e483b269e29898cd3dae238d1c..12fdb0f555ce3b143f2ce39550105fe06680c3f2 100644 --- a/arch/arm64/boot/dts/qcom/qcs405-rumi.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs405-rumi.dtsi @@ -13,6 +13,30 @@ #include "qcs405.dtsi" &soc { + usb_emu_phy: usb_emu_phy@78ccd00 { + compatible = "qcom,usb-emu-phy"; + reg = <0x78ccd00 0x9500>, + <0x79b8800 0x100>; + reg-names = "base", "qcratch_base"; + + qcom,emu-init-seq = <0xfff0 0x4 + 0xfff3 0x4 + 0x40 0x4 + 0xfff3 0x4 + 0xfff0 0x4 + 0x100000 0x20 + 0x0 0x20 + 0x1a0 0x20 + 0x100000 0x3c + 0x0 0x3c + 0x10060 0x3c + 0x0 0x4>; + }; + + usb_nop_phy: usb_nop_phy { + compatible = "usb-nop-xceiv"; + }; + timer { clock-frequency = <0x100000>; }; @@ -21,3 +45,78 @@ clock-frequency = <0x100000>; }; }; + +&usb0 { + dwc3@78c0000 { + usb-phy = <&usb_emu_phy>, <&usb_nop_phy>; + maximum-speed = "high-speed"; + dr_mode = "peripheral"; + }; +}; + +&rpm_bus { + /delete-node/ rpm-regulator-smpa1; + /delete-node/ rpm-regulator-smpa2; + /delete-node/ rpm-regulator-smpa4; + /delete-node/ rpm-regulator-ldoa1; + /delete-node/ rpm-regulator-ldoa2; + /delete-node/ rpm-regulator-ldoa3; + /delete-node/ rpm-regulator-ldoa4; + /delete-node/ rpm-regulator-ldoa5; + /delete-node/ rpm-regulator-ldoa6; + /delete-node/ rpm-regulator-ldoa7; + /delete-node/ rpm-regulator-ldoa8; + /delete-node/ rpm-regulator-ldoa9; + /delete-node/ rpm-regulator-ldoa10; + /delete-node/ rpm-regulator-ldoa11; + /delete-node/ rpm-regulator-ldoa12; + /delete-node/ rpm-regulator-ldoa13; +}; + +&soc { + /delete-node/ qcom,spmi@200f000; +}; + +&rpm_bus { + rpm-standalone; +}; + +&thermal_zones { + /delete-node/ aoss-lowf; +}; + +#include "qcs405-stub-regulator.dtsi" + +&sdhc_1 { + /* VDD external regulator is enabled/disabled by pms405_l6 */ + vdd-io-supply = <&pms405_l6>; + qcom,vdd-io-always-on; + qcom,vdd-io-lpm-sup; + qcom,vdd-io-voltage-level = <1704000 1800000>; + qcom,vdd-io-current-level = <0 325000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on>; + pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off>; + + qcom,clk-rates = <400000 20000000 25000000 50000000>; + qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v"; + + status = "ok"; +}; + +&sdhc_2 { + /* VDD is an external regulator eLDO5 */ + vdd-io-supply = <&pms405_l11>; + qcom,vdd-io-voltage-level = <2696000 3304000>; + qcom,vdd-io-current-level = <0 22000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>; + + qcom,clk-rates = <400000 20000000 25000000 50000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50"; + + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405-stub-regulator.dtsi b/arch/arm64/boot/dts/qcom/qcs405-stub-regulator.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..378135169d42f83113feb70e8fd697ee2f71f250 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs405-stub-regulator.dtsi @@ -0,0 +1,186 @@ +/* Copyright (c) 2018, 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. + */ + +/* Stub regulators */ + +/ { + /* PMS405_S1 - VDD_MX/CX supply */ + pms405_s1_level: regulator-pms405-s1-level { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_s1_level"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + pms405_s1_floor_level: regulator-pms405-s1-floor-level { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_s1_floor_level"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + pms405_s1_level_ao: regulator-pms405-s1-level-ao { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_s1_level_ao"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + /* PMS405_S2 - VDD_LPI_CX supply */ + pms405_s2_level: regulator-pms405-s2-level { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_s2_level"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + pms405_s2_floor_level: regulator-pms405-s2-floor-level { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_s2_floor_level"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + pms405_s4: regulator-pms405-s4 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_s4"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1728000>; + regulator-max-microvolt = <1920000>; + }; + + pms405_l1: regulator-pms405-l1 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l1"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1240000>; + regulator-max-microvolt = <1352000>; + }; + + pms405_l2: regulator-pms405-l2 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l2"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1048000>; + regulator-max-microvolt = <1280000>; + }; + + pms405_l3: regulator-pms405-l3 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l3"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <976000>; + regulator-max-microvolt = <1160000>; + }; + + pms405_l4: regulator-pms405-l4 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l4"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1144000>; + regulator-max-microvolt = <1256000>; + }; + + pms405_l5: regulator-pms405-l5 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l5"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pms405_l5_ao: regulator-pms405-l5-ao { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l5_ao"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pms405_l6: regulator-pms405-l6 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l6"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <1896000>; + }; + + pms405_l7: regulator-pms405-l7 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l7"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1616000>; + regulator-max-microvolt = <3000000>; + }; + + pms405_l8: regulator-pms405-l8 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l8"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1136000>; + regulator-max-microvolt = <1352000>; + }; + + /* PMS405 L9 - VDD_LPI_MX supply */ + pms405_l9_level: regulator-pms405-l9-level { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l9_level"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + pms405_l9_floor_level: regulator-pms405-l9-floor-level { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l9_floor_level"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + pms405_l10: regulator-pms405-l10 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l10"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2936000>; + regulator-max-microvolt = <3088000>; + }; + + pms405_l11: regulator-pms405-l11 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l11"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2696000>; + regulator-max-microvolt = <3304000>; + }; + + pms405_l12: regulator-pms405-l12 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l12"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2968000>; + regulator-max-microvolt = <3300000>; + }; + + pms405_l13: regulator-pms405-l13 { + compatible = "qcom,stub-regulator"; + regulator-name = "pms405_l13"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405-thermal.dtsi b/arch/arm64/boot/dts/qcom/qcs405-thermal.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..64559e4f8d32eae4cd9bbce1ccb6b5bb06a38711 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs405-thermal.dtsi @@ -0,0 +1,356 @@ +/* Copyright (c) 2018, 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 + +&soc { + qmi-tmd-devices { + compatible = "qcom,qmi-cooling-devices"; + + modem { + qcom,instance-id = <0x0>; + + modem_pa: modem_pa { + qcom,qmi-dev-name = "pa"; + #cooling-cells = <2>; + }; + + modem_proc: modem_proc { + qcom,qmi-dev-name = "modem"; + #cooling-cells = <2>; + }; + + modem_current: modem_current { + qcom,qmi-dev-name = "modem_current"; + #cooling-cells = <2>; + }; + + modem_vdd: modem_vdd { + qcom,qmi-dev-name = "cpuv_restriction_cold"; + #cooling-cells = <2>; + }; + }; + }; +}; + +&thermal_zones { + aoss-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 0>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + cpuss-0-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 1>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + cpuss-1-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + cpuss-2-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&tsens0 3>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + cpuss-3-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 4>; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + mhm-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 5>; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + gpu-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 6>; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + q6-hvx-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 7>; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + lpass-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 8>; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + wlan-usr { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 9>; + thermal-governor = "user_space"; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + cpuss-max-step { + polling-delay-passive = <50>; + polling-delay = <100>; + thermal-governor = "step_wise"; + trips { + cpu_trip:cpu-trip { + temperature = <85000>; + hysteresis = <0>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_cdev { + trip = <&cpu_trip>; + cooling-device = + <&CPU0 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + cpu1_cdev { + trip = <&cpu_trip>; + cooling-device = + <&CPU1 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + cpu2_cdev { + trip = <&cpu_trip>; + cooling-device = + <&CPU2 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + cpu3_cdev { + trip = <&cpu_trip>; + cooling-device = + <&CPU3 THERMAL_NO_LIMIT + (THERMAL_MAX_LIMIT-1)>; + }; + }; + }; + + gpu-step { + polling-delay-passive = <250>; + polling-delay = <0>; + thermal-sensors = <&tsens0 6>; + thermal-governor = "step_wise"; + trips { + gpu_step_trip: gpu-step-trip { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + }; + }; + + cpuss-0-step { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 1>; + thermal-governor = "step_wise"; + trips { + cpuss_0_step_trip: cpuss-0-step-trip { + temperature = <105000>; + hysteresis = <15000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_cdev { + trip = <&cpuss_0_step_trip>; + cooling-device = + <&CPU0 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + cpuss-1-step { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 2>; + thermal-governor = "step_wise"; + trips { + cpuss_1_step_trip: cpuss-1-step-trip { + temperature = <105000>; + hysteresis = <15000>; + type = "passive"; + }; + }; + cooling-maps { + cpu1_cdev { + trip = <&cpuss_1_step_trip>; + cooling-device = + <&CPU1 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + cpuss-2-step { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 3>; + thermal-governor = "step_wise"; + trips { + cpuss_2_step_trip: cpuss-2-step-trip { + temperature = <105000>; + hysteresis = <15000>; + type = "passive"; + }; + }; + cooling-maps { + cpu2_cdev { + trip = <&cpuss_2_step_trip>; + cooling-device = + <&CPU2 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + cpuss-3-step { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tsens0 4>; + thermal-governor = "step_wise"; + trips { + cpuss_3_step_trip: cpuss-3-step-trip { + temperature = <105000>; + hysteresis = <15000>; + type = "passive"; + }; + }; + cooling-maps { + cpu3_cdev { + trip = <&cpuss_3_step_trip>; + cooling-device = + <&CPU3 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + aoss-lowf { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "low_limits_floor"; + thermal-sensors = <&tsens0 0>; + tracks-low; + trips { + aoss_lowf: aoss-lowf { + temperature = <5000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + cpu0_cdev { + trip = <&aoss_lowf>; + cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; + cx_vdd_cdev { + trip = <&aoss_lowf>; + cooling-device = <&cx_cdev 0 0>; + }; + modem_vdd_cdev { + trip = <&aoss_lowf>; + cooling-device = <&modem_vdd 0 0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs405.dtsi b/arch/arm64/boot/dts/qcom/qcs405.dtsi index d6bfe91c3cded1a67e11de505c8a4064e800b4ce..df5ca407bf2731009b0930e0caac586a4a664f1f 100644 --- a/arch/arm64/boot/dts/qcom/qcs405.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs405.dtsi @@ -11,7 +11,13 @@ * GNU General Public License for more details. */ +#include #include "skeleton64.dtsi" +#include +#include +#include +#include +#include / { model = "Qualcomm Technologies, Inc. QCS405"; @@ -27,16 +33,87 @@ #address-cells = <2>; #size-cells = <2>; ranges; + + removed_region0: removed_region@85600000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x85600000 0x0 0x900000>; + }; + + smem_region: smem@85f00000 { + no-map; + reg = <0x0 0x85f00000 0x0 0x200000>; + }; + + removed_region1: removed_region@86100000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x86100000 0x0 0x300000>; + }; + + wlan_fw_mem: wlan_fw_mem@86400000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x86400000 0x0 0x1c00000>; + }; + + adsp_fw_mem: adsp_fw_mem@88000000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x88000000 0x0 0x1a00000>; + }; + + cdsp_fw_mem: cdsp_fw_mem@89a00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x89a00000 0x0 0x600000>; + }; + + secure_mem: secure_region { + compatible = "shared-dma-pool"; + reusable; + alignment = <0 0x400000>; + size = <0 0x7000000>; + }; + + qseecom_mem: qseecom_region { + compatible = "shared-dma-pool"; + reusable; + alignment = <0 0x400000>; + size = <0 0x1000000>; + }; + + adsp_mem: adsp_region { + compatible = "shared-dma-pool"; + reusable; + alignment = <0 0x400000>; + size = <0 0x400000>; + }; + + /* global autoconfigured region for contiguous allocations */ + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x400000>; + size = <0 0x1000000>; + linux,cma-default; + }; }; - aliases { }; + aliases { + sdhc1 = &sdhc_1; /* SDC1 eMMC slot */ + sdhc2 = &sdhc_2; /* SDC2 SD Card slot */ + }; soc: soc { }; }; #include "qcs405-pinctrl.dtsi" +#include "qcs405-blsp.dtsi" #include "qcs405-cpu.dtsi" +#include "qcs405-ion.dtsi" &soc { #address-cells = <1>; @@ -133,6 +210,28 @@ clock-frequency = <32768>; }; + clock_rpmcc: qcom,rpmcc { + compatible = "qcom,rpmcc-qcs405"; + #clock-cells = <1>; + }; + + clock_gcc: qcom,gcc { + compatible = "qcom,gcc-qcs405", "syscon"; + reg = <0x1800000 0x80000>; + reg-names = "cc_base"; + vdd_cx-supply = <&pms405_s1_level>; + clocks = <&clock_rpmcc RPM_SMD_XO_CLK_SRC>; + clock-names = "cxo"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + clock_cpu: qcom,cpu { + compatible = "qcom,dummycc"; + clock-output-names = "cpu_clocks"; + #clock-cells = <1>; + }; + cpu-pmu { compatible = "arm,armv8-pmuv3"; interrupts = <1 7 0xff00>; @@ -143,6 +242,30 @@ qcom,pipe-attr-ee; }; + slim_aud: slim@c1c0000 { + cell-index = <1>; + compatible = "qcom,slim-ngd"; + reg = <0xc1c0000 0x2c000>, + <0xc184000 0x2a000>; + reg-names = "slimbus_physical", "slimbus_bam_physical"; + interrupts = <0 163 0>, <0 180 0>; + interrupt-names = "slimbus_irq", "slimbus_bam_irq"; + qcom,apps-ch-pipes = <0x7c0000>; + qcom,ea-pc = <0x2e0>; + status = "disabled"; + }; + + slim_qca: slim@c240000 { + cell-index = <3>; + compatible = "qcom,slim-ngd"; + reg = <0xc240000 0x2c000>, + <0xc204000 0x20000>; + reg-names = "slimbus_physical", "slimbus_bam_physical"; + interrupts = <0 191 0>, <0 63 0>; + interrupt-names = "slimbus_irq", "slimbus_bam_irq"; + status = "disabled"; + }; + blsp1_uart2: serial@78b0000 { compatible = "qcom,msm-uartdm", "qcom,msm-uartdm-v1.4"; reg = <0x78b0000 0x200>; @@ -150,12 +273,37 @@ }; - dcc: dcc@b3000 { - compatible = "qcom,dcc"; - reg = <0xb3000 0x1000>, - <0xb4000 0x2000>; + dcc: dcc_v2@b2000 { + compatible = "qcom,dcc_v2"; + reg = <0x000b2000 0x1000>, + <0x000bf800 0x800>; reg-names = "dcc-base", "dcc-ram-base"; - qcom,save-reg; + dcc-ram-offset = <0x400>; + }; + + rpm_bus: qcom,rpm-smd { + compatible = "qcom,rpm-smd"; + rpm-channel-name = "rpm_requests"; + rpm-channel-type = <15>; /* SMD_APPS_RPM */ + }; + + spmi_bus: qcom,spmi@200f000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0x200f000 0x1000>, + <0x2400000 0x800000>, + <0x2c00000 0x800000>, + <0x3800000 0x200000>, + <0x200a000 0x2100>; + reg-names = "core", "chnls", "obsrvr", "intr", "cnfg"; + interrupt-names = "periph_irq"; + interrupts = ; + qcom,ee = <0>; + qcom,channel = <0>; + #address-cells = <2>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <4>; + cell-index = <0>; }; qcom,wdt@b017000 { @@ -210,4 +358,317 @@ reg = <0x94c 200>; }; }; + + tsens0: tsens@4a8000 { + compatible = "qcom,qcs405-tsens"; + reg = <0x4a8000 0x1000>, + <0x4a9000 0x1000>, + <0xa4000 0x1000>; + reg-names = "tsens_srot_physical", + "tsens_tm_physical", + "tsens_eeprom_physical"; + interrupts = <0 184 0>; + interrupt-names = "tsens-upper-lower"; + #thermal-sensor-cells = <1>; + }; + + tcsr_mutex_block: syscon@1905000 { + compatible = "syscon"; + reg = <0x1905000 0x20000>; + }; + + tcsr_mutex: hwlock { + compatible = "qcom,tcsr-mutex"; + syscon = <&tcsr_mutex_block 0 0x1000>; + #hwlock-cells = <1>; + }; + + smem: qcom,smem { + compatible = "qcom,smem"; + memory-region = <&smem_region>; + hwlocks = <&tcsr_mutex 3>; + }; + + rpm_msg_ram: memory@60000 { + compatible = "qcom,rpm-msg-ram"; + reg = <0x60000 0x6000>; + }; + + apcs: syscon@b011000 { + compatible = "syscon"; + reg = <0xb011000 0x4>; + }; + + apcs_glb: mailbox@b011000 { + compatible = "qcom,msm8916-apcs-kpss-global"; + reg = <0xb011000 0x1000>; + + #mbox-cells = <1>; + }; + + rpm-glink { + compatible = "qcom,glink-rpm"; + interrupts = ; + qcom,rpm-msg-ram = <&rpm_msg_ram>; + mboxes = <&apcs_glb 0>; + }; + + qcom,glink { + compatible = "qcom,glink"; + modem { + qcom,remote-pid = <1>; + transport = "smem"; + mboxes = <&apcs_glb 16>; + mbox-names = "mpss_smem"; + interrupts = ; + + modem_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + }; + + adsp { + qcom,remote-pid = <2>; + transport = "smem"; + mboxes = <&apcs_glb 8>; + mbox-names = "adsp_smem"; + interrupts = ; + + adsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + apr_tal_rpmsg { + qcom,glink-channels = "apr_audio_svc"; + qcom,intents = <0x200 20>; + }; + }; + + cdsp { + qcom,remote-pid = <5>; + transport = "smem"; + mboxes = <&apcs_glb 12>; + mbox-names = "cdsp_smem"; + interrupts = ; + + cdsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + }; + }; + + qcom,glinkpkt { + compatible = "qcom,glinkpkt"; + + qcom,glinkpkt-at-mdm0 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DS"; + qcom,glinkpkt-dev-name = "at_mdm0"; + }; + + qcom,glinkpkt-apr-apps2 { + qcom,glinkpkt-edge = "adsp"; + qcom,glinkpkt-ch-name = "apr_apps2"; + qcom,glinkpkt-dev-name = "apr_apps2"; + }; + + qcom,glinkpkt-data40-cntl { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA40_CNTL"; + qcom,glinkpkt-dev-name = "smdcntl8"; + }; + + qcom,glinkpkt-data1 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA1"; + qcom,glinkpkt-dev-name = "smd7"; + }; + + qcom,glinkpkt-data4 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA4"; + qcom,glinkpkt-dev-name = "smd8"; + }; + + qcom,glinkpkt-data11 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA11"; + qcom,glinkpkt-dev-name = "smd11"; + }; + }; + + qcom,smp2p-modem { + compatible = "qcom,smp2p"; + qcom,smem = <435>, <428>; + interrupts = ; + qcom,ipc = <&apcs 0 18>; + qcom,local-pid = <0>; + qcom,remote-pid = <1>; + + modem_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + modem_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-adsp { + compatible = "qcom,smp2p"; + qcom,smem = <443>, <429>; + interrupts = ; + qcom,ipc = <&apcs 0 10>; + qcom,local-pid = <0>; + qcom,remote-pid = <2>; + + adsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + adsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-cdsp { + compatible = "qcom,smp2p"; + qcom,smem = <94>, <432>; + interrupts = ; + qcom,ipc = <&apcs 0 14>; + qcom,local-pid = <0>; + qcom,remote-pid = <5>; + + cdsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + cdsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + thermal_zones: thermal-zones {}; + + usb0: hsusb@78c0000 { + compatible = "qcom,dwc-usb3-msm"; + reg = <0x78c0000 0x100000>; + reg-names = "core_base"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + interrupts = <0 32 0>; + interrupt-names = "pwr_event_irq"; + /* Using dummy Xo clock, need to check the proper mapping */ + clocks = <&clock_gcc GCC_USB_HS_SYSTEM_CLK>, + <&clock_gcc GCC_PCNOC_USB2_CLK>, + <&clock_gcc GCC_USB30_SLEEP_CLK>, + <&clock_gcc GCC_USB_HS_INACTIVITY_TIMERS_CLK>, + <&clock_gcc GCC_USB20_MOCK_UTMI_CLK>; + clock-names = "core_clk", "iface_clk", "xo", + "sleep_clk", "utmi_clk"; + + qcom,core-clk-rate = <200000000>; + qcom,core-clk-rate-hs = <66666667>; + qcom,dwc-usb3-msm-tx-fifo-size = <27696>; + resets = <&clock_gcc GCC_USB_HS_BCR>; + reset-names = "core_reset"; + + dwc3@78c0000 { + compatible = "snps,dwc3"; + reg = <0x78c0000 0xcd00>; + interrupt-parent = <&intc>; + interrupts = <0 44 0>; + tx-fifo-resize; + linux,sysdev_is_parent; + snps,disable-clk-gating; + snps,has-lpm-erratum; + snps,hird-threshold = /bits/ 8 <0x10>; + }; + }; + + sdhc_1: sdhci@7804000 { + compatible = "qcom,sdhci-msm-v5"; + reg = <0x7804000 0x1000>, <0x7805000 0x1000>; + reg-names = "hc_mem", "cmdq_mem"; + + interrupts = <0 123 0>, <0 138 0>; + interrupt-names = "hc_irq", "pwr_irq"; + + qcom,bus-width = <8>; + qcom,large-address-bus; + + qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 + 192000000 384000000>; + qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v"; + + qcom,devfreq,freq-table = <50000000 200000000>; + + clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>, + <&clock_gcc GCC_SDCC1_APPS_CLK>; + clock-names = "iface_clk", "core_clk"; + + qcom,nonremovable; + status = "disabled"; + }; + + sdhc_2: sdhci@7844000 { + compatible = "qcom,sdhci-msm-v5"; + reg = <0x7844000 0x1000>; + reg-names = "hc_mem"; + + interrupts = <0 125 0>, <0 221 0>; + interrupt-names = "hc_irq", "pwr_irq"; + + qcom,bus-width = <4>; + qcom,large-address-bus; + + qcom,clk-rates = <400000 20000000 25000000 + 50000000 100000000 201500000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", + "SDR104"; + + qcom,devfreq,freq-table = <50000000 201500000>; + + clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>, + <&clock_gcc GCC_SDCC2_APPS_CLK>; + clock-names = "iface_clk", "core_clk"; + + status = "disabled"; + }; +}; + +#include "qcs405-gdsc.dtsi" +#include "pms405.dtsi" +#include "pms405-rpm-regulator.dtsi" +#include "qcs405-regulator.dtsi" +#include "qcs405-thermal.dtsi" + +&gdsc_mdss { + status = "ok"; }; + +&gdsc_oxili_gx { + status = "ok"; +}; + +#include "qcs405-coresight.dtsi" diff --git a/arch/arm64/boot/dts/qcom/sdm640-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm640-pinctrl.dtsi deleted file mode 100644 index 819e551963f6fd2b726fab8762235068ad3ab68b..0000000000000000000000000000000000000000 --- a/arch/arm64/boot/dts/qcom/sdm640-pinctrl.dtsi +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright (c) 2018, 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 { - tlmm: pinctrl@03000000 { - compatible = "qcom,sdm640-pinctrl"; - reg = <0x03000000 0xdc2000>; - interrupts = <0 208 0>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - - qupv3_se0_2uart_pins: qupv3_se0_2uart_pins { - qupv3_se0_2uart_active: qupv3_se0_2uart_active { - mux { - pins = "gpio16", "gpio17"; - function = "qup00"; - }; - - config { - pins = "gpio16", "gpio17"; - drive-strength = <2>; - bias-disable; - }; - }; - - qupv3_se0_2uart_sleep: qupv3_se0_2uart_sleep { - mux { - pins = "gpio16", "gpio17"; - function = "gpio"; - }; - - config { - pins = "gpio16", "gpio17"; - drive-strength = <2>; - bias-disable; - }; - }; - }; - }; -}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm640-qupv3.dtsi deleted file mode 100644 index 6e3e61c630847c79fe9fcb6dd38ac7cfcbcf0562..0000000000000000000000000000000000000000 --- a/arch/arm64/boot/dts/qcom/sdm640-qupv3.dtsi +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (c) 2017-2018, 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 - -&soc { - /* QUPv3 North Instances */ - qupv3_0: qcom,qupv3_0_geni_se@8c0000 { - compatible = "qcom,qupv3-geni-se"; - reg = <0x8c0000 0x6000>; - qcom,bus-mas-id = ; - qcom,bus-slv-id = ; - }; - - /* Debug UART Instance for CDP/MTP platform */ - qupv3_se0_2uart: qcom,qup_uart@0x880000 { - compatible = "qcom,msm-geni-console"; - reg = <0x880000 0x4000>; - reg-names = "se_phys"; - clock-names = "se-clk", "m-ahb", "s-ahb"; - clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>, - <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, - <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&qupv3_se0_2uart_active>; - pinctrl-1 = <&qupv3_se0_2uart_sleep>; - interrupts = ; - qcom,wrapper-core = <&qupv3_0>; - status = "disabled"; - }; -}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-stub-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm640-stub-regulator.dtsi deleted file mode 100644 index ad9c499b245d87166d2f8ef3d4331620792fd710..0000000000000000000000000000000000000000 --- a/arch/arm64/boot/dts/qcom/sdm640-stub-regulator.dtsi +++ /dev/null @@ -1,357 +0,0 @@ -/* Copyright (c) 2018, 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 PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include - -/* Stub regulators */ - -/ { - /* pm640 S1 - VDD_CX supply */ - pm640_s1_level: regulator-pm640-s1 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_s1_level"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640_s1_level_ao: regulator-pm640-s1-level-ao { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_s1_level_ao"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - /* pm640 S3 - VDD_MX supply */ - pm640_s3_level: regulator-pm640-s3 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_s3_level"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640_s3_level_ao: regulator-pm640-s3-level-ao { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_s3_level_ao"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640l_s1: regulator-pm640l-s1 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_s1"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = <1128000>; - regulator-max-microvolt = <1128000>; - }; - - pm640l_s2: regulator-pm640l-s2 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_s2"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = <600000>; - regulator-max-microvolt = <600000>; - }; - - /* pm640l S7 - VDD_MSS supply */ - pm640l_s7_level: regulator-pm640l-s7 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_s7_level"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640l_s7_level_ao: regulator-pm640l-s7-level-ao { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_s7_level_ao"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640l_s8: regulator-pm640l-s8 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_s8"; - qcom,hpm-min-load = <100000>; - regulator-min-microvolt = <1352000>; - regulator-max-microvolt = <1352000>; - }; - - pm640_l1: regulator-pm640-l1 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l1"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - - pm640_l2: regulator-pm640-l2 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l2"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - }; - - pm640_l3: regulator-pm640-l3 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l3"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - }; - - pm640_l4: regulator-pm640-l4 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l4"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <928000>; - }; - - pm640_l5: regulator-pm640-l5 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l5"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <2700000>; - regulator-max-microvolt = <2704000>; - }; - - pm640_l6: regulator-pm640-l6 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l6"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <600000>; - regulator-max-microvolt = <600000>; - }; - - /* pm640 L7 - LPI_MX supply */ - pm640_l7_level: regulator-pm640-l7 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l7_level"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640_l7_level_ao: regulator-pm640-l7-level-ao { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l7_level_ao"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - /* pm640 L8 - LPI_CX supply */ - pm640_l8_level: regulator-pm640-l8 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l8_level"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640_l8_level_ao: regulator-pm640-l8_level_ao { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l8_level_ao"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = ; - regulator-max-microvolt = ; - }; - - pm640_l9: regulator-pm640-l9 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l9"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <728000>; - regulator-max-microvolt = <728000>; - }; - - pm640_l10: regulator-pm640-l10 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l10"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640_l11: regulator-pm640-l11 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l11"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640_l12: regulator-pm640-l12 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l12"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640_l13: regulator-pm640-l13 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l13"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640_l14: regulator-pm640-l14 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l14"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640_l15: regulator-pm640-l15 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l15"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640_l16: regulator-pm640-l16 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l16"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <2700000>; - regulator-max-microvolt = <2704000>; - }; - - pm640_l17: regulator-pm640-l17 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l17"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <3100000>; - regulator-max-microvolt = <3128000>; - }; - - pm640_l18: regulator-pm640-l18 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l18"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3300000>; - }; - - pm640_l19: regulator-pm640-l19 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640_l19"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <2848000>; - regulator-max-microvolt = <2848000>; - }; - - pm640l_l1: regulator-pm640l-l1 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l1"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640l_l2: regulator-pm640l-l2 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l2"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1300000>; - regulator-max-microvolt = <1304000>; - }; - - pm640l_l3: regulator-pm640l-l3 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l3"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1250000>; - }; - - pm640l_l4: regulator-pm640l-l4 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l4"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2950000>; - }; - - pm640l_l5: regulator-pm640l-l5 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l5"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2950000>; - }; - - pm640l_l6: regulator-pm640l-l6 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l6"; - qcom,hpm-min-load = <5000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2950000>; - }; - - pm640l_l7: regulator-pm640l-l7 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l7"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <3008000>; - regulator-max-microvolt = <3008000>; - }; - - pm640l_l8: regulator-pm640l-l8 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l8"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm640l_l9: regulator-pm640l-l9 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l9"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <2960000>; - regulator-max-microvolt = <2960000>; - }; - - pm640l_l10: regulator-pm640l-l10 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l10"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <3304000>; - regulator-max-microvolt = <3304000>; - }; - - pm640l_l11: regulator-pm640l-l11 { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_l11"; - qcom,hpm-min-load = <10000>; - regulator-min-microvolt = <2960000>; - regulator-max-microvolt = <2960000>; - }; - - pm640l_bob: regulator-pm640l-bob { - compatible = "qcom,stub-regulator"; - regulator-name = "pm640l_bob"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; -}; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-audio-overlay.dtsi index b9ffea089e099ebafe286d10e4a817a70d8b032a..118b3097b78eba4572a6c10e38001ae8e15a8eb0 100644 --- a/arch/arm64/boot/dts/qcom/sdmshrike-audio-overlay.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike-audio-overlay.dtsi @@ -47,6 +47,10 @@ qcom,pahu-ext-clk-freq = <19200000>; + asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>; + asoc-codec-names = "msm-stub-codec.1", + "msm-ext-disp-audio-codec-rx"; + qcom,wsa-max-devs = <2>; qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>, <&wsa881x_0213>, <&wsa881x_0214>; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdmshrike-cdp-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..5c9bb022d0fec4c502301f8d71517e2066615288 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdmshrike-cdp-overlay.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include +#include +#include + +#include "sdmshrike-cdp.dtsi" + +/ { + model = "CDP"; + compatible = "qcom,sdmshrike-cdp", "qcom,sdmshrike", "qcom,cdp"; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-cdp.dtsi index ec52ab55888752298078a56c93949782e07858e7..c4590ddd319943f4417f5c31c35c30a72eade083 100644 --- a/arch/arm64/boot/dts/qcom/sdmshrike-cdp.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike-cdp.dtsi @@ -12,8 +12,10 @@ #include #include +#include "sdmshrike-sde-display.dtsi" #include "sdmshrike-pmic-overlay.dtsi" +#include "sdmshrike-audio-overlay.dtsi" &soc { gpio_keys { @@ -44,6 +46,10 @@ linux,can-disable; }; }; + + sound-tavil { + qcom,us-euro-gpios = <&tavil_us_euro_switch>; + }; }; &pm855l_wled { @@ -51,6 +57,32 @@ status = "ok"; }; +&dsi_sharp_4k_dsc_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 6 0>; + qcom,platform-te-gpio = <&tlmm 8 0>; + qcom,platform-reset-gpio = <&tlmm 7 0>; +}; + +&dsi_sharp_4k_dsc_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 6 0>; + qcom,platform-te-gpio = <&tlmm 8 0>; + qcom,platform-reset-gpio = <&tlmm 7 0>; +}; + +&dsi_sharp_4k_dsc_cmd_display { + qcom,dsi-display-active; +}; + &pm855l_lcdb { status = "ok"; }; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-gpu.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..76a1e5379fc6791771a68a70267b9b1cdcff3a0b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdmshrike-gpu.dtsi @@ -0,0 +1,393 @@ +/* Copyright (c) 2018, 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 { + pil_gpu: qcom,kgsl-hyp { + compatible = "qcom,pil-tz-generic"; + qcom,pas-id = <13>; + qcom,firmware-name = "a640_zap"; + }; + + msm_bus: qcom,kgsl-busmon{ + label = "kgsl-busmon"; + compatible = "qcom,kgsl-busmon"; + }; + + gpubw: qcom,gpubw { + compatible = "qcom,devbw"; + governor = "bw_vbif"; + qcom,src-dst-ports = <26 512>; + qcom,bw-tbl = + < 0 /* off */ >, + < 381 /* 100 MHz */ >, + < 572 /* 150 MHz */ >, + < 762 /* 200 MHz */ >, + < 1144 /* 300 MHz */ >, + < 1571 /* 412 MHz */ >, + < 2086 /* 547 MHz */ >, + < 2597 /* 681 MHz */ >, + < 2929 /* 768 MHz */ >, + < 3879 /* 1017 MHz */ >, + < 4943 /* 1296 MHz */ >, + < 5931 /* 1555 MHz */ >, + < 6881 /* 1804 MHz */ >; + }; + + gpu_opp_table: gpu-opp-table { + compatible = "operating-points-v2"; + + opp-514000000 { + opp-hz = /bits/ 64 <514000000>; + opp-microvolt = ; + }; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + opp-microvolt = ; + }; + + opp-461000000 { + opp-hz = /bits/ 64 <461000000>; + opp-microvolt = ; + }; + + opp-405000000 { + opp-hz = /bits/ 64 <405000000>; + opp-microvolt = ; + }; + + opp-315000000 { + opp-hz = /bits/ 64 <315000000>; + opp-microvolt = ; + }; + + opp-256000000 { + opp-hz = /bits/ 64 <256000000>; + opp-microvolt = ; + }; + + opp-177000000 { + opp-hz = /bits/ 64 <177000000>; + opp-microvolt = ; + }; + }; + + msm_gpu: qcom,kgsl-3d0@2c00000 { + label = "kgsl-3d0"; + compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d"; + status = "ok"; + reg = <0x2c00000 0x40000>; + reg-names = "kgsl_3d0_reg_memory"; + interrupts = <0 300 0>; + interrupt-names = "kgsl_3d0_irq"; + qcom,id = <0>; + + qcom,chipid = <0x6080000>; + + qcom,initial-pwrlevel = <5>; + + qcom,idle-timeout = <80>; /* msecs */ + qcom,no-nap; + + qcom,highest-bank-bit = <16>; + + qcom,min-access-length = <32>; + + qcom,ubwc-mode = <3>; + + qcom,snapshot-size = <1048576>; /* bytes */ + + qcom,gpu-qdss-stm = <0x161c0000 0x40000>; /* base addr, size */ + + qcom,tsens-name = "tsens_tz_sensor12"; + #cooling-cells = <2>; + + qcom,pm-qos-active-latency = <460>; + + clocks = <&clock_gpucc GPU_CC_CXO_CLK>, + <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>, + <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>, + <&clock_gpucc GPU_CC_CX_GMU_CLK>, + <&clock_gpucc GPU_CC_AHB_CLK>; + + clock-names = "rbbmtimer_clk", "mem_clk", + "mem_iface_clk", "gmu_clk", + "gpu_cc_ahb"; + + qcom,isense-clk-on-level = <1>; + + /* Bus Scale Settings */ + qcom,gpubw-dev = <&gpubw>; + qcom,bus-control; + qcom,msm-bus,name = "grp3d"; + qcom,bus-width = <32>; + qcom,msm-bus,num-cases = <13>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <26 512 0 0>, + <26 512 0 400000>, /* 1 bus=100 */ + <26 512 0 600000>, /* 2 bus=150 */ + <26 512 0 800000>, /* 3 bus=200 */ + <26 512 0 1200000>, /* 4 bus=300 */ + <26 512 0 1648000>, /* 5 bus=412 */ + <26 512 0 2188000>, /* 6 bus=547 */ + <26 512 0 2724000>, /* 7 bus=681 */ + <26 512 0 3072000>, /* 8 bus=768 */ + <26 512 0 4068000>, /* 9 bus=1017 */ + <26 512 0 5184000>, /* 10 bus=1296 */ + <26 512 0 6220000>, /* 11 bus=1555 */ + <26 512 0 7216000>; /* 12 bus=1804 */ + + /* GDSC regulator names */ + regulator-names = "vddcx", "vdd"; + /* GDSC oxili regulators */ + vddcx-supply = <&gpu_cx_gdsc>; + vdd-supply = <&gpu_gx_gdsc>; + + /* GPU OPP data */ + operating-points-v2 = <&gpu_opp_table>; + + /* GPU related llc slices */ + cache-slice-names = "gpu", "gpuhtw"; + cache-slices = <&llcc 12>, <&llcc 11>; + + /* GPU Mempools */ + qcom,gpu-mempools { + #address-cells = <1>; + #size-cells = <0>; + compatible = "qcom,gpu-mempools"; + + /* 4K Page Pool configuration */ + qcom,gpu-mempool@0 { + reg = <0>; + qcom,mempool-page-size = <4096>; + qcom,mempool-reserved = <2048>; + qcom,mempool-allocate; + }; + /* 8K Page Pool configuration */ + qcom,gpu-mempool@1 { + reg = <1>; + qcom,mempool-page-size = <8192>; + qcom,mempool-reserved = <1024>; + qcom,mempool-allocate; + }; + /* 64K Page Pool configuration */ + qcom,gpu-mempool@2 { + reg = <2>; + qcom,mempool-page-size = <65536>; + qcom,mempool-reserved = <256>; + }; + /* 1M Page Pool configuration */ + qcom,gpu-mempool@3 { + reg = <3>; + qcom,mempool-page-size = <1048576>; + qcom,mempool-reserved = <32>; + }; + }; + + /* Power levels */ + qcom,gpu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,gpu-pwrlevels"; + + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <514000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <11>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <500000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <10>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <461000000>; + qcom,bus-freq = <10>; + qcom,bus-min = <9>; + qcom,bus-max = <11>; + }; + + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <405000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <10>; + }; + + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <315000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <256000000>; + qcom,bus-freq = <5>; + qcom,bus-min = <5>; + qcom,bus-max = <7>; + }; + + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <177000000>; + qcom,bus-freq = <4>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <0>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; + }; + }; + + kgsl_msm_iommu: qcom,kgsl-iommu@2ca0000 { + compatible = "qcom,kgsl-smmu-v2"; + + reg = <0x2ca0000 0x10000>; + /* CB5(ATOS) & CB5/6/7 are protected by HYP */ + qcom,protect = <0x40000 0xc000>; + qcom,micro-mmu-control = <0x6000>; + + clocks =<&clock_gcc GCC_GPU_CFG_AHB_CLK>, + <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>, + <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>; + + clock-names = "iface_clk", "mem_clk", "mem_iface_clk"; + + qcom,secure_align_mask = <0xfff>; + qcom,global_pt; + qcom,retention; + qcom,hyp_secure_alloc; + + gfx3d_user: gfx3d_user { + compatible = "qcom,smmu-kgsl-cb"; + label = "gfx3d_user"; + iommus = <&kgsl_smmu 0x0 0xC01>; + qcom,gpu-offset = <0x48000>; + }; + + gfx3d_secure: gfx3d_secure { + compatible = "qcom,smmu-kgsl-cb"; + label = "gfx3d_secure"; + iommus = <&kgsl_smmu 0x2 0xC00>; + }; + }; + + gmu_opp_table: gmu-opp-table { + compatible = "operating-points-v2"; + + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + opp-microvolt = ; + }; + + opp-400000000 { + opp-hz = /bits/ 64 <400000000>; + opp-microvolt = ; + }; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + opp-microvolt = ; + }; + }; + + gmu: qcom,gmu@2c6a000 { + label = "kgsl-gmu"; + compatible = "qcom,gpu-gmu"; + + reg = <0x2c6a000 0x30000>, <0xb200000 0x300000>; + reg-names = "kgsl_gmu_reg", "kgsl_gmu_pdc_reg"; + + interrupts = <0 304 0>, <0 305 0>; + interrupt-names = "kgsl_hfi_irq", "kgsl_gmu_irq"; + + qcom,msm-bus,name = "cnoc"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <26 10036 0 0>, /* CNOC off */ + <26 10036 0 100>; /* CNOC on */ + + regulator-names = "vddcx", "vdd"; + vddcx-supply = <&gpu_cx_gdsc>; + vdd-supply = <&gpu_gx_gdsc>; + + /* GMU OPP data */ + operating-points-v2 = <&gmu_opp_table>; + + clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>, + <&clock_gpucc GPU_CC_CXO_CLK>, + <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>, + <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>, + <&clock_gpucc GPU_CC_AHB_CLK>; + + clock-names = "gmu_clk", "cxo_clk", "axi_clk", + "memnoc_clk", "gpu_cc_ahb"; + + qcom,gmu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,gmu-pwrlevels"; + + /* GMU power levels must go from lowest to highest */ + qcom,gmu-pwrlevel@0 { + reg = <0>; + qcom,gmu-freq = <0>; + }; + + qcom,gmu-pwrlevel@1 { + reg = <1>; + qcom,gmu-freq = <200000000>; + }; + + qcom,gmu-pwrlevel@2 { + reg = <2>; + qcom,gmu-freq = <400000000>; + }; + + qcom,gmu-pwrlevel@3 { + reg = <3>; + qcom,gmu-freq = <500000000>; + }; + }; + + gmu_user: gmu_user { + compatible = "qcom,smmu-gmu-user-cb"; + iommus = <&kgsl_smmu 0x4 0xC00>; + }; + + gmu_kernel: gmu_kernel { + compatible = "qcom,smmu-gmu-kernel-cb"; + iommus = <&kgsl_smmu 0x5 0xC00>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdmshrike-mtp-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..77e99892a90c067c8aa03a0175a696b1ccede8f7 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdmshrike-mtp-overlay.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include +#include +#include + +#include "sdmshrike-mtp.dtsi" + +/ { + model = "MTP"; + compatible = "qcom,sdmshrike-mtp", "qcom,sdmshrike", "qcom,mtp"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-mtp.dtsi index ec52ab55888752298078a56c93949782e07858e7..c1c08a7afb7bf12932dbcb4b7a38e383cd033c92 100644 --- a/arch/arm64/boot/dts/qcom/sdmshrike-mtp.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike-mtp.dtsi @@ -12,8 +12,10 @@ #include #include +#include "sdmshrike-sde-display.dtsi" #include "sdmshrike-pmic-overlay.dtsi" +#include "sdmshrike-audio-overlay.dtsi" &soc { gpio_keys { @@ -51,6 +53,32 @@ status = "ok"; }; +&dsi_sharp_4k_dsc_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 6 0>; + qcom,platform-te-gpio = <&tlmm 8 0>; + qcom,platform-reset-gpio = <&tlmm 7 0>; +}; + +&dsi_sharp_4k_dsc_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-mode-gpio = <&tlmm 6 0>; + qcom,platform-te-gpio = <&tlmm 8 0>; + qcom,platform-reset-gpio = <&tlmm 7 0>; +}; + +&dsi_sharp_4k_dsc_cmd_display { + qcom,dsi-display-active; +}; + &pm855l_lcdb { status = "ok"; }; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-pinctrl.dtsi index 99e988e5a69a82038330d020e1998e1b94bfd43f..04766c9aa5d339308908a5ce8034587529ac374b 100644 --- a/arch/arm64/boot/dts/qcom/sdmshrike-pinctrl.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike-pinctrl.dtsi @@ -533,6 +533,61 @@ }; }; + pmx_sde: pmx_sde { + sde_dsi_active: sde_dsi_active { + mux { + pins = "gpio6", "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <8>; /* 8 mA */ + bias-disable = <0>; /* no pull */ + }; + }; + sde_dsi_suspend: sde_dsi_suspend { + mux { + pins = "gpio6", "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + }; + + pmx_sde_te { + sde_te_active: sde_te_active { + mux { + pins = "gpio8"; + function = "mdp_vsync"; + }; + + config { + pins = "gpio8"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + sde_te_suspend: sde_te_suspend { + mux { + pins = "gpio8"; + function = "mdp_vsync"; + }; + + config { + pins = "gpio8"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + }; + sec_tdm { sec_tdm_sleep: sec_tdm_sleep { mux { @@ -1675,16 +1730,18 @@ }; fsa_usbc_ana_en_n@100 { - mux { - pins = "gpio100"; - function = "gpio"; - }; + fsa_usbc_ana_en: fsa_usbc_ana_en { + mux { + pins = "gpio100"; + function = "gpio"; + }; - config { - pins = "gpio100"; - drive-strength = <2>; - bias-disable; - output-low; + config { + pins = "gpio100"; + drive-strength = <2>; + bias-disable; + output-low; + }; }; }; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-regulators.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-regulators.dtsi index bc99e1252116c5619ddf194bc7972e69b6518b09..249e37677acab05aa5030085a3588e841ba59f6d 100644 --- a/arch/arm64/boot/dts/qcom/sdmshrike-regulators.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike-regulators.dtsi @@ -569,11 +569,11 @@ regulator-name = "pm855p_s1_level"; qcom,set = ; regulator-min-microvolt - = ; + = ; regulator-max-microvolt = ; qcom,init-voltage-level - = ; + = ; }; }; @@ -880,4 +880,11 @@ qcom,init-mode = ; }; }; + + refgen: refgen-regulator@88e7000 { + compatible = "qcom,refgen-regulator"; + reg = <0x88e7000 0x60>; + regulator-name = "refgen"; + regulator-enable-ramp-delay = <5>; + }; }; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..2b007fccfb6c5c0b27914b399b93975d77fbffe4 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdmshrike-sde-display.dtsi @@ -0,0 +1,615 @@ +/* Copyright (c) 2016-2018, 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 "dsi-panel-sim-video.dtsi" +#include "dsi-panel-sim-cmd.dtsi" +#include "dsi-panel-sim-dsc375-cmd.dtsi" +#include "dsi-panel-sim-dualmipi-video.dtsi" +#include "dsi-panel-sim-dualmipi-cmd.dtsi" +#include "dsi-panel-sim-dualmipi-dsc375-cmd.dtsi" +#include "dsi-panel-sharp-dsc-4k-video.dtsi" +#include "dsi-panel-sharp-dsc-4k-cmd.dtsi" +#include "dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi" +#include "dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi" +#include "dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi" +#include "dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi" +#include "dsi-panel-nt35695b-truly-fhd-video.dtsi" +#include "dsi-panel-nt35695b-truly-fhd-cmd.dtsi" +#include "dsi-panel-sharp-1080p-cmd.dtsi" +#include "dsi-panel-sharp-dualmipi-1080p-120hz.dtsi" +#include "dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi" +#include "dsi-panel-sw43404-amoled-dsc-wqhd-cmd.dtsi" +#include + +&soc { + dsi_panel_pwr_supply: dsi_panel_pwr_supply { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <20>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "lab"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@2 { + reg = <2>; + qcom,supply-name = "ibb"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-post-on-sleep = <20>; + }; + }; + + dsi_panel_pwr_supply_no_labibb: dsi_panel_pwr_supply_no_labibb { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <20>; + }; + }; + + dsi_panel_pwr_supply_vdd_no_labibb: dsi_panel_pwr_supply_vdd_no_labibb { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + qcom,supply-post-on-sleep = <20>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "vdd"; + qcom,supply-min-voltage = <3000000>; + qcom,supply-max-voltage = <3000000>; + qcom,supply-enable-load = <857000>; + qcom,supply-disable-load = <0>; + qcom,supply-post-on-sleep = <0>; + }; + }; + + dsi_sharp_4k_dsc_video_display: qcom,dsi-display@0 { + label = "dsi_sharp_4k_dsc_video_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_sharp_4k_dsc_video>; + }; + + dsi_sharp_4k_dsc_cmd_display: qcom,dsi-display@1 { + label = "dsi_sharp_4k_dsc_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_sharp_4k_dsc_cmd>; + }; + + dsi_sharp_1080_cmd_display: qcom,dsi-display@2 { + label = "dsi_sharp_1080_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_sharp_1080_cmd>; + }; + + dsi_dual_sharp_1080_120hz_cmd_display: qcom,dsi-display@3 { + label = "dsi_dual_sharp_1080_120hz_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_dual_sharp_1080_120hz_cmd>; + }; + + dsi_dual_nt35597_truly_video_display: qcom,dsi-display@4 { + label = "dsi_dual_nt35597_truly_video_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_dual_nt35597_truly_video>; + }; + + dsi_dual_nt35597_truly_cmd_display: qcom,dsi-display@5 { + label = "dsi_dual_nt35597_truly_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_dual_nt35597_truly_cmd>; + }; + + dsi_nt35597_truly_dsc_cmd_display: qcom,dsi-display@6 { + label = "dsi_nt35597_truly_dsc_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <1>; + qcom,dsi-phy-num = <1>; + qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + + qcom,dsi-panel = <&dsi_nt35597_truly_dsc_cmd>; + }; + + dsi_nt35597_truly_dsc_video_display: qcom,dsi-display@7 { + label = "dsi_nt35597_truly_dsc_video_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <1>; + qcom,dsi-phy-num = <1>; + qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; + + qcom,dsi-panel = <&dsi_nt35597_truly_dsc_video>; + }; + + dsi_sim_vid_display: qcom,dsi-display@8 { + label = "dsi_sim_vid_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_sim_vid>; + }; + + dsi_dual_sim_vid_display: qcom,dsi-display@9 { + label = "dsi_dual_sim_vid_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_dual_sim_vid>; + }; + + dsi_sim_cmd_display: qcom,dsi-display@10 { + label = "dsi_sim_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_sim_cmd>; + }; + + dsi_dual_sim_cmd_display: qcom,dsi-display@11 { + label = "dsi_dual_sim_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_dual_sim_cmd>; + }; + + dsi_sim_dsc_375_cmd_display: qcom,dsi-display@12 { + label = "dsi_sim_dsc_375_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_sim_dsc_375_cmd>; + }; + + dsi_dual_sim_dsc_375_cmd_display: qcom,dsi-display@13 { + label = "dsi_dual_sim_dsc_375_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>; + }; + + dsi_sw43404_amoled_cmd_display: qcom,dsi-display@14 { + label = "dsi_sw43404_amoled_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_sw43404_amoled_cmd>; + }; + + dsi_nt35695b_truly_fhd_cmd_display: qcom,dsi-display@15 { + label = "dsi_nt35695b_truly_fhd_cmd_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>; + + }; + + dsi_nt35695b_truly_fhd_video_display: qcom,dsi-display@16 { + label = "dsi_nt35695b_truly_fhd_video_display"; + qcom,display-type = "primary"; + + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; + }; + + sde_dsi: qcom,dsi-display { + compatible = "qcom,dsi-display"; + + qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; + qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; + + clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, + <&mdss_dsi0_pll PCLK_MUX_0_CLK>, + <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, + <&mdss_dsi1_pll PCLK_MUX_1_CLK>; + clock-names = "src_byte_clk0", "src_pixel_clk0", + "src_byte_clk1", "src_pixel_clk1"; + + pinctrl-names = "panel_active", "panel_suspend"; + pinctrl-0 = <&sde_dsi_active &sde_te_active>; + pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + + qcom,platform-te-gpio = <&tlmm 8 0>; + + vddio-supply = <&pm855p_l1>; + lab-supply = <&lcdb_ldo_vreg>; + ibb-supply = <&lcdb_ncp_vreg>; + + qcom,dsi-display-list = + <&dsi_sharp_4k_dsc_video_display + &dsi_sharp_4k_dsc_cmd_display + &dsi_sharp_1080_cmd_display + &dsi_dual_sharp_1080_120hz_cmd_display + &dsi_dual_nt35597_truly_video_display + &dsi_dual_nt35597_truly_cmd_display + &dsi_nt35597_truly_dsc_cmd_display + &dsi_nt35597_truly_dsc_video_display + &dsi_sim_vid_display + &dsi_dual_sim_vid_display + &dsi_sim_cmd_display + &dsi_dual_sim_cmd_display + &dsi_sim_dsc_375_cmd_display + &dsi_dual_sim_dsc_375_cmd_display + &dsi_sw43404_amoled_cmd_display + &dsi_nt35695b_truly_fhd_cmd_display + &dsi_nt35695b_truly_fhd_video_display>; + }; + + sde_wb: qcom,wb-display@0 { + compatible = "qcom,wb-display"; + cell-index = <0>; + label = "wb_display"; + }; + + ext_disp: qcom,msm-ext-disp { + compatible = "qcom,msm-ext-disp"; + + ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx { + compatible = "qcom,msm-ext-disp-audio-codec-rx"; + }; + }; +}; + +&mdss_mdp { + connectors = <&sde_wb &sde_dsi>; +}; + +/* PHY TIMINGS REVISION P */ +&dsi_dual_nt35597_truly_video { + qcom,mdss-dsi-t-clk-post = <0x17>; + qcom,mdss-dsi-t-clk-pre = <0x18>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_nt35597_truly_cmd { + qcom,mdss-dsi-t-clk-post = <0x17>; + qcom,mdss-dsi-t-clk-pre = <0x18>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_nt35597_truly_dsc_cmd { + qcom,mdss-dsi-t-clk-post = <0x15>; + qcom,mdss-dsi-t-clk-pre = <0x12>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05 + 05 03 03 04 00 12 15]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <1>; + }; + }; +}; + +&dsi_nt35597_truly_dsc_video { + qcom,mdss-dsi-t-clk-post = <0x15>; + qcom,mdss-dsi-t-clk-pre = <0x12>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05 + 05 03 03 04 00 12 15]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <1>; + }; + }; +}; + +&dsi_sharp_4k_dsc_video { + qcom,mdss-dsi-t-clk-post = <0x18>; + qcom,mdss-dsi-t-clk-pre = <0x19>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 08 + 08 05 03 04 00 19 18]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sharp_4k_dsc_cmd { + qcom,mdss-dsi-t-clk-post = <0x18>; + qcom,mdss-dsi-t-clk-pre = <0x19>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 08 + 08 05 03 04 00 19 18]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_nt35695b_truly_fhd_video { + qcom,mdss-dsi-t-clk-post = <0x17>; + qcom,mdss-dsi-t-clk-pre = <0x19>; + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 + 08 08 05 03 04 00 19 17]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_nt35695b_truly_fhd_cmd { + qcom,mdss-dsi-t-clk-post = <0x17>; + qcom,mdss-dsi-t-clk-pre = <0x19>; + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 + 08 08 05 03 04 00 19 17]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_sharp_1080_120hz_cmd { + qcom,mdss-dsi-t-clk-post = <0x0f>; + qcom,mdss-dsi-t-clk-pre = <0x36>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 24 09 09 26 24 09 + 09 06 03 04 00]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sharp_1080_cmd { + qcom,mdss-dsi-t-clk-post = <0x0c>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 1A 06 06 22 20 07 + 07 04 03 04 00]; + qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sim_vid { + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [01 03 01 00 01 01 01 + 01 00 03 04 00 03 04]; + qcom,display-topology = <1 0 1>, + <2 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_sim_vid { + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 + 03 02 03 04 00 10 06]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sim_cmd { + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 03 01 00 01 01 02 + 01 00 03 04 00 03 04]; + qcom,display-topology = <1 0 1>, + <2 0 1>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_sim_cmd { + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0{ + qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 + 03 02 03 04 00 10 06]; + qcom,display-topology = <2 0 2>; + qcom,default-topology-index = <0>; + }; + timing@1{ + qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 + 03 02 03 04 00 10 06]; + qcom,display-topology = <2 0 2>, + <1 0 2>; + qcom,default-topology-index = <0>; + }; + timing@2{ + qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 + 03 02 03 04 00 10 06]; + qcom,display-topology = <2 0 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sim_dsc_375_cmd { + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0 { /* 1080p */ + qcom,mdss-dsi-panel-phy-timings = [00 12 03 04 07 07 04 + 04 03 03 04 00 13 07]; + qcom,display-topology = <1 1 1>; + qcom,default-topology-index = <0>; + }; + timing@1 { /* qhd */ + qcom,mdss-dsi-panel-phy-timings = [00 12 03 04 07 07 04 + 04 03 03 04 00 13 07]; + qcom,display-topology = <1 1 1>, + <2 2 1>, /* dsc merge */ + <2 1 1>; /* 3d mux */ + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_dual_sim_dsc_375_cmd { + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,mdss-dsi-display-timings { + timing@0 { /* qhd */ + qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 + 03 02 03 04 00 10 06]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + timing@1 { /* 4k */ + qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 + 03 02 03 04 00 10 06]; + qcom,display-topology = <2 2 2>; + qcom,default-topology-index = <0>; + }; + }; +}; + +&dsi_sw43404_amoled_cmd { + qcom,mdss-dsi-t-clk-post = <0x16>; + qcom,mdss-dsi-t-clk-pre = <0x16>; + qcom,mdss-dsi-display-timings { + timing@0 { + qcom,mdss-dsi-panel-phy-timings = [00 1a 07 06 22 21 07 + 07 04 03 04 00 16 16]; + qcom,display-topology = <2 2 1>; + qcom,default-topology-index = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-sde-pll.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-sde-pll.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..6b350e54feb2733463747367fdc7e50b57814af6 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdmshrike-sde-pll.dtsi @@ -0,0 +1,68 @@ +/* Copyright (c) 2016-2018, 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_dsi0_pll: qcom,mdss_dsi_pll@ae94900 { + compatible = "qcom,mdss_dsi_pll_7nm"; + label = "MDSS DSI 0 PLL"; + cell-index = <0>; + #clock-cells = <1>; + reg = <0xae94900 0x260>, + <0xae94400 0x800>, + <0xaf03000 0x8>; + reg-names = "pll_base", "phy_base", "gdsc_base"; + clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>; + clock-names = "iface_clk"; + clock-rate = <0>; + gdsc-supply = <&mdss_core_gdsc>; + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + }; + + mdss_dsi1_pll: qcom,mdss_dsi_pll@ae96900 { + compatible = "qcom,mdss_dsi_pll_7nm"; + label = "MDSS DSI 1 PLL"; + cell-index = <1>; + #clock-cells = <1>; + reg = <0xae96900 0x260>, + <0xae96400 0x800>, + <0xaf03000 0x8>; + reg-names = "pll_base", "phy_base", "gdsc_base"; + clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>; + clock-names = "iface_clk"; + clock-rate = <0>; + gdsc-supply = <&mdss_core_gdsc>; + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + }; + +}; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-sde.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-sde.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..067069d7fd2951e802e888c78d81a5429cc962e2 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdmshrike-sde.dtsi @@ -0,0 +1,599 @@ +/* Copyright (c) 2016-2018, 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 + +&soc { + mdss_mdp: qcom,mdss_mdp@ae00000 { + compatible = "qcom,sde-kms"; + reg = <0x0ae00000 0x84208>, + <0x0aeb0000 0x2008>, + <0x0aeac000 0x214>; + reg-names = "mdp_phys", + "vbif_phys", + "regdma_phys"; + + clocks = + <&clock_gcc GCC_DISP_AHB_CLK>, + <&clock_gcc GCC_DISP_HF_AXI_CLK>, + <&clock_dispcc DISP_CC_MDSS_AHB_CLK>, + <&clock_dispcc DISP_CC_MDSS_MDP_CLK>, + <&clock_dispcc DISP_CC_MDSS_VSYNC_CLK>; + clock-names = "gcc_iface", "gcc_bus", + "iface_clk", "core_clk", "vsync_clk"; + clock-rate = <0 0 0 300000000 19200000>; + clock-max-rate = <0 0 0 460000000 19200000>; + + sde-vdd-supply = <&mdss_core_gdsc>; + + /* interrupt config */ + interrupts = <0 83 0>; + interrupt-controller; + #interrupt-cells = <1>; + iommus = <&apps_smmu 0x800 0x20>, + <&apps_smmu 0xc00 0x20>; + + #address-cells = <1>; + #size-cells = <0>; + + /* hw blocks */ + qcom,sde-off = <0x1000>; + qcom,sde-len = <0x45c>; + + qcom,sde-ctl-off = <0x2000 0x2200 0x2400 + 0x2600 0x2800 0x2a00>; + qcom,sde-ctl-size = <0x1e0>; + qcom,sde-ctl-display-pref = "primary", "primary", "none", + "none", "none"; + + qcom,sde-mixer-off = <0x45000 0x46000 0x47000 + 0x48000 0x49000 0x4a000>; + qcom,sde-mixer-size = <0x320>; + qcom,sde-mixer-display-pref = "primary", "primary", "none", + "none", "none", "none"; + + qcom,sde-dspp-top-off = <0x1300>; + qcom,sde-dspp-top-size = <0x80>; + qcom,sde-dspp-off = <0x55000 0x57000 0x59000 0x5b000>; + qcom,sde-dspp-size = <0x1800>; + + qcom,sde-dest-scaler-top-off = <0x00061000>; + qcom,sde-dest-scaler-top-size = <0x1c>; + qcom,sde-dest-scaler-off = <0x800 0x1000>; + qcom,sde-dest-scaler-size = <0x800>; + + qcom,sde-wb-off = <0x66000>; + qcom,sde-wb-size = <0x2c8>; + qcom,sde-wb-xin-id = <6>; + qcom,sde-wb-id = <2>; + qcom,sde-wb-clk-ctrl = <0x3b8 24>; + + qcom,sde-intf-off = <0x6b000 0x6b800 + 0x6c000 0x6c800>; + qcom,sde-intf-size = <0x280>; + qcom,sde-intf-type = "dp", "dsi", "dsi", "dp"; + + qcom,sde-pp-off = <0x71000 0x71800 + 0x72000 0x72800 0x73000 0x73800>; + qcom,sde-pp-slave = <0x0 0x0 0x0 0x0 0x0 0x0>; + qcom,sde-pp-size = <0xd4>; + qcom,sde-pp-merge-3d-id = <0x0 0x0 0x1 0x1 0x2 0x2>; + + qcom,sde-merge-3d-off = <0x84000 0x84100 0x84200>; + qcom,sde-merge-3d-size = <0x100>; + + qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0 0x0 0x0>; + + qcom,sde-cdm-off = <0x7a200>; + qcom,sde-cdm-size = <0x224>; + + qcom,sde-dsc-off = <0x81000 0x81400 0x81800 0x81c00>; + qcom,sde-dsc-size = <0x140>; + + qcom,sde-dither-off = <0x30e0 0x30e0 0x30e0 + 0x30e0 0x30e0 0x30e0>; + qcom,sde-dither-version = <0x00010000>; + qcom,sde-dither-size = <0x20>; + + qcom,sde-sspp-type = "vig", "vig", "vig", "vig", + "dma", "dma", "dma", "dma"; + + qcom,sde-sspp-off = <0x5000 0x7000 0x9000 0xb000 + 0x25000 0x27000 0x29000 0x2b000>; + qcom,sde-sspp-src-size = <0x1f0>; + + qcom,sde-sspp-xin-id = <0 4 8 12 + 1 5 9 13>; + qcom,sde-sspp-excl-rect = <1 1 1 1 + 1 1 1 1>; + qcom,sde-sspp-smart-dma-priority = <5 6 7 8 1 2 3 4>; + qcom,sde-smart-dma-rev = "smart_dma_v2p5"; + + qcom,sde-mixer-pair-mask = <2 1 4 3 6 5>; + + qcom,sde-mixer-blend-op-off = <0x20 0x38 0x50 0x68 0x80 0x98 + 0xb0 0xc8 0xe0 0xf8 0x110>; + + /* offsets are relative to "mdp_phys + qcom,sde-off */ + qcom,sde-sspp-clk-ctrl = + <0x2ac 0>, <0x2b4 0>, <0x2bc 0>, <0x2c4 0>, + <0x2ac 8>, <0x2b4 8>, <0x2bc 8>, <0x2c4 8>; + qcom,sde-sspp-csc-off = <0x1a00>; + qcom,sde-csc-type = "csc-10bit"; + qcom,sde-qseed-type = "qseedv3"; + qcom,sde-sspp-qseed-off = <0xa00>; + qcom,sde-mixer-linewidth = <2560>; + qcom,sde-sspp-linewidth = <4096>; + qcom,sde-wb-linewidth = <4096>; + qcom,sde-mixer-blendstages = <0xb>; + qcom,sde-highest-bank-bit = <0x2>; + qcom,sde-ubwc-version = <0x300>; + qcom,sde-ubwc-bw-calc-version = <0x1>; + qcom,sde-panic-per-pipe; + qcom,sde-has-cdp; + qcom,sde-has-src-split; + qcom,sde-pipe-order-version = <0x1>; + qcom,sde-has-dim-layer; + qcom,sde-has-idle-pc; + qcom,sde-has-dest-scaler; + qcom,sde-max-dest-scaler-input-linewidth = <2048>; + qcom,sde-max-dest-scaler-output-linewidth = <2560>; + qcom,sde-max-bw-low-kbps = <9600000>; + qcom,sde-max-bw-high-kbps = <9600000>; + qcom,sde-min-core-ib-kbps = <2400000>; + qcom,sde-min-llcc-ib-kbps = <800000>; + qcom,sde-min-dram-ib-kbps = <800000>; + qcom,sde-dram-channels = <2>; + qcom,sde-num-nrt-paths = <0>; + qcom,sde-dspp-ad-version = <0x00040000>; + qcom,sde-dspp-ad-off = <0x28000 0x27000>; + + qcom,sde-vbif-off = <0>; + qcom,sde-vbif-size = <0x1040>; + qcom,sde-vbif-id = <0>; + qcom,sde-vbif-memtype-0 = <3 3 3 3 3 3 3 3>; + qcom,sde-vbif-memtype-1 = <3 3 3 3 3 3>; + + qcom,sde-vbif-qos-rt-remap = <3 3 4 4 5 5 6 6>; + qcom,sde-vbif-qos-nrt-remap = <3 3 3 3 3 3 3 3>; + + qcom,sde-danger-lut = <0x0000000f 0x0000ffff 0x00000000 + 0x00000000>; + qcom,sde-safe-lut-linear = + <4 0xfff8>, + <0 0xfff0>; + qcom,sde-safe-lut-macrotile = + <10 0xfe00>, + <11 0xfc00>, + <12 0xf800>, + <0 0xf000>; + qcom,sde-safe-lut-nrt = + <0 0xffff>; + qcom,sde-safe-lut-cwb = + <0 0xffff>; + qcom,sde-qos-lut-linear = + <4 0x00000000 0x00000357>, + <5 0x00000000 0x00003357>, + <6 0x00000000 0x00023357>, + <7 0x00000000 0x00223357>, + <8 0x00000000 0x02223357>, + <9 0x00000000 0x22223357>, + <10 0x00000002 0x22223357>, + <11 0x00000022 0x22223357>, + <12 0x00000222 0x22223357>, + <13 0x00002222 0x22223357>, + <14 0x00012222 0x22223357>, + <0 0x00112222 0x22223357>; + qcom,sde-qos-lut-macrotile = + <10 0x00000003 0x44556677>, + <11 0x00000033 0x44556677>, + <12 0x00000233 0x44556677>, + <13 0x00002233 0x44556677>, + <14 0x00012233 0x44556677>, + <0 0x00112233 0x44556677>; + qcom,sde-qos-lut-nrt = + <0 0x00000000 0x00000000>; + qcom,sde-qos-lut-cwb = + <0 0x75300000 0x00000000>; + + qcom,sde-cdp-setting = <1 1>, <1 0>; + + qcom,sde-qos-cpu-mask = <0x3>; + qcom,sde-qos-cpu-dma-latency = <300>; + + qcom,sde-reg-dma-off = <0>; + qcom,sde-reg-dma-version = <0x00010001>; + qcom,sde-reg-dma-trigger-off = <0x119c>; + + qcom,sde-sspp-vig-blocks { + qcom,sde-vig-csc-off = <0x1a00>; + qcom,sde-vig-qseed-off = <0xa00>; + qcom,sde-vig-qseed-size = <0xa0>; + qcom,sde-vig-gamut = <0x1d00 0x00050000>; + qcom,sde-vig-igc = <0x1d00 0x00050000>; + qcom,sde-vig-inverse-pma; + }; + + qcom,sde-sspp-dma-blocks { + dgm@0 { + qcom,sde-dma-igc = <0x400 0x00050000>; + qcom,sde-dma-gc = <0x600 0x00050000>; + qcom,sde-dma-inverse-pma; + qcom,sde-dma-csc-off = <0x200>; + }; + dgm@1 { + qcom,sde-dma-igc = <0x1400 0x00050000>; + qcom,sde-dma-gc = <0x600 0x00050000>; + qcom,sde-dma-inverse-pma; + qcom,sde-dma-csc-off = <0x1200>; + }; + }; + + qcom,sde-dspp-blocks { + qcom,sde-dspp-igc = <0x0 0x00030001>; + qcom,sde-dspp-hsic = <0x800 0x00010007>; + qcom,sde-dspp-memcolor = <0x880 0x00010007>; + qcom,sde-dspp-hist = <0x800 0x00010007>; + qcom,sde-dspp-sixzone= <0x900 0x00010007>; + qcom,sde-dspp-vlut = <0xa00 0x00010008>; + qcom,sde-dspp-gamut = <0x1000 0x00040001>; + qcom,sde-dspp-pcc = <0x1700 0x00040000>; + qcom,sde-dspp-gc = <0x17c0 0x00010008>; + }; + + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "sde-vdd"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + + smmu_sde_sec: qcom,smmu_sde_sec_cb { + compatible = "qcom,smmu_sde_sec"; + iommus = <&apps_smmu 0x801 0x20>, + <&apps_smmu 0xc01 0x20>; + }; + + /* data and reg bus scale settings */ + qcom,sde-data-bus { + qcom,msm-bus,name = "mdss_sde_mnoc"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + <22 773 0 0>, <23 773 0 0>, + <22 773 0 6400000>, <23 773 0 6400000>, + <22 773 0 6400000>, <23 773 0 6400000>; + }; + + qcom,sde-llcc-bus { + qcom,msm-bus,name = "mdss_sde_llcc"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <132 770 0 0>, + <132 770 0 6400000>, + <132 770 0 6400000>; + }; + + qcom,sde-ebi-bus { + qcom,msm-bus,name = "mdss_sde_ebi"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <129 512 0 0>, + <129 512 0 6400000>, + <129 512 0 6400000>; + }; + + qcom,sde-reg-bus { + qcom,msm-bus,name = "mdss_reg"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,active-only; + qcom,msm-bus,vectors-KBps = + <1 590 0 0>, + <1 590 0 76800>, + <1 590 0 150000>, + <1 590 0 300000>; + }; + }; + + sde_rscc: qcom,sde_rscc@af20000 { + cell-index = <0>; + compatible = "qcom,sde-rsc"; + reg = <0xaf20000 0x1c44>, + <0xaf30000 0x3fd4>; + reg-names = "drv", "wrapper"; + qcom,sde-rsc-version = <2>; + status = "disabled"; + + vdd-supply = <&mdss_core_gdsc>; + clocks = <&clock_dispcc DISP_CC_MDSS_RSCC_VSYNC_CLK>, + <&clock_dispcc DISP_CC_MDSS_NON_GDSC_AHB_CLK>, + <&clock_dispcc DISP_CC_MDSS_RSCC_AHB_CLK>; + clock-names = "vsync_clk", "gdsc_clk", "iface_clk"; + clock-rate = <0 0 0>; + + qcom,sde-dram-channels = <2>; + + mboxes = <&disp_rsc 0>; + mbox-names = "disp_rsc"; + + /* data and reg bus scale settings */ + qcom,sde-data-bus { + qcom,msm-bus,name = "disp_rsc_mnoc"; + qcom,msm-bus,active-only; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <2>; + qcom,msm-bus,vectors-KBps = + <20003 20515 0 0>, <20004 20515 0 0>, + <20003 20515 0 6400000>, <20004 20515 0 6400000>, + <20003 20515 0 6400000>, <20004 20515 0 6400000>; + }; + + qcom,sde-llcc-bus { + qcom,msm-bus,name = "disp_rsc_llcc"; + qcom,msm-bus,active-only; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <20001 20513 0 0>, + <20001 20513 0 6400000>, + <20001 20513 0 6400000>; + }; + + qcom,sde-ebi-bus { + qcom,msm-bus,name = "disp_rsc_ebi"; + qcom,msm-bus,active-only; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <20000 20512 0 0>, + <20000 20512 0 6400000>, + <20000 20512 0 6400000>; + }; + + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "mmcx"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + }; + + mdss_rotator: qcom,mdss_rotator@ae00000 { + compatible = "qcom,sde_rotator"; + reg = <0x0ae00000 0xac000>, + <0x0aeb8000 0x3000>; + reg-names = "mdp_phys", + "rot_vbif_phys"; + + #list-cells = <1>; + + qcom,mdss-rot-mode = <1>; + qcom,mdss-highest-bank-bit = <0x2>; + + /* Bus Scale Settings */ + qcom,msm-bus,name = "mdss_rotator"; + qcom,msm-bus,num-cases = <3>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <25 512 0 0>, + <25 512 0 6400000>, + <25 512 0 6400000>; + + rot-vdd-supply = <&mdss_core_gdsc>; + qcom,supply-names = "rot-vdd"; + + clocks = + <&clock_gcc GCC_DISP_AHB_CLK>, + <&clock_gcc GCC_DISP_SF_AXI_CLK>, + <&clock_dispcc DISP_CC_MDSS_AHB_CLK>, + <&clock_dispcc DISP_CC_MDSS_ROT_CLK>; + clock-names = "gcc_iface", "gcc_bus", + "iface_clk", "rot_clk"; + + interrupt-parent = <&mdss_mdp>; + interrupts = <2 0>; + + power-domains = <&mdss_mdp>; + + /* Offline rotator QoS setting */ + qcom,mdss-rot-vbif-qos-setting = <3 3 3 3 3 3 3 3>; + qcom,mdss-rot-vbif-memtype = <3 3>; + qcom,mdss-rot-cdp-setting = <1 1>; + qcom,mdss-rot-qos-lut = <0x0 0x0 0x0 0x0>; + qcom,mdss-rot-danger-lut = <0x0 0x0>; + qcom,mdss-rot-safe-lut = <0x0000ffff 0x0000ffff>; + + /* Inline rotator QoS Setting */ + /* setting default register values for RD - qos/danger/safe */ + qcom,mdss-inline-rot-qos-lut = <0x44556677 0x00112233 + 0x44556677 0x00112233>; + qcom,mdss-inline-rot-danger-lut = <0x0055aaff 0x0000ffff>; + qcom,mdss-inline-rot-safe-lut = <0x0000f000 0x0000ff00>; + + qcom,mdss-default-ot-rd-limit = <32>; + qcom,mdss-default-ot-wr-limit = <32>; + + qcom,mdss-sbuf-headroom = <20>; + + cache-slice-names = "rotator"; + cache-slices = <&llcc 4>; + + /* reg bus scale settings */ + rot_reg: qcom,rot-reg-bus { + qcom,msm-bus,name = "mdss_rot_reg"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,active-only; + qcom,msm-bus,vectors-KBps = + <1 590 0 0>, + <1 590 0 76800>; + }; + + smmu_rot_unsec: qcom,smmu_rot_unsec_cb { + compatible = "qcom,smmu_sde_rot_unsec"; + iommus = <&apps_smmu 0x1040 0x0>; + }; + + smmu_rot_sec: qcom,smmu_rot_sec_cb { + compatible = "qcom,smmu_sde_rot_sec"; + iommus = <&apps_smmu 0x1041 0x0>; + }; + }; + + mdss_dsi0: qcom,mdss_dsi_ctrl0@ae94000 { + compatible = "qcom,dsi-ctrl-hw-v2.3"; + label = "dsi-ctrl-0"; + cell-index = <0>; + reg = <0xae94000 0x400>, + <0xaf08000 0x4>; + reg-names = "dsi_ctrl", "disp_cc_base"; + interrupt-parent = <&mdss_mdp>; + interrupts = <4 0>; + vdda-1p2-supply = <&pm855p_l3>; + clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK>, + <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_ESC0_CLK>; + clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk", + "pixel_clk", "pixel_clk_rcg", + "esc_clk"; + + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <21800>; + qcom,supply-disable-load = <4>; + }; + }; + }; + + mdss_dsi1: qcom,mdss_dsi_ctrl1@ae96000 { + compatible = "qcom,dsi-ctrl-hw-v2.3"; + label = "dsi-ctrl-1"; + cell-index = <1>; + reg = <0xae96000 0x400>, + <0xaf08000 0x4>; + reg-names = "dsi_ctrl", "disp_cc_base"; + interrupt-parent = <&mdss_mdp>; + interrupts = <5 0>; + vdda-1p2-supply = <&pm855p_l3>; + clocks = <&clock_dispcc DISP_CC_MDSS_BYTE1_CLK>, + <&clock_dispcc DISP_CC_MDSS_BYTE1_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_BYTE1_INTF_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK1_CLK>, + <&clock_dispcc DISP_CC_MDSS_PCLK1_CLK_SRC>, + <&clock_dispcc DISP_CC_MDSS_ESC1_CLK>; + clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk", + "pixel_clk", "pixel_clk_rcg", "esc_clk"; + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <21800>; + qcom,supply-disable-load = <4>; + }; + }; + }; + + mdss_dsi_phy0: qcom,mdss_dsi_phy0@ae94400 { + compatible = "qcom,dsi-phy-v4.0"; + label = "dsi-phy-0"; + cell-index = <0>; + reg = <0xae94400 0x7c0>; + reg-names = "dsi_phy"; + vdda-0p9-supply = <&pm855_2_l5>; + qcom,platform-strength-ctrl = [55 03 + 55 03 + 55 03 + 55 03 + 55 00]; + qcom,platform-lane-config = [00 00 0a 0a + 00 00 0a 0a + 00 00 0a 0a + 00 00 0a 0a + 00 00 8a 8a]; + qcom,platform-regulator-settings = [1d 1d 1d 1d 1d]; + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-0p9"; + qcom,supply-min-voltage = <880000>; + qcom,supply-max-voltage = <880000>; + qcom,supply-enable-load = <36000>; + qcom,supply-disable-load = <32>; + }; + }; + }; + + mdss_dsi_phy1: qcom,mdss_dsi_phy0@ae96400 { + compatible = "qcom,dsi-phy-v4.0"; + label = "dsi-phy-1"; + cell-index = <1>; + reg = <0xae96400 0x7c0>; + reg-names = "dsi_phy"; + vdda-0p9-supply = <&pm855_2_l5>; + qcom,platform-strength-ctrl = [55 03 + 55 03 + 55 03 + 55 03 + 55 00]; + qcom,platform-regulator-settings = [1d 1d 1d 1d 1d]; + qcom,platform-lane-config = [00 00 0a 0a + 00 00 0a 0a + 00 00 0a 0a + 00 00 0a 0a + 00 00 8a 8a]; + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-0p9"; + qcom,supply-min-voltage = <880000>; + qcom,supply-max-voltage = <880000>; + qcom,supply-enable-load = <36000>; + qcom,supply-disable-load = <32>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike-smp2p.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike-smp2p.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..5e42f0056eecac61900e1fcf5395f7f8362d6f67 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdmshrike-smp2p.dtsi @@ -0,0 +1,96 @@ +/* Copyright (c) 2018, 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 + +&soc { + + qcom,smp2p-modem { + compatible = "qcom,smp2p"; + qcom,smem = <435>, <428>; + interrupts = ; + qcom,ipc = <&apcs 0 14>; + qcom,local-pid = <0>; + qcom,remote-pid = <1>; + + modem_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + modem_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-adsp { + compatible = "qcom,smp2p"; + qcom,smem = <443>, <429>; + interrupts = ; + qcom,ipc = <&apcs 0 10>; + qcom,local-pid = <0>; + qcom,remote-pid = <2>; + + adsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + adsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-dsps { + compatible = "qcom,smp2p"; + qcom,smem = <481>, <430>; + interrupts = ; + qcom,ipc = <&apcs 0 26>; + qcom,local-pid = <0>; + qcom,remote-pid = <3>; + + dsps_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + dsps_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-cdsp { + compatible = "qcom,smp2p"; + qcom,smem = <94>, <432>; + interrupts = ; + qcom,ipc = <&apcs 0 6>; + qcom,local-pid = <0>; + qcom,remote-pid = <5>; + + cdsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + cdsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike.dts similarity index 72% rename from arch/arm64/boot/dts/qcom/sdm640-rumi.dtsi rename to arch/arm64/boot/dts/qcom/sdmshrike.dts index e2b2d1dcfb5adf3dcc0e9b8a8ba2d2ff6df9fa0b..d6b8cc8035f2949b3fc60941784c07d9555718ce 100644 --- a/arch/arm64/boot/dts/qcom/sdm640-rumi.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike.dts @@ -10,20 +10,13 @@ * GNU General Public License for more details. */ -&soc { - timer { - clock-frequency = <5500000>; - }; +/dts-v1/; - timer@0x17c00000 { - clock-frequency = <1000000>; - }; +#include "sdmshrike.dtsi" - wdog: qcom,wdt@17c10000{ - status = "disabled"; - }; -}; - -&qupv3_se0_2uart { - status = "ok"; +/ { + model = "Qualcomm Technologies, Inc. SDMSHRIKE SoC"; + compatible = "qcom,sdmshrike"; + qcom,pmic-name = "PM855"; + qcom,board-id = <0 0>; }; diff --git a/arch/arm64/boot/dts/qcom/sdmshrike.dtsi b/arch/arm64/boot/dts/qcom/sdmshrike.dtsi index de3abbc75bd972deff6033c8e2c050c6405bb60d..f1c368db35c145d62855244d1c61faa0d2fafc26 100644 --- a/arch/arm64/boot/dts/qcom/sdmshrike.dtsi +++ b/arch/arm64/boot/dts/qcom/sdmshrike.dtsi @@ -25,7 +25,8 @@ / { model = "Qualcomm Technologies, Inc. SDMSHRIKE"; compatible = "qcom,sdmshrike"; - qcom,msm-id = <340 0x0>; + qcom,msm-name = "SDMSHRIKE"; + qcom,msm-id = <340 0x10000>; interrupt-parent = <&pdc>; aliases { @@ -44,8 +45,7 @@ device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x0>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x8000>; next-level-cache = <&L2_0>; L2_0: l2-cache { @@ -60,14 +60,23 @@ cache-level = <3>; }; }; + + L1_I_0: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + + L1_D_0: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; }; CPU1: cpu@100 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x100>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x8000>; next-level-cache = <&L2_1>; L2_1: l2-cache { @@ -76,14 +85,23 @@ cache-level = <2>; next-level-cache = <&L3_0>; }; + + L1_I_100: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + + L1_D_100: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; }; CPU2: cpu@200 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x200>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x8000>; next-level-cache = <&L2_2>; L2_2: l2-cache { @@ -92,14 +110,23 @@ cache-level = <2>; next-level-cache = <&L3_0>; }; + + L1_I_200: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + + L1_D_200: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; }; CPU3: cpu@300 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x300>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x8000>; next-level-cache = <&L2_3>; L2_3: l2-cache { @@ -108,14 +135,23 @@ cache-level = <2>; next-level-cache = <&L3_0>; }; + + L1_I_300: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; + + L1_D_300: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0xa000>; + }; }; CPU4: cpu@400 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x400>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x20000>; next-level-cache = <&L2_4>; L2_4: l2-cache { @@ -124,14 +160,23 @@ cache-level = <2>; next-level-cache = <&L3_0>; }; + + L1_I_400: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + + L1_D_400: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; }; CPU5: cpu@500 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x500>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x20000>; next-level-cache = <&L2_5>; L2_5: l2-cache { @@ -140,14 +185,23 @@ cache-level = <2>; next-level-cache = <&L3_0>; }; + + L1_I_500: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + + L1_D_500: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; }; CPU6: cpu@600 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x600>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x20000>; next-level-cache = <&L2_6>; L2_6: l2-cache { @@ -156,14 +210,23 @@ cache-level = <2>; next-level-cache = <&L3_0>; }; + + L1_I_600: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + + L1_D_600: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; }; CPU7: cpu@700 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x700>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x90000000>; + enable-method = "psci"; cache-size = <0x20000>; next-level-cache = <&L2_7>; L2_7: l2-cache { @@ -172,6 +235,16 @@ cache-level = <2>; next-level-cache = <&L3_0>; }; + + L1_I_700: l1-icache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; + + L1_D_700: l1-dcache { + compatible = "arm,arch-cache"; + qcom,dump-size = <0x14000>; + }; }; cpu-map { @@ -211,6 +284,112 @@ }; }; }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + }; + + cpuss_dump { + compatible = "qcom,cpuss-dump"; + + qcom,l1_i_cache0 { + qcom,dump-node = <&L1_I_0>; + qcom,dump-id = <0x60>; + }; + + qcom,l1_i_cache1 { + qcom,dump-node = <&L1_I_100>; + qcom,dump-id = <0x61>; + }; + + qcom,l1_i_cache2 { + qcom,dump-node = <&L1_I_200>; + qcom,dump-id = <0x62>; + }; + + qcom,l1_i_cache3 { + qcom,dump-node = <&L1_I_300>; + qcom,dump-id = <0x63>; + }; + + qcom,l1_i_cache100 { + qcom,dump-node = <&L1_I_400>; + qcom,dump-id = <0x64>; + }; + + qcom,l1_i_cache101 { + qcom,dump-node = <&L1_I_500>; + qcom,dump-id = <0x65>; + }; + + qcom,l1_i_cache102 { + qcom,dump-node = <&L1_I_600>; + qcom,dump-id = <0x66>; + }; + + qcom,l1_i_cache103 { + qcom,dump-node = <&L1_I_700>; + qcom,dump-id = <0x67>; + }; + + qcom,l1_d_cache0 { + qcom,dump-node = <&L1_D_0>; + qcom,dump-id = <0x80>; + }; + + qcom,l1_d_cache1 { + qcom,dump-node = <&L1_D_100>; + qcom,dump-id = <0x81>; + }; + + qcom,l1_d_cache2 { + qcom,dump-node = <&L1_D_200>; + qcom,dump-id = <0x82>; + }; + + qcom,l1_d_cache3 { + qcom,dump-node = <&L1_D_300>; + qcom,dump-id = <0x83>; + }; + + qcom,l1_d_cache100 { + qcom,dump-node = <&L1_D_400>; + qcom,dump-id = <0x84>; + }; + + qcom,l1_d_cache101 { + qcom,dump-node = <&L1_D_500>; + qcom,dump-id = <0x85>; + }; + + qcom,l1_d_cache102 { + qcom,dump-node = <&L1_D_600>; + qcom,dump-id = <0x86>; + }; + + qcom,l1_d_cache103 { + qcom,dump-node = <&L1_D_700>; + qcom,dump-id = <0x87>; + }; + }; + + firmware: firmware { + android { + compatible = "android,firmware"; + fstab { + compatible = "android,fstab"; + vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc/8804000.sdhci/by-name/vendor"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,slotselect"; + status = "ok"; + }; + }; + }; }; reserved-memory { @@ -337,6 +516,8 @@ }; #include "sdmshrike-gdsc.dtsi" +#include "sdmshrike-sde-pll.dtsi" +#include "sdmshrike-sde.dtsi" &soc { #address-cells = <1>; @@ -495,6 +676,24 @@ }; }; + qcom,chd_silver { + compatible = "qcom,core-hang-detect"; + label = "silver"; + qcom,threshold-arr = <0x18000058 0x18010058 + 0x18020058 0x18030058>; + qcom,config-arr = <0x18000060 0x18010060 + 0x18020060 0x18030060>; + }; + + qcom,chd_gold { + compatible = "qcom,core-hang-detect"; + label = "gold"; + qcom,threshold-arr = <0x18040058 0x18050058 + 0x18060058 0x18070058>; + qcom,config-arr = <0x18040060 0x18050060 + 0x18060060 0x18070060>; + }; + qcom,llcc@9200000 { compatible = "qcom,llcc-core", "syscon", "simple-mfd"; reg = <0x9200000 0x450000>; @@ -534,6 +733,174 @@ #mbox-cells = <1>; }; + tcsr_mutex_block: syscon@1f40000 { + compatible = "syscon"; + reg = <0x1f40000 0x20000>; + }; + + tcsr_mutex: qcom,hwlock { + compatible = "qcom,tcsr-mutex"; + syscon = <&tcsr_mutex_block 0 0x1000>; + #hwlock-cells = <1>; + }; + + smem: qcom,smem { + compatible = "qcom,smem"; + memory-region = <&smem_region>; + hwlocks = <&tcsr_mutex 3>; + }; + + apcs: syscon@17c0000c { + compatible = "syscon"; + reg = <0x17c0000c 0x4>; + }; + + apcs_glb: mailbox@17c00000 { + compatible = "qcom,sm8150-apcs-hmss-global"; + reg = <0x17c00000 0x1000>; + + #mbox-cells = <1>; + }; + + qcom,glink { + compatible = "qcom,glink"; + modem { + qcom,remote-pid = <1>; + transport = "smem"; + mboxes = <&apcs_glb 12>; + mbox-names = "mpss_smem"; + interrupts = ; + + modem_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + }; + + adsp { + qcom,remote-pid = <2>; + transport = "smem"; + mboxes = <&apcs_glb 8>; + mbox-names = "adsp_smem"; + interrupts = ; + + adsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + apr_tal_rpmsg { + qcom,glink-channels = "apr_audio_svc"; + qcom,intents = <0x200 20>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + }; + + dsps { + qcom,remote-pid = <3>; + transport = "smem"; + mboxes = <&apcs_glb 24>; + mbox-names = "dsps_smem"; + interrupts = ; + + dsps_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + }; + + cdsp { + qcom,remote-pid = <5>; + transport = "smem"; + mboxes = <&apcs_glb 4>; + mbox-names = "cdsp_smem"; + interrupts = ; + + cdsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + }; + + glink_spi_xprt_wdsp: wdsp { + qcom,remote-pid = <10>; + transport = "spi"; + tx-descriptors = <0x12000 0x12004>; + rx-descriptors = <0x1200c 0x12010>; + }; + }; + + qcom,glinkpkt { + compatible = "qcom,glinkpkt"; + + qcom,glinkpkt-at-mdm0 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DS"; + qcom,glinkpkt-dev-name = "at_mdm0"; + }; + + qcom,glinkpkt-apr-apps2 { + qcom,glinkpkt-edge = "adsp"; + qcom,glinkpkt-ch-name = "apr_apps2"; + qcom,glinkpkt-dev-name = "apr_apps2"; + }; + + qcom,glinkpkt-data40-cntl { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA40_CNTL"; + qcom,glinkpkt-dev-name = "smdcntl8"; + }; + + qcom,glinkpkt-data1 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA1"; + qcom,glinkpkt-dev-name = "smd7"; + }; + + qcom,glinkpkt-data4 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA4"; + qcom,glinkpkt-dev-name = "smd8"; + }; + + qcom,glinkpkt-data11 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA11"; + qcom,glinkpkt-dev-name = "smd11"; + }; + }; + slim_aud: slim@171c0000 { cell-index = <1>; compatible = "qcom,slim-ngd"; @@ -608,6 +975,24 @@ ; }; + snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <1 627>; + qcom,active-only; + status = "ok"; + qcom,bw-tbl = < 1 >; + }; + + cdsp_keepalive: qcom,cdsp_keepalive { + compatible = "qcom,devbw"; + governor = "powersave"; + qcom,src-dst-ports = <154 10070>; + qcom,active-only; + status = "ok"; + qcom,bw-tbl = < 1 >; + }; + clock_rpmh: qcom,rpmhclk { compatible = "qcom,rpmh-clk-sdmshrike"; mboxes = <&apps_rsc 0>; @@ -713,6 +1098,190 @@ reg-names = "rmtfs"; qcom,client-id = <0x00000001>; }; + + qcom,lpass@17300000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x17300000 0x00100>; + + vdd_cx-supply = <&pm855_2_s3_level>; + qcom,proxy-reg-names = "vdd_cx"; + + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <1>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <423>; + qcom,sysmon-id = <1>; + qcom,ssctl-instance-id = <0x14>; + qcom,firmware-name = "adsp"; + memory-region = <&pil_adsp_mem>; + + /* Inputs from lpass */ + interrupts-extended = <&pdc 0 162 1>, + <&adsp_smp2p_in 0 0>, + <&adsp_smp2p_in 2 0>, + <&adsp_smp2p_in 1 0>, + <&adsp_smp2p_in 3 0>; + + interrupt-names = "qcom,wdog", + "qcom,err-fatal", + "qcom,proxy-unvote", + "qcom,err-ready", + "qcom,stop-ack"; + + /* Outputs to lpass */ + qcom,smem-states = <&adsp_smp2p_out 0>; + qcom,smem-state-names = "qcom,force-stop"; + }; + + qcom,ssc@5c00000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x5c00000 0x4000>; + + vdd_cx-supply = <&L8E_LEVEL>; + vdd_mx-supply = <&L4E_LEVEL>; + + qcom,proxy-reg-names = "vdd_cx", "vdd_mx"; + qcom,keep-proxy-regs-on; + + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <12>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <424>; + qcom,sysmon-id = <3>; + qcom,ssctl-instance-id = <0x16>; + qcom,firmware-name = "slpi"; + status = "ok"; + memory-region = <&pil_slpi_mem>; + + /* Inputs from ssc */ + interrupts-extended = <&pdc 0 494 1>, + <&dsps_smp2p_in 0 0>, + <&dsps_smp2p_in 2 0>, + <&dsps_smp2p_in 1 0>, + <&dsps_smp2p_in 3 0>; + + interrupt-names = "qcom,wdog", + "qcom,err-fatal", + "qcom,proxy-unvote", + "qcom,err-ready", + "qcom,stop-ack"; + + /* Outputs to ssc */ + qcom,smem-states = <&dsps_smp2p_out 0>; + qcom,smem-state-names = "qcom,force-stop"; + }; + + qcom,turing@8300000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x8300000 0x100000>; + + vdd_cx-supply = <&VDD_CX_LEVEL>; + qcom,proxy-reg-names = "vdd_cx"; + + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <18>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <601>; + qcom,sysmon-id = <7>; + qcom,ssctl-instance-id = <0x17>; + qcom,firmware-name = "cdsp"; + memory-region = <&pil_cdsp_mem>; + + qcom,msm-bus,name = "pil-cdsp"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <154 10070 0 0>, + <154 10070 0 1>; + + /* Inputs from turing */ + interrupts-extended = <&pdc 0 578 1>, + <&cdsp_smp2p_in 0 0>, + <&cdsp_smp2p_in 2 0>, + <&cdsp_smp2p_in 1 0>, + <&cdsp_smp2p_in 3 0>; + + interrupt-names = "qcom,wdog", + "qcom,err-fatal", + "qcom,proxy-unvote", + "qcom,err-ready", + "qcom,stop-ack"; + + /* Outputs to turing */ + qcom,smem-states = <&cdsp_smp2p_out 0>; + qcom,smem-state-names = "qcom,force-stop"; + }; + + wdog: qcom,wdt@17c10000 { + compatible = "qcom,msm-watchdog"; + reg = <0x17c10000 0x1000>; + reg-names = "wdt-base"; + interrupts = <0 0 0>, <0 1 0>; + qcom,bark-time = <11000>; + qcom,pet-time = <9360>; + qcom,ipi-ping; + qcom,wakeup-enable; + }; + + qcom,msm-imem@146bf000 { + compatible = "qcom,msm-imem"; + reg = <0x146bf000 0x1000>; + ranges = <0x0 0x146bf000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + mem_dump_table@10 { + compatible = "qcom,msm-imem-mem_dump_table"; + reg = <0x10 8>; + }; + + restart_reason@65c { + compatible = "qcom,msm-imem-restart_reason"; + reg = <0x65c 4>; + }; + + boot_stats@6b0 { + compatible = "qcom,msm-imem-boot_stats"; + reg = <0x6b0 32>; + }; + + kaslr_offset@6d0 { + compatible = "qcom,msm-imem-kaslr_offset"; + reg = <0x6d0 12>; + }; + + pil@94c { + compatible = "qcom,msm-imem-pil"; + reg = <0x94c 200>; + }; + + diag_dload@c8 { + compatible = "qcom,msm-imem-diag-dload"; + reg = <0xc8 200>; + }; + }; + + restart@c264000 { + compatible = "qcom,pshold"; + reg = <0xc264000 0x4>, + <0x1fd3000 0x4>; + reg-names = "pshold-base", "tcsr-boot-misc-detect"; + }; + + qcom,mpm2-sleep-counter@c221000 { + compatible = "qcom,mpm2-sleep-counter"; + reg = <0xc221000 0x1000>; + clock-frequency = <32768>; + }; }; &emac_gdsc { @@ -799,7 +1368,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -807,7 +1376,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -815,7 +1384,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -823,7 +1392,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -831,7 +1400,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -839,7 +1408,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -847,7 +1416,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -855,7 +1424,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -863,7 +1432,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_DISP_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -873,7 +1442,7 @@ &gpu_gx_gdsc { parent-supply = <&pm855_1_s10_level>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&pm855_1_s10_level>; status = "ok"; }; @@ -881,7 +1450,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_VIDEO_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -889,7 +1458,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_VIDEO_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -897,7 +1466,7 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_VIDEO_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; status = "ok"; }; @@ -907,6 +1476,7 @@ status = "ok"; }; +#include "sdmshrike-smp2p.dtsi" #include "sdmshrike-pinctrl.dtsi" #include "sdmshrike-regulators.dtsi" #include "sdmshrike-ion.dtsi" @@ -914,3 +1484,5 @@ #include "msm-arm-smmu-sdmshrike.dtsi" #include "sdmshrike-usb.dtsi" #include "sdmshrike-qupv3.dtsi" +#include "sm8150-audio.dtsi" +#include "sdmshrike-gpu.dtsi" diff --git a/arch/arm64/boot/dts/qcom/sm6150-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sm6150-cdp-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..36c64e4d5610a9914d214f439b9952bf9b7c3555 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-cdp-overlay.dts @@ -0,0 +1,24 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include + +#include "sm6150-cdp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM6150 CDP"; + compatible = "qcom,sm6150-cdp", "qcom,sm6150", "qcom,cdp"; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-cdp.dts b/arch/arm64/boot/dts/qcom/sm6150-cdp.dts similarity index 78% rename from arch/arm64/boot/dts/qcom/sdm640-cdp.dts rename to arch/arm64/boot/dts/qcom/sm6150-cdp.dts index 54ca0de8c86c0b14494d6b3174f69f90087b4967..14c73223a7d9ba2d73f535652a5a25262a4ace51 100644 --- a/arch/arm64/boot/dts/qcom/sdm640-cdp.dts +++ b/arch/arm64/boot/dts/qcom/sm6150-cdp.dts @@ -12,11 +12,11 @@ /dts-v1/; -#include "sdm640.dtsi" -#include "sdm640-cdp.dtsi" +#include "sm6150.dtsi" +#include "sm6150-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. SDM640 CDP"; - compatible = "qcom,sdm640-cdp", "qcom,sdm640", "qcom,cdp"; + model = "Qualcomm Technologies, Inc. SM6150 CDP"; + compatible = "qcom,sm6150-cdp", "qcom,sm6150", "qcom,cdp"; qcom,board-id = <1 0>; }; diff --git a/arch/arm64/boot/dts/qcom/sdm640-cdp.dtsi b/arch/arm64/boot/dts/qcom/sm6150-cdp.dtsi similarity index 100% rename from arch/arm64/boot/dts/qcom/sdm640-cdp.dtsi rename to arch/arm64/boot/dts/qcom/sm6150-cdp.dtsi diff --git a/arch/arm64/boot/dts/qcom/sdm640-gdsc.dtsi b/arch/arm64/boot/dts/qcom/sm6150-gdsc.dtsi similarity index 100% rename from arch/arm64/boot/dts/qcom/sdm640-gdsc.dtsi rename to arch/arm64/boot/dts/qcom/sm6150-gdsc.dtsi diff --git a/arch/arm64/boot/dts/qcom/sdm640-ion.dtsi b/arch/arm64/boot/dts/qcom/sm6150-ion.dtsi similarity index 100% rename from arch/arm64/boot/dts/qcom/sdm640-ion.dtsi rename to arch/arm64/boot/dts/qcom/sm6150-ion.dtsi diff --git a/arch/arm64/boot/dts/qcom/sm6150-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sm6150-mtp-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..cdb2c0e4e55613de94d3f7c0e950ea541b559116 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-mtp-overlay.dts @@ -0,0 +1,24 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include + +#include "sm6150-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM6150 MTP"; + compatible = "qcom,sm6150-mtp", "qcom,sm6150", "qcom,mtp"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-mtp.dts b/arch/arm64/boot/dts/qcom/sm6150-mtp.dts similarity index 78% rename from arch/arm64/boot/dts/qcom/sdm640-mtp.dts rename to arch/arm64/boot/dts/qcom/sm6150-mtp.dts index c86f376477a8ba1c9579a61e8feecd6d2f43ae39..a45cad0604033d7d391bec2205d02d52223170d5 100644 --- a/arch/arm64/boot/dts/qcom/sdm640-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm6150-mtp.dts @@ -12,11 +12,11 @@ /dts-v1/; -#include "sdm640.dtsi" -#include "sdm640-mtp.dtsi" +#include "sm6150.dtsi" +#include "sm6150-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. SDM640 MTP"; - compatible = "qcom,sdm640-mtp", "qcom,sdm640", "qcom,mtp"; + model = "Qualcomm Technologies, Inc. SM6150 MTP"; + compatible = "qcom,sm6150-mtp", "qcom,sm6150", "qcom,mtp"; qcom,board-id = <8 0>; }; diff --git a/arch/arm64/boot/dts/qcom/sdm640-mtp.dtsi b/arch/arm64/boot/dts/qcom/sm6150-mtp.dtsi similarity index 100% rename from arch/arm64/boot/dts/qcom/sdm640-mtp.dtsi rename to arch/arm64/boot/dts/qcom/sm6150-mtp.dtsi diff --git a/arch/arm64/boot/dts/qcom/sm6150-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sm6150-pinctrl.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..1126ddac9394d8e0fd9b52cd9d99fdea51e094ba --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-pinctrl.dtsi @@ -0,0 +1,538 @@ +/* Copyright (c) 2018, 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 { + tlmm: pinctrl@03000000 { + compatible = "qcom,sm6150-pinctrl"; + reg = <0x03000000 0xdc2000>; + interrupts = <0 208 0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + /* QUPv3_0 South SE mappings */ + /* SE 0 pin mappings */ + qupv3_se0_2uart_pins: qupv3_se0_2uart_pins { + qupv3_se0_2uart_active: qupv3_se0_2uart_active { + mux { + pins = "gpio16", "gpio17"; + function = "qup00"; + }; + + config { + pins = "gpio16", "gpio17"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se0_2uart_sleep: qupv3_se0_2uart_sleep { + mux { + pins = "gpio16", "gpio17"; + function = "gpio"; + }; + + config { + pins = "gpio16", "gpio17"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + /* SE 1 pin mappings */ + qupv3_se1_i2c_pins: qupv3_se1_i2c_pins { + qupv3_se1_i2c_active: qupv3_se1_i2c_active { + mux { + pins = "gpio4", "gpio5"; + function = "qup01"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se1_i2c_sleep: qupv3_se1_i2c_sleep { + mux { + pins = "gpio4", "gpio5"; + function = "gpio"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + /* SE 2 pin mappings */ + qupv3_se2_i2c_pins: qupv3_se2_i2c_pins { + qupv3_se2_i2c_active: qupv3_se2_i2c_active { + mux { + pins = "gpio0", "gpio1"; + function = "qup02"; + }; + + config { + pins = "gpio0", "gpio1"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se2_i2c_sleep: qupv3_se2_i2c_sleep { + mux { + pins = "gpio0", "gpio1"; + function = "gpio"; + }; + + config { + pins = "gpio0", "gpio1"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se2_spi_pins: qupv3_se2_spi_pins { + qupv3_se2_spi_active: qupv3_se2_spi_active { + mux { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + function = "qup02"; + }; + + config { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se2_spi_sleep: qupv3_se2_spi_sleep { + mux { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + function = "gpio"; + }; + + config { + pins = "gpio0", "gpio1", "gpio2", + "gpio3"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 3 pin mappings */ + qupv3_se3_i2c_pins: qupv3_se3_i2c_pins { + qupv3_se3_i2c_active: qupv3_se3_i2c_active { + mux { + pins = "gpio18", "gpio19"; + function = "qup03"; + }; + + config { + pins = "gpio18", "gpio19"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se3_i2c_sleep: qupv3_se3_i2c_sleep { + mux { + pins = "gpio18", "gpio19"; + function = "gpio"; + }; + + config { + pins = "gpio18", "gpio19"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + /* QUPv3_1 North instances */ + /* SE 4 pin mappings */ + qupv3_se4_i2c_pins: qupv3_se4_i2c_pins { + qupv3_se4_i2c_active: qupv3_se4_i2c_active { + mux { + pins = "gpio20", "gpio21"; + function = "qup10"; + }; + + config { + pins = "gpio20", "gpio21"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se4_i2c_sleep: qupv3_se4_i2c_sleep { + mux { + pins = "gpio20", "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio20", "gpio21"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se4_spi_pins: qupv3_se4_spi_pins { + qupv3_se4_spi_active: qupv3_se4_spi_active { + mux { + pins = "gpio20", "gpio21", "gpio22", + "gpio23"; + function = "qup10"; + }; + + config { + pins = "gpio20", "gpio21", "gpio22", + "gpio23"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se4_spi_sleep: qupv3_se4_spi_sleep { + mux { + pins = "gpio20", "gpio21", "gpio22", + "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio20", "gpio21", "gpio22", + "gpio23"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 5 pin mappings */ + qupv3_se5_i2c_pins: qupv3_se5_i2c_pins { + qupv3_se5_i2c_active: qupv3_se5_i2c_active { + mux { + pins = "gpio14", "gpio15"; + function = "qup11"; + }; + + config { + pins = "gpio14", "gpio15"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se5_i2c_sleep: qupv3_se5_i2c_sleep { + mux { + pins = "gpio14", "gpio15"; + function = "gpio"; + }; + + config { + pins = "gpio14", "gpio15"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + /* SE 6 pin mappings */ + qupv3_se6_i2c_pins: qupv3_se6_i2c_pins { + qupv3_se6_i2c_active: qupv3_se6_i2c_active { + mux { + pins = "gpio6", "gpio7"; + function = "qup12"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se6_i2c_sleep: qupv3_se6_i2c_sleep { + mux { + pins = "gpio6", "gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio7"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se6_spi_pins: qupv3_se6_spi_pins { + qupv3_se6_spi_active: qupv3_se6_spi_active { + mux { + pins = "gpio6", "gpio7", "gpio8", + "gpio9"; + function = "qup12"; + }; + + config { + pins = "gpio6", "gpio7", "gpio8", + "gpio9"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se6_spi_sleep: qupv3_se6_spi_sleep { + mux { + pins = "gpio6", "gpio7", "gpio8", + "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio6", "gpio7", "gpio8", + "gpio9"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + /* SE 7 pin mappings */ + qupv3_se7_i2c_pins: qupv3_se7_i2c_pins { + qupv3_se7_i2c_active: qupv3_se7_i2c_active { + mux { + pins = "gpio10", "gpio11"; + function = "qup13"; + }; + + config { + pins = "gpio10", "gpio11"; + drive-strength = <2>; + bias-disable; + }; + }; + + qupv3_se7_i2c_sleep: qupv3_se7_i2c_sleep { + mux { + pins = "gpio10", "gpio11"; + function = "gpio"; + }; + + config { + pins = "gpio10", "gpio11"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + qupv3_se7_spi_pins: qupv3_se7_spi_pins { + qupv3_se7_spi_active: qupv3_se7_spi_active { + mux { + pins = "gpio10", "gpio11", "gpio12", + "gpio13"; + function = "qup13"; + }; + + config { + pins = "gpio10", "gpio11", "gpio12", + "gpio13"; + drive-strength = <6>; + bias-disable; + }; + }; + + qupv3_se7_spi_sleep: qupv3_se7_spi_sleep { + mux { + pins = "gpio10", "gpio11", "gpio12", + "gpio13"; + function = "gpio"; + }; + + config { + pins = "gpio10", "gpio11", "gpio12", + "gpio13"; + drive-strength = <6>; + bias-disable; + }; + }; + }; + + qupv3_se7_4uart_pins: qupv3_se7_4uart_pins { + qupv3_se7_ctsrx: qupv3_se7_ctsrx { + mux { + pins = "gpio10", "gpio13"; + function = "qup13"; + }; + + config { + pins = "gpio10", "gpio13"; + drive-strength = <2>; + bias-no-pull; + }; + }; + + qupv3_se7_rts: qupv3_se7_rts { + mux { + pins = "gpio11"; + function = "qup13"; + }; + + config { + pins = "gpio11"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + qupv3_se7_tx: qupv3_se7_tx { + mux { + pins = "gpio12"; + function = "qup13"; + }; + + config { + pins = "gpio12"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + + /* SDC pin type */ + sdc1_clk_on: sdc1_clk_on { + config { + pins = "sdc1_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc1_clk_off: sdc1_clk_off { + config { + pins = "sdc1_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc1_cmd_on: sdc1_cmd_on { + config { + pins = "sdc1_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc1_cmd_off: sdc1_cmd_off { + config { + pins = "sdc1_cmd"; + num-grp-pins = <1>; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc1_data_on: sdc1_data_on { + config { + pins = "sdc1_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc1_data_off: sdc1_data_off { + config { + pins = "sdc1_data"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc1_rclk_on: sdc1_rclk_on { + config { + pins = "sdc1_rclk"; + bias-pull-down; /* pull down */ + }; + }; + + sdc1_rclk_off: sdc1_rclk_off { + config { + pins = "sdc1_rclk"; + bias-pull-down; /* pull down */ + }; + }; + + sdc2_clk_on: sdc2_clk_on { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <16>; /* 16 MA */ + }; + }; + + sdc2_clk_off: sdc2_clk_off { + config { + pins = "sdc2_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_cmd_on: sdc2_cmd_on { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_cmd_off: sdc2_cmd_off { + config { + pins = "sdc2_cmd"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + sdc2_data_on: sdc2_data_on { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <10>; /* 10 MA */ + }; + }; + + sdc2_data_off: sdc2_data_off { + config { + pins = "sdc2_data"; + bias-pull-up; /* pull up */ + drive-strength = <2>; /* 2 MA */ + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-pm.dtsi b/arch/arm64/boot/dts/qcom/sm6150-pm.dtsi similarity index 94% rename from arch/arm64/boot/dts/qcom/sdm640-pm.dtsi rename to arch/arm64/boot/dts/qcom/sm6150-pm.dtsi index 81a883a79d741add0332d1648e0e9dc8be9c144d..303f0634f01b620c3ebdcabfde971222d674544e 100644 --- a/arch/arm64/boot/dts/qcom/sdm640-pm.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6150-pm.dtsi @@ -46,10 +46,10 @@ qcom,is-reset; }; - qcom,pm-cluster-level@2 { /* Cx off */ + qcom,pm-cluster-level@2 { /* Cx Ret */ reg = <2>; - label = "cx-off"; - qcom,psci-mode = <0x224>; + label = "cx-ret"; + qcom,psci-mode = <0x124>; qcom,latency-us = <4562>; qcom,ss-power = <290>; qcom,energy-overhead = <6989829>; @@ -62,7 +62,7 @@ qcom,pm-cluster-level@3 { /* LLCC off, AOSS sleep */ reg = <3>; label = "llcc-off"; - qcom,psci-mode = <0xC24>; + qcom,psci-mode = <0xB24>; qcom,latency-us = <6562>; qcom,ss-power = <165>; qcom,energy-overhead = <7000029>; @@ -166,4 +166,9 @@ reg = <0xc300000 0x1000>, <0xc3f0004 0x4>; reg-names = "phys_addr_base", "offset_addr"; }; + + qcom,rpmh-master-stats@b221200 { + compatible = "qcom,rpmh-master-stats-v1"; + reg = <0xb221200 0x60>; + }; }; diff --git a/arch/arm64/boot/dts/qcom/sm6150-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sm6150-qrd-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..2854d1471c9981b4ccac43025c518b4d6006f3f7 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-qrd-overlay.dts @@ -0,0 +1,24 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include + +#include "sm6150-qrd.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM6150 QRD"; + compatible = "qcom,sm6150-qrd", "qcom,sm6150", "qcom,qrd"; + qcom,board-id = <11 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-qrd.dts b/arch/arm64/boot/dts/qcom/sm6150-qrd.dts similarity index 78% rename from arch/arm64/boot/dts/qcom/sdm640-qrd.dts rename to arch/arm64/boot/dts/qcom/sm6150-qrd.dts index 477c2f11906b2ec4399b5abfed2a430ffddb775e..8be78315c2665b48fbb8e6439225d596bbf8e3ac 100644 --- a/arch/arm64/boot/dts/qcom/sdm640-qrd.dts +++ b/arch/arm64/boot/dts/qcom/sm6150-qrd.dts @@ -12,11 +12,11 @@ /dts-v1/; -#include "sdm640.dtsi" -#include "sdm640-qrd.dtsi" +#include "sm6150.dtsi" +#include "sm6150-qrd.dtsi" / { - model = "Qualcomm Technologies, Inc. SDM640 QRD"; - compatible = "qcom,sdm640-qrd", "qcom,sdm640", "qcom,qrd"; + model = "Qualcomm Technologies, Inc. SM6150 QRD"; + compatible = "qcom,sm6150-qrd", "qcom,sm6150", "qcom,qrd"; qcom,board-id = <11 0>; }; diff --git a/arch/arm64/boot/dts/qcom/sdm640-qrd.dtsi b/arch/arm64/boot/dts/qcom/sm6150-qrd.dtsi similarity index 100% rename from arch/arm64/boot/dts/qcom/sdm640-qrd.dtsi rename to arch/arm64/boot/dts/qcom/sm6150-qrd.dtsi diff --git a/arch/arm64/boot/dts/qcom/sm6150-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sm6150-qupv3.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..1ab49b81d1081e8bb610a65ee8c22b09a06a9e19 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-qupv3.dtsi @@ -0,0 +1,328 @@ +/* Copyright (c) 2017-2018, 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 + +&soc { + /* + * QUPv3 North & South Instances + * North 0 : SE 4 + * North 1 : SE 5 + * North 2 : SE 6 + * North 3 : SE 7 + * South 0 : SE 0 + * South 1 : SE 1 + * South 2 : SE 2 + * South 3 : SE 3 + */ + + /* QUPv3 South Instances */ + qupv3_0: qcom,qupv3_0_geni_se@8c0000 { + compatible = "qcom,qupv3-geni-se"; + reg = <0x8c0000 0x6000>; + qcom,bus-mas-id = ; + qcom,bus-slv-id = ; + qcom,iommu-s1-bypass; + + iommu_qupv3_0_geni_se_cb: qcom,iommu_qupv3_0_geni_se_cb { + compatible = "qcom,qupv3-geni-se-cb"; + iommus = <&apps_smmu 0xc3 0x0>; + }; + }; + + /* Debug UART Instance for CDP/MTP platform */ + qupv3_se0_2uart: qcom,qup_uart@0x880000 { + compatible = "qcom,msm-geni-console"; + reg = <0x880000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se0_2uart_active>; + pinctrl-1 = <&qupv3_se0_2uart_sleep>; + interrupts = ; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + /* I2C */ + qupv3_se1_i2c: i2c@884000 { + compatible = "qcom,i2c-geni"; + reg = <0x884000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S1_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 1 3 64 0>, + <&gpi_dma0 1 1 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se1_i2c_active>; + pinctrl-1 = <&qupv3_se1_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se2_i2c: i2c@888000 { + compatible = "qcom,i2c-geni"; + reg = <0x888000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 2 3 64 0>, + <&gpi_dma0 1 2 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se2_i2c_active>; + pinctrl-1 = <&qupv3_se2_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + qupv3_se3_i2c: i2c@88c000 { + compatible = "qcom,i2c-geni"; + reg = <0x88c000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + dmas = <&gpi_dma0 0 3 3 64 0>, + <&gpi_dma0 1 3 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se3_i2c_active>; + pinctrl-1 = <&qupv3_se3_i2c_sleep>; + qcom,wrapper-core = <&qupv3_0>; + status = "disabled"; + }; + + /* SPI */ + qupv3_se2_spi: spi@888000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x888000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se2_spi_active>; + pinctrl-1 = <&qupv3_se2_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_0>; + dmas = <&gpi_dma0 0 2 1 64 0>, + <&gpi_dma0 1 2 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + /* QUPv3 North instances */ + qupv3_1: qcom,qupv3_1_geni_se@ac0000 { + compatible = "qcom,qupv3-geni-se"; + reg = <0xac0000 0x6000>; + qcom,bus-mas-id = ; + qcom,bus-slv-id = ; + qcom,iommu-s1-bypass; + + iommu_qupv3_1_geni_se_cb: qcom,iommu_qupv3_1_geni_se_cb { + compatible = "qcom,qupv3-geni-se-cb"; + iommus = <&apps_smmu 0x363 0x0>; + }; + }; + + /* I2C */ + qupv3_se4_i2c: i2c@a80000 { + compatible = "qcom,i2c-geni"; + reg = <0xa80000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S0_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 0 3 64 0>, + <&gpi_dma1 1 0 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se4_i2c_active>; + pinctrl-1 = <&qupv3_se4_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se5_i2c: i2c@a84000 { + compatible = "qcom,i2c-geni"; + reg = <0xa84000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S1_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 1 3 64 0>, + <&gpi_dma1 1 1 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se5_i2c_active>; + pinctrl-1 = <&qupv3_se5_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se6_i2c: i2c@a88000 { + compatible = "qcom,i2c-geni"; + reg = <0xa88000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 2 3 64 0>, + <&gpi_dma1 1 2 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se6_i2c_active>; + pinctrl-1 = <&qupv3_se6_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + qupv3_se7_i2c: i2c@a8c000 { + compatible = "qcom,i2c-geni"; + reg = <0xa8c000 0x4000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + dmas = <&gpi_dma1 0 3 3 64 0>, + <&gpi_dma1 1 3 3 64 0>; + dma-names = "tx", "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se7_i2c_active>; + pinctrl-1 = <&qupv3_se7_i2c_sleep>; + qcom,wrapper-core = <&qupv3_1>; + status = "disabled"; + }; + + /* SPI */ + qupv3_se4_spi: spi@a80000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa80000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S0_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se4_spi_active>; + pinctrl-1 = <&qupv3_se4_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 0 1 64 0>, + <&gpi_dma1 1 0 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se6_spi: spi@a88000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa88000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se6_spi_active>; + pinctrl-1 = <&qupv3_se6_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 2 1 64 0>, + <&gpi_dma1 1 2 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + qupv3_se7_spi: spi@a8c000 { + compatible = "qcom,spi-geni"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xa8c000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se7_spi_active>; + pinctrl-1 = <&qupv3_se7_spi_sleep>; + interrupts = ; + spi-max-frequency = <50000000>; + qcom,wrapper-core = <&qupv3_1>; + dmas = <&gpi_dma1 0 3 1 64 0>, + <&gpi_dma1 1 3 1 64 0>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + /* + * HS UART instances. HS UART usecases can be supported on these + * instances only. + */ + qupv3_se7_4uart: qcom,qup_uart@0xa8c000 { + compatible = "qcom,msm-geni-serial-hs"; + reg = <0xa8c000 0x4000>; + reg-names = "se_phys"; + clock-names = "se-clk", "m-ahb", "s-ahb"; + clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qupv3_se7_ctsrx>, <&qupv3_se7_rts>, + <&qupv3_se7_tx>; + pinctrl-1 = <&qupv3_se7_ctsrx>, <&qupv3_se7_rts>, + <&qupv3_se7_tx>; + interrupts-extended = <&pdc GIC_SPI 356 0>, + <&tlmm 13 0>; + status = "disabled"; + qcom,wakeup-byte = <0xFD>; + qcom,wrapper-core = <&qupv3_1>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sm6150-rumi-overlay.dts b/arch/arm64/boot/dts/qcom/sm6150-rumi-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..7b5115ecdb68ae2562d0648efd11cf15e0cf5c72 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-rumi-overlay.dts @@ -0,0 +1,24 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include + +#include "sm6150-rumi.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM6150 RUMI"; + compatible = "qcom,sm6150-rumi", "qcom,sm6150", "qcom,rumi"; + qcom,board-id = <15 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm640-rumi.dts b/arch/arm64/boot/dts/qcom/sm6150-rumi.dts similarity index 78% rename from arch/arm64/boot/dts/qcom/sdm640-rumi.dts rename to arch/arm64/boot/dts/qcom/sm6150-rumi.dts index 0e3a602c1dacd049869fee0369b02af93106faab..fd344ffd4673837efc62856656eb669c9f6df562 100644 --- a/arch/arm64/boot/dts/qcom/sdm640-rumi.dts +++ b/arch/arm64/boot/dts/qcom/sm6150-rumi.dts @@ -13,11 +13,11 @@ /dts-v1/; /memreserve/ 0x90000000 0x00000100; -#include "sdm640.dtsi" -#include "sdm640-rumi.dtsi" +#include "sm6150.dtsi" +#include "sm6150-rumi.dtsi" / { - model = "Qualcomm Technologies, Inc. SDM640 RUMI"; - compatible = "qcom,sdm640-rumi", "qcom,sdm640", "qcom,rumi"; + model = "Qualcomm Technologies, Inc. SM6150 RUMI"; + compatible = "qcom,sm6150-rumi", "qcom,sm6150", "qcom,rumi"; qcom,board-id = <15 0>; }; diff --git a/arch/arm64/boot/dts/qcom/sm6150-rumi.dtsi b/arch/arm64/boot/dts/qcom/sm6150-rumi.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..a7e3ae5083873059df9f68a9eea9fba89cf3271d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-rumi.dtsi @@ -0,0 +1,77 @@ +/* Copyright (c) 2018, 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 { + timer { + clock-frequency = <1000000>; + }; + + timer@0x17c00000 { + clock-frequency = <1000000>; + }; + + wdog: qcom,wdt@17c10000{ + status = "disabled"; + }; +}; + +&qupv3_se0_2uart { + status = "ok"; +}; + +&sdhc_1 { + vdd-supply = <&pm6150l_l11>; + qcom,vdd-voltage-level = <2960000 2960000>; + qcom,vdd-current-level = <200 570000>; + + vdd-io-supply = <&pm6150_l12>; + qcom,vdd-io-always-on; + qcom,vdd-io-lpm-sup; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 325000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on>; + pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off>; + + qcom,clk-rates = <400000 20000000 25000000 50000000>; + qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v"; + + /delete-property/qcom,devfreq,freq-table; + + status = "ok"; +}; + +&sdhc_2 { + vdd-supply = <&pm6150l_l9>; + qcom,vdd-voltage-level = <2960000 2960000>; + qcom,vdd-current-level = <200 800000>; + + vdd-io-supply = <&pm6150l_l6>; + qcom,vdd-io-voltage-level = <1800000 2950000>; + qcom,vdd-io-current-level = <200 22000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>; + + qcom,clk-rates = <400000 20000000 25000000 50000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50"; + + /delete-property/qcom,devfreq,freq-table; + + status = "disabled"; +}; + +&spmi_bus { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/qcom/sm6150-stub-regulator.dtsi b/arch/arm64/boot/dts/qcom/sm6150-stub-regulator.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..02213050f58905ba8dfc9470c6d349f00961bb50 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150-stub-regulator.dtsi @@ -0,0 +1,345 @@ +/* Copyright (c) 2018, 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 PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +/* Stub regulators */ + +/ { + /* pm6150 S1 - VDD_CX supply */ + VDD_CX_LEVEL: + pm6150_s1_level: regulator-pm6150-s1 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_s1_level"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + VDD_CX_LEVEL_AO: + pm6150_s1_level_ao: regulator-pm6150-s1-level-ao { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_s1_level_ao"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + /* pm6150 S3 - VDD_MX supply */ + VDD_MX_LEVEL: + S3A_LEVEL: pm6150_s3_level: regulator-pm6150-s3 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_s3_level"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + VDD_MX_LEVEL_AO: + S3A_LEVEL_AO: pm6150_s3_level_ao: regulator-pm6150-s3-level-ao { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_s3_level_ao"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + S1C: pm6150l_s1: regulator-pm6150l-s1 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_s1"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = <1128000>; + regulator-max-microvolt = <1128000>; + }; + + S2C: pm6150l_s2: regulator-pm6150l-s2 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_s2"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = <348000>; + regulator-max-microvolt = <1200000>; + }; + + /* pm6150l S7 - VDD_MSS supply */ + VDD_MSS_LEVEL: + S7C_LEVEL: pm6150l_s7_level: regulator-pm6150l-s7 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_s7_level"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + S8C: pm6150l_s8: regulator-pm6150l-s8 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_s8"; + qcom,hpm-min-load = <100000>; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1400000>; + }; + + L1A: pm6150_l1: regulator-pm6150-l1 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l1"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1252000>; + }; + + L2A: pm6150_l2: regulator-pm6150-l2 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l2"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1050000>; + }; + + L3A: pm6150_l3: regulator-pm6150-l3 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l3"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1060000>; + }; + + L4A: pm6150_l4: regulator-pm6150-l4 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l4"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <875000>; + regulator-max-microvolt = <975000>; + }; + + L5A: pm6150_l5: regulator-pm6150-l5 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l5"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2970000>; + }; + + L6A: pm6150_l6: regulator-pm6150-l6 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l6"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <650000>; + }; + + /* pm6150 L7 - LPI_MX supply */ + L7A_LEVEL: pm6150_l7_level: regulator-pm6150-l7 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l7_level"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + /* pm6150 L8 - LPI_CX supply */ + L8A_LEVEL: pm6150_l8_level: regulator-pm6150-l8 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l8_level"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = ; + regulator-max-microvolt = ; + }; + + L9A: pm6150_l9: regulator-pm6150-l9 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l9"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <728000>; + }; + + L10A: pm6150_l10: regulator-pm6150-l10 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l10"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1829000>; + }; + + L11A: pm6150_l11: regulator-pm6150-l11 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l11"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1890000>; + }; + + L12A: pm6150_l12: regulator-pm6150-l12 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l12"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1950000>; + }; + + L13A: pm6150_l13: regulator-pm6150-l13 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l13"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + }; + + L14A: pm6150_l14: regulator-pm6150-l14 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l14"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1850000>; + }; + + L15A: pm6150_l15: regulator-pm6150-l15 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l15"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + }; + + L16A: pm6150_l16: regulator-pm6150-l16 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l16"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2430000>; + regulator-max-microvolt = <2970000>; + }; + + L17A: pm6150_l17: regulator-pm6150-l17 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l17"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3230000>; + }; + + L18A: pm6150_l18: regulator-pm6150-l18 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l18"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3008000>; + }; + + L19A: pm6150_l19: regulator-pm6150-l19 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150_l19"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <3008000>; + }; + + L1C: pm6150l_l1: regulator-pm6150l-l1 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l1"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + }; + + L2C: pm6150l_l2: regulator-pm6150l-l2 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l2"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1304000>; + }; + + L3C: pm6150l_l3: regulator-pm6150l-l3 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l3"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1232000>; + regulator-max-microvolt = <1260000>; + }; + + L4C: pm6150l_l4: regulator-pm6150l-l4 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l4"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <2950000>; + }; + + L5C: pm6150l_l5: regulator-pm6150l-l5 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l5"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <2950000>; + }; + + L6C: pm6150l_l6: regulator-pm6150l-l6 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l6"; + qcom,hpm-min-load = <5000>; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <3100000>; + }; + + L7C: pm6150l_l7: regulator-pm6150l-l7 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l7"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3312000>; + }; + + L8C: pm6150l_l8: regulator-pm6150l-l8 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l8"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + }; + + L9C: pm6150l_l9: regulator-pm6150l-l9 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l9"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <3312000>; + }; + + L10C: pm6150l_l10: regulator-pm6150l-l10 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l10"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <3200000>; + regulator-max-microvolt = <3312000>; + }; + + L11C: pm6150l_l11: regulator-pm6150l-l11 { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_l11"; + qcom,hpm-min-load = <10000>; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <3312000>; + }; + + BOB: pm6150l_bob: regulator-pm6150l-bob { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_bob"; + regulator-min-microvolt = <3296000>; + regulator-max-microvolt = <3960000>; + }; + + BOB_AO: pm6150l_bob_ao: regulator-pm6150l-bob-ao { + compatible = "qcom,stub-regulator"; + regulator-name = "pm6150l_bob_ao"; + regulator-min-microvolt = <3296000>; + regulator-max-microvolt = <3960000>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sm6150.dts b/arch/arm64/boot/dts/qcom/sm6150.dts new file mode 100644 index 0000000000000000000000000000000000000000..31c44c4c030536a8ea11e167a1d1dafc53cea0a6 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6150.dts @@ -0,0 +1,21 @@ +/* Copyright (c) 2018, 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 "sm6150.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM6150 SoC"; + compatible = "qcom,sm6150"; + qcom,board-id = <0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm640.dtsi b/arch/arm64/boot/dts/qcom/sm6150.dtsi similarity index 58% rename from arch/arm64/boot/dts/qcom/sdm640.dtsi rename to arch/arm64/boot/dts/qcom/sm6150.dtsi index 7a295f661a3bb26579504b3800cde749946684e9..83ee2be3ab84ab1b2fbf8eba207c6b37bf863a46 100644 --- a/arch/arm64/boot/dts/qcom/sdm640.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6150.dtsi @@ -14,22 +14,30 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include / { - model = "Qualcomm Technologies, Inc. SDM640"; - compatible = "qcom,sdm640"; + model = "Qualcomm Technologies, Inc. SM6150"; + compatible = "qcom,sm6150"; qcom,msm-id = <355 0x0>; interrupt-parent = <&pdc>; aliases { serial0 = &qupv3_se0_2uart; + sdhc1 = &sdhc_1; /* SDC1 eMMC slot */ + sdhc2 = &sdhc_2; /* SDC2 SD Card slot */ + spi0 = &qupv3_se4_spi; + i2c0 = &qupv3_se5_i2c; + i2c1 = &qupv3_se1_i2c; + i2c2 = &qupv3_se3_i2c; + hsuart0 = &qupv3_se7_4uart; }; cpus { @@ -326,6 +334,23 @@ soc: soc { }; + firmware: firmware { + android { + compatible = "android,firmware"; + fstab { + compatible = "android,fstab"; + vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc/7c4000.sdhci/by-name/vendor"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,slotselect,avb"; + status = "ok"; + }; + }; + }; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -337,16 +362,28 @@ reg = <0 0x85700000 0 0x600000>; }; - xbl_region: xbl_region@85e00000 { + xbl_aop_mem: xbl_aop_mem@85e00000 { compatible = "removed-dma-pool"; no-map; - reg = <0 0x85e00000 0 0x100000>; + reg = <0x0 0x85e00000 0x0 0x1ff000>; }; - removed_region: removed_region@85fc0000 { + sec_apps_mem: sec_apps_region@85fff000 { compatible = "removed-dma-pool"; no-map; - reg = <0 0x85fc0000 0 0x2f40000>; + reg = <0x0 0x85fff000 0x0 0x1000>; + }; + + smem_region: smem@86000000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x86000000 0x0 0x200000>; + }; + + removed_region: removed_region@86200000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0x86200000 0 0x2d00000>; }; pil_camera_mem: camera_region@8ab00000 { @@ -483,7 +520,7 @@ }; pdc: interrupt-controller@b220000{ - compatible = "qcom,pdc-sdm640"; + compatible = "qcom,pdc-sm6150"; reg = <0xb220000 0x400>; #interrupt-cells = <3>; interrupt-parent = <&intc>; @@ -617,10 +654,10 @@ interrupts = <1 5 4>; }; - qcom,msm-imem@146bf000 { + qcom,msm-imem@146aa000 { compatible = "qcom,msm-imem"; - reg = <0x146bf000 0x1000>; - ranges = <0x0 0x146bf000 0x1000>; + reg = <0x146aa000 0x1000>; + ranges = <0x0 0x146aa000 0x1000>; #address-cells = <1>; #size-cells = <1>; @@ -662,6 +699,44 @@ qcom,pipe-attr-ee; }; + gpi_dma0: qcom,gpi-dma@0x800000 { + #dma-cells = <5>; + compatible = "qcom,gpi-dma"; + reg = <0x800000 0x60000>; + reg-names = "gpi-top"; + interrupts = <0 244 0>, <0 245 0>, <0 246 0>, <0 247 0>, + <0 248 0>, <0 249 0>, <0 250 0>, <0 251 0>; + qcom,ev-factor = <2>; + qcom,max-num-gpii = <8>; + qcom,gpii-mask = <0x1f>; + iommus = <&apps_smmu 0x00d6 0x0>; + qcom,smmu-cfg = <0x1>; + qcom,iova-range = <0x0 0x100000 0x0 0x100000>; + status = "ok"; + }; + + gpi_dma1: qcom,gpi-dma@0xa00000 { + #dma-cells = <5>; + compatible = "qcom,gpi-dma"; + reg = <0xa00000 0x60000>; + reg-names = "gpi-top"; + interrupts = <0 279 0>, <0 280 0>, <0 281 0>, <0 282 0>, + <0 283 0>, <0 284 0>, <0 293 0>, <0 294 0>; + qcom,ev-factor = <2>; + qcom,max-num-gpii = <8>; + qcom,gpii-mask = <0x1f>; + qcom,smmu-cfg = <0x1>; + qcom,iova-range = <0x0 0x100000 0x0 0x100000>; + iommus = <&apps_smmu 0x0376 0x0>; + status = "ok"; + }; + + aop-msg-client { + compatible = "qcom,debugfs-qmp-client"; + mboxes = <&qmp_aop 0>; + mbox-names = "aop"; + }; + qcom,msm-rtb { compatible = "qcom,msm-rtb"; qcom,rtb-size = <0x100000>; @@ -678,16 +753,30 @@ qcom,wakeup-enable; }; + qcom,chd_sliver { + compatible = "qcom,core-hang-detect"; + label = "silver"; + qcom,threshold-arr = <0x18000058 0x18010058 + 0x18020058 0x18030058 + 0x18040058 0x18050058>; + qcom,config-arr = <0x18000060 0x18010060 + 0x18020060 0x18030060 + 0x18040060 0x18050060>; + }; + + qcom,chd_gold { + compatible = "qcom,core-hang-detect"; + label = "gold"; + qcom,threshold-arr = <0x18060058 0x18070058>; + qcom,config-arr = <0x18060060 0x18070060>; + }; + kryo-erp { compatible = "arm,arm64-kryo-cpu-erp"; interrupts = <1 6 4>, - <1 7 4>, - <0 34 4>, <0 35 4>; interrupt-names = "l1-l2-faultirq", - "l1-l2-errirq", - "l3-scu-errirq", "l3-scu-faultirq"; }; @@ -856,13 +945,426 @@ compatible = "qcom,cmd-db"; reg = <0xc3f000c 8>; }; + + qcom,llcc@9200000 { + compatible = "qcom,llcc-core", "syscon", "simple-mfd"; + reg = <0x9200000 0x450000>; + reg-names = "llcc_base"; + qcom,llcc-banks-off = <0x0>; + qcom,llcc-broadcast-off = <0x400000>; + + llcc: qcom,sm6150-llcc { + compatible = "qcom,sm6150-llcc"; + #cache-cells = <1>; + max-slices = <32>; + cap-based-alloc-and-pwr-collapse; + }; + + qcom,llcc-erp { + compatible = "qcom,llcc-erp"; + interrupt-names = "ecc_irq"; + interrupts = ; + }; + + qcom,llcc-amon { + compatible = "qcom,llcc-amon"; + }; + }; + + sdhc_1: sdhci@7c4000 { + compatible = "qcom,sdhci-msm-v5"; + reg = <0x7c4000 0x1000>, <0x7c5000 0x1000>; + reg-names = "hc_mem", "cmdq_mem"; + + interrupts = <0 641 0>, <0 644 0>; + interrupt-names = "hc_irq", "pwr_irq"; + + qcom,bus-width = <8>; + qcom,large-address-bus; + + qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 + 192000000 384000000>; + qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v"; + + qcom,devfreq,freq-table = <50000000 200000000>; + + clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>, + <&clock_gcc GCC_SDCC1_APPS_CLK>; + clock-names = "iface_clk", "core_clk"; + + qcom,nonremovable; + status = "disabled"; + }; + + sdhc_2: sdhci@8804000 { + compatible = "qcom,sdhci-msm-v5"; + reg = <0x8804000 0x1000>; + reg-names = "hc_mem"; + + interrupts = <0 204 0>, <0 222 0>; + interrupt-names = "hc_irq", "pwr_irq"; + + qcom,bus-width = <4>; + qcom,large-address-bus; + + qcom,clk-rates = <400000 20000000 25000000 + 50000000 100000000 201500000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", + "SDR104"; + + qcom,devfreq,freq-table = <50000000 201500000>; + + clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>, + <&clock_gcc GCC_SDCC2_APPS_CLK>; + clock-names = "iface_clk", "core_clk"; + + status = "disabled"; + }; + + spmi_bus: qcom,spmi@c440000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0xc440000 0x2300>, + <0xc600000 0x2000000>, + <0xe600000 0x100000>, + <0xe700000 0xa0000>, + <0xc40a000 0x26000>; + reg-names = "core", "chnls", "obsrvr", "intr", "cnfg"; + interrupt-names = "periph_irq"; + interrupts = ; + qcom,ee = <0>; + qcom,channel = <0>; + #address-cells = <2>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <4>; + cell-index = <0>; + }; + + slim_aud: slim@62dc0000 { + cell-index = <1>; + compatible = "qcom,slim-ngd"; + reg = <0x62dc0000 0x2c000>, + <0x62d84000 0x2a000>; + 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 = <0x7c0000>; + qcom,ea-pc = <0x2f0>; + status = "disabled"; + qcom,iommu-s1-bypass; + + iommu_slim_aud_ctrl_cb: qcom,iommu_slim_ctrl_cb { + compatible = "qcom,iommu-slim-ctrl-cb"; + iommus = <&apps_smmu 0x17e6 0x0>, + <&apps_smmu 0x17ed 0x0>, + <&apps_smmu 0x17ee 0x1>, + <&apps_smmu 0x17f0 0x1>; + }; + + }; + + slim_qca: slim@62e40000 { + cell-index = <3>; + compatible = "qcom,slim-ngd"; + reg = <0x62e40000 0x2c000>, + <0x62e04000 0x20000>; + reg-names = "slimbus_physical", "slimbus_bam_physical"; + interrupts = <0 291 0>, <0 292 0>; + interrupt-names = "slimbus_irq", "slimbus_bam_irq"; + status = "disabled"; + qcom,iommu-s1-bypass; + + iommu_slim_qca_ctrl_cb: qcom,iommu_slim_ctrl_cb { + compatible = "qcom,iommu-slim-ctrl-cb"; + iommus = <&apps_smmu 0x17f3 0x0>; + }; + }; + + tcsr_mutex_block: syscon@1f40000 { + compatible = "syscon"; + reg = <0x1f40000 0x20000>; + }; + + tcsr_mutex: hwlock { + compatible = "qcom,tcsr-mutex"; + syscon = <&tcsr_mutex_block 0 0x1000>; + #hwlock-cells = <1>; + }; + + smem: qcom,smem { + compatible = "qcom,smem"; + memory-region = <&smem_region>; + hwlocks = <&tcsr_mutex 3>; + }; + + apcs: syscon@17c0000c { + compatible = "syscon"; + reg = <0x17c0000c 0x4>; + }; + + apcs_glb: mailbox@17c00000 { + compatible = "qcom,sm8150-apcs-hmss-global"; + reg = <0x17c00000 0x1000>; + + #mbox-cells = <1>; + }; + + qcom,glink { + compatible = "qcom,glink"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + glink_modem: modem { + qcom,remote-pid = <1>; + transport = "smem"; + mboxes = <&apcs_glb 12>; + mbox-names = "mpss_smem"; + interrupts = ; + + label = "modem"; + qcom,glink-label = "mpss"; + + qcom,modem_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + + qcom,modem_ds { + qcom,glink-channels = "DS"; + qcom,intents = <0x4000 0x2>; + }; + + qcom,modem_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_adsp>, + <&glink_cdsp>; + }; + }; + + glink_adsp: adsp { + qcom,remote-pid = <2>; + transport = "smem"; + mboxes = <&apcs_glb 24>; + mbox-names = "adsp_smem"; + interrupts = ; + + label = "adsp"; + qcom,glink-label = "lpass"; + + qcom,adsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,apr_tal_rpmsg { + qcom,glink-channels = "apr_audio_svc"; + qcom,intents = <0x200 20>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + + qcom,adsp_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_cdsp>; + }; + }; + + glink_cdsp: cdsp { + qcom,remote-pid = <5>; + transport = "smem"; + mboxes = <&apcs_glb 4>; + mbox-names = "cdsp_smem"; + interrupts = ; + + label = "cdsp"; + qcom,glink-label = "cdsp"; + + qcom,cdsp_qrtr { + qcom,glink-channels = "IPCRTR"; + qcom,intents = <0x800 5 + 0x2000 3 + 0x4400 2>; + }; + + qcom,msm_fastrpc_rpmsg { + compatible = "qcom,msm-fastrpc-rpmsg"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + qcom,intents = <0x64 64>; + }; + + qcom,cdsp_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_adsp>; + }; + }; + + glink_spi_xprt_wdsp: wdsp { + qcom,remote-pid = <10>; + transport = "spi"; + tx-descriptors = <0x12000 0x12004>; + rx-descriptors = <0x1200c 0x12010>; + + qcom,wdsp_ctrl { + qcom,glink-channels = "g_glink_ctrl"; + qcom,intents = <0x400 1>; + }; + + qcom,wdsp_ild { + qcom,glink-channels = + "g_glink_persistent_data_ild"; + }; + + qcom,wdsp_nild { + qcom,glink-channels = + "g_glink_persistent_data_nild"; + }; + + qcom,wdsp_data { + qcom,glink-channels = "g_glink_audio_data"; + qcom,intents = <0x1000 2>; + }; + }; + }; + + qcom,glinkpkt { + compatible = "qcom,glinkpkt"; + + qcom,glinkpkt-at-mdm0 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DS"; + qcom,glinkpkt-dev-name = "at_mdm0"; + }; + + qcom,glinkpkt-apr-apps2 { + qcom,glinkpkt-edge = "adsp"; + qcom,glinkpkt-ch-name = "apr_apps2"; + qcom,glinkpkt-dev-name = "apr_apps2"; + }; + + qcom,glinkpkt-data40-cntl { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA40_CNTL"; + qcom,glinkpkt-dev-name = "smdcntl8"; + }; + + qcom,glinkpkt-data1 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA1"; + qcom,glinkpkt-dev-name = "smd7"; + }; + + qcom,glinkpkt-data4 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA4"; + qcom,glinkpkt-dev-name = "smd8"; + }; + + qcom,glinkpkt-data11 { + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA11"; + qcom,glinkpkt-dev-name = "smd11"; + }; + }; + + qmp_aop: qcom,qmp-aop@c300000 { + compatible = "qcom,qmp-mbox"; + reg = <0xc300000 0x1000>, <0x17c0000C 0x4>; + reg-names = "msgram", "irq-reg-base"; + qcom,irq-mask = <0x1>; + interrupts = ; + + label = "aop"; + qcom,early-boot; + priority = <0>; + mbox-desc-offset = <0x0>; + #mbox-cells = <1>; + }; + + qcom,smp2p-modem { + compatible = "qcom,smp2p"; + qcom,smem = <435>, <428>; + interrupts = ; + qcom,ipc = <&apcs 0 14>; + qcom,local-pid = <0>; + qcom,remote-pid = <1>; + + modem_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + modem_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-adsp { + compatible = "qcom,smp2p"; + qcom,smem = <443>, <429>; + interrupts = ; + qcom,ipc = <&apcs 0 26>; + qcom,local-pid = <0>; + qcom,remote-pid = <2>; + + adsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + adsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + qcom,smp2p-cdsp { + compatible = "qcom,smp2p"; + qcom,smem = <94>, <432>; + interrupts = ; + qcom,ipc = <&apcs 0 6>; + qcom,local-pid = <0>; + qcom,remote-pid = <5>; + + cdsp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + cdsp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; }; -#include "sdm640-pinctrl.dtsi" -#include "sdm640-stub-regulator.dtsi" -#include "sdm640-pm.dtsi" -#include "sdm640-gdsc.dtsi" -#include "sdm640-qupv3.dtsi" +#include "pm6150.dtsi" +#include "pm6150l.dtsi" +#include "sm6150-pinctrl.dtsi" +#include "sm6150-stub-regulator.dtsi" +#include "sm6150-pm.dtsi" +#include "sm6150-gdsc.dtsi" +#include "sm6150-qupv3.dtsi" &emac_gdsc { status = "ok"; @@ -960,4 +1462,5 @@ status = "ok"; }; -#include "sdm640-ion.dtsi" +#include "sm6150-ion.dtsi" +#include "msm-arm-smmu-sm6150.dtsi" diff --git a/arch/arm64/boot/dts/qcom/sm8150-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sm8150-audio-overlay.dtsi index d9944735963824862671eb4eeaf8895d3200055d..29da3c5e97aeeb107ad3c2416ed1f71997081249 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-audio-overlay.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-audio-overlay.dtsi @@ -201,7 +201,7 @@ cdc-vdd-pa-supply = <&pm855l_bob>; qcom,cdc-vdd-pa-voltage = <3300000 3300000>; - qcom,cdc-vdd-pa-current = <230000>; + qcom,cdc-vdd-pa-current = <1000000>; qcom,cdc-static-supplies = "cdc-vdd-buck-sido", "cdc-vdd-px", diff --git a/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star-overlay.dts b/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..889797e681af1073cec34733b5043a924c7ffe0a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star-overlay.dts @@ -0,0 +1,23 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include "sm8150-auto-adp-star.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150 AUTO-ADP-STAR"; + compatible = "qcom,sm8150-auto-adp-star", "qcom,sm8150", + "qcom,auto-adp-star"; + qcom,board-id = <25 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star.dts b/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star.dts new file mode 100644 index 0000000000000000000000000000000000000000..250b080ae57ce4b3bd84b8cea16e00e819da23ab --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150-auto.dtsi" +#include "sm8150-auto-adp-star.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150 AUTO-ADP-STAR"; + compatible = "qcom,sm8150-auto-adp-star", "qcom,sm8150", + "qcom,auto-adp-star"; + qcom,board-id = <25 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star.dtsi b/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..10087f62683c5f1c21d1799c3f9a7eeda5310661 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-auto-adp-star.dtsi @@ -0,0 +1,81 @@ +/* Copyright (c) 2018, 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 +#include + +#include "sm8150-auto-pmic-overlay.dtsi" + +&soc { + qcom,lpass@17300000 { + status = "disabled"; + }; + + qcom,ssc@5c00000 { + status = "disabled"; + }; + + qcom,glink { + modem { + status = "disabled"; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + label = "gpio-keys"; + + pinctrl-names = "default"; + pinctrl-0 = <&key_home_default + &key_vol_up_default>; + + home { + label = "home"; + gpios = <&pm855_1_gpios 1 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + gpio-key,wakeup; + debounce-interval = <15>; + linux,can-disable; + }; + + vol_up { + label = "volume_up"; + gpios = <&pm855_1_gpios 6 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + linux,code = ; + gpio-key,wakeup; + debounce-interval = <15>; + linux,can-disable; + }; + }; +}; + +&sdhc_2 { + vdd-supply = <&pm855_1_l17>; + qcom,vdd-voltage-level = <2950000 2960000>; + qcom,vdd-current-level = <200 800000>; + + vdd-io-supply = <&pm855_2_l13>; + qcom,vdd-io-voltage-level = <1808000 2960000>; + qcom,vdd-io-current-level = <200 22000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on + &sdc2_cmd_on &sdc2_data_on &storage_cd_default>; + pinctrl-1 = <&sdc2_clk_off + &sdc2_cmd_off &sdc2_data_off &storage_cd_default>; + + cd-gpios = <&pm855_1_gpios 4 GPIO_ACTIVE_LOW>; + + status = "ok"; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-auto-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sm8150-auto-pmic-overlay.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..cde5ee6c0807041177250c5e29780dce7f9a5a6e --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-auto-pmic-overlay.dtsi @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018, 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 "pm855.dtsi" + +pm855_1_tz: &pm855_tz { +}; + +pm855_1_clkdiv: &pm855_clkdiv { + clock-output-names = "pm855_1_div_clk1", "pm855_1_div_clk2"; +}; + +pm855_1_rtc: &pm855_rtc { +}; + +pm855_1_gpios: &pm855_gpios { + interrupts = <0x0 0xc0 0 IRQ_TYPE_NONE>, + <0x0 0xc2 0 IRQ_TYPE_NONE>, + <0x0 0xc3 0 IRQ_TYPE_NONE>, + <0x0 0xc5 0 IRQ_TYPE_NONE>, + <0x0 0xc8 0 IRQ_TYPE_NONE>, + <0x0 0xc9 0 IRQ_TYPE_NONE>; + interrupt-names = "pm855_1_gpio1", "pm855_1_gpio3", + "pm855_1_gpio4", "pm855_1_gpio6", + "pm855_1_gpio9", "pm855_1_gpio10"; + qcom,gpios-disallowed = <2 5 7 8>; +}; + +/* PM855_2: */ +&spmi_bus { + qcom,pm855@4 { + compatible = "qcom,spmi-pmic"; + reg = <0x4 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + + qcom,power-on@800 { + compatible = "qcom,qpnp-power-on"; + reg = <0x800 0x100>; + }; + + pm855_2_clkdiv: clock-controller@5b00 { + compatible = "qcom,spmi-clkdiv"; + reg = <0x5b00 0x200>; + #clock-cells = <1>; + qcom,num-clkdivs = <2>; + clock-output-names = "pm855_2_div_clk1", + "pm855_2_div_clk2"; + clocks = <&clock_rpmh RPMH_CXO_CLK>; + clock-names = "xo"; + }; + + pm855_2_gpios: pinctrl@c000 { + compatible = "qcom,spmi-gpio"; + reg = <0xc000 0xa00>; + interrupts = <0x4 0xc0 0 IRQ_TYPE_NONE>, + <0x4 0xc2 0 IRQ_TYPE_NONE>, + <0x4 0xc3 0 IRQ_TYPE_NONE>, + <0x4 0xc5 0 IRQ_TYPE_NONE>, + <0x4 0xc7 0 IRQ_TYPE_NONE>, + <0x4 0xc8 0 IRQ_TYPE_NONE>, + <0x4 0xc9 0 IRQ_TYPE_NONE>; + interrupt-names = "pm855_2_gpio1", "pm855_2_gpio3", + "pm855_2_gpio4", "pm855_2_gpio6", + "pm855_2_gpio8", "pm855_2_gpio9", + "pm855_2_gpio10"; + gpio-controller; + #gpio-cells = <2>; + qcom,gpios-disallowed = <2 5 7>; + }; + }; + + qcom,pm855@5 { + compatible ="qcom,spmi-pmic"; + reg = <0x5 SPMI_USID>; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +/* PMIC GPIO pin control configurations: */ +&pm855_1_gpios { + key_home { + key_home_default: key_home_default { + pins = "gpio1"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; + + storage_sd_detect { + storage_cd_default: storage_cd_default { + pins = "gpio4"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; + }; + + key_vol_up { + key_vol_up_default: key_vol_up_default { + pins = "gpio6"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <1>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-auto-regulator.dtsi b/arch/arm64/boot/dts/qcom/sm8150-auto-regulator.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..c9394a4100e353818fe29991d3c8b18f865e2271 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-auto-regulator.dtsi @@ -0,0 +1,707 @@ +/* Copyright (c) 2018, 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 + +&soc { + /* RPMh regulators: */ + + rpmh-regulator-smpa4 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "smpa4"; + S4A: pm855_1_s4: regulator-pm855-1-s4 { + regulator-name = "pm855_1_s4"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + }; + }; + + rpmh-regulator-smpa5 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "smpa5"; + S5A: pm855_1_s5: regulator-pm855-1-s5 { + regulator-name = "pm855_1_s5"; + qcom,set = ; + regulator-min-microvolt = <1824000>; + regulator-max-microvolt = <2040000>; + qcom,init-voltage = <1824000>; + }; + }; + + rpmh-regulator-smpa6 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "smpa6"; + S6A: pm855_1_s6: regulator-pm855-1-s6 { + regulator-name = "pm855_1_s6"; + qcom,set = ; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1128000>; + qcom,init-voltage = <600000>; + }; + }; + + rpmh-regulator-smpa7 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "smpa7"; + S7A: pm855_1_s7: regulator-pm855-1-s7 { + regulator-name = "pm855_1_s7"; + qcom,set = ; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <600000>; + qcom,init-voltage = <600000>; + }; + }; + + /* PM855_1 S8 = VDD_MODEM supply */ + rpmh-regulator-msslvl { + compatible = "qcom,rpmh-arc-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "mss.lvl"; + S1A_LEVEL: pm855_1_s8_level: regulator-pm855-1-s8-level { + regulator-name = "pm855_1_s8_level"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + }; + }; + + rpmh-regulator-ldoa1 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa1"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L1A: pm855_1_l1: regulator-pm855-1-l1 { + regulator-name = "pm855_1_l1"; + qcom,set = ; + regulator-min-microvolt = <752000>; + regulator-max-microvolt = <752000>; + qcom,init-voltage = <752000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa2 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa2"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + L2A: pm855_1_l2: regulator-pm855-1-l2 { + regulator-name = "pm855_1_l2"; + qcom,set = ; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + qcom,init-voltage = <3072000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa3 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa3"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L3A: pm855_1_l3: regulator-pm855-1-l3 { + regulator-name = "pm855_1_l3"; + qcom,set = ; + regulator-min-microvolt = <480000>; + regulator-max-microvolt = <932000>; + qcom,init-voltage = <480000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa5 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa5"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + + L5A: pm855_1_l5: regulator-pm855-1-l5 { + regulator-name = "pm855_1_l5"; + qcom,set = ; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + qcom,init-voltage = <880000>; + qcom,init-mode = ; + }; + + L5A_AO: pm855_1_l5_ao: regulator-pm855-1-l5-ao { + regulator-name = "pm855_1_l5_ao"; + qcom,set = ; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + qcom,init-voltage = <880000>; + qcom,init-mode = ; + }; + + regulator-pm855-1-l5-so { + regulator-name = "pm855_1_l5_so"; + qcom,set = ; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + qcom,init-voltage = <880000>; + qcom,init-mode = ; + qcom,init-enable = <0>; + }; + }; + + rpmh-regulator-ldoa6 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa6"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + L6A: pm855_1_l6: regulator-pm855-1-l6 { + regulator-name = "pm855_1_l6"; + qcom,set = ; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,init-voltage = <1200000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa7 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa7"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L7A: pm855_1_l7: regulator-pm855-1-l7 { + regulator-name = "pm855_1_l7"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa10 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa10"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 10000>; + L10A: pm855_1_l10: regulator-pm855-1-l10 { + regulator-name = "pm855_1_l10"; + qcom,set = ; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <2504000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa11 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa11"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L11A: pm855_1_l11: regulator-pm855-1-l11 { + regulator-name = "pm855_1_l11"; + qcom,set = ; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + qcom,init-voltage = <800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa12 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa12"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L12A: pm855_1_l12: regulator-pm855-1-l12 { + regulator-name = "pm855_1_l12"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + + L12A_AO: pm855_1_l12_ao: regulator-pm855-1-l12-ao { + regulator-name = "pm855_1_l12_ao"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + + regulator-pm855-1-l12-so { + regulator-name = "pm855_1_l12_so"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + qcom,init-enable = <0>; + }; + }; + + rpmh-regulator-ldoa13 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa13"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L13A: pm855_1_l13: regulator-pm855-1-l13 { + regulator-name = "pm855_1_l13"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2704000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa15 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa15"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L15A: pm855_1_l15: regulator-pm855-1-l15 { + regulator-name = "pm855_1_l15"; + qcom,set = ; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <1704000>; + qcom,init-voltage = <1704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa16 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa16"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L16A: pm855_1_l16: regulator-pm855-1-l16 { + regulator-name = "pm855_1_l16"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoa17 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoa17"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L17A: pm855_1_l17: regulator-pm855-1-l17 { + regulator-name = "pm855_1_l17"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + /* PM855_2 S3 + S2 + S1 = 3 phase VDD_GFX supply */ + rpmh-regulator-gfxlvl { + compatible = "qcom,rpmh-arc-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "gfx.lvl"; + S3C_LEVEL: pm855_2_s3_level: regulator-pm855-2-s3-level { + regulator-name = "pm855_2_s3_level"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + }; + }; + + rpmh-regulator-smpc4 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "smpc4"; + S4C: pm855_2_s4: regulator-pm855-2-s4 { + regulator-name = "pm855_2_s4"; + qcom,set = ; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1400000>; + qcom,init-voltage = <1200000>; + }; + }; + + rpmh-regulator-smpc5 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "smpc5"; + S5C: pm855_2_s5: regulator-pm855-2-s5 { + regulator-name = "pm855_2_s5"; + qcom,set = ; + regulator-min-microvolt = <1824000>; + regulator-max-microvolt = <2040000>; + qcom,init-voltage = <1824000>; + }; + }; + + rpmh-regulator-smpc6 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "smpc6"; + S6C: pm855_2_s6: regulator-pm855-2-s6 { + regulator-name = "pm855_2_s6"; + qcom,set = ; + regulator-min-microvolt = <1128000>; + regulator-max-microvolt = <1128000>; + qcom,init-voltage = <1128000>; + }; + }; + + /* PM855_2 S9 + S8 + S7 = VDD_CX supply */ + rpmh-regulator-cxlvl { + compatible = "qcom,rpmh-arc-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "cx.lvl"; + pm855_2_s9_level-parent-supply = <&VDD_MX_LEVEL>; + pm855_2_s9_level_ao-parent-supply = <&VDD_MX_LEVEL_AO>; + + VDD_CX_LEVEL: VDD_MMCX_LEVEL: + S9C_LEVEL: pm855_2_s9_level: regulator-pm855-2-s9-level { + regulator-name = "pm855_2_s9_level"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + qcom,min-dropout-voltage-level = <(-1)>; + }; + + VDD_CX_LEVEL_AO: VDD_MMCX_LEVEL_AO: S9C_LEVEL_AO: + pm855_2_s9_level_ao: regulator-pm855-2-s9-level-ao { + regulator-name = "pm855_2_s9_level_ao"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + qcom,min-dropout-voltage-level = <(-1)>; + }; + + cx_cdev: regulator-cdev { + compatible = "qcom,rpmh-reg-cdev"; + mboxes = <&qmp_aop 0>; + qcom,reg-resource-name = "cx"; + #cooling-cells = <2>; + }; + }; + + /* PM855_2 S10 = VDD_MX supply */ + rpmh-regulator-mxlvl { + compatible = "qcom,rpmh-arc-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "mx.lvl"; + + VDD_MX_LEVEL: + S10C_LEVEL: pm855_2_s10_level: regulator-pm855-2-s10-level { + regulator-name = "pm855_2_s10_level"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + }; + + VDD_MX_LEVEL_AO: S10C_LEVEL_AO: + pm855_2_s10_level_ao: regulator-pm855-2-s10-level-ao { + regulator-name = "pm855_2_s10_level_ao"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + }; + + mx_cdev: mx-cdev-lvl { + compatible = "qcom,regulator-cooling-device"; + regulator-cdev-supply = <&VDD_MX_LEVEL>; + regulator-levels = ; + #cooling-cells = <2>; + }; + }; + + rpmh-regulator-ldoc1 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc1"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L1C: pm855_2_l1: regulator-pm855-2-l1 { + regulator-name = "pm855_2_l1"; + qcom,set = ; + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1304000>; + qcom,init-voltage = <1304000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoc2 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc2"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L2C: pm855_2_l2: regulator-pm855-2-l2 { + regulator-name = "pm855_2_l2"; + qcom,set = ; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <2928000>; + qcom,init-voltage = <1704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoc5 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc5"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L5C: pm855_2_l5: regulator-pm855-2-l5 { + regulator-name = "pm855_2_l5"; + qcom,set = ; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,init-voltage = <1200000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoc7 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc7"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L7C: pm855_2_l7: regulator-pm855-2-l7 { + regulator-name = "pm855_2_l7"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoc8 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc8"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L8C: pm855_2_l8: regulator-pm855-2-l8 { + regulator-name = "pm855_2_l8"; + qcom,set = ; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,init-voltage = <1200000>; + qcom,init-mode = ; + }; + }; + + /* PM855_2 L11 = VDD_EBI supply */ + rpmh-regulator-ebilvl { + compatible = "qcom,rpmh-arc-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ebi.lvl"; + L11C_LEVEL: pm855_2_l11_level: regulator-pm855-2-l11-level { + regulator-name = "pm855_2_l11_level"; + qcom,set = ; + regulator-min-microvolt + = ; + regulator-max-microvolt + = ; + qcom,init-voltage-level + = ; + }; + + ebi_cdev: regulator-cdev { + compatible = "qcom,rpmh-reg-cdev"; + mboxes = <&qmp_aop 0>; + qcom,reg-resource-name = "ebi"; + #cooling-cells = <2>; + }; + }; + + rpmh-regulator-ldoc12 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc12"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L12C: pm855_2_l12: regulator-pm855-2-l12 { + regulator-name = "pm855_2_l12"; + qcom,set = ; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <1888000>; + qcom,init-voltage = <1704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoc13 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc13"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L13C: pm855_2_l13: regulator-pm855-2-l13 { + regulator-name = "pm855_2_l13"; + qcom,set = ; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2960000>; + qcom,init-voltage = <1800000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoc16 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc16"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + L16C: pm855_2_l16: regulator-pm855-2-l16 { + regulator-name = "pm855_2_l16"; + qcom,set = ; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <3008000>; + qcom,init-voltage = <2704000>; + qcom,init-mode = ; + }; + }; + + rpmh-regulator-ldoc18 { + compatible = "qcom,rpmh-vrm-regulator"; + mboxes = <&apps_rsc 0>; + qcom,resource-name = "ldoc18"; + qcom,regulator-type = "pmic5-ldo"; + qcom,supported-modes = + ; + qcom,mode-threshold-currents = <0 1>; + proxy-supply = <&pm855_2_l18>; + L18C: pm855_2_l18: regulator-pm855-2-l18 { + regulator-name = "pm855_2_l18"; + qcom,set = ; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <23800>; + qcom,init-voltage = <880000>; + qcom,init-mode = ; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-auto.dts b/arch/arm64/boot/dts/qcom/sm8150-auto.dts new file mode 100644 index 0000000000000000000000000000000000000000..7dcd050726bbb1831165ece728f45606217cc2c9 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-auto.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150 AUTO SoC"; + compatible = "qcom,sm8150"; + qcom,pmic-name = "PM855"; + qcom,board-id = <0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-auto.dtsi b/arch/arm64/boot/dts/qcom/sm8150-auto.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..51d91e8ee294d862e339fc93fae3cd16b25a9af2 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-auto.dtsi @@ -0,0 +1,309 @@ +/* Copyright (c) 2018, 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 "sm8150.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150 AUTO"; + qcom,msm-name = "SM8150 AUTO"; + qcom,msm-id = <362 0x10000>; +}; + +/* Remove regulator nodes specific to SM8150 */ +&soc { + /delete-node/ regulator-pm855-s4; + /delete-node/ rpmh-regulator-msslvl; + /delete-node/ rpmh-regulator-smpa2; + /delete-node/ rpmh-regulator-ebilvl; + /delete-node/ rpmh-regulator-smpa5; + /delete-node/ rpmh-regulator-smpa6; + /delete-node/ rpmh-regulator-ldoa1; + /delete-node/ rpmh-regulator-ldoa2; + /delete-node/ rpmh-regulator-ldoa3; + /delete-node/ rpmh-regulator-lmxlvl; + /delete-node/ rpmh-regulator-ldoa5; + /delete-node/ rpmh-regulator-ldoa6; + /delete-node/ rpmh-regulator-ldoa7; + /delete-node/ rpmh-regulator-lcxlvl; + /delete-node/ rpmh-regulator-ldoa9; + /delete-node/ rpmh-regulator-ldoa10; + /delete-node/ rpmh-regulator-ldoa11; + /delete-node/ rpmh-regulator-ldoa12; + /delete-node/ rpmh-regulator-ldoa13; + /delete-node/ rpmh-regulator-ldoa14; + /delete-node/ rpmh-regulator-ldoa15; + /delete-node/ rpmh-regulator-ldoa16; + /delete-node/ rpmh-regulator-ldoa17; + /delete-node/ rpmh-regulator-smpc1; + /delete-node/ rpmh-regulator-gfxlvl; + /delete-node/ rpmh-regulator-mxlvl; + /delete-node/ rpmh-regulator-mmcxlvl; + /delete-node/ rpmh-regulator-cxlvl; + /delete-node/ rpmh-regulator-smpc8; + /delete-node/ rpmh-regulator-ldoc1; + /delete-node/ rpmh-regulator-ldoc2; + /delete-node/ rpmh-regulator-ldoc3; + /delete-node/ rpmh-regulator-ldoc4; + /delete-node/ rpmh-regulator-ldoc5; + /delete-node/ rpmh-regulator-ldoc6; + /delete-node/ rpmh-regulator-ldoc7; + /delete-node/ rpmh-regulator-ldoc8; + /delete-node/ rpmh-regulator-ldoc9; + /delete-node/ rpmh-regulator-ldoc10; + /delete-node/ rpmh-regulator-ldoc11; + /delete-node/ rpmh-regulator-bobc1; + /delete-node/ rpmh-regulator-smpf2; + /delete-node/ rpmh-regulator-ldof2; + /delete-node/ rpmh-regulator-ldof5; + /delete-node/ rpmh-regulator-ldof6; +}; + +/* Add regulator nodes specific to SM8150 Auto */ +#include "sm8150-auto-regulator.dtsi" + +&cam_csiphy0 { + mipi-csi-vdd-supply = <&pm855_2_l8>; +}; + +&cam_csiphy1 { + mipi-csi-vdd-supply = <&pm855_2_l8>; +}; + +&cam_csiphy2 { + mipi-csi-vdd-supply = <&pm855_2_l8>; +}; + +&cam_csiphy3 { + mipi-csi-vdd-supply = <&pm855_2_l8>; +}; + +&pcie0 { + vreg-1.8-supply = <&pm855_2_l8>; + vreg-0.9-supply = <&pm855_2_l18>; +}; + +&pcie1 { + vreg-1.8-supply = <&pm855_2_l8>; + vreg-0.9-supply = <&pm855_2_l18>; +}; + +&mdss_dsi_phy0 { + vdda-0p9-supply = <&pm855_2_l18>; +}; + +&mdss_dsi_phy1 { + vdda-0p9-supply = <&pm855_2_l18>; +}; + +&mdss_dsi0 { + vdda-1p2-supply = <&pm855_2_l8>; +}; + +&mdss_dsi1 { + vdda-1p2-supply = <&pm855_2_l8>; +}; + +&sde_dp { + vdda-1p2-supply = <&pm855_2_l8>; + vdda-0p9-supply = <&pm855_2_l18>; +}; + +&lmh_dcvs1 { + isens_vref_0p8-supply = <&pm855_1_l5_ao>; + isens_vref_1p8-supply = <&pm855_1_l12_ao>; +}; + +&usb2_phy0 { + vdd-supply = <&pm855_1_l5>; + vdda18-supply = <&pm855_1_l12>; + vdda33-supply = <&pm855_1_l2>; +}; + +&usb_qmp_dp_phy { + vdd-supply = <&pm855_1_l5>; + core-supply = <&pm855_2_l8>; +}; + +&icnss { + vdd-cx-mx-supply = <&pm855_1_l1>; + vdd-1.8-xo-supply = <&pm855_1_l7>; + vdd-1.3-rfa-supply = <&pm855_2_l1>; + /delete-property/ vdd-3.3-ch0-supply; +}; + +&pil_ssc { + vdd_cx-supply = <&VDD_CX_LEVEL>; + vdd_mx-supply = <&VDD_MX_LEVEL>; +}; + +&pil_modem { + vdd_mss-supply = <&pm855_1_s8_level>; +}; + +&wil6210 { + /delete-property/ vddio-supply; +}; + +&gpu_gx_gdsc { + parent-supply = <&pm855_2_s3_level>; + vdd_parent-supply = <&pm855_2_s3_level>; +}; + +&thermal_zones { + aoss0-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-0-0-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-0-1-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-0-2-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-0-3-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpuss-0-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpuss-1-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-0-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-1-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-2-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-3-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-4-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-5-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-6-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cpu-1-7-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + gpuss-0-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + aoss-1-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cwlan-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + video-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + ddr-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + q6-hvx-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + camera-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + cmpss-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + mdm-core-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + npu-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + mdm-vec-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + mdm-scl-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; + gpuss-1-lowf { + cooling-maps { + /delete-node/ mmcx_vdd_cdev; + }; + }; +}; + +&usb0 { + dwc3@a600000 { + usb-phy = <&usb2_phy0>, <&usb_nop_phy>; + maximum-speed = "high-speed"; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-bus.dtsi b/arch/arm64/boot/dts/qcom/sm8150-bus.dtsi index fe60b85216f19863ea4e82b9f7efdac34f5efa51..1c8943d80814335e5a689508caf19bb55430b5c5 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-bus.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-bus.dtsi @@ -49,7 +49,7 @@ cell-id = ; label = "disp_rsc"; qcom,rsc-dev; - qcom,req_state = <3>; + qcom,req_state = <2>; }; /*BCMs*/ @@ -1327,6 +1327,15 @@ qcom,forwarding; }; + mas_alc: mas-alc { + cell-id = ; + label = "mas-alc"; + qcom,buswidth = <1>; + qcom,agg-ports = <1>; + qcom,bus-dev = <&fab_mc_virt>; + qcom,bcms = <&bcm_alc>; + }; + mas_qnm_mnoc_hf_display: mas-qnm-mnoc-hf_display { cell-id = ; label = "mas-qnm-mnoc-hf_display"; diff --git a/arch/arm64/boot/dts/qcom/sm8150-camera-sensor-qrd.dtsi b/arch/arm64/boot/dts/qcom/sm8150-camera-sensor-qrd.dtsi index 22ae92b0744263c7460f78bcd971fc7c1e4c90b3..88a22ce3ed9227c4a3df2ea158e95b25a76b7594 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-camera-sensor-qrd.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-camera-sensor-qrd.dtsi @@ -58,8 +58,8 @@ cam_vaf-supply = <&pm8009_s2>; regulator-names = "cam_vaf"; rgltr-cntrl-support; - rgltr-min-voltage = <2850000>; - rgltr-max-voltage = <2850000>; + rgltr-min-voltage = <2856000>; + rgltr-max-voltage = <2856000>; rgltr-load-current = <0>; }; @@ -157,8 +157,8 @@ regulator-names = "cam_vio", "cam_vana", "cam_vdig", "cam_clk", "cam_vaf"; rgltr-cntrl-support; - rgltr-min-voltage = <0 2850000 1200000 0 2856000>; - rgltr-max-voltage = <0 2850000 1200000 0 2856000>; + rgltr-min-voltage = <0 2856000 1200000 0 2856000>; + rgltr-max-voltage = <0 2856000 1200000 0 2856000>; rgltr-load-current = <0 80000 1200000 0 0>; gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; @@ -196,8 +196,8 @@ regulator-names = "cam_vio", "cam_vana", "cam_vdig", "cam_clk", "cam_vaf"; rgltr-cntrl-support; - rgltr-min-voltage = <0 2850000 1200000 0 2856000>; - rgltr-max-voltage = <0 2850000 1200000 0 2856000>; + rgltr-min-voltage = <0 2856000 1200000 0 2856000>; + rgltr-max-voltage = <0 2856000 1200000 0 2856000>; rgltr-load-current = <0 80000 1200000 0 0>; gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; diff --git a/arch/arm64/boot/dts/qcom/sm8150-camera.dtsi b/arch/arm64/boot/dts/qcom/sm8150-camera.dtsi index 9414d473494aac38d7c877b70cb717afb984478e..5ee151c9dfdcc0395eb20b0a047552cb44baee89 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-camera.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-camera.dtsi @@ -992,7 +992,7 @@ <400000000 0 600000000 0>; clock-cntl-level = "svs", "turbo"; fw_name = "CAMERA_ICP.elf"; - ubwc-cfg = <0x7F 0x1FF>; + ubwc-cfg = <0x7B 0x1EF>; status = "ok"; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-cdp.dtsi b/arch/arm64/boot/dts/qcom/sm8150-cdp.dtsi index 55c7e9b917eb57ad152cef1e6506b717cb6fa8b0..b2c0206a693592ec2889efd21fc062550db38589 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-cdp.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-cdp.dtsi @@ -12,12 +12,12 @@ #include #include -#include #include "sm8150-pmic-overlay.dtsi" #include "sm8150-audio-overlay.dtsi" #include "sm8150-sde-display.dtsi" #include "sm8150-camera-sensor-cdp.dtsi" +#include "sm8150-thermal-overlay.dtsi" &qupv3_se12_2uart { status = "ok"; @@ -82,14 +82,18 @@ }; }; - sound-tavil { - qcom,model = "sm8150-tavil-cdp-snd-card"; - qcom,us-euro-gpios = <&tavil_us_euro_switch>; + qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clock-frequency = <25000000>; + qcom,ipc-gpio = <&tlmm 118 0>; + qcom,finger-detect-gpio = <&pm855_gpios 1 0>; }; }; -&mdss_mdp { - #cooling-cells = <2>; +&snd_934x { + qcom,model = "sm8150-tavil-cdp-snd-card"; + qcom,us-euro-gpios = <&tavil_us_euro_switch>; }; &dsi_sharp_4k_dsc_cmd { @@ -114,6 +118,30 @@ qcom,platform-reset-gpio = <&tlmm 6 0>; }; +&dsi_sim_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_sim_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + &dsi_dual_nt35597_truly_video { qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; @@ -136,6 +164,18 @@ qcom,platform-reset-gpio = <&tlmm 6 0>; }; +&dsi_sim_dsc_375_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_dsc_375_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + &dsi_nt35597_truly_dsc_cmd { qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; @@ -212,6 +252,8 @@ vdda-pll-supply = <&pm855l_l3>; vdda-phy-max-microamp = <90200>; vdda-pll-max-microamp = <19000>; + + status = "ok"; }; &ufshc_mem { @@ -225,6 +267,8 @@ qcom,vddp-ref-clk-supply = <&pm855_l9>; qcom,vddp-ref-clk-max-microamp = <100>; + + status = "ok"; }; &sdhc_2 { @@ -256,6 +300,12 @@ &pm855b_charger { qcom,batteryless-platform; + io-channels = <&pm855b_vadc ADC_USB_IN_V_16>, + <&pm855b_vadc ADC_USB_IN_I>, + <&pm855b_vadc ADC_CHG_TEMP>; + io-channel-names = "usb_in_voltage", + "usb_in_current", + "chg_temp"; }; &pm855b_vadc { @@ -273,14 +323,6 @@ qcom,pre-scaling = <1 1>; }; - smb_therm { - reg = ; - label = "smb_therm"; - qcom,ratiometric; - qcom,hw-settle-time = <200>; - qcom,pre-scaling = <1 1>; - }; - vcoin { reg = ; label = "vcoin"; @@ -390,115 +432,118 @@ }; }; +&wil6210 { + status = "ok"; +}; + +&mhi_0 { + mhi,fw-name = "debug.mbn"; + status = "okay"; +}; + +&pm855b_adc_tm { + wp_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm855_adc_tm { + xo_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm855l_adc_tm { + camera_flash_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + skin_msm_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa_therm2 { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + &thermal_zones { - pm855b_tz { - cooling-maps { - trip0_bat { - trip = <&pm855b_trip0>; - cooling-device = - <&pm855b_charger (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip1_bat { - trip = <&pm855b_trip1>; - cooling-device = - <&pm855b_charger THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + wp-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; }; }; - pm855_tz { - cooling-maps { - trip0_cpu0 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU0 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu1 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU1 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu2 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU2 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu3 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU3 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu4 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU4 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu5 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU5 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu6 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU6 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu7 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU7 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip1_cpu1 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU1 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu2 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU2 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + xo-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855_adc_tm ADC_XO_THERM_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip1_cpu3 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU3 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu4 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU4 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu5 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU5 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + camera-flash-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM1_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip1_cpu6 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU6 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + skin-msm-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip1_cpu7 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU7 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + pa-therm2 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM3_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-coresight.dtsi b/arch/arm64/boot/dts/qcom/sm8150-coresight.dtsi index 07ab05a24ae08f99d5a7d53eb559ceeff8425c8f..5da67a724097827ff04a75642d9360d74c447f30 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-coresight.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-coresight.dtsi @@ -197,8 +197,15 @@ <&tmc_etf_swao_in_funnel_swao>; }; }; - port@1 { + reg = <5>; + funnel_swao_in_funnel_ssc: endpoint { + slave-mode; + remote-endpoint= + <&funnel_ssc_out_funnel_swao>; + }; + }; + port@2 { reg = <6>; funnel_swao_in_replicator1_out: endpoint { slave-mode; @@ -206,7 +213,7 @@ <&replicator1_out_funnel_swao>; }; }; - port@2 { + port@3 { reg = <7>; funnel_swao_in_tpda_swao: endpoint { slave-mode; @@ -523,6 +530,15 @@ }; port@1 { + reg = <1>; + funnel_in0_in_funnel_spss: endpoint { + slave-mode; + remote-endpoint = + <&funnel_spss_out_funnel_in0>; + }; + }; + + port@2 { reg = <6>; funnel_in0_in_funnel_qatb: endpoint { slave-mode; @@ -531,7 +547,7 @@ }; }; - port@2 { + port@3 { reg = <7>; funnel_in0_in_stm: endpoint { slave-mode; @@ -565,6 +581,14 @@ }; }; port@1 { + reg = <2>; + funnel_in1_in_funnel_dl_south: endpoint { + slave-mode; + remote-endpoint = + <&funnel_dl_south_out_funnel_in1>; + }; + }; + port@2 { reg = <3>; funnel_in1_in_modem_etm0: endpoint { slave-mode; @@ -572,7 +596,7 @@ <&modem_etm0_out_funnel_in1>; }; }; - port@2 { + port@3 { reg = <4>; funnel_in1_in_replicator_swao: endpoint { slave-mode; @@ -580,7 +604,7 @@ <&replicator_swao_out_funnel_in1>; }; }; - port@3 { + port@4 { reg = <6>; funnel_in1_in_funnel_dl_north: endpoint { slave-mode; @@ -738,6 +762,15 @@ }; port@8 { + reg = <10>; + tpda_in_tpdm_prng: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_prng_out_tpda>; + }; + }; + + port@9 { reg = <13>; tpda_in_tpdm_pimem: endpoint { slave-mode; @@ -746,7 +779,7 @@ }; }; - port@9 { + port@10 { reg = <14>; tpda_in_tpdm_npu: endpoint { slave-mode; @@ -851,8 +884,9 @@ compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b908>; - reg = <0x6846000 0x1000>; - reg-names = "funnel-base"; + reg = <0x6867020 0x10>, + <0x6846000 0x1000>; + reg-names = "funnel-base-dummy", "funnel-base-real"; coresight-name = "coresight-funnel-lpass-1"; @@ -981,6 +1015,58 @@ }; }; + funnel_dl_south: funnel@69c2000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + reg = <0x69c2000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-dl-south"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + funnel_dl_south_out_funnel_in1: endpoint { + remote-endpoint = + <&funnel_in1_in_funnel_dl_south>; + }; + }; + + port@1 { + reg = <0>; + funnel_dl_south_in_tpdm_dl_south: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_dl_south_out_funnel_dl_south>; + }; + }; + }; + }; + + tpdm_dl_south: tpdm@69c0000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x69c0000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-dl-south"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_dl_south_out_funnel_dl_south: endpoint { + remote-endpoint = + <&funnel_dl_south_in_tpdm_dl_south>; + }; + }; + }; + funnel_dl_north: funnel@6ac2000 { compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b908>; @@ -1016,6 +1102,113 @@ }; }; + tpdm_prng: tpdm@684c000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x684c000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-prng"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_prng_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_prng>; + }; + }; + }; + + funnel_spss: funnel@6883000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6883000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-spss"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_spss_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_funnel_spss>; + }; + }; + + port@1 { + reg = <0>; + funnel_spss_in_tpda_spss: endpoint { + slave-mode; + remote-endpoint = + <&tpda_spss_out_funnel_spss>; + }; + }; + }; + }; + + tpda_spss: tpda@6882000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b969>; + reg = <0x06882000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda-spss"; + + qcom,tpda-atid = <70>; + qcom,cmb-elem-size = <0 32>; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_spss_out_funnel_spss: endpoint { + remote-endpoint = + <&funnel_spss_in_tpda_spss>; + }; + }; + + port@1 { + reg = <0>; + tpda_spss_in_tpdm_spss: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_spss_out_tpda_spss>; + }; + }; + }; + }; + + tpdm_spss: tpdm@6880000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b968>; + reg = <0x6880000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-spss"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + port { + tpdm_spss_out_tpda_spss: endpoint { + remote-endpoint = <&tpda_spss_in_tpdm_spss>; + }; + }; + }; + tpdm_qm: tpdm@69d0000 { compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b968>; @@ -1233,18 +1426,21 @@ }; }; - funnel_dl_mm1: funnel@6c0b000 { + funnel_dl_mm1: funnel_1@6c0b000 { compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b908>; - reg = <0x6c0b000 0x1000>; - reg-names = "funnel-base"; + reg = <0x6867000 0x10>, + <0x6c0b000 0x1000>; + reg-names = "funnel-base-dummy", "funnel-base-real"; coresight-name = "coresight-funnel-dl-mm1"; clocks = <&clock_aop QDSS_CLK>; clock-names = "apb_pclk"; + qcom,duplicate-funnel; + ports { #address-cells = <1>; #size-cells = <0>; @@ -1327,8 +1523,9 @@ compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b908>; - reg = <0x6861000 0x1000>; - reg-names = "funnel-base"; + reg = <0x6867010 0x10>, + <0x6861000 0x1000>; + reg-names = "funnel-base-dummy", "funnel-base-real"; coresight-name = "coresight-funnel-turing-1"; @@ -1771,6 +1968,9 @@ clocks = <&clock_aop QDSS_CLK>; clock-names = "apb_pclk"; + qcom,cti-gpio-trigout = <4>; + pinctrl-names = "cti-trigout-pctrl"; + pinctrl-0 = <&trigout_a>; }; cti3: cti@6013000 { @@ -2132,6 +2332,20 @@ }; }; + ssc_etm0 { + compatible = "qcom,coresight-remote-etm"; + + coresight-name = "coresight-ssc-etm0"; + qcom,inst-id = <8>; + + port { + ssc_etm0_out_funnel_ssc: endpoint { + remote-endpoint = + <&funnel_ssc_in_ssc_etm0>; + }; + }; + }; + funnel_apss_merg: funnel@7810000 { compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b908>; @@ -2452,4 +2666,39 @@ }; }; }; + + funnel_ssc: funnel@6b14000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6b14000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-ssc"; + + clocks = <&clock_aop QDSS_CLK>; + clock-names = "apb_pclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_ssc_out_funnel_swao: endpoint { + remote-endpoint = + <&funnel_swao_in_funnel_ssc>; + }; + }; + + port@1 { + reg = <0>; + funnel_ssc_in_ssc_etm0: endpoint { + slave-mode; + remote-endpoint = + <&ssc_etm0_out_funnel_ssc>; + }; + }; + }; + }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-gdsc.dtsi b/arch/arm64/boot/dts/qcom/sm8150-gdsc.dtsi index b86390bfd5745abb685ab0636c2eb10f7051e79c..f83eb2a10064e22255c3ec9825a414393238f9b8 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-gdsc.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-gdsc.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 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 @@ -193,6 +193,8 @@ reg = <0xaf03000 0x4>; qcom,support-hw-trigger; status = "disabled"; + proxy-supply = <&mdss_core_gdsc>; + qcom,proxy-consumer-enable; }; /* GDSCs in Graphics CC */ diff --git a/arch/arm64/boot/dts/qcom/sm8150-gpu.dtsi b/arch/arm64/boot/dts/qcom/sm8150-gpu.dtsi index d4e541997c65f542afe80fa3bfd79a34362a7b01..eafe57aa26419f6ba54356761e28b2db8ef47643 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-gpu.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-gpu.dtsi @@ -26,30 +26,12 @@ compatible = "qcom,devbw"; governor = "bw_vbif"; qcom,src-dst-ports = <26 512>; - qcom,bw-tbl = - < 0 /* off */ >, - < 381 /* 100 MHz */ >, - < 572 /* 150 MHz */ >, - < 762 /* 200 MHz */ >, - < 1144 /* 300 MHz */ >, - < 1571 /* 412 MHz */ >, - < 2086 /* 547 MHz */ >, - < 2597 /* 681 MHz */ >, - < 2929 /* 768 MHz */ >, - < 3879 /* 1017 MHz */ >, - < 4943 /* 1296 MHz */ >, - < 5931 /* 1555 MHz */ >, - < 6881 /* 1804 MHz */ >; + operating-points-v2 = <&suspendable_ddr_bw_opp_table>; }; gpu_opp_table: gpu-opp-table { compatible = "operating-points-v2"; - opp-620000000 { - opp-hz = /bits/ 64 <620000000>; - opp-microvolt = ; - }; - opp-600000000 { opp-hz = /bits/ 64 <600000000>; opp-microvolt = ; @@ -204,23 +186,14 @@ qcom,gpu-pwrlevel@0 { reg = <0>; - qcom,gpu-freq = <620000000>; - qcom,bus-freq = <12>; - qcom,bus-min = <11>; - qcom,bus-max = <12>; - }; - - - qcom,gpu-pwrlevel@1 { - reg = <1>; qcom,gpu-freq = <600000000>; qcom,bus-freq = <12>; qcom,bus-min = <10>; qcom,bus-max = <12>; }; - qcom,gpu-pwrlevel@2 { - reg = <2>; + qcom,gpu-pwrlevel@1 { + reg = <1>; qcom,gpu-freq = <553850000>; qcom,bus-freq = <10>; qcom,bus-min = <9>; @@ -228,16 +201,16 @@ }; - qcom,gpu-pwrlevel@3 { - reg = <3>; + qcom,gpu-pwrlevel@2 { + reg = <2>; qcom,gpu-freq = <486460000>; qcom,bus-freq = <9>; qcom,bus-min = <8>; qcom,bus-max = <10>; }; - qcom,gpu-pwrlevel@4 { - reg = <4>; + qcom,gpu-pwrlevel@3 { + reg = <3>; qcom,gpu-freq = <379650000>; qcom,bus-freq = <8>; qcom,bus-min = <7>; @@ -245,24 +218,24 @@ }; - qcom,gpu-pwrlevel@5 { - reg = <5>; + qcom,gpu-pwrlevel@4 { + reg = <4>; qcom,gpu-freq = <309110000>; qcom,bus-freq = <5>; qcom,bus-min = <5>; qcom,bus-max = <7>; }; - qcom,gpu-pwrlevel@6 { - reg = <6>; + qcom,gpu-pwrlevel@5 { + reg = <5>; qcom,gpu-freq = <215000000>; qcom,bus-freq = <4>; qcom,bus-min = <3>; qcom,bus-max = <5>; }; - qcom,gpu-pwrlevel@7 { - reg = <7>; + qcom,gpu-pwrlevel@6 { + reg = <6>; qcom,gpu-freq = <0>; qcom,bus-freq = <0>; qcom,bus-min = <0>; @@ -286,7 +259,6 @@ clock-names = "iface_clk", "mem_clk", "mem_iface_clk"; qcom,secure_align_mask = <0xfff>; - qcom,global_pt; qcom,retention; qcom,hyp_secure_alloc; @@ -304,20 +276,6 @@ }; }; - gmu_opp_table: gmu-opp-table { - compatible = "operating-points-v2"; - - opp-200000000 { - opp-hz = /bits/ 64 <200000000>; - opp-microvolt = ; - }; - - opp-500000000 { - opp-hz = /bits/ 64 <500000000>; - opp-microvolt = ; - }; - }; - gmu: qcom,gmu@0x2C6A000 { label = "kgsl-gmu"; compatible = "qcom,gpu-gmu"; @@ -339,9 +297,6 @@ vddcx-supply = <&gpu_cx_gdsc>; vdd-supply = <&gpu_gx_gdsc>; - /* GMU OPP data */ - operating-points-v2 = <&gmu_opp_table>; - clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>, <&clock_gpucc GPU_CC_CXO_CLK>, <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>, @@ -367,11 +322,6 @@ reg = <1>; qcom,gmu-freq = <200000000>; }; - - qcom,gmu-pwrlevel@2 { - reg = <2>; - qcom,gmu-freq = <500000000>; - }; }; gmu_user: gmu_user { diff --git a/arch/arm64/boot/dts/qcom/sm8150-mtp.dtsi b/arch/arm64/boot/dts/qcom/sm8150-mtp.dtsi index 95806c047b4d9eab1b730598ac4da16924173309..df86deaa6f2d55fd4048d09d50a5241a9f3896da 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-mtp.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-mtp.dtsi @@ -12,12 +12,13 @@ #include #include -#include #include "sm8150-pmic-overlay.dtsi" #include "sm8150-audio-overlay.dtsi" #include "sm8150-sde-display.dtsi" #include "sm8150-camera-sensor-mtp.dtsi" +#include "sm8150-thermal-overlay.dtsi" +#include "smb1390.dtsi" &qupv3_se12_2uart { status = "ok"; @@ -86,6 +87,14 @@ linux,can-disable; }; }; + + qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clock-frequency = <25000000>; + qcom,ipc-gpio = <&tlmm 118 0>; + qcom,finger-detect-gpio = <&pm855_gpios 1 0>; + }; }; &snd_9360 { @@ -114,6 +123,30 @@ qcom,platform-reset-gpio = <&tlmm 6 0>; }; +&dsi_sim_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_sim_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + &dsi_dual_nt35597_truly_video { qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; @@ -136,6 +169,18 @@ qcom,platform-reset-gpio = <&tlmm 6 0>; }; +&dsi_sim_dsc_375_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + +&dsi_dual_sim_dsc_375_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,platform-reset-gpio = <&tlmm 6 0>; +}; + &dsi_nt35597_truly_dsc_cmd { qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; @@ -205,10 +250,6 @@ }; }; -&mdss_mdp { - #cooling-cells = <2>; -}; - &ufsphy_mem { compatible = "qcom,ufs-phy-qmp-v4"; @@ -216,6 +257,8 @@ vdda-pll-supply = <&pm855l_l3>; vdda-phy-max-microamp = <90200>; vdda-pll-max-microamp = <19000>; + + status = "ok"; }; &ufshc_mem { @@ -229,6 +272,8 @@ qcom,vddp-ref-clk-supply = <&pm855_l9>; qcom,vddp-ref-clk-max-microamp = <100>; + + status = "ok"; }; &pm855l_wled { @@ -277,14 +322,6 @@ qcom,pre-scaling = <1 1>; }; - smb_therm { - reg = ; - label = "smb_therm"; - qcom,ratiometric; - qcom,hw-settle-time = <200>; - qcom,pre-scaling = <1 1>; - }; - vcoin { reg = ; label = "vcoin"; @@ -394,116 +431,132 @@ }; }; +&wil6210 { + status = "ok"; +}; + +&mhi_0 { + mhi,fw-name = "debug.mbn"; + status = "okay"; +}; + +&pm855b_adc_tm { + wp_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm855_adc_tm { + xo_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm855l_adc_tm { + camera_flash_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + skin_msm_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa_therm2 { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + &thermal_zones { - pm855b_tz { - cooling-maps { - trip0_bat { - trip = <&pm855b_trip0>; - cooling-device = - <&pm855b_charger (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip1_bat { - trip = <&pm855b_trip1>; - cooling-device = - <&pm855b_charger THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + wp-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; }; }; - pm855_tz { - cooling-maps { - trip0_cpu0 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU0 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; + xo-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855_adc_tm ADC_XO_THERM_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip0_cpu1 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU1 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu2 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU2 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu3 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU3 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu4 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU4 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu5 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU5 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu6 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU6 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu7 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU7 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip1_cpu1 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU1 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu2 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU2 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu3 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU3 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu4 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU4 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu5 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU5 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + camera-flash-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM1_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip1_cpu6 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU6 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + skin-msm-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip1_cpu7 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU7 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + pa-therm2 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM3_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; }; }; }; + +&pm855b_charger { + io-channels = <&pm855b_vadc ADC_USB_IN_V_16>, + <&pm855b_vadc ADC_USB_IN_I>, + <&pm855b_vadc ADC_CHG_TEMP>; + io-channel-names = "usb_in_voltage", + "usb_in_current", + "chg_temp"; +}; + +&smb1390_charger { + status = "ok"; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-npu.dtsi b/arch/arm64/boot/dts/qcom/sm8150-npu.dtsi index a40378cd0569deaf712d59a92f246c51e077361b..9dc312e994e4dbd694954e5ace815414ec4729f5 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-npu.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-npu.dtsi @@ -6,28 +6,203 @@ * * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ &soc { - msm_npu: qcom,msm_npu { + msm_npu: qcom,msm_npu@9800000 { compatible = "qcom,msm-npu"; status = "ok"; reg = <0x9800000 0x800000>; reg-names = "npu_base"; - interrupts = <0 346 0>; - clocks = <&clock_npucc NPU_CC_XO_CLK>, - <&clock_npucc NPU_CC_NPU_CORE_CLK>, - <&clock_npucc NPU_CC_CAL_DP_CLK>, - <&clock_npucc NPU_CC_ARMWIC_CORE_CLK>, - <&clock_npucc NPU_CC_COMP_NOC_AXI_CLK>, - <&clock_npucc NPU_CC_CONF_NOC_AHB_CLK>; - clock-names = "xo", "core", "cal_dp", "armwic", - "axi", "ahb"; + interrupts = ; + iommus = <&apps_smmu 0x1461 0x0>, <&apps_smmu 0x2061 0x0>; + cache-slice-names = "npu"; + cache-slices = <&llcc 23>; + clocks = <&clock_npucc NPU_CC_CAL_DP_CLK>, + <&clock_npucc NPU_CC_CAL_DP_CLK_SRC>, + <&clock_npucc NPU_CC_XO_CLK>, + <&clock_npucc NPU_CC_ARMWIC_CORE_CLK>, + <&clock_npucc NPU_CC_BTO_CORE_CLK>, + <&clock_npucc NPU_CC_BWMON_CLK>, + <&clock_npucc NPU_CC_CAL_DP_CDC_CLK>, + <&clock_npucc NPU_CC_COMP_NOC_AXI_CLK>, + <&clock_npucc NPU_CC_CONF_NOC_AHB_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_APB_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_ATB_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_CLK>, + <&clock_npucc NPU_CC_NPU_CORE_CLK_SRC>, + <&clock_npucc NPU_CC_NPU_CORE_CTI_CLK>, + <&clock_npucc NPU_CC_NPU_CPC_CLK>, + <&clock_npucc NPU_CC_NPU_CPC_TIMER_CLK>, + <&clock_npucc NPU_CC_PERF_CNT_CLK>, + <&clock_npucc NPU_CC_QTIMER_CORE_CLK>, + <&clock_npucc NPU_CC_SLEEP_CLK>; + clock-names = "cal_dp_clk", + "cal_dp_clk_src", + "xo_clk", + "armwic_core_clk", + "bto_core_clk", + "bwmon_clk", + "cal_dp_cdc_clk", + "comp_noc_axi_clk", + "conf_noc_ahb_clk", + "npu_core_apb_clk", + "npu_core_atb_clk", + "npu_core_clk", + "npu_core_clk_src", + "npu_core_cti_clk", + "npu_cpc_clk", + "npu_cpc_timer_clk", + "perf_cnt_clk", + "qtimer_core_clk", + "sleep_clk"; vdd-supply = <&npu_core_gdsc>; vdd_cx-supply = <&VDD_CX_LEVEL>; qcom,proxy-reg-names ="vdd", "vdd_cx"; qcom,vdd_cx-uV-uA = ; + mboxes = <&qmp_npu0 0>, <&qmp_npu1 0>; + mbox-names = "npu_low", "npu_high"; + #cooling-cells = <2>; + qcom,npubw-dev = <&npu_npu_ddr_bw>; + qcom,npu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + compatible = "qcom,npu-pwrlevels"; + initial-pwrlevel = <4>; + qcom,npu-pwrlevel@0 { + reg = <0>; + clk-freq = <9600000 + 9600000 + 19200000 + 19200000 + 19200000 + 19200000 + 9600000 + 60000000 + 19200000 + 19200000 + 30000000 + 19200000 + 19200000 + 19200000 + 19200000 + 19200000 + 9600000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@1 { + reg = <1>; + clk-freq = <300000000 + 300000000 + 19200000 + 100000000 + 19200000 + 19200000 + 300000000 + 150000000 + 19200000 + 19200000 + 60000000 + 100000000 + 100000000 + 37500000 + 100000000 + 19200000 + 300000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@2 { + reg = <2>; + clk-freq = <350000000 + 350000000 + 19200000 + 150000000 + 19200000 + 19200000 + 350000000 + 200000000 + 37500000 + 19200000 + 120000000 + 150000000 + 150000000 + 75000000 + 150000000 + 19200000 + 350000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@3 { + reg = <3>; + clk-freq = <400000000 + 400000000 + 19200000 + 200000000 + 19200000 + 19200000 + 400000000 + 300000000 + 37500000 + 19200000 + 120000000 + 200000000 + 200000000 + 75000000 + 200000000 + 19200000 + 400000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@4 { + reg = <4>; + clk-freq = <600000000 + 600000000 + 19200000 + 300000000 + 19200000 + 19200000 + 600000000 + 403000000 + 75000000 + 19200000 + 240000000 + 300000000 + 300000000 + 150000000 + 300000000 + 19200000 + 600000000 + 19200000 + 0>; + }; + qcom,npu-pwrlevel@5 { + reg = <5>; + clk-freq = <715000000 + 715000000 + 19200000 + 350000000 + 19200000 + 19200000 + 715000000 + 533000000 + 75000000 + 19200000 + 240000000 + 350000000 + 350000000 + 150000000 + 350000000 + 19200000 + 715000000 + 19200000 + 0>; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-pcie.dtsi b/arch/arm64/boot/dts/qcom/sm8150-pcie.dtsi index 10e13adfb85007883b65767fa139a2a994a06474..fe51104a1b2557f31718ea7f80e801c51be10c54 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-pcie.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-pcie.dtsi @@ -90,8 +90,7 @@ "msi_24", "msi_25", "msi_26", "msi_27", "msi_28", "msi_29", "msi_30", "msi_31"; - qcom,phy-sequence = <0x02b4 0x03 0x0 - 0x0840 0x03 0x0 + qcom,phy-sequence = <0x0840 0x03 0x0 0x0094 0x08 0x0 0x0154 0x32 0x0 0x016c 0x08 0x0 @@ -155,12 +154,12 @@ 0x0588 0xe4 0x0 0x058c 0xec 0x0 0x0590 0x39 0x0 - 0x0594 0x36 0x0 - 0x0570 0xbd 0x0 - 0x0574 0xbd 0x0 - 0x0578 0x7f 0x0 - 0x057c 0xfb 0x0 - 0x0580 0x53 0x0 + 0x0594 0x37 0x0 + 0x0570 0x7f 0x0 + 0x0574 0xff 0x0 + 0x0578 0xff 0x0 + 0x057c 0xdb 0x0 + 0x0580 0x76 0x0 0x04fc 0x00 0x0 0x04f8 0xc0 0x0 0x0414 0x04 0x0 @@ -174,6 +173,19 @@ 0x0988 0x66 0x0 0x0998 0x08 0x0 0x08dc 0x0d 0x0 + 0x04f0 0x6a 0x0 + 0x04f4 0x07 0x0 + 0x04b4 0x02 0x0 + 0x04b8 0x02 0x0 + 0x04bc 0xaa 0x0 + 0x04d4 0x54 0x0 + 0x04d8 0x37 0x0 + 0x0460 0xa0 0x0 + 0x05c4 0x0c 0x0 + 0x0464 0xc0 0x0 + 0x04c0 0x0f 0x0 + 0x0284 0x05 0x0 + 0x05c0 0x14 0x0 0x0800 0x00 0x0 0x0844 0x03 0x0>; @@ -195,10 +207,6 @@ qcom,vreg-cx-voltage-level = ; - qcom,l1-supported; - qcom,l1ss-supported; - qcom,aux-clk-sync; - qcom,max-link-speed = <0x3>; qcom,ep-latency = <10>; @@ -206,6 +214,7 @@ qcom,slv-addr-space-size = <0x4000000>; qcom,phy-status-offset = <0x814>; + qcom,phy-power-down-offset = <0x840>; qcom,boot-option = <0x1>; @@ -262,7 +271,7 @@ "pcie_tbu_clk", "pcie_phy_refgen_clk", "pcie_phy_aux_clk"; - max-clock-frequency-hz = <0>, <0>, <19200000>, <0>, <0>, + max-clock-frequency-hz = <0>, <0>, <19200000>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <100000000>, <0>; resets = <&clock_gcc GCC_PCIE_0_BCR>, @@ -349,121 +358,151 @@ "msi_28", "msi_29", "msi_30", "msi_31"; qcom,phy-sequence = <0x0a40 0x03 0x0 - 0x0094 0x08 0x0 - 0x0154 0x32 0x0 - 0x016c 0x08 0x0 + 0x0010 0x00 0x0 + 0x001c 0x31 0x0 + 0x0020 0x01 0x0 + 0x0024 0xde 0x0 + 0x0028 0x07 0x0 + 0x0030 0x4c 0x0 + 0x0034 0x06 0x0 + 0x0048 0x90 0x0 + 0x0050 0x07 0x0 0x0058 0x0f 0x0 + 0x0074 0x06 0x0 + 0x0078 0x06 0x0 + 0x007c 0x16 0x0 + 0x0080 0x16 0x0 + 0x0084 0x36 0x0 + 0x0088 0x36 0x0 + 0x0094 0x08 0x0 0x00a4 0x42 0x0 - 0x0110 0x24 0x0 - 0x011c 0x03 0x0 - 0x0118 0xb4 0x0 - 0x010c 0x02 0x0 - 0x01bc 0x11 0x0 - 0x00bc 0x82 0x0 - 0x00d4 0x03 0x0 - 0x00d0 0x55 0x0 - 0x00cc 0x55 0x0 - 0x00b0 0x1a 0x0 0x00ac 0x0a 0x0 + 0x00b0 0x1a 0x0 + 0x00b4 0x14 0x0 + 0x00b8 0x34 0x0 + 0x00bc 0x82 0x0 0x00c4 0x68 0x0 - 0x00e0 0x02 0x0 - 0x00dc 0xaa 0x0 + 0x00cc 0x55 0x0 + 0x00d0 0x55 0x0 + 0x00d4 0x03 0x0 0x00d8 0xab 0x0 - 0x00b8 0x34 0x0 - 0x00b4 0x14 0x0 - 0x01bc 0x01 0x0 + 0x00dc 0xaa 0x0 + 0x00e0 0x02 0x0 + 0x010c 0x02 0x0 + 0x0110 0x24 0x0 + 0x0118 0xb4 0x0 + 0x011c 0x03 0x0 + 0x0154 0x32 0x0 0x0158 0x01 0x0 - 0x0074 0x06 0x0 - 0x007c 0x16 0x0 - 0x0084 0x36 0x0 - 0x0078 0x06 0x0 - 0x0080 0x16 0x0 - 0x0088 0x36 0x0 - 0x01b0 0x1e 0x0 + 0x016c 0x08 0x0 0x01ac 0xb9 0x0 - 0x01b8 0x18 0x0 + 0x01b0 0x1e 0x0 0x01b4 0x94 0x0 - 0x0050 0x07 0x0 - 0x0010 0x00 0x0 - 0x001c 0x31 0x0 - 0x0020 0x01 0x0 - 0x0024 0xde 0x0 - 0x0028 0x07 0x0 - 0x0030 0x4c 0x0 - 0x0034 0x06 0x0 - 0x069c 0x12 0x0 - 0x029c 0x12 0x0 + 0x01b8 0x18 0x0 + 0x01bc 0x01 0x0 0x0284 0x05 0x0 - 0x0684 0x05 0x0 - 0x0e38 0x03 0x0 - 0x051c 0x03 0x0 - 0x091c 0x03 0x0 - 0x0518 0x1c 0x0 - 0x0918 0x1c 0x0 - 0x0524 0x14 0x0 - 0x0924 0x14 0x0 - 0x08ec 0x0e 0x0 + 0x029c 0x12 0x0 + 0x0414 0x04 0x0 + 0x0434 0x7f 0x0 + 0x0444 0x70 0x0 + 0x04d8 0x01 0x0 0x04ec 0x0e 0x0 - 0x08f0 0x4a 0x0 0x04f0 0x4a 0x0 - 0x08f4 0x0f 0x0 0x04f4 0x0f 0x0 - 0x05b4 0x04 0x0 - 0x09b4 0x04 0x0 - 0x0834 0x7f 0x0 - 0x0434 0x7f 0x0 - 0x0844 0x70 0x0 - 0x0444 0x70 0x0 + 0x04f8 0xc0 0x0 + 0x04fc 0x00 0x0 0x0510 0x17 0x0 - 0x0910 0x17 0x0 - 0x08d8 0x01 0x0 - 0x04d8 0x01 0x0 - 0x0998 0xd4 0x0 + 0x0518 0x1c 0x0 + 0x051c 0x03 0x0 + 0x0524 0x14 0x0 + 0x0570 0x7f 0x0 + 0x0574 0xff 0x0 + 0x0578 0xff 0x0 + 0x057c 0xdb 0x0 + 0x0580 0x76 0x0 + 0x0584 0x24 0x0 + 0x0588 0xe4 0x0 + 0x058c 0xec 0x0 + 0x0590 0x39 0x0 + 0x0594 0x07 0x0 0x0598 0xd4 0x0 - 0x099c 0x54 0x0 0x059c 0x54 0x0 0x05a0 0xdb 0x0 - 0x09a0 0xdb 0x0 0x05a4 0x39 0x0 - 0x09a4 0x39 0x0 0x05a8 0x31 0x0 - 0x09a8 0x31 0x0 - 0x0584 0x24 0x0 + 0x05b4 0x04 0x0 + 0x0684 0x05 0x0 + 0x069c 0x12 0x0 + 0x0814 0x04 0x0 + 0x0834 0x7f 0x0 + 0x0844 0x70 0x0 + 0x08d8 0x01 0x0 + 0x08ec 0x0e 0x0 + 0x08f0 0x4a 0x0 + 0x08f4 0x0f 0x0 + 0x08f8 0xc0 0x0 + 0x08fc 0x00 0x0 + 0x0910 0x17 0x0 + 0x0918 0x1c 0x0 + 0x091c 0x03 0x0 + 0x0924 0x14 0x0 + 0x0970 0x7f 0x0 + 0x0974 0xff 0x0 + 0x0978 0xff 0x0 + 0x097c 0xdb 0x0 + 0x0980 0x76 0x0 0x0984 0x24 0x0 0x0988 0xe4 0x0 - 0x0588 0xe4 0x0 0x098c 0xec 0x0 - 0x058c 0xec 0x0 0x0990 0x39 0x0 - 0x0590 0x39 0x0 - 0x0994 0x36 0x0 - 0x0594 0x36 0x0 - 0x0570 0xbd 0x0 - 0x0970 0xbd 0x0 - 0x0974 0xbd 0x0 - 0x0574 0xbd 0x0 - 0x0578 0x7f 0x0 - 0x0978 0x7f 0x0 - 0x097c 0xfb 0x0 - 0x057c 0xfb 0x0 - 0x0980 0x53 0x0 - 0x0580 0x53 0x0 - 0x08fc 0x00 0x0 - 0x04fc 0x00 0x0 - 0x08f8 0xc0 0x0 - 0x04f8 0xc0 0x0 - 0x0414 0x04 0x0 - 0x0814 0x04 0x0 + 0x0994 0x07 0x0 + 0x0998 0xd4 0x0 + 0x099c 0x54 0x0 + 0x09a0 0xdb 0x0 + 0x09a4 0x39 0x0 + 0x09a8 0x31 0x0 + 0x09b4 0x04 0x0 + 0x0a98 0x01 0x0 + 0x0abc 0x56 0x0 + 0x0adc 0x0d 0x0 + 0x0b88 0x66 0x0 0x0ba4 0x01 0x0 - 0x0e90 0x00 0x0 + 0x0b98 0x08 0x0 + 0x0e14 0x07 0x0 + 0x0e1c 0xc1 0x0 0x0e40 0x01 0x0 0x0e48 0x01 0x0 + 0x0e78 0x50 0x0 + 0x0e90 0x00 0x0 + 0x0ea0 0x11 0x0 + 0x0e38 0x03 0x0 0x0e50 0x00 0x0 - 0x0048 0x90 0x0 - 0x0e1c 0xc1 0x0 - 0x0b88 0x66 0x0 - 0x0b98 0x08 0x0 - 0x0adc 0x0d 0x0 + 0x04f0 0x6a 0x0 + 0x04f4 0x07 0x0 + 0x04b4 0x02 0x0 + 0x04b8 0x02 0x0 + 0x04bc 0xaa 0x0 + 0x04d4 0x54 0x0 + 0x04d8 0x37 0x0 + 0x0460 0xa0 0x0 + 0x05c4 0x0c 0x0 + 0x0464 0xc0 0x0 + 0x04c0 0x0f 0x0 + 0x0284 0x05 0x0 + 0x05c0 0x14 0x0 + 0x08f0 0x6a 0x0 + 0x08f4 0x07 0x0 + 0x08b4 0x02 0x0 + 0x08b8 0x02 0x0 + 0x08bc 0xaa 0x0 + 0x08d4 0x54 0x0 + 0x08d8 0x37 0x0 + 0x0860 0xa0 0x0 + 0x09c4 0x0c 0x0 + 0x0864 0xc0 0x0 + 0x08c0 0x0f 0x0 + 0x0684 0x05 0x0 + 0x09c0 0x14 0x0 0x0a00 0x00 0x0 0x0a44 0x03 0x0>; @@ -485,10 +524,6 @@ qcom,vreg-cx-voltage-level = ; - qcom,l1-supported; - qcom,l1ss-supported; - qcom,aux-clk-sync; - qcom,max-link-speed = <0x3>; qcom,ep-latency = <10>; @@ -496,6 +531,7 @@ qcom,slv-addr-space-size = <0x20000000>; qcom,phy-status-offset = <0xa14>; + qcom,phy-power-down-offset = <0xa40>; qcom,boot-option = <0x1>; @@ -509,22 +545,22 @@ qcom,smmu-sid-base = <0x1e00>; - iommu-map = <0x0 &apps_smmu 0x1e10 0x1>, - <0x100 &apps_smmu 0x1e11 0x1>, - <0x200 &apps_smmu 0x1e12 0x1>, - <0x300 &apps_smmu 0x1e13 0x1>, - <0x400 &apps_smmu 0x1e14 0x1>, - <0x500 &apps_smmu 0x1e15 0x1>, - <0x600 &apps_smmu 0x1e16 0x1>, - <0x700 &apps_smmu 0x1e17 0x1>, - <0x800 &apps_smmu 0x1e18 0x1>, - <0x900 &apps_smmu 0x1e19 0x1>, - <0xa00 &apps_smmu 0x1e1a 0x1>, - <0xb00 &apps_smmu 0x1e1b 0x1>, - <0xc00 &apps_smmu 0x1e1c 0x1>, - <0xd00 &apps_smmu 0x1e1d 0x1>, - <0xe00 &apps_smmu 0x1e1e 0x1>, - <0xf00 &apps_smmu 0x1e1f 0x1>; + iommu-map = <0x0 &apps_smmu 0x1e00 0x1>, + <0x100 &apps_smmu 0x1e01 0x1>, + <0x200 &apps_smmu 0x1e02 0x1>, + <0x300 &apps_smmu 0x1e03 0x1>, + <0x400 &apps_smmu 0x1e04 0x1>, + <0x500 &apps_smmu 0x1e05 0x1>, + <0x600 &apps_smmu 0x1e06 0x1>, + <0x700 &apps_smmu 0x1e07 0x1>, + <0x800 &apps_smmu 0x1e08 0x1>, + <0x900 &apps_smmu 0x1e09 0x1>, + <0xa00 &apps_smmu 0x1e0a 0x1>, + <0xb00 &apps_smmu 0x1e0b 0x1>, + <0xc00 &apps_smmu 0x1e0c 0x1>, + <0xd00 &apps_smmu 0x1e0d 0x1>, + <0xe00 &apps_smmu 0x1e0e 0x1>, + <0xf00 &apps_smmu 0x1e0f 0x1>; qcom,msm-bus,name = "pcie1"; qcom,msm-bus,num-cases = <2>; @@ -552,7 +588,7 @@ "pcie_tbu_clk", "pcie_phy_refgen_clk", "pcie_phy_aux_clk"; - max-clock-frequency-hz = <0>, <0>, <19200000>, <0>, <0>, + max-clock-frequency-hz = <0>, <0>, <19200000>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <100000000>, <0>; resets = <&clock_gcc GCC_PCIE_1_BCR>, diff --git a/arch/arm64/boot/dts/qcom/sm8150-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sm8150-pinctrl.dtsi index c1189d70af53a1d30757afef6a0bcbdeb35191c2..618c6044d1e5dcaae53b70d831cd672ca00fd72a 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-pinctrl.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-pinctrl.dtsi @@ -283,6 +283,48 @@ }; }; + pmx_ts_active { + ts_active: ts_active { + mux { + pins = "gpio122", "gpio54"; + function = "gpio"; + }; + config { + pins = "gpio122", "gpio54"; + drive-strength = <8>; + bias-pull-up; + }; + }; + }; + + pmx_ts_int_suspend { + ts_int_suspend: ts_int_suspend { + mux { + pins = "gpio122"; + function = "gpio"; + }; + config { + pins = "gpio122"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + + pmx_ts_reset_suspend { + ts_reset_suspend: ts_reset_suspend { + mux { + pins = "gpio54"; + function = "gpio"; + }; + config { + pins = "gpio54"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; + pcie0 { pcie0_clkreq_default: pcie0_clkreq_default { mux { @@ -3884,5 +3926,17 @@ drive-strength = <2>; /* 2 MA */ }; }; + + trigout_a: trigout_a { + mux { + pins = "gpio49"; + function = "qdss_cti"; + }; + config { + pins = "gpio49"; + drive-strength = <2>; + bias-disable; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sm8150-pmic-overlay.dtsi index d2d119e59898a086b27d2bfb8beb55f34ffcfd14..bb75c2ad6c825032c10ad7e5f39a69d474ea1f1a 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-pmic-overlay.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-pmic-overlay.dtsi @@ -68,5 +68,5 @@ }; &usb0 { - extcon = <&pm855b_pdphy>, <&pm855b_pdphy>, <&eud>; + extcon = <&pm855b_pdphy>, <&eud>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-qrd.dtsi b/arch/arm64/boot/dts/qcom/sm8150-qrd.dtsi index 5ef91a3b09073d1baced97483251dda5b0e71f9a..b0ad9112b4c0caf6998ea8c3ba3b96d85c7d09da 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-qrd.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-qrd.dtsi @@ -12,12 +12,39 @@ #include #include -#include #include "sm8150-pmic-overlay.dtsi" #include "sm8150-audio-overlay.dtsi" #include "sm8150-sde-display.dtsi" #include "sm8150-camera-sensor-qrd.dtsi" +#include "sm8150-thermal-overlay.dtsi" +#include "smb1390.dtsi" + +&vendor { + bluetooth: bt_wcn3990 { + compatible = "qca,wcn3990"; + qca,bt-vdd-core-supply = <&pm855_l7>; + qca,bt-vdd-pa-supply = <&pm855l_l2>; + qca,bt-vdd-ldo-supply = <&pm855l_l11>; + + qca,bt-vdd-core-voltage-level = <1800000 1800000>; + qca,bt-vdd-pa-voltage-level = <1304000 1304000>; + qca,bt-vdd-ldo-voltage-level = <3312000 3312000>; + + qca,bt-vdd-core-current-level = <0>; /* LPM/PFM */ + qca,bt-vdd-pa-current-level = <0>; /* LPM/PFM */ + qca,bt-vdd-ldo-current-level = <0>; /* LPM/PFM */ + }; + + qrd_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <15>; + #include "fg-gen4-batterydata-mlp466076-3250mah.dtsi" + }; +}; + +&pm855b_fg { + qcom,battery-data = <&qrd_batterydata>; +}; &soc { gpio_keys { @@ -48,10 +75,34 @@ linux,can-disable; }; }; + + qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clock-frequency = <25000000>; + qcom,ipc-gpio = <&tlmm 118 0>; + qcom,finger-detect-gpio = <&pm855_gpios 1 0>; + }; }; -&mdss_mdp { - #cooling-cells = <2>; +&qupv3_se17_i2c { + status = "ok"; + + st_fts@49 { + compatible = "st,fts"; + reg = <0x49>; + interrupt-parent = <&tlmm>; + interrupts = <122 0x2008>; + vdd-supply = <&pm855_s4>; + avdd-supply = <&pm855_l17>; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; + pinctrl-0 = <&ts_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + st,irq-gpio = <&tlmm 122 0x2008>; + st,reset-gpio = <&tlmm 54 0x00>; + st,regulator_dvdd = "vdd"; + st,regulator_avdd = "avdd"; + }; }; &snd_9360 { @@ -78,6 +129,8 @@ "AIF4 VI", "MCLK", "RX_BIAS", "MCLK", "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", "DMIC0", "MIC BIAS1", "MIC BIAS1", "Digital Mic0", "DMIC2", "MIC BIAS3", @@ -93,13 +146,6 @@ qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrRight"; }; -/* Force USB to high speed mode before USB re-driver enabled on QRD855 */ -&usb0 { - dwc3@a600000 { - maximum-speed = "high-speed"; - }; -}; - &qupv3_se9_i2c { status = "ok"; nq@28 { @@ -120,35 +166,6 @@ }; }; -&tlmm { - display_panel_avdd_eldo_default: display_panel_avdd_eldo_default { - mux { - pins = "gpio130"; - function = "gpio"; - }; - config { - pins = "gpio130"; - drive-strength = <8>; - bias-disable = <0>; - output-low; - }; - }; -}; - -&soc { - display_panel_avdd_eldo: display-gpio-regulator@0 { - compatible = "regulator-fixed"; - regulator-name = "display_panel_avdd_eldo"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-enable-ramp-delay = <233>; - gpio = <&tlmm 130 0>; - enable-active-high; - pinctrl-names = "default"; - pintctrl-0 = <&display_panel_avdd_eldo_default>; - }; -}; - &dsi_panel_pwr_supply_vdd_no_labibb { qcom,panel-supply-entry@1 { qcom,supply-min-voltage = <1800000>; @@ -160,7 +177,7 @@ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_vdd_no_labibb>; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; qcom,mdss-dsi-bl-min-level = <1>; - qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-dsi-bl-max-level = <1023>; qcom,mdss-dsi-mode-sel-gpio-state = "single_port"; qcom,panel-mode-gpio = <&tlmm 7 0>; qcom,platform-te-gpio = <&tlmm 8 0>; @@ -169,6 +186,9 @@ &dsi_sw43404_amoled_cmd_display { qcom,dsi-display-active; +}; + +&sde_dsi { vdd-supply = <&display_panel_avdd_eldo>; }; @@ -195,6 +215,8 @@ vdda-pll-supply = <&pm855l_l3>; vdda-phy-max-microamp = <90200>; vdda-pll-max-microamp = <19000>; + + status = "ok"; }; &ufshc_mem { @@ -208,6 +230,8 @@ qcom,vddp-ref-clk-supply = <&pm855_l9>; qcom,vddp-ref-clk-max-microamp = <100>; + + status = "ok"; }; &sdhc_2 { @@ -243,14 +267,6 @@ qcom,pre-scaling = <1 1>; }; - smb_therm { - reg = ; - label = "smb_therm"; - qcom,ratiometric; - qcom,hw-settle-time = <200>; - qcom,pre-scaling = <1 1>; - }; - vcoin { reg = ; label = "vcoin"; @@ -360,116 +376,127 @@ }; }; +&wil6210 { + status = "ok"; +}; + +&pm855b_adc_tm { + wp_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm855_adc_tm { + xo_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm855l_adc_tm { + camera_flash_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + skin_msm_therm { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa_therm2 { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + &thermal_zones { - pm855b_tz { - cooling-maps { - trip0_bat { - trip = <&pm855b_trip0>; - cooling-device = - <&pm855b_charger (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip1_bat { - trip = <&pm855b_trip1>; - cooling-device = - <&pm855b_charger THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + wp-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855b_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; }; }; - pm855_tz { - cooling-maps { - trip0_cpu0 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU0 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu1 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU1 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; + xo-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855_adc_tm ADC_XO_THERM_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip0_cpu2 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU2 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu3 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU3 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu4 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU4 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu5 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU5 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu6 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU6 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip0_cpu7 { - trip = <&pm855_trip0>; - cooling-device = - <&CPU7 (THERMAL_MAX_LIMIT-1) - (THERMAL_MAX_LIMIT-1)>; - }; - trip1_cpu1 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU1 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu2 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU2 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu3 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU3 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu4 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU4 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; - }; - trip1_cpu5 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU5 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + camera-flash-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM1_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip1_cpu6 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU6 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + skin-msm-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM2_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; - trip1_cpu7 { - trip = <&pm855_trip1>; - cooling-device = - <&CPU7 THERMAL_MAX_LIMIT - THERMAL_MAX_LIMIT>; + }; + }; + + pa-therm2 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-governor = "user_space"; + thermal-sensors = <&pm855l_adc_tm ADC_AMUX_THM3_PU2>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; }; }; }; }; + +&pm855b_charger { + io-channels = <&pm855b_vadc ADC_USB_IN_V_16>, + <&pm855b_vadc ADC_USB_IN_I>, + <&pm855b_vadc ADC_CHG_TEMP>; + io-channel-names = "usb_in_voltage", + "usb_in_current", + "chg_temp"; +}; + +&smb1390_charger { + status = "ok"; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-regulator.dtsi b/arch/arm64/boot/dts/qcom/sm8150-regulator.dtsi index 7d8768c343a306c05674820f413cd73a0154961a..9eead42647742072f6cd01c01760d59c984fe9d2 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-regulator.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-regulator.dtsi @@ -195,11 +195,14 @@ ; qcom,mode-threshold-currents = <0 1>; + proxy-supply = <&pm855_l5>; L5A: pm855_l5: regulator-pm855-l5 { regulator-name = "pm855_l5"; qcom,set = ; regulator-min-microvolt = <880000>; regulator-max-microvolt = <880000>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <23800>; qcom,init-voltage = <880000>; qcom,init-mode = ; }; @@ -402,11 +405,14 @@ ; qcom,mode-threshold-currents = <0 10000>; + proxy-supply = <&pm855_l14>; L14A: pm855_l14: regulator-pm855-l14 { regulator-name = "pm855_l14"; qcom,set = ; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1880000>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <115000>; qcom,init-voltage = <1800000>; qcom,init-mode = ; }; @@ -683,11 +689,14 @@ ; qcom,mode-threshold-currents = <0 1>; + proxy-supply = <&pm855l_l3>; L3C: pm855l_l3: regulator-pm855l-l3 { regulator-name = "pm855l_l3"; qcom,set = ; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <51800>; qcom,init-voltage = <1200000>; qcom,init-mode = ; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi index f81aafcb168b837e26ffb340480bcf7bb139f630..89b027c8e4549f9c1c9e23744e5afa7742dcd32f 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-sde-display.dtsi @@ -30,6 +30,21 @@ #include "dsi-panel-sw43404-amoled-dsc-wqhd-cmd.dtsi" #include +&tlmm { + display_panel_avdd_eldo_default: display_panel_avdd_eldo_default { + mux { + pins = "gpio130"; + function = "gpio"; + }; + config { + pins = "gpio130"; + drive-strength = <8>; + bias-disable = <0>; + output-high; + }; + }; +}; + &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { #address-cells = <1>; @@ -105,377 +120,249 @@ }; }; + display_panel_avdd_eldo: display-gpio-regulator@0 { + compatible = "regulator-fixed"; + regulator-name = "display_panel_avdd_eldo"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-enable-ramp-delay = <233>; + gpio = <&tlmm 130 0>; + enable-active-high; + regulator-boot-on; + pinctrl-names = "default"; + pintctrl-0 = <&display_panel_avdd_eldo_default>; + }; + dsi_sharp_4k_dsc_video_display: qcom,dsi-display@0 { - compatible = "qcom,dsi-display"; label = "dsi_sharp_4k_dsc_video_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_4k_dsc_video>; - - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_sharp_4k_dsc_cmd_display: qcom,dsi-display@1 { - compatible = "qcom,dsi-display"; label = "dsi_sharp_4k_dsc_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_sharp_4k_dsc_cmd>; - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_sharp_1080_cmd_display: qcom,dsi-display@2 { - compatible = "qcom,dsi-display"; label = "dsi_sharp_1080_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0>; - qcom,dsi-phy = <&mdss_dsi_phy0>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; qcom,dsi-panel = <&dsi_sharp_1080_cmd>; - - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_dual_sharp_1080_120hz_cmd_display: qcom,dsi-display@3 { - compatible = "qcom,dsi-display"; label = "dsi_dual_sharp_1080_120hz_cmd_display"; qcom,display-type = "primary"; - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; qcom,dsi-panel = <&dsi_dual_sharp_1080_120hz_cmd>; - - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_dual_nt35597_truly_video_display: qcom,dsi-display@4 { - compatible = "qcom,dsi-display"; label = "dsi_dual_nt35597_truly_video_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; qcom,dsi-panel = <&dsi_dual_nt35597_truly_video>; - - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_dual_nt35597_truly_cmd_display: qcom,dsi-display@5 { - compatible = "qcom,dsi-display"; label = "dsi_dual_nt35597_truly_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; qcom,dsi-panel = <&dsi_dual_nt35597_truly_cmd>; - - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_nt35597_truly_dsc_cmd_display: qcom,dsi-display@6 { - compatible = "qcom,dsi-display"; label = "dsi_nt35597_truly_dsc_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy1>; - clocks = <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, - <&mdss_dsi1_pll PCLK_MUX_1_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; + qcom,dsi-ctrl-num = <1>; + qcom,dsi-phy-num = <1>; + qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; qcom,dsi-panel = <&dsi_nt35597_truly_dsc_cmd>; - - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_nt35597_truly_dsc_video_display: qcom,dsi-display@7 { - compatible = "qcom,dsi-display"; label = "dsi_nt35597_truly_dsc_video_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy1>; - clocks = <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, - <&mdss_dsi1_pll PCLK_MUX_1_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; + qcom,dsi-ctrl-num = <1>; + qcom,dsi-phy-num = <1>; + qcom,dsi-select-clocks = "src_byte_clk1", "src_pixel_clk1"; - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; qcom,dsi-panel = <&dsi_nt35597_truly_dsc_video>; - - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_sim_vid_display: qcom,dsi-display@8 { - compatible = "qcom,dsi-display"; label = "dsi_sim_vid_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0>; - qcom,dsi-phy = <&mdss_dsi_phy0>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_vid>; }; dsi_dual_sim_vid_display: qcom,dsi-display@9 { - compatible = "qcom,dsi-display"; label = "dsi_dual_sim_vid_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_vid>; }; dsi_sim_cmd_display: qcom,dsi-display@10 { - compatible = "qcom,dsi-display"; label = "dsi_sim_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0>; - qcom,dsi-phy = <&mdss_dsi_phy0>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_cmd>; }; dsi_dual_sim_cmd_display: qcom,dsi-display@11 { - compatible = "qcom,dsi-display"; label = "dsi_dual_sim_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_cmd>; }; dsi_sim_dsc_375_cmd_display: qcom,dsi-display@12 { - compatible = "qcom,dsi-display"; label = "dsi_sim_dsc_375_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0>; - qcom,dsi-phy = <&mdss_dsi_phy0>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_sim_dsc_375_cmd>; }; dsi_dual_sim_dsc_375_cmd_display: qcom,dsi-display@13 { - compatible = "qcom,dsi-display"; label = "dsi_dual_sim_dsc_375_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; - qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + qcom,dsi-ctrl-num = <0 1>; + qcom,dsi-phy-num = <0 1>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>; }; dsi_sw43404_amoled_cmd_display: qcom,dsi-display@14 { - compatible = "qcom,dsi-display"; label = "dsi_sw43404_amoled_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0>; - qcom,dsi-phy = <&mdss_dsi_phy0>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_sw43404_amoled_cmd>; - vddio-supply = <&pm855_l14>; }; dsi_nt35695b_truly_fhd_cmd_display: qcom,dsi-display@15 { - compatible = "qcom,dsi-display"; label = "dsi_nt35695b_truly_fhd_cmd_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0>; - qcom,dsi-phy = <&mdss_dsi_phy0>; - clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; - - pinctrl-names = "panel_active", "panel_suspend"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>; - vddio-supply = <&pm855_l14>; - lab-supply = <&lcdb_ldo_vreg>; - ibb-supply = <&lcdb_ncp_vreg>; }; dsi_nt35695b_truly_fhd_video_display: qcom,dsi-display@16 { - compatible = "qcom,dsi-display"; label = "dsi_nt35695b_truly_fhd_video_display"; qcom,display-type = "primary"; - qcom,dsi-ctrl = <&mdss_dsi0>; - qcom,dsi-phy = <&mdss_dsi_phy0>; + qcom,dsi-ctrl-num = <0>; + qcom,dsi-phy-num = <0>; + qcom,dsi-select-clocks = "src_byte_clk0", "src_pixel_clk0"; + + qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; + }; + + sde_dsi: qcom,dsi-display { + compatible = "qcom,dsi-display"; + + qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>; + qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>; + clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>, - <&mdss_dsi0_pll PCLK_MUX_0_CLK>; - clock-names = "src_byte_clk", "src_pixel_clk"; + <&mdss_dsi0_pll PCLK_MUX_0_CLK>, + <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>, + <&mdss_dsi1_pll PCLK_MUX_1_CLK>; + clock-names = "src_byte_clk0", "src_pixel_clk0", + "src_byte_clk1", "src_pixel_clk1"; pinctrl-names = "panel_active", "panel_suspend"; pinctrl-0 = <&sde_dsi_active &sde_te_active>; pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; - qcom,platform-te-gpio = <&tlmm 8 0>; - qcom,platform-reset-gpio = <&tlmm 6 0>; - qcom,panel-mode-gpio = <&tlmm 7 0>; - qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>; + qcom,platform-te-gpio = <&tlmm 8 0>; vddio-supply = <&pm855_l14>; lab-supply = <&lcdb_ldo_vreg>; ibb-supply = <&lcdb_ncp_vreg>; + vdd-supply = <&display_panel_avdd_eldo>; + + qcom,dsi-display-list = + <&dsi_sharp_4k_dsc_video_display + &dsi_sharp_4k_dsc_cmd_display + &dsi_sharp_1080_cmd_display + &dsi_dual_sharp_1080_120hz_cmd_display + &dsi_dual_nt35597_truly_video_display + &dsi_dual_nt35597_truly_cmd_display + &dsi_nt35597_truly_dsc_cmd_display + &dsi_nt35597_truly_dsc_video_display + &dsi_sim_vid_display + &dsi_dual_sim_vid_display + &dsi_sim_cmd_display + &dsi_dual_sim_cmd_display + &dsi_sim_dsc_375_cmd_display + &dsi_dual_sim_dsc_375_cmd_display + &dsi_sw43404_amoled_cmd_display + &dsi_nt35695b_truly_fhd_cmd_display + &dsi_nt35695b_truly_fhd_video_display>; }; sde_wb: qcom,wb-display@0 { @@ -508,13 +395,11 @@ }; &mdss_mdp { - connectors = <&sde_wb &sde_dp>; + connectors = <&sde_wb &sde_dp &sde_dsi>; }; /* PHY TIMINGS REVISION P */ &dsi_dual_nt35597_truly_video { - qcom,mdss-dsi-t-clk-post = <0x17>; - qcom,mdss-dsi-t-clk-pre = <0x18>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 @@ -527,8 +412,6 @@ }; &dsi_dual_nt35597_truly_cmd { - qcom,mdss-dsi-t-clk-post = <0x17>; - qcom,mdss-dsi-t-clk-pre = <0x18>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 @@ -541,8 +424,6 @@ }; &dsi_nt35597_truly_dsc_cmd { - qcom,mdss-dsi-t-clk-post = <0x15>; - qcom,mdss-dsi-t-clk-pre = <0x12>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05 @@ -556,8 +437,6 @@ }; &dsi_nt35597_truly_dsc_video { - qcom,mdss-dsi-t-clk-post = <0x15>; - qcom,mdss-dsi-t-clk-pre = <0x12>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05 @@ -571,8 +450,6 @@ }; &dsi_sharp_4k_dsc_video { - qcom,mdss-dsi-t-clk-post = <0x18>; - qcom,mdss-dsi-t-clk-pre = <0x19>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 08 @@ -584,8 +461,6 @@ }; &dsi_sharp_4k_dsc_cmd { - qcom,mdss-dsi-t-clk-post = <0x18>; - qcom,mdss-dsi-t-clk-pre = <0x19>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 08 @@ -597,8 +472,6 @@ }; &dsi_nt35695b_truly_fhd_video { - qcom,mdss-dsi-t-clk-post = <0x17>; - qcom,mdss-dsi-t-clk-pre = <0x19>; qcom,mdss-dsi-display-timings { timing@0 { qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 @@ -610,8 +483,6 @@ }; &dsi_nt35695b_truly_fhd_cmd { - qcom,mdss-dsi-t-clk-post = <0x17>; - qcom,mdss-dsi-t-clk-pre = <0x19>; qcom,mdss-dsi-display-timings { timing@0 { qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 @@ -623,8 +494,6 @@ }; &dsi_dual_sharp_1080_120hz_cmd { - qcom,mdss-dsi-t-clk-post = <0x0f>; - qcom,mdss-dsi-t-clk-pre = <0x36>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 24 09 09 26 24 09 @@ -637,8 +506,6 @@ }; &dsi_sharp_1080_cmd { - qcom,mdss-dsi-t-clk-post = <0x0c>; - qcom,mdss-dsi-t-clk-pre = <0x29>; qcom,mdss-dsi-display-timings { timing@0{ qcom,mdss-dsi-panel-phy-timings = [00 1A 06 06 22 20 07 @@ -650,12 +517,10 @@ }; &dsi_sim_vid { - qcom,mdss-dsi-t-clk-post = <0x0d>; - qcom,mdss-dsi-t-clk-pre = <0x2d>; qcom,mdss-dsi-display-timings { timing@0{ - qcom,mdss-dsi-panel-phy-timings = [01 03 01 00 01 01 01 - 01 00 03 04 00 03 04]; + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; qcom,display-topology = <1 0 1>, <2 0 1>; qcom,default-topology-index = <0>; @@ -664,12 +529,10 @@ }; &dsi_dual_sim_vid { - qcom,mdss-dsi-t-clk-post = <0x0d>; - qcom,mdss-dsi-t-clk-pre = <0x2d>; qcom,mdss-dsi-display-timings { timing@0{ - qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 - 03 02 03 04 00 10 06]; + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; qcom,display-topology = <2 0 2>, <1 0 2>; qcom,default-topology-index = <0>; @@ -678,39 +541,57 @@ }; &dsi_sim_cmd { - qcom,mdss-dsi-t-clk-post = <0x0d>; - qcom,mdss-dsi-t-clk-pre = <0x2d>; qcom,mdss-dsi-display-timings { timing@0{ - qcom,mdss-dsi-panel-phy-timings = [00 03 01 00 01 01 02 - 01 00 03 04 00 03 04]; + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; qcom,display-topology = <1 0 1>, - <2 0 1>; - qcom,default-topology-index = <0>; + <2 2 1>; + qcom,default-topology-index = <1>; + qcom,panel-roi-alignment = <720 40 720 40 720 40>; + qcom,partial-update-enabled = "single_roi"; + }; + + timing@1{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; + qcom,display-topology = <1 0 1>, + <2 2 1>; + qcom,default-topology-index = <1>; + qcom,panel-roi-alignment = <540 40 540 40 540 40>; + qcom,partial-update-enabled = "single_roi"; + }; + + timing@2{ + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; + qcom,display-topology = <1 0 1>, + <2 2 1>; + qcom,default-topology-index = <1>; + qcom,panel-roi-alignment = <360 40 360 40 360 40>; + qcom,partial-update-enabled = "single_roi"; }; }; }; &dsi_dual_sim_cmd { - qcom,mdss-dsi-t-clk-post = <0x0d>; - qcom,mdss-dsi-t-clk-pre = <0x2d>; qcom,mdss-dsi-display-timings { timing@0{ - qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 - 03 02 03 04 00 10 06]; + qcom,mdss-dsi-panel-phy-timings = [00 24 09 09 26 24 09 + 09 06 03 04 00]; qcom,display-topology = <2 0 2>; qcom,default-topology-index = <0>; }; timing@1{ - qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 - 03 02 03 04 00 10 06]; + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; qcom,display-topology = <2 0 2>, <1 0 2>; qcom,default-topology-index = <0>; }; timing@2{ - qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 - 03 02 03 04 00 10 06]; + qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 08 + 08 05 03 04 00 19 18]; qcom,display-topology = <2 0 2>; qcom,default-topology-index = <0>; }; @@ -718,18 +599,16 @@ }; &dsi_sim_dsc_375_cmd { - qcom,mdss-dsi-t-clk-post = <0x0d>; - qcom,mdss-dsi-t-clk-pre = <0x2d>; qcom,mdss-dsi-display-timings { timing@0 { /* 1080p */ - qcom,mdss-dsi-panel-phy-timings = [00 12 03 04 07 07 04 - 04 03 03 04 00 13 07]; + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; qcom,display-topology = <1 1 1>; qcom,default-topology-index = <0>; }; timing@1 { /* qhd */ - qcom,mdss-dsi-panel-phy-timings = [00 12 03 04 07 07 04 - 04 03 03 04 00 13 07]; + qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 08 + 08 05 03 04 00 19 18]; qcom,display-topology = <1 1 1>, <2 2 1>, /* dsc merge */ <2 1 1>; /* 3d mux */ @@ -739,18 +618,16 @@ }; &dsi_dual_sim_dsc_375_cmd { - qcom,mdss-dsi-t-clk-post = <0x0d>; - qcom,mdss-dsi-t-clk-pre = <0x2d>; qcom,mdss-dsi-display-timings { timing@0 { /* qhd */ - qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 - 03 02 03 04 00 10 06]; + qcom,mdss-dsi-panel-phy-timings = [00 1c 08 07 23 22 07 + 07 05 03 04 00 18 17]; qcom,display-topology = <2 2 2>; qcom,default-topology-index = <0>; }; timing@1 { /* 4k */ - qcom,mdss-dsi-panel-phy-timings = [00 13 03 03 05 06 03 - 03 02 03 04 00 10 06]; + qcom,mdss-dsi-panel-phy-timings = [00 1e 08 07 24 22 08 + 08 05 03 04 00 19 18]; qcom,display-topology = <2 2 2>; qcom,default-topology-index = <0>; }; @@ -758,13 +635,11 @@ }; &dsi_sw43404_amoled_cmd { - qcom,mdss-dsi-t-clk-post = <0x16>; - qcom,mdss-dsi-t-clk-pre = <0x16>; qcom,mdss-dsi-display-timings { timing@0 { qcom,mdss-dsi-panel-phy-timings = [00 1a 07 06 22 21 07 07 04 03 04 00 16 16]; - qcom,display-topology = <2 2 1>; + qcom,display-topology = <2 1 1>; qcom,default-topology-index = <0>; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-sde.dtsi b/arch/arm64/boot/dts/qcom/sm8150-sde.dtsi index e9aecdb0046f67ba8548a3a9ed15df71548e7461..77f613398f54f0e6feab82f2cafbd07ad7537a4a 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-sde.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-sde.dtsi @@ -79,7 +79,7 @@ qcom,sde-intf-off = <0x6b000 0x6b800 0x6c000 0x6c800>; - qcom,sde-intf-size = <0x280>; + qcom,sde-intf-size = <0x2b8>; qcom,sde-intf-type = "dp", "dsi", "dsi", "dp"; qcom,sde-pp-off = <0x71000 0x71800 @@ -123,6 +123,11 @@ qcom,sde-mixer-blend-op-off = <0x20 0x38 0x50 0x68 0x80 0x98 0xb0 0xc8 0xe0 0xf8 0x110>; + qcom,sde-max-per-pipe-bw-kbps = <4500000 4500000 + 4500000 4500000 + 4500000 4500000 + 4500000 4500000>; + /* offsets are relative to "mdp_phys + qcom,sde-off */ qcom,sde-sspp-clk-ctrl = <0x2ac 0>, <0x2b4 0>, <0x2bc 0>, <0x2c4 0>, @@ -147,8 +152,8 @@ qcom,sde-has-dest-scaler; qcom,sde-max-dest-scaler-input-linewidth = <2048>; qcom,sde-max-dest-scaler-output-linewidth = <2560>; - qcom,sde-max-bw-low-kbps = <9600000>; - qcom,sde-max-bw-high-kbps = <9600000>; + qcom,sde-max-bw-low-kbps = <12800000>; + qcom,sde-max-bw-high-kbps = <12800000>; qcom,sde-min-core-ib-kbps = <2400000>; qcom,sde-min-llcc-ib-kbps = <800000>; qcom,sde-min-dram-ib-kbps = <800000>; @@ -166,44 +171,22 @@ qcom,sde-vbif-qos-rt-remap = <3 3 4 4 5 5 6 6>; qcom,sde-vbif-qos-nrt-remap = <3 3 3 3 3 3 3 3>; - qcom,sde-danger-lut = <0x0000000f 0x0000ffff 0x00000000 - 0x00000000>; - qcom,sde-safe-lut-linear = - <4 0xfff8>, - <0 0xfff0>; - qcom,sde-safe-lut-macrotile = - <10 0xfe00>, - <11 0xfc00>, - <12 0xf800>, - <0 0xf000>; - qcom,sde-safe-lut-nrt = - <0 0xffff>; - qcom,sde-safe-lut-cwb = - <0 0xffff>; - qcom,sde-qos-lut-linear = - <4 0x00000000 0x00000357>, - <5 0x00000000 0x00003357>, - <6 0x00000000 0x00023357>, - <7 0x00000000 0x00223357>, - <8 0x00000000 0x02223357>, - <9 0x00000000 0x22223357>, - <10 0x00000002 0x22223357>, - <11 0x00000022 0x22223357>, - <12 0x00000222 0x22223357>, - <13 0x00002222 0x22223357>, - <14 0x00012222 0x22223357>, - <0 0x00112222 0x22223357>; - qcom,sde-qos-lut-macrotile = - <10 0x00000003 0x44556677>, - <11 0x00000033 0x44556677>, - <12 0x00000233 0x44556677>, - <13 0x00002233 0x44556677>, - <14 0x00012233 0x44556677>, - <0 0x00112233 0x44556677>; - qcom,sde-qos-lut-nrt = - <0 0x00000000 0x00000000>; - qcom,sde-qos-lut-cwb = - <0 0x75300000 0x00000000>; + /* macrotile & macrotile-qseed has the same configs */ + qcom,sde-danger-lut = <0x0000000f 0x0000ffff + 0x00000000 0x00000000 0x0000ffff>; + + qcom,sde-safe-lut-linear = <0 0xfff8>; + qcom,sde-safe-lut-macrotile = <0 0xf000>; + /* same as safe-lut-macrotile */ + qcom,sde-safe-lut-macrotile-qseed = <0 0xf000>; + qcom,sde-safe-lut-nrt = <0 0xffff>; + qcom,sde-safe-lut-cwb = <0 0xffff>; + + qcom,sde-qos-lut-linear = <0 0x00112222 0x22223357>; + qcom,sde-qos-lut-macrotile = <0 0x00112233 0x44556677>; + qcom,sde-qos-lut-macrotile-qseed = <0 0x00112233 0x66777777>; + qcom,sde-qos-lut-nrt = <0 0x00000000 0x00000000>; + qcom,sde-qos-lut-cwb = <0 0x75300000 0x00000000>; qcom,sde-cdp-setting = <1 1>, <1 0>; @@ -313,7 +296,6 @@ qcom,msm-bus,name = "mdss_reg"; qcom,msm-bus,num-cases = <4>; qcom,msm-bus,num-paths = <1>; - qcom,msm-bus,active-only; qcom,msm-bus,vectors-KBps = <1 590 0 0>, <1 590 0 76800>, @@ -442,7 +424,7 @@ qcom,mdss-inline-rot-qos-lut = <0x44556677 0x00112233 0x44556677 0x00112233>; qcom,mdss-inline-rot-danger-lut = <0x0055aaff 0x0000ffff>; - qcom,mdss-inline-rot-safe-lut = <0x0000f000 0x0000ff00>; + qcom,mdss-inline-rot-safe-lut = <0x0000f000 0x0000f000>; qcom,mdss-default-ot-rd-limit = <32>; qcom,mdss-default-ot-wr-limit = <32>; @@ -457,7 +439,6 @@ qcom,msm-bus,name = "mdss_rot_reg"; qcom,msm-bus,num-cases = <2>; qcom,msm-bus,num-paths = <1>; - qcom,msm-bus,active-only; qcom,msm-bus,vectors-KBps = <1 590 0 0>, <1 590 0 76800>; @@ -507,6 +488,19 @@ qcom,supply-disable-load = <4>; }; }; + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "refgen"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; }; mdss_dsi1: qcom,mdss_dsi_ctrl1@ae96000 { @@ -540,6 +534,19 @@ qcom,supply-disable-load = <4>; }; }; + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "refgen"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; }; mdss_dsi_phy0: qcom,mdss_dsi_phy0@ae94400 { @@ -661,16 +668,18 @@ qcom,aux-cfg0-settings = [20 00]; qcom,aux-cfg1-settings = [24 13]; qcom,aux-cfg2-settings = [28 24]; - qcom,aux-cfg3-settings = [2c 10]; + qcom,aux-cfg3-settings = [2c 00]; qcom,aux-cfg4-settings = [30 0a]; qcom,aux-cfg5-settings = [34 26]; qcom,aux-cfg6-settings = [38 0a]; qcom,aux-cfg7-settings = [3c 03]; - qcom,aux-cfg8-settings = [40 bb]; + qcom,aux-cfg8-settings = [40 b7]; qcom,aux-cfg9-settings = [44 03]; qcom,max-pclk-frequency-khz = <675000>; + qcom,mst-enable; + qcom,ctrl-supply-entries { #address-cells = <1>; #size-cells = <0>; @@ -698,5 +707,19 @@ qcom,supply-disable-load = <32>; }; }; + + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "refgen"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-sdx50m.dtsi b/arch/arm64/boot/dts/qcom/sm8150-sdx50m.dtsi index a47cac09c479d4e9ef7b7ca3ecba3810337ce4ec..c003c56ff41cb63595aa32f2d882b69310fd9039 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-sdx50m.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-sdx50m.dtsi @@ -22,7 +22,7 @@ qcom,mdm2ap-status-gpio = <&tlmm 142 0x00>; qcom,ap2mdm-status-gpio = <&tlmm 135 0x00>; qcom,ap2mdm-soft-reset-gpio = <&pm855l_gpios 9 0>; - qcom,mdm-link-info = "0304_00.01.00"; + qcom,mdm-link-info = "0305_01.01.00"; qcom,esoc-skip-restart-for-mdm-crash; status = "ok"; }; @@ -33,8 +33,34 @@ /* MDM PON conrol*/ pins = "gpio9"; function = "normal"; - output-low; power-source = <1>; /* 1.8V */ }; }; }; + +&wil6210 { + status = "disabled"; +}; + +&mhi_0 { + esoc-names = "mdm"; + esoc-0 = <&mdm3>; + qcom,smmu-cfg = <0x1d>; + qcom,addr-win = <0x0 0x20000000 0x0 0x3fffffff>; + mhi,fw-name = "sdx50m/sbl1.mbn"; + status = "okay"; +}; + +&pcie1 { + dma-coherent; +}; + +&soc { + imp: qcom,ipa-mhi-proxy { + compatible = "qcom,ipa-mhi-proxy"; + qcom,ctrl-iova = <0x00010000 0x0FFF0000>; + qcom,data-iova = <0x10000000 0x0FFFFFFF>; + qcom,mhi-chdb-base = <0x40300300>; + qcom,mhi-erdb-base = <0x40300700>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-smp2p.dtsi b/arch/arm64/boot/dts/qcom/sm8150-smp2p.dtsi index 060c5004082ffd8dc89a53345a454a7e2a0621d2..7026077f28f625710cf46bf4f3b2523bcc074521 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-smp2p.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-smp2p.dtsi @@ -32,6 +32,18 @@ interrupt-controller; #interrupt-cells = <2>; }; + + smp2p_ipa_1_out: qcom,smp2p-ipa-1-out { + qcom,entry-name = "ipa"; + #qcom,smem-state-cells = <1>; + }; + + /* ipa - inbound entry from mss */ + smp2p_ipa_1_in: qcom,smp2p-ipa-1-in { + qcom,entry-name = "ipa"; + interrupt-controller; + #interrupt-cells = <2>; + }; }; qcom,smp2p-adsp@1799000c { diff --git a/arch/arm64/boot/dts/qcom/sm8150-thermal-overlay.dtsi b/arch/arm64/boot/dts/qcom/sm8150-thermal-overlay.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..498979674519d6efd46e69f08fe475439d5e50ce --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150-thermal-overlay.dtsi @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2018, 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 + +&thermal_zones { + pm855b_tz { + cooling-maps { + trip0_bat { + trip = <&pm855b_trip0>; + cooling-device = + <&pm855b_charger (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip1_bat { + trip = <&pm855b_trip1>; + cooling-device = + <&pm855b_charger THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + pm855_tz { + cooling-maps { + trip0_cpu0 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU0 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu1 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU1 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu2 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU2 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu3 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU3 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu4 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU4 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu5 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU5 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu6 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU6 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip0_cpu7 { + trip = <&pm855_trip0>; + cooling-device = + <&CPU7 (THERMAL_MAX_LIMIT-1) + (THERMAL_MAX_LIMIT-1)>; + }; + trip1_cpu1 { + trip = <&pm855_trip1>; + cooling-device = + <&CPU1 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + trip1_cpu2 { + trip = <&pm855_trip1>; + cooling-device = + <&CPU2 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + trip1_cpu3 { + trip = <&pm855_trip1>; + cooling-device = + <&CPU3 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + trip1_cpu4 { + trip = <&pm855_trip1>; + cooling-device = + <&CPU4 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + trip1_cpu5 { + trip = <&pm855_trip1>; + cooling-device = + <&CPU5 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + trip1_cpu6 { + trip = <&pm855_trip1>; + cooling-device = + <&CPU6 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + trip1_cpu7 { + trip = <&pm855_trip1>; + cooling-device = + <&CPU7 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + soc { + cooling-maps { + soc_cpu4 { + trip = <&soc_trip>; + cooling-device = + <&CPU4 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + soc_cpu5 { + trip = <&soc_trip>; + cooling-device = + <&CPU5 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + soc_cpu6 { + trip = <&soc_trip>; + cooling-device = + <&CPU6 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + soc_cpu7 { + trip = <&soc_trip>; + cooling-device = + <&CPU7 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + pm855b-vbat-lvl0 { + cooling-maps { + vbat_cpu4 { + trip = <&vbat_lvl0>; + cooling-device = + <&CPU4 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + vbat_cpu5 { + trip = <&vbat_lvl0>; + cooling-device = + <&CPU5 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + vbat_cpu6 { + trip = <&vbat_lvl0>; + cooling-device = + <&CPU6 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + vbat_cpu7 { + trip = <&vbat_lvl0>; + cooling-device = + <&CPU7 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + pm855b-ibat-lvl0 { + cooling-maps { + ibat_cpu4 { + trip = <&ibat_lvl0>; + cooling-device = + <&CPU4 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + ibat_cpu5 { + trip = <&ibat_lvl0>; + cooling-device = + <&CPU5 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + ibat_cpu6 { + trip = <&ibat_lvl0>; + cooling-device = + <&CPU6 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + ibat_cpu7 { + trip = <&ibat_lvl0>; + cooling-device = + <&CPU7 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; + + pm855l-vph-lvl0 { + disable-thermal-zone; + cooling-maps { + vph_cpu4 { + trip = <&vph_lvl0>; + cooling-device = + <&CPU4 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + vph_cpu5 { + trip = <&vph_lvl0>; + cooling-device = + <&CPU5 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + vph_cpu6 { + trip = <&vph_lvl0>; + cooling-device = + <&CPU6 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + vph_cpu7 { + trip = <&vph_lvl0>; + cooling-device = + <&CPU7 THERMAL_MAX_LIMIT + THERMAL_MAX_LIMIT>; + }; + }; + }; +}; + +&mdss_mdp { + #cooling-cells = <2>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-thermal.dtsi b/arch/arm64/boot/dts/qcom/sm8150-thermal.dtsi index 43cd31adfaaa7242f461701e346fcb0cb75e6374..a58ef5406ffa30c139d59762a2ecadd4a24958cf 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-thermal.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-thermal.dtsi @@ -547,6 +547,11 @@ trip = <&aoss0_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&aoss0_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -604,6 +609,11 @@ trip = <&cpu00_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu00_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -661,6 +671,11 @@ trip = <&cpu01_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu01_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -718,6 +733,11 @@ trip = <&cpu02_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu02_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -775,6 +795,11 @@ trip = <&cpu03_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu03_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -832,6 +857,11 @@ trip = <&cpuss0_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpuss0_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -889,6 +919,11 @@ trip = <&cpuss1_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpuss1_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -946,6 +981,11 @@ trip = <&cpu10_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu10_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1003,6 +1043,11 @@ trip = <&cpu11_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu11_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1060,6 +1105,11 @@ trip = <&cpu12_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu12_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1117,6 +1167,11 @@ trip = <&cpu13_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu13_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1174,6 +1229,11 @@ trip = <&cpu14_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu14_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1231,6 +1291,11 @@ trip = <&cpu15_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu15_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1288,6 +1353,11 @@ trip = <&cpu16_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu16_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1345,6 +1415,11 @@ trip = <&cpu17_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cpu17_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1402,6 +1477,11 @@ trip = <&gpuss0_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&gpuss0_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1459,6 +1539,11 @@ trip = <&aoss1_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&aoss1_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1516,6 +1601,11 @@ trip = <&cwlan_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cwlan_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1573,6 +1663,11 @@ trip = <&video_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&video_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1630,6 +1725,11 @@ trip = <&ddr_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&ddr_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1687,6 +1787,11 @@ trip = <&q6_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&q6_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1744,6 +1849,11 @@ trip = <&camera_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&camera_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1801,6 +1911,11 @@ trip = <&cmpss_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&cmpss_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1858,6 +1973,11 @@ trip = <&mdm_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&mdm_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1915,6 +2035,11 @@ trip = <&npu_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&npu_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -1972,6 +2097,11 @@ trip = <&mdmv_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&mdmv_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -2029,6 +2159,11 @@ trip = <&mdms_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&mdms_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -2086,6 +2221,11 @@ trip = <&gpuss1_trip>; cooling-device = <&slpi_vdd 0 0>; }; + gpu_vdd_cdev { + trip = <&gpuss1_trip>; + cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-2) + (THERMAL_MAX_LIMIT-2)>; + }; }; }; @@ -2100,6 +2240,13 @@ type = "passive"; }; }; + cooling-maps { + gpu_cdev { + trip = <&gpu_trip0>; + cooling-device = <&msm_gpu THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; }; apc-0-max-step { @@ -2197,4 +2344,26 @@ }; }; }; + + npu-step { + polling-delay-passive = <10>; + polling-delay = <0>; + thermal-sensors = <&tsens1 8>; + thermal-governor = "step_wise"; + trips { + npu_trip0: npu-trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + }; + cooling-maps { + npu_cdev { + trip = <&npu_trip0>; + cooling-device = + <&msm_npu THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-usb.dtsi b/arch/arm64/boot/dts/qcom/sm8150-usb.dtsi index 38791aeea3da408e81cbdb05a1ca91ad6e5a89ab..5d167d90bd827ffdca64943f5180702f36fafe53 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-usb.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-usb.dtsi @@ -22,7 +22,6 @@ reg-names = "core_base"; iommus = <&apps_smmu 0x140 0x0>; - qcom,smmu-s1-bypass; #address-cells = <1>; #size-cells = <1>; ranges; @@ -56,15 +55,24 @@ qcom,dwc-usb3-msm-tx-fifo-size = <27696>; qcom,msm-bus,name = "usb0"; - qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-cases = <3>; qcom,msm-bus,num-paths = <3>; qcom,msm-bus,vectors-KBps = + /* suspend vote */ , , , + + /* nominal vote */ , , + , + + /* svs vote */ + , + , ; dwc3@a600000 { @@ -77,9 +85,11 @@ snps,has-lpm-erratum; snps,hird-threshold = /bits/ 8 <0x10>; snps,usb3_lpm_capable; + snps,ssp-u3-u0-quirk; + snps,usb3-u1u2-disable; usb-core-id = <0>; tx-fifo-resize; - maximum-speed = "super-speed"; + maximum-speed = "super-speed-plus"; dr_mode = "otg"; }; @@ -152,7 +162,6 @@ USB3_DP_QSERDES_COM_SSC_STEP_SIZE1_MODE1 0xde 0 USB3_DP_QSERDES_COM_SSC_STEP_SIZE2_MODE1 0x07 0 USB3_DP_QSERDES_COM_SYSCLK_BUF_ENABLE 0x0a 0 - USB3_DP_QSERDES_COM_CMN_IPTRIM 0x14 0 USB3_DP_QSERDES_COM_CP_CTRL_MODE0 0x06 0 USB3_DP_QSERDES_COM_CP_CTRL_MODE1 0x06 0 USB3_DP_QSERDES_COM_PLL_RCTRL_MODE0 0x16 0 @@ -173,22 +182,23 @@ USB3_DP_QSERDES_COM_DIV_FRAC_START1_MODE1 0xab 0 USB3_DP_QSERDES_COM_DIV_FRAC_START2_MODE1 0xea 0 USB3_DP_QSERDES_COM_DIV_FRAC_START3_MODE1 0x02 0 - USB3_DP_QSERDES_COM_VCO_TUNE_CTRL 0x00 0 - USB3_DP_QSERDES_COM_VCO_TUNE_MAP 0x02 0 USB3_DP_QSERDES_COM_VCO_TUNE1_MODE0 0x24 0 USB3_DP_QSERDES_COM_VCO_TUNE1_MODE1 0x24 0 USB3_DP_QSERDES_COM_VCO_TUNE2_MODE1 0x02 0 USB3_DP_QSERDES_COM_HSCLK_SEL 0x01 0 USB3_DP_QSERDES_COM_CORECLK_DIV_MODE1 0x08 0 + USB3_DP_QSERDES_COM_SVS_MODE_CLK_SEL 0x5 0 USB3_DP_QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0xca 0 USB3_DP_QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1e 0 USB3_DP_QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0xca 0 USB3_DP_QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1e 0 USB3_DP_QSERDES_COM_BIN_VCOCAL_HSCLK_SEL 0x11 0 - USB3_DP_QSERDES_TXA_RES_CODE_LANE_TX 0xd4 0 - USB3_DP_QSERDES_TXA_RES_CODE_LANE_RX 0xd0 0 + USB3_DP_QSERDES_COM_VCO_TUNE_MAP 0x02 0 + USB3_DP_QSERDES_COM_CMN_IPTRIM 0x20 0 USB3_DP_QSERDES_TXA_LANE_MODE_1 0x05 0 USB3_DP_QSERDES_TXA_RCV_DETECT_LVL_2 0x12 0 + USB3_DP_QSERDES_TXA_RES_CODE_LANE_TX 0xe4 0 + USB3_DP_QSERDES_TXA_RES_CODE_LANE_RX 0xd0 0 USB3_DP_QSERDES_RXA_UCDR_SO_GAIN 0x04 0 USB3_DP_QSERDES_RXA_UCDR_FASTLOCK_FO_GAIN 0x2f 0 USB3_DP_QSERDES_RXA_UCDR_SO_SATURATION_AND_ENABLE 0x7f 0 @@ -199,32 +209,34 @@ USB3_DP_QSERDES_RXA_UCDR_SB2_THRESH2 0x08 0 USB3_DP_QSERDES_RXA_UCDR_SB2_GAIN1 0x05 0 USB3_DP_QSERDES_RXA_UCDR_SB2_GAIN2 0x05 0 - USB3_DP_QSERDES_RXA_VGA_CAL_CNTRL2 0x0f 0 + USB3_DP_QSERDES_RXA_VGA_CAL_CNTRL1 0x54 0 + USB3_DP_QSERDES_RXA_VGA_CAL_CNTRL2 0x08 0 + USB3_DP_QSERDES_RXA_GM_CAL 0x1f 0 USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL2 0x0f 0 USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL3 0x4a 0 - USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL4 0x08 0 + USB3_DP_QSERDES_RXA_RX_EQU_ADAPTOR_CNTRL4 0x0a 0 USB3_DP_QSERDES_RXA_RX_IDAC_TSETTLE_LOW 0xc0 0 USB3_DP_QSERDES_RXA_RX_IDAC_TSETTLE_HIGH 0x00 0 USB3_DP_QSERDES_RXA_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x77 0 USB3_DP_QSERDES_RXA_RX_OFFSET_ADAPTOR_CNTRL2 0x80 0 USB3_DP_QSERDES_RXA_SIGDET_CNTRL 0x04 0 USB3_DP_QSERDES_RXA_SIGDET_DEGLITCH_CNTRL 0x0e 0 - USB3_DP_QSERDES_RXA_RX_MODE_00_LOW 0x36 0 - USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH 0x36 0 - USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH2 0xb6 0 - USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH3 0x17 0 - USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH4 0x7c 0 - USB3_DP_QSERDES_RXA_RX_MODE_01_LOW 0xfc 0 - USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH 0xfc 0 - USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH2 0xff 0 - USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH3 0x23 0 - USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH4 0x34 0 + USB3_DP_QSERDES_RXA_RX_MODE_00_LOW 0x3f 0 + USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH 0xbf 0 + USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH2 0x3f 0 + USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH3 0x3f 0 + USB3_DP_QSERDES_RXA_RX_MODE_00_HIGH4 0x8a 0 + USB3_DP_QSERDES_RXA_RX_MODE_01_LOW 0xdc 0 + USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH 0xdc 0 + USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH2 0x5c 0 + USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH3 0x0b 0 + USB3_DP_QSERDES_RXA_RX_MODE_01_HIGH4 0xb3 0 USB3_DP_QSERDES_RXA_DFE_EN_TIMER 0x04 0 USB3_DP_QSERDES_RXA_DFE_CTLE_POST_CAL_OFFSET 0x30 0 - USB3_DP_QSERDES_TXB_RES_CODE_LANE_TX 0xd4 0 - USB3_DP_QSERDES_TXB_RES_CODE_LANE_RX 0xd0 0 USB3_DP_QSERDES_TXB_LANE_MODE_1 0x05 0 USB3_DP_QSERDES_TXB_RCV_DETECT_LVL_2 0x12 0 + USB3_DP_QSERDES_TXB_RES_CODE_LANE_TX 0xe4 0 + USB3_DP_QSERDES_TXB_RES_CODE_LANE_RX 0xd0 0 USB3_DP_QSERDES_RXB_UCDR_SO_GAIN 0x04 0 USB3_DP_QSERDES_RXB_UCDR_FASTLOCK_FO_GAIN 0x2f 0 USB3_DP_QSERDES_RXB_UCDR_SO_SATURATION_AND_ENABLE 0x7f 0 @@ -235,26 +247,28 @@ USB3_DP_QSERDES_RXB_UCDR_SB2_THRESH2 0x08 0 USB3_DP_QSERDES_RXB_UCDR_SB2_GAIN1 0x05 0 USB3_DP_QSERDES_RXB_UCDR_SB2_GAIN2 0x05 0 - USB3_DP_QSERDES_RXB_VGA_CAL_CNTRL2 0x0f 0 + USB3_DP_QSERDES_RXB_VGA_CAL_CNTRL1 0x54 0 + USB3_DP_QSERDES_RXB_VGA_CAL_CNTRL2 0x08 0 + USB3_DP_QSERDES_RXB_GM_CAL 0x1f 0 USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL2 0x0f 0 USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL3 0x4a 0 - USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL4 0x08 0 + USB3_DP_QSERDES_RXB_RX_EQU_ADAPTOR_CNTRL4 0x0a 0 USB3_DP_QSERDES_RXB_RX_IDAC_TSETTLE_LOW 0xc0 0 USB3_DP_QSERDES_RXB_RX_IDAC_TSETTLE_HIGH 0x00 0 USB3_DP_QSERDES_RXB_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x77 0 USB3_DP_QSERDES_RXB_RX_OFFSET_ADAPTOR_CNTRL2 0x80 0 USB3_DP_QSERDES_RXB_SIGDET_CNTRL 0x04 0 USB3_DP_QSERDES_RXB_SIGDET_DEGLITCH_CNTRL 0x0e 0 - USB3_DP_QSERDES_RXB_RX_MODE_00_LOW 0x36 0 - USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH 0x36 0 - USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH2 0xb6 0 - USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH3 0x17 0 - USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH4 0x7c 0 - USB3_DP_QSERDES_RXB_RX_MODE_01_LOW 0xfc 0 - USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH 0xfc 0 - USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH2 0xff 0 - USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH3 0x23 0 - USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH4 0x34 0 + USB3_DP_QSERDES_RXB_RX_MODE_00_LOW 0x3f 0 + USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH 0xbf 0 + USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH2 0x3f 0 + USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH3 0x3f 0 + USB3_DP_QSERDES_RXB_RX_MODE_00_HIGH4 0x8a 0 + USB3_DP_QSERDES_RXB_RX_MODE_01_LOW 0xdc 0 + USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH 0xdc 0 + USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH2 0x5c 0 + USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH3 0x0b 0 + USB3_DP_QSERDES_RXB_RX_MODE_01_HIGH4 0xb3 0 USB3_DP_QSERDES_RXB_DFE_EN_TIMER 0x04 0 USB3_DP_QSERDES_RXB_DFE_CTLE_POST_CAL_OFFSET 0x30 0 USB3_DP_PCS_LOCK_DETECT_CONFIG1 0xd0 0 @@ -265,11 +279,7 @@ USB3_DP_PCS_ALIGN_DETECT_CONFIG2 0x13 0 USB3_DP_PCS_EQ_CONFIG1 0x0d 0 USB3_DP_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0xf8 0 - USB3_DP_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x55 0 - USB3_DP_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x30 0 - USB3_DP_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x05 0 - USB3_DP_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x15 0 - USB3_DP_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x04 0 + USB3_DP_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x07 0 0xffffffff 0xffffffff 0x00>; qcom,qmp-phy-reg-offset = @@ -303,7 +313,7 @@ usb_audio_qmi_dev { compatible = "qcom,usb-audio-qmi-dev"; - iommus = <&apps_smmu 0x182f 0x0>; + iommus = <&apps_smmu 0x1b2f 0x0>; qcom,usb-audio-stream-id = <0xf>; qcom,usb-audio-intr-num = <2>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-vidc.dtsi b/arch/arm64/boot/dts/qcom/sm8150-vidc.dtsi index bc406ad1a75703d34c1ee1e0dde28057298d5ddd..b95c24621064cedce27cbaeda335fae2b6f287dc 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-vidc.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-vidc.dtsi @@ -31,22 +31,22 @@ cvp-supply = <&mvs1_gdsc>; /* Clocks */ - clock-names = "core_clk", "iface_clk", - "vcodec_clk", "cvp_clk", "gcc_video_axi0", - "gcc_video_axi1", "gcc_video_axic"; - clocks = <&clock_videocc VIDEO_CC_MVSC_CORE_CLK>, - <&clock_videocc VIDEO_CC_IRIS_AHB_CLK>, - <&clock_videocc VIDEO_CC_MVS0_CORE_CLK>, - <&clock_videocc VIDEO_CC_MVS1_CORE_CLK>, + clock-names = "gcc_video_axic", "gcc_video_axi0", + "gcc_video_axi1", "core_clk", "vcodec_clk", + "cvp_clk"; + clocks = <&clock_gcc GCC_VIDEO_AXIC_CLK>, <&clock_gcc GCC_VIDEO_AXI0_CLK>, <&clock_gcc GCC_VIDEO_AXI1_CLK>, - <&clock_gcc GCC_VIDEO_AXIC_CLK>; - qcom,proxy-clock-names = "core_clk", "iface_clk", - "vcodec_clk", "cvp_clk", "gcc_video_axi0", - "gcc_video_axi1", "gcc_video_axic"; - qcom,clock-configs = <0x1 0x0 0x1 0x1 0x0 0x0 0x0>; - qcom,allowed-clock-rates = <200000000 225000000 - 300000000 365000000 432000000 480000000>; + <&clock_videocc VIDEO_CC_MVSC_CORE_CLK>, + <&clock_videocc VIDEO_CC_MVS0_CORE_CLK>, + <&clock_videocc VIDEO_CC_MVS1_CORE_CLK>; + qcom,proxy-clock-names = "gcc_video_axic", + "gcc_video_axi0", "gcc_video_axi1", + "core_clk", "vcodec_clk", "cvp_clk"; + + qcom,clock-configs = <0x0 0x0 0x0 0x1 0x1 0x1>; + qcom,allowed-clock-rates = <225000000 300000000 + 365000000 432000000 480000000>; /* Buses */ bus_cnoc { @@ -63,7 +63,7 @@ label = "venus-ddr"; qcom,bus-master = ; qcom,bus-slave = ; - qcom,bus-governor = "performance"; + qcom,bus-governor = "msm-vidc-ddr"; qcom,bus-range-kbps = <1000 6533000>; }; arm9_bus_ddr { @@ -79,7 +79,7 @@ label = "venus-llcc"; qcom,bus-master = ; qcom,bus-slave = ; - qcom,bus-governor = "performance"; + qcom,bus-governor = "msm-vidc-llcc"; qcom,bus-range-kbps = <1000 1326000>; }; @@ -88,8 +88,7 @@ compatible = "qcom,msm-vidc,context-bank"; label = "venus_ns"; iommus = - <&apps_smmu 0x1300 0x20>, - <&apps_smmu 0x1340 0x0>; + <&apps_smmu 0x1300 0x60>; buffer-types = <0xfff>; virtual-addr-pool = <0x70800000 0x6f800000>; }; @@ -118,8 +117,7 @@ compatible = "qcom,msm-vidc,context-bank"; label = "venus_sec_non_pixel"; iommus = - <&apps_smmu 0x1304 0x20>, - <&apps_smmu 0x1344 0x0>; + <&apps_smmu 0x1304 0x60>; buffer-types = <0x480>; virtual-addr-pool = <0x1000000 0x24800000>; qcom,secure-context-bank; diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi index 8e6abaca41ae48819a303bfa9e07240c23e1b110..1fc7ec32a9d0cce458ccb550ad365b80aa13fc43 100644 --- a/arch/arm64/boot/dts/qcom/sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi @@ -27,12 +27,13 @@ #include #define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024)) +#define BW_OPP_ENTRY(mhz, w) opp-mhz {opp-hz = /bits/ 64 ;} / { model = "Qualcomm Technologies, Inc. SM8150"; compatible = "qcom,sm8150"; qcom,msm-name = "SM8150"; - qcom,msm-id = <339 0x0>; + qcom,msm-id = <339 0x10000>; interrupt-parent = <&pdc>; aliases { @@ -40,6 +41,9 @@ sdhc2 = &sdhc_2; /* SDC2 SD card slot */ pci-domain0 = &pcie0; /* PCIe0 domain */ pci-domain1 = &pcie1; /* PCIe1 domain */ + mhi0 = &mhi_0; + mhi_netdev0 = &mhi_netdev_0; + mhi_netdev1 = &mhi_netdev_1; }; aliases { @@ -87,7 +91,7 @@ qcom,dump-size = <0x9000>; }; - L1_TLB_0: l1-tlb { + L2_TLB_0: l2-tlb { qcom,dump-size = <0x5000>; }; }; @@ -120,7 +124,7 @@ qcom,dump-size = <0x9000>; }; - L1_TLB_100: l1-tlb { + L2_TLB_100: l2-tlb { qcom,dump-size = <0x5000>; }; }; @@ -153,7 +157,7 @@ qcom,dump-size = <0x9000>; }; - L1_TLB_200: l1-tlb { + L2_TLB_200: l2-tlb { qcom,dump-size = <0x5000>; }; }; @@ -186,7 +190,7 @@ qcom,dump-size = <0x9000>; }; - L1_TLB_300: l1-tlb { + L2_TLB_300: l2-tlb { qcom,dump-size = <0x5000>; }; }; @@ -207,6 +211,7 @@ cache-size = <0x40000>; cache-level = <2>; next-level-cache = <&L3_0>; + qcom,dump-size = <0x88000>; }; L1_I_400: l1-icache { @@ -219,7 +224,15 @@ qcom,dump-size = <0x12000>; }; - L1_TLB_400: l1-tlb { + L1_ITLB_400: l1-itlb { + qcom,dump-size = <0x300>; + }; + + L1_DTLB_400: l1-dtlb { + qcom,dump-size = <0x480>; + }; + + L2_TLB_400: l2-tlb { qcom,dump-size = <0x7800>; }; }; @@ -240,6 +253,7 @@ cache-size = <0x40000>; cache-level = <2>; next-level-cache = <&L3_0>; + qcom,dump-size = <0x88000>; }; L1_I_500: l1-icache { @@ -252,7 +266,15 @@ qcom,dump-size = <0x12000>; }; - L1_TLB_500: l1-tlb { + L1_ITLB_500: l1-itlb { + qcom,dump-size = <0x300>; + }; + + L1_DTLB_500: l1-dtlb { + qcom,dump-size = <0x480>; + }; + + L2_TLB_500: l2-tlb { qcom,dump-size = <0x7800>; }; }; @@ -273,6 +295,7 @@ cache-size = <0x40000>; cache-level = <2>; next-level-cache = <&L3_0>; + qcom,dump-size = <0x88000>; }; L1_I_600: l1-icache { @@ -285,7 +308,15 @@ qcom,dump-size = <0x12000>; }; - L1_TLB_600: l1-tlb { + L1_ITLB_600: l1-itlb { + qcom,dump-size = <0x300>; + }; + + L1_DTLB_600: l1-dtlb { + qcom,dump-size = <0x480>; + }; + + L2_TLB_600: l2-tlb { qcom,dump-size = <0x7800>; }; }; @@ -306,6 +337,7 @@ cache-size = <0x80000>; cache-level = <2>; next-level-cache = <&L3_0>; + qcom,dump-size = <0x110000>; }; L1_I_700: l1-icache { @@ -318,7 +350,15 @@ qcom,dump-size = <0x12000>; }; - L1_TLB_700: l1-tlb { + L1_ITLB_700: l1-itlb { + qcom,dump-size = <0x300>; + }; + + L1_DTLB_700: l1-dtlb { + qcom,dump-size = <0x480>; + }; + + L2_TLB_700: l2-tlb { qcom,dump-size = <0x7800>; }; }; @@ -643,12 +683,22 @@ reg = <0 0x9e400000 0 0x1400000>; }; + cont_splash_memory: cont_splash_region@9c000000 { + reg = <0x0 0x9c000000 0x0 0x02400000>; + label = "cont_splash_region"; + }; + + disp_rdump_memory: disp_rdump_region@9c000000 { + reg = <0x0 0x9c000000 0x0 0x00800000>; + label = "disp_rdump_region"; + }; + adsp_mem: adsp_region { compatible = "shared-dma-pool"; alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; reusable; alignment = <0x0 0x400000>; - size = <0x0 0xc00000>; + size = <0x0 0x1000000>; }; qseecom_ta_mem: qseecom_ta_region { @@ -667,12 +717,12 @@ size = <0x0 0x800000>; }; - secure_display_memory: secure_display_region { + secure_display_memory: secure_display_region { /* Secure UI */ compatible = "shared-dma-pool"; alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; reusable; alignment = <0x0 0x400000>; - size = <0x0 0x5c00000>; + size = <0x0 0x7800000>; }; dump_mem: mem_dump_region { @@ -728,6 +778,14 @@ status = "ok"; }; + qcom,spcom { + compatible = "qcom,spcom"; + + /* predefined channels, remote side is server */ + qcom,spcom-ch-names = "sp_kernel", "sp_ssr"; + status = "ok"; + }; + jtag_mm0: jtagmm@7040000 { compatible = "qcom,jtagv8-mm"; reg = <0x7040000 0x1000>; @@ -910,19 +968,23 @@ reg-names = "lagg-base"; }; + llcc_bw_opp_table: llcc-bw-opp-table { + compatible = "operating-points-v2"; + BW_OPP_ENTRY( 150, 16); /* 2288 MB/s */ + BW_OPP_ENTRY( 200, 16); /* 3051 MB/s */ + BW_OPP_ENTRY( 403, 16); /* 6149 MB/s */ + BW_OPP_ENTRY( 533, 16); /* 8132 MB/s */ + BW_OPP_ENTRY( 666, 16); /* 10162 MB/s */ + BW_OPP_ENTRY( 777, 16); /* 11856 MB/s */ + }; + cpu_cpu_llcc_bw: qcom,cpu-cpu-llcc-bw { compatible = "qcom,devbw"; governor = "performance"; qcom,src-dst-ports = ; qcom,active-only; - qcom,bw-tbl = - < MHZ_TO_MBPS(150, 16) >, /* 2288 MB/s */ - < MHZ_TO_MBPS(200, 16) >, /* 4577 MB/s */ - < MHZ_TO_MBPS(403, 16) >, /* 6149 MB/s */ - < MHZ_TO_MBPS(533, 16) >, /* 8132 MB/s */ - < MHZ_TO_MBPS(666, 16) >, /* 10162 MB/s */ - < MHZ_TO_MBPS(777, 16) >; /* 11856 MB/s */ + operating-points-v2 = <&llcc_bw_opp_table>; }; cpu_cpu_llcc_bwmon: qcom,cpu-cpu-llcc-bwmon@90b6400 { @@ -936,24 +998,28 @@ qcom,count-unit = <0x10000>; }; + ddr_bw_opp_table: ddr-bw-opp-table { + compatible = "operating-points-v2"; + BW_OPP_ENTRY( 200, 4); /* 762 MB/s */ + BW_OPP_ENTRY( 300, 4); /* 1144 MB/s */ + BW_OPP_ENTRY( 451, 4); /* 1720 MB/s */ + BW_OPP_ENTRY( 547, 4); /* 2086 MB/s */ + BW_OPP_ENTRY( 681, 4); /* 2597 MB/s */ + BW_OPP_ENTRY( 768, 4); /* 2929 MB/s */ + BW_OPP_ENTRY(1017, 4); /* 3879 MB/s */ + BW_OPP_ENTRY(1296, 4); /* 4943 MB/s */ + BW_OPP_ENTRY(1555, 4); /* 5931 MB/s */ + BW_OPP_ENTRY(1804, 4); /* 6881 MB/s */ + BW_OPP_ENTRY(2092, 4); /* 7980 MB/s */ + }; + cpu_llcc_ddr_bw: qcom,cpu-llcc-ddr-bw { compatible = "qcom,devbw"; governor = "performance"; qcom,src-dst-ports = ; qcom,active-only; - qcom,bw-tbl = - < MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */ - < MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */ - < MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */ - < MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */ - < MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */ - < MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */ - < MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */ - < MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */ - < MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */ - < MHZ_TO_MBPS(1804, 4) >, /* 6881 MB/s */ - < MHZ_TO_MBPS(2092, 4) >; /* 7980 MB/s */ + operating-points-v2 = <&ddr_bw_opp_table>; }; cpu_llcc_ddr_bwmon: qcom,cpu-llcc-ddr-bwmon@90cd000 { @@ -966,23 +1032,27 @@ qcom,count-unit = <0x10000>; }; + suspendable_ddr_bw_opp_table: suspendable-ddr-bw-opp-table { + compatible = "operating-points-v2"; + BW_OPP_ENTRY( 0, 4); /* 0 MB/s */ + BW_OPP_ENTRY( 200, 4); /* 762 MB/s */ + BW_OPP_ENTRY( 300, 4); /* 1144 MB/s */ + BW_OPP_ENTRY( 451, 4); /* 1720 MB/s */ + BW_OPP_ENTRY( 547, 4); /* 2086 MB/s */ + BW_OPP_ENTRY( 681, 4); /* 2597 MB/s */ + BW_OPP_ENTRY( 768, 4); /* 2929 MB/s */ + BW_OPP_ENTRY(1017, 4); /* 3879 MB/s */ + BW_OPP_ENTRY(1296, 4); /* 4943 MB/s */ + BW_OPP_ENTRY(1555, 4); /* 5931 MB/s */ + BW_OPP_ENTRY(1804, 4); /* 6881 MB/s */ + BW_OPP_ENTRY(2092, 4); /* 7980 MB/s */ + }; + npu_npu_ddr_bw: qcom,npu-npu-ddr-bw { compatible = "qcom,devbw"; governor = "performance"; qcom,src-dst-ports = ; - qcom,bw-tbl = - < MHZ_TO_MBPS( 0, 4) >, /* 0 MB/s */ - < MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */ - < MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */ - < MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */ - < MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */ - < MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */ - < MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */ - < MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */ - < MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */ - < MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */ - < MHZ_TO_MBPS(1804, 4) >, /* 6881 MB/s */ - < MHZ_TO_MBPS(2092, 4) >; /* 7980 MB/s */ + operating-points-v2 = <&suspendable_ddr_bw_opp_table>; }; npu_npu_ddr_bwmon: qcom,npu-npu-ddr-bwmon@9960300 { @@ -1017,11 +1087,13 @@ qcom,cachemiss-ev = <0x17>; qcom,core-dev-table = < 300000 300000000 >, - < 576000 576000000 >, - < 672000 768000000 >, - < 864000 960000000 >, - < 1171200 1228800000 >, - < 1267200 1344000000 >; + < 480000 403200000 >, + < 672000 480000000 >, + < 768000 576000000 >, + < 864000 672000000 >, + < 979200 768000000 >, + < 1075200 864000000 >, + < 1267200 960000000 >; }; cpu4_cpu_l3_lat: qcom,cpu4-cpu-l3-lat { @@ -1038,11 +1110,11 @@ qcom,cachemiss-ev = <0x17>; qcom,core-dev-table = < 300000 300000000 >, - < 576000 576000000 >, - < 768000 768000000 >, - < 960000 960000000 >, - < 1248000 1228800000 >, - < 1593600 1344000000 >; + < 768000 576000000 >, + < 1152000 768000000 >, + < 1344000 960000000 >, + < 1689600 1228800000 >, + < 2016000 1344000000 >; }; cpu0_cpu_llcc_lat: qcom,cpu0-cpu-llcc-lat { @@ -1051,13 +1123,7 @@ qcom,src-dst-ports = ; qcom,active-only; - qcom,bw-tbl = - < MHZ_TO_MBPS(150, 16) >, /* 2288 MB/s */ - < MHZ_TO_MBPS(200, 16) >, /* 4577 MB/s */ - < MHZ_TO_MBPS(403, 16) >, /* 6149 MB/s */ - < MHZ_TO_MBPS(533, 16) >, /* 8132 MB/s */ - < MHZ_TO_MBPS(666, 16) >, /* 10162 MB/s */ - < MHZ_TO_MBPS(777, 16) >; /* 11856 MB/s */ + operating-points-v2 = <&llcc_bw_opp_table>; }; cpu0_cpu_llcc_latmon: qcom,cpu0-cpu-llcc-latmon { @@ -1067,11 +1133,9 @@ qcom,cachemiss-ev = <0x2A>; qcom,core-dev-table = < 300000 MHZ_TO_MBPS(150, 16) >, - < 576000 MHZ_TO_MBPS(200, 16) >, - < 672000 MHZ_TO_MBPS(403, 16) >, - < 864000 MHZ_TO_MBPS(533, 16) >, - < 1171200 MHZ_TO_MBPS(666, 16) >, - < 1267200 MHZ_TO_MBPS(777, 16) >; + < 768000 MHZ_TO_MBPS(200, 16) >, + < 1075200 MHZ_TO_MBPS(403, 16) >, + < 1267200 MHZ_TO_MBPS(403, 16) >; }; cpu4_cpu_llcc_lat: qcom,cpu4-cpu-llcc-lat { @@ -1080,13 +1144,7 @@ qcom,src-dst-ports = ; qcom,active-only; - qcom,bw-tbl = - < MHZ_TO_MBPS(150, 16) >, /* 2288 MB/s */ - < MHZ_TO_MBPS(200, 16) >, /* 4577 MB/s */ - < MHZ_TO_MBPS(403, 16) >, /* 6149 MB/s */ - < MHZ_TO_MBPS(533, 16) >, /* 8132 MB/s */ - < MHZ_TO_MBPS(666, 16) >, /* 10162 MB/s */ - < MHZ_TO_MBPS(777, 16) >; /* 11856 MB/s */ + operating-points-v2 = <&llcc_bw_opp_table>; }; cpu4_cpu_llcc_latmon: qcom,cpu4-cpu-llcc-latmon { @@ -1098,9 +1156,10 @@ < 300000 MHZ_TO_MBPS(150, 16) >, < 576000 MHZ_TO_MBPS(200, 16) >, < 768000 MHZ_TO_MBPS(403, 16) >, - < 960000 MHZ_TO_MBPS(533, 16) >, - < 1248000 MHZ_TO_MBPS(666, 16) >, - < 1593600 MHZ_TO_MBPS(777, 16) >; + < 960000 MHZ_TO_MBPS(403, 16) >, + < 1248000 MHZ_TO_MBPS(533, 16) >, + < 1728000 MHZ_TO_MBPS(666, 16) >, + < 2016000 MHZ_TO_MBPS(777, 16) >; }; cpu0_llcc_ddr_lat: qcom,cpu0-llcc-ddr-lat { @@ -1109,18 +1168,7 @@ qcom,src-dst-ports = ; qcom,active-only; - qcom,bw-tbl = - < MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */ - < MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */ - < MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */ - < MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */ - < MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */ - < MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */ - < MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */ - < MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */ - < MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */ - < MHZ_TO_MBPS(1804, 4) >, /* 6881 MB/s */ - < MHZ_TO_MBPS(2092, 4) >; /* 7980 MB/s */ + operating-points-v2 = <&ddr_bw_opp_table>; }; cpu0_llcc_ddr_latmon: qcom,cpu0-llcc-ddr-latmon { @@ -1130,11 +1178,9 @@ qcom,cachemiss-ev = <0x1000>; qcom,core-dev-table = < 300000 MHZ_TO_MBPS( 200, 4) >, - < 576000 MHZ_TO_MBPS( 451, 4) >, - < 672000 MHZ_TO_MBPS( 768, 4) >, - < 864000 MHZ_TO_MBPS(1017, 4) >, - < 1171200 MHZ_TO_MBPS(1555, 4) >, - < 1267200 MHZ_TO_MBPS(1804, 4) >; + < 768000 MHZ_TO_MBPS( 451, 4) >, + < 1075200 MHZ_TO_MBPS( 547, 4) >, + < 1267200 MHZ_TO_MBPS( 768, 4) >; }; cpu4_llcc_ddr_lat: qcom,cpu4-llcc-ddr-lat { @@ -1143,18 +1189,7 @@ qcom,src-dst-ports = ; qcom,active-only; - qcom,bw-tbl = - < MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */ - < MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */ - < MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */ - < MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */ - < MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */ - < MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */ - < MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */ - < MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */ - < MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */ - < MHZ_TO_MBPS(1804, 4) >, /* 6881 MB/s */ - < MHZ_TO_MBPS(2092, 4) >; /* 7980 MB/s */ + operating-points-v2 = <&ddr_bw_opp_table>; }; cpu4_llcc_ddr_latmon: qcom,cpu4-llcc-ddr-latmon { @@ -1165,11 +1200,12 @@ qcom,core-dev-table = < 300000 MHZ_TO_MBPS( 200, 4) >, < 576000 MHZ_TO_MBPS( 451, 4) >, - < 768000 MHZ_TO_MBPS( 768, 4) >, - < 960000 MHZ_TO_MBPS(1017, 4) >, - < 1248000 MHZ_TO_MBPS(1555, 4) >, - < 1593600 MHZ_TO_MBPS(1804, 4) >, - < 1689600 MHZ_TO_MBPS(2092, 4) >; + < 768000 MHZ_TO_MBPS( 547, 4) >, + < 960000 MHZ_TO_MBPS( 768, 4) >, + < 1248000 MHZ_TO_MBPS(1017, 4) >, + < 1728000 MHZ_TO_MBPS(1555, 4) >, + < 2016000 MHZ_TO_MBPS(1804, 4) >, + < 2054400 MHZ_TO_MBPS(2092, 4) >; }; cpu4_cpu_ddr_latfloor: qcom,cpu4-cpu-ddr-latfloor { @@ -1178,18 +1214,7 @@ qcom,src-dst-ports = ; qcom,active-only; - qcom,bw-tbl = - < MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */ - < MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */ - < MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */ - < MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */ - < MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */ - < MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */ - < MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */ - < MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */ - < MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */ - < MHZ_TO_MBPS(1804, 4) >, /* 6881 MB/s */ - < MHZ_TO_MBPS(2092, 4) >; /* 7980 MB/s */ + operating-points-v2 = <&ddr_bw_opp_table>; }; cpu4_computemon: qcom,cpu4-computemon { @@ -1271,13 +1296,35 @@ clock-frequency = <32768>; }; + bus_proxy_client: qcom,bus_proxy_client { + compatible = "qcom,bus-proxy-client"; + qcom,msm-bus,name = "bus-proxy-client"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <3>; + qcom,msm-bus,vectors-KBps = + , + , + , + , + , + ; + status = "ok"; + }; + + keepalive_opp_table: keepalive-opp-table { + compatible = "operating-points-v2"; + opp-1 { + opp-hz = /bits/ 64 < 1 >; + }; + }; + snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive { compatible = "qcom,devbw"; governor = "powersave"; qcom,src-dst-ports = <1 627>; qcom,active-only; status = "ok"; - qcom,bw-tbl = < 1 >; + operating-points-v2 = <&keepalive_opp_table>; }; cdsp_keepalive: qcom,cdsp_keepalive { @@ -1286,7 +1333,7 @@ qcom,src-dst-ports = <154 10070>; qcom,active-only; status = "ok"; - qcom,bw-tbl = < 1 >; + operating-points-v2 = <&keepalive_opp_table>; }; clock_rpmh: qcom,rpmhclk { @@ -1389,6 +1436,11 @@ #reset-cells = <1>; }; + cpucc_debug: syscon@182a0018 { + compatible = "syscon"; + reg = <0x182a0018 0x4>; + }; + clock_cpucc: qcom,cpucc { compatible = "qcom,clk-cpu-osm"; reg = <0x18321000 0x1400>, @@ -1410,6 +1462,7 @@ qcom,dispcc = <&clock_dispcc>; qcom,npucc = <&clock_npucc>; qcom,gpucc = <&clock_gpucc>; + qcom,cpucc = <&cpucc_debug>; clock-names = "xo_clk_src"; clocks = <&clock_rpmh RPMH_CXO_CLK>; #clock-cells = <1>; @@ -1533,7 +1586,7 @@ mbox-names = "adsp-pil"; }; - qcom,ssc@5c00000 { + pil_ssc: qcom,ssc@5c00000 { compatible = "qcom,pil-tz-generic"; reg = <0x5c00000 0x4000>; @@ -1864,43 +1917,103 @@ qcom,dump-id = <0x87>; }; - qcom,l1_tlb_dump0 { - qcom,dump-node = <&L1_TLB_0>; + qcom,l1_i_tlb_dump400 { + qcom,dump-node = <&L1_ITLB_400>; + qcom,dump-id = <0x24>; + }; + + qcom,l1_i_tlb_dump500 { + qcom,dump-node = <&L1_ITLB_500>; + qcom,dump-id = <0x25>; + }; + + qcom,l1_i_tlb_dump600 { + qcom,dump-node = <&L1_ITLB_600>; + qcom,dump-id = <0x26>; + }; + + qcom,l1_i_tlb_dump700 { + qcom,dump-node = <&L1_ITLB_700>; + qcom,dump-id = <0x27>; + }; + + qcom,l1_d_tlb_dump400 { + qcom,dump-node = <&L1_DTLB_400>; + qcom,dump-id = <0x44>; + }; + + qcom,l1_d_tlb_dump500 { + qcom,dump-node = <&L1_DTLB_500>; + qcom,dump-id = <0x45>; + }; + + qcom,l1_d_tlb_dump600 { + qcom,dump-node = <&L1_DTLB_600>; + qcom,dump-id = <0x46>; + }; + + qcom,l1_d_tlb_dump700 { + qcom,dump-node = <&L1_DTLB_700>; + qcom,dump-id = <0x47>; + }; + + qcom,l2_cache_dump400 { + qcom,dump-node = <&L2_4>; + qcom,dump-id = <0xc4>; + }; + + qcom,l2_cache_dump500 { + qcom,dump-node = <&L2_5>; + qcom,dump-id = <0xc5>; + }; + + qcom,l2_cache_dump600 { + qcom,dump-node = <&L2_6>; + qcom,dump-id = <0xc6>; + }; + + qcom,l2_cache_dump700 { + qcom,dump-node = <&L2_7>; + qcom,dump-id = <0xc7>; + }; + + qcom,l2_tlb_dump0 { + qcom,dump-node = <&L2_TLB_0>; qcom,dump-id = <0x120>; }; - qcom,l1_tlb_dump100 { - qcom,dump-node = <&L1_TLB_100>; + qcom,l2_tlb_dump100 { + qcom,dump-node = <&L2_TLB_100>; qcom,dump-id = <0x121>; }; - qcom,l1_tlb_dump200 { - qcom,dump-node = <&L1_TLB_200>; + qcom,l2_tlb_dump200 { + qcom,dump-node = <&L2_TLB_200>; qcom,dump-id = <0x122>; }; - qcom,l1_tlb_dump300 { - qcom,dump-node = <&L1_TLB_300>; + qcom,l2_tlb_dump300 { + qcom,dump-node = <&L2_TLB_300>; qcom,dump-id = <0x123>; }; - qcom,l1_tlb_dump400 { - qcom,dump-node = <&L1_TLB_400>; + qcom,l2_tlb_dump400 { + qcom,dump-node = <&L2_TLB_400>; qcom,dump-id = <0x124>; }; - qcom,l1_tlb_dump500 { - qcom,dump-node = <&L1_TLB_500>; + qcom,l2_tlb_dump500 { + qcom,dump-node = <&L2_TLB_500>; qcom,dump-id = <0x125>; }; - qcom,l1_tlb_dump600 { - qcom,dump-node = <&L1_TLB_600>; + qcom,l2_tlb_dump600 { + qcom,dump-node = <&L2_TLB_600>; qcom,dump-id = <0x126>; }; - qcom,l1_tlb_dump700 { - qcom,dump-node = <&L1_TLB_700>; + qcom,l2_tlb_dump700 { + qcom,dump-node = <&L2_TLB_700>; qcom,dump-id = <0x127>; }; }; @@ -2068,80 +2181,64 @@ qcom,msm_fastrpc_compute_cb1 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1401 0x40>, + iommus = <&apps_smmu 0x1401 0x2040>, <&apps_smmu 0x1421 0x0>, - <&apps_smmu 0x2001 0x20>, + <&apps_smmu 0x2001 0x420>, <&apps_smmu 0x2041 0x0>; dma-coherent; }; - qcom,msm_fastrpc_compute_cb2 { + qcom,msm_fastrpc_compute_cb4 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1402 0x40>, - <&apps_smmu 0x1422 0x0>, - <&apps_smmu 0x2002 0x20>, - <&apps_smmu 0x2042 0x0>; + iommus = <&apps_smmu 0x4 0x3440>, + <&apps_smmu 0x24 0x3400>; dma-coherent; }; - qcom,msm_fastrpc_compute_cb3 { + qcom,msm_fastrpc_compute_cb5 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1403 0x40>, - <&apps_smmu 0x1423 0x0>, - <&apps_smmu 0x2003 0x20>, - <&apps_smmu 0x2043 0x0>; + iommus = <&apps_smmu 0x5 0x3440>, + <&apps_smmu 0x25 0x3400>; dma-coherent; }; - qcom,msm_fastrpc_compute_cb4 { + qcom,msm_fastrpc_compute_cb6 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1404 0x40>, - <&apps_smmu 0x1424 0x0>, - <&apps_smmu 0x2004 0x20>, - <&apps_smmu 0x2044 0x0>; + iommus = <&apps_smmu 0x6 0x3460>; dma-coherent; }; - qcom,msm_fastrpc_compute_cb5 { + qcom,msm_fastrpc_compute_cb7 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1405 0x40>, - <&apps_smmu 0x1425 0x0>, - <&apps_smmu 0x2005 0x20>, - <&apps_smmu 0x2045 0x0>; + iommus = <&apps_smmu 0x7 0x3460>; dma-coherent; }; - qcom,msm_fastrpc_compute_cb6 { + qcom,msm_fastrpc_compute_cb8 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1406 0x40>, - <&apps_smmu 0x1426 0x0>, - <&apps_smmu 0x2006 0x20>, - <&apps_smmu 0x2046 0x0>; + iommus = <&apps_smmu 0x8 0x3460>; dma-coherent; }; - qcom,msm_fastrpc_compute_cb7 { + qcom,msm_fastrpc_compute_cb2 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1407 0x40>, - <&apps_smmu 0x1427 0x0>, - <&apps_smmu 0x2007 0x20>, - <&apps_smmu 0x2047 0x0>; + iommus = <&apps_smmu 0x2 0x3440>, + <&apps_smmu 0x22 0x3400>; dma-coherent; }; - qcom,msm_fastrpc_compute_cb8 { + qcom,msm_fastrpc_compute_cb3 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; - iommus = <&apps_smmu 0x1408 0x40>, - <&apps_smmu 0x1428 0x0>, - <&apps_smmu 0x2008 0x20>, - <&apps_smmu 0x2048 0x0>; + iommus = <&apps_smmu 0x3 0x3440>, + <&apps_smmu 0x1423 0x0>, + <&apps_smmu 0x2023 0x0>; dma-coherent; }; @@ -2149,10 +2246,7 @@ compatible = "qcom,msm-fastrpc-compute-cb"; label = "cdsprpc-smd"; qcom,secure-context-bank; - iommus = <&apps_smmu 0x1409 0x40>, - <&apps_smmu 0x1429 0x0>, - <&apps_smmu 0x2009 0x20>, - <&apps_smmu 0x2049 0x0>; + iommus = <&apps_smmu 0x9 0x3460>; dma-coherent; }; @@ -2176,6 +2270,28 @@ iommus = <&apps_smmu 0x1b25 0x0>; dma-coherent; }; + + qcom,msm_fastrpc_compute_cb13 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "sdsprpc-smd"; + iommus = <&apps_smmu 0x5a1 0x0>; + dma-coherent; + }; + + qcom,msm_fastrpc_compute_cb14 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "sdsprpc-smd"; + iommus = <&apps_smmu 0x5a2 0x0>; + dma-coherent; + }; + + qcom,msm_fastrpc_compute_cb15 { + compatible = "qcom,msm-fastrpc-compute-cb"; + label = "sdsprpc-smd"; + iommus = <&apps_smmu 0x5a3 0x0>; + shared-cb = <5>; + dma-coherent; + }; }; sdhc_2: sdhci@8804000 { @@ -2260,9 +2376,9 @@ interrupts = <0 129 0>; #mbox-cells = <1>; qcom,drv-id = <0>; - qcom,tcs-config = , - , + qcom,tcs-config = , , + , ; }; @@ -2299,20 +2415,34 @@ #interrupt-cells = <3>; }; + qcom,qsee_irq_bridge { + compatible = "qcom,qsee-ipc-irq-bridge"; + + qcom,qsee-ipc-irq-spss { + qcom,dev-name = "qsee_ipc_irq_spss"; + label = "spss"; + interrupt-parent = <&intsp>; + interrupts = <1 0 IRQ_TYPE_EDGE_RISING>; + }; + }; + qcom,glink { compatible = "qcom,glink"; #address-cells = <1>; #size-cells = <1>; ranges; - modem { + glink_modem: modem { qcom,remote-pid = <1>; transport = "smem"; mboxes = <&apcs_glb 12>; mbox-names = "mpss_smem"; interrupts = ; - modem_qrtr { + label = "modem"; + qcom,glink-label = "mpss"; + + qcom,modem_qrtr { qcom,glink-channels = "IPCRTR"; qcom,intents = <0x800 5 0x2000 3 @@ -2325,27 +2455,38 @@ qcom,intents = <0x64 64>; }; - modem_ds { + qcom,modem_ds { qcom,glink-channels = "DS"; qcom,intents = <0x4000 0x2>; }; + + qcom,modem_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_adsp>, + <&glink_slpi>, + <&glink_cdsp>, + <&glink_spss>; + }; }; - adsp { + glink_adsp: adsp { qcom,remote-pid = <2>; transport = "smem"; mboxes = <&apcs_glb 8>; mbox-names = "adsp_smem"; interrupts = ; - adsp_qrtr { + label = "adsp"; + qcom,glink-label = "lpass"; + + qcom,adsp_qrtr { qcom,glink-channels = "IPCRTR"; qcom,intents = <0x800 5 0x2000 3 0x4400 2>; }; - apr_tal_rpmsg { + qcom,apr_tal_rpmsg { qcom,glink-channels = "apr_audio_svc"; qcom,intents = <0x200 20>; }; @@ -2355,16 +2496,26 @@ qcom,glink-channels = "fastrpcglink-apps-dsp"; qcom,intents = <0x64 64>; }; + + qcom,adsp_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_slpi>, + <&glink_cdsp>; + }; }; - dsps { + glink_slpi: dsps { qcom,remote-pid = <3>; transport = "smem"; mboxes = <&apcs_glb 24>; mbox-names = "dsps_smem"; interrupts = ; - dsps_qrtr { + label = "slpi"; + qcom,glink-label = "dsps"; + + qcom,slpi_qrtr { qcom,glink-channels = "IPCRTR"; qcom,intents = <0x800 5 0x2000 3 @@ -2376,16 +2527,26 @@ qcom,glink-channels = "fastrpcglink-apps-dsp"; qcom,intents = <0x64 64>; }; + + qcom,slpi_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_adsp>, + <&glink_cdsp>; + }; }; - cdsp { + glink_cdsp: cdsp { qcom,remote-pid = <5>; transport = "smem"; mboxes = <&apcs_glb 4>; mbox-names = "cdsp_smem"; interrupts = ; - cdsp_qrtr { + label = "cdsp"; + qcom,glink-label = "cdsp"; + + qcom,cdsp_qrtr { qcom,glink-channels = "IPCRTR"; qcom,intents = <0x800 5 0x2000 3 @@ -2397,9 +2558,16 @@ qcom,glink-channels = "fastrpcglink-apps-dsp"; qcom,intents = <0x64 64>; }; + + qcom,cdsp_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>, + <&glink_adsp>, + <&glink_slpi>; + }; }; - spss { + glink_spss: spss { qcom,remote-pid = <8>; transport = "spss"; mboxes = <&sp_scsr 0>; @@ -2411,30 +2579,40 @@ <0x1885010 0x4>; reg-names = "qcom,spss-addr", "qcom,spss-size"; + + label = "spss"; + qcom,glink-label = "spss"; + + qcom,spss_glink_ssr { + qcom,glink-channels = "glink_ssr"; + qcom,notify-edges = <&glink_modem>; + }; }; glink_spi_xprt_wdsp: wdsp { - qcom,remote-pid = <10>; transport = "spi"; tx-descriptors = <0x12000 0x12004>; rx-descriptors = <0x1200c 0x12010>; - wdsp_ctrl { + label = "wdsp"; + qcom,glink-label = "wdsp"; + + qcom,wdsp_ctrl { qcom,glink-channels = "g_glink_ctrl"; qcom,intents = <0x400 1>; }; - wdsp_ild { + qcom,wdsp_ild { qcom,glink-channels = "g_glink_persistent_data_ild"; }; - wdsp_nild { + qcom,wdsp_nild { qcom,glink-channels = "g_glink_persistent_data_nild"; }; - wdsp_data { + qcom,wdsp_data { qcom,glink-channels = "g_glink_audio_data"; qcom,intents = <0x1000 2>; }; @@ -2585,6 +2763,10 @@ <&apps_smmu 0x0516 0x0011>; }; + qcom_msmhdcp: qcom,msm_hdcp { + compatible = "qcom,msm-hdcp"; + }; + qcom_crypto: qcrypto@1de0000 { compatible = "qcom,qcrypto"; reg = <0x1de0000 0x20000>, @@ -3024,6 +3206,7 @@ reg = <0x0 0x200000>; reg-names = "rmtfs"; qcom,client-id = <0x00000001>; + qcom,guard-memory; }; qcom,msm_gsi { @@ -3062,34 +3245,52 @@ qcom,msm-bus,num-paths = <4>; qcom,msm-bus,vectors-KBps = /* No vote */ - <90 512 0 0>, - <90 585 0 0>, - <1 676 0 0>, - <143 777 0 0>, + , + , + , + , + /* SVS2 */ - <90 512 3616000 7232000>, - <90 585 300000 600000>, - <1 676 90000 180000>, /*gcc_config_noc_clk_src */ - <143 777 0 120>, /* IB defined for IPA2X_clk in MHz*/ + , + , + , + , + /* SVS */ - <90 512 6640000 13280000>, - <90 585 400000 800000>, - <1 676 100000 200000>, - <143 777 0 250>, /* IB defined for IPA2X_clk in MHz*/ + , + , + , + , + /* NOMINAL */ - <90 512 10400000 20800000>, - <90 585 800000 1600000>, - <1 676 200000 400000>, - <143 777 0 440>, /* IB defined for IPA2X_clk in MHz*/ + , + , + , + , + /* TURBO */ - <90 512 10400000 20800000>, - <90 585 960000 1920000>, - <1 676 266000 532000>, - <143 777 0 500>; /* IB defined for IPA clk in MHz*/ + , + , + , + ; + qcom,bus-vector-names = "MIN", "SVS2", "SVS", "NOMINAL", "TURBO"; qcom,throughput-threshold = <310 600 1000>; qcom,scaling-exceptions = <>; + + /* smp2p information */ + qcom,smp2p_map_ipa_1_out { + compatible = "qcom,smp2p-map-ipa-1-out"; + qcom,smem-states = <&smp2p_ipa_1_out 0>; + qcom,smem-state-names = "ipa-smp2p-out"; + }; + + qcom,smp2p_map_ipa_1_in { + compatible = "qcom,smp2p-map-ipa-1-in"; + interrupts-extended = <&smp2p_ipa_1_in 0 0>; + interrupt-names = "ipa-smp2p-in"; + }; }; ipa_smmu_ap: ipa_smmu_ap { @@ -3132,7 +3333,7 @@ mbox-names = "aop"; }; - qcom,icnss@18800000 { + icnss: qcom,icnss@18800000 { compatible = "qcom,icnss"; reg = <0x18800000 0x800000>, <0xa0000000 0x10000000>, @@ -3153,6 +3354,7 @@ <0 425 0 /* CE11 */ >; qcom,wlan-msa-memory = <0x100000>; qcom,gpio-force-fatal-error = <&smp2pgpio_wlan_1_in 0 0>; + qcom,gpio-early-crash-ind = <&smp2pgpio_wlan_1_in 1 0>; qcom,smmu-s1-bypass; vdd-cx-mx-supply = <&pm855_l1>; @@ -3162,6 +3364,99 @@ qcom,vdd-cx-mx-config = <752000 752000>; qcom,vdd-3.3-ch0-config = <3104000 3312000>; }; + + wil6210: qcom,wil6210 { + compatible = "qcom,wil6210"; + qcom,pcie-parent = <&pcie1>; + pinctrl-names = "default"; + pinctrl-0 = <&wil6210_refclk3_en_pin>; + qcom,wigig-en = <&tlmm 131 0>; + qcom,msm-bus,name = "wil6210"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <100 512 0 0>, + <100 512 600000 800000>; /* ~4.6Gbps (MCS12) */ + qcom,use-ext-supply; + vddio-supply= <&pm855_s5>; + qcom,use-ext-clocks; + clocks = <&clock_rpmh RPMH_RF_CLK3>, + <&clock_rpmh RPMH_RF_CLK3_A>; + clock-names = "rf_clk3_clk", "rf_clk3_pin_clk"; + qcom,smmu-support; + qcom,smmu-mapping = <0x20000000 0xe0000000>; + qcom,smmu-s1-en; + qcom,smmu-fast-map; + qcom,smmu-coherent; + status = "disabled"; + }; + + mhi_0: qcom,mhi@0 { + /* controller specific configuration */ + compatible = "qcom,mhi"; + qcom,pci-domain = <1>; + qcom,pci-bus = <1>; + qcom,pci-slot = <0>; + qcom,smmu-cfg = <0x3>; + qcom,msm-bus,name = "mhi"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <100 512 0 0>, + <100 512 1200000000 650000000>; + + /* mhi bus specific settings */ + mhi,max-channels = <106>; + mhi,chan-cfg = + <0 64 2 1 2 0 2 0 0>, <1 64 2 2 2 0 2 0 0>, + <2 128 1 1 2 0 1 0 0>, <3 128 1 2 2 0 1 0 0>, + <4 64 1 1 2 0 2 0 0>, <5 64 3 2 2 0 2 0 0>, + <8 64 1 1 2 0 2 0 0>, <9 64 1 2 2 0 2 0 0>, + <10 64 1 1 2 0 2 0 0>, <11 64 1 2 2 0 2 0 0>, + <14 64 1 1 2 0 2 0 0>, <15 64 2 2 2 0 2 0 0>, + <16 64 3 1 2 0 2 0 0>, <17 64 3 2 2 0 2 0 0>, + <18 64 1 1 2 0 2 0 0>, <19 64 1 2 2 0 2 0 8>, + <20 64 2 1 2 0 2 1 16>, <21 64 2 2 2 0 2 0 24>, + <22 64 2 1 2 0 2 0 0>, <23 64 2 2 2 0 2 0 0>, + <24 64 2 1 2 0 1 0 0>, <25 64 2 2 2 0 1 0 0>, + <26 64 3 1 2 0 2 0 0>, <27 64 3 2 2 0 2 0 0>, + <32 64 3 1 2 0 2 0 0>, <33 64 3 2 2 0 2 0 0>, + <100 512 4 1 3 1 2 1 0x4>, <101 512 5 2 3 1 2 1 0>; + mhi,chan-names = "LOOPBACK", "LOOPBACK", + "SAHARA", "SAHARA", + "DIAG", "DIAG", + "QDSS", "QDSS", + "EFS", "EFS", + "QMI0", "QMI0", + "QMI1", "QMI1", + "IP_CTRL", "IP_CTRL", + "IPCR", "IPCR", + "TF", "TF", + "BL", "BL", + "DCI", "DCI", + "DUN", "DUN", + "IP_HW0", "IP_HW0"; + mhi,ev-cfg = <32 0 1 0 1 2 0x8>, + <256 1 2 0 1 2 0>, + <256 1 3 0 1 2 0>, + <256 1 4 0 1 2 0>, + <1024 5 5 100 1 3 0x1>, + <1024 5 6 101 1 3 0x3>; + mhi,timeout = <2000>; + status = "disabled"; + + mhi_netdev_0: mhi_rmnet@0 { + mhi,chan = "IP_HW0"; + mhi,interface-name = "rmnet_mhi"; + mhi,mru = <0x4000>; + }; + + mhi_netdev_1: mhi_rmnet@1 { + mhi,chan = "IP_HW_ADPL"; + mhi,interface-name = "rmnet_mhi"; + mhi,mru = <0x4000>; + }; + }; }; &emac_gdsc { @@ -3228,7 +3523,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "bps_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3236,7 +3537,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "ipe_0_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3244,7 +3551,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "ipe_1_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3252,7 +3565,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "ife_0_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3260,7 +3579,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "ife_1_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3268,7 +3593,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "titan_top_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3276,7 +3607,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_DISP_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "mdss_core_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3286,7 +3623,7 @@ &gpu_gx_gdsc { parent-supply = <&pm855l_s2_level>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&pm855l_s2_level>; status = "ok"; }; @@ -3294,7 +3631,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_VIDEO_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "mvsc_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3302,7 +3645,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_VIDEO_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "mvs0_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; @@ -3310,7 +3659,13 @@ clock-names = "ahb_clk"; clocks = <&clock_gcc GCC_VIDEO_AHB_CLK>; parent-supply = <&VDD_MMCX_LEVEL>; - qcom,vote-parent-supply-voltage; + vdd_parent-supply = <&VDD_MMCX_LEVEL>; + qcom,msm-bus,name = "mvs1_gdsc_ahb"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + , + ; status = "ok"; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sm8150p-cdp-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..ee12483bfb652f1c07b50cd8cb2ce0ee8185ae6a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-cdp-overlay.dts @@ -0,0 +1,27 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include +#include +#include +#include + +#include "sm8150-cdp.dtsi" + +/ { + model = "CDP"; + compatible = "qcom,sm8150p-cdp", "qcom,sm8150p", "qcom,cdp"; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-cdp.dts b/arch/arm64/boot/dts/qcom/sm8150p-cdp.dts new file mode 100644 index 0000000000000000000000000000000000000000..0020b813b64d6620cee9b2b6af62cb63db3a39d1 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-cdp.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p.dtsi" +#include "sm8150-cdp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P CDP"; + compatible = "qcom,sm8150p-cdp", "qcom,sm8150p", "qcom,cdp"; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sm8150p-mtp-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..d435721f3863ee58e32e804920fb28436a056ff3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-mtp-overlay.dts @@ -0,0 +1,27 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include +#include +#include +#include + +#include "sm8150-mtp.dtsi" + +/ { + model = "MTP"; + compatible = "qcom,sm8150p-mtp", "qcom,sm8150p", "qcom,mtp"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-mtp.dts b/arch/arm64/boot/dts/qcom/sm8150p-mtp.dts new file mode 100644 index 0000000000000000000000000000000000000000..9a7fd33ae9b24549689392dea552cd933ff98b5c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-mtp.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p.dtsi" +#include "sm8150-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P MTP"; + compatible = "qcom,sm8150p-mtp", "qcom,sm8150p", "qcom,mtp"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sm8150p-qrd-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..b8ea18b755dc84d6c4e0618dd559e6cd42e3e686 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-qrd-overlay.dts @@ -0,0 +1,27 @@ +/* Copyright (c) 2018, 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/; +/plugin/; + +#include +#include +#include +#include + +#include "sm8150-qrd.dtsi" + +/ { + model = "QRD"; + compatible = "qcom,sm8150p-qrd", "qcom,sm8150p", "qcom,qrd"; + qcom,board-id = <11 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-qrd.dts b/arch/arm64/boot/dts/qcom/sm8150p-qrd.dts new file mode 100644 index 0000000000000000000000000000000000000000..50039ff12ad8a3f9b07c463e179cacfff7774fce --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-qrd.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p.dtsi" +#include "sm8150-qrd.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P QRD"; + compatible = "qcom,sm8150p-qrd", "qcom,sm8150p", "qcom,qrd"; + qcom,board-id = <11 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-v2-cdp.dts b/arch/arm64/boot/dts/qcom/sm8150p-v2-cdp.dts new file mode 100644 index 0000000000000000000000000000000000000000..70ff898cfff4d6c3dad678c462f1f19ce437b0a5 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-v2-cdp.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p-v2.dtsi" +#include "sm8150-cdp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P V2 CDP"; + compatible = "qcom,sm8150p-cdp", "qcom,sm8150p", "qcom,cdp"; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-v2-mtp.dts b/arch/arm64/boot/dts/qcom/sm8150p-v2-mtp.dts new file mode 100644 index 0000000000000000000000000000000000000000..a799969fc5a9370345368015998cc7b8d0224c58 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-v2-mtp.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p-v2.dtsi" +#include "sm8150-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P V2 MTP"; + compatible = "qcom,sm8150p-mtp", "qcom,sm8150p", "qcom,mtp"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-v2-qrd.dts b/arch/arm64/boot/dts/qcom/sm8150p-v2-qrd.dts new file mode 100644 index 0000000000000000000000000000000000000000..9e2b45317c811fc6bbf06cb4e9e22cc8f3f02880 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-v2-qrd.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p-v2.dtsi" +#include "sm8150-qrd.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P V2 QRD"; + compatible = "qcom,sm8150p-qrd", "qcom,sm8150p", "qcom,qrd"; + qcom,board-id = <11 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-v2.dts b/arch/arm64/boot/dts/qcom/sm8150p-v2.dts new file mode 100644 index 0000000000000000000000000000000000000000..78791b7a3cc7e56f7e312b3b9de61f798300ad96 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-v2.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p-v2.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P v2 SoC"; + compatible = "qcom,sm8150p"; + qcom,pmic-name = "PM855"; + qcom,board-id = <0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p-v2.dtsi b/arch/arm64/boot/dts/qcom/sm8150p-v2.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..4351f650b1ea3de2a23b9fb115e8043d4295e615 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p-v2.dtsi @@ -0,0 +1,19 @@ +/* Copyright (c) 2018, 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 "sm8150-v2.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P v2"; + qcom,msm-name = "SM8150P v2"; + qcom,msm-id = <356 0x20000>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p.dts b/arch/arm64/boot/dts/qcom/sm8150p.dts new file mode 100644 index 0000000000000000000000000000000000000000..d069c38c17f3cb26e15b9f4bc1171c69ed7ba811 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p.dts @@ -0,0 +1,22 @@ +/* Copyright (c) 2018, 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 "sm8150p.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P v1 SoC"; + compatible = "qcom,sm8150p"; + qcom,pmic-name = "PM855"; + qcom,board-id = <0 0>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150p.dtsi b/arch/arm64/boot/dts/qcom/sm8150p.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..87632a0933e0b14315aad6246f5efc0d5a5d4f7e --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8150p.dtsi @@ -0,0 +1,19 @@ +/* Copyright (c) 2018, 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 "sm8150.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SM8150P v1"; + qcom,msm-name = "SM8150P v1"; + qcom,msm-id = <356 0x10000>; +}; diff --git a/arch/arm64/boot/dts/qcom/smb1390.dtsi b/arch/arm64/boot/dts/qcom/smb1390.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..ef7fba85db74ef2b3d5b7d90b2f01653b0876533 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/smb1390.dtsi @@ -0,0 +1,62 @@ +/* Copyright (c) 2018, 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 + +&qupv3_se4_i2c { + smb1390: qcom,smb1390@10 { + compatible = "qcom,i2c-pmic"; + reg = <0x10>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&spmi_bus>; + interrupts = <0x2 0xC5 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt_names = "smb1390"; + interrupt-controller; + #interrupt-cells = <3>; + qcom,periph-map = <0x10>; + + smb1390_revid: qcom,revid { + compatible = "qcom,qpnp-revid"; + reg = <0x100>; + }; + + smb1390_charger: qcom,charge_pump { + compatible = "qcom,smb1390-charger"; + qcom,pmic-revid = <&smb1390_revid>; + interrupt-parent = <&smb1390>; + status = "disabled"; + + io-channels = <&pm855b_vadc ADC_AMUX_THM2>; + io-channel-names = "cp_die_temp"; + + qcom,core { + interrupts = <0x10 0x0 IRQ_TYPE_EDGE_RISING>, + <0x10 0x1 IRQ_TYPE_EDGE_RISING>, + <0x10 0x2 IRQ_TYPE_EDGE_RISING>, + <0x10 0x3 IRQ_TYPE_EDGE_RISING>, + <0x10 0x4 IRQ_TYPE_EDGE_RISING>, + <0x10 0x5 IRQ_TYPE_EDGE_RISING>, + <0x10 0x6 IRQ_TYPE_EDGE_RISING>, + <0x10 0x7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "switcher-off-window", + "switcher-off-fault", + "tsd-fault", + "irev-fault", + "vph-ov-hard", + "vph-ov-soft", + "ilim", + "temp-alarm"; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi index 910628d18add07d9a39974bc6ce2ac4a403adb81..1fc5060d7027e6e4b852c6cd71d430d2e7ab3990 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi @@ -155,17 +155,6 @@ regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; }; - - vdd_log: vdd-log { - compatible = "pwm-regulator"; - pwms = <&pwm2 0 25000 0>; - regulator-name = "vdd_log"; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <1400000>; - regulator-always-on; - regulator-boot-on; - status = "okay"; - }; }; &cpu_b0 { diff --git a/arch/arm64/configs/misc_debug_defconfig b/arch/arm64/configs/misc_debug_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..316fec31dbf4858ac8dc084ddd65ec2de82c3ef4 --- /dev/null +++ b/arch/arm64/configs/misc_debug_defconfig @@ -0,0 +1,4 @@ +CONFIG_FRAME_WARN=0 +CONFIG_KASAN=y +CONFIG_KASAN_INLINE=y +CONFIG_KCOV=y diff --git a/arch/arm64/configs/qcs405-perf_defconfig b/arch/arm64/configs/qcs405-perf_defconfig index f693c01d810f98b7a3e7f639c3c18b33d8684f63..924c745c5d1a2ed3839e8d084d0c2214dd1bfed7 100644 --- a/arch/arm64/configs/qcs405-perf_defconfig +++ b/arch/arm64/configs/qcs405-perf_defconfig @@ -42,6 +42,8 @@ CONFIG_PM_WAKELOCKS=y CONFIG_PM_WAKELOCKS_LIMIT=0 # CONFIG_PM_WAKELOCKS_GC is not set CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -165,6 +167,7 @@ CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y CONFIG_NET_CLS_ACT=y +CONFIG_QRTR=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_BT=y @@ -247,6 +250,16 @@ CONFIG_SLIMBUS=y CONFIG_SLIMBUS_MSM_NGD=y CONFIG_PINCTRL_QCS405=y CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_MFD_SYSCON=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_FAN53555=y @@ -297,11 +310,15 @@ CONFIG_DUAL_ROLE_USB_INTF=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y CONFIG_MMC_TEST=m CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y CONFIG_RTC_CLASS=y CONFIG_DMADEVICES=y CONFIG_QCOM_SPS_DMA=y @@ -309,15 +326,21 @@ CONFIG_UIO=y CONFIG_STAGING=y CONFIG_ASHMEM=y CONFIG_ION=y +CONFIG_QCOM_CLK_SMD_RPM=y +CONFIG_MDM_GCC_QCS405=y CONFIG_HWSPINLOCK=y CONFIG_MAILBOX=y CONFIG_IOMMU_IO_PGTABLE_LPAE=y +CONFIG_RPMSG_QCOM_SMD=y +CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y +CONFIG_QCOM_SMD_RPM=y CONFIG_QCOM_SCM=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_MSM_RPM_SMD=y CONFIG_PWM=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/arch/arm64/configs/qcs405_defconfig b/arch/arm64/configs/qcs405_defconfig index 4c23c8ff83966c70af3707fab6f9bf52871633e3..b858c18e51848574141180a2ac093ba849f86db2 100644 --- a/arch/arm64/configs/qcs405_defconfig +++ b/arch/arm64/configs/qcs405_defconfig @@ -48,6 +48,8 @@ CONFIG_PM_WAKELOCKS_LIMIT=0 # CONFIG_PM_WAKELOCKS_GC is not set CONFIG_PM_DEBUG=y CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -172,6 +174,7 @@ CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y CONFIG_NET_CLS_ACT=y +CONFIG_QRTR=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y @@ -257,6 +260,16 @@ CONFIG_SLIMBUS=y CONFIG_SLIMBUS_MSM_NGD=y CONFIG_PINCTRL_QCS405=y CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_MFD_SYSCON=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_FAN53555=y @@ -300,20 +313,30 @@ CONFIG_USB_STORAGE_ALAUDA=y CONFIG_USB_STORAGE_KARMA=y CONFIG_USB_STORAGE_CYPRESS_ATACB=y CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y CONFIG_USB_SERIAL=y CONFIG_USB_EHSET_TEST_FIXTURE=y CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_QCOM_EMU_PHY=y CONFIG_DUAL_ROLE_USB_INTF=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_DEBUG_FILES=y CONFIG_USB_GADGET_DEBUG_FS=y CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_DIAG=y CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y CONFIG_MMC_TEST=m +CONFIG_MMC_RING_BUFFER=y CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y CONFIG_RTC_CLASS=y CONFIG_DMADEVICES=y CONFIG_QCOM_SPS_DMA=y @@ -321,16 +344,22 @@ CONFIG_UIO=y CONFIG_STAGING=y CONFIG_ASHMEM=y CONFIG_ION=y +CONFIG_QCOM_CLK_SMD_RPM=y +CONFIG_MDM_GCC_QCS405=y CONFIG_HWSPINLOCK=y CONFIG_MAILBOX=y CONFIG_IOMMU_IO_PGTABLE_LPAE=y CONFIG_QCOM_LAZY_MAPPING=y +CONFIG_RPMSG_QCOM_SMD=y +CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y +CONFIG_QCOM_SMD_RPM=y CONFIG_QCOM_SCM=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_MSM_RPM_SMD=y CONFIG_PWM=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y @@ -382,11 +411,16 @@ CONFIG_IPC_LOGGING=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_LKDTM=y CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y CONFIG_CORESIGHT_STM=y CONFIG_CORESIGHT_CTI=y CONFIG_CORESIGHT_TPDA=y CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 CONFIG_CORESIGHT_EVENT=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y diff --git a/arch/arm64/configs/sdmshrike-perf_defconfig b/arch/arm64/configs/sdmshrike-perf_defconfig index 696d6e0dff12bfc14fbf3fc7ac10da8ff77edf4f..75a748c2e54b0e7cf87429f6b39921797d84ffdb 100644 --- a/arch/arm64/configs/sdmshrike-perf_defconfig +++ b/arch/arm64/configs/sdmshrike-perf_defconfig @@ -11,6 +11,8 @@ CONFIG_RCU_FAST_NO_HZ=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y CONFIG_RT_GROUP_SCHED=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y @@ -132,13 +134,16 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -156,6 +161,7 @@ CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y @@ -191,6 +197,8 @@ CONFIG_QRTR=y CONFIG_QRTR_SMD=y CONFIG_BT=y CONFIG_CFG80211=y +CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y CONFIG_RFKILL=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y @@ -251,13 +259,14 @@ CONFIG_SLIMBUS_MSM_NGD=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_PINCTRL_SDMSHRIKE=y CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y CONFIG_THERMAL=y CONFIG_QCOM_SPMI_TEMP_ALARM=y CONFIG_THERMAL_TSENS=y CONFIG_MFD_SPMI_PMIC=y -CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_QPNP_LCDB=y CONFIG_REGULATOR_REFGEN=y @@ -270,7 +279,12 @@ CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_ADV_DEBUG=y CONFIG_VIDEO_FIXED_MINOR_RANGES=y CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_FB=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_DRM=y +CONFIG_DRM_MSM_REGISTER_LOGGING=y +CONFIG_DRM_SDE_EVTLOG_DEBUG=y +CONFIG_DRM_SDE_RSC=y CONFIG_FB_ARMCLCD=y CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y CONFIG_LOGO=y @@ -339,6 +353,7 @@ CONFIG_ASHMEM=y CONFIG_ION=y CONFIG_QCOM_GENI_SE=y CONFIG_QPNP_REVID=y +CONFIG_QCOM_MDSS_PLL=y CONFIG_SPMI_PMIC_CLKDIV=y CONFIG_MSM_CLK_AOP_QMP=y CONFIG_MSM_NPUCC_SM8150=y @@ -354,20 +369,42 @@ CONFIG_QCOM_APCS_IPC=y CONFIG_MSM_QMP=y CONFIG_ARM_SMMU=y CONFIG_QCOM_LAZY_MAPPING=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_QCOM_GLINK_SMEM=y CONFIG_QCOM_LLCC=y CONFIG_QCOM_SDMSHRIKE_LLCC=y CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_SMP2P=y CONFIG_QCOM_SMSM=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_SYSMON_QMI_COMM=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_QCOM_SECURE_BUFFER=y CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y CONFIG_QCOM_COMMAND_DB=y CONFIG_QTI_RPMH_API=y CONFIG_QCOM_GLINK=y CONFIG_QCOM_GLINK_PKT=y +CONFIG_QCOM_FSA4480_I2C=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_QCOMCCI_HWMON=y +CONFIG_QCOM_M4M_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_QCOM_DEVFREQ_DEVBW=y CONFIG_EXTCON_USB_GPIO=y CONFIG_IIO=y CONFIG_QCOM_SPMI_ADC5=y @@ -384,10 +421,10 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y -CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_ECRYPT_FS=y CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_PRINTK_TIME=y diff --git a/arch/arm64/configs/sdmshrike_defconfig b/arch/arm64/configs/sdmshrike_defconfig index 00d4b73cfafa7005df4c975802a692a48b07873e..830f06dc204a5e72c7fa33a7b3c2f25159418b43 100644 --- a/arch/arm64/configs/sdmshrike_defconfig +++ b/arch/arm64/configs/sdmshrike_defconfig @@ -10,6 +10,8 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_CGROUP_FREEZER=y @@ -137,13 +139,16 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -161,6 +166,7 @@ CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y @@ -198,6 +204,8 @@ CONFIG_QRTR=y CONFIG_QRTR_SMD=y CONFIG_BT=y CONFIG_CFG80211=y +CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_RFKILL=y @@ -261,13 +269,14 @@ CONFIG_SLIMBUS_MSM_NGD=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_PINCTRL_SDMSHRIKE=y CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y CONFIG_THERMAL=y CONFIG_QCOM_SPMI_TEMP_ALARM=y CONFIG_THERMAL_TSENS=y CONFIG_MFD_SPMI_PMIC=y -CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_QPNP_LCDB=y CONFIG_REGULATOR_REFGEN=y @@ -280,7 +289,12 @@ CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_ADV_DEBUG=y CONFIG_VIDEO_FIXED_MINOR_RANGES=y CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_FB=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_DRM=y +CONFIG_DRM_MSM_REGISTER_LOGGING=y +CONFIG_DRM_SDE_EVTLOG_DEBUG=y +CONFIG_DRM_SDE_RSC=y CONFIG_FB_VIRTUAL=y CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y @@ -352,6 +366,7 @@ CONFIG_ASHMEM=y CONFIG_ION=y CONFIG_QCOM_GENI_SE=y CONFIG_QPNP_REVID=y +CONFIG_QCOM_MDSS_PLL=y CONFIG_SPMI_PMIC_CLKDIV=y CONFIG_MSM_CLK_AOP_QMP=y CONFIG_MSM_NPUCC_SM8150=y @@ -367,20 +382,43 @@ CONFIG_QCOM_APCS_IPC=y CONFIG_MSM_QMP=y CONFIG_ARM_SMMU=y CONFIG_QCOM_LAZY_MAPPING=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_QCOM_GLINK_SMEM=y CONFIG_QCOM_LLCC=y CONFIG_QCOM_SDMSHRIKE_LLCC=y CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_QCOM_WDOG_IPI_ENABLE=y CONFIG_QCOM_SMP2P=y CONFIG_QCOM_SMSM=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_SYSMON_QMI_COMM=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_QCOM_SECURE_BUFFER=y CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y CONFIG_QCOM_COMMAND_DB=y CONFIG_QTI_RPMH_API=y CONFIG_QCOM_GLINK=y CONFIG_QCOM_GLINK_PKT=y +CONFIG_QCOM_FSA4480_I2C=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_QCOMCCI_HWMON=y +CONFIG_QCOM_M4M_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_QCOM_DEVFREQ_DEVBW=y CONFIG_EXTCON_USB_GPIO=y CONFIG_IIO=y CONFIG_QCOM_SPMI_ADC5=y @@ -399,11 +437,11 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y -CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_EFIVAR_FS=y CONFIG_ECRYPT_FS=y CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y # CONFIG_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y @@ -435,6 +473,7 @@ CONFIG_SCHEDSTATS=y CONFIG_SCHED_STACK_END_CHECK=y # CONFIG_DEBUG_PREEMPT is not set CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_PANIC_ON_BUG=y CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_ATOMIC_SLEEP=y CONFIG_DEBUG_LIST=y diff --git a/arch/arm64/configs/sdm640-perf_defconfig b/arch/arm64/configs/sdmsteppe-perf_defconfig similarity index 89% rename from arch/arm64/configs/sdm640-perf_defconfig rename to arch/arm64/configs/sdmsteppe-perf_defconfig index ddc26f4d1ad2323878b33a8825ea2a1ed2b9938a..32ed0b0d85dcf13b8a4ab4014236239b61436f70 100644 --- a/arch/arm64/configs/sdm640-perf_defconfig +++ b/arch/arm64/configs/sdmsteppe-perf_defconfig @@ -15,6 +15,8 @@ CONFIG_RCU_NOCB_CPU=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y CONFIG_RT_GROUP_SCHED=y CONFIG_CGROUP_FREEZER=y CONFIG_CPUSETS=y @@ -50,7 +52,7 @@ CONFIG_MODULE_SIG_FORCE=y CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y -CONFIG_ARCH_SM8150=y +CONFIG_ARCH_SM6150=y CONFIG_PCI=y CONFIG_PCI_MSM=y CONFIG_SCHED_MC=y @@ -64,6 +66,8 @@ CONFIG_SECCOMP=y # CONFIG_HARDEN_BRANCH_PREDICTOR is not set CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y # CONFIG_ARM64_VHE is not set CONFIG_RANDOMIZE_BASE=y # CONFIG_EFI is not set @@ -90,6 +94,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y CONFIG_INET=y +CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y @@ -125,6 +130,7 @@ CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LOG=y CONFIG_NETFILTER_XT_TARGET_MARK=y CONFIG_NETFILTER_XT_TARGET_NFLOG=y @@ -154,12 +160,14 @@ CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -178,6 +186,7 @@ CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y @@ -215,11 +224,15 @@ CONFIG_QRTR_SMD=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y CONFIG_BT=y CONFIG_MSM_BT_POWER=y CONFIG_CFG80211=y +CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y CONFIG_RFKILL=y +CONFIG_NFC_NQ=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y CONFIG_DMA_CMA=y @@ -235,12 +248,10 @@ CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y CONFIG_CHR_DEV_SCH=y CONFIG_SCSI_CONSTANTS=y -CONFIG_SCSI_LOGGING=y CONFIG_SCSI_SCAN_ASYNC=y CONFIG_SCSI_UFSHCD=y CONFIG_SCSI_UFSHCD_PLATFORM=y CONFIG_SCSI_UFS_QCOM=y -CONFIG_SCSI_UFSHCD_CMD_LOGGING=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_UEVENT=y @@ -274,6 +285,7 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_UINPUT=y # CONFIG_SERIO_SERPORT is not set @@ -282,6 +294,7 @@ CONFIG_INPUT_UINPUT=y # CONFIG_DEVMEM is not set CONFIG_SERIAL_MSM_GENI=y CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_DIAG_CHAR=y CONFIG_MSM_ADSPRPC=y CONFIG_I2C_CHARDEV=y @@ -296,12 +309,13 @@ CONFIG_PM855B_PMIC_SIMULATOR=y CONFIG_PM855L_PMIC_SIMULATOR=y CONFIG_SLIMBUS_MSM_NGD=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y -CONFIG_PINCTRL_SDM640=y +CONFIG_PINCTRL_SM6150=y CONFIG_GPIO_SYSFS=y CONFIG_POWER_RESET_QCOM=y CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_FG_GEN4=y CONFIG_QPNP_SMB5=y CONFIG_THERMAL=y CONFIG_THERMAL_WRITABLE_TRIPS=y @@ -314,7 +328,10 @@ CONFIG_THERMAL_TSENS=y CONFIG_QTI_THERMAL_LIMITS_DCVS=y CONFIG_QTI_VIRTUAL_SENSOR=y CONFIG_QTI_AOP_REG_COOLING_DEVICE=y +CONFIG_QTI_QMI_COOLING_DEVICE=y CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_QTI_BCL_PMIC5=y +CONFIG_QTI_BCL_SOC_DRIVER=y CONFIG_MFD_SPMI_PMIC=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_QPNP_LCDB=y @@ -336,8 +353,10 @@ CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y CONFIG_MSM_NPU=y CONFIG_DRM=y CONFIG_DRM_MSM_REGISTER_LOGGING=y +CONFIG_DRM_SDE_EVTLOG_DEBUG=y CONFIG_DRM_SDE_RSC=y CONFIG_FB_ARMCLCD=y +CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set @@ -349,7 +368,11 @@ CONFIG_SND_USB_AUDIO_QMI=y CONFIG_SND_SOC=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y @@ -359,6 +382,7 @@ CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_HCD_PLATFORM=y CONFIG_USB_STORAGE=y CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y CONFIG_USB_ISP1760=y CONFIG_USB_ISP1760_HOST_ROLE=y CONFIG_NOP_USB_XCEIV=y @@ -367,7 +391,7 @@ CONFIG_USB_MSM_SSPHY_QMP=y CONFIG_MSM_HSUSB_PHY=y CONFIG_DUAL_ROLE_USB_INTF=y CONFIG_USB_GADGET=y -CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_NCM=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y @@ -382,17 +406,26 @@ CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y CONFIG_USB_CONFIGFS_F_CDEV=y CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_QDSS=y CONFIG_USB_PD_POLICY=y CONFIG_QPNP_USB_PDPHY=y CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y CONFIG_MMC_TEST=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_MSM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_HAPTICS=y +CONFIG_LEDS_QTI_TRI_LED=y +CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_EDAC=y CONFIG_EDAC_KRYO_ARM64=y CONFIG_EDAC_KRYO_ARM64_PANIC_ON_CE=y @@ -410,6 +443,7 @@ CONFIG_QCOM_GENI_SE=y CONFIG_QPNP_REVID=y CONFIG_SPS=y CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_USB_BAM=y CONFIG_IPA3=y CONFIG_IPA_WDI_UNIFIED_API=y CONFIG_RMNET_IPA3=y @@ -440,9 +474,11 @@ CONFIG_IOMMU_DEBUG_TRACKING=y CONFIG_IOMMU_TESTS=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_QCOM_CPUSS_DUMP=y CONFIG_QCOM_RUN_QUEUE_STATS=y CONFIG_QCOM_LLCC=y -CONFIG_QCOM_SM8150_LLCC=y +CONFIG_QCOM_SM6150_LLCC=y CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y CONFIG_QCOM_MEMORY_DUMP_V2=y @@ -455,9 +491,12 @@ CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_SYSMON_QMI_COMM=y CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_BOOT_STATS=y CONFIG_QCOM_DCC_V2=y CONFIG_QCOM_SECURE_BUFFER=y CONFIG_ICNSS=y +CONFIG_ICNSS_QMI=y +CONFIG_QCOM_EUD=y CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y CONFIG_QCOM_COMMAND_DB=y @@ -465,8 +504,13 @@ CONFIG_QCOM_EARLY_RANDOM=y CONFIG_QTI_RPMH_API=y CONFIG_QCOM_GLINK=y CONFIG_QCOM_GLINK_PKT=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_MSM_CDSP_LOADER=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_PM=y +CONFIG_QCOM_FSA4480_I2C=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_QMP_DEBUGFS_CLIENT=y CONFIG_DEVFREQ_GOV_PASSIVE=y CONFIG_QCOM_BIMC_BWMON=y CONFIG_ARM_MEMLAT_MON=y @@ -477,20 +521,26 @@ CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y CONFIG_DEVFREQ_GOV_MEMLAT=y CONFIG_DEVFREQ_SIMPLE_DEV=y CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON_USB_GPIO=y CONFIG_IIO=y CONFIG_QCOM_SPMI_ADC5=y CONFIG_PWM=y +CONFIG_PWM_QTI_LPG=y +CONFIG_QCOM_KGSL=y CONFIG_ARM_GIC_V3_ACL=y CONFIG_QCOM_LLCC_PMU=y CONFIG_RAS=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y +CONFIG_NVMEM_SPMI_SDAM=y +CONFIG_SENSORS_SSC=y CONFIG_ESOC=y CONFIG_ESOC_DEV=y CONFIG_ESOC_CLIENT=y CONFIG_ESOC_MDM_4x=y CONFIG_ESOC_MDM_DRV=y CONFIG_ESOC_MDM_DBG_ENG=y +CONFIG_MSM_TZ_LOG=y CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y @@ -503,6 +553,7 @@ CONFIG_VFAT_FS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_ECRYPT_FS=y CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_PRINTK_TIME=y @@ -523,6 +574,8 @@ CONFIG_CORESIGHT_TPDA=y CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_HWEVENT=y CONFIG_CORESIGHT_DUMMY=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 CONFIG_CORESIGHT_EVENT=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y @@ -534,7 +587,9 @@ CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_ANSI_CPRNG=y -CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCEDEV=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_CRYPTO_SHA2_ARM64_CE=y diff --git a/arch/arm64/configs/sdm640_defconfig b/arch/arm64/configs/sdmsteppe_defconfig similarity index 90% rename from arch/arm64/configs/sdm640_defconfig rename to arch/arm64/configs/sdmsteppe_defconfig index f46e79f7f86a93f3190b7fa74a818c77c48ead62..60c548962a1a2243fe7f36c7226e87912fc578a4 100644 --- a/arch/arm64/configs/sdm640_defconfig +++ b/arch/arm64/configs/sdmsteppe_defconfig @@ -14,6 +14,8 @@ CONFIG_RCU_NOCB_CPU=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y CONFIG_RT_GROUP_SCHED=y CONFIG_CGROUP_FREEZER=y CONFIG_CPUSETS=y @@ -52,7 +54,7 @@ CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y # CONFIG_IOSCHED_DEADLINE is not set CONFIG_ARCH_QCOM=y -CONFIG_ARCH_SDM640=y +CONFIG_ARCH_SM6150=y CONFIG_PCI=y CONFIG_PCI_MSM=y CONFIG_SCHED_MC=y @@ -95,6 +97,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y CONFIG_INET=y +CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y @@ -130,6 +133,7 @@ CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LOG=y CONFIG_NETFILTER_XT_TARGET_MARK=y CONFIG_NETFILTER_XT_TARGET_NFLOG=y @@ -159,12 +163,14 @@ CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -183,6 +189,7 @@ CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y @@ -222,12 +229,16 @@ CONFIG_QRTR_SMD=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y CONFIG_BT=y CONFIG_MSM_BT_POWER=y CONFIG_CFG80211=y +CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_RFKILL=y +CONFIG_NFC_NQ=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y CONFIG_DMA_CMA=y @@ -272,6 +283,7 @@ CONFIG_PPPOLAC=y CONFIG_PPPOPNS=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y +CONFIG_WIL6210=m CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_CLD_LL_CORE=y CONFIG_INPUT_EVDEV=y @@ -280,6 +292,7 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_UINPUT=y # CONFIG_SERIO_SERPORT is not set @@ -290,6 +303,7 @@ CONFIG_SERIAL_MSM_GENI_CONSOLE=y CONFIG_SERIAL_DEV_BUS=y CONFIG_TTY_PRINTK=y CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_DIAG_CHAR=y CONFIG_MSM_ADSPRPC=y CONFIG_I2C_CHARDEV=y @@ -304,12 +318,13 @@ CONFIG_PM855B_PMIC_SIMULATOR=y CONFIG_PM855L_PMIC_SIMULATOR=y CONFIG_SLIMBUS_MSM_NGD=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y -CONFIG_PINCTRL_SDM640=y +CONFIG_PINCTRL_SM6150=y CONFIG_GPIO_SYSFS=y CONFIG_POWER_RESET_QCOM=y CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_FG_GEN4=y CONFIG_QPNP_SMB5=y CONFIG_THERMAL=y CONFIG_THERMAL_WRITABLE_TRIPS=y @@ -322,7 +337,10 @@ CONFIG_THERMAL_TSENS=y CONFIG_QTI_THERMAL_LIMITS_DCVS=y CONFIG_QTI_VIRTUAL_SENSOR=y CONFIG_QTI_AOP_REG_COOLING_DEVICE=y +CONFIG_QTI_QMI_COOLING_DEVICE=y CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_QTI_BCL_PMIC5=y +CONFIG_QTI_BCL_SOC_DRIVER=y CONFIG_MFD_SPMI_PMIC=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_QPNP_LCDB=y @@ -344,10 +362,12 @@ CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y CONFIG_MSM_NPU=y CONFIG_DRM=y CONFIG_DRM_MSM_REGISTER_LOGGING=y +CONFIG_DRM_SDE_EVTLOG_DEBUG=y CONFIG_DRM_SDE_RSC=y CONFIG_FB_VIRTUAL=y CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set @@ -359,7 +379,11 @@ CONFIG_SND_USB_AUDIO_QMI=y CONFIG_SND_SOC=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y @@ -369,6 +393,7 @@ CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_HCD_PLATFORM=y CONFIG_USB_STORAGE=y CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y CONFIG_USB_ISP1760=y CONFIG_USB_ISP1760_HOST_ROLE=y CONFIG_NOP_USB_XCEIV=y @@ -377,7 +402,7 @@ CONFIG_USB_MSM_SSPHY_QMP=y CONFIG_MSM_HSUSB_PHY=y CONFIG_DUAL_ROLE_USB_INTF=y CONFIG_USB_GADGET=y -CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_NCM=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y @@ -392,17 +417,27 @@ CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y CONFIG_USB_CONFIGFS_F_CDEV=y CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_QDSS=y CONFIG_USB_PD_POLICY=y CONFIG_QPNP_USB_PDPHY=y CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y CONFIG_MMC_TEST=y +CONFIG_MMC_RING_BUFFER=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_MSM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_HAPTICS=y +CONFIG_LEDS_QTI_TRI_LED=y +CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_EDAC=y CONFIG_EDAC_KRYO_ARM64=y CONFIG_EDAC_KRYO_ARM64_PANIC_ON_CE=y @@ -424,6 +459,7 @@ CONFIG_QCOM_GENI_SE=y CONFIG_QPNP_REVID=y CONFIG_SPS=y CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_USB_BAM=y CONFIG_IPA3=y CONFIG_IPA_WDI_UNIFIED_API=y CONFIG_RMNET_IPA3=y @@ -454,9 +490,11 @@ CONFIG_IOMMU_DEBUG_TRACKING=y CONFIG_IOMMU_TESTS=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_QCOM_CPUSS_DUMP=y CONFIG_QCOM_RUN_QUEUE_STATS=y CONFIG_QCOM_LLCC=y -CONFIG_QCOM_SM8150_LLCC=y +CONFIG_QCOM_SM6150_LLCC=y CONFIG_QCOM_QMI_HELPERS=y CONFIG_QCOM_SMEM=y CONFIG_QCOM_MEMORY_DUMP_V2=y @@ -465,28 +503,29 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_WDOG_IPI_ENABLE=y CONFIG_QCOM_SMP2P=y CONFIG_MSM_SERVICE_LOCATOR=y -CONFIG_MSM_SERVICE_NOTIFIER=y -CONFIG_MSM_SUBSYSTEM_RESTART=y -CONFIG_MSM_PIL=y -CONFIG_MSM_SYSMON_QMI_COMM=y -CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_CORE_HANG_DETECT=y CONFIG_QCOM_DCC_V2=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y CONFIG_QCOM_SECURE_BUFFER=y CONFIG_ICNSS=y CONFIG_ICNSS_DEBUG=y +CONFIG_ICNSS_QMI=y +CONFIG_QCOM_EUD=y CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y CONFIG_QCOM_COMMAND_DB=y CONFIG_QCOM_EARLY_RANDOM=y -CONFIG_MSM_SPSS_UTILS=y CONFIG_QTI_RPMH_API=y CONFIG_QCOM_GLINK=y CONFIG_QCOM_GLINK_PKT=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_MSM_CDSP_LOADER=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_PM=y CONFIG_QCOM_FSA4480_I2C=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_QMP_DEBUGFS_CLIENT=y CONFIG_DEVFREQ_GOV_PASSIVE=y CONFIG_QCOM_BIMC_BWMON=y CONFIG_ARM_MEMLAT_MON=y @@ -497,17 +536,20 @@ CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y CONFIG_DEVFREQ_GOV_MEMLAT=y CONFIG_DEVFREQ_SIMPLE_DEV=y CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON_USB_GPIO=y CONFIG_IIO=y CONFIG_QCOM_SPMI_ADC5=y CONFIG_PWM=y +CONFIG_PWM_QTI_LPG=y CONFIG_QCOM_KGSL=y CONFIG_ARM_GIC_V3_ACL=y -CONFIG_QTI_PDC_SM8150=y CONFIG_PHY_XGENE=y CONFIG_QCOM_LLCC_PMU=y CONFIG_RAS=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y +CONFIG_NVMEM_SPMI_SDAM=y +CONFIG_SENSORS_SSC=y CONFIG_ESOC=y CONFIG_ESOC_DEV=y CONFIG_ESOC_CLIENT=y @@ -515,6 +557,7 @@ CONFIG_ESOC_DEBUG=y CONFIG_ESOC_MDM_4x=y CONFIG_ESOC_MDM_DRV=y CONFIG_ESOC_MDM_DBG_ENG=y +CONFIG_MSM_TZ_LOG=y CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y @@ -528,6 +571,7 @@ CONFIG_TMPFS_POSIX_ACL=y CONFIG_EFIVAR_FS=y CONFIG_ECRYPT_FS=y CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y # CONFIG_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y @@ -565,9 +609,11 @@ CONFIG_SCHED_STACK_END_CHECK=y CONFIG_DEBUG_SPINLOCK=y CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_LOCK_TORTURE_TEST=m CONFIG_DEBUG_SG=y CONFIG_DEBUG_NOTIFIERS=y CONFIG_DEBUG_CREDENTIALS=y +CONFIG_RCU_TORTURE_TEST=m CONFIG_FAULT_INJECTION=y CONFIG_FAIL_PAGE_ALLOC=y CONFIG_UFS_FAULT_INJECTION=y @@ -580,6 +626,9 @@ CONFIG_FUNCTION_TRACER=y CONFIG_IRQSOFF_TRACER=y CONFIG_PREEMPT_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_LKDTM=m +CONFIG_ATOMIC64_SELFTEST=m +CONFIG_TEST_USER_COPY=m CONFIG_MEMTEST=y CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_PID_IN_CONTEXTIDR=y @@ -594,6 +643,8 @@ CONFIG_CORESIGHT_TPDA=y CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_HWEVENT=y CONFIG_CORESIGHT_DUMMY=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 CONFIG_CORESIGHT_TGU=y CONFIG_CORESIGHT_EVENT=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y @@ -607,7 +658,9 @@ CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_ANSI_CPRNG=y -CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCEDEV=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_CRYPTO_SHA2_ARM64_CE=y diff --git a/arch/arm64/configs/sm8150-auto-perf_defconfig b/arch/arm64/configs/sm8150-auto-perf_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..9f890929e330fe5ebcd961c76548fbc31523e884 --- /dev/null +++ b/arch/arm64/configs/sm8150-auto-perf_defconfig @@ -0,0 +1,614 @@ +CONFIG_LOCALVERSION="-perf" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_SCHED_WALT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_RCU_NOCB_CPU=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_SCHED_CORE_CTL=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_SCHED_AUTOGROUP=y +CONFIG_SCHED_TUNE=y +CONFIG_DEFAULT_USE_ENERGY_AWARE=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_KALLSYMS_ALL=y +CONFIG_BPF_SYSCALL=y +# CONFIG_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_SM8150=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +CONFIG_HZ_100=y +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_SECCOMP=y +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +# CONFIG_HARDEN_BRANCH_PREDICTOR is not set +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +# CONFIG_ARM64_VHE is not set +CONFIG_RANDOMIZE_BASE=y +# CONFIG_EFI is not set +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_BOOST=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y +CONFIG_QRTR_MHI=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_RFKILL=y +CONFIG_NFC_NQ=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y +CONFIG_DMA_CMA=y +CONFIG_MHI_BUS=y +CONFIG_MHI_QCOM=y +CONFIG_MHI_NETDEV=y +CONFIG_MHI_UCI=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_QSEECOM=y +CONFIG_UID_SYS_STATS=y +CONFIG_MEMORY_STATE_TIME=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_SKY2=y +CONFIG_RMNET=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_USBNET=y +CONFIG_WIL6210=m +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_CLD_LL_CORE=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ST=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +CONFIG_SERIAL_MSM_GENI=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_DIAG_CHAR=y +CONFIG_MSM_FASTCVPD=y +CONFIG_MSM_ADSPRPC=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QCOM_GENI=y +CONFIG_SPI=y +CONFIG_SPI_QCOM_GENI=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_SPMI_SIMULATOR=y +CONFIG_PM855_PMIC_SIMULATOR=y +CONFIG_PM855B_PMIC_SIMULATOR=y +CONFIG_PM855L_PMIC_SIMULATOR=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_SM8150=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_FG_GEN4=y +CONFIG_QPNP_SMB5=y +CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_QCOM_SPMI_TEMP_ALARM=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_THERMAL_LIMITS_DCVS=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_AOP_REG_COOLING_DEVICE=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_QTI_BCL_PMIC5=y +CONFIG_QTI_BCL_SOC_DRIVER=y +CONFIG_QTI_ADC_TM=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_QPNP_LCDB=y +CONFIG_REGULATOR_REFGEN=y +CONFIG_REGULATOR_RPMH=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SPECTRA_CAMERA=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_VIDC_GOVERNORS=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_MSM_NPU=y +CONFIG_DRM=y +CONFIG_DRM_MSM_REGISTER_LOGGING=y +CONFIG_DRM_SDE_EVTLOG_DEBUG=y +CONFIG_DRM_SDE_RSC=y +CONFIG_FB_ARMCLCD=y +CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y +CONFIG_SND_SOC=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_QCOM_EMU_PHY=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_HSUSB_PHY=y +CONFIG_DUAL_ROLE_USB_INTF=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=900 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_USB_PD_POLICY=y +CONFIG_QPNP_USB_PDPHY=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_MMC_TEST=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_HAPTICS=y +CONFIG_LEDS_QTI_TRI_LED=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_EDAC=y +CONFIG_EDAC_KRYO_ARM64=y +CONFIG_EDAC_KRYO_ARM64_PANIC_ON_CE=y +CONFIG_EDAC_KRYO_ARM64_PANIC_ON_UE=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_GPI_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ION=y +CONFIG_QCOM_GENI_SE=y +CONFIG_QPNP_REVID=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_USB_BAM=y +CONFIG_IPA3=y +CONFIG_IPA_WDI_UNIFIED_API=y +CONFIG_RMNET_IPA3=y +CONFIG_RNDIS_IPA=y +CONFIG_IPA_UT=y +CONFIG_MSM_11AD=m +CONFIG_QCOM_MDSS_PLL=y +CONFIG_SPMI_PMIC_CLKDIV=y +CONFIG_MSM_CLK_AOP_QMP=y +CONFIG_MSM_GCC_SM8150=y +CONFIG_MSM_NPUCC_SM8150=y +CONFIG_MSM_VIDEOCC_SM8150=y +CONFIG_MSM_CAMCC_SM8150=y +CONFIG_CLOCK_CPU_OSM=y +CONFIG_MSM_DISPCC_SM8150=y +CONFIG_MSM_DEBUGCC_SM8150=y +CONFIG_MSM_CLK_RPMH=y +CONFIG_MSM_GPUCC_SM8150=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_QCOM_APCS_IPC=y +CONFIG_MSM_QMP=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_ARM_SMMU=y +CONFIG_QCOM_LAZY_MAPPING=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_RPMSG_QCOM_GLINK_SPI=y +CONFIG_QCOM_CPUSS_DUMP=y +CONFIG_QCOM_RUN_QUEUE_STATS=y +CONFIG_QCOM_LLCC=y +CONFIG_QCOM_SM8150_LLCC=y +CONFIG_QCOM_QMI_HELPERS=y +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_QCOM_SMP2P=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_SYSMON_QMI_COMM=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_QCOM_DCC_V2=y +CONFIG_QCOM_SECURE_BUFFER=y +CONFIG_ICNSS=y +CONFIG_ICNSS_QMI=y +CONFIG_QCOM_EUD=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_QCOM_BUS_CONFIG_RPMH=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_EARLY_RANDOM=y +CONFIG_MSM_SPSS_UTILS=y +CONFIG_MSM_SPCOM=y +CONFIG_QTI_RPMH_API=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y +CONFIG_QCOM_GLINK=y +CONFIG_QCOM_GLINK_PKT=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_MSM_CDSP_LOADER=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_PM=y +CONFIG_QCOM_FSA4480_I2C=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_QMP_DEBUGFS_CLIENT=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_QCOMCCI_HWMON=y +CONFIG_QCOM_M4M_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_DEVFREQ_SIMPLE_DEV=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_IIO=y +CONFIG_QCOM_SPMI_ADC5=y +CONFIG_PWM=y +CONFIG_PWM_QTI_LPG=y +CONFIG_QCOM_KGSL=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_ARM_DSU_PMU=y +CONFIG_QCOM_LLCC_PMU=y +CONFIG_RAS=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_NVMEM_SPMI_SDAM=y +CONFIG_SENSORS_SSC=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +CONFIG_ESOC_MDM_4x=y +CONFIG_ESOC_MDM_DRV=y +CONFIG_ESOC_MDM_DBG_ENG=y +CONFIG_MSM_TZ_LOG=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_IPC_LOGGING=y +CONFIG_DEBUG_ALIGN_RODATA=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_DUMMY=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_EVENT=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y +CONFIG_SECURITY=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_FORTIFY_SOURCE=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y diff --git a/arch/arm64/configs/sm8150-auto_defconfig b/arch/arm64/configs/sm8150-auto_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..4db51119b7ce98c4dba88c35edc837276680afb9 --- /dev/null +++ b/arch/arm64/configs/sm8150-auto_defconfig @@ -0,0 +1,694 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_SCHED_WALT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_RCU_NOCB_CPU=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_CGROUP_DEBUG=y +CONFIG_SCHED_CORE_CTL=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_SCHED_AUTOGROUP=y +CONFIG_SCHED_TUNE=y +CONFIG_DEFAULT_USE_ENERGY_AWARE=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +CONFIG_BPF_SYSCALL=y +# CONFIG_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_SM8150=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +CONFIG_HZ_100=y +CONFIG_CLEANCACHE=y +CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y +CONFIG_ZSMALLOC=y +CONFIG_SECCOMP=y +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +# CONFIG_HARDEN_BRANCH_PREDICTOR is not set +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +# CONFIG_ARM64_VHE is not set +CONFIG_RANDOMIZE_BASE=y +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_BOOST=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_DNS_RESOLVER=y +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y +CONFIG_QRTR_MHI=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y +# CONFIG_CFG80211_CRDA_SUPPORT is not set +CONFIG_RFKILL=y +CONFIG_NFC_NQ=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y +CONFIG_DMA_CMA=y +CONFIG_MHI_BUS=y +CONFIG_MHI_DEBUG=y +CONFIG_MHI_QCOM=y +CONFIG_MHI_NETDEV=y +CONFIG_MHI_UCI=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_QSEECOM=y +CONFIG_UID_SYS_STATS=y +CONFIG_MEMORY_STATE_TIME=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_SCSI_UFSHCD_CMD_LOGGING=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_RMNET=y +CONFIG_PHYLIB=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_WIL6210=m +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_CLD_LL_CORE=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ST=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM_GENI=y +CONFIG_SERIAL_MSM_GENI_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_DIAG_CHAR=y +CONFIG_MSM_FASTCVPD=y +CONFIG_MSM_ADSPRPC=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QCOM_GENI=y +CONFIG_SPI=y +CONFIG_SPI_QCOM_GENI=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_SPMI_SIMULATOR=y +CONFIG_PM855_PMIC_SIMULATOR=y +CONFIG_PM855B_PMIC_SIMULATOR=y +CONFIG_PM855L_PMIC_SIMULATOR=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_SM8150=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_FG_GEN4=y +CONFIG_QPNP_SMB5=y +CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_LOW_LIMITS=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_QCOM_SPMI_TEMP_ALARM=y +CONFIG_THERMAL_TSENS=y +CONFIG_QTI_THERMAL_LIMITS_DCVS=y +CONFIG_QTI_VIRTUAL_SENSOR=y +CONFIG_QTI_AOP_REG_COOLING_DEVICE=y +CONFIG_QTI_QMI_COOLING_DEVICE=y +CONFIG_REGULATOR_COOLING_DEVICE=y +CONFIG_QTI_BCL_PMIC5=y +CONFIG_QTI_BCL_SOC_DRIVER=y +CONFIG_QTI_ADC_TM=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_QPNP_LCDB=y +CONFIG_REGULATOR_REFGEN=y +CONFIG_REGULATOR_RPMH=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SPECTRA_CAMERA=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_VIDC_GOVERNORS=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_MSM_NPU=y +CONFIG_DRM=y +CONFIG_DRM_MSM_REGISTER_LOGGING=y +CONFIG_DRM_SDE_EVTLOG_DEBUG=y +CONFIG_DRM_SDE_RSC=y +CONFIG_FB_VIRTUAL=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y +CONFIG_SND_SOC=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_MSM=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_QCOM_EMU_PHY=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_HSUSB_PHY=y +CONFIG_DUAL_ROLE_USB_INTF=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=900 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_USB_PD_POLICY=y +CONFIG_QPNP_USB_PDPHY=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_MMC_TEST=y +CONFIG_MMC_RING_BUFFER=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_HAPTICS=y +CONFIG_LEDS_QTI_TRI_LED=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_EDAC=y +CONFIG_EDAC_KRYO_ARM64=y +CONFIG_EDAC_KRYO_ARM64_PANIC_ON_CE=y +CONFIG_EDAC_KRYO_ARM64_PANIC_ON_UE=y +CONFIG_EDAC_QCOM_LLCC=y +CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE=y +CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_GPI_DMA=y +CONFIG_QCOM_GPI_DMA_DEBUG=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ION=y +CONFIG_QCOM_GENI_SE=y +CONFIG_QPNP_REVID=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_USB_BAM=y +CONFIG_IPA3=y +CONFIG_IPA_WDI_UNIFIED_API=y +CONFIG_RMNET_IPA3=y +CONFIG_RNDIS_IPA=y +CONFIG_IPA_UT=y +CONFIG_MSM_11AD=m +CONFIG_QCOM_MDSS_PLL=y +CONFIG_SPMI_PMIC_CLKDIV=y +CONFIG_MSM_CLK_AOP_QMP=y +CONFIG_MSM_GCC_SM8150=y +CONFIG_MSM_NPUCC_SM8150=y +CONFIG_MSM_VIDEOCC_SM8150=y +CONFIG_MSM_CAMCC_SM8150=y +CONFIG_CLOCK_CPU_OSM=y +CONFIG_MSM_DISPCC_SM8150=y +CONFIG_MSM_DEBUGCC_SM8150=y +CONFIG_MSM_CLK_RPMH=y +CONFIG_MSM_GPUCC_SM8150=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_QCOM_APCS_IPC=y +CONFIG_MSM_QMP=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_ARM_SMMU=y +CONFIG_QCOM_LAZY_MAPPING=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_RPMSG_QCOM_GLINK_SPI=y +CONFIG_QCOM_CPUSS_DUMP=y +CONFIG_QCOM_RUN_QUEUE_STATS=y +CONFIG_QCOM_LLCC=y +CONFIG_QCOM_SM8150_LLCC=y +CONFIG_QCOM_QMI_HELPERS=y +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_QCOM_WDOG_IPI_ENABLE=y +CONFIG_QCOM_SMP2P=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_SYSMON_QMI_COMM=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_QCOM_DCC_V2=y +CONFIG_MSM_GLADIATOR_HANG_DETECT=y +CONFIG_QCOM_SECURE_BUFFER=y +CONFIG_ICNSS=y +CONFIG_ICNSS_DEBUG=y +CONFIG_ICNSS_QMI=y +CONFIG_QCOM_EUD=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_QCOM_BUS_CONFIG_RPMH=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_EARLY_RANDOM=y +CONFIG_MSM_SPSS_UTILS=y +CONFIG_MSM_SPCOM=y +CONFIG_QTI_RPMH_API=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y +CONFIG_QCOM_GLINK=y +CONFIG_QCOM_GLINK_PKT=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_MSM_CDSP_LOADER=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_PM=y +CONFIG_QCOM_FSA4480_I2C=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_QMP_DEBUGFS_CLIENT=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_QCOMCCI_HWMON=y +CONFIG_QCOM_M4M_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_DEVFREQ_SIMPLE_DEV=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_IIO=y +CONFIG_QCOM_SPMI_ADC5=y +CONFIG_PWM=y +CONFIG_PWM_QTI_LPG=y +CONFIG_QCOM_KGSL=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_PHY_XGENE=y +CONFIG_ARM_DSU_PMU=y +CONFIG_QCOM_LLCC_PMU=y +CONFIG_RAS=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_NVMEM_SPMI_SDAM=y +CONFIG_SENSORS_SSC=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +CONFIG_ESOC_DEBUG=y +CONFIG_ESOC_MDM_4x=y +CONFIG_ESOC_MDM_DRV=y +CONFIG_ESOC_MDM_DBG_ENG=y +CONFIG_MSM_TZ_LOG=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_EFIVAR_FS=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_PAGE_OWNER=y +CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y +CONFIG_DEBUG_SECTION_MISMATCH=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_SLUB_DEBUG_PANIC_ON=y +CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y +CONFIG_PAGE_POISONING=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +CONFIG_SLUB_DEBUG_ON=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_WQ_WATCHDOG=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_PANIC_ON_SCHED_BUG=y +CONFIG_PANIC_ON_RT_THROTTLING=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_LOCK_TORTURE_TEST=m +CONFIG_DEBUG_SG=y +CONFIG_DEBUG_NOTIFIERS=y +CONFIG_DEBUG_CREDENTIALS=y +CONFIG_RCU_TORTURE_TEST=m +CONFIG_FAULT_INJECTION=y +CONFIG_FAIL_PAGE_ALLOC=y +CONFIG_UFS_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y +CONFIG_IPC_LOGGING=y +CONFIG_QCOM_RTB=y +CONFIG_QCOM_RTB_SEPARATE_CPUS=y +CONFIG_FUNCTION_TRACER=y +CONFIG_PREEMPTIRQ_EVENTS=y +CONFIG_IRQSOFF_TRACER=y +CONFIG_PREEMPT_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_LKDTM=m +CONFIG_ATOMIC64_SELFTEST=m +CONFIG_TEST_USER_COPY=m +CONFIG_MEMTEST=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +CONFIG_PID_IN_CONTEXTIDR=y +CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_DUMMY=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_TGU=y +CONFIG_CORESIGHT_EVENT=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y +CONFIG_SECURITY=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_HARDENED_USERCOPY_PAGESPAN=y +CONFIG_FORTIFY_SOURCE=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +CONFIG_XZ_DEC=y diff --git a/arch/arm64/configs/sm8150-perf_defconfig b/arch/arm64/configs/sm8150-perf_defconfig index 926d06fd7b66879d3da7f518a93b3a1acb48b90b..68a24be453eab0a82c0b36ab4fb196a5bbde4900 100644 --- a/arch/arm64/configs/sm8150-perf_defconfig +++ b/arch/arm64/configs/sm8150-perf_defconfig @@ -43,6 +43,7 @@ CONFIG_SLAB_FREELIST_RANDOM=y CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_PROFILING=y CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_REFCOUNT_FULL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y @@ -141,6 +142,7 @@ CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -158,8 +160,10 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y @@ -221,6 +225,7 @@ CONFIG_NET_ACT_MIRRED=y CONFIG_NET_ACT_SKBEDIT=y CONFIG_QRTR=y CONFIG_QRTR_SMD=y +CONFIG_QRTR_MHI=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y @@ -228,16 +233,23 @@ CONFIG_SOCKEV_NLMCAST=y CONFIG_BT=y CONFIG_MSM_BT_POWER=y CONFIG_CFG80211=y +CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y CONFIG_RFKILL=y CONFIG_NFC_NQ=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y CONFIG_DMA_CMA=y +CONFIG_MHI_BUS=y +CONFIG_MHI_QCOM=y +CONFIG_MHI_NETDEV=y +CONFIG_MHI_UCI=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_HDCP_QSEECOM=y CONFIG_QSEECOM=y CONFIG_UID_SYS_STATS=y CONFIG_MEMORY_STATE_TIME=y @@ -282,6 +294,7 @@ CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ST=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y @@ -294,6 +307,7 @@ CONFIG_SERIAL_MSM_GENI=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_DIAG_CHAR=y +CONFIG_MSM_FASTCVPD=y CONFIG_MSM_ADSPRPC=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_QCOM_GENI=y @@ -315,6 +329,7 @@ CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y CONFIG_QPNP_FG_GEN4=y CONFIG_QPNP_SMB5=y +CONFIG_SMB1390_CHARGE_PUMP=y CONFIG_THERMAL=y CONFIG_THERMAL_WRITABLE_TRIPS=y CONFIG_THERMAL_GOV_USER_SPACE=y @@ -330,8 +345,11 @@ CONFIG_QTI_QMI_COOLING_DEVICE=y CONFIG_REGULATOR_COOLING_DEVICE=y CONFIG_QTI_BCL_PMIC5=y CONFIG_QTI_BCL_SOC_DRIVER=y +CONFIG_QTI_ADC_TM=y +CONFIG_MFD_I2C_PMIC=y CONFIG_MFD_SPMI_PMIC=y CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PROXY_CONSUMER=y CONFIG_REGULATOR_QPNP_LCDB=y CONFIG_REGULATOR_REFGEN=y CONFIG_REGULATOR_RPMH=y @@ -366,7 +384,11 @@ CONFIG_SND_USB_AUDIO_QMI=y CONFIG_SND_SOC=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y @@ -442,6 +464,7 @@ CONFIG_IPA3=y CONFIG_IPA_WDI_UNIFIED_API=y CONFIG_RMNET_IPA3=y CONFIG_RNDIS_IPA=y +CONFIG_IPA3_MHI_PROXY=y CONFIG_IPA_UT=y CONFIG_MSM_11AD=m CONFIG_QCOM_MDSS_PLL=y @@ -469,6 +492,7 @@ CONFIG_IOMMU_TESTS=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_QCOM_GLINK_SMEM=y CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_RPMSG_QCOM_GLINK_SPI=y CONFIG_QCOM_CPUSS_DUMP=y CONFIG_QCOM_RUN_QUEUE_STATS=y CONFIG_QCOM_LLCC=y @@ -495,10 +519,15 @@ CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y CONFIG_QCOM_COMMAND_DB=y CONFIG_QCOM_EARLY_RANDOM=y +CONFIG_MSM_SPSS_UTILS=y +CONFIG_MSM_SPCOM=y CONFIG_QTI_RPMH_API=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y CONFIG_QCOM_GLINK=y CONFIG_QCOM_GLINK_PKT=y +CONFIG_QTI_RPM_STATS_LOG=y CONFIG_MSM_CDSP_LOADER=y +CONFIG_QCOM_SMCINVOKE=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_PM=y CONFIG_QCOM_FSA4480_I2C=y @@ -521,6 +550,7 @@ CONFIG_PWM=y CONFIG_PWM_QTI_LPG=y CONFIG_QCOM_KGSL=y CONFIG_ARM_GIC_V3_ACL=y +CONFIG_ARM_DSU_PMU=y CONFIG_QCOM_LLCC_PMU=y CONFIG_RAS=y CONFIG_ANDROID=y @@ -538,6 +568,7 @@ CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y CONFIG_FUSE_FS=y diff --git a/arch/arm64/configs/sm8150_defconfig b/arch/arm64/configs/sm8150_defconfig index 1557925a6bf9bb67fd706e6159bf168f75ac7794..6e3a07d543c51982f4d3fb26605974e5f18e74e6 100644 --- a/arch/arm64/configs/sm8150_defconfig +++ b/arch/arm64/configs/sm8150_defconfig @@ -43,6 +43,8 @@ CONFIG_SLAB_FREELIST_RANDOM=y CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_PROFILING=y CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_REFCOUNT_FULL=y +CONFIG_PANIC_ON_REFCOUNT_ERROR=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y @@ -63,10 +65,12 @@ CONFIG_PREEMPT=y CONFIG_HZ_100=y CONFIG_CLEANCACHE=y CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y CONFIG_ZSMALLOC=y CONFIG_SECCOMP=y # CONFIG_UNMAP_KERNEL_AT_EL0 is not set # CONFIG_HARDEN_BRANCH_PREDICTOR is not set +CONFIG_PRINT_VMEMLAYOUT=y CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_CP15_BARRIER_EMULATION=y @@ -144,6 +148,7 @@ CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -161,8 +166,10 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y @@ -226,6 +233,7 @@ CONFIG_NET_ACT_SKBEDIT=y CONFIG_DNS_RESOLVER=y CONFIG_QRTR=y CONFIG_QRTR_SMD=y +CONFIG_QRTR_MHI=y CONFIG_RMNET_DATA=y CONFIG_RMNET_DATA_FC=y CONFIG_RMNET_DATA_DEBUG_PKT=y @@ -233,6 +241,8 @@ CONFIG_SOCKEV_NLMCAST=y CONFIG_BT=y CONFIG_MSM_BT_POWER=y CONFIG_CFG80211=y +CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_RFKILL=y @@ -240,10 +250,16 @@ CONFIG_NFC_NQ=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y CONFIG_DMA_CMA=y +CONFIG_MHI_BUS=y +CONFIG_MHI_DEBUG=y +CONFIG_MHI_QCOM=y +CONFIG_MHI_NETDEV=y +CONFIG_MHI_UCI=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_HDCP_QSEECOM=y CONFIG_QSEECOM=y CONFIG_UID_SYS_STATS=y CONFIG_MEMORY_STATE_TIME=y @@ -289,6 +305,7 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ST=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y @@ -303,6 +320,7 @@ CONFIG_TTY_PRINTK=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_DIAG_CHAR=y +CONFIG_MSM_FASTCVPD=y CONFIG_MSM_ADSPRPC=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_QCOM_GENI=y @@ -324,6 +342,7 @@ CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y CONFIG_QPNP_FG_GEN4=y CONFIG_QPNP_SMB5=y +CONFIG_SMB1390_CHARGE_PUMP=y CONFIG_THERMAL=y CONFIG_THERMAL_WRITABLE_TRIPS=y CONFIG_THERMAL_GOV_USER_SPACE=y @@ -339,8 +358,11 @@ CONFIG_QTI_QMI_COOLING_DEVICE=y CONFIG_REGULATOR_COOLING_DEVICE=y CONFIG_QTI_BCL_PMIC5=y CONFIG_QTI_BCL_SOC_DRIVER=y +CONFIG_QTI_ADC_TM=y +CONFIG_MFD_I2C_PMIC=y CONFIG_MFD_SPMI_PMIC=y CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PROXY_CONSUMER=y CONFIG_REGULATOR_QPNP_LCDB=y CONFIG_REGULATOR_REFGEN=y CONFIG_REGULATOR_RPMH=y @@ -377,7 +399,11 @@ CONFIG_SND_USB_AUDIO_QMI=y CONFIG_SND_SOC=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y @@ -458,6 +484,7 @@ CONFIG_IPA3=y CONFIG_IPA_WDI_UNIFIED_API=y CONFIG_RMNET_IPA3=y CONFIG_RNDIS_IPA=y +CONFIG_IPA3_MHI_PROXY=y CONFIG_IPA_UT=y CONFIG_MSM_11AD=m CONFIG_QCOM_MDSS_PLL=y @@ -485,6 +512,7 @@ CONFIG_IOMMU_TESTS=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_QCOM_GLINK_SMEM=y CONFIG_RPMSG_QCOM_GLINK_SPSS=y +CONFIG_RPMSG_QCOM_GLINK_SPI=y CONFIG_QCOM_CPUSS_DUMP=y CONFIG_QCOM_RUN_QUEUE_STATS=y CONFIG_QCOM_LLCC=y @@ -516,10 +544,14 @@ CONFIG_QCOM_BUS_CONFIG_RPMH=y CONFIG_QCOM_COMMAND_DB=y CONFIG_QCOM_EARLY_RANDOM=y CONFIG_MSM_SPSS_UTILS=y +CONFIG_MSM_SPCOM=y CONFIG_QTI_RPMH_API=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y CONFIG_QCOM_GLINK=y CONFIG_QCOM_GLINK_PKT=y +CONFIG_QTI_RPM_STATS_LOG=y CONFIG_MSM_CDSP_LOADER=y +CONFIG_QCOM_SMCINVOKE=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_PM=y CONFIG_QCOM_FSA4480_I2C=y @@ -543,6 +575,7 @@ CONFIG_PWM_QTI_LPG=y CONFIG_QCOM_KGSL=y CONFIG_ARM_GIC_V3_ACL=y CONFIG_PHY_XGENE=y +CONFIG_ARM_DSU_PMU=y CONFIG_QCOM_LLCC_PMU=y CONFIG_RAS=y CONFIG_ANDROID=y @@ -561,6 +594,7 @@ CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y CONFIG_FUSE_FS=y @@ -622,6 +656,7 @@ CONFIG_IPC_LOGGING=y CONFIG_QCOM_RTB=y CONFIG_QCOM_RTB_SEPARATE_CPUS=y CONFIG_FUNCTION_TRACER=y +CONFIG_PREEMPTIRQ_EVENTS=y CONFIG_IRQSOFF_TRACER=y CONFIG_PREEMPT_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile index 9be9809fd924b5600cb5178fd9df6e386cbb62d9..e761c0a7a181ceac4095b8764716470dc4f5cd65 100644 --- a/arch/arm64/crypto/Makefile +++ b/arch/arm64/crypto/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_CRYPTO_CRC32_ARM64_CE) += crc32-ce.o crc32-ce-y:= crc32-ce-core.o crc32-ce-glue.o obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o -CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto -Wa,-march=armv8-a+crypto +aes-ce-cipher-y := aes-ce-core.o aes-ce-glue.o obj-$(CONFIG_CRYPTO_AES_ARM64_CE_CCM) += aes-ce-ccm.o aes-ce-ccm-y := aes-ce-ccm-glue.o aes-ce-ccm-core.o @@ -61,6 +61,7 @@ CFLAGS_aes-glue-ce.o := -DUSE_V8_CRYPTO_EXTENSIONS $(obj)/aes-glue-%.o: $(src)/aes-glue.c FORCE $(call if_changed_rule,cc_o_c) +ifdef REGENERATE_ARM64_CRYPTO quiet_cmd_perlasm = PERLASM $@ cmd_perlasm = $(PERL) $(<) void $(@) @@ -69,5 +70,6 @@ $(src)/sha256-core.S_shipped: $(src)/sha512-armv8.pl $(src)/sha512-core.S_shipped: $(src)/sha512-armv8.pl $(call cmd,perlasm) +endif .PRECIOUS: $(obj)/sha256-core.S $(obj)/sha512-core.S diff --git a/arch/arm64/crypto/aes-ce-core.S b/arch/arm64/crypto/aes-ce-core.S new file mode 100644 index 0000000000000000000000000000000000000000..8efdfdade393650ee8627a9a77be204f36116b2a --- /dev/null +++ b/arch/arm64/crypto/aes-ce-core.S @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 - 2017 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + + .arch armv8-a+crypto + +ENTRY(__aes_ce_encrypt) + sub w3, w3, #2 + ld1 {v0.16b}, [x2] + ld1 {v1.4s}, [x0], #16 + cmp w3, #10 + bmi 0f + bne 3f + mov v3.16b, v1.16b + b 2f +0: mov v2.16b, v1.16b + ld1 {v3.4s}, [x0], #16 +1: aese v0.16b, v2.16b + aesmc v0.16b, v0.16b +2: ld1 {v1.4s}, [x0], #16 + aese v0.16b, v3.16b + aesmc v0.16b, v0.16b +3: ld1 {v2.4s}, [x0], #16 + subs w3, w3, #3 + aese v0.16b, v1.16b + aesmc v0.16b, v0.16b + ld1 {v3.4s}, [x0], #16 + bpl 1b + aese v0.16b, v2.16b + eor v0.16b, v0.16b, v3.16b + st1 {v0.16b}, [x1] + ret +ENDPROC(__aes_ce_encrypt) + +ENTRY(__aes_ce_decrypt) + sub w3, w3, #2 + ld1 {v0.16b}, [x2] + ld1 {v1.4s}, [x0], #16 + cmp w3, #10 + bmi 0f + bne 3f + mov v3.16b, v1.16b + b 2f +0: mov v2.16b, v1.16b + ld1 {v3.4s}, [x0], #16 +1: aesd v0.16b, v2.16b + aesimc v0.16b, v0.16b +2: ld1 {v1.4s}, [x0], #16 + aesd v0.16b, v3.16b + aesimc v0.16b, v0.16b +3: ld1 {v2.4s}, [x0], #16 + subs w3, w3, #3 + aesd v0.16b, v1.16b + aesimc v0.16b, v0.16b + ld1 {v3.4s}, [x0], #16 + bpl 1b + aesd v0.16b, v2.16b + eor v0.16b, v0.16b, v3.16b + st1 {v0.16b}, [x1] + ret +ENDPROC(__aes_ce_decrypt) + +/* + * __aes_ce_sub() - use the aese instruction to perform the AES sbox + * substitution on each byte in 'input' + */ +ENTRY(__aes_ce_sub) + dup v1.4s, w0 + movi v0.16b, #0 + aese v0.16b, v1.16b + umov w0, v0.s[0] + ret +ENDPROC(__aes_ce_sub) + +ENTRY(__aes_ce_invert) + ld1 {v0.4s}, [x1] + aesimc v1.16b, v0.16b + st1 {v1.4s}, [x0] + ret +ENDPROC(__aes_ce_invert) diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-glue.c similarity index 62% rename from arch/arm64/crypto/aes-ce-cipher.c rename to arch/arm64/crypto/aes-ce-glue.c index 6a75cd75ed11c60f828ff96b4ed0f09c4b288c6c..e6b3227bbf5798188e7d233d209f5f51376e3e25 100644 --- a/arch/arm64/crypto/aes-ce-cipher.c +++ b/arch/arm64/crypto/aes-ce-glue.c @@ -29,6 +29,13 @@ struct aes_block { u8 b[AES_BLOCK_SIZE]; }; +asmlinkage void __aes_ce_encrypt(u32 *rk, u8 *out, const u8 *in, int rounds); +asmlinkage void __aes_ce_decrypt(u32 *rk, u8 *out, const u8 *in, int rounds); + +asmlinkage u32 __aes_ce_sub(u32 l); +asmlinkage void __aes_ce_invert(struct aes_block *out, + const struct aes_block *in); + static int num_rounds(struct crypto_aes_ctx *ctx) { /* @@ -44,10 +51,6 @@ static int num_rounds(struct crypto_aes_ctx *ctx) static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) { struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); - struct aes_block *out = (struct aes_block *)dst; - struct aes_block const *in = (struct aes_block *)src; - void *dummy0; - int dummy1; if (!may_use_simd()) { __aes_arm64_encrypt(ctx->key_enc, dst, src, num_rounds(ctx)); @@ -55,49 +58,13 @@ static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) } kernel_neon_begin(); - - __asm__(" ld1 {v0.16b}, %[in] ;" - " ld1 {v1.4s}, [%[key]], #16 ;" - " cmp %w[rounds], #10 ;" - " bmi 0f ;" - " bne 3f ;" - " mov v3.16b, v1.16b ;" - " b 2f ;" - "0: mov v2.16b, v1.16b ;" - " ld1 {v3.4s}, [%[key]], #16 ;" - "1: aese v0.16b, v2.16b ;" - " aesmc v0.16b, v0.16b ;" - "2: ld1 {v1.4s}, [%[key]], #16 ;" - " aese v0.16b, v3.16b ;" - " aesmc v0.16b, v0.16b ;" - "3: ld1 {v2.4s}, [%[key]], #16 ;" - " subs %w[rounds], %w[rounds], #3 ;" - " aese v0.16b, v1.16b ;" - " aesmc v0.16b, v0.16b ;" - " ld1 {v3.4s}, [%[key]], #16 ;" - " bpl 1b ;" - " aese v0.16b, v2.16b ;" - " eor v0.16b, v0.16b, v3.16b ;" - " st1 {v0.16b}, %[out] ;" - - : [out] "=Q"(*out), - [key] "=r"(dummy0), - [rounds] "=r"(dummy1) - : [in] "Q"(*in), - "1"(ctx->key_enc), - "2"(num_rounds(ctx) - 2) - : "cc"); - + __aes_ce_encrypt(ctx->key_enc, dst, src, num_rounds(ctx)); kernel_neon_end(); } static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) { struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); - struct aes_block *out = (struct aes_block *)dst; - struct aes_block const *in = (struct aes_block *)src; - void *dummy0; - int dummy1; if (!may_use_simd()) { __aes_arm64_decrypt(ctx->key_dec, dst, src, num_rounds(ctx)); @@ -105,62 +72,10 @@ static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) } kernel_neon_begin(); - - __asm__(" ld1 {v0.16b}, %[in] ;" - " ld1 {v1.4s}, [%[key]], #16 ;" - " cmp %w[rounds], #10 ;" - " bmi 0f ;" - " bne 3f ;" - " mov v3.16b, v1.16b ;" - " b 2f ;" - "0: mov v2.16b, v1.16b ;" - " ld1 {v3.4s}, [%[key]], #16 ;" - "1: aesd v0.16b, v2.16b ;" - " aesimc v0.16b, v0.16b ;" - "2: ld1 {v1.4s}, [%[key]], #16 ;" - " aesd v0.16b, v3.16b ;" - " aesimc v0.16b, v0.16b ;" - "3: ld1 {v2.4s}, [%[key]], #16 ;" - " subs %w[rounds], %w[rounds], #3 ;" - " aesd v0.16b, v1.16b ;" - " aesimc v0.16b, v0.16b ;" - " ld1 {v3.4s}, [%[key]], #16 ;" - " bpl 1b ;" - " aesd v0.16b, v2.16b ;" - " eor v0.16b, v0.16b, v3.16b ;" - " st1 {v0.16b}, %[out] ;" - - : [out] "=Q"(*out), - [key] "=r"(dummy0), - [rounds] "=r"(dummy1) - : [in] "Q"(*in), - "1"(ctx->key_dec), - "2"(num_rounds(ctx) - 2) - : "cc"); - + __aes_ce_decrypt(ctx->key_dec, dst, src, num_rounds(ctx)); kernel_neon_end(); } -/* - * aes_sub() - use the aese instruction to perform the AES sbox substitution - * on each byte in 'input' - */ -static u32 aes_sub(u32 input) -{ - u32 ret; - - __asm__("dup v1.4s, %w[in] ;" - "movi v0.16b, #0 ;" - "aese v0.16b, v1.16b ;" - "umov %w[out], v0.4s[0] ;" - - : [out] "=r"(ret) - : [in] "r"(input) - : "v0","v1"); - - return ret; -} - int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, unsigned int key_len) { @@ -189,7 +104,7 @@ int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, u32 *rki = ctx->key_enc + (i * kwords); u32 *rko = rki + kwords; - rko[0] = ror32(aes_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0]; + rko[0] = ror32(__aes_ce_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0]; rko[1] = rko[0] ^ rki[1]; rko[2] = rko[1] ^ rki[2]; rko[3] = rko[2] ^ rki[3]; @@ -202,7 +117,7 @@ int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, } else if (key_len == AES_KEYSIZE_256) { if (i >= 6) break; - rko[4] = aes_sub(rko[3]) ^ rki[4]; + rko[4] = __aes_ce_sub(rko[3]) ^ rki[4]; rko[5] = rko[4] ^ rki[5]; rko[6] = rko[5] ^ rki[6]; rko[7] = rko[6] ^ rki[7]; @@ -221,13 +136,7 @@ int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, key_dec[0] = key_enc[j]; for (i = 1, j--; j > 0; i++, j--) - __asm__("ld1 {v0.4s}, %[in] ;" - "aesimc v1.16b, v0.16b ;" - "st1 {v1.4s}, %[out] ;" - - : [out] "=Q"(key_dec[i]) - : [in] "Q"(key_enc[j]) - : "v0","v1"); + __aes_ce_invert(key_dec + i, key_enc + j); key_dec[i] = key_enc[0]; kernel_neon_end(); diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c index efbeb3e0dcfb048099095a971512d8a9a1f62595..656b959bcdaa60882055d79b2f2cc5587472f4cb 100644 --- a/arch/arm64/crypto/sha1-ce-glue.c +++ b/arch/arm64/crypto/sha1-ce-glue.c @@ -29,6 +29,14 @@ struct sha1_ce_state { asmlinkage void sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src, int blocks); +#ifdef CONFIG_CFI_CLANG +static inline void __cfi_sha1_ce_transform(struct sha1_state *sst, + u8 const *src, int blocks) +{ + sha1_ce_transform((struct sha1_ce_state *)sst, src, blocks); +} +#define sha1_ce_transform __cfi_sha1_ce_transform +#endif const u32 sha1_ce_offsetof_count = offsetof(struct sha1_ce_state, sst.count); const u32 sha1_ce_offsetof_finalize = offsetof(struct sha1_ce_state, finalize); diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c index fd1ff2b13dfa35aa356b2511744808a848a56a97..ddda74844c430252678891f748508cf29af87284 100644 --- a/arch/arm64/crypto/sha2-ce-glue.c +++ b/arch/arm64/crypto/sha2-ce-glue.c @@ -29,6 +29,14 @@ struct sha256_ce_state { asmlinkage void sha2_ce_transform(struct sha256_ce_state *sst, u8 const *src, int blocks); +#ifdef CONFIG_CFI_CLANG +static inline void __cfi_sha2_ce_transform(struct sha256_state *sst, + u8 const *src, int blocks) +{ + sha2_ce_transform((struct sha256_ce_state *)sst, src, blocks); +} +#define sha2_ce_transform __cfi_sha2_ce_transform +#endif const u32 sha256_ce_offsetof_count = offsetof(struct sha256_ce_state, sst.count); diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 8ad208cb866c15dd587263203c299c73e9aee05f..8abec9f7f430cde43759a92c4253a1c26a10fc2f 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -71,6 +71,9 @@ struct kvm_arch { /* Interrupt controller */ struct vgic_dist vgic; + + /* Mandated version of PSCI */ + u32 psci_version; }; #define KVM_NR_MEM_OBJS 40 diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 4572a9b560fa3d73a7db4a850215bcbc9049c8f1..20bfb8e676e043236f63a0165eb78e63437284ac 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -29,7 +29,9 @@ ({ \ u64 reg; \ asm volatile(ALTERNATIVE("mrs %0, " __stringify(r##nvh),\ - "mrs_s %0, " __stringify(r##vh),\ + DEFINE_MRS_S \ + "mrs_s %0, " __stringify(r##vh) "\n"\ + UNDEFINE_MRS_S, \ ARM64_HAS_VIRT_HOST_EXTN) \ : "=r" (reg)); \ reg; \ @@ -39,7 +41,9 @@ do { \ u64 __val = (u64)(v); \ asm volatile(ALTERNATIVE("msr " __stringify(r##nvh) ", %x0",\ - "msr_s " __stringify(r##vh) ", %x0",\ + DEFINE_MSR_S \ + "msr_s " __stringify(r##vh) ", %x0\n"\ + UNDEFINE_MSR_S, \ ARM64_HAS_VIRT_HOST_EXTN) \ : : "rZ" (__val)); \ } while (0) diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index d4bae7d6e0d8b9786f3961d7dfe9627f9c5a57d8..932d340acd2eee70a55d072fd0ef9261353cf979 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -189,6 +189,9 @@ extern u64 kimage_vaddr; /* the offset between the kernel virtual and physical mappings */ extern u64 kimage_voffset; +/* physical memory limit imposed by the booloader */ +extern phys_addr_t bootloader_memory_limit; + static inline unsigned long kaslr_offset(void) { return kimage_vaddr - KIMAGE_VADDR; diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 6dd83d75b82ab8b9808f8b60b3ac6252344222a8..dd6c38205afc5b01bedcf3fe1b2d1498d7e27f1c 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -87,6 +87,13 @@ extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, pgprot_t prot, bool page_mappings_only); extern void *fixmap_remap_fdt(phys_addr_t dt_phys); extern void mark_linear_text_alias_ro(void); +#ifdef CONFIG_MEMORY_HOTPLUG +extern void hotplug_paging(phys_addr_t start, phys_addr_t size); +#ifdef CONFIG_MEMORY_HOTREMOVE +extern void remove_pagetable(unsigned long start, + unsigned long end, bool direct); +#endif +#endif #endif /* !__ASSEMBLY__ */ #endif diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 726e82454c3a030ee381194c62d964fa20285445..38aad4f8d50cd2aaf0eefea89c8dc91d3f7760c5 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -138,7 +138,7 @@ static inline void cpu_install_idmap(void) * Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD, * avoiding the possibility of conflicting TLB entries being allocated. */ -static inline void cpu_replace_ttbr1(pgd_t *pgd) +static inline void __nocfi cpu_replace_ttbr1(pgd_t *pgd) { typedef void (ttbr_replace_func)(phys_addr_t); extern ttbr_replace_func idmap_cpu_replace_ttbr1; diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index fe38e0ec2ebad83d96dd0dbf9c85b05b1fecf437..90ac66bd39ee81e7be8b2fb7cff65302c4ce7ce0 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -449,6 +449,11 @@ static inline phys_addr_t pmd_page_paddr(pmd_t pmd) return pmd_val(pmd) & PHYS_MASK & (s32)PAGE_MASK; } +static inline unsigned long pmd_page_vaddr(pmd_t pmd) +{ + return (unsigned long) __va(pmd_page_paddr(pmd)); +} + /* Find an entry in the third-level page table. */ #define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) @@ -500,6 +505,11 @@ static inline phys_addr_t pud_page_paddr(pud_t pud) return pud_val(pud) & PHYS_MASK & (s32)PAGE_MASK; } +static inline unsigned long pud_page_vaddr(pud_t pud) +{ + return (unsigned long) __va(pud_page_paddr(pud)); +} + /* Find an entry in the second-level page table. */ #define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) @@ -552,6 +562,11 @@ static inline phys_addr_t pgd_page_paddr(pgd_t pgd) return pgd_val(pgd) & PHYS_MASK & (s32)PAGE_MASK; } +static inline unsigned long pgd_page_vaddr(pgd_t pgd) +{ + return (unsigned long) __va(pgd_page_paddr(pgd)); +} + /* Find an entry in the frst-level page table. */ #define pud_index(addr) (((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1)) diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h index 32ce13b7b1c006a430d9a4ed005698a8fd92bec3..cae05ab5b467b0e95870ec6f6d0edabc37266a16 100644 --- a/arch/arm64/include/asm/spinlock.h +++ b/arch/arm64/include/asm/spinlock.h @@ -88,8 +88,8 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock) " cbnz %w1, 1f\n" " add %w1, %w0, %3\n" " casa %w0, %w1, %2\n" - " and %w1, %w1, #0xffff\n" - " eor %w1, %w1, %w0, lsr #16\n" + " sub %w1, %w1, %3\n" + " eor %w1, %w1, %w0\n" "1:") : "=&r" (lockval), "=&r" (tmp), "+Q" (*lock) : "I" (1 << TICKET_SHIFT) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index ede80d47d0effdb749a51df45d39dd576611fc9c..3bdec2f5cbb423bbdce450375e492ac45c2be1f7 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -465,20 +465,39 @@ #include -asm( -" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" -" .equ .L__reg_num_x\\num, \\num\n" -" .endr\n" +#define __DEFINE_MRS_MSR_S_REGNUM \ +" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" \ +" .equ .L__reg_num_x\\num, \\num\n" \ +" .endr\n" \ " .equ .L__reg_num_xzr, 31\n" -"\n" -" .macro mrs_s, rt, sreg\n" - __emit_inst(0xd5200000|(\\sreg)|(.L__reg_num_\\rt)) + +#define DEFINE_MRS_S \ + __DEFINE_MRS_MSR_S_REGNUM \ +" .macro mrs_s, rt, sreg\n" \ +" .inst 0xd5200000|(\\sreg)|(.L__reg_num_\\rt)\n" \ " .endm\n" -"\n" -" .macro msr_s, sreg, rt\n" - __emit_inst(0xd5000000|(\\sreg)|(.L__reg_num_\\rt)) + +#define DEFINE_MSR_S \ + __DEFINE_MRS_MSR_S_REGNUM \ +" .macro msr_s, sreg, rt\n" \ +" .inst 0xd5000000|(\\sreg)|(.L__reg_num_\\rt)\n" \ " .endm\n" -); + +#define UNDEFINE_MRS_S \ +" .purgem mrs_s\n" + +#define UNDEFINE_MSR_S \ +" .purgem msr_s\n" + +#define __mrs_s(r, v) \ + DEFINE_MRS_S \ +" mrs_s %0, " __stringify(r) "\n" \ + UNDEFINE_MRS_S : "=r" (v) + +#define __msr_s(r, v) \ + DEFINE_MSR_S \ +" msr_s " __stringify(r) ", %x0\n" \ + UNDEFINE_MSR_S : : "rZ" (v) /* * Unlike read_cpuid, calls to read_sysreg are never expected to be @@ -504,15 +523,15 @@ asm( * For registers without architectural names, or simply unsupported by * GAS. */ -#define read_sysreg_s(r) ({ \ - u64 __val; \ - asm volatile("mrs_s %0, " __stringify(r) : "=r" (__val)); \ - __val; \ +#define read_sysreg_s(r) ({ \ + u64 __val; \ + asm volatile(__mrs_s(r, __val)); \ + __val; \ }) -#define write_sysreg_s(v, r) do { \ - u64 __val = (u64)(v); \ - asm volatile("msr_s " __stringify(r) ", %x0" : : "rZ" (__val)); \ +#define write_sysreg_s(v, r) do { \ + u64 __val = (u64)(v); \ + asm volatile(__msr_s(r, __val)); \ } while (0) static inline void config_sctlr_el1(u32 clear, u32 set) diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 51149ec75fe480b324fd74d2697579a936438fdc..9f74ce5899f007b1bda2b0f221bc6db73f4542aa 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -200,6 +200,12 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2) #define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2) +/* KVM-as-firmware specific pseudo-registers */ +#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_FW | ((r) & 0xffff)) +#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) + /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 1211ffc725a55fde0881982e7c48d19960f6156c..f18295e46438fae5d69628187227af522163a878 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -842,7 +842,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, ID_AA64PFR0_CSV3_SHIFT); } -static int kpti_install_ng_mappings(void *__unused) +static int __nocfi kpti_install_ng_mappings(void *__unused) { typedef void (kpti_remap_fn)(int, int, phys_addr_t); extern kpti_remap_fn idmap_kpti_install_ng_mappings; diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds index 22e36a21c1134576eb58a9209d75f2c6b2f09f85..99eb7c292494191b41a3d7dfa739b502eb64e80a 100644 --- a/arch/arm64/kernel/module.lds +++ b/arch/arm64/kernel/module.lds @@ -1,5 +1,5 @@ SECTIONS { - .plt (NOLOAD) : { BYTE(0) } - .init.plt (NOLOAD) : { BYTE(0) } - .text.ftrace_trampoline (NOLOAD) : { BYTE(0) } + .plt : { BYTE(0) } + .init.plt : { BYTE(0) } + .text.ftrace_trampoline : { BYTE(0) } } diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index d73a6db9adf0bb4a6f9a23e769f4f34834013d7e..2d0e5030f1c1454c30cbefbc3b5d620c03e770fb 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -1005,18 +1005,14 @@ static int armv8pmu_probe_pmu(struct arm_pmu *cpu_pmu) __armv8pmu_probe_pmu, &probe, 1); if (ret) - goto probe_fail; + return ret; - if (!probe.present) { - ret = -ENODEV; - goto probe_fail; - } + if (!probe.present) + return -ENODEV; - return 0; + idle_notifier_register(&pmu_idle_nb->perf_cpu_idle_nb); -probe_fail: - idle_notifier_unregister(&pmu_idle_nb->perf_cpu_idle_nb); - return ret; + return 0; } static int armv8_pmu_init(struct arm_pmu *cpu_pmu) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 6fd6528a3b51bad35fd707c70a686a25f06bceb6..08f2c94d1c20df41fdb11cf20256add77ee3082f 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -254,11 +254,9 @@ void __show_regs(struct pt_regs *regs) } show_regs_print_info(KERN_DEFAULT); - print_symbol("PC is at %s\n", instruction_pointer(regs)); - print_symbol("LR is at %s\n", lr); - printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n", - regs->pc, lr, regs->pstate); - printk("sp : %016llx\n", sp); + print_symbol("pc : %s\n", regs->pc); + print_symbol("lr : %s\n", lr); + printk("sp : %016llx pstate : %08llx\n", sp, regs->pstate); i = top_reg; diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 0fdbecd8fd110e15387dc3696caa906ec52f1f3b..41bdbbb41223ea85a683c44176dfd47a6f0e3a86 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -58,6 +58,9 @@ #include #include #include +#include + +#include #define CREATE_TRACE_POINTS #include @@ -437,6 +440,7 @@ void __init smp_cpus_done(unsigned int max_cpus) setup_cpu_features(); hyp_mode_check(); apply_alternatives_all(); + scm_enable_mem_protection(); mark_linear_text_alias_ro(); } @@ -857,6 +861,7 @@ static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs) pr_crit("CPU%u: stopping\n", cpu); __show_regs(regs); dump_stack(); + dump_stack_minidump(regs->sp); raw_spin_unlock(&stop_lock); } diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 207778611fb97d37f0d6d4c1b94d4eb7443e963e..0249306818c978a21ea48078b1c09412798541d4 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -60,55 +60,9 @@ static const char *handler[]= { int show_unhandled_signals = 0; -/* - * Dump out the contents of some kernel memory nicely... - */ -static void dump_mem(const char *lvl, const char *str, unsigned long bottom, - unsigned long top) -{ - unsigned long first; - mm_segment_t fs; - int i; - - /* - * We need to switch to kernel mode so that we can use __get_user - * to safely read from kernel space. - */ - fs = get_fs(); - set_fs(KERNEL_DS); - - printk("%s%s(0x%016lx to 0x%016lx)\n", lvl, str, bottom, top); - - for (first = bottom & ~31; first < top; first += 32) { - unsigned long p; - char str[sizeof(" 12345678") * 8 + 1]; - - memset(str, ' ', sizeof(str)); - str[sizeof(str) - 1] = '\0'; - - for (p = first, i = 0; i < (32 / 8) - && p < top; i++, p += 8) { - if (p >= bottom && p < top) { - unsigned long val; - - if (__get_user(val, (unsigned long *)p) == 0) - sprintf(str + i * 17, " %016lx", val); - else - sprintf(str + i * 17, " ????????????????"); - } - } - printk("%s%04lx:%s\n", lvl, first & 0xffff, str); - } - - set_fs(fs); -} - static void dump_backtrace_entry(unsigned long where) { - /* - * Note that 'where' can have a physical address, but it's not handled. - */ - print_ip_sym(where); + printk(" %pS\n", (void *)where); } static void __dump_instr(const char *lvl, struct pt_regs *regs) @@ -173,10 +127,7 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) skip = !!regs; printk("Call trace:\n"); - while (1) { - unsigned long stack; - int ret; - + do { /* skip until specified stack frame */ if (!skip) { dump_backtrace_entry(frame.pc); @@ -191,17 +142,7 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) */ dump_backtrace_entry(regs->pc); } - ret = unwind_frame(tsk, &frame); - if (ret < 0) - break; - if (in_entry_text(frame.pc)) { - stack = frame.fp - offsetof(struct pt_regs, stackframe); - - if (on_accessible_stack(tsk, stack)) - dump_mem("", "Exception stack", stack, - stack + sizeof(struct pt_regs)); - } - } + } while (!unwind_frame(tsk, &frame)); put_task_stack(tsk); } diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index b215c712d89704e3247951fde01bcd4224b0c190..ef3f9d9d4062b4cef8277f1b3057b1e01cf56d2b 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -15,6 +15,7 @@ obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) ccflags-y := -shared -fno-common -fno-builtin ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \ $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) +ccflags-y += $(DISABLE_LTO) # Disable gcov profiling for VDSO code GCOV_PROFILE := n diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index ddfd3c0942f7acf27d3bb0396ebbd3ba87303296..cb3e4393b412ed979387a745098e4de04d0a172a 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -61,7 +61,7 @@ jiffies = jiffies_64; #define TRAMP_TEXT \ . = ALIGN(PAGE_SIZE); \ VMLINUX_SYMBOL(__entry_tramp_text_start) = .; \ - *(.entry.tramp.text) \ + KEEP(*(.entry.tramp.text)) \ . = ALIGN(PAGE_SIZE); \ VMLINUX_SYMBOL(__entry_tramp_text_end) = .; #else @@ -150,11 +150,11 @@ SECTIONS . = ALIGN(4); .altinstructions : { __alt_instructions = .; - *(.altinstructions) + KEEP(*(.altinstructions)) __alt_instructions_end = .; } .altinstr_replacement : { - *(.altinstr_replacement) + KEEP(*(.altinstr_replacement)) } . = ALIGN(PAGE_SIZE); diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index 5c7f657dd20740c1b21e75d13cf711f4b7bfbdf8..811f04c5760e40c5fd36adc6cb931921cbfa850a 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -205,7 +206,7 @@ static int get_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu) { return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu) - + NUM_TIMER_REGS; + + kvm_arm_get_fw_num_regs(vcpu) + NUM_TIMER_REGS; } /** @@ -225,6 +226,11 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) uindices++; } + ret = kvm_arm_copy_fw_reg_indices(vcpu, uindices); + if (ret) + return ret; + uindices += kvm_arm_get_fw_num_regs(vcpu); + ret = copy_timer_indices(vcpu, uindices); if (ret) return ret; @@ -243,6 +249,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return get_core_reg(vcpu, reg); + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW) + return kvm_arm_get_fw_reg(vcpu, reg); + if (is_timer_reg(reg->id)) return get_timer_reg(vcpu, reg); @@ -259,6 +268,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return set_core_reg(vcpu, reg); + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW) + return kvm_arm_set_fw_reg(vcpu, reg); + if (is_timer_reg(reg->id)) return set_timer_reg(vcpu, reg); diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile index f04400d494b7a079393ddd5b2c5a2fbe10db3c72..bfa00a9e161d67e1c27a1a24357fdd955f642755 100644 --- a/arch/arm64/kvm/hyp/Makefile +++ b/arch/arm64/kvm/hyp/Makefile @@ -3,7 +3,11 @@ # Makefile for Kernel-based Virtual Machine module, HYP part # -ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING +ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING $(DISABLE_CFI) + +ifeq ($(cc-name),clang) +ccflags-y += -fno-jump-tables +endif KVM=../../../../virt/kvm diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 87fe156574f60ec87613c33a329c5136f4a831d1..3a9b5e83c9d4115935e38572c44119cc74d7c254 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -1092,6 +1092,8 @@ static int __get_iommu_pgprot(unsigned long attrs, int prot, prot |= IOMMU_NOEXEC; if (attrs & DMA_ATTR_IOMMU_USE_UPSTREAM_HINT) prot |= IOMMU_USE_UPSTREAM_HINT; + if (attrs & DMA_ATTR_IOMMU_USE_LLC_NWA) + prot |= IOMMU_USE_LLC_NWA; if (coherent) prot |= IOMMU_CACHE; diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 885402869696b8276ee4c4485f879a60b4242613..9b82719e309fba38b2f0f6318a27dfe39eefc9d7 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -313,6 +313,7 @@ static void __init arm64_memory_present(void) #endif static phys_addr_t memory_limit = (phys_addr_t)ULLONG_MAX; +phys_addr_t bootloader_memory_limit; /* * Limit the memory size that was specified via FDT. @@ -395,6 +396,12 @@ void __init arm64_memblock_init(void) memblock_remove(0, memstart_addr); } + /* + * Save bootloader imposed memory limit before we overwirte + * memblock. + */ + bootloader_memory_limit = memblock_end_of_DRAM(); + /* * Apply the memory limit if it was set. Since the kernel may be loaded * high up in memory, add back the kernel region that must be accessible @@ -602,6 +609,7 @@ void __init mem_init(void) mem_init_print_info(NULL); +#ifdef CONFIG_PRINT_VMEMLAYOUT #define MLK(b, t) b, t, ((t) - (b)) >> 10 #define MLM(b, t) b, t, ((t) - (b)) >> 20 #define MLG(b, t) b, t, ((t) - (b)) >> 30 @@ -644,7 +652,7 @@ void __init mem_init(void) #undef MLK #undef MLM #undef MLK_ROUNDUP - +#endif /* * Check boundaries twice: Some fundamental inconsistencies can be * detected at build time already. @@ -725,3 +733,133 @@ static int __init register_mem_limit_dumper(void) return 0; } __initcall(register_mem_limit_dumper); + +#ifdef CONFIG_MEMORY_HOTPLUG +int arch_add_memory(int nid, u64 start, u64 size, bool want_memblock) +{ + pg_data_t *pgdat; + unsigned long start_pfn = start >> PAGE_SHIFT; + unsigned long nr_pages = size >> PAGE_SHIFT; + unsigned long end_pfn = start_pfn + nr_pages; + unsigned long max_sparsemem_pfn = 1UL << (MAX_PHYSMEM_BITS-PAGE_SHIFT); + int ret; + + if (end_pfn > max_sparsemem_pfn) { + pr_err("end_pfn too big"); + return -1; + } + hotplug_paging(start, size); + + /* + * Mark the first page in the range as unusable. This is needed + * because __add_section (within __add_pages) wants pfn_valid + * of it to be false, and in arm64 pfn falid is implemented by + * just checking at the nomap flag for existing blocks. + * + * A small trick here is that __add_section() requires only + * phys_start_pfn (that is the first pfn of a section) to be + * invalid. Regardless of whether it was assumed (by the function + * author) that all pfns within a section are either all valid + * or all invalid, it allows to avoid looping twice (once here, + * second when memblock_clear_nomap() is called) through all + * pfns of the section and modify only one pfn. Thanks to that, + * further, in __add_zone() only this very first pfn is skipped + * and corresponding page is not flagged reserved. Therefore it + * is enough to correct this setup only for it. + * + * When arch_add_memory() returns the walk_memory_range() function + * is called and passed with online_memory_block() callback, + * which execution finally reaches the memory_block_action() + * function, where also only the first pfn of a memory block is + * checked to be reserved. Above, it was first pfn of a section, + * here it is a block but + * (drivers/base/memory.c): + * sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; + * (include/linux/memory.h): + * #define MIN_MEMORY_BLOCK_SIZE (1UL << SECTION_SIZE_BITS) + * so we can consider block and section equivalently + */ + memblock_mark_nomap(start, 1<> PAGE_SHIFT; + unsigned long nr_pages = size >> PAGE_SHIFT; + struct page *page = pfn_to_page(start_pfn); + struct zone *zone; + int ret = 0; + + zone = page_zone(page); + ret = __remove_pages(zone, start_pfn, nr_pages); + WARN_ON_ONCE(ret); + + kernel_physical_mapping_remove(start, start + size); + + return ret; +} + +#endif /* CONFIG_MEMORY_HOTREMOVE */ +static int arm64_online_page(struct page *page) +{ + unsigned long phy_addr = page_to_phys(page); + + if (phy_addr + PAGE_SIZE >= bootloader_memory_limit) + return -EINVAL; + + __online_page_set_limits(page); + __online_page_increment_counters(page); + __online_page_free(page); + + return 0; +} + +static int __init arm64_memory_hotplug_init(void) +{ + set_online_page_callback(&arm64_online_page); + return 0; +} +core_initcall(arm64_memory_hotplug_init); +#endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 3e350ae97369f196c92225ad83f2776f13b19dd5..a7c6f6239e34c8bf219d7a6dc02c0e1038c42900 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -688,6 +688,412 @@ void __init paging_init(void) SWAPPER_DIR_SIZE - PAGE_SIZE); } +#ifdef CONFIG_MEMORY_HOTPLUG +/* + * hotplug_paging() is used by memory hotplug to build new page tables + * for hot added memory. + */ +void hotplug_paging(phys_addr_t start, phys_addr_t size) +{ + + struct page *pg; + phys_addr_t pgd_phys = pgd_pgtable_alloc(); + pgd_t *pgd = pgd_set_fixmap(pgd_phys); + + memcpy(pgd, swapper_pg_dir, PAGE_SIZE); + + __create_pgd_mapping(pgd, start, __phys_to_virt(start), size, + PAGE_KERNEL, pgd_pgtable_alloc, !debug_pagealloc_enabled()); + + cpu_replace_ttbr1(__va(pgd_phys)); + memcpy(swapper_pg_dir, pgd, PAGE_SIZE); + cpu_replace_ttbr1(swapper_pg_dir); + + pgd_clear_fixmap(); + + pg = phys_to_page(pgd_phys); + pgtable_page_dtor(pg); + __free_pages(pg, 0); +} + +#ifdef CONFIG_MEMORY_HOTREMOVE +#define PAGE_INUSE 0xFD + +static void free_pagetable(struct page *page, int order, bool direct) +{ + unsigned long magic; + unsigned int nr_pages = 1 << order; + + /* bootmem page has reserved flag */ + if (PageReserved(page)) { + __ClearPageReserved(page); + + magic = (unsigned long)page->lru.next; + if (magic == SECTION_INFO || magic == MIX_SECTION_INFO) { + while (nr_pages--) + put_page_bootmem(page++); + } else { + while (nr_pages--) + free_reserved_page(page++); + } + } else { + /* + * Only direct pagetable allocation (those allocated via + * hotplug) call the pgtable_page_ctor; vmemmap pgtable + * allocations don't. + */ + if (direct) + pgtable_page_dtor(page); + + free_pages((unsigned long)page_address(page), order); + } +} + +static void free_pte_table(pmd_t *pmd, bool direct) +{ + pte_t *pte_start, *pte; + struct page *page; + int i; + + pte_start = (pte_t *) pmd_page_vaddr(*pmd); + /* Check if there is no valid entry in the PMD */ + for (i = 0; i < PTRS_PER_PTE; i++) { + pte = pte_start + i; + if (!pte_none(*pte)) + return; + } + + page = pmd_page(*pmd); + + free_pagetable(page, 0, direct); + + /* + * This spin lock could be only taken in _pte_aloc_kernel + * in mm/memory.c and nowhere else (for arm64). Not sure if + * the function above can be called concurrently. In doubt, + * I am living it here for now, but it probably can be removed + */ + spin_lock(&init_mm.page_table_lock); + pmd_clear(pmd); + spin_unlock(&init_mm.page_table_lock); +} + +static void free_pmd_table(pud_t *pud, bool direct) +{ + pmd_t *pmd_start, *pmd; + struct page *page; + int i; + + pmd_start = (pmd_t *) pud_page_vaddr(*pud); + /* Check if there is no valid entry in the PMD */ + for (i = 0; i < PTRS_PER_PMD; i++) { + pmd = pmd_start + i; + if (!pmd_none(*pmd)) + return; + } + + page = pud_page(*pud); + + free_pagetable(page, 0, direct); + + /* + * This spin lock could be only taken in _pte_aloc_kernel + * in mm/memory.c and nowhere else (for arm64). Not sure if + * the function above can be called concurrently. In doubt, + * I am living it here for now, but it probably can be removed + */ + spin_lock(&init_mm.page_table_lock); + pud_clear(pud); + spin_unlock(&init_mm.page_table_lock); +} + +/* + * When the PUD is folded on the PGD (three levels of paging), + * there's no need to free PUDs + */ +#if CONFIG_PGTABLE_LEVELS > 3 +static void free_pud_table(pgd_t *pgd, bool direct) +{ + pud_t *pud_start, *pud; + struct page *page; + int i; + + pud_start = (pud_t *) pgd_page_vaddr(*pgd); + /* Check if there is no valid entry in the PUD */ + for (i = 0; i < PTRS_PER_PUD; i++) { + pud = pud_start + i; + if (!pud_none(*pud)) + return; + } + + page = pgd_page(*pgd); + + free_pagetable(page, 0, direct); + + /* + * This spin lock could be only + * taken in _pte_aloc_kernel in + * mm/memory.c and nowhere else + * (for arm64). Not sure if the + * function above can be called + * concurrently. In doubt, + * I am living it here for now, + * but it probably can be removed. + */ + spin_lock(&init_mm.page_table_lock); + pgd_clear(pgd); + spin_unlock(&init_mm.page_table_lock); +} +#endif + +static void remove_pte_table(pte_t *pte, unsigned long addr, + unsigned long end, bool direct) +{ + unsigned long next; + void *page_addr; + + for (; addr < end; addr = next, pte++) { + next = (addr + PAGE_SIZE) & PAGE_MASK; + if (next > end) + next = end; + + if (!pte_present(*pte)) + continue; + + if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) { + /* + * Do not free direct mapping pages since they were + * freed when offlining, or simplely not in use. + */ + if (!direct) + free_pagetable(pte_page(*pte), 0, direct); + + /* + * This spin lock could be only + * taken in _pte_aloc_kernel in + * mm/memory.c and nowhere else + * (for arm64). Not sure if the + * function above can be called + * concurrently. In doubt, + * I am living it here for now, + * but it probably can be removed. + */ + spin_lock(&init_mm.page_table_lock); + pte_clear(&init_mm, addr, pte); + spin_unlock(&init_mm.page_table_lock); + } else { + /* + * If we are here, we are freeing vmemmap pages since + * direct mapped memory ranges to be freed are aligned. + * + * If we are not removing the whole page, it means + * other page structs in this page are being used and + * we canot remove them. So fill the unused page_structs + * with 0xFD, and remove the page when it is wholly + * filled with 0xFD. + */ + memset((void *)addr, PAGE_INUSE, next - addr); + + page_addr = page_address(pte_page(*pte)); + if (!memchr_inv(page_addr, PAGE_INUSE, PAGE_SIZE)) { + free_pagetable(pte_page(*pte), 0, direct); + + /* + * This spin lock could be only + * taken in _pte_aloc_kernel in + * mm/memory.c and nowhere else + * (for arm64). Not sure if the + * function above can be called + * concurrently. In doubt, + * I am living it here for now, + * but it probably can be removed. + */ + spin_lock(&init_mm.page_table_lock); + pte_clear(&init_mm, addr, pte); + spin_unlock(&init_mm.page_table_lock); + } + } + } + + // I am adding this flush here in simmetry to the x86 code. + // Why do I need to call it here and not in remove_p[mu]d + flush_tlb_all(); +} + +static void remove_pmd_table(pmd_t *pmd, unsigned long addr, + unsigned long end, bool direct) +{ + unsigned long next; + void *page_addr; + pte_t *pte; + + for (; addr < end; addr = next, pmd++) { + next = pmd_addr_end(addr, end); + + if (!pmd_present(*pmd)) + continue; + + // check if we are using 2MB section mappings + if (pmd_sect(*pmd)) { + if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) { + if (!direct) { + free_pagetable(pmd_page(*pmd), + get_order(PMD_SIZE), direct); + } + /* + * This spin lock could be only + * taken in _pte_aloc_kernel in + * mm/memory.c and nowhere else + * (for arm64). Not sure if the + * function above can be called + * concurrently. In doubt, + * I am living it here for now, + * but it probably can be removed. + */ + spin_lock(&init_mm.page_table_lock); + pmd_clear(pmd); + spin_unlock(&init_mm.page_table_lock); + } else { + /* If here, we are freeing vmemmap pages. */ + memset((void *)addr, PAGE_INUSE, next - addr); + + page_addr = page_address(pmd_page(*pmd)); + if (!memchr_inv(page_addr, PAGE_INUSE, + PMD_SIZE)) { + free_pagetable(pmd_page(*pmd), + get_order(PMD_SIZE), direct); + + /* + * This spin lock could be only + * taken in _pte_aloc_kernel in + * mm/memory.c and nowhere else + * (for arm64). Not sure if the + * function above can be called + * concurrently. In doubt, + * I am living it here for now, + * but it probably can be removed. + */ + spin_lock(&init_mm.page_table_lock); + pmd_clear(pmd); + spin_unlock(&init_mm.page_table_lock); + } + } + continue; + } + + BUG_ON(!pmd_table(*pmd)); + + pte = pte_offset_map(pmd, addr); + remove_pte_table(pte, addr, next, direct); + free_pte_table(pmd, direct); + } +} + +static void remove_pud_table(pud_t *pud, unsigned long addr, + unsigned long end, bool direct) +{ + unsigned long next; + pmd_t *pmd; + void *page_addr; + + for (; addr < end; addr = next, pud++) { + next = pud_addr_end(addr, end); + if (!pud_present(*pud)) + continue; + /* + * If we are using 4K granules, check if we are using + * 1GB section mapping. + */ + if (pud_sect(*pud)) { + if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) { + if (!direct) { + free_pagetable(pud_page(*pud), + get_order(PUD_SIZE), direct); + } + + /* + * This spin lock could be only + * taken in _pte_aloc_kernel in + * mm/memory.c and nowhere else + * (for arm64). Not sure if the + * function above can be called + * concurrently. In doubt, + * I am living it here for now, + * but it probably can be removed. + */ + spin_lock(&init_mm.page_table_lock); + pud_clear(pud); + spin_unlock(&init_mm.page_table_lock); + } else { + /* If here, we are freeing vmemmap pages. */ + memset((void *)addr, PAGE_INUSE, next - addr); + + page_addr = page_address(pud_page(*pud)); + if (!memchr_inv(page_addr, PAGE_INUSE, + PUD_SIZE)) { + + free_pagetable(pud_page(*pud), + get_order(PUD_SIZE), direct); + + /* + * This spin lock could be only + * taken in _pte_aloc_kernel in + * mm/memory.c and nowhere else + * (for arm64). Not sure if the + * function above can be called + * concurrently. In doubt, + * I am living it here for now, + * but it probably can be removed. + */ + spin_lock(&init_mm.page_table_lock); + pud_clear(pud); + spin_unlock(&init_mm.page_table_lock); + } + } + continue; + } + + BUG_ON(!pud_table(*pud)); + + pmd = pmd_offset(pud, addr); + remove_pmd_table(pmd, addr, next, direct); + free_pmd_table(pud, direct); + } +} + +void remove_pagetable(unsigned long start, unsigned long end, bool direct) +{ + unsigned long next; + unsigned long addr; + pgd_t *pgd; + pud_t *pud; + + for (addr = start; addr < end; addr = next) { + next = pgd_addr_end(addr, end); + + pgd = pgd_offset_k(addr); + if (pgd_none(*pgd)) + continue; + + pud = pud_offset(pgd, addr); + remove_pud_table(pud, addr, next, direct); + /* + * When the PUD is folded on the PGD (three levels of paging), + * I did already clear the PMD page in free_pmd_table, + * and reset the corresponding PGD==PUD entry. + */ +#if CONFIG_PGTABLE_LEVELS > 3 + free_pud_table(pgd, direct); +#endif + } + + flush_tlb_all(); +} + + +#endif /* CONFIG_MEMORY_HOTREMOVE */ +#endif /* CONFIG_MEMORY_HOTPLUG */ + /* * Check whether a kernel address is valid (derived from arch/x86/). */ @@ -769,6 +1175,9 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) #endif /* CONFIG_ARM64_64K_PAGES */ void vmemmap_free(unsigned long start, unsigned long end) { +#ifdef CONFIG_MEMORY_HOTREMOVE + remove_pagetable(start, end, false); +#endif } #endif /* CONFIG_SPARSEMEM_VMEMMAP */ diff --git a/arch/microblaze/Kconfig.platform b/arch/microblaze/Kconfig.platform index 1b3d8c8491018734b3cca9f5ecd5441e52f8c8b2..f7f1739c11b9fef091b1176c0120c4613232cfeb 100644 --- a/arch/microblaze/Kconfig.platform +++ b/arch/microblaze/Kconfig.platform @@ -20,6 +20,7 @@ config OPT_LIB_FUNCTION config OPT_LIB_ASM bool "Optimalized lib function ASM" depends on OPT_LIB_FUNCTION && (XILINX_MICROBLAZE0_USE_BARREL = 1) + depends on CPU_BIG_ENDIAN default n help Allows turn on optimalized library function (memcpy and memmove). diff --git a/arch/microblaze/lib/fastcopy.S b/arch/microblaze/lib/fastcopy.S index 62021d7e249e0665cd6a1483c12de5b8b0bf5739..fdc48bb065d89fe3b2443ef054d1a6ece3744b54 100644 --- a/arch/microblaze/lib/fastcopy.S +++ b/arch/microblaze/lib/fastcopy.S @@ -29,10 +29,6 @@ * between mem locations with size of xfer spec'd in bytes */ -#ifdef __MICROBLAZEEL__ -#error Microblaze LE not support ASM optimized lib func. Disable OPT_LIB_ASM. -#endif - #include .text .globl memcpy diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile index c675eece389a41c036c4ff2e1aeaf89509368a1f..adce180f3ee4264cabfdf85e7032f7c661b6a4c0 100644 --- a/arch/mips/boot/compressed/Makefile +++ b/arch/mips/boot/compressed/Makefile @@ -133,4 +133,8 @@ vmlinuz.srec: vmlinuz uzImage.bin: vmlinuz.bin FORCE $(call if_changed,uimage,none) -clean-files := $(objtree)/vmlinuz $(objtree)/vmlinuz.{32,ecoff,bin,srec} +clean-files += $(objtree)/vmlinuz +clean-files += $(objtree)/vmlinuz.32 +clean-files += $(objtree)/vmlinuz.ecoff +clean-files += $(objtree)/vmlinuz.bin +clean-files += $(objtree)/vmlinuz.srec diff --git a/arch/mips/generic/irq.c b/arch/mips/generic/irq.c index 394f8161e4628680e82a79bf81c63160e0752bf8..cb7fdaeef426c88bc4a90a8d980b73a39b5bf824 100644 --- a/arch/mips/generic/irq.c +++ b/arch/mips/generic/irq.c @@ -22,10 +22,10 @@ int get_c0_fdc_int(void) { int mips_cpu_fdc_irq; - if (cpu_has_veic) - panic("Unimplemented!"); - else if (mips_gic_present()) + if (mips_gic_present()) mips_cpu_fdc_irq = gic_get_c0_fdc_int(); + else if (cpu_has_veic) + panic("Unimplemented!"); else if (cp0_fdc_irq >= 0) mips_cpu_fdc_irq = MIPS_CPU_IRQ_BASE + cp0_fdc_irq; else @@ -38,10 +38,10 @@ int get_c0_perfcount_int(void) { int mips_cpu_perf_irq; - if (cpu_has_veic) - panic("Unimplemented!"); - else if (mips_gic_present()) + if (mips_gic_present()) mips_cpu_perf_irq = gic_get_c0_perfcount_int(); + else if (cpu_has_veic) + panic("Unimplemented!"); else if (cp0_perfcount_irq >= 0) mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; else @@ -54,10 +54,10 @@ unsigned int get_c0_compare_int(void) { int mips_cpu_timer_irq; - if (cpu_has_veic) - panic("Unimplemented!"); - else if (mips_gic_present()) + if (mips_gic_present()) mips_cpu_timer_irq = gic_get_c0_compare_int(); + else if (cpu_has_veic) + panic("Unimplemented!"); else mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq; diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h index b713069472903c26f21c11424113765a5156d955..06629011a4342f490bf4bcabd9e4fe06ed0952ac 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -654,6 +654,13 @@ __clear_user(void __user *addr, __kernel_size_t size) { __kernel_size_t res; +#ifdef CONFIG_CPU_MICROMIPS +/* micromips memset / bzero also clobbers t7 & t8 */ +#define bzero_clobbers "$4", "$5", "$6", __UA_t0, __UA_t1, "$15", "$24", "$31" +#else +#define bzero_clobbers "$4", "$5", "$6", __UA_t0, __UA_t1, "$31" +#endif /* CONFIG_CPU_MICROMIPS */ + if (eva_kernel_access()) { __asm__ __volatile__( "move\t$4, %1\n\t" @@ -663,7 +670,7 @@ __clear_user(void __user *addr, __kernel_size_t size) "move\t%0, $6" : "=r" (res) : "r" (addr), "r" (size) - : "$4", "$5", "$6", __UA_t0, __UA_t1, "$31"); + : bzero_clobbers); } else { might_fault(); __asm__ __volatile__( @@ -674,7 +681,7 @@ __clear_user(void __user *addr, __kernel_size_t size) "move\t%0, $6" : "=r" (res) : "r" (addr), "r" (size) - : "$4", "$5", "$6", __UA_t0, __UA_t1, "$31"); + : bzero_clobbers); } return res; diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S index a1456664d6c28c9ccc8f837e341fe1a760b3c2d6..f7327979a8f8e163547dafe4302d12caab809fbd 100644 --- a/arch/mips/lib/memset.S +++ b/arch/mips/lib/memset.S @@ -219,7 +219,7 @@ 1: PTR_ADDIU a0, 1 /* fill bytewise */ R10KCBARRIER(0(ra)) bne t1, a0, 1b - sb a1, -1(a0) + EX(sb, a1, -1(a0), .Lsmall_fixup\@) 2: jr ra /* done */ move a2, zero @@ -252,13 +252,18 @@ PTR_L t0, TI_TASK($28) andi a2, STORMASK LONG_L t0, THREAD_BUADDR(t0) - LONG_ADDU a2, t1 + LONG_ADDU a2, a0 jr ra LONG_SUBU a2, t0 .Llast_fixup\@: jr ra - andi v1, a2, STORMASK + nop + +.Lsmall_fixup\@: + PTR_SUBU a2, t1, a0 + jr ra + PTR_ADDIU a2, 1 .endm diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c index 8b937300fb7f0de6b7cc0323a15b03ab76e72e25..fd26fadc86171d52a757f521617863d9fa1d3b7c 100644 --- a/arch/mips/txx9/rbtx4939/setup.c +++ b/arch/mips/txx9/rbtx4939/setup.c @@ -186,7 +186,7 @@ static void __init rbtx4939_update_ioc_pen(void) #define RBTX4939_MAX_7SEGLEDS 8 -#if IS_ENABLED(CONFIG_LEDS_CLASS) +#if IS_BUILTIN(CONFIG_LEDS_CLASS) static u8 led_val[RBTX4939_MAX_7SEGLEDS]; struct rbtx4939_led_data { struct led_classdev cdev; @@ -261,7 +261,7 @@ static inline void rbtx4939_led_setup(void) static void __rbtx4939_7segled_putc(unsigned int pos, unsigned char val) { -#if IS_ENABLED(CONFIG_LEDS_CLASS) +#if IS_BUILTIN(CONFIG_LEDS_CLASS) unsigned long flags; local_irq_save(flags); /* bit7: reserved for LED class */ diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c index d8f77358e2ba29746730f34ba652db327bb2f1d5..513826a43efd8a04a08bc2a4e38761b6add43743 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -651,6 +651,10 @@ static int match_pci_device(struct device *dev, int index, (modpath->mod == PCI_FUNC(devfn))); } + /* index might be out of bounds for bc[] */ + if (index >= 6) + return 0; + id = PCI_SLOT(pdev->devfn) | (PCI_FUNC(pdev->devfn) << 5); return (modpath->bc[index] == id); } diff --git a/arch/parisc/kernel/hpmc.S b/arch/parisc/kernel/hpmc.S index 8d072c44f300c16d45ba8f4ee0c2eee6435e4ddd..781c3b9a3e46afcfa47dde5ae852f5a8f76630b5 100644 --- a/arch/parisc/kernel/hpmc.S +++ b/arch/parisc/kernel/hpmc.S @@ -84,6 +84,7 @@ END(hpmc_pim_data) .text .import intr_save, code + .align 16 ENTRY_CFI(os_hpmc) .os_hpmc: @@ -300,12 +301,15 @@ os_hpmc_6: b . nop + .align 16 /* make function length multiple of 16 bytes */ ENDPROC_CFI(os_hpmc) .os_hpmc_end: __INITRODATA +.globl os_hpmc_size .align 4 - .export os_hpmc_size + .type os_hpmc_size, @object + .size os_hpmc_size, 4 os_hpmc_size: .word .os_hpmc_end-.os_hpmc diff --git a/arch/powerpc/include/asm/barrier.h b/arch/powerpc/include/asm/barrier.h index 10daa1d56e0a44f40ea082aea13af9104a686805..c7c63959ba91066f69b1147f59d4df4e20b39b1f 100644 --- a/arch/powerpc/include/asm/barrier.h +++ b/arch/powerpc/include/asm/barrier.h @@ -35,7 +35,8 @@ #define rmb() __asm__ __volatile__ ("sync" : : : "memory") #define wmb() __asm__ __volatile__ ("sync" : : : "memory") -#ifdef __SUBARCH_HAS_LWSYNC +/* The sub-arch has lwsync */ +#if defined(__powerpc64__) || defined(CONFIG_PPC_E500MC) # define SMPWMB LWSYNC #else # define SMPWMB eieio diff --git a/arch/powerpc/include/asm/book3s/64/pgalloc.h b/arch/powerpc/include/asm/book3s/64/pgalloc.h index 1fcfa425cefaf205fe787cc9a480265aec758e0f..f326b40b7c7bb2884ef90708d474949d83f0621c 100644 --- a/arch/powerpc/include/asm/book3s/64/pgalloc.h +++ b/arch/powerpc/include/asm/book3s/64/pgalloc.h @@ -73,10 +73,16 @@ static inline void radix__pgd_free(struct mm_struct *mm, pgd_t *pgd) static inline pgd_t *pgd_alloc(struct mm_struct *mm) { + pgd_t *pgd; + if (radix_enabled()) return radix__pgd_alloc(mm); - return kmem_cache_alloc(PGT_CACHE(PGD_INDEX_SIZE), - pgtable_gfp_flags(mm, GFP_KERNEL)); + + pgd = kmem_cache_alloc(PGT_CACHE(PGD_INDEX_SIZE), + pgtable_gfp_flags(mm, GFP_KERNEL)); + memset(pgd, 0, PGD_TABLE_SIZE); + + return pgd; } static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 726c23304a5706ea806a0ea08c3f68c088042d06..8eb3ebca02dfac9d3a411c76cfac398436a306fd 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -21,6 +21,9 @@ /* We calculate number of sg entries based on PAGE_SIZE */ #define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry)) +/* Default time to sleep or delay between OPAL_BUSY/OPAL_BUSY_EVENT loops */ +#define OPAL_BUSY_DELAY_MS 10 + /* /sys/firmware/opal */ extern struct kobject *opal_kobj; diff --git a/arch/powerpc/include/asm/synch.h b/arch/powerpc/include/asm/synch.h index 63e7f5a1f1055480af62d6e6fcf6b9a10c453d9f..6ec546090ba1b186625d0effb5e1f4af64422d4f 100644 --- a/arch/powerpc/include/asm/synch.h +++ b/arch/powerpc/include/asm/synch.h @@ -6,10 +6,6 @@ #include #include -#if defined(__powerpc64__) || defined(CONFIG_PPC_E500MC) -#define __SUBARCH_HAS_LWSYNC -#endif - #ifndef __ASSEMBLY__ extern unsigned int __start___lwsync_fixup, __stop___lwsync_fixup; extern void do_lwsync_fixups(unsigned long value, void *fixup_start, diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index 61d6049f4c1eced70ad3fef32f99b3a438da3eab..8aaec831053af0bf4e6dcb6a8fda63737f65a93d 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h @@ -607,6 +607,8 @@ struct kvm_ppc_rmmu_info { #define KVM_REG_PPC_TIDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbc) #define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd) +#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe) + /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs */ diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 7275fed271afa601711b2f43b0da2c2377c68eff..f047ae1b62712ca1b2dca4d704c7d6b366c08bfc 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -86,6 +86,7 @@ static int hv_mode; static struct { u64 lpcr; + u64 lpcr_clear; u64 hfscr; u64 fscr; } system_registers; @@ -115,6 +116,8 @@ static void cpufeatures_flush_tlb(void) static void __restore_cpu_cpufeatures(void) { + u64 lpcr; + /* * LPCR is restored by the power on engine already. It can be changed * after early init e.g., by radix enable, and we have no unified API @@ -127,8 +130,10 @@ static void __restore_cpu_cpufeatures(void) * The best we can do to accommodate secondary boot and idle restore * for now is "or" LPCR with existing. */ - - mtspr(SPRN_LPCR, system_registers.lpcr | mfspr(SPRN_LPCR)); + lpcr = mfspr(SPRN_LPCR); + lpcr |= system_registers.lpcr; + lpcr &= ~system_registers.lpcr_clear; + mtspr(SPRN_LPCR, lpcr); if (hv_mode) { mtspr(SPRN_LPID, 0); mtspr(SPRN_HFSCR, system_registers.hfscr); @@ -351,8 +356,9 @@ static int __init feat_enable_mmu_hash_v3(struct dt_cpu_feature *f) { u64 lpcr; + system_registers.lpcr_clear |= (LPCR_ISL | LPCR_UPRT | LPCR_HR); lpcr = mfspr(SPRN_LPCR); - lpcr &= ~LPCR_ISL; + lpcr &= ~(LPCR_ISL | LPCR_UPRT | LPCR_HR); mtspr(SPRN_LPCR, lpcr); cur_cpu_spec->mmu_features |= MMU_FTRS_HASH_BASE; diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 8b840191df59680b7030f3202d1b83571c7d8f6a..ca2243df9cb2ecce7a805c33aff253d6678f9252 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -207,18 +207,18 @@ static void *eeh_report_error(void *data, void *userdata) if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe)) return NULL; + + device_lock(&dev->dev); dev->error_state = pci_channel_io_frozen; driver = eeh_pcid_get(dev); - if (!driver) return NULL; + if (!driver) goto out_no_dev; eeh_disable_irq(dev); if (!driver->err_handler || - !driver->err_handler->error_detected) { - eeh_pcid_put(dev); - return NULL; - } + !driver->err_handler->error_detected) + goto out; rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen); @@ -227,7 +227,10 @@ static void *eeh_report_error(void *data, void *userdata) if (*res == PCI_ERS_RESULT_NONE) *res = rc; edev->in_error = true; +out: eeh_pcid_put(dev); +out_no_dev: + device_unlock(&dev->dev); return NULL; } @@ -250,15 +253,14 @@ static void *eeh_report_mmio_enabled(void *data, void *userdata) if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe)) return NULL; + device_lock(&dev->dev); driver = eeh_pcid_get(dev); - if (!driver) return NULL; + if (!driver) goto out_no_dev; if (!driver->err_handler || !driver->err_handler->mmio_enabled || - (edev->mode & EEH_DEV_NO_HANDLER)) { - eeh_pcid_put(dev); - return NULL; - } + (edev->mode & EEH_DEV_NO_HANDLER)) + goto out; rc = driver->err_handler->mmio_enabled(dev); @@ -266,7 +268,10 @@ static void *eeh_report_mmio_enabled(void *data, void *userdata) if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; if (*res == PCI_ERS_RESULT_NONE) *res = rc; +out: eeh_pcid_put(dev); +out_no_dev: + device_unlock(&dev->dev); return NULL; } @@ -289,20 +294,20 @@ static void *eeh_report_reset(void *data, void *userdata) if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe)) return NULL; + + device_lock(&dev->dev); dev->error_state = pci_channel_io_normal; driver = eeh_pcid_get(dev); - if (!driver) return NULL; + if (!driver) goto out_no_dev; eeh_enable_irq(dev); if (!driver->err_handler || !driver->err_handler->slot_reset || (edev->mode & EEH_DEV_NO_HANDLER) || - (!edev->in_error)) { - eeh_pcid_put(dev); - return NULL; - } + (!edev->in_error)) + goto out; rc = driver->err_handler->slot_reset(dev); if ((*res == PCI_ERS_RESULT_NONE) || @@ -310,7 +315,10 @@ static void *eeh_report_reset(void *data, void *userdata) if (*res == PCI_ERS_RESULT_DISCONNECT && rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; +out: eeh_pcid_put(dev); +out_no_dev: + device_unlock(&dev->dev); return NULL; } @@ -361,10 +369,12 @@ static void *eeh_report_resume(void *data, void *userdata) if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe)) return NULL; + + device_lock(&dev->dev); dev->error_state = pci_channel_io_normal; driver = eeh_pcid_get(dev); - if (!driver) return NULL; + if (!driver) goto out_no_dev; was_in_error = edev->in_error; edev->in_error = false; @@ -374,13 +384,15 @@ static void *eeh_report_resume(void *data, void *userdata) !driver->err_handler->resume || (edev->mode & EEH_DEV_NO_HANDLER) || !was_in_error) { edev->mode &= ~EEH_DEV_NO_HANDLER; - eeh_pcid_put(dev); - return NULL; + goto out; } driver->err_handler->resume(dev); +out: eeh_pcid_put(dev); +out_no_dev: + device_unlock(&dev->dev); return NULL; } @@ -400,22 +412,25 @@ static void *eeh_report_failure(void *data, void *userdata) if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe)) return NULL; + + device_lock(&dev->dev); dev->error_state = pci_channel_io_perm_failure; driver = eeh_pcid_get(dev); - if (!driver) return NULL; + if (!driver) goto out_no_dev; eeh_disable_irq(dev); if (!driver->err_handler || - !driver->err_handler->error_detected) { - eeh_pcid_put(dev); - return NULL; - } + !driver->err_handler->error_detected) + goto out; driver->err_handler->error_detected(dev, pci_channel_io_perm_failure); +out: eeh_pcid_put(dev); +out_no_dev: + device_unlock(&dev->dev); return NULL; } diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 2e8d1b2b5af4571132c38593ef1ca160347efecf..8545a9523b9bc0aa3bd325345527ad29ff81e182 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -807,7 +807,8 @@ static void eeh_restore_bridge_bars(struct eeh_dev *edev) eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]); /* PCI Command: 0x4 */ - eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1]); + eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1] | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); /* Check the PCIe link is ready */ eeh_bridge_check_link(edev); diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index e91b40aa5417396931a6f8064b111183532fc929..f9ca4bb3d48ea14ff1652e7b9cec381a8cd57581 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -704,7 +704,7 @@ EXC_COMMON_BEGIN(bad_addr_slb) ld r3, PACA_EXSLB+EX_DAR(r13) std r3, _DAR(r1) beq cr6, 2f - li r10, 0x480 /* fix trap number for I-SLB miss */ + li r10, 0x481 /* fix trap number for I-SLB miss */ std r10, _TRAP(r1) 2: bl save_nvgprs addi r3, r1, STACK_FRAME_OVERHEAD diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 4e65bf82f5e0b1b368170177a43b4cd830fc0932..0ce8b0e5d7ba27912f75a4823bd76c1fea1989ff 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -430,6 +430,14 @@ void force_external_irq_replay(void) */ WARN_ON(!arch_irqs_disabled()); + /* + * Interrupts must always be hard disabled before irq_happened is + * modified (to prevent lost update in case of interrupt between + * load and store). + */ + __hard_irq_disable(); + local_paca->irq_happened |= PACA_IRQ_HARD_DIS; + /* Indicate in the PACA that we have an interrupt to replay */ local_paca->irq_happened |= PACA_IRQ_EE; } diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index bebc3007a793af02394909f47d29f30ab79c5b86..10b46b35c059fe0c01424e3e0b1b1d90260abb76 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -457,29 +457,33 @@ static int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) } kretprobe_assert(ri, orig_ret_address, trampoline_address); - regs->nip = orig_ret_address; + /* - * Make LR point to the orig_ret_address. - * When the 'nop' inside the kretprobe_trampoline - * is optimized, we can do a 'blr' after executing the - * detour buffer code. + * We get here through one of two paths: + * 1. by taking a trap -> kprobe_handler() -> here + * 2. by optprobe branch -> optimized_callback() -> opt_pre_handler() -> here + * + * When going back through (1), we need regs->nip to be setup properly + * as it is used to determine the return address from the trap. + * For (2), since nip is not honoured with optprobes, we instead setup + * the link register properly so that the subsequent 'blr' in + * kretprobe_trampoline jumps back to the right instruction. + * + * For nip, we should set the address to the previous instruction since + * we end up emulating it in kprobe_handler(), which increments the nip + * again. */ + regs->nip = orig_ret_address - 4; regs->link = orig_ret_address; - reset_current_kprobe(); kretprobe_hash_unlock(current, &flags); - preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); } - /* - * By returning a non-zero value, we are telling - * kprobe_handler() that we don't want the post_handler - * to run (and have re-enabled preemption) - */ - return 1; + + return 0; } NOKPROBE_SYMBOL(trampoline_probe_handler); diff --git a/arch/powerpc/kernel/machine_kexec_file_64.c b/arch/powerpc/kernel/machine_kexec_file_64.c index 992c0d258e5d564c3b57526e968b26e78bb9292d..c66132b145ebc717d16334c280b9614d2d7bc48c 100644 --- a/arch/powerpc/kernel/machine_kexec_file_64.c +++ b/arch/powerpc/kernel/machine_kexec_file_64.c @@ -43,7 +43,7 @@ int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, /* We don't support crash kernels yet. */ if (image->type == KEXEC_TYPE_CRASH) - return -ENOTSUPP; + return -EOPNOTSUPP; for (i = 0; i < ARRAY_SIZE(kexec_file_loaders); i++) { fops = kexec_file_loaders[i]; diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 13c9dcdcba6922e32d4c00267201f47e9498f805..d17007451f625a90c95f32d9835e82ae2d21be68 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -336,7 +336,7 @@ void system_reset_exception(struct pt_regs *regs) * No debugger or crash dump registered, print logs then * panic. */ - __die("System Reset", regs, SIGABRT); + die("System Reset", regs, SIGABRT); mdelay(2*MSEC_PER_SEC); /* Wait a little while for others to print */ add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 2645d484e945beb1a3bc82ce735e507c5620b004..df9b53f40b1eb71568716eb4dd44a52d5ccd815e 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -1348,12 +1348,8 @@ static unsigned long resize_hpt_rehash_hpte(struct kvm_resize_hpt *resize, } new_pteg = hash & new_hash_mask; - if (vpte & HPTE_V_SECONDARY) { - BUG_ON(~pteg != (hash & old_hash_mask)); - new_pteg = ~new_pteg; - } else { - BUG_ON(pteg != (hash & old_hash_mask)); - } + if (vpte & HPTE_V_SECONDARY) + new_pteg = ~hash & new_hash_mask; new_idx = new_pteg * HPTES_PER_GROUP + (idx % HPTES_PER_GROUP); new_hptep = (__be64 *)(new->virt + (new_idx << 4)); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index f48e3379a18a880b9b193937f9c3aabe125464d9..e094dc90ff1b3cde5b93f0aced194d319e628071 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1497,6 +1497,10 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_ARCH_COMPAT: *val = get_reg_val(id, vcpu->arch.vcore->arch_compat); break; + case KVM_REG_PPC_DEC_EXPIRY: + *val = get_reg_val(id, vcpu->arch.dec_expires + + vcpu->arch.vcore->tb_offset); + break; default: r = -EINVAL; break; @@ -1724,6 +1728,10 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_ARCH_COMPAT: r = kvmppc_set_arch_compat(vcpu, set_reg_val(id, *val)); break; + case KVM_REG_PPC_DEC_EXPIRY: + vcpu->arch.dec_expires = set_reg_val(id, *val) - + vcpu->arch.vcore->tb_offset; + break; default: r = -EINVAL; break; diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index 4efe364f11881b573acb1c18356be40bd34a553a..4962d537c186eb7ee3d2e497144430ba544387c8 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c @@ -447,8 +447,6 @@ static void do_tlbies(struct kvm *kvm, unsigned long *rbvalues, for (i = 0; i < npages; ++i) { asm volatile(PPC_TLBIE_5(%0,%1,0,0,0) : : "r" (rbvalues[i]), "r" (kvm->arch.lpid)); - trace_tlbie(kvm->arch.lpid, 0, rbvalues[i], - kvm->arch.lpid, 0, 0, 0); } asm volatile("eieio; tlbsync; ptesync" : : : "memory"); kvm->arch.tlbie_lock = 0; @@ -458,8 +456,6 @@ static void do_tlbies(struct kvm *kvm, unsigned long *rbvalues, for (i = 0; i < npages; ++i) { asm volatile(PPC_TLBIEL(%0,%1,0,0,0) : : "r" (rbvalues[i]), "r" (0)); - trace_tlbie(kvm->arch.lpid, 1, rbvalues[i], - 0, 0, 0, 0); } asm volatile("ptesync" : : : "memory"); } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 2b02d51d14d89bc896b23f0b886c05cb0b0a4c40..ecb45361095baaaf5ffe2073c9472bc38a93ffb8 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -758,7 +758,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) hrtimer_init(&vcpu->arch.dec_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); vcpu->arch.dec_timer.function = kvmppc_decrementer_wakeup; - vcpu->arch.dec_expires = ~(u64)0; + vcpu->arch.dec_expires = get_tb(); #ifdef CONFIG_KVM_EXIT_TIMING mutex_init(&vcpu->arch.exit_timing_lock); diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c index a95ea007d654d5db2b78d811a4ef21a750a95609..d0c0b8443dcfc0e6e56a347a449d65db9640193b 100644 --- a/arch/powerpc/lib/feature-fixups.c +++ b/arch/powerpc/lib/feature-fixups.c @@ -55,7 +55,7 @@ static int patch_alt_instruction(unsigned int *src, unsigned int *dest, unsigned int *target = (unsigned int *)branch_target(src); /* Branch within the section doesn't need translating */ - if (target < alt_start || target >= alt_end) { + if (target < alt_start || target > alt_end) { instr = translate_branch(dest, src); if (!instr) return 1; diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 67ec2e927253ef47077e3b7daf91ff8d3998ecd4..87687e46b48bb4c70f450299a8af9257b8c799bf 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -872,6 +872,12 @@ static void __init htab_initialize(void) /* Using a hypervisor which owns the htab */ htab_address = NULL; _SDR1 = 0; + /* + * On POWER9, we need to do a H_REGISTER_PROC_TBL hcall + * to inform the hypervisor that we wish to use the HPT. + */ + if (cpu_has_feature(CPU_FTR_ARCH_300)) + register_process_table(0, 0, 0); #ifdef CONFIG_FA_DUMP /* * If firmware assisted dump is active firmware preserves diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 4362b86ef84c5daa404e806cf3802128b77b8532..9c2f83331e5b75cadf31e33ee1846e3e27c4cc23 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -143,6 +143,7 @@ int arch_add_memory(int nid, u64 start, u64 size, bool want_memblock) start, start + size, rc); return -EFAULT; } + flush_inval_dcache_range(start, start + size); return __add_pages(nid, start_pfn, nr_pages, want_memblock); } @@ -171,6 +172,7 @@ int arch_remove_memory(u64 start, u64 size) /* Remove htab bolted mappings for this section of memory */ start = (unsigned long)__va(start); + flush_inval_dcache_range(start, start + size); ret = remove_section_mapping(start, start + size); /* Ensure all vmalloc mappings are flushed in case they also diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index a81279249bfb14529d702fb8ffd142264108e20f..9fead0796364b87368daa51fb648206ec5475aeb 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -546,7 +546,7 @@ static int numa_setup_cpu(unsigned long lcpu) nid = of_node_to_nid_single(cpu); out_present: - if (nid < 0 || !node_online(nid)) + if (nid < 0 || !node_possible(nid)) nid = first_online_node; map_cpu_to_node(lcpu, nid); @@ -887,6 +887,32 @@ static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn) NODE_DATA(nid)->node_spanned_pages = spanned_pages; } +static void __init find_possible_nodes(void) +{ + struct device_node *rtas; + u32 numnodes, i; + + if (min_common_depth <= 0) + return; + + rtas = of_find_node_by_path("/rtas"); + if (!rtas) + return; + + if (of_property_read_u32_index(rtas, + "ibm,max-associativity-domains", + min_common_depth, &numnodes)) + goto out; + + for (i = 0; i < numnodes; i++) { + if (!node_possible(i)) + node_set(i, node_possible_map); + } + +out: + of_node_put(rtas); +} + void __init initmem_init(void) { int nid, cpu; @@ -900,12 +926,15 @@ void __init initmem_init(void) memblock_dump_all(); /* - * Reduce the possible NUMA nodes to the online NUMA nodes, - * since we do not support node hotplug. This ensures that we - * lower the maximum NUMA node ID to what is actually present. + * Modify the set of possible NUMA nodes to reflect information + * available about the set of online nodes, and the set of nodes + * that we expect to make use of for this platform's affinity + * calculations. */ nodes_and(node_possible_map, node_possible_map, node_online_map); + find_possible_nodes(); + for_each_online_node(nid) { unsigned long start_pfn, end_pfn; @@ -1246,6 +1275,40 @@ static long vphn_get_associativity(unsigned long cpu, return rc; } +static inline int find_and_online_cpu_nid(int cpu) +{ + __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0}; + int new_nid; + + /* Use associativity from first thread for all siblings */ + vphn_get_associativity(cpu, associativity); + new_nid = associativity_to_nid(associativity); + if (new_nid < 0 || !node_possible(new_nid)) + new_nid = first_online_node; + + if (NODE_DATA(new_nid) == NULL) { +#ifdef CONFIG_MEMORY_HOTPLUG + /* + * Need to ensure that NODE_DATA is initialized for a node from + * available memory (see memblock_alloc_try_nid). If unable to + * init the node, then default to nearest node that has memory + * installed. + */ + if (try_online_node(new_nid)) + new_nid = first_online_node; +#else + /* + * Default to using the nearest node that has memory installed. + * Otherwise, it would be necessary to patch the kernel MM code + * to deal with more memoryless-node error conditions. + */ + new_nid = first_online_node; +#endif + } + + return new_nid; +} + /* * Update the CPU maps and sysfs entries for a single CPU when its NUMA * characteristics change. This function doesn't perform any locking and is @@ -1313,7 +1376,6 @@ int numa_update_cpu_topology(bool cpus_locked) { unsigned int cpu, sibling, changed = 0; struct topology_update_data *updates, *ud; - __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0}; cpumask_t updated_cpus; struct device *dev; int weight, new_nid, i = 0; @@ -1348,11 +1410,7 @@ int numa_update_cpu_topology(bool cpus_locked) continue; } - /* Use associativity from first thread for all siblings */ - vphn_get_associativity(cpu, associativity); - new_nid = associativity_to_nid(associativity); - if (new_nid < 0 || !node_online(new_nid)) - new_nid = first_online_node; + new_nid = find_and_online_cpu_nid(cpu); if (new_nid == numa_cpu_lookup_table[cpu]) { cpumask_andnot(&cpu_associativity_changes_mask, diff --git a/arch/powerpc/platforms/powernv/npu-dma.c b/arch/powerpc/platforms/powernv/npu-dma.c index 2cb6cbea4b3b377329803a1a825360c60df10835..4043109f4051eb5b5a90f46213ea7c5d4c68bd78 100644 --- a/arch/powerpc/platforms/powernv/npu-dma.c +++ b/arch/powerpc/platforms/powernv/npu-dma.c @@ -33,6 +33,13 @@ #define npu_to_phb(x) container_of(x, struct pnv_phb, npu) +/* + * When an address shootdown range exceeds this threshold we invalidate the + * entire TLB on the GPU for the given PID rather than each specific address in + * the range. + */ +#define ATSD_THRESHOLD (2*1024*1024) + /* * Other types of TCE cache invalidation are not functional in the * hardware. @@ -621,11 +628,19 @@ static void pnv_npu2_mn_invalidate_range(struct mmu_notifier *mn, struct npu_context *npu_context = mn_to_npu_context(mn); unsigned long address; - for (address = start; address < end; address += PAGE_SIZE) - mmio_invalidate(npu_context, 1, address, false); + if (end - start > ATSD_THRESHOLD) { + /* + * Just invalidate the entire PID if the address range is too + * large. + */ + mmio_invalidate(npu_context, 0, 0, true); + } else { + for (address = start; address < end; address += PAGE_SIZE) + mmio_invalidate(npu_context, 1, address, false); - /* Do the flush only on the final addess == end */ - mmio_invalidate(npu_context, 1, address, true); + /* Do the flush only on the final addess == end */ + mmio_invalidate(npu_context, 1, address, true); + } } static const struct mmu_notifier_ops nv_nmmu_notifier_ops = { diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c index b150f4deaccfc5f153e6c3527579b9dc288cdab3..6914b289c86ba461894dd64c65553b1df6eae898 100644 --- a/arch/powerpc/platforms/powernv/opal-imc.c +++ b/arch/powerpc/platforms/powernv/opal-imc.c @@ -126,9 +126,11 @@ static void disable_nest_pmu_counters(void) const struct cpumask *l_cpumask; get_online_cpus(); - for_each_online_node(nid) { + for_each_node_with_cpus(nid) { l_cpumask = cpumask_of_node(nid); - cpu = cpumask_first(l_cpumask); + cpu = cpumask_first_and(l_cpumask, cpu_online_mask); + if (cpu >= nr_cpu_ids) + continue; opal_imc_counters_stop(OPAL_IMC_COUNTERS_NEST, get_hard_smp_processor_id(cpu)); } diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c index 9db4398ded5de1f6c8e9a672a51008df1fbc85d7..1bceb95f422d0f828017128580695c0d4c87ba47 100644 --- a/arch/powerpc/platforms/powernv/opal-nvram.c +++ b/arch/powerpc/platforms/powernv/opal-nvram.c @@ -11,6 +11,7 @@ #define DEBUG +#include #include #include #include @@ -56,9 +57,17 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_write_nvram(__pa(buf), count, off); - if (rc == OPAL_BUSY_EVENT) + if (rc == OPAL_BUSY_EVENT) { + msleep(OPAL_BUSY_DELAY_MS); opal_poll_events(NULL); + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } } + + if (rc) + return -EIO; + *index += count; return count; } diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c index f8868864f373ed5eecba333a5b1a2b83e38e0ec6..aa2a5139462ea5f6c81f51c8f025c9af84ce0be2 100644 --- a/arch/powerpc/platforms/powernv/opal-rtc.c +++ b/arch/powerpc/platforms/powernv/opal-rtc.c @@ -48,10 +48,12 @@ unsigned long __init opal_get_boot_time(void) while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); - if (rc == OPAL_BUSY_EVENT) + if (rc == OPAL_BUSY_EVENT) { + mdelay(OPAL_BUSY_DELAY_MS); opal_poll_events(NULL); - else if (rc == OPAL_BUSY) - mdelay(10); + } else if (rc == OPAL_BUSY) { + mdelay(OPAL_BUSY_DELAY_MS); + } } if (rc != OPAL_SUCCESS) return 0; diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 4ac419c7eb4c9891a8cd71fffe5ce107cce0c6f8..560aefde06c0240a8c6071e3a92ff062337afc1d 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -742,7 +742,7 @@ static void cmm_exit(void) * Return value: * 0 on success / other on failure **/ -static int cmm_set_disable(const char *val, struct kernel_param *kp) +static int cmm_set_disable(const char *val, const struct kernel_param *kp) { int disable = simple_strtoul(val, NULL, 10); diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 495ba4e7336d11447bfc9e2010fcdf2c0fbff6af..55e97565ed2ddeacf834a1e31a6b57ba584e7dc5 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -726,15 +726,18 @@ static int pseries_lpar_resize_hpt(unsigned long shift) return 0; } -/* Actually only used for radix, so far */ static int pseries_lpar_register_process_table(unsigned long base, unsigned long page_size, unsigned long table_size) { long rc; - unsigned long flags = PROC_TABLE_NEW; + unsigned long flags = 0; + if (table_size) + flags |= PROC_TABLE_NEW; if (radix_enabled()) flags |= PROC_TABLE_RADIX | PROC_TABLE_GTSE; + else + flags |= PROC_TABLE_HPT_SLB; for (;;) { rc = plpar_hcall_norets(H_REGISTER_PROC_TBL, flags, base, page_size, table_size); @@ -760,6 +763,7 @@ void __init hpte_init_pseries(void) mmu_hash_ops.flush_hash_range = pSeries_lpar_flush_hash_range; mmu_hash_ops.hpte_clear_all = pseries_hpte_clear_all; mmu_hash_ops.hugepage_invalidate = pSeries_lpar_hugepage_invalidate; + register_process_table = pseries_lpar_register_process_table; if (firmware_has_feature(FW_FEATURE_HPT_RESIZE)) mmu_hash_ops.resize_hpt = pseries_lpar_resize_hpt; diff --git a/arch/powerpc/sysdev/xive/native.c b/arch/powerpc/sysdev/xive/native.c index ebc244b08d6748512c19199446d25f7ac49fca9b..0f89ee557b044f66435b2e145c58fa4e7076e0d4 100644 --- a/arch/powerpc/sysdev/xive/native.c +++ b/arch/powerpc/sysdev/xive/native.c @@ -388,6 +388,10 @@ static void xive_native_setup_cpu(unsigned int cpu, struct xive_cpu *xc) if (xive_pool_vps == XIVE_INVALID_VP) return; + /* Check if pool VP already active, if it is, pull it */ + if (in_be32(xive_tima + TM_QW2_HV_POOL + TM_WORD2) & TM_QW2W2_VP) + in_be64(xive_tima + TM_SPC_PULL_POOL_CTX); + /* Enable the pool VP */ vp = xive_pool_vps + cpu; pr_debug("CPU %d setting up pool VP 0x%x\n", cpu, vp); diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index ae55e715cc748e0148601c9cd05de1f969f60560..49fb6614ea8c79a2cd5cfdf291c40b9ff35684ea 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -121,6 +121,7 @@ config S390 select GENERIC_CLOCKEVENTS select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_DEVICES if !SMP + select GENERIC_CPU_VULNERABILITIES select GENERIC_FIND_FIRST_BIT select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL @@ -538,6 +539,51 @@ config ARCH_RANDOM If unsure, say Y. +config KERNEL_NOBP + def_bool n + prompt "Enable modified branch prediction for the kernel by default" + help + If this option is selected the kernel will switch to a modified + branch prediction mode if the firmware interface is available. + The modified branch prediction mode improves the behaviour in + regard to speculative execution. + + With the option enabled the kernel parameter "nobp=0" or "nospec" + can be used to run the kernel in the normal branch prediction mode. + + With the option disabled the modified branch prediction mode is + enabled with the "nobp=1" kernel parameter. + + If unsure, say N. + +config EXPOLINE + def_bool n + prompt "Avoid speculative indirect branches in the kernel" + help + Compile the kernel with the expoline compiler options to guard + against kernel-to-user data leaks by avoiding speculative indirect + branches. + Requires a compiler with -mindirect-branch=thunk support for full + protection. The kernel may run slower. + + If unsure, say N. + +choice + prompt "Expoline default" + depends on EXPOLINE + default EXPOLINE_FULL + +config EXPOLINE_OFF + bool "spectre_v2=off" + +config EXPOLINE_AUTO + bool "spectre_v2=auto" + +config EXPOLINE_FULL + bool "spectre_v2=on" + +endchoice + endmenu menu "Memory setup" @@ -812,6 +858,7 @@ config PFAULT config SHARED_KERNEL bool "VM shared kernel support" depends on !JUMP_LABEL + depends on !ALTERNATIVES help Select this option, if you want to share the text segment of the Linux kernel between different VM guests. This reduces memory diff --git a/arch/s390/Makefile b/arch/s390/Makefile index dac821cfcd430310a95624754d7dd814bcd7705b..ec3fa105f4481f4b08a910f36cbac215c492f81a 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -81,6 +81,16 @@ ifeq ($(call cc-option-yn,-mwarn-dynamicstack),y) cflags-$(CONFIG_WARN_DYNAMIC_STACK) += -mwarn-dynamicstack endif +ifdef CONFIG_EXPOLINE + ifeq ($(call cc-option-yn,$(CC_FLAGS_MARCH) -mindirect-branch=thunk),y) + CC_FLAGS_EXPOLINE := -mindirect-branch=thunk + CC_FLAGS_EXPOLINE += -mfunction-return=thunk + CC_FLAGS_EXPOLINE += -mindirect-branch-table + export CC_FLAGS_EXPOLINE + cflags-y += $(CC_FLAGS_EXPOLINE) -DCC_USING_EXPOLINE + endif +endif + ifdef CONFIG_FUNCTION_TRACER # make use of hotpatch feature if the compiler supports it cc_hotpatch := -mhotpatch=0,3 diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index cf8a2d92467f363a6a2195f77a6d07dff2b5d3d0..45eb5999110bed8e40ca1d0690508684422e937a 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -320,7 +320,7 @@ static void hypfs_kill_super(struct super_block *sb) if (sb->s_root) hypfs_delete_tree(sb->s_root); - if (sb_info->update_file) + if (sb_info && sb_info->update_file) hypfs_remove(sb_info->update_file); kfree(sb->s_fs_info); sb->s_fs_info = NULL; diff --git a/arch/s390/include/asm/alternative.h b/arch/s390/include/asm/alternative.h new file mode 100644 index 0000000000000000000000000000000000000000..a72002056b54848103fc626338fee956c145bfc9 --- /dev/null +++ b/arch/s390/include/asm/alternative.h @@ -0,0 +1,149 @@ +#ifndef _ASM_S390_ALTERNATIVE_H +#define _ASM_S390_ALTERNATIVE_H + +#ifndef __ASSEMBLY__ + +#include +#include +#include + +struct alt_instr { + s32 instr_offset; /* original instruction */ + s32 repl_offset; /* offset to replacement instruction */ + u16 facility; /* facility bit set for replacement */ + u8 instrlen; /* length of original instruction */ + u8 replacementlen; /* length of new instruction */ +} __packed; + +void apply_alternative_instructions(void); +void apply_alternatives(struct alt_instr *start, struct alt_instr *end); + +/* + * |661: |662: |6620 |663: + * +-----------+---------------------+ + * | oldinstr | oldinstr_padding | + * | +----------+----------+ + * | | | | + * | | >6 bytes |6/4/2 nops| + * | |6 bytes jg-----------> + * +-----------+---------------------+ + * ^^ static padding ^^ + * + * .altinstr_replacement section + * +---------------------+-----------+ + * |6641: |6651: + * | alternative instr 1 | + * +-----------+---------+- - - - - -+ + * |6642: |6652: | + * | alternative instr 2 | padding + * +---------------------+- - - - - -+ + * ^ runtime ^ + * + * .altinstructions section + * +---------------------------------+ + * | alt_instr entries for each | + * | alternative instr | + * +---------------------------------+ + */ + +#define b_altinstr(num) "664"#num +#define e_altinstr(num) "665"#num + +#define e_oldinstr_pad_end "663" +#define oldinstr_len "662b-661b" +#define oldinstr_total_len e_oldinstr_pad_end"b-661b" +#define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b" +#define oldinstr_pad_len(num) \ + "-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \ + "((" altinstr_len(num) ")-(" oldinstr_len "))" + +#define INSTR_LEN_SANITY_CHECK(len) \ + ".if " len " > 254\n" \ + "\t.error \"cpu alternatives does not support instructions " \ + "blocks > 254 bytes\"\n" \ + ".endif\n" \ + ".if (" len ") %% 2\n" \ + "\t.error \"cpu alternatives instructions length is odd\"\n" \ + ".endif\n" + +#define OLDINSTR_PADDING(oldinstr, num) \ + ".if " oldinstr_pad_len(num) " > 6\n" \ + "\tjg " e_oldinstr_pad_end "f\n" \ + "6620:\n" \ + "\t.fill (" oldinstr_pad_len(num) " - (6620b-662b)) / 2, 2, 0x0700\n" \ + ".else\n" \ + "\t.fill " oldinstr_pad_len(num) " / 6, 6, 0xc0040000\n" \ + "\t.fill " oldinstr_pad_len(num) " %% 6 / 4, 4, 0x47000000\n" \ + "\t.fill " oldinstr_pad_len(num) " %% 6 %% 4 / 2, 2, 0x0700\n" \ + ".endif\n" + +#define OLDINSTR(oldinstr, num) \ + "661:\n\t" oldinstr "\n662:\n" \ + OLDINSTR_PADDING(oldinstr, num) \ + e_oldinstr_pad_end ":\n" \ + INSTR_LEN_SANITY_CHECK(oldinstr_len) + +#define OLDINSTR_2(oldinstr, num1, num2) \ + "661:\n\t" oldinstr "\n662:\n" \ + ".if " altinstr_len(num1) " < " altinstr_len(num2) "\n" \ + OLDINSTR_PADDING(oldinstr, num2) \ + ".else\n" \ + OLDINSTR_PADDING(oldinstr, num1) \ + ".endif\n" \ + e_oldinstr_pad_end ":\n" \ + INSTR_LEN_SANITY_CHECK(oldinstr_len) + +#define ALTINSTR_ENTRY(facility, num) \ + "\t.long 661b - .\n" /* old instruction */ \ + "\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \ + "\t.word " __stringify(facility) "\n" /* facility bit */ \ + "\t.byte " oldinstr_total_len "\n" /* source len */ \ + "\t.byte " altinstr_len(num) "\n" /* alt instruction len */ + +#define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \ + b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \ + INSTR_LEN_SANITY_CHECK(altinstr_len(num)) + +/* alternative assembly primitive: */ +#define ALTERNATIVE(oldinstr, altinstr, facility) \ + ".pushsection .altinstr_replacement, \"ax\"\n" \ + ALTINSTR_REPLACEMENT(altinstr, 1) \ + ".popsection\n" \ + OLDINSTR(oldinstr, 1) \ + ".pushsection .altinstructions,\"a\"\n" \ + ALTINSTR_ENTRY(facility, 1) \ + ".popsection\n" + +#define ALTERNATIVE_2(oldinstr, altinstr1, facility1, altinstr2, facility2)\ + ".pushsection .altinstr_replacement, \"ax\"\n" \ + ALTINSTR_REPLACEMENT(altinstr1, 1) \ + ALTINSTR_REPLACEMENT(altinstr2, 2) \ + ".popsection\n" \ + OLDINSTR_2(oldinstr, 1, 2) \ + ".pushsection .altinstructions,\"a\"\n" \ + ALTINSTR_ENTRY(facility1, 1) \ + ALTINSTR_ENTRY(facility2, 2) \ + ".popsection\n" + +/* + * Alternative instructions for different CPU types or capabilities. + * + * This allows to use optimized instructions even on generic binary + * kernels. + * + * oldinstr is padded with jump and nops at compile time if altinstr is + * longer. altinstr is padded with jump and nops at run-time during patching. + * + * For non barrier like inlines please define new variants + * without volatile and memory clobber. + */ +#define alternative(oldinstr, altinstr, facility) \ + asm volatile(ALTERNATIVE(oldinstr, altinstr, facility) : : : "memory") + +#define alternative_2(oldinstr, altinstr1, facility1, altinstr2, facility2) \ + asm volatile(ALTERNATIVE_2(oldinstr, altinstr1, facility1, \ + altinstr2, facility2) ::: "memory") + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_S390_ALTERNATIVE_H */ diff --git a/arch/s390/include/asm/barrier.h b/arch/s390/include/asm/barrier.h index 10432607a573625be45d23e6263c05e378a0bdfe..f9eddbca79d2859f856454a2a0dbce68ab9eca36 100644 --- a/arch/s390/include/asm/barrier.h +++ b/arch/s390/include/asm/barrier.h @@ -49,6 +49,30 @@ do { \ #define __smp_mb__before_atomic() barrier() #define __smp_mb__after_atomic() barrier() +/** + * array_index_mask_nospec - generate a mask for array_idx() that is + * ~0UL when the bounds check succeeds and 0 otherwise + * @index: array element index + * @size: number of elements in array + */ +#define array_index_mask_nospec array_index_mask_nospec +static inline unsigned long array_index_mask_nospec(unsigned long index, + unsigned long size) +{ + unsigned long mask; + + if (__builtin_constant_p(size) && size > 0) { + asm(" clgr %2,%1\n" + " slbgr %0,%0\n" + :"=d" (mask) : "d" (size-1), "d" (index) :"cc"); + return mask; + } + asm(" clgr %1,%2\n" + " slbgr %0,%0\n" + :"=d" (mask) : "d" (size), "d" (index) :"cc"); + return ~mask; +} + #include #endif /* __ASM_BARRIER_H */ diff --git a/arch/s390/include/asm/eadm.h b/arch/s390/include/asm/eadm.h index eb5323161f11e63a3fc6f98ef3a8062ee1bb4e24..bb63b2afdf6fc1c0d8cd6628d0e0573ddc2945af 100644 --- a/arch/s390/include/asm/eadm.h +++ b/arch/s390/include/asm/eadm.h @@ -4,7 +4,7 @@ #include #include -#include +#include struct arqb { u64 data; diff --git a/arch/s390/include/asm/facility.h b/arch/s390/include/asm/facility.h index f040644575b7104c27d770e4f5b42ca85134be1b..2d58478c2745b202cd0a1b4572528b727f7b5c80 100644 --- a/arch/s390/include/asm/facility.h +++ b/arch/s390/include/asm/facility.h @@ -15,6 +15,24 @@ #define MAX_FACILITY_BIT (sizeof(((struct lowcore *)0)->stfle_fac_list) * 8) +static inline void __set_facility(unsigned long nr, void *facilities) +{ + unsigned char *ptr = (unsigned char *) facilities; + + if (nr >= MAX_FACILITY_BIT) + return; + ptr[nr >> 3] |= 0x80 >> (nr & 7); +} + +static inline void __clear_facility(unsigned long nr, void *facilities) +{ + unsigned char *ptr = (unsigned char *) facilities; + + if (nr >= MAX_FACILITY_BIT) + return; + ptr[nr >> 3] &= ~(0x80 >> (nr & 7)); +} + static inline int __test_facility(unsigned long nr, void *facilities) { unsigned char *ptr; diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 51375e766e905a955a4df21fa185c669bfaa9544..d660e784e4454ec5635fd20f8ac1a342f3b52ccf 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -210,7 +210,8 @@ struct kvm_s390_sie_block { __u16 ipa; /* 0x0056 */ __u32 ipb; /* 0x0058 */ __u32 scaoh; /* 0x005c */ - __u8 reserved60; /* 0x0060 */ +#define FPF_BPBC 0x20 + __u8 fpf; /* 0x0060 */ #define ECB_GS 0x40 #define ECB_TE 0x10 #define ECB_SRSI 0x04 diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index 917f7344cab6fbf627ee29f788f1de467b0690e0..88a212df0dbcdb5997b6a5025e4fb2063ba214e1 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -140,7 +140,9 @@ struct lowcore { /* Per cpu primary space access list */ __u32 paste[16]; /* 0x0400 */ - __u8 pad_0x04c0[0x0e00-0x0440]; /* 0x0440 */ + /* br %r1 trampoline */ + __u16 br_r1_trampoline; /* 0x0440 */ + __u8 pad_0x0442[0x0e00-0x0442]; /* 0x0442 */ /* * 0xe00 contains the address of the IPL Parameter Information @@ -155,7 +157,8 @@ struct lowcore { __u8 pad_0x0e20[0x0f00-0x0e20]; /* 0x0e20 */ /* Extended facility list */ - __u64 stfle_fac_list[32]; /* 0x0f00 */ + __u64 stfle_fac_list[16]; /* 0x0f00 */ + __u64 alt_stfle_fac_list[16]; /* 0x0f80 */ __u8 pad_0x1000[0x11b0-0x1000]; /* 0x1000 */ /* Pointer to the machine check extended save area */ diff --git a/arch/s390/include/asm/nospec-branch.h b/arch/s390/include/asm/nospec-branch.h new file mode 100644 index 0000000000000000000000000000000000000000..b4bd8c41e9d33d4f01ef8950be79f0fd02cd59c6 --- /dev/null +++ b/arch/s390/include/asm/nospec-branch.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_S390_EXPOLINE_H +#define _ASM_S390_EXPOLINE_H + +#ifndef __ASSEMBLY__ + +#include + +extern int nospec_disable; + +void nospec_init_branches(void); +void nospec_auto_detect(void); +void nospec_revert(s32 *start, s32 *end); + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_S390_EXPOLINE_H */ diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 9cf92abe23c32ab495b5e4640f7904ab37cd0d54..0a39cd102c49263903ed43f269c32690738dd418 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -89,6 +89,7 @@ void cpu_detect_mhz_feature(void); extern const struct seq_operations cpuinfo_op; extern int sysctl_ieee_emulation_warnings; extern void execve_tail(void); +extern void __bpon(void); /* * User space process size: 2GB for 31 bit, 4TB or 8PT for 64 bit. @@ -377,6 +378,9 @@ extern void memcpy_absolute(void *, void *, size_t); memcpy_absolute(&(dest), &__tmp, sizeof(__tmp)); \ } while (0) +extern int s390_isolate_bp(void); +extern int s390_isolate_bp_guest(void); + #endif /* __ASSEMBLY__ */ #endif /* __ASM_S390_PROCESSOR_H */ diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index 0880a37b6d3befe6baf5afbf345c3a4b19397897..301b4f70bf313b8a482a48877085f10e98d29f3c 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -60,6 +60,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); #define TIF_GUARDED_STORAGE 4 /* load guarded storage control block */ #define TIF_PATCH_PENDING 5 /* pending live patching update */ #define TIF_PGSTE 6 /* New mm's will use 4K page tables */ +#define TIF_ISOLATE_BP 8 /* Run process with isolated BP */ +#define TIF_ISOLATE_BP_GUEST 9 /* Run KVM guests with isolated BP */ #define TIF_31BIT 16 /* 32bit process */ #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ @@ -80,6 +82,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); #define _TIF_UPROBE _BITUL(TIF_UPROBE) #define _TIF_GUARDED_STORAGE _BITUL(TIF_GUARDED_STORAGE) #define _TIF_PATCH_PENDING _BITUL(TIF_PATCH_PENDING) +#define _TIF_ISOLATE_BP _BITUL(TIF_ISOLATE_BP) +#define _TIF_ISOLATE_BP_GUEST _BITUL(TIF_ISOLATE_BP_GUEST) #define _TIF_31BIT _BITUL(TIF_31BIT) #define _TIF_SINGLE_STEP _BITUL(TIF_SINGLE_STEP) diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h index 9ad172dcd912d5763b0bf954617c9e398ad31aa8..a3938db010f77a69b53924e7bf9252e8a12fb0a4 100644 --- a/arch/s390/include/uapi/asm/kvm.h +++ b/arch/s390/include/uapi/asm/kvm.h @@ -228,6 +228,7 @@ struct kvm_guest_debug_arch { #define KVM_SYNC_RICCB (1UL << 7) #define KVM_SYNC_FPRS (1UL << 8) #define KVM_SYNC_GSCB (1UL << 9) +#define KVM_SYNC_BPBC (1UL << 10) /* length and alignment of the sdnx as a power of two */ #define SDNXC 8 #define SDNXL (1UL << SDNXC) @@ -251,7 +252,9 @@ struct kvm_sync_regs { }; __u8 reserved[512]; /* for future vector expansion */ __u32 fpc; /* valid on KVM_SYNC_VRS or KVM_SYNC_FPRS */ - __u8 padding1[52]; /* riccb needs to be 64byte aligned */ + __u8 bpbc : 1; /* bp mode */ + __u8 reserved2 : 7; + __u8 padding1[51]; /* riccb needs to be 64byte aligned */ __u8 riccb[64]; /* runtime instrumentation controls block */ __u8 padding2[192]; /* sdnx needs to be 256byte aligned */ union { diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 4ce2d05929a7a21853e15d10d317fa0a31591d7d..a3a4cafb6080ff526575cd81aedc0df9088c87a0 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -29,6 +29,7 @@ UBSAN_SANITIZE_early.o := n # ifneq ($(CC_FLAGS_MARCH),-march=z900) CFLAGS_REMOVE_als.o += $(CC_FLAGS_MARCH) +CFLAGS_REMOVE_als.o += $(CC_FLAGS_EXPOLINE) CFLAGS_als.o += -march=z900 AFLAGS_REMOVE_head.o += $(CC_FLAGS_MARCH) AFLAGS_head.o += -march=z900 @@ -57,10 +58,13 @@ obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o -obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o +obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o +obj-y += nospec-branch.o extra-y += head.o head64.o vmlinux.lds +CFLAGS_REMOVE_nospec-branch.o += $(CC_FLAGS_EXPOLINE) + obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SCHED_TOPOLOGY) += topology.o diff --git a/arch/s390/kernel/alternative.c b/arch/s390/kernel/alternative.c new file mode 100644 index 0000000000000000000000000000000000000000..b57b293998dc8442fc233c9ead5651004296aef1 --- /dev/null +++ b/arch/s390/kernel/alternative.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#define MAX_PATCH_LEN (255 - 1) + +static int __initdata_or_module alt_instr_disabled; + +static int __init disable_alternative_instructions(char *str) +{ + alt_instr_disabled = 1; + return 0; +} + +early_param("noaltinstr", disable_alternative_instructions); + +struct brcl_insn { + u16 opc; + s32 disp; +} __packed; + +static u16 __initdata_or_module nop16 = 0x0700; +static u32 __initdata_or_module nop32 = 0x47000000; +static struct brcl_insn __initdata_or_module nop48 = { + 0xc004, 0 +}; + +static const void *nops[] __initdata_or_module = { + &nop16, + &nop32, + &nop48 +}; + +static void __init_or_module add_jump_padding(void *insns, unsigned int len) +{ + struct brcl_insn brcl = { + 0xc0f4, + len / 2 + }; + + memcpy(insns, &brcl, sizeof(brcl)); + insns += sizeof(brcl); + len -= sizeof(brcl); + + while (len > 0) { + memcpy(insns, &nop16, 2); + insns += 2; + len -= 2; + } +} + +static void __init_or_module add_padding(void *insns, unsigned int len) +{ + if (len > 6) + add_jump_padding(insns, len); + else if (len >= 2) + memcpy(insns, nops[len / 2 - 1], len); +} + +static void __init_or_module __apply_alternatives(struct alt_instr *start, + struct alt_instr *end) +{ + struct alt_instr *a; + u8 *instr, *replacement; + u8 insnbuf[MAX_PATCH_LEN]; + + /* + * The scan order should be from start to end. A later scanned + * alternative code can overwrite previously scanned alternative code. + */ + for (a = start; a < end; a++) { + int insnbuf_sz = 0; + + instr = (u8 *)&a->instr_offset + a->instr_offset; + replacement = (u8 *)&a->repl_offset + a->repl_offset; + + if (!__test_facility(a->facility, + S390_lowcore.alt_stfle_fac_list)) + continue; + + if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) { + WARN_ONCE(1, "cpu alternatives instructions length is " + "odd, skipping patching\n"); + continue; + } + + memcpy(insnbuf, replacement, a->replacementlen); + insnbuf_sz = a->replacementlen; + + if (a->instrlen > a->replacementlen) { + add_padding(insnbuf + a->replacementlen, + a->instrlen - a->replacementlen); + insnbuf_sz += a->instrlen - a->replacementlen; + } + + s390_kernel_write(instr, insnbuf, insnbuf_sz); + } +} + +void __init_or_module apply_alternatives(struct alt_instr *start, + struct alt_instr *end) +{ + if (!alt_instr_disabled) + __apply_alternatives(start, end); +} + +extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; +void __init apply_alternative_instructions(void) +{ + apply_alternatives(__alt_instructions, __alt_instructions_end); +} diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index f7b280f0ab1682004a545199905c779abaac3ffe..a3219837fa702212e918e187ccd12c4eab824d55 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -329,6 +329,11 @@ static noinline __init void setup_facility_list(void) { stfle(S390_lowcore.stfle_fac_list, ARRAY_SIZE(S390_lowcore.stfle_fac_list)); + memcpy(S390_lowcore.alt_stfle_fac_list, + S390_lowcore.stfle_fac_list, + sizeof(S390_lowcore.alt_stfle_fac_list)); + if (!IS_ENABLED(CONFIG_KERNEL_NOBP)) + __clear_facility(82, S390_lowcore.alt_stfle_fac_list); } static __init void detect_diag9c(void) diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 7c6904d616d877330ae5768a9ad348e298a1562e..ed9aaa212d4a10495964d9349affc256ce1ffe05 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -106,6 +106,7 @@ _PIF_WORK = (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART) aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE) j 3f 1: UPDATE_VTIME %r14,%r15,\timer + BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP 2: lg %r15,__LC_ASYNC_STACK # load async stack 3: la %r11,STACK_FRAME_OVERHEAD(%r15) .endm @@ -158,6 +159,130 @@ _PIF_WORK = (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART) tm off+\addr, \mask .endm + .macro BPOFF + .pushsection .altinstr_replacement, "ax" +660: .long 0xb2e8c000 + .popsection +661: .long 0x47000000 + .pushsection .altinstructions, "a" + .long 661b - . + .long 660b - . + .word 82 + .byte 4 + .byte 4 + .popsection + .endm + + .macro BPON + .pushsection .altinstr_replacement, "ax" +662: .long 0xb2e8d000 + .popsection +663: .long 0x47000000 + .pushsection .altinstructions, "a" + .long 663b - . + .long 662b - . + .word 82 + .byte 4 + .byte 4 + .popsection + .endm + + .macro BPENTER tif_ptr,tif_mask + .pushsection .altinstr_replacement, "ax" +662: .word 0xc004, 0x0000, 0x0000 # 6 byte nop + .word 0xc004, 0x0000, 0x0000 # 6 byte nop + .popsection +664: TSTMSK \tif_ptr,\tif_mask + jz . + 8 + .long 0xb2e8d000 + .pushsection .altinstructions, "a" + .long 664b - . + .long 662b - . + .word 82 + .byte 12 + .byte 12 + .popsection + .endm + + .macro BPEXIT tif_ptr,tif_mask + TSTMSK \tif_ptr,\tif_mask + .pushsection .altinstr_replacement, "ax" +662: jnz . + 8 + .long 0xb2e8d000 + .popsection +664: jz . + 8 + .long 0xb2e8c000 + .pushsection .altinstructions, "a" + .long 664b - . + .long 662b - . + .word 82 + .byte 8 + .byte 8 + .popsection + .endm + +#ifdef CONFIG_EXPOLINE + + .macro GEN_BR_THUNK name,reg,tmp + .section .text.\name,"axG",@progbits,\name,comdat + .globl \name + .hidden \name + .type \name,@function +\name: + .cfi_startproc +#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES + exrl 0,0f +#else + larl \tmp,0f + ex 0,0(\tmp) +#endif + j . +0: br \reg + .cfi_endproc + .endm + + GEN_BR_THUNK __s390x_indirect_jump_r1use_r9,%r9,%r1 + GEN_BR_THUNK __s390x_indirect_jump_r1use_r14,%r14,%r1 + GEN_BR_THUNK __s390x_indirect_jump_r11use_r14,%r14,%r11 + + .macro BASR_R14_R9 +0: brasl %r14,__s390x_indirect_jump_r1use_r9 + .pushsection .s390_indirect_branches,"a",@progbits + .long 0b-. + .popsection + .endm + + .macro BR_R1USE_R14 +0: jg __s390x_indirect_jump_r1use_r14 + .pushsection .s390_indirect_branches,"a",@progbits + .long 0b-. + .popsection + .endm + + .macro BR_R11USE_R14 +0: jg __s390x_indirect_jump_r11use_r14 + .pushsection .s390_indirect_branches,"a",@progbits + .long 0b-. + .popsection + .endm + +#else /* CONFIG_EXPOLINE */ + + .macro BASR_R14_R9 + basr %r14,%r9 + .endm + + .macro BR_R1USE_R14 + br %r14 + .endm + + .macro BR_R11USE_R14 + br %r14 + .endm + +#endif /* CONFIG_EXPOLINE */ + + .section .kprobes.text, "ax" .Ldummy: /* @@ -170,6 +295,11 @@ _PIF_WORK = (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART) */ nop 0 +ENTRY(__bpon) + .globl __bpon + BPON + BR_R1USE_R14 + /* * Scheduler resume function, called by switch_to * gpr2 = (task_struct *) prev @@ -193,9 +323,9 @@ ENTRY(__switch_to) mvc __LC_CURRENT_PID(4,%r0),__TASK_pid(%r3) # store pid of next lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_LPP - bzr %r14 + jz 0f .insn s,0xb2800000,__LC_LPP # set program parameter - br %r14 +0: BR_R1USE_R14 .L__critical_start: @@ -207,9 +337,11 @@ ENTRY(__switch_to) */ ENTRY(sie64a) stmg %r6,%r14,__SF_GPRS(%r15) # save kernel registers + lg %r12,__LC_CURRENT stg %r2,__SF_EMPTY(%r15) # save control block pointer stg %r3,__SF_EMPTY+8(%r15) # save guest register save area xc __SF_EMPTY+16(8,%r15),__SF_EMPTY+16(%r15) # reason code = 0 + mvc __SF_EMPTY+24(8,%r15),__TI_flags(%r12) # copy thread flags TSTMSK __LC_CPU_FLAGS,_CIF_FPU # load guest fp/vx registers ? jno .Lsie_load_guest_gprs brasl %r14,load_fpu_regs # load guest fp/vx regs @@ -226,8 +358,12 @@ ENTRY(sie64a) jnz .Lsie_skip TSTMSK __LC_CPU_FLAGS,_CIF_FPU jo .Lsie_skip # exit if fp/vx regs changed + BPEXIT __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST) .Lsie_entry: sie 0(%r14) +.Lsie_exit: + BPOFF + BPENTER __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST) .Lsie_skip: ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE lctlg %c1,%c1,__LC_USER_ASCE # load primary asce @@ -248,9 +384,15 @@ ENTRY(sie64a) sie_exit: lg %r14,__SF_EMPTY+8(%r15) # load guest register save area stmg %r0,%r13,0(%r14) # save guest gprs 0-13 + xgr %r0,%r0 # clear guest registers to + xgr %r1,%r1 # prevent speculative use + xgr %r2,%r2 + xgr %r3,%r3 + xgr %r4,%r4 + xgr %r5,%r5 lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers lg %r2,__SF_EMPTY+16(%r15) # return exit reason code - br %r14 + BR_R1USE_R14 .Lsie_fault: lghi %r14,-EFAULT stg %r14,__SF_EMPTY+16(%r15) # set exit reason code @@ -273,6 +415,7 @@ ENTRY(system_call) stpt __LC_SYNC_ENTER_TIMER .Lsysc_stmg: stmg %r8,%r15,__LC_SAVE_AREA_SYNC + BPOFF lg %r12,__LC_CURRENT lghi %r13,__TASK_thread lghi %r14,_PIF_SYSCALL @@ -281,12 +424,15 @@ ENTRY(system_call) la %r11,STACK_FRAME_OVERHEAD(%r15) # pointer to pt_regs .Lsysc_vtime: UPDATE_VTIME %r8,%r9,__LC_SYNC_ENTER_TIMER + BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP stmg %r0,%r7,__PT_R0(%r11) mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC mvc __PT_PSW(16,%r11),__LC_SVC_OLD_PSW mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC stg %r14,__PT_FLAGS(%r11) .Lsysc_do_svc: + # clear user controlled register to prevent speculative use + xgr %r0,%r0 # load address of system call table lg %r10,__THREAD_sysc_table(%r13,%r12) llgh %r8,__PT_INT_CODE+2(%r11) @@ -305,7 +451,7 @@ ENTRY(system_call) lgf %r9,0(%r8,%r10) # get system call add. TSTMSK __TI_flags(%r12),_TIF_TRACE jnz .Lsysc_tracesys - basr %r14,%r9 # call sys_xxxx + BASR_R14_R9 # call sys_xxxx stg %r2,__PT_R2(%r11) # store return value .Lsysc_return: @@ -317,6 +463,7 @@ ENTRY(system_call) jnz .Lsysc_work # check for work TSTMSK __LC_CPU_FLAGS,_CIF_WORK jnz .Lsysc_work + BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP .Lsysc_restore: lg %r14,__LC_VDSO_PER_CPU lmg %r0,%r10,__PT_R0(%r11) @@ -481,7 +628,7 @@ ENTRY(system_call) lmg %r3,%r7,__PT_R3(%r11) stg %r7,STACK_FRAME_OVERHEAD(%r15) lg %r2,__PT_ORIG_GPR2(%r11) - basr %r14,%r9 # call sys_xxx + BASR_R14_R9 # call sys_xxx stg %r2,__PT_R2(%r11) # store return value .Lsysc_tracenogo: TSTMSK __TI_flags(%r12),_TIF_TRACE @@ -505,7 +652,7 @@ ENTRY(ret_from_fork) lmg %r9,%r10,__PT_R9(%r11) # load gprs ENTRY(kernel_thread_starter) la %r2,0(%r10) - basr %r14,%r9 + BASR_R14_R9 j .Lsysc_tracenogo /* @@ -514,6 +661,7 @@ ENTRY(kernel_thread_starter) ENTRY(pgm_check_handler) stpt __LC_SYNC_ENTER_TIMER + BPOFF stmg %r8,%r15,__LC_SAVE_AREA_SYNC lg %r10,__LC_LAST_BREAK lg %r12,__LC_CURRENT @@ -540,6 +688,7 @@ ENTRY(pgm_check_handler) aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE) j 4f 2: UPDATE_VTIME %r14,%r15,__LC_SYNC_ENTER_TIMER + BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP lg %r15,__LC_KERNEL_STACK lgr %r14,%r12 aghi %r14,__TASK_thread # pointer to thread_struct @@ -550,6 +699,15 @@ ENTRY(pgm_check_handler) 3: stg %r10,__THREAD_last_break(%r14) 4: la %r11,STACK_FRAME_OVERHEAD(%r15) stmg %r0,%r7,__PT_R0(%r11) + # clear user controlled registers to prevent speculative use + xgr %r0,%r0 + xgr %r1,%r1 + xgr %r2,%r2 + xgr %r3,%r3 + xgr %r4,%r4 + xgr %r5,%r5 + xgr %r6,%r6 + xgr %r7,%r7 mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC stmg %r8,%r9,__PT_PSW(%r11) mvc __PT_INT_CODE(4,%r11),__LC_PGM_ILC @@ -571,9 +729,9 @@ ENTRY(pgm_check_handler) nill %r10,0x007f sll %r10,2 je .Lpgm_return - lgf %r1,0(%r10,%r1) # load address of handler routine + lgf %r9,0(%r10,%r1) # load address of handler routine lgr %r2,%r11 # pass pointer to pt_regs - basr %r14,%r1 # branch to interrupt-handler + BASR_R14_R9 # branch to interrupt-handler .Lpgm_return: LOCKDEP_SYS_EXIT tm __PT_PSW+1(%r11),0x01 # returning to user ? @@ -609,12 +767,23 @@ ENTRY(pgm_check_handler) ENTRY(io_int_handler) STCK __LC_INT_CLOCK stpt __LC_ASYNC_ENTER_TIMER + BPOFF stmg %r8,%r15,__LC_SAVE_AREA_ASYNC lg %r12,__LC_CURRENT larl %r13,cleanup_critical lmg %r8,%r9,__LC_IO_OLD_PSW SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_ENTER_TIMER stmg %r0,%r7,__PT_R0(%r11) + # clear user controlled registers to prevent speculative use + xgr %r0,%r0 + xgr %r1,%r1 + xgr %r2,%r2 + xgr %r3,%r3 + xgr %r4,%r4 + xgr %r5,%r5 + xgr %r6,%r6 + xgr %r7,%r7 + xgr %r10,%r10 mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC stmg %r8,%r9,__PT_PSW(%r11) mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID @@ -649,9 +818,13 @@ ENTRY(io_int_handler) lg %r14,__LC_VDSO_PER_CPU lmg %r0,%r10,__PT_R0(%r11) mvc __LC_RETURN_PSW(16),__PT_PSW(%r11) + tm __PT_PSW+1(%r11),0x01 # returning to user ? + jno .Lio_exit_kernel + BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP .Lio_exit_timer: stpt __LC_EXIT_TIMER mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER +.Lio_exit_kernel: lmg %r11,%r15,__PT_R11(%r11) lpswe __LC_RETURN_PSW .Lio_done: @@ -814,12 +987,23 @@ ENTRY(io_int_handler) ENTRY(ext_int_handler) STCK __LC_INT_CLOCK stpt __LC_ASYNC_ENTER_TIMER + BPOFF stmg %r8,%r15,__LC_SAVE_AREA_ASYNC lg %r12,__LC_CURRENT larl %r13,cleanup_critical lmg %r8,%r9,__LC_EXT_OLD_PSW SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_ENTER_TIMER stmg %r0,%r7,__PT_R0(%r11) + # clear user controlled registers to prevent speculative use + xgr %r0,%r0 + xgr %r1,%r1 + xgr %r2,%r2 + xgr %r3,%r3 + xgr %r4,%r4 + xgr %r5,%r5 + xgr %r6,%r6 + xgr %r7,%r7 + xgr %r10,%r10 mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC stmg %r8,%r9,__PT_PSW(%r11) lghi %r1,__LC_EXT_PARAMS2 @@ -852,11 +1036,12 @@ ENTRY(psw_idle) .Lpsw_idle_stcctm: #endif oi __LC_CPU_FLAGS+7,_CIF_ENABLED_WAIT + BPON STCK __CLOCK_IDLE_ENTER(%r2) stpt __TIMER_IDLE_ENTER(%r2) .Lpsw_idle_lpsw: lpswe __SF_EMPTY(%r15) - br %r14 + BR_R1USE_R14 .Lpsw_idle_end: /* @@ -870,7 +1055,7 @@ ENTRY(save_fpu_regs) lg %r2,__LC_CURRENT aghi %r2,__TASK_thread TSTMSK __LC_CPU_FLAGS,_CIF_FPU - bor %r14 + jo .Lsave_fpu_regs_exit stfpc __THREAD_FPU_fpc(%r2) lg %r3,__THREAD_FPU_regs(%r2) TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX @@ -897,7 +1082,8 @@ ENTRY(save_fpu_regs) std 15,120(%r3) .Lsave_fpu_regs_done: oi __LC_CPU_FLAGS+7,_CIF_FPU - br %r14 +.Lsave_fpu_regs_exit: + BR_R1USE_R14 .Lsave_fpu_regs_end: EXPORT_SYMBOL(save_fpu_regs) @@ -915,7 +1101,7 @@ load_fpu_regs: lg %r4,__LC_CURRENT aghi %r4,__TASK_thread TSTMSK __LC_CPU_FLAGS,_CIF_FPU - bnor %r14 + jno .Lload_fpu_regs_exit lfpc __THREAD_FPU_fpc(%r4) TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX lg %r4,__THREAD_FPU_regs(%r4) # %r4 <- reg save area @@ -942,7 +1128,8 @@ load_fpu_regs: ld 15,120(%r4) .Lload_fpu_regs_done: ni __LC_CPU_FLAGS+7,255-_CIF_FPU - br %r14 +.Lload_fpu_regs_exit: + BR_R1USE_R14 .Lload_fpu_regs_end: .L__critical_end: @@ -952,6 +1139,7 @@ load_fpu_regs: */ ENTRY(mcck_int_handler) STCK __LC_MCCK_CLOCK + BPOFF la %r1,4095 # revalidate r1 spt __LC_CPU_TIMER_SAVE_AREA-4095(%r1) # revalidate cpu timer lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# revalidate gprs @@ -982,6 +1170,16 @@ ENTRY(mcck_int_handler) .Lmcck_skip: lghi %r14,__LC_GPREGS_SAVE_AREA+64 stmg %r0,%r7,__PT_R0(%r11) + # clear user controlled registers to prevent speculative use + xgr %r0,%r0 + xgr %r1,%r1 + xgr %r2,%r2 + xgr %r3,%r3 + xgr %r4,%r4 + xgr %r5,%r5 + xgr %r6,%r6 + xgr %r7,%r7 + xgr %r10,%r10 mvc __PT_R8(64,%r11),0(%r14) stmg %r8,%r9,__PT_PSW(%r11) xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11) @@ -1007,6 +1205,7 @@ ENTRY(mcck_int_handler) mvc __LC_RETURN_MCCK_PSW(16),__PT_PSW(%r11) # move return PSW tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ? jno 0f + BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP stpt __LC_EXIT_TIMER mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER 0: lmg %r11,%r15,__PT_R11(%r11) @@ -1102,7 +1301,7 @@ cleanup_critical: jl 0f clg %r9,BASED(.Lcleanup_table+104) # .Lload_fpu_regs_end jl .Lcleanup_load_fpu_regs -0: br %r14 +0: BR_R11USE_R14 .align 8 .Lcleanup_table: @@ -1133,11 +1332,12 @@ cleanup_critical: clg %r9,BASED(.Lsie_crit_mcck_length) jh 1f oi __LC_CPU_FLAGS+7, _CIF_MCCK_GUEST -1: lg %r9,__SF_EMPTY(%r15) # get control block pointer +1: BPENTER __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST) + lg %r9,__SF_EMPTY(%r15) # get control block pointer ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE lctlg %c1,%c1,__LC_USER_ASCE # load primary asce larl %r9,sie_exit # skip forward to sie_exit - br %r14 + BR_R11USE_R14 #endif .Lcleanup_system_call: @@ -1175,6 +1375,7 @@ cleanup_critical: stg %r15,__LC_SYSTEM_TIMER 0: # update accounting time stamp mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER + BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP # set up saved register r11 lg %r15,__LC_KERNEL_STACK la %r9,STACK_FRAME_OVERHEAD(%r15) @@ -1190,7 +1391,7 @@ cleanup_critical: stg %r15,56(%r11) # r15 stack pointer # set new psw address and exit larl %r9,.Lsysc_do_svc - br %r14 + BR_R11USE_R14 .Lcleanup_system_call_insn: .quad system_call .quad .Lsysc_stmg @@ -1202,7 +1403,7 @@ cleanup_critical: .Lcleanup_sysc_tif: larl %r9,.Lsysc_tif - br %r14 + BR_R11USE_R14 .Lcleanup_sysc_restore: # check if stpt has been executed @@ -1219,14 +1420,14 @@ cleanup_critical: mvc 0(64,%r11),__PT_R8(%r9) lmg %r0,%r7,__PT_R0(%r9) 1: lmg %r8,%r9,__LC_RETURN_PSW - br %r14 + BR_R11USE_R14 .Lcleanup_sysc_restore_insn: .quad .Lsysc_exit_timer .quad .Lsysc_done - 4 .Lcleanup_io_tif: larl %r9,.Lio_tif - br %r14 + BR_R11USE_R14 .Lcleanup_io_restore: # check if stpt has been executed @@ -1240,7 +1441,7 @@ cleanup_critical: mvc 0(64,%r11),__PT_R8(%r9) lmg %r0,%r7,__PT_R0(%r9) 1: lmg %r8,%r9,__LC_RETURN_PSW - br %r14 + BR_R11USE_R14 .Lcleanup_io_restore_insn: .quad .Lio_exit_timer .quad .Lio_done - 4 @@ -1293,17 +1494,17 @@ cleanup_critical: # prepare return psw nihh %r8,0xfcfd # clear irq & wait state bits lg %r9,48(%r11) # return from psw_idle - br %r14 + BR_R11USE_R14 .Lcleanup_idle_insn: .quad .Lpsw_idle_lpsw .Lcleanup_save_fpu_regs: larl %r9,save_fpu_regs - br %r14 + BR_R11USE_R14 .Lcleanup_load_fpu_regs: larl %r9,load_fpu_regs - br %r14 + BR_R11USE_R14 /* * Integer constants @@ -1323,7 +1524,6 @@ cleanup_critical: .Lsie_crit_mcck_length: .quad .Lsie_skip - .Lsie_entry #endif - .section .rodata, "a" #define SYSCALL(esame,emu) .long esame .globl sys_call_table diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 8e622bb52f7a95fd59c2f89aec95f04c616633cf..b565e784bae82c7a18237aabdef79126a4c723e8 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -564,6 +564,7 @@ static struct kset *ipl_kset; static void __ipl_run(void *unused) { + __bpon(); diag308(DIAG308_LOAD_CLEAR, NULL); if (MACHINE_IS_VM) __cpcmd("IPL", NULL, 0, NULL); @@ -799,6 +800,7 @@ static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, /* copy and convert to ebcdic */ memcpy(ipb->hdr.loadparm, buf, lp_len); ASCEBC(ipb->hdr.loadparm, LOADPARM_LEN); + ipb->hdr.flags |= DIAG308_FLAGS_LP_VALID; return len; } diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 1a27f307a92079e79ead66c5e50e4f54e992530c..b441e069e674f53fc23948179a7bdc0f170902da 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #if 0 #define DEBUGP printk @@ -168,7 +171,11 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, me->arch.got_offset = me->core_layout.size; me->core_layout.size += me->arch.got_size; me->arch.plt_offset = me->core_layout.size; - me->core_layout.size += me->arch.plt_size; + if (me->arch.plt_size) { + if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) + me->arch.plt_size += PLT_ENTRY_SIZE; + me->core_layout.size += me->arch.plt_size; + } return 0; } @@ -322,9 +329,20 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, unsigned int *ip; ip = me->core_layout.base + me->arch.plt_offset + info->plt_offset; - ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */ - ip[1] = 0x100a0004; - ip[2] = 0x07f10000; + ip[0] = 0x0d10e310; /* basr 1,0 */ + ip[1] = 0x100a0004; /* lg 1,10(1) */ + if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) { + unsigned int *ij; + ij = me->core_layout.base + + me->arch.plt_offset + + me->arch.plt_size - PLT_ENTRY_SIZE; + ip[2] = 0xa7f40000 + /* j __jump_r1 */ + (unsigned int)(u16) + (((unsigned long) ij - 8 - + (unsigned long) ip) / 2); + } else { + ip[2] = 0x07f10000; /* br %r1 */ + } ip[3] = (unsigned int) (val >> 32); ip[4] = (unsigned int) val; info->plt_initialized = 1; @@ -429,6 +447,45 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *me) { + const Elf_Shdr *s; + char *secstrings, *secname; + void *aseg; + + if (IS_ENABLED(CONFIG_EXPOLINE) && + !nospec_disable && me->arch.plt_size) { + unsigned int *ij; + + ij = me->core_layout.base + me->arch.plt_offset + + me->arch.plt_size - PLT_ENTRY_SIZE; + if (test_facility(35)) { + ij[0] = 0xc6000000; /* exrl %r0,.+10 */ + ij[1] = 0x0005a7f4; /* j . */ + ij[2] = 0x000007f1; /* br %r1 */ + } else { + ij[0] = 0x44000000 | (unsigned int) + offsetof(struct lowcore, br_r1_trampoline); + ij[1] = 0xa7f40000; /* j . */ + } + } + + secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { + aseg = (void *) s->sh_addr; + secname = secstrings + s->sh_name; + + if (!strcmp(".altinstructions", secname)) + /* patch .altinstructions */ + apply_alternatives(aseg, aseg + s->sh_size); + + if (IS_ENABLED(CONFIG_EXPOLINE) && + (!strncmp(".s390_indirect", secname, 14))) + nospec_revert(aseg, aseg + s->sh_size); + + if (IS_ENABLED(CONFIG_EXPOLINE) && + (!strncmp(".s390_return", secname, 12))) + nospec_revert(aseg, aseg + s->sh_size); + } + jump_label_apply_nops(me); return 0; } diff --git a/arch/s390/kernel/nospec-branch.c b/arch/s390/kernel/nospec-branch.c new file mode 100644 index 0000000000000000000000000000000000000000..9f3b5b3827435f62f1279872033a508355c6292c --- /dev/null +++ b/arch/s390/kernel/nospec-branch.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +static int __init nobp_setup_early(char *str) +{ + bool enabled; + int rc; + + rc = kstrtobool(str, &enabled); + if (rc) + return rc; + if (enabled && test_facility(82)) { + /* + * The user explicitely requested nobp=1, enable it and + * disable the expoline support. + */ + __set_facility(82, S390_lowcore.alt_stfle_fac_list); + if (IS_ENABLED(CONFIG_EXPOLINE)) + nospec_disable = 1; + } else { + __clear_facility(82, S390_lowcore.alt_stfle_fac_list); + } + return 0; +} +early_param("nobp", nobp_setup_early); + +static int __init nospec_setup_early(char *str) +{ + __clear_facility(82, S390_lowcore.alt_stfle_fac_list); + return 0; +} +early_param("nospec", nospec_setup_early); + +static int __init nospec_report(void) +{ + if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) + pr_info("Spectre V2 mitigation: execute trampolines.\n"); + if (__test_facility(82, S390_lowcore.alt_stfle_fac_list)) + pr_info("Spectre V2 mitigation: limited branch prediction.\n"); + return 0; +} +arch_initcall(nospec_report); + +#ifdef CONFIG_SYSFS +ssize_t cpu_show_spectre_v1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "Mitigation: __user pointer sanitization\n"); +} + +ssize_t cpu_show_spectre_v2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) + return sprintf(buf, "Mitigation: execute trampolines\n"); + if (__test_facility(82, S390_lowcore.alt_stfle_fac_list)) + return sprintf(buf, "Mitigation: limited branch prediction.\n"); + return sprintf(buf, "Vulnerable\n"); +} +#endif + +#ifdef CONFIG_EXPOLINE + +int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF); + +static int __init nospectre_v2_setup_early(char *str) +{ + nospec_disable = 1; + return 0; +} +early_param("nospectre_v2", nospectre_v2_setup_early); + +void __init nospec_auto_detect(void) +{ + if (IS_ENABLED(CC_USING_EXPOLINE)) { + /* + * The kernel has been compiled with expolines. + * Keep expolines enabled and disable nobp. + */ + nospec_disable = 0; + __clear_facility(82, S390_lowcore.alt_stfle_fac_list); + } + /* + * If the kernel has not been compiled with expolines the + * nobp setting decides what is done, this depends on the + * CONFIG_KERNEL_NP option and the nobp/nospec parameters. + */ +} + +static int __init spectre_v2_setup_early(char *str) +{ + if (str && !strncmp(str, "on", 2)) { + nospec_disable = 0; + __clear_facility(82, S390_lowcore.alt_stfle_fac_list); + } + if (str && !strncmp(str, "off", 3)) + nospec_disable = 1; + if (str && !strncmp(str, "auto", 4)) + nospec_auto_detect(); + return 0; +} +early_param("spectre_v2", spectre_v2_setup_early); + +static void __init_or_module __nospec_revert(s32 *start, s32 *end) +{ + enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type; + u8 *instr, *thunk, *br; + u8 insnbuf[6]; + s32 *epo; + + /* Second part of the instruction replace is always a nop */ + memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x00, 0x00 }, 4); + for (epo = start; epo < end; epo++) { + instr = (u8 *) epo + *epo; + if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04) + type = BRCL_EXPOLINE; /* brcl instruction */ + else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05) + type = BRASL_EXPOLINE; /* brasl instruction */ + else + continue; + thunk = instr + (*(int *)(instr + 2)) * 2; + if (thunk[0] == 0xc6 && thunk[1] == 0x00) + /* exrl %r0, */ + br = thunk + (*(int *)(thunk + 2)) * 2; + else if (thunk[0] == 0xc0 && (thunk[1] & 0x0f) == 0x00 && + thunk[6] == 0x44 && thunk[7] == 0x00 && + (thunk[8] & 0x0f) == 0x00 && thunk[9] == 0x00 && + (thunk[1] & 0xf0) == (thunk[8] & 0xf0)) + /* larl %rx, + ex %r0,0(%rx) */ + br = thunk + (*(int *)(thunk + 2)) * 2; + else + continue; + if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0) + continue; + switch (type) { + case BRCL_EXPOLINE: + /* brcl to thunk, replace with br + nop */ + insnbuf[0] = br[0]; + insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); + break; + case BRASL_EXPOLINE: + /* brasl to thunk, replace with basr + nop */ + insnbuf[0] = 0x0d; + insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); + break; + } + + s390_kernel_write(instr, insnbuf, 6); + } +} + +void __init_or_module nospec_revert(s32 *start, s32 *end) +{ + if (nospec_disable) + __nospec_revert(start, end); +} + +extern s32 __nospec_call_start[], __nospec_call_end[]; +extern s32 __nospec_return_start[], __nospec_return_end[]; +void __init nospec_init_branches(void) +{ + nospec_revert(__nospec_call_start, __nospec_call_end); + nospec_revert(__nospec_return_start, __nospec_return_end); +} + +#endif /* CONFIG_EXPOLINE */ diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c index 5362fd868d0d42e73eaf73c8abb63e7dc94d4580..6fe2e1875058bec89419d680bef0110249ba9ed3 100644 --- a/arch/s390/kernel/processor.c +++ b/arch/s390/kernel/processor.c @@ -197,3 +197,21 @@ const struct seq_operations cpuinfo_op = { .stop = c_stop, .show = show_cpuinfo, }; + +int s390_isolate_bp(void) +{ + if (!test_facility(82)) + return -EOPNOTSUPP; + set_thread_flag(TIF_ISOLATE_BP); + return 0; +} +EXPORT_SYMBOL(s390_isolate_bp); + +int s390_isolate_bp_guest(void) +{ + if (!test_facility(82)) + return -EOPNOTSUPP; + set_thread_flag(TIF_ISOLATE_BP_GUEST); + return 0; +} +EXPORT_SYMBOL(s390_isolate_bp_guest); diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 164a1e16b53edfb2d60da9e1a459630023bc392a..98c1f79411426a18384c2e6bcb645af91649842d 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -66,6 +66,8 @@ #include #include #include +#include +#include #include "entry.h" /* @@ -338,7 +340,9 @@ static void __init setup_lowcore(void) lc->preempt_count = S390_lowcore.preempt_count; lc->stfl_fac_list = S390_lowcore.stfl_fac_list; memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list, - MAX_FACILITY_BIT/8); + sizeof(lc->stfle_fac_list)); + memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list, + sizeof(lc->alt_stfle_fac_list)); if (MACHINE_HAS_VX || MACHINE_HAS_GS) { unsigned long bits, size; @@ -381,6 +385,7 @@ static void __init setup_lowcore(void) #ifdef CONFIG_SMP lc->spinlock_lockval = arch_spin_lockval(0); #endif + lc->br_r1_trampoline = 0x07f1; /* br %r1 */ set_prefix((u32)(unsigned long) lc); lowcore_ptr[0] = lc; @@ -892,6 +897,9 @@ void __init setup_arch(char **cmdline_p) init_mm.end_data = (unsigned long) &_edata; init_mm.brk = (unsigned long) &_end; + if (IS_ENABLED(CONFIG_EXPOLINE_AUTO)) + nospec_auto_detect(); + parse_early_param(); #ifdef CONFIG_CRASH_DUMP /* Deactivate elfcorehdr= kernel parameter */ @@ -955,6 +963,10 @@ void __init setup_arch(char **cmdline_p) conmode_default(); set_preferred_console(); + apply_alternative_instructions(); + if (IS_ENABLED(CONFIG_EXPOLINE)) + nospec_init_branches(); + /* Setup zfcpdump support */ setup_zfcpdump(); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 7ffaf9fd6d19796762a57504e44464fb0a24950b..ae5df41778037fd4ec5df4c422295dfaaa3da68e 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -228,6 +228,7 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) lc->mcesad = mcesa_origin | mcesa_bits; lc->cpu_nr = cpu; lc->spinlock_lockval = arch_spin_lockval(cpu); + lc->br_r1_trampoline = 0x07f1; /* br %r1 */ if (vdso_alloc_per_cpu(lc)) goto out; lowcore_ptr[cpu] = lc; @@ -282,7 +283,9 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu) __ctl_store(lc->cregs_save_area, 0, 15); save_access_regs((unsigned int *) lc->access_regs_save_area); memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list, - MAX_FACILITY_BIT/8); + sizeof(lc->stfle_fac_list)); + memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list, + sizeof(lc->alt_stfle_fac_list)); } static void pcpu_attach_task(struct pcpu *pcpu, struct task_struct *tsk) @@ -332,6 +335,7 @@ static void pcpu_delegate(struct pcpu *pcpu, void (*func)(void *), mem_assign_absolute(lc->restart_fn, (unsigned long) func); mem_assign_absolute(lc->restart_data, (unsigned long) data); mem_assign_absolute(lc->restart_source, source_cpu); + __bpon(); asm volatile( "0: sigp 0,%0,%2 # sigp restart to target cpu\n" " brc 2,0b # busy, try again\n" @@ -907,6 +911,7 @@ void __cpu_die(unsigned int cpu) void __noreturn cpu_die(void) { idle_task_exit(); + __bpon(); pcpu_sigp_retry(pcpu_devices + smp_processor_id(), SIGP_STOP, 0); for (;;) ; } diff --git a/arch/s390/kernel/uprobes.c b/arch/s390/kernel/uprobes.c index d9d1f512f019415ed36912e7afd81156fbd284ae..5007fac01bb5e215392362768898cc829cc121d0 100644 --- a/arch/s390/kernel/uprobes.c +++ b/arch/s390/kernel/uprobes.c @@ -150,6 +150,15 @@ unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, return orig; } +bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx, + struct pt_regs *regs) +{ + if (ctx == RP_CHECK_CHAIN_CALL) + return user_stack_pointer(regs) <= ret->stack; + else + return user_stack_pointer(regs) < ret->stack; +} + /* Instruction Emulation */ static void adjust_psw_addr(psw_t *psw, unsigned long len) diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 96a713a470e77a67a56c0b18f3db29b02ccf2471..85dd3c7bdd86bb7586d216c2691f854c94684e30 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -105,6 +105,43 @@ SECTIONS EXIT_DATA } + /* + * struct alt_inst entries. From the header (alternative.h): + * "Alternative instructions for different CPU types or capabilities" + * Think locking instructions on spinlocks. + * Note, that it is a part of __init region. + */ + . = ALIGN(8); + .altinstructions : { + __alt_instructions = .; + *(.altinstructions) + __alt_instructions_end = .; + } + + /* + * And here are the replacement instructions. The linker sticks + * them as binary blobs. The .altinstructions has enough data to + * get the address and the length of them to patch the kernel safely. + * Note, that it is a part of __init region. + */ + .altinstr_replacement : { + *(.altinstr_replacement) + } + + /* + * Table with the patch locations to undo expolines + */ + .nospec_call_table : { + __nospec_call_start = . ; + *(.s390_indirect*) + __nospec_call_end = . ; + } + .nospec_return_table : { + __nospec_return_start = . ; + *(.s390_return*) + __nospec_return_end = . ; + } + /* early.c uses stsi, which requires page aligned data. */ . = ALIGN(PAGE_SIZE); INIT_DATA_SECTION(0x100) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 0fa3a788dd209e6ed2aea2fc25792124953b5719..4f6adbea592b373902a6a30f43f7e4fedbbbe95e 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -449,6 +449,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_S390_GS: r = test_facility(133); break; + case KVM_CAP_S390_BPB: + r = test_facility(82); + break; default: r = 0; } @@ -601,7 +604,7 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) case KVM_CAP_S390_GS: r = -EINVAL; mutex_lock(&kvm->lock); - if (atomic_read(&kvm->online_vcpus)) { + if (kvm->created_vcpus) { r = -EBUSY; } else if (test_facility(133)) { set_kvm_facility(kvm->arch.model.fac_mask, 133); @@ -1121,7 +1124,7 @@ static int kvm_s390_set_processor_feat(struct kvm *kvm, return -EINVAL; mutex_lock(&kvm->lock); - if (!atomic_read(&kvm->online_vcpus)) { + if (!kvm->created_vcpus) { bitmap_copy(kvm->arch.cpu_feat, (unsigned long *) data.feat, KVM_S390_VM_CPU_FEAT_NR_BITS); ret = 0; @@ -2231,6 +2234,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) kvm_s390_set_prefix(vcpu, 0); if (test_kvm_facility(vcpu->kvm, 64)) vcpu->run->kvm_valid_regs |= KVM_SYNC_RICCB; + if (test_kvm_facility(vcpu->kvm, 82)) + vcpu->run->kvm_valid_regs |= KVM_SYNC_BPBC; if (test_kvm_facility(vcpu->kvm, 133)) vcpu->run->kvm_valid_regs |= KVM_SYNC_GSCB; /* fprs can be synchronized via vrs, even if the guest has no vx. With @@ -2372,6 +2377,7 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu) current->thread.fpu.fpc = 0; vcpu->arch.sie_block->gbea = 1; vcpu->arch.sie_block->pp = 0; + vcpu->arch.sie_block->fpf &= ~FPF_BPBC; vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; kvm_clear_async_pf_completion_queue(vcpu); if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) @@ -3318,6 +3324,11 @@ static void sync_regs(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) vcpu->arch.sie_block->ecd |= ECD_HOSTREGMGMT; vcpu->arch.gs_enabled = 1; } + if ((kvm_run->kvm_dirty_regs & KVM_SYNC_BPBC) && + test_kvm_facility(vcpu->kvm, 82)) { + vcpu->arch.sie_block->fpf &= ~FPF_BPBC; + vcpu->arch.sie_block->fpf |= kvm_run->s.regs.bpbc ? FPF_BPBC : 0; + } save_access_regs(vcpu->arch.host_acrs); restore_access_regs(vcpu->run->s.regs.acrs); /* save host (userspace) fprs/vrs */ @@ -3364,6 +3375,7 @@ static void store_regs(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) kvm_run->s.regs.pft = vcpu->arch.pfault_token; kvm_run->s.regs.pfs = vcpu->arch.pfault_select; kvm_run->s.regs.pfc = vcpu->arch.pfault_compare; + kvm_run->s.regs.bpbc = (vcpu->arch.sie_block->fpf & FPF_BPBC) == FPF_BPBC; save_access_regs(vcpu->run->s.regs.acrs); restore_access_regs(vcpu->arch.host_acrs); /* Save guest register state */ diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index b18b5652e5c59b84f64f24a89fb2a14033e65e36..eb7b530d1783f7c8da48f0ff328e15a808a71b2f 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -31,7 +31,11 @@ struct vsie_page { * the same offset as that in struct sie_page! */ struct mcck_volatile_info mcck_info; /* 0x0200 */ - /* the pinned originial scb */ + /* + * The pinned original scb. Be aware that other VCPUs can modify + * it while we read from it. Values that are used for conditions or + * are reused conditionally, should be accessed via READ_ONCE. + */ struct kvm_s390_sie_block *scb_o; /* 0x0218 */ /* the shadow gmap in use by the vsie_page */ struct gmap *gmap; /* 0x0220 */ @@ -143,12 +147,13 @@ static int shadow_crycb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) { struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; - u32 crycb_addr = scb_o->crycbd & 0x7ffffff8U; + const uint32_t crycbd_o = READ_ONCE(scb_o->crycbd); + const u32 crycb_addr = crycbd_o & 0x7ffffff8U; unsigned long *b1, *b2; u8 ecb3_flags; scb_s->crycbd = 0; - if (!(scb_o->crycbd & vcpu->arch.sie_block->crycbd & CRYCB_FORMAT1)) + if (!(crycbd_o & vcpu->arch.sie_block->crycbd & CRYCB_FORMAT1)) return 0; /* format-1 is supported with message-security-assist extension 3 */ if (!test_kvm_facility(vcpu->kvm, 76)) @@ -186,12 +191,15 @@ static void prepare_ibc(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) { struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; + /* READ_ONCE does not work on bitfields - use a temporary variable */ + const uint32_t __new_ibc = scb_o->ibc; + const uint32_t new_ibc = READ_ONCE(__new_ibc) & 0x0fffU; __u64 min_ibc = (sclp.ibc >> 16) & 0x0fffU; scb_s->ibc = 0; /* ibc installed in g2 and requested for g3 */ - if (vcpu->kvm->arch.model.ibc && (scb_o->ibc & 0x0fffU)) { - scb_s->ibc = scb_o->ibc & 0x0fffU; + if (vcpu->kvm->arch.model.ibc && new_ibc) { + scb_s->ibc = new_ibc; /* takte care of the minimum ibc level of the machine */ if (scb_s->ibc < min_ibc) scb_s->ibc = min_ibc; @@ -226,6 +234,12 @@ static void unshadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) memcpy(scb_o->gcr, scb_s->gcr, 128); scb_o->pp = scb_s->pp; + /* branch prediction */ + if (test_kvm_facility(vcpu->kvm, 82)) { + scb_o->fpf &= ~FPF_BPBC; + scb_o->fpf |= scb_s->fpf & FPF_BPBC; + } + /* interrupt intercept */ switch (scb_s->icptcode) { case ICPT_PROGI: @@ -256,6 +270,10 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) { struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; + /* READ_ONCE does not work on bitfields - use a temporary variable */ + const uint32_t __new_prefix = scb_o->prefix; + const uint32_t new_prefix = READ_ONCE(__new_prefix); + const bool wants_tx = READ_ONCE(scb_o->ecb) & ECB_TE; bool had_tx = scb_s->ecb & ECB_TE; unsigned long new_mso = 0; int rc; @@ -268,6 +286,7 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) scb_s->ecb3 = 0; scb_s->ecd = 0; scb_s->fac = 0; + scb_s->fpf = 0; rc = prepare_cpuflags(vcpu, vsie_page); if (rc) @@ -302,14 +321,14 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) scb_s->icpua = scb_o->icpua; if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_SM)) - new_mso = scb_o->mso & 0xfffffffffff00000UL; + new_mso = READ_ONCE(scb_o->mso) & 0xfffffffffff00000UL; /* if the hva of the prefix changes, we have to remap the prefix */ - if (scb_s->mso != new_mso || scb_s->prefix != scb_o->prefix) + if (scb_s->mso != new_mso || scb_s->prefix != new_prefix) prefix_unmapped(vsie_page); /* SIE will do mso/msl validity and exception checks for us */ scb_s->msl = scb_o->msl & 0xfffffffffff00000UL; scb_s->mso = new_mso; - scb_s->prefix = scb_o->prefix; + scb_s->prefix = new_prefix; /* We have to definetly flush the tlb if this scb never ran */ if (scb_s->ihcpu != 0xffffU) @@ -321,12 +340,15 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_ESOP)) scb_s->ecb |= scb_o->ecb & ECB_HOSTPROTINT; /* transactional execution */ - if (test_kvm_facility(vcpu->kvm, 73)) { + if (test_kvm_facility(vcpu->kvm, 73) && wants_tx) { /* remap the prefix is tx is toggled on */ - if ((scb_o->ecb & ECB_TE) && !had_tx) + if (!had_tx) prefix_unmapped(vsie_page); - scb_s->ecb |= scb_o->ecb & ECB_TE; + scb_s->ecb |= ECB_TE; } + /* branch prediction */ + if (test_kvm_facility(vcpu->kvm, 82)) + scb_s->fpf |= scb_o->fpf & FPF_BPBC; /* SIMD */ if (test_kvm_facility(vcpu->kvm, 129)) { scb_s->eca |= scb_o->eca & ECA_VX; @@ -544,9 +566,9 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) gpa_t gpa; int rc = 0; - gpa = scb_o->scaol & ~0xfUL; + gpa = READ_ONCE(scb_o->scaol) & ~0xfUL; if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_64BSCAO)) - gpa |= (u64) scb_o->scaoh << 32; + gpa |= (u64) READ_ONCE(scb_o->scaoh) << 32; if (gpa) { if (!(gpa & ~0x1fffUL)) rc = set_validity_icpt(scb_s, 0x0038U); @@ -566,7 +588,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) scb_s->scaol = (u32)(u64)hpa; } - gpa = scb_o->itdba & ~0xffUL; + gpa = READ_ONCE(scb_o->itdba) & ~0xffUL; if (gpa && (scb_s->ecb & ECB_TE)) { if (!(gpa & ~0x1fffU)) { rc = set_validity_icpt(scb_s, 0x0080U); @@ -581,7 +603,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) scb_s->itdba = hpa; } - gpa = scb_o->gvrd & ~0x1ffUL; + gpa = READ_ONCE(scb_o->gvrd) & ~0x1ffUL; if (gpa && (scb_s->eca & ECA_VX) && !(scb_s->ecd & ECD_HOSTREGMGMT)) { if (!(gpa & ~0x1fffUL)) { rc = set_validity_icpt(scb_s, 0x1310U); @@ -599,7 +621,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) scb_s->gvrd = hpa; } - gpa = scb_o->riccbd & ~0x3fUL; + gpa = READ_ONCE(scb_o->riccbd) & ~0x3fUL; if (gpa && (scb_s->ecb3 & ECB3_RI)) { if (!(gpa & ~0x1fffUL)) { rc = set_validity_icpt(scb_s, 0x0043U); @@ -617,8 +639,8 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) if ((scb_s->ecb & ECB_GS) && !(scb_s->ecd & ECD_HOSTREGMGMT)) { unsigned long sdnxc; - gpa = scb_o->sdnxo & ~0xfUL; - sdnxc = scb_o->sdnxo & 0xfUL; + gpa = READ_ONCE(scb_o->sdnxo) & ~0xfUL; + sdnxc = READ_ONCE(scb_o->sdnxo) & 0xfUL; if (!gpa || !(gpa & ~0x1fffUL)) { rc = set_validity_icpt(scb_s, 0x10b0U); goto unpin; @@ -785,7 +807,7 @@ static void retry_vsie_icpt(struct vsie_page *vsie_page) static int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) { struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; - __u32 fac = vsie_page->scb_o->fac & 0x7ffffff8U; + __u32 fac = READ_ONCE(vsie_page->scb_o->fac) & 0x7ffffff8U; if (fac && test_kvm_facility(vcpu->kvm, 7)) { retry_vsie_icpt(vsie_page); @@ -809,6 +831,7 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) { struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; + int guest_bp_isolation; int rc; handle_last_fault(vcpu, vsie_page); @@ -819,6 +842,20 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) s390_handle_mcck(); srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); + + /* save current guest state of bp isolation override */ + guest_bp_isolation = test_thread_flag(TIF_ISOLATE_BP_GUEST); + + /* + * The guest is running with BPBC, so we have to force it on for our + * nested guest. This is done by enabling BPBC globally, so the BPBC + * control in the SCB (which the nested guest can modify) is simply + * ignored. + */ + if (test_kvm_facility(vcpu->kvm, 82) && + vcpu->arch.sie_block->fpf & FPF_BPBC) + set_thread_flag(TIF_ISOLATE_BP_GUEST); + local_irq_disable(); guest_enter_irqoff(); local_irq_enable(); @@ -828,6 +865,11 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) local_irq_disable(); guest_exit_irqoff(); local_irq_enable(); + + /* restore guest state for bp isolation override */ + if (!guest_bp_isolation) + clear_thread_flag(TIF_ISOLATE_BP_GUEST); + vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); if (rc == -EINTR) { diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h index fd9d9bac7cfa7b3ac96b1fa54dfba71f96e60916..79c3bdaaa0b49f54b4df82e6db32cf6b7c147dba 100644 --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h @@ -980,7 +980,7 @@ void update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmd); #define __HAVE_ARCH_PMDP_INVALIDATE -extern void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, +extern pmd_t pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp); #define __HAVE_ARCH_PGTABLE_DEPOSIT diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c index 4ae86bc0d35c720ea01e9ea0df3f3af4845e5bff..847ddffbf38ad797afbdef3777cdfea552f282d3 100644 --- a/arch/sparc/mm/tlb.c +++ b/arch/sparc/mm/tlb.c @@ -219,17 +219,28 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr, } } +static inline pmd_t pmdp_establish(struct vm_area_struct *vma, + unsigned long address, pmd_t *pmdp, pmd_t pmd) +{ + pmd_t old; + + do { + old = *pmdp; + } while (cmpxchg64(&pmdp->pmd, old.pmd, pmd.pmd) != old.pmd); + + return old; +} + /* * This routine is only called when splitting a THP */ -void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, +pmd_t pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp) { - pmd_t entry = *pmdp; - - pmd_val(entry) &= ~_PAGE_VALID; + pmd_t old, entry; - set_pmd_at(vma->vm_mm, address, pmdp, entry); + entry = __pmd(pmd_val(*pmdp) & ~_PAGE_VALID); + old = pmdp_establish(vma, address, pmdp, entry); flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE); /* @@ -240,6 +251,8 @@ void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, if ((pmd_val(entry) & _PAGE_PMD_HUGE) && !is_huge_zero_page(pmd_page(entry))) (vma->vm_mm)->context.thp_pte_count--; + + return old; } void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c index 2db18cbbb0eaba83aba45bd809ba4ed0834e80b1..c0197097c86e5075c146ad17142895e70b9fe25c 100644 --- a/arch/um/os-Linux/file.c +++ b/arch/um/os-Linux/file.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index a86d7cc2c2d82fa7d0e5c13d1f3188efcd660fba..bf0acb8aad8b20e31a4591ec5f1889d38d68d010 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -16,6 +16,7 @@ #include #include #include +#include void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = { [SIGTRAP] = relay_signal, @@ -159,7 +160,7 @@ static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = { static void hard_handler(int sig, siginfo_t *si, void *p) { - struct ucontext *uc = p; + ucontext_t *uc = p; mcontext_t *mc = &uc->uc_mcontext; unsigned long pending = 1UL << sig; diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..1b9020912aae5f1f874bf2242e537eb75a9da7a4 --- /dev/null +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -0,0 +1,444 @@ +CONFIG_POSIX_MQUEUE=y +# CONFIG_FHANDLE is not set +# CONFIG_USELIB is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_LZ4 is not set +CONFIG_KALLSYMS_ALL=y +# CONFIG_PCSPKR_PLATFORM is not set +CONFIG_BPF_SYSCALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_SMP=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y +CONFIG_PARAVIRT_SPINLOCKS=y +CONFIG_MCORE2=y +CONFIG_PROCESSOR_SELECT=y +# CONFIG_CPU_SUP_CENTAUR is not set +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +# CONFIG_MICROCODE is not set +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 +CONFIG_TRANSPARENT_HUGEPAGE=y +# CONFIG_MTRR is not set +CONFIG_HZ_100=y +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +CONFIG_PHYSICAL_START=0x200000 +CONFIG_PHYSICAL_ALIGN=0x1000000 +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 reboot=p" +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_ACPI_PROCFS_POWER=y +# CONFIG_ACPI_FAN is not set +# CONFIG_ACPI_THERMAL is not set +# CONFIG_X86_PM_TIMER is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_X86_ACPI_CPUFREQ=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_MSI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_IA32_EMULATION=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_HTCP is not set +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_RFKILL=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEBUG_DEVRES=y +CONFIG_OF=y +CONFIG_OF_UNITTEST=y +# CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_UID_SYS_STATS=y +CONFIG_MEMORY_STATE_TIME=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_VIRTIO=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +# CONFIG_ETHERNET is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_AX88179_178A is not set +# CONFIG_USB_NET_CDCETHER is not set +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_WLAN_VENDOR_ADMTEK is not set +# CONFIG_WLAN_VENDOR_ATH is not set +# CONFIG_WLAN_VENDOR_ATMEL is not set +# CONFIG_WLAN_VENDOR_BROADCOM is not set +# CONFIG_WLAN_VENDOR_CISCO is not set +# CONFIG_WLAN_VENDOR_INTEL is not set +# CONFIG_WLAN_VENDOR_INTERSIL is not set +# CONFIG_WLAN_VENDOR_MARVELL is not set +# CONFIG_WLAN_VENDOR_MEDIATEK is not set +# CONFIG_WLAN_VENDOR_RALINK is not set +# CONFIG_WLAN_VENDOR_REALTEK is not set +# CONFIG_WLAN_VENDOR_RSI is not set +# CONFIG_WLAN_VENDOR_ST is not set +# CONFIG_WLAN_VENDOR_TI is not set +# CONFIG_WLAN_VENDOR_ZYDAS is not set +# CONFIG_WLAN_VENDOR_QUANTENNA is not set +CONFIG_MAC80211_HWSIM=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_EXAR is not set +CONFIG_SERIAL_8250_NR_UARTS=48 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_INTEL is not set +# CONFIG_HW_RANDOM_AMD is not set +# CONFIG_HW_RANDOM_VIA is not set +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_HPET=y +# CONFIG_HPET_MMAP_DEFAULT is not set +# CONFIG_DEVPORT is not set +# CONFIG_ACPI_I2C_OPREGION is not set +# CONFIG_I2C_COMPAT is not set +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_PTP_1588_CLOCK=y +# CONFIG_HWMON is not set +# CONFIG_X86_PKG_TEMP_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_SOFT_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +# CONFIG_VGA_ARB is not set +CONFIG_DRM=y +# CONFIG_DRM_FBDEV_EMULATION is not set +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +# CONFIG_HID_GENERIC is not set +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_GADGET=y +CONFIG_USB_DUMMY_HCD=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_SW_SYNC=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_VSOC=y +CONFIG_ION=y +# CONFIG_X86_PLATFORM_DEVICES is not set +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +# CONFIG_FIRMWARE_MEMMAP is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_QFMT_V2=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_SDCARD_FS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_HARDLOCKUP_DETECTOR=y +CONFIG_PANIC_TIMEOUT=5 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_ENABLE_DEFAULT_TRACERS=y +# CONFIG_KPROBE_EVENTS is not set +# CONFIG_UPROBE_EVENTS is not set +CONFIG_IO_DELAY_NONE=y +CONFIG_DEBUG_BOOT_PARAMS=y +CONFIG_OPTIMIZE_INLINING=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_PATH=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_DEV_VIRTIO=y diff --git a/arch/x86/crypto/cast5_avx_glue.c b/arch/x86/crypto/cast5_avx_glue.c index dbea6020ffe7d462bd93bf703ccecbe1f817d3f4..575292a33bdf205d3c959bd69cb34d2b7f211d17 100644 --- a/arch/x86/crypto/cast5_avx_glue.c +++ b/arch/x86/crypto/cast5_avx_glue.c @@ -66,8 +66,6 @@ static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, void (*fn)(struct cast5_ctx *ctx, u8 *dst, const u8 *src); int err; - fn = (enc) ? cast5_ecb_enc_16way : cast5_ecb_dec_16way; - err = blkcipher_walk_virt(desc, walk); desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; @@ -79,6 +77,7 @@ static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, /* Process multi-block batch */ if (nbytes >= bsize * CAST5_PARALLEL_BLOCKS) { + fn = (enc) ? cast5_ecb_enc_16way : cast5_ecb_dec_16way; do { fn(ctx, wdst, wsrc); diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index a0b86cf486e0adcf1e7d505bd003056a3e2a1b87..2e9d58cc371e6d99b014bbe666b598c33fd4bdf9 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -110,12 +110,19 @@ static int hv_cpu_init(unsigned int cpu) */ void hyperv_init(void) { - u64 guest_id; + u64 guest_id, required_msrs; union hv_x64_msr_hypercall_contents hypercall_msr; if (x86_hyper_type != X86_HYPER_MS_HYPERV) return; + /* Absolutely required MSRs */ + required_msrs = HV_X64_MSR_HYPERCALL_AVAILABLE | + HV_X64_MSR_VP_INDEX_AVAILABLE; + + if ((ms_hyperv.features & required_msrs) != required_msrs) + return; + /* Allocate percpu VP index */ hv_vp_index = kmalloc_array(num_possible_cpus(), sizeof(*hv_vp_index), GFP_KERNEL); diff --git a/arch/x86/hyperv/mmu.c b/arch/x86/hyperv/mmu.c index 9cc9e1c1e2dbcf6047c9ebd37f280c66b9e7e29a..56c9ebac946fe5a3778b992cb82a3a43ea32b0b8 100644 --- a/arch/x86/hyperv/mmu.c +++ b/arch/x86/hyperv/mmu.c @@ -137,7 +137,12 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus, } if (info->mm) { + /* + * AddressSpace argument must match the CR3 with PCID bits + * stripped out. + */ flush->address_space = virt_to_phys(info->mm->pgd); + flush->address_space &= CR3_ADDR_MASK; flush->flags = 0; } else { flush->address_space = 0; @@ -219,7 +224,12 @@ static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus, } if (info->mm) { + /* + * AddressSpace argument must match the CR3 with PCID bits + * stripped out. + */ flush->address_space = virt_to_phys(info->mm->pgd); + flush->address_space &= CR3_ADDR_MASK; flush->flags = 0; } else { flush->address_space = 0; @@ -278,8 +288,6 @@ void hyperv_setup_mmu_ops(void) if (!(ms_hyperv.hints & HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED)) return; - setup_clear_cpu_cap(X86_FEATURE_PCID); - if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED)) { pr_info("Using hypercall for remote TLB flush\n"); pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others; diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index 8ec99a55e6b9d17df05bc02fcff637204c07ffa3..bf253ad93bbcbd6f60e8138d2f2fe12bcad8a425 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h @@ -34,6 +34,7 @@ extern asmlinkage void kvm_posted_intr_wakeup_ipi(void); extern asmlinkage void kvm_posted_intr_nested_ipi(void); extern asmlinkage void error_interrupt(void); extern asmlinkage void irq_work_interrupt(void); +extern asmlinkage void uv_bau_message_intr1(void); extern asmlinkage void spurious_interrupt(void); extern asmlinkage void thermal_interrupt(void); diff --git a/arch/x86/include/asm/i8259.h b/arch/x86/include/asm/i8259.h index c8376b40e8829d3c8dafc1ea05501558774d7033..5cdcdbd4d892029f7cbd90ab30dfe0832e5780f4 100644 --- a/arch/x86/include/asm/i8259.h +++ b/arch/x86/include/asm/i8259.h @@ -69,6 +69,11 @@ struct legacy_pic { extern struct legacy_pic *legacy_pic; extern struct legacy_pic null_legacy_pic; +static inline bool has_legacy_pic(void) +{ + return legacy_pic != &null_legacy_pic; +} + static inline int nr_legacy_irqs(void) { return legacy_pic->nr_legacy_irqs; diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index b1e8d8db921fc3e6178151fca2dbae87a4ac60cd..340070415c2c3a93b0305ed7624ceaa62bd4362f 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -346,6 +346,7 @@ enum smca_bank_types { SMCA_IF, /* Instruction Fetch */ SMCA_L2_CACHE, /* L2 Cache */ SMCA_DE, /* Decoder Unit */ + SMCA_RESERVED, /* Reserved */ SMCA_EX, /* Execution Unit */ SMCA_FP, /* Floating Point */ SMCA_L3_CACHE, /* L3 Cache */ @@ -376,6 +377,7 @@ struct smca_bank { extern struct smca_bank smca_banks[MAX_NR_BANKS]; extern const char *smca_get_long_name(enum smca_bank_types t); +extern bool amd_mce_is_memory_error(struct mce *m); extern int mce_threshold_create_device(unsigned int cpu); extern int mce_threshold_remove_device(unsigned int cpu); @@ -384,6 +386,7 @@ extern int mce_threshold_remove_device(unsigned int cpu); static inline int mce_threshold_create_device(unsigned int cpu) { return 0; }; static inline int mce_threshold_remove_device(unsigned int cpu) { return 0; }; +static inline bool amd_mce_is_memory_error(struct mce *m) { return false; }; #endif diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h index 55520cec8b27d69727092e6fd81d3a0c0f4db252..6cf0e4cb7b9763a7d4d10438017a73aac737720b 100644 --- a/arch/x86/include/asm/microcode.h +++ b/arch/x86/include/asm/microcode.h @@ -37,7 +37,13 @@ struct cpu_signature { struct device; -enum ucode_state { UCODE_ERROR, UCODE_OK, UCODE_NFOUND }; +enum ucode_state { + UCODE_OK = 0, + UCODE_NEW, + UCODE_UPDATED, + UCODE_NFOUND, + UCODE_ERROR, +}; struct microcode_ops { enum ucode_state (*request_microcode_user) (int cpu, @@ -54,7 +60,7 @@ struct microcode_ops { * are being called. * See also the "Synchronization" section in microcode_core.c. */ - int (*apply_microcode) (int cpu); + enum ucode_state (*apply_microcode) (int cpu); int (*collect_cpu_info) (int cpu, struct cpu_signature *csig); }; diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 15fc074bd6281378d1905af1afcaaab02a29d569..3222c7746cb1f857b686dbbdf5d06c475cf6aaad 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -968,4 +968,5 @@ bool xen_set_default_idle(void); void stop_this_cpu(void *dummy); void df_debug(struct pt_regs *regs, long error_code); +void microcode_check(void); #endif /* _ASM_X86_PROCESSOR_H */ diff --git a/arch/x86/include/uapi/asm/msgbuf.h b/arch/x86/include/uapi/asm/msgbuf.h index 809134c644a677032e9eb2ae5d5a844806bc347e..90ab9a795b49329b5d06cdb45e15cb800a04ff63 100644 --- a/arch/x86/include/uapi/asm/msgbuf.h +++ b/arch/x86/include/uapi/asm/msgbuf.h @@ -1 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_X64_MSGBUF_H +#define __ASM_X64_MSGBUF_H + +#if !defined(__x86_64__) || !defined(__ILP32__) #include +#else +/* + * The msqid64_ds structure for x86 architecture with x32 ABI. + * + * On x86-32 and x86-64 we can just use the generic definition, but + * x32 uses the same binary layout as x86_64, which is differnet + * from other 32-bit architectures. + */ + +struct msqid64_ds { + struct ipc64_perm msg_perm; + __kernel_time_t msg_stime; /* last msgsnd time */ + __kernel_time_t msg_rtime; /* last msgrcv time */ + __kernel_time_t msg_ctime; /* last change time */ + __kernel_ulong_t msg_cbytes; /* current number of bytes on queue */ + __kernel_ulong_t msg_qnum; /* number of messages in queue */ + __kernel_ulong_t msg_qbytes; /* max number of bytes on queue */ + __kernel_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_pid_t msg_lrpid; /* last receive pid */ + __kernel_ulong_t __unused4; + __kernel_ulong_t __unused5; +}; + +#endif + +#endif /* __ASM_GENERIC_MSGBUF_H */ diff --git a/arch/x86/include/uapi/asm/shmbuf.h b/arch/x86/include/uapi/asm/shmbuf.h index 83c05fc2de385c3260286bf12fed846617c8c095..644421f3823beefb16ddc83979fbdf01c15c6a7a 100644 --- a/arch/x86/include/uapi/asm/shmbuf.h +++ b/arch/x86/include/uapi/asm/shmbuf.h @@ -1 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_X86_SHMBUF_H +#define __ASM_X86_SHMBUF_H + +#if !defined(__x86_64__) || !defined(__ILP32__) #include +#else +/* + * The shmid64_ds structure for x86 architecture with x32 ABI. + * + * On x86-32 and x86-64 we can just use the generic definition, but + * x32 uses the same binary layout as x86_64, which is differnet + * from other 32-bit architectures. + */ + +struct shmid64_ds { + struct ipc64_perm shm_perm; /* operation perms */ + size_t shm_segsz; /* size of segment (bytes) */ + __kernel_time_t shm_atime; /* last attach time */ + __kernel_time_t shm_dtime; /* last detach time */ + __kernel_time_t shm_ctime; /* last change time */ + __kernel_pid_t shm_cpid; /* pid of creator */ + __kernel_pid_t shm_lpid; /* pid of last operator */ + __kernel_ulong_t shm_nattch; /* no. of current attaches */ + __kernel_ulong_t __unused4; + __kernel_ulong_t __unused5; +}; + +struct shminfo64 { + __kernel_ulong_t shmmax; + __kernel_ulong_t shmmin; + __kernel_ulong_t shmmni; + __kernel_ulong_t shmseg; + __kernel_ulong_t shmall; + __kernel_ulong_t __unused1; + __kernel_ulong_t __unused2; + __kernel_ulong_t __unused3; + __kernel_ulong_t __unused4; +}; + +#endif + +#endif /* __ASM_X86_SHMBUF_H */ diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 9c2a002d9297cca69a973465584a75e496e50245..6dda3595acf8571b750fb58236e889a5e991396f 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -215,6 +215,10 @@ acpi_parse_x2apic(struct acpi_subtable_header *header, const unsigned long end) apic_id = processor->local_apic_id; enabled = processor->lapic_flags & ACPI_MADT_ENABLED; + /* Ignore invalid ID */ + if (apic_id == 0xffffffff) + return 0; + /* * We need to register disabled CPU as well to permit * counting disabled CPUs. This allows us to size diff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c index f5d92bc3b8844422628b8e6d741be34d727aa45b..2c4d5ece74565f10330b4121af72f33622f820bc 100644 --- a/arch/x86/kernel/aperture_64.c +++ b/arch/x86/kernel/aperture_64.c @@ -30,6 +30,7 @@ #include #include #include +#include /* * Using 512M as goal, in case kexec will load kernel_big @@ -56,6 +57,33 @@ int fallback_aper_force __initdata; int fix_aperture __initdata = 1; +#ifdef CONFIG_PROC_VMCORE +/* + * If the first kernel maps the aperture over e820 RAM, the kdump kernel will + * use the same range because it will remain configured in the northbridge. + * Trying to dump this area via /proc/vmcore may crash the machine, so exclude + * it from vmcore. + */ +static unsigned long aperture_pfn_start, aperture_page_count; + +static int gart_oldmem_pfn_is_ram(unsigned long pfn) +{ + return likely((pfn < aperture_pfn_start) || + (pfn >= aperture_pfn_start + aperture_page_count)); +} + +static void exclude_from_vmcore(u64 aper_base, u32 aper_order) +{ + aperture_pfn_start = aper_base >> PAGE_SHIFT; + aperture_page_count = (32 * 1024 * 1024) << aper_order >> PAGE_SHIFT; + WARN_ON(register_oldmem_pfn_is_ram(&gart_oldmem_pfn_is_ram)); +} +#else +static void exclude_from_vmcore(u64 aper_base, u32 aper_order) +{ +} +#endif + /* This code runs before the PCI subsystem is initialized, so just access the northbridge directly. */ @@ -435,8 +463,16 @@ int __init gart_iommu_hole_init(void) out: if (!fix && !fallback_aper_force) { - if (last_aper_base) + if (last_aper_base) { + /* + * If this is the kdump kernel, the first kernel + * may have allocated the range over its e820 RAM + * and fixed up the northbridge + */ + exclude_from_vmcore(last_aper_base, last_aper_order); + return 1; + } return 0; } @@ -473,6 +509,14 @@ int __init gart_iommu_hole_init(void) return 0; } + /* + * If this is the kdump kernel _and_ the first kernel did not + * configure the aperture in the northbridge, this range may + * overlap with the first kernel's memory. We can't access the + * range through vmcore even though it should be part of the dump. + */ + exclude_from_vmcore(aper_alloc, aper_order); + /* Fix up the north bridges */ for (i = 0; i < amd_nb_bus_dev_ranges[i].dev_limit; i++) { int bus, dev_base, dev_limit; diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index c0b694810ff45adaf54fb8db5211f65a1f89b876..02cfc615e3fb36e80e0bd105d6cd2cebfef48934 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -1140,16 +1140,25 @@ static void __init decode_gam_rng_tbl(unsigned long ptr) uv_gre_table = gre; for (; gre->type != UV_GAM_RANGE_TYPE_UNUSED; gre++) { + unsigned long size = ((unsigned long)(gre->limit - lgre) + << UV_GAM_RANGE_SHFT); + int order = 0; + char suffix[] = " KMGTPE"; + + while (size > 9999 && order < sizeof(suffix)) { + size /= 1024; + order++; + } + if (!index) { pr_info("UV: GAM Range Table...\n"); pr_info("UV: # %20s %14s %5s %4s %5s %3s %2s\n", "Range", "", "Size", "Type", "NASID", "SID", "PN"); } - pr_info("UV: %2d: 0x%014lx-0x%014lx %5luG %3d %04x %02x %02x\n", + pr_info("UV: %2d: 0x%014lx-0x%014lx %5lu%c %3d %04x %02x %02x\n", index++, (unsigned long)lgre << UV_GAM_RANGE_SHFT, (unsigned long)gre->limit << UV_GAM_RANGE_SHFT, - ((unsigned long)(gre->limit - lgre)) >> - (30 - UV_GAM_RANGE_SHFT), /* 64M -> 1G */ + size, suffix[order], gre->type, gre->nasid, gre->sockid, gre->pnode); lgre = gre->limit; diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 651b7afed4dada9255fdf587fd312acc95d62358..cf6380200dc29f897d1e6317d3445fda909ee95c 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1724,3 +1724,33 @@ static int __init init_cpu_syscore(void) return 0; } core_initcall(init_cpu_syscore); + +/* + * The microcode loader calls this upon late microcode load to recheck features, + * only when microcode has been updated. Caller holds microcode_mutex and CPU + * hotplug lock. + */ +void microcode_check(void) +{ + struct cpuinfo_x86 info; + + perf_check_microcode(); + + /* Reload CPUID max function as it might've changed. */ + info.cpuid_level = cpuid_eax(0); + + /* + * Copy all capability leafs to pick up the synthetic ones so that + * memcmp() below doesn't fail on that. The ones coming from CPUID will + * get overwritten in get_cpu_cap(). + */ + memcpy(&info.x86_capability, &boot_cpu_data.x86_capability, sizeof(info.x86_capability)); + + get_cpu_cap(&info); + + if (!memcmp(&info.x86_capability, &boot_cpu_data.x86_capability, sizeof(info.x86_capability))) + return; + + pr_warn("x86/CPU: CPU features have changed after loading microcode, but might not take effect.\n"); + pr_warn("x86/CPU: Please consider either early loading through initrd/built-in or a potential BIOS update.\n"); +} diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index e13d652fc30a3d2a1b492baa3563ec3b4382345f..28d27de08545943c9cbb1f91ec052b964b3e45dd 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -512,10 +512,8 @@ static int mce_usable_address(struct mce *m) bool mce_is_memory_error(struct mce *m) { if (m->cpuvendor == X86_VENDOR_AMD) { - /* ErrCodeExt[20:16] */ - u8 xec = (m->status >> 16) & 0x1f; + return amd_mce_is_memory_error(m); - return (xec == 0x0 || xec == 0x8); } else if (m->cpuvendor == X86_VENDOR_INTEL) { /* * Intel SDM Volume 3B - 15.9.2 Compound Error Codes diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index 486f640b02efd1313911c494588bb7875e7e5759..259c75d7a2a0b3d19cf8cd0f340a039c306c12f5 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -82,6 +82,7 @@ static struct smca_bank_name smca_names[] = { [SMCA_IF] = { "insn_fetch", "Instruction Fetch Unit" }, [SMCA_L2_CACHE] = { "l2_cache", "L2 Cache" }, [SMCA_DE] = { "decode_unit", "Decode Unit" }, + [SMCA_RESERVED] = { "reserved", "Reserved" }, [SMCA_EX] = { "execution_unit", "Execution Unit" }, [SMCA_FP] = { "floating_point", "Floating Point Unit" }, [SMCA_L3_CACHE] = { "l3_cache", "L3 Cache" }, @@ -110,9 +111,26 @@ const char *smca_get_long_name(enum smca_bank_types t) } EXPORT_SYMBOL_GPL(smca_get_long_name); +static enum smca_bank_types smca_get_bank_type(unsigned int bank) +{ + struct smca_bank *b; + + if (bank >= MAX_NR_BANKS) + return N_SMCA_BANK_TYPES; + + b = &smca_banks[bank]; + if (!b->hwid) + return N_SMCA_BANK_TYPES; + + return b->hwid->bank_type; +} + static struct smca_hwid smca_hwid_mcatypes[] = { /* { bank_type, hwid_mcatype, xec_bitmap } */ + /* Reserved type */ + { SMCA_RESERVED, HWID_MCATYPE(0x00, 0x0), 0x0 }, + /* ZN Core (HWID=0xB0) MCA types */ { SMCA_LS, HWID_MCATYPE(0xB0, 0x0), 0x1FFFEF }, { SMCA_IF, HWID_MCATYPE(0xB0, 0x1), 0x3FFF }, @@ -416,7 +434,25 @@ static u32 get_block_address(unsigned int cpu, u32 current_addr, u32 low, u32 hi { u32 addr = 0, offset = 0; + if ((bank >= mca_cfg.banks) || (block >= NR_BLOCKS)) + return addr; + + /* Get address from already initialized block. */ + if (per_cpu(threshold_banks, cpu)) { + struct threshold_bank *bankp = per_cpu(threshold_banks, cpu)[bank]; + + if (bankp && bankp->blocks) { + struct threshold_block *blockp = &bankp->blocks[block]; + + if (blockp) + return blockp->address; + } + } + if (mce_flags.smca) { + if (smca_get_bank_type(bank) == SMCA_RESERVED) + return addr; + if (!block) { addr = MSR_AMD64_SMCA_MCx_MISC(bank); } else { @@ -738,6 +774,17 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) } EXPORT_SYMBOL_GPL(umc_normaddr_to_sysaddr); +bool amd_mce_is_memory_error(struct mce *m) +{ + /* ErrCodeExt[20:16] */ + u8 xec = (m->status >> 16) & 0x1f; + + if (mce_flags.smca) + return smca_get_bank_type(m->bank) == SMCA_UMC && xec == 0x0; + + return m->bank == 4 && xec == 0x8; +} + static void __log_error(unsigned int bank, u64 status, u64 addr, u64 misc) { struct mce m; @@ -1036,7 +1083,7 @@ static struct kobj_type threshold_ktype = { static const char *get_name(unsigned int bank, struct threshold_block *b) { - unsigned int bank_type; + enum smca_bank_types bank_type; if (!mce_flags.smca) { if (b && bank == 4) @@ -1045,11 +1092,10 @@ static const char *get_name(unsigned int bank, struct threshold_block *b) return th_names[bank]; } - if (!smca_banks[bank].hwid) + bank_type = smca_get_bank_type(bank); + if (bank_type >= N_SMCA_BANK_TYPES) return NULL; - bank_type = smca_banks[bank].hwid->bank_type; - if (b && bank_type == SMCA_UMC) { if (b->block < ARRAY_SIZE(smca_umc_block_names)) return smca_umc_block_names[b->block]; diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 330b8462d426faad0dccdc480f34eec34cd8b92f..48179928ff38cf12476ce27cd096383aee1feb93 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -339,7 +339,7 @@ int __init save_microcode_in_initrd_amd(unsigned int cpuid_1_eax) return -EINVAL; ret = load_microcode_amd(true, x86_family(cpuid_1_eax), desc.data, desc.size); - if (ret != UCODE_OK) + if (ret > UCODE_UPDATED) return -EINVAL; return 0; @@ -498,7 +498,7 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size, return patch_size; } -static int apply_microcode_amd(int cpu) +static enum ucode_state apply_microcode_amd(int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); struct microcode_amd *mc_amd; @@ -512,7 +512,7 @@ static int apply_microcode_amd(int cpu) p = find_patch(cpu); if (!p) - return 0; + return UCODE_NFOUND; mc_amd = p->data; uci->mc = p->data; @@ -523,13 +523,13 @@ static int apply_microcode_amd(int cpu) if (rev >= mc_amd->hdr.patch_id) { c->microcode = rev; uci->cpu_sig.rev = rev; - return 0; + return UCODE_OK; } if (__apply_microcode_amd(mc_amd)) { pr_err("CPU%d: update failed for patch_level=0x%08x\n", cpu, mc_amd->hdr.patch_id); - return -1; + return UCODE_ERROR; } pr_info("CPU%d: new patch_level=0x%08x\n", cpu, mc_amd->hdr.patch_id); @@ -537,7 +537,7 @@ static int apply_microcode_amd(int cpu) uci->cpu_sig.rev = mc_amd->hdr.patch_id; c->microcode = mc_amd->hdr.patch_id; - return 0; + return UCODE_UPDATED; } static int install_equiv_cpu_table(const u8 *buf) @@ -683,27 +683,35 @@ static enum ucode_state __load_microcode_amd(u8 family, const u8 *data, static enum ucode_state load_microcode_amd(bool save, u8 family, const u8 *data, size_t size) { + struct ucode_patch *p; enum ucode_state ret; /* free old equiv table */ free_equiv_cpu_table(); ret = __load_microcode_amd(family, data, size); - - if (ret != UCODE_OK) + if (ret != UCODE_OK) { cleanup(); + return ret; + } -#ifdef CONFIG_X86_32 - /* save BSP's matching patch for early load */ - if (save) { - struct ucode_patch *p = find_patch(0); - if (p) { - memset(amd_ucode_patch, 0, PATCH_MAX_SIZE); - memcpy(amd_ucode_patch, p->data, min_t(u32, ksize(p->data), - PATCH_MAX_SIZE)); - } + p = find_patch(0); + if (!p) { + return ret; + } else { + if (boot_cpu_data.microcode == p->patch_id) + return ret; + + ret = UCODE_NEW; } -#endif + + /* save BSP's matching patch for early load */ + if (!save) + return ret; + + memset(amd_ucode_patch, 0, PATCH_MAX_SIZE); + memcpy(amd_ucode_patch, p->data, min_t(u32, ksize(p->data), PATCH_MAX_SIZE)); + return ret; } diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index e4fc595cd6eaa6ee9b51a380e62fa05270c184e1..c8e0cda0f272f3b005fa7bba2726fccd41e87f80 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -22,13 +22,16 @@ #define pr_fmt(fmt) "microcode: " fmt #include +#include #include #include #include #include #include +#include #include #include +#include #include #include @@ -64,6 +67,11 @@ LIST_HEAD(microcode_cache); */ static DEFINE_MUTEX(microcode_mutex); +/* + * Serialize late loading so that CPUs get updated one-by-one. + */ +static DEFINE_SPINLOCK(update_lock); + struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; struct cpu_info_ctx { @@ -373,26 +381,23 @@ static int collect_cpu_info(int cpu) return ret; } -struct apply_microcode_ctx { - int err; -}; - static void apply_microcode_local(void *arg) { - struct apply_microcode_ctx *ctx = arg; + enum ucode_state *err = arg; - ctx->err = microcode_ops->apply_microcode(smp_processor_id()); + *err = microcode_ops->apply_microcode(smp_processor_id()); } static int apply_microcode_on_target(int cpu) { - struct apply_microcode_ctx ctx = { .err = 0 }; + enum ucode_state err; int ret; - ret = smp_call_function_single(cpu, apply_microcode_local, &ctx, 1); - if (!ret) - ret = ctx.err; - + ret = smp_call_function_single(cpu, apply_microcode_local, &err, 1); + if (!ret) { + if (err == UCODE_ERROR) + ret = 1; + } return ret; } @@ -489,31 +494,122 @@ static void __exit microcode_dev_exit(void) /* fake device for request_firmware */ static struct platform_device *microcode_pdev; -static int reload_for_cpu(int cpu) +/* + * Late loading dance. Why the heavy-handed stomp_machine effort? + * + * - HT siblings must be idle and not execute other code while the other sibling + * is loading microcode in order to avoid any negative interactions caused by + * the loading. + * + * - In addition, microcode update on the cores must be serialized until this + * requirement can be relaxed in the future. Right now, this is conservative + * and good. + */ +#define SPINUNIT 100 /* 100 nsec */ + +static int check_online_cpus(void) { - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - enum ucode_state ustate; - int err = 0; + if (num_online_cpus() == num_present_cpus()) + return 0; - if (!uci->valid) - return err; + pr_err("Not all CPUs online, aborting microcode update.\n"); - ustate = microcode_ops->request_microcode_fw(cpu, µcode_pdev->dev, true); - if (ustate == UCODE_OK) - apply_microcode_on_target(cpu); - else - if (ustate == UCODE_ERROR) - err = -EINVAL; - return err; + return -EINVAL; +} + +static atomic_t late_cpus_in; +static atomic_t late_cpus_out; + +static int __wait_for_cpus(atomic_t *t, long long timeout) +{ + int all_cpus = num_online_cpus(); + + atomic_inc(t); + + while (atomic_read(t) < all_cpus) { + if (timeout < SPINUNIT) { + pr_err("Timeout while waiting for CPUs rendezvous, remaining: %d\n", + all_cpus - atomic_read(t)); + return 1; + } + + ndelay(SPINUNIT); + timeout -= SPINUNIT; + + touch_nmi_watchdog(); + } + return 0; +} + +/* + * Returns: + * < 0 - on error + * 0 - no update done + * 1 - microcode was updated + */ +static int __reload_late(void *info) +{ + int cpu = smp_processor_id(); + enum ucode_state err; + int ret = 0; + + /* + * Wait for all CPUs to arrive. A load will not be attempted unless all + * CPUs show up. + * */ + if (__wait_for_cpus(&late_cpus_in, NSEC_PER_SEC)) + return -1; + + spin_lock(&update_lock); + apply_microcode_local(&err); + spin_unlock(&update_lock); + + /* siblings return UCODE_OK because their engine got updated already */ + if (err > UCODE_NFOUND) { + pr_warn("Error reloading microcode on CPU %d\n", cpu); + ret = -1; + } else if (err == UCODE_UPDATED || err == UCODE_OK) { + ret = 1; + } + + /* + * Increase the wait timeout to a safe value here since we're + * serializing the microcode update and that could take a while on a + * large number of CPUs. And that is fine as the *actual* timeout will + * be determined by the last CPU finished updating and thus cut short. + */ + if (__wait_for_cpus(&late_cpus_out, NSEC_PER_SEC * num_online_cpus())) + panic("Timeout during microcode update!\n"); + + return ret; +} + +/* + * Reload microcode late on all CPUs. Wait for a sec until they + * all gather together. + */ +static int microcode_reload_late(void) +{ + int ret; + + atomic_set(&late_cpus_in, 0); + atomic_set(&late_cpus_out, 0); + + ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask); + if (ret > 0) + microcode_check(); + + return ret; } static ssize_t reload_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { + enum ucode_state tmp_ret = UCODE_OK; + int bsp = boot_cpu_data.cpu_index; unsigned long val; - int cpu; - ssize_t ret = 0, tmp_ret; + ssize_t ret = 0; ret = kstrtoul(buf, 0, &val); if (ret) @@ -522,23 +618,24 @@ static ssize_t reload_store(struct device *dev, if (val != 1) return size; + tmp_ret = microcode_ops->request_microcode_fw(bsp, µcode_pdev->dev, true); + if (tmp_ret != UCODE_NEW) + return size; + get_online_cpus(); - mutex_lock(µcode_mutex); - for_each_online_cpu(cpu) { - tmp_ret = reload_for_cpu(cpu); - if (tmp_ret != 0) - pr_warn("Error reloading microcode on CPU %d\n", cpu); - /* save retval of the first encountered reload error */ - if (!ret) - ret = tmp_ret; - } - if (!ret) - perf_check_microcode(); + ret = check_online_cpus(); + if (ret) + goto put; + + mutex_lock(µcode_mutex); + ret = microcode_reload_late(); mutex_unlock(µcode_mutex); + +put: put_online_cpus(); - if (!ret) + if (ret >= 0) ret = size; return ret; @@ -606,10 +703,8 @@ static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw) if (system_state != SYSTEM_RUNNING) return UCODE_NFOUND; - ustate = microcode_ops->request_microcode_fw(cpu, µcode_pdev->dev, - refresh_fw); - - if (ustate == UCODE_OK) { + ustate = microcode_ops->request_microcode_fw(cpu, µcode_pdev->dev, refresh_fw); + if (ustate == UCODE_NEW) { pr_debug("CPU%d updated upon init\n", cpu); apply_microcode_on_target(cpu); } diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index a15db2b4e0d66a8b5c4d2468359eeafb85401151..1c2cfa0644aa979c97cc01a42925a44c25f9f852 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -485,7 +485,6 @@ static void show_saved_mc(void) */ static void save_mc_for_early(u8 *mc, unsigned int size) { -#ifdef CONFIG_HOTPLUG_CPU /* Synchronization during CPU hotplug. */ static DEFINE_MUTEX(x86_cpu_microcode_mutex); @@ -495,7 +494,6 @@ static void save_mc_for_early(u8 *mc, unsigned int size) show_saved_mc(); mutex_unlock(&x86_cpu_microcode_mutex); -#endif } static bool load_builtin_intel_microcode(struct cpio_data *cp) @@ -589,6 +587,23 @@ static int apply_microcode_early(struct ucode_cpu_info *uci, bool early) if (!mc) return 0; + /* + * Save us the MSR write below - which is a particular expensive + * operation - when the other hyperthread has updated the microcode + * already. + */ + rev = intel_get_microcode_revision(); + if (rev >= mc->hdr.rev) { + uci->cpu_sig.rev = rev; + return UCODE_OK; + } + + /* + * Writeback and invalidate caches before updating microcode to avoid + * internal issues depending on what the microcode is updating. + */ + native_wbinvd(); + /* write microcode via MSR 0x79 */ native_wrmsrl(MSR_IA32_UCODE_WRITE, (unsigned long)mc->bits); @@ -772,27 +787,44 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) return 0; } -static int apply_microcode_intel(int cpu) +static enum ucode_state apply_microcode_intel(int cpu) { + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct cpuinfo_x86 *c = &cpu_data(cpu); struct microcode_intel *mc; - struct ucode_cpu_info *uci; - struct cpuinfo_x86 *c; static int prev_rev; u32 rev; /* We should bind the task to the CPU */ if (WARN_ON(raw_smp_processor_id() != cpu)) - return -1; + return UCODE_ERROR; - uci = ucode_cpu_info + cpu; - mc = uci->mc; + /* Look for a newer patch in our cache: */ + mc = find_patch(uci); if (!mc) { - /* Look for a newer patch in our cache: */ - mc = find_patch(uci); + mc = uci->mc; if (!mc) - return 0; + return UCODE_NFOUND; } + /* + * Save us the MSR write below - which is a particular expensive + * operation - when the other hyperthread has updated the microcode + * already. + */ + rev = intel_get_microcode_revision(); + if (rev >= mc->hdr.rev) { + uci->cpu_sig.rev = rev; + c->microcode = rev; + return UCODE_OK; + } + + /* + * Writeback and invalidate caches before updating microcode to avoid + * internal issues depending on what the microcode is updating. + */ + native_wbinvd(); + /* write microcode via MSR 0x79 */ wrmsrl(MSR_IA32_UCODE_WRITE, (unsigned long)mc->bits); @@ -801,7 +833,7 @@ static int apply_microcode_intel(int cpu) if (rev != mc->hdr.rev) { pr_err("CPU%d update to revision 0x%x failed\n", cpu, mc->hdr.rev); - return -1; + return UCODE_ERROR; } if (rev != prev_rev) { @@ -813,12 +845,10 @@ static int apply_microcode_intel(int cpu) prev_rev = rev; } - c = &cpu_data(cpu); - uci->cpu_sig.rev = rev; c->microcode = rev; - return 0; + return UCODE_UPDATED; } static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, @@ -830,6 +860,7 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, unsigned int leftover = size; unsigned int curr_mc_size = 0, new_mc_size = 0; unsigned int csig, cpf; + enum ucode_state ret = UCODE_OK; while (leftover) { struct microcode_header_intel mc_header; @@ -871,6 +902,7 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, new_mc = mc; new_mc_size = mc_size; mc = NULL; /* trigger new vmalloc */ + ret = UCODE_NEW; } ucode_ptr += mc_size; @@ -900,7 +932,7 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n", cpu, new_rev, uci->cpu_sig.rev); - return UCODE_OK; + return ret; } static int get_ucode_fw(void *to, const void *from, size_t n) diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index afbecff161d162c11a82d0610ae406b910c3f791..a2d8a3908670993c898cd0d22cbdd6742e51b4da 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -109,7 +109,7 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, struct stack_info stack_info = {0}; unsigned long visit_mask = 0; int graph_idx = 0; - bool partial; + bool partial = false; printk("%sCall Trace:\n", log_lvl); diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c index a5962408001547b3106a813b73d14052299d6c97..0c5256653d6c5256eef39c5dc6768d49e40889a0 100644 --- a/arch/x86/kernel/idt.c +++ b/arch/x86/kernel/idt.c @@ -140,6 +140,9 @@ static const __initconst struct idt_data apic_idts[] = { # ifdef CONFIG_IRQ_WORK INTG(IRQ_WORK_VECTOR, irq_work_interrupt), # endif +#ifdef CONFIG_X86_UV + INTG(UV_BAU_MESSAGE, uv_bau_message_intr1), +#endif INTG(SPURIOUS_APIC_VECTOR, spurious_interrupt), INTG(ERROR_APIC_VECTOR, error_interrupt), #endif diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 2651ca2112c45b5e09d5c666bdba0c14e8943eb5..6b841262b7905efb84eabc07749254cababfeab1 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1613,6 +1613,8 @@ static inline void mwait_play_dead(void) void *mwait_ptr; int i; + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + return; if (!this_cpu_has(X86_FEATURE_MWAIT)) return; if (!this_cpu_has(X86_FEATURE_CLFLUSH)) diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 47506567435ee98f051cbb161bf5d01a1004ac53..0bf06fa3027e00edd4138f70b16936d3ecd98e81 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -25,6 +25,7 @@ #include #include #include +#include unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */ EXPORT_SYMBOL(cpu_khz); @@ -316,7 +317,7 @@ static unsigned long calc_hpet_ref(u64 deltatsc, u64 hpet1, u64 hpet2) hpet2 -= hpet1; tmp = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); do_div(tmp, 1000000); - do_div(deltatsc, tmp); + deltatsc = div64_u64(deltatsc, tmp); return (unsigned long) deltatsc; } @@ -363,6 +364,20 @@ static unsigned long pit_calibrate_tsc(u32 latch, unsigned long ms, int loopmin) unsigned long tscmin, tscmax; int pitcnt; + if (!has_legacy_pic()) { + /* + * Relies on tsc_early_delay_calibrate() to have given us semi + * usable udelay(), wait for the same 50ms we would have with + * the PIT loop below. + */ + udelay(10 * USEC_PER_MSEC); + udelay(10 * USEC_PER_MSEC); + udelay(10 * USEC_PER_MSEC); + udelay(10 * USEC_PER_MSEC); + udelay(10 * USEC_PER_MSEC); + return ULONG_MAX; + } + /* Set the Gate high, disable speaker */ outb((inb(0x61) & ~0x02) | 0x01, 0x61); @@ -487,6 +502,9 @@ static unsigned long quick_pit_calibrate(void) u64 tsc, delta; unsigned long d1, d2; + if (!has_legacy_pic()) + return 0; + /* Set the Gate high, disable speaker */ outb((inb(0x61) & ~0x02) | 0x01, 0x61); diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index f438e0c4aa8cda07f6e6c6813f054ad8b020ac92..43bbece92632a7bc9c1693bae2e75d99ed3d6d33 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -3019,7 +3019,7 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn) return RET_PF_RETRY; } - return RET_PF_EMULATE; + return -EFAULT; } static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu, diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index ae4803b213d070a1d5000d69706ba270d4dd6988..bdd84ce4491e5375ffab668b2d9fe01207173fc7 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -6765,7 +6765,21 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu) if (!is_guest_mode(vcpu) && !kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) { trace_kvm_fast_mmio(gpa); - return kvm_skip_emulated_instruction(vcpu); + /* + * Doing kvm_skip_emulated_instruction() depends on undefined + * behavior: Intel's manual doesn't mandate + * VM_EXIT_INSTRUCTION_LEN to be set in VMCS when EPT MISCONFIG + * occurs and while on real hardware it was observed to be set, + * other hypervisors (namely Hyper-V) don't set it, we end up + * advancing IP with some random value. Disable fast mmio when + * running nested and keep it for real hardware in hope that + * VM_EXIT_INSTRUCTION_LEN will always be set correctly. + */ + if (!static_cpu_has(X86_FEATURE_HYPERVISOR)) + return kvm_skip_emulated_instruction(vcpu); + else + return x86_emulate_instruction(vcpu, gpa, EMULTYPE_SKIP, + NULL, 0) == EMULATE_DONE; } ret = kvm_mmu_page_fault(vcpu, gpa, PFERR_RSVD_MASK, NULL, 0); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b9afb4784d1289fca5e742517ad466ecf1b5eff5..3b2c3aa2cd076bdc7c4e23bc6befe4a6e8ff40b3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4225,13 +4225,14 @@ long kvm_arch_vm_ioctl(struct file *filp, mutex_unlock(&kvm->lock); break; case KVM_XEN_HVM_CONFIG: { + struct kvm_xen_hvm_config xhc; r = -EFAULT; - if (copy_from_user(&kvm->arch.xen_hvm_config, argp, - sizeof(struct kvm_xen_hvm_config))) + if (copy_from_user(&xhc, argp, sizeof(xhc))) goto out; r = -EINVAL; - if (kvm->arch.xen_hvm_config.flags) + if (xhc.flags) goto out; + memcpy(&kvm->arch.xen_hvm_config, &xhc, sizeof(xhc)); r = 0; break; } @@ -5698,7 +5699,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, * handle watchpoints yet, those would be handled in * the emulate_ops. */ - if (kvm_vcpu_check_breakpoint(vcpu, &r)) + if (!(emulation_type & EMULTYPE_SKIP) && + kvm_vcpu_check_breakpoint(vcpu, &r)) return r; ctxt->interruptibility = 0; diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index fe85d1204db8f43f8d16a9f80c4d9b3398114d6a..642357aff2162fea504cc25f93375d9f4a7204e1 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1180,8 +1180,7 @@ void __init mem_init(void) after_bootmem = 1; /* Register memory areas for /proc/kcore */ - kclist_add(&kcore_vsyscall, (void *)VSYSCALL_ADDR, - PAGE_SIZE, KCORE_OTHER); + kclist_add(&kcore_vsyscall, (void *)VSYSCALL_ADDR, PAGE_SIZE, KCORE_USER); mem_init_print_info(NULL); } diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index abff76bda9e6c81fa29bc949cf3972e1893e9ccd..a7a7677265b6f73779d486da8bb5ae0b83e48062 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -592,7 +592,7 @@ enum __force_cpu_type { static int force_cpu_type; -static int set_cpu_type(const char *str, struct kernel_param *kp) +static int set_cpu_type(const char *str, const struct kernel_param *kp) { if (!strcmp(str, "timer")) { force_cpu_type = timer; diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 7d5d53f36a7abd90754edd64ce3b8edeb5651382..0b530c53de1f8bc4edf2d4deac6b60620de4c491 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -2254,8 +2254,6 @@ static int __init uv_bau_init(void) init_uvhub(uvhub, vector, uv_base_pnode); } - alloc_intr_gate(vector, uv_bau_message_intr1); - for_each_possible_blade(uvhub) { if (uv_blade_nr_possible_cpus(uvhub)) { unsigned long val; diff --git a/arch/x86/power/hibernate_32.c b/arch/x86/power/hibernate_32.c index c35fdb585c6853a1f281b043d54ca3b59c801d06..afc4ed7b1578267c0f87a858aea0de5494829ec1 100644 --- a/arch/x86/power/hibernate_32.c +++ b/arch/x86/power/hibernate_32.c @@ -145,7 +145,7 @@ static inline void resume_init_first_level_page_table(pgd_t *pg_dir) #endif } -int swsusp_arch_resume(void) +asmlinkage int swsusp_arch_resume(void) { int error; diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c index f910c514438f168a5f09afbd312311498abe2c50..0ef5e520496891eefe9e30269d6dd779027223fd 100644 --- a/arch/x86/power/hibernate_64.c +++ b/arch/x86/power/hibernate_64.c @@ -174,7 +174,7 @@ static int relocate_restore_code(void) return 0; } -int swsusp_arch_resume(void) +asmlinkage int swsusp_arch_resume(void) { int error; diff --git a/arch/x86/um/stub_segv.c b/arch/x86/um/stub_segv.c index 1518d2805ae81733eb0629989f4d39ae595502c2..27361cbb7ca9bed47194fe4127e231a8d3c39c0d 100644 --- a/arch/x86/um/stub_segv.c +++ b/arch/x86/um/stub_segv.c @@ -6,11 +6,12 @@ #include #include #include +#include void __attribute__ ((__section__ (".__syscall_stub"))) stub_segv_handler(int sig, siginfo_t *info, void *p) { - struct ucontext *uc = p; + ucontext_t *uc = p; GET_FAULTINFO_FROM_MC(*((struct faultinfo *) STUB_DATA), &uc->uc_mcontext); diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index f896c2975545cea9a2f6ef084a7734de832085db..fcd8789470d1e822943fc7e1bc1ecc146759c9fb 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -1258,10 +1258,6 @@ asmlinkage __visible void __init xen_start_kernel(void) */ __userpte_alloc_gfp &= ~__GFP_HIGHMEM; - /* Work out if we support NX */ - get_cpu_cap(&boot_cpu_data); - x86_configure_nx(); - /* Get mfn list */ xen_build_dynamic_phys_to_machine(); @@ -1271,6 +1267,10 @@ asmlinkage __visible void __init xen_start_kernel(void) */ xen_setup_gdt(0); + /* Work out if we support NX */ + get_cpu_cap(&boot_cpu_data); + x86_configure_nx(); + xen_init_irq_ops(); /* Let's presume PV guests always boot on vCPU with id 0. */ diff --git a/arch/x86/xen/mmu_hvm.c b/arch/x86/xen/mmu_hvm.c index 2cfcfe4f6b2a054e52868f6fa49e35caf3c2fe54..dd2ad82eee80dfea0ef21c07ae4182694bd71b69 100644 --- a/arch/x86/xen/mmu_hvm.c +++ b/arch/x86/xen/mmu_hvm.c @@ -75,6 +75,6 @@ void __init xen_hvm_init_mmu_ops(void) if (is_pagetable_dying_supported()) pv_mmu_ops.exit_mmap = xen_hvm_exit_mmap; #ifdef CONFIG_PROC_VMCORE - register_oldmem_pfn_is_ram(&xen_oldmem_pfn_is_ram); + WARN_ON(register_oldmem_pfn_is_ram(&xen_oldmem_pfn_is_ram)); #endif } diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c index ceefb9a706d64c3a520c80fb39bc8265b1c144c0..5d53e504acae58d02fa3d8c0b5cef4b625e758aa 100644 --- a/block/bfq-cgroup.c +++ b/block/bfq-cgroup.c @@ -749,10 +749,11 @@ static void bfq_pd_offline(struct blkg_policy_data *pd) unsigned long flags; int i; + spin_lock_irqsave(&bfqd->lock, flags); + if (!entity) /* root group */ - return; + goto put_async_queues; - spin_lock_irqsave(&bfqd->lock, flags); /* * Empty all service_trees belonging to this group before * deactivating the group itself. @@ -783,6 +784,8 @@ static void bfq_pd_offline(struct blkg_policy_data *pd) } __bfq_deactivate_entity(entity, false); + +put_async_queues: bfq_put_async_queues(bfqd, bfqg); spin_unlock_irqrestore(&bfqd->lock, flags); diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 0f860cf0d56d12f3eef5d24cbb40fdf3ac6a1287..56c9cd01fd1d6cc70e4c895acb292f5f80808b9e 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -4447,8 +4447,16 @@ static void bfq_prepare_request(struct request *rq, struct bio *bio) bool new_queue = false; bool bfqq_already_existing = false, split = false; - if (!rq->elv.icq) + /* + * Even if we don't have an icq attached, we should still clear + * the scheduler pointers, as they might point to previously + * allocated bic/bfqq structs. + */ + if (!rq->elv.icq) { + rq->elv.priv[0] = rq->elv.priv[1] = NULL; return; + } + bic = icq_to_bic(rq->elv.icq); spin_lock_irq(&bfqd->lock); diff --git a/block/bio.c b/block/bio.c index 7f978eac9a7ae9729082a87ffb39b7c4db3a544a..90f19d7df66cf73ce80e8eade310105bfa17ba3d 100644 --- a/block/bio.c +++ b/block/bio.c @@ -43,9 +43,9 @@ * break badly! cannot be bigger than what you can fit into an * unsigned short */ -#define BV(x) { .nr_vecs = x, .name = "biovec-"__stringify(x) } +#define BV(x, n) { .nr_vecs = x, .name = "biovec-"#n } static struct biovec_slab bvec_slabs[BVEC_POOL_NR] __read_mostly = { - BV(1), BV(4), BV(16), BV(64), BV(128), BV(BIO_MAX_PAGES), + BV(1, 1), BV(4, 4), BV(16, 16), BV(64, 64), BV(128, 128), BV(BIO_MAX_PAGES, max), }; #undef BV @@ -1893,7 +1893,7 @@ struct bio *bio_split(struct bio *bio, int sectors, bio_advance(bio, split->bi_iter.bi_size); if (bio_flagged(bio, BIO_TRACE_COMPLETION)) - bio_set_flag(bio, BIO_TRACE_COMPLETION); + bio_set_flag(split, BIO_TRACE_COMPLETION); return split; } diff --git a/block/blk-core.c b/block/blk-core.c index 2d26ae58612e190af1119699a2981991c634c5ae..322941cc379fcc0082e3880481a6679a93973e7d 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -3067,6 +3067,8 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, { if (bio_has_data(bio)) rq->nr_phys_segments = bio_phys_segments(q, bio); + else if (bio_op(bio) == REQ_OP_DISCARD) + rq->nr_phys_segments = 1; rq->__data_len = bio->bi_iter.bi_size; rq->bio = rq->biotail = bio; diff --git a/block/blk-merge.c b/block/blk-merge.c index f5dedd57dff6b40fb6e88faa532bb88a94fcde61..8d60a5bbcef930d514cc3a75a273bc3506922904 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -551,6 +551,24 @@ static bool req_no_special_merge(struct request *req) return !q->mq_ops && req->special; } +static bool req_attempt_discard_merge(struct request_queue *q, struct request *req, + struct request *next) +{ + unsigned short segments = blk_rq_nr_discard_segments(req); + + if (segments >= queue_max_discard_segments(q)) + goto no_merge; + if (blk_rq_sectors(req) + bio_sectors(next->bio) > + blk_rq_get_max_sectors(req, blk_rq_pos(req))) + goto no_merge; + + req->nr_phys_segments = segments + blk_rq_nr_discard_segments(next); + return true; +no_merge: + req_set_nomerge(q, req); + return false; +} + static int ll_merge_requests_fn(struct request_queue *q, struct request *req, struct request *next) { @@ -684,9 +702,13 @@ static struct request *attempt_merge(struct request_queue *q, * If we are allowed to merge, then append bio list * from next to rq and release next. merge_requests_fn * will have updated segment counts, update sector - * counts here. + * counts here. Handle DISCARDs separately, as they + * have separate settings. */ - if (!ll_merge_requests_fn(q, req, next)) + if (req_op(req) == REQ_OP_DISCARD) { + if (!req_attempt_discard_merge(q, req, next)) + return NULL; + } else if (!ll_merge_requests_fn(q, req, next)) return NULL; /* @@ -716,7 +738,8 @@ static struct request *attempt_merge(struct request_queue *q, req->__data_len += blk_rq_bytes(next); - elv_merge_requests(q, req, next); + if (req_op(req) != REQ_OP_DISCARD) + elv_merge_requests(q, req, next); /* * 'next' is going away, so update stats accordingly diff --git a/block/blk-mq-cpumap.c b/block/blk-mq-cpumap.c index 9f8cffc8a701ec24c87729b9ef4a1048cc7d205f..3eb169f15842c04723b51cbe9e63d47b9f5d156e 100644 --- a/block/blk-mq-cpumap.c +++ b/block/blk-mq-cpumap.c @@ -16,11 +16,6 @@ static int cpu_to_queue_index(unsigned int nr_queues, const int cpu) { - /* - * Non present CPU will be mapped to queue index 0. - */ - if (!cpu_present(cpu)) - return 0; return cpu % nr_queues; } diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index de294d775acfa413854c109eeaa08b9b6bdfd354..d95439154556d58a6d33742c921112d144de09e3 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -704,7 +704,11 @@ static ssize_t blk_mq_debugfs_write(struct file *file, const char __user *buf, const struct blk_mq_debugfs_attr *attr = m->private; void *data = d_inode(file->f_path.dentry->d_parent)->i_private; - if (!attr->write) + /* + * Attributes that only implement .seq_ops are read-only and 'attr' is + * the same with 'data' in this case. + */ + if (attr == data || !attr->write) return -EPERM; return attr->write(data, buf, count, ppos); diff --git a/block/blk-mq.c b/block/blk-mq.c index f1fb126a3be5830bcbe479fa3f9bd811722e0d0a..007f966113640bc1c1f3295e856eaf3b75ae0829 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1143,9 +1143,27 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) /* * We should be running this queue from one of the CPUs that * are mapped to it. + * + * There are at least two related races now between setting + * hctx->next_cpu from blk_mq_hctx_next_cpu() and running + * __blk_mq_run_hw_queue(): + * + * - hctx->next_cpu is found offline in blk_mq_hctx_next_cpu(), + * but later it becomes online, then this warning is harmless + * at all + * + * - hctx->next_cpu is found online in blk_mq_hctx_next_cpu(), + * but later it becomes offline, then the warning can't be + * triggered, and we depend on blk-mq timeout handler to + * handle dispatched requests to this hctx */ - WARN_ON(!cpumask_test_cpu(raw_smp_processor_id(), hctx->cpumask) && - cpu_online(hctx->next_cpu)); + if (!cpumask_test_cpu(raw_smp_processor_id(), hctx->cpumask) && + cpu_online(hctx->next_cpu)) { + printk(KERN_WARNING "run queue from wrong CPU %d, hctx %s\n", + raw_smp_processor_id(), + cpumask_empty(hctx->cpumask) ? "inactive": "active"); + dump_stack(); + } /* * We can't run the queue inline with ints disabled. Ensure that @@ -1928,7 +1946,8 @@ static void blk_mq_exit_hctx(struct request_queue *q, { blk_mq_debugfs_unregister_hctx(hctx); - blk_mq_tag_idle(hctx); + if (blk_mq_hw_queue_mapped(hctx)) + blk_mq_tag_idle(hctx); if (set->ops->exit_request) set->ops->exit_request(set, hctx->fq->flush_rq, hctx_idx); @@ -2314,6 +2333,9 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set, struct blk_mq_hw_ctx **hctxs = q->queue_hw_ctx; blk_mq_sysfs_unregister(q); + + /* protect against switching io scheduler */ + mutex_lock(&q->sysfs_lock); for (i = 0; i < set->nr_hw_queues; i++) { int node; @@ -2358,6 +2380,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set, } } q->nr_hw_queues = i; + mutex_unlock(&q->sysfs_lock); blk_mq_sysfs_register(q); } @@ -2528,9 +2551,27 @@ static int blk_mq_alloc_rq_maps(struct blk_mq_tag_set *set) static int blk_mq_update_queue_map(struct blk_mq_tag_set *set) { - if (set->ops->map_queues) + if (set->ops->map_queues) { + int cpu; + /* + * transport .map_queues is usually done in the following + * way: + * + * for (queue = 0; queue < set->nr_hw_queues; queue++) { + * mask = get_cpu_mask(queue) + * for_each_cpu(cpu, mask) + * set->mq_map[cpu] = queue; + * } + * + * When we need to remap, the table has to be cleared for + * killing stale mapping since one CPU may not be mapped + * to any hw queue. + */ + for_each_possible_cpu(cpu) + set->mq_map[cpu] = 0; + return set->ops->map_queues(set); - else + } else return blk_mq_map_queues(set); } diff --git a/block/partitions/msdos.c b/block/partitions/msdos.c index 0af3a3db6fb07dab3ad72bb9e5e8431cea020370..82c44f7df91130fd7a8484f7162a110aaf875a75 100644 --- a/block/partitions/msdos.c +++ b/block/partitions/msdos.c @@ -301,7 +301,9 @@ static void parse_bsd(struct parsed_partitions *state, continue; bsd_start = le32_to_cpu(p->p_offset); bsd_size = le32_to_cpu(p->p_size); - if (memcmp(flavour, "bsd\0", 4) == 0) + /* FreeBSD has relative offset if C partition offset is zero */ + if (memcmp(flavour, "bsd\0", 4) == 0 && + le32_to_cpu(l->d_partitions[2].p_offset) == 0) bsd_start += offset; if (offset == bsd_start && size == bsd_size) /* full parent partition, we have it already */ diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..36c8a933f84367e0d1fa541613b61c8479539086 --- /dev/null +++ b/build.config.cuttlefish.x86_64 @@ -0,0 +1,15 @@ +ARCH=x86_64 +BRANCH=android-4.14 +CLANG_TRIPLE=x86_64-linux-gnu- +CROSS_COMPILE=x86_64-linux-androidkernel- +DEFCONFIG=x86_64_cuttlefish_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +POST_DEFCONFIG_CMDS="check_defconfig" +CLANG_PREBUILT_BIN=prebuilts/clang/host/linux-x86/clang-4630689/bin +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" diff --git a/crypto/Makefile b/crypto/Makefile index d953cc1d57c4d50e7ab1ef48e15eb66c41a7b3e7..2ae78fb2a39016dc11124c3bc2ea175ac6442e1f 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_CRYPTO_TWOFISH_COMMON) += twofish_common.o obj-$(CONFIG_CRYPTO_SERPENT) += serpent_generic.o CFLAGS_serpent_generic.o := $(call cc-option,-fsched-pressure) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149 obj-$(CONFIG_CRYPTO_AES) += aes_generic.o +CFLAGS_aes_generic.o := $(call cc-ifversion, -ge, 0701, -Os) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83356 obj-$(CONFIG_CRYPTO_AES_TI) += aes_ti.o obj-$(CONFIG_CRYPTO_CAMELLIA) += camellia_generic.o obj-$(CONFIG_CRYPTO_CAST_COMMON) += cast_common.o diff --git a/crypto/ahash.c b/crypto/ahash.c index f75b5c1f71521c5ee73ff9137fc2242338708726..3980e9e45289e6639ab82e23072fca60d210759c 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -92,13 +92,14 @@ int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err) if (nbytes && walk->offset & alignmask && !err) { walk->offset = ALIGN(walk->offset, alignmask + 1); - walk->data += walk->offset; - nbytes = min(nbytes, ((unsigned int)(PAGE_SIZE)) - walk->offset); walk->entrylen -= nbytes; - return nbytes; + if (nbytes) { + walk->data += walk->offset; + return nbytes; + } } if (walk->flags & CRYPTO_ALG_ASYNC) diff --git a/crypto/drbg.c b/crypto/drbg.c index 70018397e59abf10d125864f5770c41cd13f2021..6c3221313753ab0802b8b463a3b5b42061690cf7 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -1134,8 +1134,10 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg) if (!drbg) return; kzfree(drbg->Vbuf); + drbg->Vbuf = NULL; drbg->V = NULL; kzfree(drbg->Cbuf); + drbg->Cbuf = NULL; drbg->C = NULL; kzfree(drbg->scratchpadbuf); drbg->scratchpadbuf = NULL; diff --git a/crypto/lrw.c b/crypto/lrw.c index eb681e9fe5743fb4b539622bf90cbe966f7219c3..fdba6dd6db635c4b98b6406e07f175a56c7aec1f 100644 --- a/crypto/lrw.c +++ b/crypto/lrw.c @@ -313,7 +313,7 @@ static void exit_crypt(struct skcipher_request *req) rctx->left = 0; if (rctx->ext) - kfree(rctx->ext); + kzfree(rctx->ext); } static int do_encrypt(struct skcipher_request *req, int err) diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 014790b9215988b00fe093eba0d4ac3042187491..b0de033a529986b391ca6c6a2ff3889afdf7d0fa 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -548,7 +548,7 @@ static const struct akcipher_testvec rsa_tv_template[] = { static const struct akcipher_testvec pkcs1pad_rsa_tv_template[] = { { .key = - "\x30\x82\x03\x1f\x02\x01\x10\x02\x82\x01\x01\x00\xd7\x1e\x77\x82" + "\x30\x82\x03\x1f\x02\x01\x00\x02\x82\x01\x01\x00\xd7\x1e\x77\x82" "\x8c\x92\x31\xe7\x69\x02\xa2\xd5\x5c\x78\xde\xa2\x0c\x8f\xfe\x28" "\x59\x31\xdf\x40\x9c\x60\x61\x06\xb9\x2f\x62\x40\x80\x76\xcb\x67" "\x4a\xb5\x59\x56\x69\x17\x07\xfa\xf9\x4c\xbd\x6c\x37\x7a\x46\x7d" @@ -597,8 +597,8 @@ static const struct akcipher_testvec pkcs1pad_rsa_tv_template[] = { "\xfe\xf8\x27\x1b\xd6\x55\x60\x5e\x48\xb7\x6d\x9a\xa8\x37\xf9\x7a" "\xde\x1b\xcd\x5d\x1a\x30\xd4\xe9\x9e\x5b\x3c\x15\xf8\x9c\x1f\xda" "\xd1\x86\x48\x55\xce\x83\xee\x8e\x51\xc7\xde\x32\x12\x47\x7d\x46" - "\xb8\x35\xdf\x41\x02\x01\x30\x02\x01\x30\x02\x01\x30\x02\x01\x30" - "\x02\x01\x30", + "\xb8\x35\xdf\x41\x02\x01\x00\x02\x01\x00\x02\x01\x00\x02\x01\x00" + "\x02\x01\x00", .key_len = 804, /* * m is SHA256 hash of following message: diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 032ae44710e5d877770ce746f2412393f2171189..a2be3fd2c72b3d4b84cbd695ab871798fc2a626f 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -465,6 +465,8 @@ static int acpi_lpss_create_device(struct acpi_device *adev, acpi_dev_free_resource_list(&resource_list); if (!pdata->mmio_base) { + /* Avoid acpi_bus_attach() instantiating a pdev for this dev. */ + adev->pnp.type.platform_id = 0; /* Skip the device, but continue the namespace scan. */ ret = 0; goto err_out; diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 0972ec0e2eb8cb867868401dcf1af2446a7f9696..dbdd460a995865618c784840d0728fd0933d1560 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -80,8 +80,8 @@ MODULE_PARM_DESC(report_key_events, static bool device_id_scheme = false; module_param(device_id_scheme, bool, 0444); -static bool only_lcd = false; -module_param(only_lcd, bool, 0444); +static int only_lcd = -1; +module_param(only_lcd, int, 0444); static int register_count; static DEFINE_MUTEX(register_count_mutex); @@ -2123,6 +2123,25 @@ static int __init intel_opregion_present(void) return opregion; } +static bool dmi_is_desktop(void) +{ + const char *chassis_type; + + chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); + if (!chassis_type) + return false; + + if (!strcmp(chassis_type, "3") || /* 3: Desktop */ + !strcmp(chassis_type, "4") || /* 4: Low Profile Desktop */ + !strcmp(chassis_type, "5") || /* 5: Pizza Box */ + !strcmp(chassis_type, "6") || /* 6: Mini Tower */ + !strcmp(chassis_type, "7") || /* 7: Tower */ + !strcmp(chassis_type, "11")) /* 11: Main Server Chassis */ + return true; + + return false; +} + int acpi_video_register(void) { int ret = 0; @@ -2136,6 +2155,20 @@ int acpi_video_register(void) goto leave; } + /* + * We're seeing a lot of bogus backlight interfaces on newer machines + * without a LCD such as desktops, servers and HDMI sticks. Checking + * the lcd flag fixes this, so enable this on any machines which are + * win8 ready (where we also prefer the native backlight driver, so + * normally the acpi_video code should not register there anyways). + */ + if (only_lcd == -1) { + if (dmi_is_desktop() && acpi_osi_is_win8()) + only_lcd = true; + else + only_lcd = false; + } + dmi_check_system(video_dmi_table); ret = acpi_bus_register_driver(&acpi_video_bus); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index b6d58cc58f5fa29d3f628bf02331a706553c8bfb..f0348e388d01b8a561a085042ecdd903a5642bbc 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -146,6 +146,12 @@ int acpi_bus_get_status(struct acpi_device *device) return 0; } + /* Battery devices must have their deps met before calling _STA */ + if (acpi_device_is_battery(device) && device->dep_unmet) { + acpi_set_device_status(device, 0); + return 0; + } + status = acpi_bus_get_status_handle(device->handle, &sta); if (ACPI_FAILURE(status)) return -ENODEV; diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index ef1856b15488be10cf81f8abfa20c630f461768d..891b0921a3077aefebd0014a848608f2ba26cfdb 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -557,7 +557,8 @@ static int acpi_button_remove(struct acpi_device *device) return 0; } -static int param_set_lid_init_state(const char *val, struct kernel_param *kp) +static int param_set_lid_init_state(const char *val, + const struct kernel_param *kp) { int result = 0; @@ -575,7 +576,8 @@ static int param_set_lid_init_state(const char *val, struct kernel_param *kp) return result; } -static int param_get_lid_init_state(char *buffer, struct kernel_param *kp) +static int param_get_lid_init_state(char *buffer, + const struct kernel_param *kp) { switch (lid_init_state) { case ACPI_BUTTON_LID_INIT_OPEN: diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index df842465634a9de096a7a21f2bf74f85528bfc65..30a5729565575f83cb02700ac2050f35abab5e5d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1516,7 +1516,7 @@ static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events) } acpi_handle_info(ec->handle, - "GPE=0x%lx, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", + "GPE=0x%x, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", ec->gpe, ec->command_addr, ec->data_addr); return ret; } @@ -1927,6 +1927,9 @@ static int acpi_ec_suspend_noirq(struct device *dev) ec->reference_count >= 1) acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); + if (acpi_sleep_no_ec_events()) + acpi_ec_enter_noirq(ec); + return 0; } @@ -1934,6 +1937,9 @@ static int acpi_ec_resume_noirq(struct device *dev) { struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); + if (acpi_sleep_no_ec_events()) + acpi_ec_leave_noirq(ec); + if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && ec->reference_count >= 1) acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); @@ -1956,7 +1962,8 @@ static const struct dev_pm_ops acpi_ec_pm = { SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume) }; -static int param_set_event_clearing(const char *val, struct kernel_param *kp) +static int param_set_event_clearing(const char *val, + const struct kernel_param *kp) { int result = 0; @@ -1974,7 +1981,8 @@ static int param_set_event_clearing(const char *val, struct kernel_param *kp) return result; } -static int param_get_event_clearing(char *buffer, struct kernel_param *kp) +static int param_get_event_clearing(char *buffer, + const struct kernel_param *kp) { switch (ec_event_clearing) { case ACPI_EC_EVT_TIMING_STATUS: diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c index 6c7dd7af789e453ce3d16ae80b849ef38bc4a3c0..dd70d6c2bca03eb227fe834fa14e33636f842d48 100644 --- a/drivers/acpi/ec_sys.c +++ b/drivers/acpi/ec_sys.c @@ -128,7 +128,7 @@ static int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) return -ENOMEM; } - if (!debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe)) + if (!debugfs_create_x32("gpe", 0444, dev_dir, &first_ec->gpe)) goto error; if (!debugfs_create_bool("use_global_lock", 0444, dev_dir, &first_ec->global_lock)) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ede83d38beed50b7889e86c2d046641f158ae641..2cd2ae152ab73350fdea678091c22834c11ad722 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -159,7 +159,7 @@ static inline void acpi_early_processor_osc(void) {} -------------------------------------------------------------------------- */ struct acpi_ec { acpi_handle handle; - unsigned long gpe; + u32 gpe; unsigned long command_addr; unsigned long data_addr; bool global_lock; diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index f14b4326e855bc5f55d77c195037b5ce3ca5312e..d56822f58ab124143baef6fa2a5322db5dcef0f0 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1022,8 +1022,11 @@ static ssize_t scrub_show(struct device *dev, if (nd_desc) { struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); + mutex_lock(&acpi_desc->init_mutex); rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, - (work_busy(&acpi_desc->work)) ? "+\n" : "\n"); + work_busy(&acpi_desc->work) + && !acpi_desc->cancel ? "+\n" : "\n"); + mutex_unlock(&acpi_desc->init_mutex); } device_unlock(dev); return rc; @@ -2313,7 +2316,7 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, struct acpi_nfit_system_address *spa = nfit_spa->spa; struct nd_blk_region_desc *ndbr_desc; struct nfit_mem *nfit_mem; - int blk_valid = 0, rc; + int rc; if (!nvdimm) { dev_err(acpi_desc->dev, "spa%d dimm: %#x not found\n", @@ -2333,15 +2336,14 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, if (!nfit_mem || !nfit_mem->bdw) { dev_dbg(acpi_desc->dev, "spa%d %s missing bdw\n", spa->range_index, nvdimm_name(nvdimm)); - } else { - mapping->size = nfit_mem->bdw->capacity; - mapping->start = nfit_mem->bdw->start_address; - ndr_desc->num_lanes = nfit_mem->bdw->windows; - blk_valid = 1; + break; } + mapping->size = nfit_mem->bdw->capacity; + mapping->start = nfit_mem->bdw->start_address; + ndr_desc->num_lanes = nfit_mem->bdw->windows; ndr_desc->mapping = mapping; - ndr_desc->num_mappings = blk_valid; + ndr_desc->num_mappings = 1; ndbr_desc = to_blk_region_desc(ndr_desc); ndbr_desc->enable = acpi_nfit_blk_region_enable; ndbr_desc->do_io = acpi_desc->blk_do_io; @@ -2748,15 +2750,21 @@ static void acpi_nfit_scrub(struct work_struct *work) static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) { struct nfit_spa *nfit_spa; - int rc; - list_for_each_entry(nfit_spa, &acpi_desc->spas, list) - if (nfit_spa_type(nfit_spa->spa) == NFIT_SPA_DCR) { - /* BLK regions don't need to wait for ars results */ - rc = acpi_nfit_register_region(acpi_desc, nfit_spa); - if (rc) - return rc; - } + list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { + int rc, type = nfit_spa_type(nfit_spa->spa); + + /* PMEM and VMEM will be registered by the ARS workqueue */ + if (type == NFIT_SPA_PM || type == NFIT_SPA_VOLATILE) + continue; + /* BLK apertures belong to BLK region registration below */ + if (type == NFIT_SPA_BDW) + continue; + /* BLK regions don't need to wait for ARS results */ + rc = acpi_nfit_register_region(acpi_desc, nfit_spa); + if (rc) + return rc; + } acpi_desc->ars_start_flags = 0; if (!acpi_desc->cancel) diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 18b72eec350764813dcde187cdd665e3d21ba191..c7cf48ad5cb9dafc42826a26fedd229733cb87ef 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -159,7 +159,7 @@ void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) { int ret; - if (ignore_ppc) { + if (ignore_ppc || !pr->performance) { /* * Only when it is notification event, the _OST object * will be evaluated. Otherwise it is skipped. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 2f2f50322ffb7f0c2ed74a0a542c1a6ad5383b89..c0984d33c4c856dae04fc4eb7b111e24d287a120 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1568,6 +1568,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); acpi_init_coherency(device); + /* Assume there are unmet deps until acpi_device_dep_initialize() runs */ + device->dep_unmet = 1; } void acpi_device_add_finalize(struct acpi_device *device) @@ -1591,6 +1593,14 @@ static int acpi_add_single_object(struct acpi_device **child, } acpi_init_device_object(device, handle, type, sta); + /* + * For ACPI_BUS_TYPE_DEVICE getting the status is delayed till here so + * that we can call acpi_bus_get_status() and use its quirk handling. + * Note this must be done before the get power-/wakeup_dev-flags calls. + */ + if (type == ACPI_BUS_TYPE_DEVICE) + acpi_bus_get_status(device); + acpi_bus_get_power_flags(device); acpi_bus_get_wakeup_device_flags(device); @@ -1663,9 +1673,11 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type, return -ENODEV; *type = ACPI_BUS_TYPE_DEVICE; - status = acpi_bus_get_status_handle(handle, sta); - if (ACPI_FAILURE(status)) - *sta = 0; + /* + * acpi_add_single_object updates this once we've an acpi_device + * so that acpi_bus_get_status' quirk handling can be used. + */ + *sta = 0; break; case ACPI_TYPE_PROCESSOR: *type = ACPI_BUS_TYPE_PROCESSOR; @@ -1763,6 +1775,8 @@ static void acpi_device_dep_initialize(struct acpi_device *adev) acpi_status status; int i; + adev->dep_unmet = 0; + if (!acpi_has_method(adev->handle, "_DEP")) return; diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 0fd57bf33524a46938396210eabfad57226100f6..9e728a1494f64131195808bdc5068d82e3f2bb6b 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -230,7 +230,8 @@ module_param_cb(trace_method_name, ¶m_ops_trace_method, &trace_method_name, module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644); module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644); -static int param_set_trace_state(const char *val, struct kernel_param *kp) +static int param_set_trace_state(const char *val, + const struct kernel_param *kp) { acpi_status status; const char *method = trace_method_name; @@ -266,7 +267,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp) return 0; } -static int param_get_trace_state(char *buffer, struct kernel_param *kp) +static int param_get_trace_state(char *buffer, const struct kernel_param *kp) { if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED)) return sprintf(buffer, "disable"); @@ -295,7 +296,8 @@ MODULE_PARM_DESC(aml_debug_output, "To enable/disable the ACPI Debug Object output."); /* /sys/module/acpi/parameters/acpica_version */ -static int param_get_acpica_version(char *buffer, struct kernel_param *kp) +static int param_get_acpica_version(char *buffer, + const struct kernel_param *kp) { int result; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 601e5d372887d548c89e6e1dd8cae5533a2e3927..43587ac680e472afe6ac5cbb0bbbfa3b0c9f93e7 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -219,6 +219,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = { "3570R/370R/470R/450R/510R/4450RV"), }, }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */ + .callback = video_detect_force_video, + .ident = "SAMSUNG 670Z5E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"), + }, + }, { /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ .callback = video_detect_force_video, diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 88313da1407e33dc8a982375631212a3a80fcbe4..8a99fbe5759fe316974f1c9e25f3615b2c0b1683 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -69,11 +69,12 @@ static ssize_t driver_override_show(struct device *_dev, struct device_attribute *attr, char *buf) { struct amba_device *dev = to_amba_device(_dev); + ssize_t len; - if (!dev->driver_override) - return 0; - - return sprintf(buf, "%s\n", dev->driver_override); + device_lock(_dev); + len = sprintf(buf, "%s\n", dev->driver_override); + device_unlock(_dev); + return len; } static ssize_t driver_override_store(struct device *_dev, @@ -81,7 +82,7 @@ static ssize_t driver_override_store(struct device *_dev, const char *buf, size_t count) { struct amba_device *dev = to_amba_device(_dev); - char *driver_override, *old = dev->driver_override, *cp; + char *driver_override, *old, *cp; /* We need to keep extra room for a newline */ if (count >= (PAGE_SIZE - 1)) @@ -95,12 +96,15 @@ static ssize_t driver_override_store(struct device *_dev, if (cp) *cp = '\0'; + device_lock(_dev); + old = dev->driver_override; if (strlen(driver_override)) { dev->driver_override = driver_override; } else { kfree(driver_override); dev->driver_override = NULL; } + device_unlock(_dev); kfree(old); diff --git a/drivers/android/binder.c b/drivers/android/binder.c index ac976864b3d1401cd89ea5628f589d510e7a5832..751777fd64bb7274376ca110c2cea32b2eaca19b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -151,7 +151,7 @@ static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait); static int binder_stop_on_user_error; static int binder_set_stop_on_user_error(const char *val, - struct kernel_param *kp) + const struct kernel_param *kp) { int ret; @@ -2999,6 +2999,14 @@ static void binder_transaction(struct binder_proc *proc, else return_error = BR_DEAD_REPLY; mutex_unlock(&context->context_mgr_node_lock); + if (target_node && target_proc == proc) { + binder_user_error("%d:%d got transaction to context manager from process owning it\n", + proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; + goto err_invalid_target_handle; + } } if (!target_node) { /* diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index ae8c0043e8c470514c6afc4a8f873298e92dd423..b7fe1bb7e0391288f9fca78774a25288daf2bc1e 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -158,9 +158,9 @@ int detect_share_cap_flag(void) if (!policy) return 0; - if (cpumask_equal(topology_sibling_cpumask(cpu), + if (cpumask_equal(cpu_cpu_mask(cpu), policy->related_cpus)) { - share_cap_level = share_cap_thread; + share_cap_level = share_cap_die; continue; } @@ -170,9 +170,9 @@ int detect_share_cap_flag(void) continue; } - if (cpumask_equal(cpu_cpu_mask(cpu), + if (cpumask_equal(topology_sibling_cpumask(cpu), policy->related_cpus)) { - share_cap_level = share_cap_die; + share_cap_level = share_cap_thread; continue; } } @@ -268,7 +268,7 @@ int topology_smt_flags(void) if (asym_cpucap == asym_thread) flags |= SD_ASYM_CPUCAPACITY; - if (share_cap == share_cap_thread) + if (share_cap >= share_cap_thread) flags |= SD_SHARE_CAP_STATES; return flags; @@ -281,7 +281,7 @@ int topology_core_flags(void) if (asym_cpucap == asym_core) flags |= SD_ASYM_CPUCAPACITY; - if (share_cap == share_cap_core) + if (share_cap >= share_cap_core) flags |= SD_SHARE_CAP_STATES; return flags; @@ -294,7 +294,7 @@ int topology_cpu_flags(void) if (asym_cpucap == asym_die) flags |= SD_ASYM_CPUCAPACITY; - if (share_cap == share_cap_die) + if (share_cap >= share_cap_die) flags |= SD_SHARE_CAP_STATES; return flags; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 1d60b58a8c19ec6ac5f0a04fc119a685100f4619..8ccb67c3c1e6797fcd05050ab456f1babd478d5b 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -530,7 +530,36 @@ memory_probe_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(probe, S_IWUSR, NULL, memory_probe_store); -#endif + +#ifdef CONFIG_MEMORY_HOTREMOVE +static ssize_t +memory_remove_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u64 phys_addr; + int nid, ret; + unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block; + + ret = kstrtoull(buf, 0, &phys_addr); + if (ret) + return ret; + + if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1)) + return -EINVAL; + + nid = memory_add_physaddr_to_nid(phys_addr); + ret = lock_device_hotplug_sysfs(); + if (ret) + return ret; + + remove_memory(nid, phys_addr, + MIN_MEMORY_BLOCK_SIZE * sections_per_block); + unlock_device_hotplug(); + return count; +} +static DEVICE_ATTR(remove, S_IWUSR, NULL, memory_remove_store); +#endif /* CONFIG_MEMORY_HOTREMOVE */ +#endif /* CONFIG_ARCH_MEMORY_PROBE */ #ifdef CONFIG_MEMORY_FAILURE /* @@ -790,6 +819,9 @@ bool is_memblock_offlined(struct memory_block *mem) static struct attribute *memory_root_attrs[] = { #ifdef CONFIG_ARCH_MEMORY_PROBE &dev_attr_probe.attr, +#ifdef CONFIG_MEMORY_HOTREMOVE + &dev_attr_remove.attr, +#endif #endif #ifdef CONFIG_MEMORY_FAILURE diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 70f8904f46a31d4df75b99c5abbfe9cb83904aa2..b3b78079aa9f9e0a69c9f59540e68cb1c1bfcb73 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2206,6 +2206,38 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, return 0; } +static int genpd_iterate_idle_states(struct device_node *dn, + struct genpd_power_state *states) +{ + int ret; + struct of_phandle_iterator it; + struct device_node *np; + int i = 0; + + ret = of_count_phandle_with_args(dn, "domain-idle-states", NULL); + if (ret <= 0) + return ret; + + /* Loop over the phandles until all the requested entry is found */ + of_for_each_phandle(&it, ret, dn, "domain-idle-states", NULL, 0) { + np = it.node; + if (!of_match_node(idle_state_match, np)) + continue; + if (states) { + ret = genpd_parse_state(&states[i], np); + if (ret) { + pr_err("Parsing idle state node %pOF failed with err %d\n", + np, ret); + of_node_put(np); + return ret; + } + } + i++; + } + + return i; +} + /** * of_genpd_parse_idle_states: Return array of idle states for the genpd. * @@ -2215,49 +2247,31 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, * * Returns the device states parsed from the OF node. The memory for the states * is allocated by this function and is the responsibility of the caller to - * free the memory after use. + * free the memory after use. If no domain idle states is found it returns + * -EINVAL and in case of errors, a negative error code. */ int of_genpd_parse_idle_states(struct device_node *dn, struct genpd_power_state **states, int *n) { struct genpd_power_state *st; - struct device_node *np; - int i = 0; - int err, ret; - int count; - struct of_phandle_iterator it; - const struct of_device_id *match_id; + int ret; - count = of_count_phandle_with_args(dn, "domain-idle-states", NULL); - if (count <= 0) - return -EINVAL; + ret = genpd_iterate_idle_states(dn, NULL); + if (ret <= 0) + return ret < 0 ? ret : -EINVAL; - st = kcalloc(count, sizeof(*st), GFP_KERNEL); + st = kcalloc(ret, sizeof(*st), GFP_KERNEL); if (!st) return -ENOMEM; - /* Loop over the phandles until all the requested entry is found */ - of_for_each_phandle(&it, err, dn, "domain-idle-states", NULL, 0) { - np = it.node; - match_id = of_match_node(idle_state_match, np); - if (!match_id) - continue; - ret = genpd_parse_state(&st[i++], np); - if (ret) { - pr_err - ("Parsing idle state node %pOF failed with err %d\n", - np, ret); - of_node_put(np); - kfree(st); - return ret; - } + ret = genpd_iterate_idle_states(dn, st); + if (ret <= 0) { + kfree(st); + return ret < 0 ? ret : -EINVAL; } - *n = i; - if (!i) - kfree(st); - else - *states = st; + *states = st; + *n = ret; return 0; } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 635f26b61a87a00318bb7f39e709095dff09cc37..6ea0115f69e8db383593871331b8fc75111f265d 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -132,7 +132,11 @@ extern int pm_async_enabled; /* drivers/base/power/main.c */ extern struct list_head dpm_list; /* The active device list */ +#ifdef CONFIG_QCOM_SHOW_RESUME_IRQ extern int msm_show_resume_irq_mask; +#else +#define msm_show_resume_irq_mask 0 +#endif static inline struct device *to_device(struct list_head *entry) { diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index ae0429827f31052fff58362131e171f33ff27a17..67c50738834b73160dd375cca84e6306a7fd0b22 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c @@ -323,7 +323,8 @@ void dev_pm_arm_wake_irq(struct wake_irq *wirq) return; if (device_may_wakeup(wirq->dev)) { - if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) + if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED && + !pm_runtime_status_suspended(wirq->dev)) enable_irq(wirq->irq); enable_irq_wake(wirq->irq); @@ -345,7 +346,8 @@ void dev_pm_disarm_wake_irq(struct wake_irq *wirq) if (device_may_wakeup(wirq->dev)) { disable_irq_wake(wirq->irq); - if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) + if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED && + !pm_runtime_status_suspended(wirq->dev)) disable_irq_nosync(wirq->irq); } } diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b9a779a4a739cda497351be2b2dc51d69f35c022..efdadd153abef7f41c9a5d6319557ebc9e8ff298 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1739,7 +1739,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; - if (map->max_raw_write && map->max_raw_write > val_len) + if (map->max_raw_write && map->max_raw_write < val_len) return -E2BIG; map->lock(map->lock_arg); diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 2d7178f7754edddf06278e46e87cc9aefc6d4427..16965964873e9a8a26d43d907d282cc9cba09d1d 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef CONFIG_BLK_DEV_RAM_DAX #include #include @@ -449,6 +450,7 @@ static struct brd_device *brd_alloc(int i) disk->flags = GENHD_FL_EXT_DEVT; sprintf(disk->disk_name, "ram%d", i); set_capacity(disk, rd_size * 2); + disk->queue->backing_dev_info->capabilities |= BDI_CAP_SYNCHRONOUS_IO; #ifdef CONFIG_BLK_DEV_RAM_DAX queue_flag_set_unlocked(QUEUE_FLAG_DAX, brd->brd_queue); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 19858a146f30e1ab4ca716fcdd3a3e3da29ea07c..7548521566223bc76206ab68d67bd73f031113b4 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1098,11 +1098,15 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if (info->lo_encrypt_type) { unsigned int type = info->lo_encrypt_type; - if (type >= MAX_LO_CRYPT) - return -EINVAL; + if (type >= MAX_LO_CRYPT) { + err = -EINVAL; + goto exit; + } xfer = xfer_funcs[type]; - if (xfer == NULL) - return -EINVAL; + if (xfer == NULL) { + err = -EINVAL; + goto exit; + } } else xfer = NULL; diff --git a/drivers/block/swim.c b/drivers/block/swim.c index 84434d3ea19b8f3a7500972219b952a6b84072a2..e88d50f75a4a3ecf62e4a086e2676731243aa386 100644 --- a/drivers/block/swim.c +++ b/drivers/block/swim.c @@ -110,7 +110,7 @@ struct iwm { /* Select values for swim_select and swim_readbit */ #define READ_DATA_0 0x074 -#define TWOMEG_DRIVE 0x075 +#define ONEMEG_DRIVE 0x075 #define SINGLE_SIDED 0x076 #define DRIVE_PRESENT 0x077 #define DISK_IN 0x170 @@ -118,9 +118,9 @@ struct iwm { #define TRACK_ZERO 0x172 #define TACHO 0x173 #define READ_DATA_1 0x174 -#define MFM_MODE 0x175 +#define GCR_MODE 0x175 #define SEEK_COMPLETE 0x176 -#define ONEMEG_MEDIA 0x177 +#define TWOMEG_MEDIA 0x177 /* Bits in handshake register */ @@ -612,7 +612,6 @@ static void setup_medium(struct floppy_state *fs) struct floppy_struct *g; fs->disk_in = 1; fs->write_protected = swim_readbit(base, WRITE_PROT); - fs->type = swim_readbit(base, ONEMEG_MEDIA); if (swim_track00(base)) printk(KERN_ERR @@ -620,6 +619,9 @@ static void setup_medium(struct floppy_state *fs) swim_track00(base); + fs->type = swim_readbit(base, TWOMEG_MEDIA) ? + HD_MEDIA : DD_MEDIA; + fs->head_number = swim_readbit(base, SINGLE_SIDED) ? 1 : 2; get_floppy_geometry(fs, 0, &g); fs->total_secs = g->size; fs->secpercyl = g->head * g->sect; @@ -646,7 +648,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) swim_write(base, setup, S_IBM_DRIVE | S_FCLK_DIV2); udelay(10); - swim_drive(base, INTERNAL_DRIVE); + swim_drive(base, fs->location); swim_motor(base, ON); swim_action(base, SETMFM); if (fs->ejected) @@ -656,6 +658,8 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) goto out; } + set_capacity(fs->disk, fs->total_secs); + if (mode & FMODE_NDELAY) return 0; @@ -727,14 +731,9 @@ static int floppy_ioctl(struct block_device *bdev, fmode_t mode, if (copy_to_user((void __user *) param, (void *) &floppy_type, sizeof(struct floppy_struct))) return -EFAULT; - break; - - default: - printk(KERN_DEBUG "SWIM floppy_ioctl: unknown cmd %d\n", - cmd); - return -ENOSYS; + return 0; } - return 0; + return -ENOTTY; } static int floppy_getgeo(struct block_device *bdev, struct hd_geometry *geo) @@ -795,7 +794,7 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data) struct swim_priv *swd = data; int drive = (*part & 3); - if (drive > swd->floppy_count) + if (drive >= swd->floppy_count) return NULL; *part = 0; @@ -813,10 +812,9 @@ static int swim_add_floppy(struct swim_priv *swd, enum drive_location location) swim_motor(base, OFF); - if (swim_readbit(base, SINGLE_SIDED)) - fs->head_number = 1; - else - fs->head_number = 2; + fs->type = HD_MEDIA; + fs->head_number = 2; + fs->ref_count = 0; fs->ejected = 1; @@ -834,10 +832,12 @@ static int swim_floppy_init(struct swim_priv *swd) /* scan floppy drives */ swim_drive(base, INTERNAL_DRIVE); - if (swim_readbit(base, DRIVE_PRESENT)) + if (swim_readbit(base, DRIVE_PRESENT) && + !swim_readbit(base, ONEMEG_DRIVE)) swim_add_floppy(swd, INTERNAL_DRIVE); swim_drive(base, EXTERNAL_DRIVE); - if (swim_readbit(base, DRIVE_PRESENT)) + if (swim_readbit(base, DRIVE_PRESENT) && + !swim_readbit(base, ONEMEG_DRIVE)) swim_add_floppy(swd, EXTERNAL_DRIVE); /* register floppy drives */ @@ -861,7 +861,6 @@ static int swim_floppy_init(struct swim_priv *swd) &swd->lock); if (!swd->unit[drive].disk->queue) { err = -ENOMEM; - put_disk(swd->unit[drive].disk); goto exit_put_disks; } blk_queue_bounce_limit(swd->unit[drive].disk->queue, @@ -911,7 +910,7 @@ static int swim_probe(struct platform_device *dev) goto out; } - swim_base = ioremap(res->start, resource_size(res)); + swim_base = (struct swim __iomem *)res->start; if (!swim_base) { ret = -ENOMEM; goto out_release_io; @@ -923,7 +922,7 @@ static int swim_probe(struct platform_device *dev) if (!get_swim_mode(swim_base)) { printk(KERN_INFO "SWIM device not found !\n"); ret = -ENODEV; - goto out_iounmap; + goto out_release_io; } /* set platform driver data */ @@ -931,7 +930,7 @@ static int swim_probe(struct platform_device *dev) swd = kzalloc(sizeof(struct swim_priv), GFP_KERNEL); if (!swd) { ret = -ENOMEM; - goto out_iounmap; + goto out_release_io; } platform_set_drvdata(dev, swd); @@ -945,8 +944,6 @@ static int swim_probe(struct platform_device *dev) out_kfree: kfree(swd); -out_iounmap: - iounmap(swim_base); out_release_io: release_mem_region(res->start, resource_size(res)); out: @@ -974,8 +971,6 @@ static int swim_remove(struct platform_device *dev) for (drive = 0; drive < swd->floppy_count; drive++) floppy_eject(&swd->unit[drive]); - iounmap(swd->base); - res = platform_get_resource(dev, IORESOURCE_MEM, 0); if (res) release_mem_region(res->start, resource_size(res)); diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 9f931f8f6b4ced797175c95bf77fe455469ef463..0d7527c6825a913f3ff4f75e03d49602be73cacc 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -148,7 +148,7 @@ struct swim3 { #define MOTOR_ON 2 #define RELAX 3 /* also eject in progress */ #define READ_DATA_0 4 -#define TWOMEG_DRIVE 5 +#define ONEMEG_DRIVE 5 #define SINGLE_SIDED 6 /* drive or diskette is 4MB type? */ #define DRIVE_PRESENT 7 #define DISK_IN 8 @@ -156,9 +156,9 @@ struct swim3 { #define TRACK_ZERO 10 #define TACHO 11 #define READ_DATA_1 12 -#define MFM_MODE 13 +#define GCR_MODE 13 #define SEEK_COMPLETE 14 -#define ONEMEG_MEDIA 15 +#define TWOMEG_MEDIA 15 /* Definitions of values used in writing and formatting */ #define DATA_ESCAPE 0x99 diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index f149d3e612341d1f5e70797a8f62d0330fa3973d..88564222f4737f162d21a7df59f8b0317f932fad 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -122,14 +122,6 @@ static inline bool is_partial_io(struct bio_vec *bvec) } #endif -static void zram_revalidate_disk(struct zram *zram) -{ - revalidate_disk(zram->disk); - /* revalidate_disk reset the BDI_CAP_STABLE_WRITES so set again */ - zram->disk->queue->backing_dev_info->capabilities |= - BDI_CAP_STABLE_WRITES; -} - /* * Check if request is within bounds and aligned on zram logical blocks. */ @@ -1373,7 +1365,8 @@ static ssize_t disksize_store(struct device *dev, zram->comp = comp; zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); - zram_revalidate_disk(zram); + + revalidate_disk(zram->disk); up_write(&zram->init_lock); return len; @@ -1420,7 +1413,7 @@ static ssize_t reset_store(struct device *dev, /* Make sure all the pending I/O are finished */ fsync_bdev(bdev); zram_reset_device(zram); - zram_revalidate_disk(zram); + revalidate_disk(zram->disk); bdput(bdev); mutex_lock(&bdev->bd_mutex); @@ -1539,6 +1532,7 @@ static int zram_add(void) /* zram devices sort of resembles non-rotational disks */ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue); + /* * To ensure that we always get PAGE_SIZE aligned * and n*PAGE_SIZED sized I/O requests. @@ -1563,6 +1557,8 @@ static int zram_add(void) if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE) blk_queue_max_write_zeroes_sectors(zram->disk->queue, UINT_MAX); + zram->disk->queue->backing_dev_info->capabilities |= + (BDI_CAP_STABLE_WRITES | BDI_CAP_SYNCHRONOUS_IO); add_disk(zram->disk); ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj, diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c index 6792e045c7f0499259c08449dcbcd20ab6cc94d6..92ea0135d363e09fabc9d11b0d1e44b08f7a3597 100644 --- a/drivers/bluetooth/btfm_slim.c +++ b/drivers/bluetooth/btfm_slim.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -137,6 +137,14 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, prop.dataf = ((rates == 48000) || (rates == 44100) || (rates == 88200) || (rates == 96000)) ? SLIM_CH_DATAF_NOT_DEFINED : SLIM_CH_DATAF_LPCM_AUDIO; + + /* for feedback channel PCM bit should not be set */ + if (btfm_feedback_ch_setting) { + BTFMSLIM_DBG("port open for feedback ch, not setting PCM bit"); + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + /* reset so that next port open sets the data format properly */ + btfm_feedback_ch_setting = 0; + } prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; prop.ratem = ((rates == 44100) || (rates == 88200)) ? (rates/11025) : (rates/4000); diff --git a/drivers/bluetooth/btfm_slim.h b/drivers/bluetooth/btfm_slim.h index cc9d14d3de43f682ac82018f563c7633181ba875..fc653fde261210d2075ffd062e0b42ecafb77461 100644 --- a/drivers/bluetooth/btfm_slim.h +++ b/drivers/bluetooth/btfm_slim.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -78,6 +78,8 @@ struct btfmslim { uint8_t rxport, uint8_t enable); }; +extern int btfm_feedback_ch_setting; + /** * btfm_slim_hw_init: Initialize slimbus slave device * Returns: diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c index 19b2e281ad7a26988605f19204c0d3b823e505a1..b85e9123ee81c2aca71d397d95dc8fd22c7b8a1f 100644 --- a/drivers/bluetooth/btfm_slim_codec.c +++ b/drivers/bluetooth/btfm_slim_codec.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,7 +27,7 @@ #include static int bt_soc_enable_status; - +int btfm_feedback_ch_setting; static int btfm_slim_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) @@ -54,10 +54,27 @@ static int bt_soc_status_put(struct snd_kcontrol *kcontrol, return 1; } +static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = btfm_feedback_ch_setting; + return 1; +} + +static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + btfm_feedback_ch_setting = ucontrol->value.integer.value[0]; + return 1; +} + static const struct snd_kcontrol_new status_controls[] = { SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0, bt_soc_status_get, - bt_soc_status_put) + bt_soc_status_put), + SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0, + btfm_get_feedback_ch_setting, + btfm_put_feedback_ch_setting) }; @@ -372,9 +389,11 @@ static struct snd_soc_dai_driver btfmslim_dai[] = { .capture = { .stream_name = "SCO TX Capture", /* 8 KHz or 16 KHz */ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 + | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 + | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ - .rate_max = 16000, + .rate_max = 96000, .rate_min = 8000, .channels_min = 1, .channels_max = 1, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b2c0306f97eda77325f13431678ccc45037e248b..e9dff868c028022a423f9bb16fc76b1e36990247 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -277,6 +277,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe09f), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0489, 0xe0a2), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME }, /* Broadcom BCM2035 */ diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 73d2d88ddc03987cf4275d51b2fb1a952a924d15..32527bdf4b50995445196b8074c96033c9123615 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -694,22 +694,6 @@ static const struct acpi_gpio_mapping acpi_bcm_int_first_gpios[] = { #ifdef CONFIG_ACPI /* IRQ polarity of some chipsets are not defined correctly in ACPI table. */ static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = { - { - .ident = "Asus T100TA", - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, - "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), - }, - }, - { - .ident = "Asus T100CHI", - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, - "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100CHI"), - }, - }, { /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */ .ident = "Lenovo ThinkPad 8", .matches = { @@ -730,7 +714,9 @@ static int bcm_resource(struct acpi_resource *ares, void *data) switch (ares->type) { case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: irq = &ares->data.extended_irq; - dev->irq_active_low = irq->polarity == ACPI_ACTIVE_LOW; + if (irq->polarity != ACPI_ACTIVE_LOW) + dev_info(&dev->pdev->dev, "ACPI Interrupt resource is active-high, this is usually wrong, treating the IRQ as active-low\n"); + dev->irq_active_low = true; break; case ACPI_RESOURCE_TYPE_GPIO: diff --git a/drivers/bus/mhi/core/mhi_boot.c b/drivers/bus/mhi/core/mhi_boot.c index 43efe15a7d384c964cb6d1db7bcfc0a765c646a0..47276a3ed03e94f6d227c71dc12b250c3558e4bd 100644 --- a/drivers/bus/mhi/core/mhi_boot.c +++ b/drivers/bus/mhi/core/mhi_boot.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -42,6 +43,97 @@ static void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, } } +/* collect rddm during kernel panic */ +static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) +{ + int ret; + struct mhi_buf *mhi_buf; + u32 sequence_id; + u32 rx_status; + enum MHI_EE ee; + struct image_info *rddm_image = mhi_cntrl->rddm_image; + const u32 delayus = 100; + u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus; + void __iomem *base = mhi_cntrl->bhi; + + MHI_LOG("Entered with pm_state:%s dev_state:%s ee:%s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state), + TO_MHI_STATE_STR(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(mhi_cntrl->ee)); + + /* + * This should only be executing during a kernel panic, we expect all + * other cores to shutdown while we're collecting rddm buffer. After + * returning from this function, we expect device to reset. + * + * Normaly, we would read/write pm_state only after grabbing + * pm_lock, since we're in a panic, skipping it. + */ + + if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) + return -EIO; + + /* + * There is no gurantee this state change would take effect since + * we're setting it w/o grabbing pmlock, it's best effort + */ + mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT; + /* update should take the effect immediately */ + smp_wmb(); + + /* setup the RX vector table */ + mhi_rddm_prepare(mhi_cntrl, rddm_image); + mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1]; + + MHI_LOG("Starting BHIe programming for RDDM\n"); + + mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS, + upper_32_bits(mhi_buf->dma_addr)); + + mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS, + lower_32_bits(mhi_buf->dma_addr)); + + mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len); + sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK; + + if (unlikely(!sequence_id)) + sequence_id = 1; + + + mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS, + BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT, + sequence_id); + + MHI_LOG("Trigger device into RDDM mode\n"); + mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR); + + MHI_LOG("Waiting for image download completion\n"); + while (retry--) { + ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, + BHIE_RXVECSTATUS_STATUS_BMSK, + BHIE_RXVECSTATUS_STATUS_SHFT, + &rx_status); + if (ret) + return -EIO; + + if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) { + MHI_LOG("RDDM successfully collected\n"); + return 0; + } + + udelay(delayus); + } + + ee = mhi_get_exec_env(mhi_cntrl); + ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status); + + MHI_ERR("Did not complete RDDM transfer\n"); + MHI_ERR("Current EE:%s\n", TO_MHI_EXEC_STR(ee)); + MHI_ERR("RXVEC_STATUS:0x%x, ret:%d\n", rx_status, ret); + + return -EIO; +} + /* download ramdump image from device */ int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic) { @@ -56,6 +148,9 @@ int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic) if (!rddm_image) return -ENOMEM; + if (in_panic) + return __mhi_download_rddm_in_panic(mhi_cntrl); + MHI_LOG("Waiting for device to enter RDDM state from EE:%s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee)); diff --git a/drivers/bus/mhi/core/mhi_dtr.c b/drivers/bus/mhi/core/mhi_dtr.c index bc1735937d371b5b142bb3add931665706f5ab63..a70ec2553ba02a91107199d3a005952eb72d2b8e 100644 --- a/drivers/bus/mhi/core/mhi_dtr.c +++ b/drivers/bus/mhi/core/mhi_dtr.c @@ -119,8 +119,13 @@ long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg) } EXPORT_SYMBOL(mhi_ioctl); -static void mhi_dtr_xfer_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) +static void mhi_dtr_dl_xfer_cb(struct mhi_device *mhi_dev, + struct mhi_result *mhi_result) +{ +} + +static void mhi_dtr_ul_xfer_cb(struct mhi_device *mhi_dev, + struct mhi_result *mhi_result) { struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan; @@ -163,8 +168,8 @@ static struct mhi_driver mhi_dtr_driver = { .id_table = mhi_dtr_table, .remove = mhi_dtr_remove, .probe = mhi_dtr_probe, - .ul_xfer_cb = mhi_dtr_xfer_cb, - .dl_xfer_cb = mhi_dtr_xfer_cb, + .ul_xfer_cb = mhi_dtr_ul_xfer_cb, + .dl_xfer_cb = mhi_dtr_dl_xfer_cb, .driver = { .name = "MHI_DTR", .owner = THIS_MODULE, diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index 4d5cb568fe881308b93451f98ec41515d7ecbc78..b30143823631a0b9f098fbc997d4d5c10f86a6d4 100644 --- a/drivers/bus/mhi/core/mhi_init.c +++ b/drivers/bus/mhi/core/mhi_init.c @@ -616,7 +616,19 @@ int mhi_device_configure(struct mhi_device *mhi_dev, struct mhi_chan_ctxt *ch_ctxt; int er_index, chan; - mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan : mhi_dev->dl_chan; + switch (dir) { + case DMA_TO_DEVICE: + mhi_chan = mhi_dev->ul_chan; + break; + case DMA_BIDIRECTIONAL: + case DMA_FROM_DEVICE: + case DMA_NONE: + mhi_chan = mhi_dev->dl_chan; + break; + default: + return -EINVAL; + } + er_index = mhi_chan->er_index; chan = mhi_chan->chan; @@ -823,6 +835,22 @@ static int of_parse_ch_cfg(struct mhi_controller *mhi_cntrl, mhi_chan->offload_ch = !!(bit_cfg & MHI_CH_CFG_BIT_OFFLOAD_CH); mhi_chan->db_cfg.reset_req = !!(bit_cfg & MHI_CH_CFG_BIT_DBMODE_RESET_CH); + mhi_chan->pre_alloc = !!(bit_cfg & MHI_CH_CFG_BIT_PRE_ALLOC); + mhi_chan->auto_start = !!(bit_cfg & MHI_CH_CFG_BIT_AUTO_START); + + if (mhi_chan->pre_alloc && + (mhi_chan->dir != DMA_FROM_DEVICE || + mhi_chan->xfer_type != MHI_XFER_BUFFER)) + goto error_chan_cfg; + + /* bi-dir and dirctionless channels must be a offload chan */ + if ((mhi_chan->dir == DMA_BIDIRECTIONAL || + mhi_chan->dir == DMA_NONE) && !mhi_chan->offload_ch) + goto error_chan_cfg; + + /* if mhi host allocate the buffers then client cannot queue */ + if (mhi_chan->pre_alloc) + mhi_chan->queue_xfer = mhi_queue_nop; ret = of_property_read_string_index(of_node, "mhi,chan-names", i, &mhi_chan->name); @@ -1112,26 +1140,30 @@ static int mhi_driver_probe(struct device *dev) struct mhi_event *mhi_event; struct mhi_chan *ul_chan = mhi_dev->ul_chan; struct mhi_chan *dl_chan = mhi_dev->dl_chan; - bool offload_ch = ((ul_chan && ul_chan->offload_ch) || - (dl_chan && dl_chan->offload_ch)); + bool auto_start = false; + int ret; - /* all offload channels require status_cb to be defined */ - if (offload_ch) { - if (!mhi_dev->status_cb) + + if (ul_chan) { + /* lpm notification require status_cb */ + if (ul_chan->lpm_notify && !mhi_drv->status_cb) return -EINVAL; - mhi_dev->status_cb = mhi_drv->status_cb; - } - if (ul_chan && !offload_ch) { - if (!mhi_drv->ul_xfer_cb) + if (!ul_chan->offload_ch && !mhi_drv->ul_xfer_cb) return -EINVAL; + ul_chan->xfer_cb = mhi_drv->ul_xfer_cb; + mhi_dev->status_cb = mhi_drv->status_cb; + auto_start = ul_chan->auto_start; } - if (dl_chan && !offload_ch) { - if (!mhi_drv->dl_xfer_cb) + if (dl_chan) { + if (dl_chan->lpm_notify && !mhi_drv->status_cb) return -EINVAL; - dl_chan->xfer_cb = mhi_drv->dl_xfer_cb; + + if (!dl_chan->offload_ch && !mhi_drv->dl_xfer_cb) + return -EINVAL; + mhi_event = &mhi_cntrl->mhi_event[dl_chan->er_index]; /* @@ -1141,10 +1173,20 @@ static int mhi_driver_probe(struct device *dev) */ if (mhi_event->cl_manage && !mhi_drv->status_cb) return -EINVAL; + + dl_chan->xfer_cb = mhi_drv->dl_xfer_cb; + + /* ul & dl uses same status cb */ mhi_dev->status_cb = mhi_drv->status_cb; + auto_start = (auto_start || dl_chan->auto_start); } - return mhi_drv->probe(mhi_dev, mhi_dev->id); + ret = mhi_drv->probe(mhi_dev, mhi_dev->id); + + if (!ret && auto_start) + mhi_prepare_for_transfer(mhi_dev); + + return ret; } static int mhi_driver_remove(struct device *dev) @@ -1200,6 +1242,9 @@ static int mhi_driver_remove(struct device *dev) !mhi_chan->offload_ch) mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); + /* remove associated device */ + mhi_chan->mhi_dev = NULL; + mutex_unlock(&mhi_chan->mutex); } diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h index 6b4815f48dbebc22177458e69560c33a919f55a7..bd26ad6b257cb8558cdf0ca1557706b4ecfa5c9d 100644 --- a/drivers/bus/mhi/core/mhi_internal.h +++ b/drivers/bus/mhi/core/mhi_internal.h @@ -339,6 +339,8 @@ enum MHI_CH_CFG { #define MHI_CH_CFG_BIT_LPM_NOTIFY BIT(0) /* require LPM notification */ #define MHI_CH_CFG_BIT_OFFLOAD_CH BIT(1) /* satellite mhi devices */ #define MHI_CH_CFG_BIT_DBMODE_RESET_CH BIT(2) /* require db mode to reset */ +#define MHI_CH_CFG_BIT_PRE_ALLOC BIT(3) /* host allocate buffers for DL */ +#define MHI_CH_CFG_BIT_AUTO_START BIT(4) /* host auto start channels */ enum MHI_EV_CFG { MHI_EV_CFG_ELEMENTS = 0, @@ -565,6 +567,8 @@ struct mhi_chan { bool lpm_notify; bool configured; bool offload_ch; + bool pre_alloc; + bool auto_start; /* functions that generate the transfer ring elements */ int (*gen_tre)(struct mhi_controller *, struct mhi_chan *, void *, void *, size_t, enum MHI_FLAGS); @@ -654,6 +658,7 @@ void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr, void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd); void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); +void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, enum MHI_STATE state); /* memory allocation methods */ static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c index 82f09418fca379a438d4c47ae86e4777f1748ce3..4695fe7605ebc6e0f6e710e2fdfe9f5a91d2c9e7 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.c @@ -23,6 +23,9 @@ #include #include "mhi_internal.h" +static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, + struct mhi_chan *mhi_chan); + int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, @@ -458,32 +461,16 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, int mhi_destroy_device(struct device *dev, void *data) { struct mhi_device *mhi_dev; - struct mhi_driver *mhi_drv; struct mhi_controller *mhi_cntrl; - struct mhi_chan *mhi_chan; - int dir; if (dev->bus != &mhi_bus_type) return 0; mhi_dev = to_mhi_device(dev); - mhi_drv = to_mhi_driver(dev->driver); mhi_cntrl = mhi_dev->mhi_cntrl; MHI_LOG("destroy device for chan:%s\n", mhi_dev->chan_name); - for (dir = 0; dir < 2; dir++) { - mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; - - if (!mhi_chan) - continue; - - /* remove device associated with the channel */ - mutex_lock(&mhi_chan->mutex); - mhi_chan->mhi_dev = NULL; - mutex_unlock(&mhi_chan->mutex); - } - /* notify the client and remove the device from mhi bus */ device_del(dev); put_device(dev); @@ -522,12 +509,18 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl) if (!mhi_dev) return; - if (mhi_chan->dir == DMA_TO_DEVICE) { + switch (mhi_chan->dir) { + case DMA_TO_DEVICE: mhi_dev->ul_chan = mhi_chan; mhi_dev->ul_chan_id = mhi_chan->chan; mhi_dev->ul_xfer = mhi_chan->queue_xfer; mhi_dev->ul_event_id = mhi_chan->er_index; - } else { + break; + case DMA_NONE: + case DMA_BIDIRECTIONAL: + mhi_dev->ul_chan_id = mhi_chan->chan; + case DMA_FROM_DEVICE: + /* we use dl_chan for offload channels */ mhi_dev->dl_chan = mhi_chan; mhi_dev->dl_chan_id = mhi_chan->chan; mhi_dev->dl_xfer = mhi_chan->queue_xfer; @@ -661,6 +654,22 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, mhi_cntrl->wake_put(mhi_cntrl, false); read_unlock_bh(&mhi_cntrl->pm_lock); } + + /* + * recycle the buffer if buffer is pre-allocated, + * if there is error, not much we can do apart from + * dropping the packet + */ + if (mhi_chan->pre_alloc) { + if (mhi_queue_buf(mhi_chan->mhi_dev, mhi_chan, + buf_info->cb_buf, + buf_info->len, MHI_EOT)) { + MHI_ERR( + "Error recycling buffer for chan:%d\n", + mhi_chan->chan); + kfree(buf_info->cb_buf); + } + } }; break; } /* CC_EOT */ @@ -1082,6 +1091,40 @@ static int __mhi_prepare_channel(struct mhi_controller *mhi_cntrl, mhi_chan->ch_state = MHI_CH_STATE_ENABLED; write_unlock_irq(&mhi_chan->lock); + /* pre allocate buffer for xfer ring */ + if (mhi_chan->pre_alloc) { + int nr_el = get_nr_avail_ring_elements(mhi_cntrl, + &mhi_chan->tre_ring); + + while (nr_el--) { + void *buf; + + buf = kmalloc(MHI_MAX_MTU, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto error_pre_alloc; + } + + /* prepare transfer descriptors */ + ret = mhi_chan->gen_tre(mhi_cntrl, mhi_chan, buf, buf, + MHI_MAX_MTU, MHI_EOT); + if (ret) { + MHI_ERR("Chan:%d error prepare buffer\n", + mhi_chan->chan); + kfree(buf); + goto error_pre_alloc; + } + } + + read_lock_bh(&mhi_cntrl->pm_lock); + if (MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state)) { + read_lock_irq(&mhi_chan->lock); + mhi_ring_chan_db(mhi_cntrl, mhi_chan); + read_unlock_irq(&mhi_chan->lock); + } + read_unlock_bh(&mhi_cntrl->pm_lock); + } + read_lock_bh(&mhi_cntrl->pm_lock); mhi_cntrl->wake_put(mhi_cntrl, false); read_unlock_bh(&mhi_cntrl->pm_lock); @@ -1104,6 +1147,17 @@ static int __mhi_prepare_channel(struct mhi_controller *mhi_cntrl, error_init_chan: mutex_unlock(&mhi_chan->mutex); + return ret; + +error_pre_alloc: + + read_lock_bh(&mhi_cntrl->pm_lock); + mhi_cntrl->wake_put(mhi_cntrl, false); + read_unlock_bh(&mhi_cntrl->pm_lock); + + mutex_unlock(&mhi_chan->mutex); + __mhi_unprepare_channel(mhi_cntrl, mhi_chan); + return ret; } @@ -1115,6 +1169,7 @@ void mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) struct mhi_ring *ev_ring, *buf_ring, *tre_ring; unsigned long flags; int chan = mhi_chan->chan; + struct mhi_result result; /* nothing to reset, client don't queue buffers */ if (mhi_chan->offload_ch) @@ -1159,6 +1214,8 @@ void mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) /* reset any pending buffers */ buf_ring = &mhi_chan->buf_ring; tre_ring = &mhi_chan->tre_ring; + result.transaction_status = -ENOTCONN; + result.bytes_xferd = 0; while (tre_ring->rp != tre_ring->wp) { struct mhi_buf_info *buf_info = buf_ring->rp; @@ -1169,6 +1226,13 @@ void mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) buf_info->len, buf_info->dir); mhi_del_ring_element(mhi_cntrl, buf_ring); mhi_del_ring_element(mhi_cntrl, tre_ring); + + if (mhi_chan->pre_alloc) { + kfree(buf_info->cb_buf); + } else { + result.buf_addr = buf_info->cb_buf; + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); + } } read_unlock_bh(&mhi_cntrl->pm_lock); @@ -1324,7 +1388,7 @@ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) struct mhi_chan *mhi_chan; for (dir = 0; dir < 2; dir++) { - mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; + mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan; if (!mhi_chan) continue; @@ -1341,7 +1405,7 @@ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) error_open_chan: for (--dir; dir >= 0; dir--) { - mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; + mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan; if (!mhi_chan) continue; diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index 5dee57f117fdf9bac0b66c61f72839928d18c86d..c476a204f28f8db2ce907eec00ebf6e4b27db9c3 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.c @@ -161,8 +161,7 @@ enum MHI_PM_STATE __must_check mhi_tryset_pm_state( return mhi_cntrl->pm_state; } -static void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, - enum MHI_STATE state) +void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, enum MHI_STATE state) { if (state == MHI_STATE_RESET) { mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, diff --git a/drivers/bus/mhi/devices/Kconfig b/drivers/bus/mhi/devices/Kconfig index 40f964d4c522216d1d28affd690d7fc412ed3376..83b96733cc350986fd990c271692221d939d2477 100644 --- a/drivers/bus/mhi/devices/Kconfig +++ b/drivers/bus/mhi/devices/Kconfig @@ -7,4 +7,13 @@ config MHI_NETDEV MHI based net device driver for transferring IP traffic between host and modem. By enabling this driver, clients can transfer data using standard network interface. + +config MHI_UCI + tristate "MHI UCI" + depends on MHI_BUS + help + MHI based uci driver is for transferring data between host and + modem using standard file operations from user space. Open, read, + write, ioctl, and close operations are supported by this driver. + endmenu diff --git a/drivers/bus/mhi/devices/Makefile b/drivers/bus/mhi/devices/Makefile index ee12a642bc730dcc11c3346b5a74af966c8b45db..300eed18af8aa9b1d81548df74c02f489b755c3a 100644 --- a/drivers/bus/mhi/devices/Makefile +++ b/drivers/bus/mhi/devices/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MHI_NETDEV) +=mhi_netdev.o +obj-$(CONFIG_MHI_UCI) +=mhi_uci.o diff --git a/drivers/bus/mhi/devices/mhi_netdev.c b/drivers/bus/mhi/devices/mhi_netdev.c index abd8dd660646f4114fc38b6cd4f920e2b650e3b9..af5c5db4fc05e8913c3dcb5bf5386c89d9765fb5 100644 --- a/drivers/bus/mhi/devices/mhi_netdev.c +++ b/drivers/bus/mhi/devices/mhi_netdev.c @@ -99,7 +99,6 @@ struct mhi_skb_priv { struct mhi_netdev { int alias; struct mhi_device *mhi_dev; - spinlock_t tx_lock; spinlock_t rx_lock; bool enabled; rwlock_t pm_lock; /* state change lock */ @@ -107,8 +106,6 @@ struct mhi_netdev { struct work_struct alloc_work; int wake; - struct sk_buff_head tx_submitted; - struct sk_buff_head rx_submitted; struct sk_buff_head rx_allocated; u32 mru; @@ -196,18 +193,16 @@ static int mhi_netdev_alloc_skb(struct mhi_netdev *mhi_netdev, gfp_t gfp_t) skb->destructor = mhi_netdev_skb_destructor; spin_lock_bh(&mhi_netdev->rx_lock); - skb_queue_tail(&mhi_netdev->rx_submitted, skb); ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, skb, skb_priv->size, MHI_EOT); + spin_unlock_bh(&mhi_netdev->rx_lock); + if (ret) { - skb_dequeue_tail(&mhi_netdev->rx_submitted); - spin_unlock_bh(&mhi_netdev->rx_lock); MSG_ERR("Failed to queue skb, ret:%d\n", ret); ret = -EIO; goto error_queue; } - spin_unlock_bh(&mhi_netdev->rx_lock); read_unlock_bh(&mhi_netdev->pm_lock); } @@ -273,14 +268,12 @@ static int mhi_netdev_skb_recycle(struct mhi_netdev *mhi_netdev, gfp_t gfp_t) } skb_priv = (struct mhi_skb_priv *)(skb->cb); - skb_queue_tail(&mhi_netdev->rx_submitted, skb); ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, skb, skb_priv->size, MHI_EOT); /* failed to queue buffer */ if (ret) { MSG_ERR("Failed to queue skb, ret:%d\n", ret); - skb_dequeue_tail(&mhi_netdev->rx_submitted); skb_queue_tail(&mhi_netdev->rx_allocated, skb); goto error_queue; } @@ -299,23 +292,11 @@ static void mhi_netdev_dealloc(struct mhi_netdev *mhi_netdev) { struct sk_buff *skb; - skb_queue_purge(&mhi_netdev->tx_submitted); - if (!mhi_netdev->recycle_buf) { - skb_queue_purge(&mhi_netdev->rx_submitted); - } else { - skb = skb_dequeue(&mhi_netdev->rx_submitted); - while (skb) { - skb->destructor = NULL; - kfree_skb(skb); - skb = skb_dequeue(&mhi_netdev->rx_submitted); - } - + skb = skb_dequeue(&mhi_netdev->rx_allocated); + while (skb) { + skb->destructor = NULL; + kfree_skb(skb); skb = skb_dequeue(&mhi_netdev->rx_allocated); - while (skb) { - skb->destructor = NULL; - kfree_skb(skb); - skb = skb_dequeue(&mhi_netdev->rx_allocated); - } } } @@ -431,21 +412,16 @@ static int mhi_netdev_xmit(struct sk_buff *skb, struct net_device *dev) goto mhi_xmit_exit; } - spin_lock_bh(&mhi_netdev->tx_lock); - skb_queue_tail(&mhi_netdev->tx_submitted, skb); res = mhi_queue_transfer(mhi_dev, DMA_TO_DEVICE, skb, skb->len, MHI_EOT); if (res) { MSG_VERB("Failed to queue with reason:%d\n", res); - skb_dequeue_tail(&mhi_netdev->tx_submitted); netif_stop_queue(dev); - spin_unlock_bh(&mhi_netdev->tx_lock); mhi_netdev->stats.tx_full++; res = NETDEV_TX_BUSY; goto mhi_xmit_exit; } - spin_unlock_bh(&mhi_netdev->tx_lock); mhi_netdev->stats.tx_pkts++; mhi_xmit_exit: @@ -639,8 +615,6 @@ static int mhi_netdev_enable_iface(struct mhi_netdev *mhi_netdev) goto net_dev_reg_fail; } - skb_queue_head_init(&mhi_netdev->tx_submitted); - skb_queue_head_init(&mhi_netdev->rx_submitted); skb_queue_head_init(&mhi_netdev->rx_allocated); } @@ -697,21 +671,15 @@ static void mhi_netdev_xfer_ul_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) { struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct sk_buff *skb = skb_dequeue(&mhi_netdev->tx_submitted); + struct sk_buff *skb = mhi_result->buf_addr; struct net_device *ndev = mhi_netdev->ndev; - unsigned long flags; - - MHI_ASSERT(skb != mhi_result->buf_addr, "ooo ul cb"); ndev->stats.tx_packets++; ndev->stats.tx_bytes += skb->len; dev_kfree_skb(skb); - if (netif_queue_stopped(ndev)) { - spin_lock_irqsave(&mhi_netdev->tx_lock, flags); + if (netif_queue_stopped(ndev)) netif_wake_queue(ndev); - spin_unlock_irqrestore(&mhi_netdev->tx_lock, flags); - } } static int mhi_netdev_process_fragment(struct mhi_netdev *mhi_netdev, @@ -739,12 +707,6 @@ static int mhi_netdev_process_fragment(struct mhi_netdev *mhi_netdev, return -ENOMEM; } - /* recycle the skb */ - if (mhi_netdev->recycle_buf) - mhi_netdev_skb_destructor(skb); - else - dev_kfree_skb(skb); - mhi_netdev->stats.rx_frag++; return 0; @@ -754,11 +716,14 @@ static void mhi_netdev_xfer_dl_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) { struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct sk_buff *skb = skb_dequeue(&mhi_netdev->rx_submitted); + struct sk_buff *skb = mhi_result->buf_addr; struct net_device *dev = mhi_netdev->ndev; int ret = 0; - MHI_ASSERT(skb != mhi_result->buf_addr, "ooo dl cb"); + if (mhi_result->transaction_status == -ENOTCONN) { + dev_kfree_skb(skb); + return; + } skb_put(skb, mhi_result->bytes_xferd); dev->stats.rx_packets++; @@ -768,6 +733,13 @@ static void mhi_netdev_xfer_dl_cb(struct mhi_device *mhi_dev, if (mhi_result->transaction_status == -EOVERFLOW || mhi_netdev->frag_skb) { ret = mhi_netdev_process_fragment(mhi_netdev, skb); + + /* recycle the skb */ + if (mhi_netdev->recycle_buf) + mhi_netdev_skb_destructor(skb); + else + dev_kfree_skb(skb); + if (ret) return; } @@ -982,7 +954,6 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev, mhi_netdev->rx_queue = mhi_netdev->recycle_buf ? mhi_netdev_skb_recycle : mhi_netdev_alloc_skb; - spin_lock_init(&mhi_netdev->tx_lock); spin_lock_init(&mhi_netdev->rx_lock); rwlock_init(&mhi_netdev->pm_lock); INIT_WORK(&mhi_netdev->alloc_work, mhi_netdev_alloc_work); diff --git a/drivers/bus/mhi/devices/mhi_uci.c b/drivers/bus/mhi/devices/mhi_uci.c new file mode 100644 index 0000000000000000000000000000000000000000..c8a3fdbfa9178c58d2afefa49b2a9de437bd4301 --- /dev/null +++ b/drivers/bus/mhi/devices/mhi_uci.c @@ -0,0 +1,693 @@ +/* Copyright (c) 2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "mhi" +#define MHI_UCI_DRIVER_NAME "mhi_uci" + +struct uci_chan { + wait_queue_head_t wq; + spinlock_t lock; + struct list_head pending; /* user space waiting to read */ + struct uci_buf *cur_buf; /* current buffer user space reading */ + size_t rx_size; +}; + +struct uci_buf { + void *data; + size_t len; + struct list_head node; +}; + +struct uci_dev { + struct list_head node; + dev_t devt; + struct device *dev; + struct mhi_device *mhi_dev; + const char *chan; + struct mutex mutex; /* sync open and close */ + struct uci_chan ul_chan; + struct uci_chan dl_chan; + size_t mtu; + int ref_count; + bool enabled; + void *ipc_log; +}; + +struct mhi_uci_drv { + struct list_head head; + struct mutex lock; + struct class *class; + int major; + dev_t dev_t; +}; + +enum MHI_DEBUG_LEVEL msg_lvl = MHI_MSG_LVL_ERROR; + +#ifdef CONFIG_MHI_DEBUG + +#define IPC_LOG_LVL (MHI_MSG_LVL_VERBOSE) +#define MHI_UCI_IPC_LOG_PAGES (25) + +#else + +#define IPC_LOG_LVL (MHI_MSG_LVL_ERROR) +#define MHI_UCI_IPC_LOG_PAGES (1) + +#endif + +#ifdef CONFIG_MHI_DEBUG + +#define MSG_VERB(fmt, ...) do { \ + if (msg_lvl <= MHI_MSG_LVL_VERBOSE) \ + pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__); \ + if (uci_dev->ipc_log && (IPC_LOG_LVL <= MHI_MSG_LVL_VERBOSE)) \ + ipc_log_string(uci_dev->ipc_log, "[D][%s] " fmt, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +#else + +#define MSG_VERB(fmt, ...) + +#endif + +#define MSG_LOG(fmt, ...) do { \ + if (msg_lvl <= MHI_MSG_LVL_INFO) \ + pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__); \ + if (uci_dev->ipc_log && (IPC_LOG_LVL <= MHI_MSG_LVL_INFO)) \ + ipc_log_string(uci_dev->ipc_log, "[I][%s] " fmt, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +#define MSG_ERR(fmt, ...) do { \ + if (msg_lvl <= MHI_MSG_LVL_ERROR) \ + pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \ + if (uci_dev->ipc_log && (IPC_LOG_LVL <= MHI_MSG_LVL_ERROR)) \ + ipc_log_string(uci_dev->ipc_log, "[E][%s] " fmt, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +#define MAX_UCI_DEVICES (64) + +static DECLARE_BITMAP(uci_minors, MAX_UCI_DEVICES); +static struct mhi_uci_drv mhi_uci_drv; + +static int mhi_queue_inbound(struct uci_dev *uci_dev) +{ + struct mhi_device *mhi_dev = uci_dev->mhi_dev; + int nr_trbs = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); + size_t mtu = uci_dev->mtu; + void *buf; + struct uci_buf *uci_buf; + int ret = -EIO, i; + + for (i = 0; i < nr_trbs; i++) { + buf = kmalloc(mtu + sizeof(*uci_buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + uci_buf = buf + mtu; + uci_buf->data = buf; + + MSG_VERB("Allocated buf %d of %d size %ld\n", i, nr_trbs, mtu); + + ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, buf, mtu, + MHI_EOT); + if (ret) { + kfree(buf); + MSG_ERR("Failed to queue buffer %d\n", i); + return ret; + } + } + + return ret; +} + +static long mhi_uci_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct uci_dev *uci_dev = file->private_data; + struct mhi_device *mhi_dev = uci_dev->mhi_dev; + long ret = -ERESTARTSYS; + + mutex_lock(&uci_dev->mutex); + if (uci_dev->enabled) + ret = mhi_ioctl(mhi_dev, cmd, arg); + mutex_unlock(&uci_dev->mutex); + + return ret; +} + +static int mhi_uci_release(struct inode *inode, struct file *file) +{ + struct uci_dev *uci_dev = file->private_data; + + mutex_lock(&uci_dev->mutex); + uci_dev->ref_count--; + if (!uci_dev->ref_count) { + struct uci_buf *itr, *tmp; + struct uci_chan *uci_chan; + + MSG_LOG("Last client left, closing node\n"); + + if (uci_dev->enabled) + mhi_unprepare_from_transfer(uci_dev->mhi_dev); + + /* clean inbound channel */ + uci_chan = &uci_dev->dl_chan; + list_for_each_entry_safe(itr, tmp, &uci_chan->pending, node) { + list_del(&itr->node); + kfree(itr->data); + } + if (uci_chan->cur_buf) + kfree(uci_chan->cur_buf->data); + + uci_chan->cur_buf = NULL; + + if (!uci_dev->enabled) { + MSG_LOG("Node is deleted, freeing dev node\n"); + mutex_unlock(&uci_dev->mutex); + mutex_destroy(&uci_dev->mutex); + clear_bit(MINOR(uci_dev->devt), uci_minors); + kfree(uci_dev); + return 0; + } + } + + mutex_unlock(&uci_dev->mutex); + + MSG_LOG("exit: ref_count:%d\n", uci_dev->ref_count); + + return 0; +} + +static unsigned int mhi_uci_poll(struct file *file, poll_table *wait) +{ + struct uci_dev *uci_dev = file->private_data; + struct mhi_device *mhi_dev = uci_dev->mhi_dev; + struct uci_chan *uci_chan; + unsigned int mask = 0; + + poll_wait(file, &uci_dev->dl_chan.wq, wait); + poll_wait(file, &uci_dev->ul_chan.wq, wait); + + uci_chan = &uci_dev->dl_chan; + spin_lock_bh(&uci_chan->lock); + if (!uci_dev->enabled) { + mask = POLLERR; + } else if (!list_empty(&uci_chan->pending) || uci_chan->cur_buf) { + MSG_VERB("Client can read from node\n"); + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_bh(&uci_chan->lock); + + uci_chan = &uci_dev->ul_chan; + spin_lock_bh(&uci_chan->lock); + if (!uci_dev->enabled) { + mask |= POLLERR; + } else if (mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE) > 0) { + MSG_VERB("Client can write to node\n"); + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_bh(&uci_chan->lock); + + MSG_LOG("Client attempted to poll, returning mask 0x%x\n", mask); + + return mask; +} + +static ssize_t mhi_uci_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *offp) +{ + struct uci_dev *uci_dev = file->private_data; + struct mhi_device *mhi_dev = uci_dev->mhi_dev; + struct uci_chan *uci_chan = &uci_dev->ul_chan; + size_t bytes_xfered = 0; + int ret; + + if (!buf || !count) + return -EINVAL; + + /* confirm channel is active */ + spin_lock_bh(&uci_chan->lock); + if (!uci_dev->enabled) { + spin_unlock_bh(&uci_chan->lock); + return -ERESTARTSYS; + } + + MSG_VERB("Enter: to xfer:%lu bytes\n", count); + + while (count) { + size_t xfer_size; + void *kbuf; + enum MHI_FLAGS flags; + + spin_unlock_bh(&uci_chan->lock); + + /* wait for free descriptors */ + ret = wait_event_interruptible(uci_chan->wq, + (!uci_dev->enabled) || + mhi_get_no_free_descriptors + (mhi_dev, DMA_TO_DEVICE) > 0); + + if (ret == -ERESTARTSYS) { + MSG_LOG("Exit signal caught for node\n"); + return -ERESTARTSYS; + } + + xfer_size = min_t(size_t, count, uci_dev->mtu); + kbuf = kmalloc(xfer_size, GFP_KERNEL); + if (!kbuf) { + MSG_ERR("Failed to allocate memory %lu\n", xfer_size); + return -ENOMEM; + } + + ret = copy_from_user(kbuf, buf, xfer_size); + if (unlikely(ret)) { + kfree(kbuf); + return ret; + } + + spin_lock_bh(&uci_chan->lock); + flags = (count - xfer_size) ? MHI_EOB : MHI_EOT; + if (uci_dev->enabled) + ret = mhi_queue_transfer(mhi_dev, DMA_TO_DEVICE, kbuf, + xfer_size, flags); + else + ret = -ERESTARTSYS; + + if (ret) { + kfree(kbuf); + goto sys_interrupt; + } + + bytes_xfered += xfer_size; + count -= xfer_size; + buf += xfer_size; + } + + spin_unlock_bh(&uci_chan->lock); + MSG_VERB("Exit: Number of bytes xferred:%lu\n", bytes_xfered); + + return bytes_xfered; + +sys_interrupt: + spin_unlock_bh(&uci_chan->lock); + + return ret; +} + +static ssize_t mhi_uci_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + struct uci_dev *uci_dev = file->private_data; + struct mhi_device *mhi_dev = uci_dev->mhi_dev; + struct uci_chan *uci_chan = &uci_dev->dl_chan; + struct uci_buf *uci_buf; + char *ptr; + size_t to_copy; + int ret = 0; + + if (!buf) + return -EINVAL; + + MSG_VERB("Client provided buf len:%lu\n", count); + + /* confirm channel is active */ + spin_lock_bh(&uci_chan->lock); + if (!uci_dev->enabled) { + spin_unlock_bh(&uci_chan->lock); + return -ERESTARTSYS; + } + + /* No data available to read, wait */ + if (!uci_chan->cur_buf && list_empty(&uci_chan->pending)) { + MSG_VERB("No data available to read waiting\n"); + + spin_unlock_bh(&uci_chan->lock); + ret = wait_event_interruptible(uci_chan->wq, + (!uci_dev->enabled || + !list_empty(&uci_chan->pending))); + if (ret == -ERESTARTSYS) { + MSG_LOG("Exit signal caught for node\n"); + return -ERESTARTSYS; + } + + spin_lock_bh(&uci_chan->lock); + if (!uci_dev->enabled) { + MSG_LOG("node is disabled\n"); + ret = -ERESTARTSYS; + goto read_error; + } + } + + /* new read, get the next descriptor from the list */ + if (!uci_chan->cur_buf) { + uci_buf = list_first_entry_or_null(&uci_chan->pending, + struct uci_buf, node); + if (unlikely(!uci_buf)) { + ret = -EIO; + goto read_error; + } + + list_del(&uci_buf->node); + uci_chan->cur_buf = uci_buf; + uci_chan->rx_size = uci_buf->len; + MSG_VERB("Got pkt of size:%zu\n", uci_chan->rx_size); + } + + uci_buf = uci_chan->cur_buf; + spin_unlock_bh(&uci_chan->lock); + + /* Copy the buffer to user space */ + to_copy = min_t(size_t, count, uci_chan->rx_size); + ptr = uci_buf->data + (uci_buf->len - uci_chan->rx_size); + ret = copy_to_user(buf, ptr, to_copy); + if (ret) + return ret; + + MSG_VERB("Copied %lu of %lu bytes\n", to_copy, uci_chan->rx_size); + uci_chan->rx_size -= to_copy; + + /* we finished with this buffer, queue it back to hardware */ + if (!uci_chan->rx_size) { + spin_lock_bh(&uci_chan->lock); + uci_chan->cur_buf = NULL; + + if (uci_dev->enabled) + ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, + uci_buf->data, uci_dev->mtu, + MHI_EOT); + else + ret = -ERESTARTSYS; + + if (ret) { + MSG_ERR("Failed to recycle element\n"); + kfree(uci_buf->data); + goto read_error; + } + + spin_unlock_bh(&uci_chan->lock); + } + + MSG_VERB("Returning %lu bytes\n", to_copy); + + return to_copy; + +read_error: + spin_unlock_bh(&uci_chan->lock); + + return ret; +} + +static int mhi_uci_open(struct inode *inode, struct file *filp) +{ + struct uci_dev *uci_dev; + int ret = -EIO; + struct uci_buf *buf_itr, *tmp; + struct uci_chan *dl_chan; + + mutex_lock(&mhi_uci_drv.lock); + list_for_each_entry(uci_dev, &mhi_uci_drv.head, node) { + if (uci_dev->devt == inode->i_rdev) { + ret = 0; + break; + } + } + mutex_unlock(&mhi_uci_drv.lock); + + /* could not find a minor node */ + if (ret) + return ret; + + mutex_lock(&uci_dev->mutex); + if (!uci_dev->enabled) { + MSG_ERR("Node exist, but not in active state!\n"); + goto error_open_chan; + } + + uci_dev->ref_count++; + + MSG_LOG("Node open, ref counts %u\n", uci_dev->ref_count); + + if (uci_dev->ref_count == 1) { + MSG_LOG("Starting channel\n"); + ret = mhi_prepare_for_transfer(uci_dev->mhi_dev); + if (ret) { + MSG_ERR("Error starting transfer channels\n"); + uci_dev->ref_count--; + goto error_open_chan; + } + + ret = mhi_queue_inbound(uci_dev); + if (ret) + goto error_rx_queue; + } + + filp->private_data = uci_dev; + mutex_unlock(&uci_dev->mutex); + + return 0; + + error_rx_queue: + dl_chan = &uci_dev->dl_chan; + mhi_unprepare_from_transfer(uci_dev->mhi_dev); + list_for_each_entry_safe(buf_itr, tmp, &dl_chan->pending, node) { + list_del(&buf_itr->node); + kfree(buf_itr->data); + } + + error_open_chan: + mutex_unlock(&uci_dev->mutex); + + return ret; +} + +static const struct file_operations mhidev_fops = { + .open = mhi_uci_open, + .release = mhi_uci_release, + .read = mhi_uci_read, + .write = mhi_uci_write, + .poll = mhi_uci_poll, + .unlocked_ioctl = mhi_uci_ioctl, +}; + +static void mhi_uci_remove(struct mhi_device *mhi_dev) +{ + struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev); + + MSG_LOG("Enter\n"); + + /* disable the node */ + mutex_lock(&uci_dev->mutex); + spin_lock_irq(&uci_dev->dl_chan.lock); + spin_lock_irq(&uci_dev->ul_chan.lock); + uci_dev->enabled = false; + spin_unlock_irq(&uci_dev->ul_chan.lock); + spin_unlock_irq(&uci_dev->dl_chan.lock); + wake_up(&uci_dev->dl_chan.wq); + wake_up(&uci_dev->ul_chan.wq); + + /* delete the node to prevent new opens */ + device_destroy(mhi_uci_drv.class, uci_dev->devt); + uci_dev->dev = NULL; + mutex_lock(&mhi_uci_drv.lock); + list_del(&uci_dev->node); + mutex_unlock(&mhi_uci_drv.lock); + + /* safe to free memory only if all file nodes are closed */ + if (!uci_dev->ref_count) { + mutex_unlock(&uci_dev->mutex); + mutex_destroy(&uci_dev->mutex); + clear_bit(MINOR(uci_dev->devt), uci_minors); + kfree(uci_dev); + return; + } + + mutex_unlock(&uci_dev->mutex); + MSG_LOG("Exit\n"); +} + +static int mhi_uci_probe(struct mhi_device *mhi_dev, + const struct mhi_device_id *id) +{ + struct uci_dev *uci_dev; + int minor; + char node_name[32]; + int dir; + + uci_dev = kzalloc(sizeof(*uci_dev), GFP_KERNEL); + if (!uci_dev) + return -ENOMEM; + + mutex_init(&uci_dev->mutex); + uci_dev->mhi_dev = mhi_dev; + + minor = find_first_zero_bit(uci_minors, MAX_UCI_DEVICES); + if (minor >= MAX_UCI_DEVICES) { + kfree(uci_dev); + return -ENOSPC; + } + + mutex_lock(&uci_dev->mutex); + mutex_lock(&mhi_uci_drv.lock); + + uci_dev->devt = MKDEV(mhi_uci_drv.major, minor); + uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev, + uci_dev->devt, uci_dev, + DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d", + mhi_dev->dev_id, mhi_dev->domain, + mhi_dev->bus, mhi_dev->slot, "_pipe_", + mhi_dev->ul_chan_id); + set_bit(minor, uci_minors); + + /* create debugging buffer */ + snprintf(node_name, sizeof(node_name), "mhi_uci_%04x_%02u.%02u.%02u_%d", + mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot, + mhi_dev->ul_chan_id); + uci_dev->ipc_log = ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES, + node_name, 0); + + for (dir = 0; dir < 2; dir++) { + struct uci_chan *uci_chan = (dir) ? + &uci_dev->ul_chan : &uci_dev->dl_chan; + spin_lock_init(&uci_chan->lock); + init_waitqueue_head(&uci_chan->wq); + INIT_LIST_HEAD(&uci_chan->pending); + }; + + uci_dev->mtu = id->driver_data; + mhi_device_set_devdata(mhi_dev, uci_dev); + uci_dev->enabled = true; + + list_add(&uci_dev->node, &mhi_uci_drv.head); + mutex_unlock(&mhi_uci_drv.lock); + mutex_unlock(&uci_dev->mutex); + + MSG_LOG("channel:%s successfully probed\n", mhi_dev->chan_name); + + return 0; +}; + +static void mhi_ul_xfer_cb(struct mhi_device *mhi_dev, + struct mhi_result *mhi_result) +{ + struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev); + struct uci_chan *uci_chan = &uci_dev->ul_chan; + + MSG_VERB("status:%d xfer_len:%zu\n", mhi_result->transaction_status, + mhi_result->bytes_xferd); + + kfree(mhi_result->buf_addr); + if (!mhi_result->transaction_status) + wake_up(&uci_chan->wq); +} + +static void mhi_dl_xfer_cb(struct mhi_device *mhi_dev, + struct mhi_result *mhi_result) +{ + struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev); + struct uci_chan *uci_chan = &uci_dev->dl_chan; + unsigned long flags; + struct uci_buf *buf; + + MSG_VERB("status:%d receive_len:%zu\n", mhi_result->transaction_status, + mhi_result->bytes_xferd); + + if (mhi_result->transaction_status == -ENOTCONN) { + kfree(mhi_result->buf_addr); + return; + } + + spin_lock_irqsave(&uci_chan->lock, flags); + buf = mhi_result->buf_addr + uci_dev->mtu; + buf->data = mhi_result->buf_addr; + buf->len = mhi_result->bytes_xferd; + list_add_tail(&buf->node, &uci_chan->pending); + spin_unlock_irqrestore(&uci_chan->lock, flags); + + wake_up(&uci_chan->wq); +} + +/* .driver_data stores max mtu */ +static const struct mhi_device_id mhi_uci_match_table[] = { + { .chan = "LOOPBACK", .driver_data = 0x1000 }, + { .chan = "SAHARA", .driver_data = 0x8000 }, + { .chan = "EFS", .driver_data = 0x1000 }, + { .chan = "QMI0", .driver_data = 0x1000 }, + { .chan = "QMI1", .driver_data = 0x1000 }, + { .chan = "TF", .driver_data = 0x1000 }, + { .chan = "BL", .driver_data = 0x1000 }, + { .chan = "DUN", .driver_data = 0x1000 }, + { NULL }, +}; + +static struct mhi_driver mhi_uci_driver = { + .id_table = mhi_uci_match_table, + .remove = mhi_uci_remove, + .probe = mhi_uci_probe, + .ul_xfer_cb = mhi_ul_xfer_cb, + .dl_xfer_cb = mhi_dl_xfer_cb, + .driver = { + .name = MHI_UCI_DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int mhi_uci_init(void) +{ + int ret; + + ret = register_chrdev(0, MHI_UCI_DRIVER_NAME, &mhidev_fops); + if (ret < 0) + return ret; + + mhi_uci_drv.major = ret; + mhi_uci_drv.class = class_create(THIS_MODULE, MHI_UCI_DRIVER_NAME); + if (IS_ERR(mhi_uci_drv.class)) + return -ENODEV; + + mutex_init(&mhi_uci_drv.lock); + INIT_LIST_HEAD(&mhi_uci_drv.head); + + ret = mhi_driver_register(&mhi_uci_driver); + if (ret) + class_destroy(mhi_uci_drv.class); + + return ret; +} + +module_init(mhi_uci_init); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("MHI_UCI"); +MODULE_DESCRIPTION("MHI UCI Driver"); diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index e36d160c458fdb334556129b9559d147aefb2472..5f7d86509f2f523fc48c7c5fe3dc616cdfafadec 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2374,7 +2374,7 @@ static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi, if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT) return media_changed(cdi, 1); - if ((unsigned int)arg >= cdi->capacity) + if (arg >= cdi->capacity) return -EINVAL; info = kmalloc(sizeof(*info), GFP_KERNEL); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 647e74a3b9e3637b943981d1807aa50a5e2c9171..d96a3842a4002e05d4753bd4765b9c5c3de5513d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -589,6 +589,14 @@ config TILE_SROM source "drivers/char/xillybus/Kconfig" source "drivers/char/diag/Kconfig" +config MSM_FASTCVPD + bool "QTI FASTCVP driver" + depends on QCOM_GLINK + help + This driver exposes APIs to video driver to share + HFI command queue address to CDSP and handle errors + to exit gracefully in case video and cdsp subsystems crash. + config MSM_ADSPRPC tristate "QTI ADSP RPC driver" depends on QCOM_GLINK diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 8127b328b88ff6349c35cac4636a8f8692d3f453..96ecee7cebc04d841a0b0418c5af74cfb91f7a72 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_MSM_ADSPRPC) += adsprpc.o ifdef CONFIG_COMPAT obj-$(CONFIG_MSM_ADSPRPC) += adsprpc_compat.o endif +obj-$(CONFIG_MSM_FASTCVPD) += fastcvpd.o \ No newline at end of file diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 10c29acecfcab03b9837e92fde55330dcb3ae923..d5b76c3cd9bf8eb5702972b4e4551fb3746863d8 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -430,7 +430,7 @@ static void fastrpc_buf_free(struct fastrpc_buf *buf, int cache) int destVM[1] = {VMID_HLOS}; int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; - if (fl->sctx->smmu.cb) + if (fl->sctx->smmu.cb && fl->cid != SDSP_DOMAIN_ID) buf->phys &= ~((uint64_t)fl->sctx->smmu.cb << 32); vmid = fl->apps->channel[fl->cid].vmid; if (vmid) { @@ -754,7 +754,8 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, } map->phys = sg_dma_address(map->table->sgl); if (sess->smmu.cb) { - map->phys += ((uint64_t)sess->smmu.cb << 32); + if (fl->cid != SDSP_DOMAIN_ID) + map->phys += ((uint64_t)sess->smmu.cb << 32); for_each_sg(map->table->sgl, sgl, map->table->nents, sgl_index) map->size += sg_dma_len(sgl); @@ -831,7 +832,7 @@ static int fastrpc_buf_alloc(struct fastrpc_file *fl, size_t size, } if (err) goto bail; - if (fl->sctx->smmu.cb) + if (fl->sctx->smmu.cb && fl->cid != SDSP_DOMAIN_ID) buf->phys += ((uint64_t)fl->sctx->smmu.cb << 32); vmid = fl->apps->channel[fl->cid].vmid; if (vmid) { @@ -1892,7 +1893,8 @@ static int fastrpc_init_process(struct fastrpc_file *fl, if (err && (init->flags == FASTRPC_INIT_CREATE_STATIC)) me->staticpd_flags = 0; if (mem && err) { - if (mem->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) + if (mem->flags == ADSP_MMAP_REMOTE_HEAP_ADDR + && me->channel[fl->cid].rhvm.vmid) hyp_assign_phys(mem->phys, (uint64_t)mem->size, me->channel[fl->cid].rhvm.vmid, me->channel[fl->cid].rhvm.vmcount, @@ -1988,7 +1990,8 @@ static int fastrpc_mmap_on_dsp(struct fastrpc_file *fl, uint32_t flags, desc.arginfo = SCM_ARGS(3); err = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL, TZ_PIL_PROTECT_MEM_SUBSYS_ID), &desc); - } else if (flags == ADSP_MMAP_REMOTE_HEAP_ADDR) { + } else if (flags == ADSP_MMAP_REMOTE_HEAP_ADDR + && me->channel[fl->cid].rhvm.vmid) { VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size, hlosvm, 1, me->channel[fl->cid].rhvm.vmid, me->channel[fl->cid].rhvm.vmperm, @@ -2041,12 +2044,15 @@ static int fastrpc_munmap_on_dsp_rh(struct fastrpc_file *fl, err = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL, TZ_PIL_CLEAR_PROTECT_MEM_SUBSYS_ID), &desc); } else if (map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) { - VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size, + if (me->channel[fl->cid].rhvm.vmid) { + VERIFY(err, !hyp_assign_phys(map->phys, + (uint64_t)map->size, me->channel[fl->cid].rhvm.vmid, me->channel[fl->cid].rhvm.vmcount, destVM, destVMperm, 1)); - if (err) - goto bail; + if (err) + goto bail; + } } bail: @@ -2955,8 +2961,9 @@ static int fastrpc_cb_probe(struct device *dev) struct fastrpc_session_ctx *sess; struct of_phandle_args iommuspec; const char *name; - unsigned int start = 0x80000000; - int err = 0, i; + dma_addr_t start = 0x80000000; + int err = 0; + unsigned int sharedcb_count = 0, cid, i, j; int secure_vmid = VMID_CP_PIXEL; VERIFY(err, NULL != (name = of_get_property(dev->of_node, @@ -2972,6 +2979,7 @@ static int fastrpc_cb_probe(struct device *dev) VERIFY(err, i < NUM_CHANNELS); if (err) goto bail; + cid = i; chan = &gcinfo[i]; VERIFY(err, chan->sesscount < NUM_SESSIONS); if (err) @@ -2982,12 +2990,24 @@ static int fastrpc_cb_probe(struct device *dev) if (err) goto bail; sess = &chan->session[chan->sesscount]; - sess->smmu.cb = iommuspec.args[0] & 0xf; sess->used = 0; sess->smmu.coherent = of_property_read_bool(dev->of_node, "dma-coherent"); sess->smmu.secure = of_property_read_bool(dev->of_node, "qcom,secure-context-bank"); + + /* Software workaround for SMMU interconnect HW bug */ + if (cid == SDSP_DOMAIN_ID) { + sess->smmu.cb = iommuspec.args[0] & 0x3; + VERIFY(err, sess->smmu.cb); + if (err) + goto bail; + start += ((uint64_t)sess->smmu.cb << 32); + dma_set_mask(dev, DMA_BIT_MASK(34)); + } else { + sess->smmu.cb = iommuspec.args[0] & 0xf; + } + if (sess->smmu.secure) start = 0x60000000; VERIFY(err, !IS_ERR_OR_NULL(sess->smmu.mapping = @@ -3004,8 +3024,33 @@ static int fastrpc_cb_probe(struct device *dev) VERIFY(err, !arm_iommu_attach_device(dev, sess->smmu.mapping)); if (err) goto bail; + sess->smmu.dev = dev; sess->smmu.enabled = 1; + if (!sess->smmu.dev->dma_parms) + sess->smmu.dev->dma_parms = devm_kzalloc(sess->smmu.dev, + sizeof(*sess->smmu.dev->dma_parms), GFP_KERNEL); + dma_set_max_seg_size(sess->smmu.dev, DMA_BIT_MASK(32)); + dma_set_seg_boundary(sess->smmu.dev, DMA_BIT_MASK(64)); + + if (of_get_property(dev->of_node, "shared-cb", NULL) != NULL) { + VERIFY(err, !of_property_read_u32(dev->of_node, "shared-cb", + &sharedcb_count)); + if (err) + goto bail; + if (sharedcb_count > 0) { + struct fastrpc_session_ctx *dup_sess; + + for (j = 1; j < sharedcb_count && + chan->sesscount < NUM_SESSIONS; j++) { + chan->sesscount++; + dup_sess = &chan->session[chan->sesscount]; + memcpy(dup_sess, sess, + sizeof(struct fastrpc_session_ctx)); + } + } + } + chan->sesscount++; debugfs_global_file = debugfs_create_file("global", 0644, debugfs_root, NULL, &debugfs_fops); diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 7075b20695e10ec2f7e673a187b25eeff01c44e5..c98d12f76568a92804f6dea4c81a8f1d7041b7b7 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef CONFIG_DIAG_OVER_USB #include #endif @@ -261,7 +262,7 @@ static int diag_dci_init_buffer(struct diag_dci_buffer_t *buffer, int type) switch (type) { case DCI_BUF_PRIMARY: buffer->capacity = IN_BUF_SIZE; - buffer->data = kzalloc(buffer->capacity, GFP_KERNEL); + buffer->data = vzalloc(buffer->capacity); if (!buffer->data) return -ENOMEM; break; @@ -271,7 +272,7 @@ static int diag_dci_init_buffer(struct diag_dci_buffer_t *buffer, int type) break; case DCI_BUF_CMD: buffer->capacity = DIAG_MAX_REQ_SIZE + DCI_BUF_SIZE; - buffer->data = kzalloc(buffer->capacity, GFP_KERNEL); + buffer->data = vzalloc(buffer->capacity); if (!buffer->data) return -ENOMEM; break; @@ -2728,7 +2729,7 @@ static int diag_dci_init_remote(void) create_dci_event_mask_tbl(temp->event_mask_composite); } - partial_pkt.data = kzalloc(MAX_DCI_PACKET_SZ, GFP_KERNEL); + partial_pkt.data = vzalloc(MAX_DCI_PACKET_SZ); if (!partial_pkt.data) return -ENOMEM; @@ -2782,7 +2783,7 @@ int diag_dci_init(void) goto err; if (driver->apps_dci_buf == NULL) { - driver->apps_dci_buf = kzalloc(DCI_BUF_SIZE, GFP_KERNEL); + driver->apps_dci_buf = vzalloc(DCI_BUF_SIZE); if (driver->apps_dci_buf == NULL) goto err; } @@ -2799,12 +2800,12 @@ int diag_dci_init(void) return DIAG_DCI_NO_ERROR; err: pr_err("diag: Could not initialize diag DCI buffers"); - kfree(driver->apps_dci_buf); + vfree(driver->apps_dci_buf); driver->apps_dci_buf = NULL; if (driver->diag_dci_wq) destroy_workqueue(driver->diag_dci_wq); - kfree(partial_pkt.data); + vfree(partial_pkt.data); partial_pkt.data = NULL; mutex_destroy(&driver->dci_mutex); mutex_destroy(&dci_log_mask_mutex); @@ -2824,9 +2825,9 @@ void diag_dci_channel_init(void) void diag_dci_exit(void) { - kfree(partial_pkt.data); + vfree(partial_pkt.data); partial_pkt.data = NULL; - kfree(driver->apps_dci_buf); + vfree(driver->apps_dci_buf); driver->apps_dci_buf = NULL; mutex_destroy(&driver->dci_mutex); mutex_destroy(&dci_log_mask_mutex); @@ -2964,7 +2965,7 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) new_entry->in_service = 0; INIT_LIST_HEAD(&new_entry->list_write_buf); mutex_init(&new_entry->write_buf_mutex); - new_entry->dci_log_mask = kzalloc(DCI_LOG_MASK_SIZE, GFP_KERNEL); + new_entry->dci_log_mask = vzalloc(DCI_LOG_MASK_SIZE); if (!new_entry->dci_log_mask) { pr_err("diag: Unable to create log mask for client, %d", driver->dci_client_id); @@ -2972,14 +2973,14 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) } create_dci_log_mask_tbl(new_entry->dci_log_mask, DCI_LOG_MASK_CLEAN); - new_entry->dci_event_mask = kzalloc(DCI_EVENT_MASK_SIZE, GFP_KERNEL); + new_entry->dci_event_mask = vzalloc(DCI_EVENT_MASK_SIZE); if (!new_entry->dci_event_mask) goto fail_alloc; create_dci_event_mask_tbl(new_entry->dci_event_mask); new_entry->buffers = kcalloc(new_entry->num_buffers, sizeof(struct diag_dci_buf_peripheral_t), - GFP_KERNEL); + GFP_KERNEL); if (!new_entry->buffers) { pr_err("diag: Unable to allocate buffers for peripherals in %s\n", __func__); @@ -3003,7 +3004,7 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) if (!proc_buf->buf_primary) goto fail_alloc; proc_buf->buf_cmd = kzalloc(sizeof(struct diag_dci_buffer_t), - GFP_KERNEL); + GFP_KERNEL); if (!proc_buf->buf_cmd) goto fail_alloc; err = diag_dci_init_buffer(proc_buf->buf_primary, @@ -3036,7 +3037,7 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) if (proc_buf) { mutex_destroy(&proc_buf->health_mutex); if (proc_buf->buf_primary) { - kfree(proc_buf->buf_primary->data); + vfree(proc_buf->buf_primary->data); proc_buf->buf_primary->data = NULL; mutex_destroy( &proc_buf->buf_primary->data_mutex); @@ -3044,7 +3045,7 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) kfree(proc_buf->buf_primary); proc_buf->buf_primary = NULL; if (proc_buf->buf_cmd) { - kfree(proc_buf->buf_cmd->data); + vfree(proc_buf->buf_cmd->data); proc_buf->buf_cmd->data = NULL; mutex_destroy( &proc_buf->buf_cmd->data_mutex); @@ -3053,9 +3054,9 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) proc_buf->buf_cmd = NULL; } } - kfree(new_entry->dci_event_mask); + vfree(new_entry->dci_event_mask); new_entry->dci_event_mask = NULL; - kfree(new_entry->dci_log_mask); + vfree(new_entry->dci_log_mask); new_entry->dci_log_mask = NULL; kfree(new_entry->buffers); new_entry->buffers = NULL; @@ -3090,7 +3091,7 @@ int diag_dci_deinit_client(struct diag_dci_client_tbl *entry) * Clear the client's log and event masks, update the cumulative * masks and send the masks to peripherals */ - kfree(entry->dci_log_mask); + vfree(entry->dci_log_mask); entry->dci_log_mask = NULL; diag_dci_invalidate_cumulative_log_mask(token); if (token == DCI_LOCAL_PROC) @@ -3098,7 +3099,7 @@ int diag_dci_deinit_client(struct diag_dci_client_tbl *entry) ret = dci_ops_tbl[token].send_log_mask(token); if (ret != DIAG_DCI_NO_ERROR) return ret; - kfree(entry->dci_event_mask); + vfree(entry->dci_event_mask); entry->dci_event_mask = NULL; diag_dci_invalidate_cumulative_event_mask(token); if (token == DCI_LOCAL_PROC) @@ -3161,12 +3162,12 @@ int diag_dci_deinit_client(struct diag_dci_client_tbl *entry) } mutex_lock(&proc_buf->buf_primary->data_mutex); - kfree(proc_buf->buf_primary->data); + vfree(proc_buf->buf_primary->data); proc_buf->buf_primary->data = NULL; mutex_unlock(&proc_buf->buf_primary->data_mutex); mutex_lock(&proc_buf->buf_cmd->data_mutex); - kfree(proc_buf->buf_cmd->data); + vfree(proc_buf->buf_cmd->data); proc_buf->buf_cmd->data = NULL; mutex_unlock(&proc_buf->buf_cmd->data_mutex); diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 25e6815e372309edd7b0076c40b0f4b0d54f8854..14177c6cb46088b247e346c7473028e1820db962 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -27,9 +27,6 @@ #define DIAG_SET_FEATURE_MASK(x) (feature_bytes[(x)/8] |= (1 << (x & 0x7))) -#define diag_check_update(x) \ - (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x)))) \ - struct diag_mask_info msg_mask; struct diag_mask_info msg_bt_mask; struct diag_mask_info log_mask; @@ -64,6 +61,20 @@ static const struct diag_ssid_range_t msg_mask_tbl[] = { { .ssid_first = MSG_SSID_25, .ssid_last = MSG_SSID_25_LAST } }; +static int diag_check_update(int md_peripheral, int pid) +{ + int ret; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); + ret = (!info || (info && + (info->peripheral_mask & MD_PERIPHERAL_MASK(md_peripheral)))); + mutex_unlock(&driver->md_session_lock); + + return ret; +} + static int diag_apps_responds(void) { /* @@ -825,7 +836,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); mutex_unlock(&driver->md_session_lock); - if (diag_check_update(APPS_DATA)) + if (diag_check_update(APPS_DATA, pid)) diag_update_userspace_clients(MSG_MASKS_TYPE); /* @@ -849,7 +860,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_MD_SESSIONS; i++) { if (i == APPS_DATA) continue; - if (!diag_check_update(i)) + if (!diag_check_update(i, pid)) continue; if (i > NUM_PERIPHERALS) peripheral = diag_search_peripheral_by_pd(i); @@ -919,7 +930,7 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); mutex_unlock(&driver->md_session_lock); - if (diag_check_update(APPS_DATA)) + if (diag_check_update(APPS_DATA, pid)) diag_update_userspace_clients(MSG_MASKS_TYPE); /* @@ -937,7 +948,7 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_MD_SESSIONS; i++) { if (i == APPS_DATA) continue; - if (!diag_check_update(i)) + if (!diag_check_update(i, pid)) continue; if (i > NUM_PERIPHERALS) peripheral = diag_search_peripheral_by_pd(i); @@ -1027,7 +1038,7 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, mask_info->status = DIAG_CTRL_MASK_VALID; mutex_unlock(&mask_info->lock); mutex_unlock(&driver->md_session_lock); - if (diag_check_update(APPS_DATA)) + if (diag_check_update(APPS_DATA, pid)) diag_update_userspace_clients(EVENT_MASKS_TYPE); /* @@ -1046,7 +1057,7 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_MD_SESSIONS; i++) { if (i == APPS_DATA) continue; - if (!diag_check_update(i)) + if (!diag_check_update(i, pid)) continue; if (i > NUM_PERIPHERALS) peripheral = diag_search_peripheral_by_pd(i); @@ -1098,7 +1109,7 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, } mutex_unlock(&mask_info->lock); mutex_unlock(&driver->md_session_lock); - if (diag_check_update(APPS_DATA)) + if (diag_check_update(APPS_DATA, pid)) diag_update_userspace_clients(EVENT_MASKS_TYPE); /* @@ -1110,7 +1121,7 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_MD_SESSIONS; i++) { if (i == APPS_DATA) continue; - if (!diag_check_update(i)) + if (!diag_check_update(i, pid)) continue; if (i > NUM_PERIPHERALS) peripheral = diag_search_peripheral_by_pd(i); @@ -1373,7 +1384,7 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, } mutex_unlock(&mask_info->lock); mutex_unlock(&driver->md_session_lock); - if (diag_check_update(APPS_DATA)) + if (diag_check_update(APPS_DATA, pid)) diag_update_userspace_clients(LOG_MASKS_TYPE); /* @@ -1404,7 +1415,7 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_MD_SESSIONS; i++) { if (i == APPS_DATA) continue; - if (!diag_check_update(i)) + if (!diag_check_update(i, pid)) continue; if (i > NUM_PERIPHERALS) peripheral = diag_search_peripheral_by_pd(i); @@ -1459,7 +1470,7 @@ static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, } mask_info->status = DIAG_CTRL_MASK_ALL_DISABLED; mutex_unlock(&driver->md_session_lock); - if (diag_check_update(APPS_DATA)) + if (diag_check_update(APPS_DATA, pid)) diag_update_userspace_clients(LOG_MASKS_TYPE); /* @@ -1477,7 +1488,7 @@ static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, for (i = 0; i < NUM_MD_SESSIONS; i++) { if (i == APPS_DATA) continue; - if (!diag_check_update(i)) + if (!diag_check_update(i, pid)) continue; if (i > NUM_PERIPHERALS) peripheral = diag_search_peripheral_by_pd(i); @@ -2044,14 +2055,6 @@ int diag_copy_to_user_msg_mask(char __user *buf, size_t count, __func__, mask_info->ptr, mask_info->update_buf); return -EINVAL; } - mutex_lock(&driver->diag_maskclear_mutex); - if (driver->mask_clear) { - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, - "diag:%s: count = %zu\n", __func__, count); - mutex_unlock(&driver->diag_maskclear_mutex); - return -EIO; - } - mutex_unlock(&driver->diag_maskclear_mutex); mutex_lock(&mask_info->lock); mutex_lock(&driver->msg_mask_lock); @@ -2188,9 +2191,12 @@ void diag_send_updates_peripheral(uint8_t peripheral) if (driver->time_sync_enabled) diag_send_time_sync_update(peripheral); mutex_lock(&driver->md_session_lock); - diag_send_msg_mask_update(peripheral, ALL_SSID, ALL_SSID); - diag_send_log_mask_update(peripheral, ALL_EQUIP_ID); - diag_send_event_mask_update(peripheral); + if (driver->set_mask_cmd) { + diag_send_msg_mask_update(peripheral, + ALL_SSID, ALL_SSID); + diag_send_log_mask_update(peripheral, ALL_EQUIP_ID); + diag_send_event_mask_update(peripheral); + } mutex_unlock(&driver->md_session_lock); diag_send_real_time_update(peripheral, driver->real_time_mode[DIAG_LOCAL_PROC]); @@ -2227,6 +2233,7 @@ int diag_process_apps_masks(unsigned char *buf, int len, int pid) break; case DIAG_CMD_OP_SET_LOG_MASK: hdlr = diag_cmd_set_log_mask; + driver->set_mask_cmd = 1; break; case DIAG_CMD_OP_GET_LOG_MASK: hdlr = diag_cmd_get_log_mask; @@ -2246,17 +2253,21 @@ int diag_process_apps_masks(unsigned char *buf, int len, int pid) break; case DIAG_CMD_OP_SET_MSG_MASK: hdlr = diag_cmd_set_msg_mask; + driver->set_mask_cmd = 1; break; case DIAG_CMD_OP_SET_ALL_MSG_MASK: hdlr = diag_cmd_set_all_msg_mask; + driver->set_mask_cmd = 1; break; } } else if (*buf == DIAG_CMD_GET_EVENT_MASK) { hdlr = diag_cmd_get_event_mask; } else if (*buf == DIAG_CMD_SET_EVENT_MASK) { hdlr = diag_cmd_update_event_mask; + driver->set_mask_cmd = 1; } else if (*buf == DIAG_CMD_EVENT_TOGGLE) { hdlr = diag_cmd_toggle_events; + driver->set_mask_cmd = 1; } if (hdlr) diff --git a/drivers/char/diag/diag_usb.c b/drivers/char/diag/diag_usb.c index 8f6cd5892201c49c2f09bb14cf6d0b6972d53c33..bad5fcf0bbce7749f9560b1f64a9676826de9ec0 100644 --- a/drivers/char/diag/diag_usb.c +++ b/drivers/char/diag/diag_usb.c @@ -206,7 +206,19 @@ static void usb_connect_work_fn(struct work_struct *work) { struct diag_usb_info *ch = container_of(work, struct diag_usb_info, connect_work); + + wait_event_interruptible(ch->wait_q, ch->enabled > 0); + ch->max_size = usb_diag_request_size(ch->hdl); + atomic_set(&ch->connected, 1); + + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: USB channel %s: disconnected_status: %d, connected_status: %d\n", + ch->name, atomic_read(&ch->disconnected), atomic_read(&ch->connected)); + usb_connect(ch); + + if (atomic_read(&ch->disconnected)) + wake_up_interruptible(&ch->wait_q); } /* @@ -231,6 +243,19 @@ static void usb_disconnect_work_fn(struct work_struct *work) { struct diag_usb_info *ch = container_of(work, struct diag_usb_info, disconnect_work); + + atomic_set(&ch->disconnected, 1); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: USB channel %s: disconnected_status: %d, connected_status: %d\n", + ch->name, atomic_read(&ch->disconnected), atomic_read(&ch->connected)); + + wait_event_interruptible(ch->wait_q, atomic_read(&ch->connected) > 0); + atomic_set(&ch->connected, 0); + atomic_set(&ch->disconnected, 0); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: USB channel %s: Cleared disconnected(%d) and connected(%d) status\n", + ch->name, atomic_read(&ch->disconnected), atomic_read(&ch->connected)); + usb_disconnect(ch); } @@ -308,23 +333,25 @@ static void diag_usb_write_done(struct diag_usb_info *ch, if (!ch || !req) return; + spin_lock_irqsave(&ch->write_lock, flags); ch->write_cnt++; entry = diag_usb_buf_tbl_get(ch, req->context); if (!entry) { pr_err_ratelimited("diag: In %s, unable to find entry %pK in the table\n", __func__, req->context); + spin_unlock_irqrestore(&ch->write_lock, flags); return; } if (atomic_read(&entry->ref_count) != 0) { DIAG_LOG(DIAG_DEBUG_MUX, "partial write_done ref %d\n", atomic_read(&entry->ref_count)); diag_ws_on_copy_complete(DIAG_WS_MUX); + spin_unlock_irqrestore(&ch->write_lock, flags); diagmem_free(driver, req, ch->mempool); return; } DIAG_LOG(DIAG_DEBUG_MUX, "full write_done, ctxt: %d\n", ctxt); - spin_lock_irqsave(&ch->write_lock, flags); list_del(&entry->track); ctxt = entry->ctxt; buf = entry->buf; @@ -355,15 +382,15 @@ static void diag_usb_notifier(void *priv, unsigned int event, switch (event) { case USB_DIAG_CONNECT: - usb_info->max_size = usb_diag_request_size(usb_info->hdl); - atomic_set(&usb_info->connected, 1); - pr_info("diag: USB channel %s connected\n", usb_info->name); - queue_work(usb_info->usb_wq, + pr_info("diag: USB channel %s: Received Connect event\n", + usb_info->name); + if (!atomic_read(&usb_info->connected)) + queue_work(usb_info->usb_wq, &usb_info->connect_work); break; case USB_DIAG_DISCONNECT: - atomic_set(&usb_info->connected, 0); - pr_info("diag: USB channel %s disconnected\n", usb_info->name); + pr_info("diag: USB channel %s: Received Disconnect event\n", + usb_info->name); queue_work(usb_info->usb_wq, &usb_info->disconnect_work); break; @@ -616,6 +643,7 @@ int diag_usb_register(int id, int ctxt, struct diag_mux_ops *ops) if (!ch->read_ptr) goto err; atomic_set(&ch->connected, 0); + atomic_set(&ch->disconnected, 0); atomic_set(&ch->read_pending, 0); /* * This function is called when the mux registers with Diag-USB. @@ -629,6 +657,7 @@ int diag_usb_register(int id, int ctxt, struct diag_mux_ops *ops) INIT_WORK(&(ch->read_done_work), usb_read_done_work_fn); INIT_WORK(&(ch->connect_work), usb_connect_work_fn); INIT_WORK(&(ch->disconnect_work), usb_disconnect_work_fn); + init_waitqueue_head(&ch->wait_q); strlcpy(wq_name, "DIAG_USB_", DIAG_USB_STRING_SZ); strlcat(wq_name, ch->name, sizeof(ch->name)); ch->usb_wq = create_singlethread_workqueue(wq_name); @@ -641,7 +670,9 @@ int diag_usb_register(int id, int ctxt, struct diag_mux_ops *ops) goto err; } ch->enabled = 1; - pr_debug("diag: Successfully registered USB %s\n", ch->name); + wake_up_interruptible(&ch->wait_q); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: Successfully registered USB %s\n", ch->name); return 0; err: diff --git a/drivers/char/diag/diag_usb.h b/drivers/char/diag/diag_usb.h index e6c76b9da2909d212ffc8a3e18379300df523d84..5b291b2d61a07d3855eaf63b47062e5e579cd0f9 100644 --- a/drivers/char/diag/diag_usb.h +++ b/drivers/char/diag/diag_usb.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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,7 @@ struct diag_usb_info { atomic_t connected; atomic_t diag_state; atomic_t read_pending; + atomic_t disconnected; int enabled; int mempool; int max_size; @@ -70,6 +71,7 @@ struct diag_usb_info { struct work_struct connect_work; struct work_struct disconnect_work; struct workqueue_struct *usb_wq; + wait_queue_head_t wait_q; }; #ifdef CONFIG_DIAG_OVER_USB diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index a635c1c4bf01836443fac77c25d5cbd8dc557595..bf945f450a69d66ba761432615e5554854ea583c 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -538,8 +538,6 @@ struct diagchar_dev { struct class *diagchar_class; struct device *diag_dev; int ref_count; - int mask_clear; - struct mutex diag_maskclear_mutex; struct mutex diag_notifier_mutex; struct mutex diagchar_mutex; struct mutex diag_file_mutex; @@ -670,6 +668,7 @@ struct diagchar_dev { struct diag_mask_info *log_mask; struct diag_mask_info *event_mask; struct diag_mask_info *build_time_mask; + uint8_t set_mask_cmd; uint8_t msg_mask_tbl_count; uint8_t bt_msg_mask_tbl_count; uint16_t event_mask_size; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index abbcf94bbc0618f309fd5775b501a75f37deadae..ff53215026ca0534bce5af0b0dca3d3e4dba22cf 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -370,8 +370,8 @@ static int diagchar_open(struct inode *inode, struct file *file) return -ENOMEM; fail: - mutex_unlock(&driver->diagchar_mutex); driver->num_clients--; + mutex_unlock(&driver->diagchar_mutex); pr_err_ratelimited("diag: Insufficient memory for new client"); return -ENOMEM; } @@ -459,10 +459,6 @@ static void diag_close_logging_process(const int pid) if (diag_mask_clear_param) diag_clear_masks(pid); - mutex_lock(&driver->diag_maskclear_mutex); - driver->mask_clear = 1; - mutex_unlock(&driver->diag_maskclear_mutex); - mutex_lock(&driver->diagchar_mutex); p_mask = diag_translate_kernel_to_user_mask(session_mask); @@ -559,9 +555,7 @@ static int diagchar_close(struct inode *inode, struct file *file) DIAG_LOG(DIAG_DEBUG_USERSPACE, "diag: process exit %s\n", current->comm); ret = diag_remove_client_entry(file); - mutex_lock(&driver->diag_maskclear_mutex); - driver->mask_clear = 0; - mutex_unlock(&driver->diag_maskclear_mutex); + return ret; } @@ -1675,6 +1669,51 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask) return ret; } +static void diag_switch_logging_clear_mask( + struct diag_logging_mode_param_t *param, int pid) +{ + int new_mode; + struct diag_md_session_t *session_info = NULL; + + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_pid(pid); + if (!session_info) { + DIAG_LOG(DIAG_DEBUG_USERSPACE, "Invalid pid: %d\n", pid); + mutex_unlock(&driver->md_session_lock); + return; + } + mutex_unlock(&driver->md_session_lock); + + if (!param) + return; + + if (!param->peripheral_mask) { + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "asking for mode switch with no peripheral mask set\n"); + return; + } + + switch (param->req_mode) { + case CALLBACK_MODE: + case UART_MODE: + case SOCKET_MODE: + case MEMORY_DEVICE_MODE: + new_mode = DIAG_MEMORY_DEVICE_MODE; + break; + case USB_MODE: + new_mode = DIAG_USB_MODE; + break; + default: + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "Request to switch to invalid mode: %d\n", + param->req_mode); + return; + } + if ((new_mode == DIAG_USB_MODE) && diag_mask_clear_param) + diag_clear_masks(pid); + +} + static int diag_switch_logging(struct diag_logging_mode_param_t *param) { int new_mode, i = 0; @@ -2502,6 +2541,7 @@ long diagchar_compat_ioctl(struct file *filp, if (copy_from_user((void *)&mode_param, (void __user *)ioarg, sizeof(mode_param))) return -EFAULT; + diag_switch_logging_clear_mask(&mode_param, current->tgid); mutex_lock(&driver->diagchar_mutex); result = diag_switch_logging(&mode_param); mutex_unlock(&driver->diagchar_mutex); @@ -2633,6 +2673,7 @@ long diagchar_ioctl(struct file *filp, if (copy_from_user((void *)&mode_param, (void __user *)ioarg, sizeof(mode_param))) return -EFAULT; + diag_switch_logging_clear_mask(&mode_param, current->tgid); mutex_lock(&driver->diagchar_mutex); result = diag_switch_logging(&mode_param); mutex_unlock(&driver->diagchar_mutex); @@ -3906,7 +3947,6 @@ static int __init diagchar_init(void) non_hdlc_data.len = 0; mutex_init(&driver->hdlc_disable_mutex); mutex_init(&driver->diagchar_mutex); - mutex_init(&driver->diag_maskclear_mutex); mutex_init(&driver->diag_notifier_mutex); mutex_init(&driver->diag_file_mutex); mutex_init(&driver->delayed_rsp_mutex); diff --git a/drivers/char/diag/diagfwd_socket.c b/drivers/char/diag/diagfwd_socket.c index 7c5f207a02a53e12fe5f19d428417a13fb9e5e79..13be6d6cdf69c352dbfa9c2210e238d9c4d38be0 100644 --- a/drivers/char/diag/diagfwd_socket.c +++ b/drivers/char/diag/diagfwd_socket.c @@ -500,6 +500,9 @@ static void __socket_close_channel(struct diag_socket_info *info) diagfwd_channel_close(info->fwd_ctxt); atomic_set(&info->opened, 0); + /* Don't close the server. Server should always remain open */ + if (info->port_type == PORT_TYPE_SERVER) + return; write_lock_bh(&info->hdl->sk->sk_callback_lock); info->hdl->sk->sk_user_data = NULL; @@ -509,13 +512,13 @@ static void __socket_close_channel(struct diag_socket_info *info) sock_release(info->hdl); info->hdl = NULL; + cancel_work(&info->read_work); + wake_up_interruptible(&info->read_wait_q); + spin_lock_irqsave(&info->lock, flags); info->data_ready = 0; spin_unlock_irqrestore(&info->lock, flags); - cancel_work(&info->read_work); - wake_up_interruptible(&info->read_wait_q); - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n", info->name); } @@ -593,6 +596,44 @@ static void diag_socket_queue_read(void *ctxt) queue_work(info->wq, &(info->read_work)); } +static void handle_ctrl_pkt(struct diag_socket_info *info, void *buf, int len) +{ + const struct qrtr_ctrl_pkt *pkt = buf; + u32 node; + u32 port; + + if (len < sizeof(struct qrtr_ctrl_pkt)) + return; + + switch (le32_to_cpu(pkt->cmd)) { + case QRTR_TYPE_BYE: + node = le32_to_cpu(pkt->client.node); + if (info->remote_addr.sq_node == node) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s rcvd bye\n", + info->name); + + mutex_lock(&driver->diag_notifier_mutex); + socket_close_channel(info); + mutex_unlock(&driver->diag_notifier_mutex); + } + break; + case QRTR_TYPE_DEL_CLIENT: + node = le32_to_cpu(pkt->client.node); + port = le32_to_cpu(pkt->client.port); + + if (info->remote_addr.sq_node == node && + info->remote_addr.sq_port == port) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s rcvd del client\n", + info->name); + + mutex_lock(&driver->diag_notifier_mutex); + socket_close_channel(info); + mutex_unlock(&driver->diag_notifier_mutex); + } + break; + } +} + static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len) { int err = 0; @@ -683,8 +724,10 @@ static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len) } else if (read_len <= 0) goto fail; - if (src_addr.sq_port == QRTR_PORT_CTRL) + if (src_addr.sq_port == QRTR_PORT_CTRL) { + handle_ctrl_pkt(info, temp, read_len); continue; + } if (!atomic_read(&info->opened) && info->port_type == PORT_TYPE_SERVER) { @@ -694,6 +737,11 @@ static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len) * channel open for communication. */ memcpy(&info->remote_addr, &src_addr, sizeof(src_addr)); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "%s first client [0x%x:0x%x]\n", + info->name, src_addr.sq_node, + src_addr.sq_port); + if (info->ins_id == INST_ID_DCI) atomic_set(&info->opened, 1); else @@ -917,45 +965,9 @@ static void diag_del_server(struct qmi_handle *qmi, struct qmi_service *svc) socket_close_channel(info); } -static void diag_del_client(struct qmi_handle *qmi, unsigned int node_id, - unsigned int port_id) -{ - - struct diag_socket_info *info = NULL; - int i; - - for (i = 0; i < NUM_PERIPHERALS; i++) { - if ((socket_data[i].remote_addr.sq_node == node_id) && - (socket_data[i].remote_addr.sq_port == port_id)) { - info = &socket_data[i]; - break; - } - - if ((socket_cntl[i].remote_addr.sq_node == node_id) && - (socket_cntl[i].remote_addr.sq_port == port_id)) { - info = &socket_cntl[i]; - break; - } - - if ((socket_dci[i].remote_addr.sq_node == node_id) && - (socket_dci[i].remote_addr.sq_port == port_id)) { - info = &socket_dci[i]; - break; - } - } - if (!info) - return; - - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s rcvd del client\n", info->name); - mutex_lock(&driver->diag_notifier_mutex); - socket_close_channel(info); - mutex_unlock(&driver->diag_notifier_mutex); -} - static struct qmi_ops diag_qmi_cntl_ops = { .new_server = diag_new_server, .del_server = diag_del_server, - .del_client = diag_del_client, }; int diag_socket_init(void) diff --git a/drivers/char/fastcvpd.c b/drivers/char/fastcvpd.c new file mode 100644 index 0000000000000000000000000000000000000000..ef043bc852734cae42d0136649a505e2b5ed77d7 --- /dev/null +++ b/drivers/char/fastcvpd.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2018, 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 +#include +#include +#include "linux/fastcvpd.h" + +#define FASTCVPD_VIDEO_SEND_HFI_CMD_QUEUE 0 +#define FASTCVPD_VIDEO_SUSPEND 1 +#define FASTCVPD_VIDEO_RESUME 2 +#define FASTCVPD_VIDEO_SHUTDOWN 3 + +struct fastcvpd_cmd_msg { + uint32_t cmd_msg_type; + int ret_val; + uint64_t msg_ptr; + uint32_t msg_ptr_len; +}; + +struct fastcvpd_apps { + struct rpmsg_device *chan; + struct mutex smd_mutex; + int rpmsg_register; +}; + +static struct fastcvpd_apps gfa_cv; + +static int fastcvpd_send_cmd(void *msg, uint32_t len) +{ + struct fastcvpd_apps *me = &gfa_cv; + int err; + + if (IS_ERR_OR_NULL(me->chan)) { + err = -EINVAL; + goto bail; + } + err = rpmsg_send(me->chan->ept, msg, len); + +bail: + return err; +} + +static int fastcvpd_rpmsg_probe(struct rpmsg_device *rpdev) +{ + int err = 0; + struct fastcvpd_apps *me = &gfa_cv; + + if (strcmp(rpdev->dev.parent->of_node->name, "cdsp")) { + pr_err("%s: Failed to probe rpmsg device.Node name:%s\n", + __func__, rpdev->dev.parent->of_node->name); + err = -EINVAL; + goto bail; + } + mutex_lock(&me->smd_mutex); + me->chan = rpdev; + mutex_unlock(&me->smd_mutex); + pr_debug("%s: Successfully probed\n", __func__); +bail: + return err; +} + +static void fastcvpd_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct fastcvpd_apps *me = &gfa_cv; + + mutex_lock(&me->smd_mutex); + me->chan = NULL; + mutex_unlock(&me->smd_mutex); +} + +static int fastcvpd_rpmsg_callback(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 addr) +{ + return 0; +} + +int fastcvpd_video_send_cmd_hfi_queue(phys_addr_t *phys_addr, + uint32_t size_in_bytes) +{ + int err; + struct fastcvpd_cmd_msg cmd_msg; + + cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_SEND_HFI_CMD_QUEUE; + cmd_msg.msg_ptr = (uint64_t)phys_addr; + cmd_msg.msg_ptr_len = size_in_bytes; + + err = fastcvpd_send_cmd + (&cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + return err; +} +EXPORT_SYMBOL(fastcvpd_video_send_cmd_hfi_queue); + +int fastcvpd_video_suspend(uint32_t session_flag) +{ + int err = 0; + struct fastcvpd_cmd_msg cmd_msg; + + cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_SUSPEND; + err = fastcvpd_send_cmd + (&cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + return err; +} +EXPORT_SYMBOL(fastcvpd_video_suspend); + +int fastcvpd_video_resume(uint32_t session_flag) +{ + int err; + struct fastcvpd_cmd_msg cmd_msg; + + cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_RESUME; + err = fastcvpd_send_cmd + (&cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + return err; +} +EXPORT_SYMBOL(fastcvpd_video_resume); + +int fastcvpd_video_shutdown(uint32_t session_flag) +{ + int err; + struct fastcvpd_cmd_msg cmd_msg; + + cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_SHUTDOWN; + err = fastcvpd_send_cmd + (&cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + return err; +} +EXPORT_SYMBOL(fastcvpd_video_shutdown); + +static const struct rpmsg_device_id fastcvpd_rpmsg_match[] = { + { FASTCVPD_GLINK_GUID }, + { }, +}; + +static struct rpmsg_driver fastcvpd_rpmsg_client = { + .id_table = fastcvpd_rpmsg_match, + .probe = fastcvpd_rpmsg_probe, + .remove = fastcvpd_rpmsg_remove, + .callback = fastcvpd_rpmsg_callback, + .drv = { + .name = "qcom,msm_fastcvpd_rpmsg", + }, +}; + +static int __init fastcvpd_device_init(void) +{ + struct fastcvpd_apps *me = &gfa_cv; + int err; + + mutex_init(&me->smd_mutex); + err = register_rpmsg_driver(&fastcvpd_rpmsg_client); + if (err) { + pr_err("%s : register_rpmsg_driver failed with err %d\n", + __func__, err); + goto register_bail; + } + me->rpmsg_register = 1; + return 0; + +register_bail: + return err; +} + +static void __exit fastcvpd_device_exit(void) +{ + struct fastcvpd_apps *me = &gfa_cv; + + if (me->rpmsg_register == 1) + unregister_rpmsg_driver(&fastcvpd_rpmsg_client); +} + +late_initcall(fastcvpd_device_init); +module_exit(fastcvpd_device_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index b338a4becbf8c72baa8a8e9b6b0396c1e6ee3c60..845efa0f724f03158696fd32426ca470b721adbc 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -251,8 +251,9 @@ static int ipmi_powernv_probe(struct platform_device *pdev) ipmi->irq = opal_event_request(prop); } - if (request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH, - "opal-ipmi", ipmi)) { + rc = request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH, + "opal-ipmi", ipmi); + if (rc) { dev_warn(dev, "Unable to request irq\n"); goto err_dispose; } diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 9f2e3be2c5b8b37d059d9aca5d2f4677753ce034..676c910e990fb753218f91aebb699d6fff577438 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -66,7 +66,7 @@ static void (*specific_poweroff_func)(ipmi_user_t user); /* Holds the old poweroff function so we can restore it on removal. */ static void (*old_poweroff_func)(void); -static int set_param_ifnum(const char *val, struct kernel_param *kp) +static int set_param_ifnum(const char *val, const struct kernel_param *kp) { int rv = param_set_int(val, kp); if (rv) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index c04aa11f0e214566cb8895c94bee9c0290d55b57..9abc067f57994731204fd90aa2b093cd4e7f64d9 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1345,7 +1345,7 @@ static unsigned int num_slave_addrs; #define IPMI_MEM_ADDR_SPACE 1 static const char * const addr_space_to_str[] = { "i/o", "mem" }; -static int hotmod_handler(const char *val, struct kernel_param *kp); +static int hotmod_handler(const char *val, const struct kernel_param *kp); module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" @@ -1811,7 +1811,7 @@ static struct smi_info *smi_info_alloc(void) return info; } -static int hotmod_handler(const char *val, struct kernel_param *kp) +static int hotmod_handler(const char *val, const struct kernel_param *kp) { char *str = kstrdup(val, GFP_KERNEL); int rv; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 3a70dba2c645ac1b7ab905c3bf4faa571e2e420d..f11224a5dc5c787b7c4cc168c1230cc5cb94bc01 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -137,7 +137,7 @@ static ssize_t read_mem(struct file *file, char __user *buf, while (count > 0) { unsigned long remaining; - int allowed; + int allowed, probe; sz = size_inside_page(p, count); @@ -160,9 +160,9 @@ static ssize_t read_mem(struct file *file, char __user *buf, if (!ptr) goto failed; - err = probe_kernel_read(bounce, ptr, sz); + probe = probe_kernel_read(bounce, ptr, sz); unxlate_dev_mem_ptr(p, ptr); - if (err) + if (probe) goto failed; remaining = copy_to_user(buf, bounce, sz); diff --git a/drivers/char/random.c b/drivers/char/random.c index ea0115cf5fc0d01faa9b20fa2f1c8dd690a9ab80..ddc493d976fdc5e559cac9999156649445d741f5 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -261,6 +261,7 @@ #include #include #include +#include #include #include #include @@ -427,8 +428,9 @@ struct crng_state primary_crng = { * its value (from 0->1->2). */ static int crng_init = 0; -#define crng_ready() (likely(crng_init > 0)) +#define crng_ready() (likely(crng_init > 1)) static int crng_init_cnt = 0; +static unsigned long crng_global_init_time = 0; #define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE) static void _extract_crng(struct crng_state *crng, __u8 out[CHACHA20_BLOCK_SIZE]); @@ -437,6 +439,16 @@ static void _crng_backtrack_protect(struct crng_state *crng, static void process_random_ready_list(void); static void _get_random_bytes(void *buf, int nbytes); +static struct ratelimit_state unseeded_warning = + RATELIMIT_STATE_INIT("warn_unseeded_randomness", HZ, 3); +static struct ratelimit_state urandom_warning = + RATELIMIT_STATE_INIT("warn_urandom_randomness", HZ, 3); + +static int ratelimit_disable __read_mostly; + +module_param_named(ratelimit_disable, ratelimit_disable, int, 0644); +MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression"); + /********************************************************************** * * OS independent entropy store. Here are the functions which handle @@ -732,7 +744,7 @@ static void credit_entropy_bits(struct entropy_store *r, int nbits) static int credit_entropy_bits_safe(struct entropy_store *r, int nbits) { - const int nbits_max = (int)(~0U >> (ENTROPY_SHIFT + 1)); + const int nbits_max = r->poolinfo->poolwords * 32; if (nbits < 0) return -EINVAL; @@ -786,6 +798,43 @@ static void crng_initialize(struct crng_state *crng) crng->init_time = jiffies - CRNG_RESEED_INTERVAL - 1; } +#ifdef CONFIG_NUMA +static void do_numa_crng_init(struct work_struct *work) +{ + int i; + struct crng_state *crng; + struct crng_state **pool; + + pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL); + for_each_online_node(i) { + crng = kmalloc_node(sizeof(struct crng_state), + GFP_KERNEL | __GFP_NOFAIL, i); + spin_lock_init(&crng->lock); + crng_initialize(crng); + pool[i] = crng; + } + mb(); + if (cmpxchg(&crng_node_pool, NULL, pool)) { + for_each_node(i) + kfree(pool[i]); + kfree(pool); + } +} + +static DECLARE_WORK(numa_crng_init_work, do_numa_crng_init); + +static void numa_crng_init(void) +{ + schedule_work(&numa_crng_init_work); +} +#else +static void numa_crng_init(void) {} +#endif + +/* + * crng_fast_load() can be called by code in the interrupt service + * path. So we can't afford to dilly-dally. + */ static int crng_fast_load(const char *cp, size_t len) { unsigned long flags; @@ -793,7 +842,7 @@ static int crng_fast_load(const char *cp, size_t len) if (!spin_trylock_irqsave(&primary_crng.lock, flags)) return 0; - if (crng_ready()) { + if (crng_init != 0) { spin_unlock_irqrestore(&primary_crng.lock, flags); return 0; } @@ -812,6 +861,51 @@ static int crng_fast_load(const char *cp, size_t len) return 1; } +/* + * crng_slow_load() is called by add_device_randomness, which has two + * attributes. (1) We can't trust the buffer passed to it is + * guaranteed to be unpredictable (so it might not have any entropy at + * all), and (2) it doesn't have the performance constraints of + * crng_fast_load(). + * + * So we do something more comprehensive which is guaranteed to touch + * all of the primary_crng's state, and which uses a LFSR with a + * period of 255 as part of the mixing algorithm. Finally, we do + * *not* advance crng_init_cnt since buffer we may get may be something + * like a fixed DMI table (for example), which might very well be + * unique to the machine, but is otherwise unvarying. + */ +static int crng_slow_load(const char *cp, size_t len) +{ + unsigned long flags; + static unsigned char lfsr = 1; + unsigned char tmp; + unsigned i, max = CHACHA20_KEY_SIZE; + const char * src_buf = cp; + char * dest_buf = (char *) &primary_crng.state[4]; + + if (!spin_trylock_irqsave(&primary_crng.lock, flags)) + return 0; + if (crng_init != 0) { + spin_unlock_irqrestore(&primary_crng.lock, flags); + return 0; + } + if (len > max) + max = len; + + for (i = 0; i < max ; i++) { + tmp = lfsr; + lfsr >>= 1; + if (tmp & 1) + lfsr ^= 0xE1; + tmp = dest_buf[i % CHACHA20_KEY_SIZE]; + dest_buf[i % CHACHA20_KEY_SIZE] ^= src_buf[i % len] ^ lfsr; + lfsr += (tmp << 3) | (tmp >> 5); + } + spin_unlock_irqrestore(&primary_crng.lock, flags); + return 1; +} + static void crng_reseed(struct crng_state *crng, struct entropy_store *r) { unsigned long flags; @@ -830,7 +924,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) _crng_backtrack_protect(&primary_crng, buf.block, CHACHA20_KEY_SIZE); } - spin_lock_irqsave(&primary_crng.lock, flags); + spin_lock_irqsave(&crng->lock, flags); for (i = 0; i < 8; i++) { unsigned long rv; if (!arch_get_random_seed_long(&rv) && @@ -840,13 +934,26 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) } memzero_explicit(&buf, sizeof(buf)); crng->init_time = jiffies; - spin_unlock_irqrestore(&primary_crng.lock, flags); + spin_unlock_irqrestore(&crng->lock, flags); if (crng == &primary_crng && crng_init < 2) { invalidate_batched_entropy(); + numa_crng_init(); crng_init = 2; process_random_ready_list(); wake_up_interruptible(&crng_init_wait); pr_notice("random: crng init done\n"); + if (unseeded_warning.missed) { + pr_notice("random: %d get_random_xx warning(s) missed " + "due to ratelimiting\n", + unseeded_warning.missed); + unseeded_warning.missed = 0; + } + if (urandom_warning.missed) { + pr_notice("random: %d urandom warning(s) missed " + "due to ratelimiting\n", + urandom_warning.missed); + urandom_warning.missed = 0; + } } } @@ -855,8 +962,9 @@ static void _extract_crng(struct crng_state *crng, { unsigned long v, flags; - if (crng_init > 1 && - time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL)) + if (crng_ready() && + (time_after(crng_global_init_time, crng->init_time) || + time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL))) crng_reseed(crng, crng == &primary_crng ? &input_pool : NULL); spin_lock_irqsave(&crng->lock, flags); if (arch_get_random_long(&v)) @@ -981,10 +1089,8 @@ void add_device_randomness(const void *buf, unsigned int size) unsigned long time = random_get_entropy() ^ jiffies; unsigned long flags; - if (!crng_ready()) { - crng_fast_load(buf, size); - return; - } + if (!crng_ready() && size) + crng_slow_load(buf, size); trace_add_device_randomness(size, _RET_IP_); spin_lock_irqsave(&input_pool.lock, flags); @@ -1141,7 +1247,7 @@ void add_interrupt_randomness(int irq, int irq_flags) fast_mix(fast_pool); add_interrupt_bench(cycles); - if (!crng_ready()) { + if (unlikely(crng_init == 0)) { if ((fast_pool->count >= 64) && crng_fast_load((char *) fast_pool->pool, sizeof(fast_pool->pool))) { @@ -1491,8 +1597,9 @@ static void _warn_unseeded_randomness(const char *func_name, void *caller, #ifndef CONFIG_WARN_ALL_UNSEEDED_RANDOM print_once = true; #endif - pr_notice("random: %s called from %pS with crng_init=%d\n", - func_name, caller, crng_init); + if (__ratelimit(&unseeded_warning)) + pr_notice("random: %s called from %pS with crng_init=%d\n", + func_name, caller, crng_init); } /* @@ -1682,28 +1789,14 @@ static void init_std_data(struct entropy_store *r) */ static int rand_initialize(void) { -#ifdef CONFIG_NUMA - int i; - struct crng_state *crng; - struct crng_state **pool; -#endif - init_std_data(&input_pool); init_std_data(&blocking_pool); crng_initialize(&primary_crng); - -#ifdef CONFIG_NUMA - pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL); - for_each_online_node(i) { - crng = kmalloc_node(sizeof(struct crng_state), - GFP_KERNEL | __GFP_NOFAIL, i); - spin_lock_init(&crng->lock); - crng_initialize(crng); - pool[i] = crng; + crng_global_init_time = jiffies; + if (ratelimit_disable) { + urandom_warning.interval = 0; + unseeded_warning.interval = 0; } - mb(); - crng_node_pool = pool; -#endif return 0; } early_initcall(rand_initialize); @@ -1771,9 +1864,10 @@ urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) if (!crng_ready() && maxwarn > 0) { maxwarn--; - printk(KERN_NOTICE "random: %s: uninitialized urandom read " - "(%zd bytes read)\n", - current->comm, nbytes); + if (__ratelimit(&urandom_warning)) + printk(KERN_NOTICE "random: %s: uninitialized " + "urandom read (%zd bytes read)\n", + current->comm, nbytes); spin_lock_irqsave(&primary_crng.lock, flags); crng_init_cnt = 0; spin_unlock_irqrestore(&primary_crng.lock, flags); @@ -1877,6 +1971,14 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg) input_pool.entropy_count = 0; blocking_pool.entropy_count = 0; return 0; + case RNDRESEEDCRNG: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (crng_init < 2) + return -ENODATA; + crng_reseed(&primary_crng, NULL); + crng_global_init_time = jiffies - 1; + return 0; default: return -EINVAL; } @@ -2214,7 +2316,7 @@ void add_hwgenerator_randomness(const char *buffer, size_t count, { struct entropy_store *poolp = &input_pool; - if (!crng_ready()) { + if (unlikely(crng_init == 0)) { crng_fast_load(buffer, count); return; } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 5294442505cb5c7c2dcb34303596fd9a8236618c..dba5259def6038645dbe24d95f0243c0b6282443 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -328,7 +328,7 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -static bool tpm_validate_command(struct tpm_chip *chip, +static int tpm_validate_command(struct tpm_chip *chip, struct tpm_space *space, const u8 *cmd, size_t len) @@ -340,10 +340,10 @@ static bool tpm_validate_command(struct tpm_chip *chip, unsigned int nr_handles; if (len < TPM_HEADER_SIZE) - return false; + return -EINVAL; if (!space) - return true; + return 0; if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) { cc = be32_to_cpu(header->ordinal); @@ -352,7 +352,7 @@ static bool tpm_validate_command(struct tpm_chip *chip, if (i < 0) { dev_dbg(&chip->dev, "0x%04X is an invalid command\n", cc); - return false; + return -EOPNOTSUPP; } attrs = chip->cc_attrs_tbl[i]; @@ -362,27 +362,47 @@ static bool tpm_validate_command(struct tpm_chip *chip, goto err_len; } - return true; + return 0; err_len: dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__, len); - return false; + return -EINVAL; } -/** - * tmp_transmit - Internal kernel interface to transmit TPM commands. - * - * @chip: TPM chip to use - * @buf: TPM command buffer - * @bufsiz: length of the TPM command buffer - * @flags: tpm transmit flags - bitmap - * - * Return: - * 0 when the operation is successful. - * A negative number for system errors (errno). - */ -ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, - u8 *buf, size_t bufsiz, unsigned int flags) +static int tpm_request_locality(struct tpm_chip *chip) +{ + int rc; + + if (!chip->ops->request_locality) + return 0; + + rc = chip->ops->request_locality(chip, 0); + if (rc < 0) + return rc; + + chip->locality = rc; + + return 0; +} + +static void tpm_relinquish_locality(struct tpm_chip *chip) +{ + int rc; + + if (!chip->ops->relinquish_locality) + return; + + rc = chip->ops->relinquish_locality(chip, chip->locality); + if (rc) + dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); + + chip->locality = -1; +} + +static ssize_t tpm_try_transmit(struct tpm_chip *chip, + struct tpm_space *space, + u8 *buf, size_t bufsiz, + unsigned int flags) { struct tpm_output_header *header = (void *)buf; int rc; @@ -391,8 +411,20 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, unsigned long stop; bool need_locality; - if (!tpm_validate_command(chip, space, buf, bufsiz)) - return -EINVAL; + rc = tpm_validate_command(chip, space, buf, bufsiz); + if (rc == -EINVAL) + return rc; + /* + * If the command is not implemented by the TPM, synthesize a + * response with a TPM2_RC_COMMAND_CODE return for user-space. + */ + if (rc == -EOPNOTSUPP) { + header->length = cpu_to_be32(sizeof(*header)); + header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); + header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | + TSS2_RESMGR_TPM_RC_LAYER); + return bufsiz; + } if (bufsiz > TPM_BUFSIZE) bufsiz = TPM_BUFSIZE; @@ -410,8 +442,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, if (!(flags & TPM_TRANSMIT_UNLOCKED)) mutex_lock(&chip->tpm_mutex); - if (chip->dev.parent) - pm_runtime_get_sync(chip->dev.parent); if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, true); @@ -419,14 +449,15 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, /* Store the decision as chip->locality will be changed. */ need_locality = chip->locality == -1; - if (!(flags & TPM_TRANSMIT_RAW) && - need_locality && chip->ops->request_locality) { - rc = chip->ops->request_locality(chip, 0); + if (!(flags & TPM_TRANSMIT_RAW) && need_locality) { + rc = tpm_request_locality(chip); if (rc < 0) goto out_no_locality; - chip->locality = rc; } + if (chip->dev.parent) + pm_runtime_get_sync(chip->dev.parent); + rc = tpm2_prepare_space(chip, space, ordinal, buf); if (rc) goto out; @@ -487,27 +518,83 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, rc = tpm2_commit_space(chip, space, ordinal, buf, &len); out: - if (need_locality && chip->ops->relinquish_locality) { - chip->ops->relinquish_locality(chip, chip->locality); - chip->locality = -1; - } + if (chip->dev.parent) + pm_runtime_put_sync(chip->dev.parent); + + if (need_locality) + tpm_relinquish_locality(chip); + out_no_locality: if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); - if (chip->dev.parent) - pm_runtime_put_sync(chip->dev.parent); - if (!(flags & TPM_TRANSMIT_UNLOCKED)) mutex_unlock(&chip->tpm_mutex); return rc ? rc : len; } /** - * tmp_transmit_cmd - send a tpm command to the device + * tpm_transmit - Internal kernel interface to transmit TPM commands. + * + * @chip: TPM chip to use + * @space: tpm space + * @buf: TPM command buffer + * @bufsiz: length of the TPM command buffer + * @flags: tpm transmit flags - bitmap + * + * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY + * returns from the TPM and retransmits the command after a delay up + * to a maximum wait of TPM2_DURATION_LONG. + * + * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2 + * only + * + * Return: + * the length of the return when the operation is successful. + * A negative number for system errors (errno). + */ +ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, + u8 *buf, size_t bufsiz, unsigned int flags) +{ + struct tpm_output_header *header = (struct tpm_output_header *)buf; + /* space for header and handles */ + u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)]; + unsigned int delay_msec = TPM2_DURATION_SHORT; + u32 rc = 0; + ssize_t ret; + const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE, + bufsiz); + + /* + * Subtlety here: if we have a space, the handles will be + * transformed, so when we restore the header we also have to + * restore the handles. + */ + memcpy(save, buf, save_size); + + for (;;) { + ret = tpm_try_transmit(chip, space, buf, bufsiz, flags); + if (ret < 0) + break; + rc = be32_to_cpu(header->return_code); + if (rc != TPM2_RC_RETRY) + break; + delay_msec *= 2; + if (delay_msec > TPM2_DURATION_LONG) { + dev_err(&chip->dev, "TPM is in retry loop\n"); + break; + } + tpm_msleep(delay_msec); + memcpy(buf, save, save_size); + } + return ret; +} +/** + * tpm_transmit_cmd - send a tpm command to the device * The function extracts tpm out header return code * * @chip: TPM chip to use + * @space: tpm space * @buf: TPM command buffer * @bufsiz: length of the buffer * @min_rsp_body_length: minimum expected length of response body @@ -959,6 +1046,10 @@ int tpm_do_selftest(struct tpm_chip *chip) loops = jiffies_to_msecs(duration) / delay_msec; rc = tpm_continue_selftest(chip); + if (rc == TPM_ERR_INVALID_POSTINIT) { + chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED; + dev_info(&chip->dev, "TPM not ready (%d)\n", rc); + } /* This may fail if there was no TPM driver during a suspend/resume * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST) */ diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 2d5466a72e40f82b3272b857b74a1f822f82b966..b83b30a3eea5ef4e2de8b60a720d7dd571d45479 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -93,14 +93,20 @@ enum tpm2_structures { TPM2_ST_SESSIONS = 0x8002, }; +/* Indicates from what layer of the software stack the error comes from */ +#define TSS2_RC_LAYER_SHIFT 16 +#define TSS2_RESMGR_TPM_RC_LAYER (11 << TSS2_RC_LAYER_SHIFT) + enum tpm2_return_codes { TPM2_RC_SUCCESS = 0x0000, TPM2_RC_HASH = 0x0083, /* RC_FMT1 */ TPM2_RC_HANDLE = 0x008B, TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ TPM2_RC_DISABLED = 0x0120, + TPM2_RC_COMMAND_CODE = 0x0143, TPM2_RC_TESTING = 0x090A, /* RC_WARN */ TPM2_RC_REFERENCE_H0 = 0x0910, + TPM2_RC_RETRY = 0x0922, }; enum tpm2_algorithms { diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 8f0a98dea327a5bc90d7fcf3529c3701d17abd6b..bb756ad7897e7ca2b445cfa37a16a3edfd7926a6 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -117,6 +117,25 @@ struct tpm2_crb_smc { u32 smc_func_id; }; +static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, + unsigned long timeout) +{ + ktime_t start; + ktime_t stop; + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(timeout)); + + do { + if ((ioread32(reg) & mask) == value) + return true; + + usleep_range(50, 100); + } while (ktime_before(ktime_get(), stop)); + + return ((ioread32(reg) & mask) == value); +} + /** * crb_go_idle - request tpm crb device to go the idle state * @@ -132,37 +151,24 @@ struct tpm2_crb_smc { * * Return: 0 always */ -static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv) +static int crb_go_idle(struct device *dev, struct crb_priv *priv) { if ((priv->flags & CRB_FL_ACPI_START) || (priv->flags & CRB_FL_CRB_SMC_START)) return 0; iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); - /* we don't really care when this settles */ + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, + CRB_CTRL_REQ_GO_IDLE/* mask */, + 0, /* value */ + TPM2_TIMEOUT_C)) { + dev_warn(dev, "goIdle timed out\n"); + return -ETIME; + } return 0; } -static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, - unsigned long timeout) -{ - ktime_t start; - ktime_t stop; - - start = ktime_get(); - stop = ktime_add(start, ms_to_ktime(timeout)); - - do { - if ((ioread32(reg) & mask) == value) - return true; - - usleep_range(50, 100); - } while (ktime_before(ktime_get(), stop)); - - return false; -} - /** * crb_cmd_ready - request tpm crb device to enter ready state * @@ -177,8 +183,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, * * Return: 0 on success -ETIME on timeout; */ -static int __maybe_unused crb_cmd_ready(struct device *dev, - struct crb_priv *priv) +static int crb_cmd_ready(struct device *dev, struct crb_priv *priv) { if ((priv->flags & CRB_FL_ACPI_START) || (priv->flags & CRB_FL_CRB_SMC_START)) @@ -196,11 +201,11 @@ static int __maybe_unused crb_cmd_ready(struct device *dev, return 0; } -static int crb_request_locality(struct tpm_chip *chip, int loc) +static int __crb_request_locality(struct device *dev, + struct crb_priv *priv, int loc) { - struct crb_priv *priv = dev_get_drvdata(&chip->dev); u32 value = CRB_LOC_STATE_LOC_ASSIGNED | - CRB_LOC_STATE_TPM_REG_VALID_STS; + CRB_LOC_STATE_TPM_REG_VALID_STS; if (!priv->regs_h) return 0; @@ -208,21 +213,45 @@ static int crb_request_locality(struct tpm_chip *chip, int loc) iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl); if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value, TPM2_TIMEOUT_C)) { - dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); + dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); return -ETIME; } return 0; } -static void crb_relinquish_locality(struct tpm_chip *chip, int loc) +static int crb_request_locality(struct tpm_chip *chip, int loc) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); + return __crb_request_locality(&chip->dev, priv, loc); +} + +static int __crb_relinquish_locality(struct device *dev, + struct crb_priv *priv, int loc) +{ + u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | + CRB_LOC_STATE_TPM_REG_VALID_STS; + u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS; + if (!priv->regs_h) - return; + return 0; iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl); + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value, + TPM2_TIMEOUT_C)) { + dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); + return -ETIME; + } + + return 0; +} + +static int crb_relinquish_locality(struct tpm_chip *chip, int loc) +{ + struct crb_priv *priv = dev_get_drvdata(&chip->dev); + + return __crb_relinquish_locality(&chip->dev, priv, loc); } static u8 crb_status(struct tpm_chip *chip) @@ -466,6 +495,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, dev_warn(dev, FW_BUG "Bad ACPI memory layout"); } + ret = __crb_request_locality(dev, priv, 0); + if (ret) + return ret; + priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address, sizeof(struct crb_regs_tail)); if (IS_ERR(priv->regs_t)) @@ -522,6 +555,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, crb_go_idle(dev, priv); + __crb_relinquish_locality(dev, priv, 0); + return ret; } @@ -589,10 +624,14 @@ static int crb_acpi_add(struct acpi_device *device) chip->acpi_dev_handle = device->handle; chip->flags = TPM_CHIP_FLAG_TPM2; - rc = crb_cmd_ready(dev, priv); + rc = __crb_request_locality(dev, priv, 0); if (rc) return rc; + rc = crb_cmd_ready(dev, priv); + if (rc) + goto out; + pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -602,12 +641,15 @@ static int crb_acpi_add(struct acpi_device *device) crb_go_idle(dev, priv); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); - return rc; + goto out; } - pm_runtime_put(dev); + pm_runtime_put_sync(dev); - return 0; +out: + __crb_relinquish_locality(dev, priv, 0); + + return rc; } static int crb_acpi_remove(struct acpi_device *device) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index a21e31c2b952ed4b62b89b33879df4bf48e8ecda..58123df6b5f6bf77a8bcfb73e33b9a7e81572add 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -77,11 +77,13 @@ static bool check_locality(struct tpm_chip *chip, int l) return false; } -static void release_locality(struct tpm_chip *chip, int l) +static int release_locality(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); + + return 0; } static int request_locality(struct tpm_chip *chip, int l) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index d1aed2513bd93a0dd749e84911f9830a867b1b3d..a089474cb046af5d8dba1e7e5e855a566c2144ba 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -422,7 +422,7 @@ static void reclaim_dma_bufs(void) } } -static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size, +static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size, int pages) { struct port_buffer *buf; @@ -445,16 +445,16 @@ static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size, return buf; } - if (is_rproc_serial(vq->vdev)) { + if (is_rproc_serial(vdev)) { /* * Allocate DMA memory from ancestor. When a virtio * device is created by remoteproc, the DMA memory is * associated with the grandparent device: * vdev => rproc => platform-dev. */ - if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent) + if (!vdev->dev.parent || !vdev->dev.parent->parent) goto free_buf; - buf->dev = vq->vdev->dev.parent->parent; + buf->dev = vdev->dev.parent->parent; /* Increase device refcnt to avoid freeing it */ get_device(buf->dev); @@ -838,7 +838,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, count = min((size_t)(32 * 1024), count); - buf = alloc_buf(port->out_vq, count, 0); + buf = alloc_buf(port->portdev->vdev, count, 0); if (!buf) return -ENOMEM; @@ -957,7 +957,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, if (ret < 0) goto error_out; - buf = alloc_buf(port->out_vq, 0, pipe->nrbufs); + buf = alloc_buf(port->portdev->vdev, 0, pipe->nrbufs); if (!buf) { ret = -ENOMEM; goto error_out; @@ -1374,7 +1374,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) nr_added_bufs = 0; do { - buf = alloc_buf(vq, PAGE_SIZE, 0); + buf = alloc_buf(vq->vdev, PAGE_SIZE, 0); if (!buf) break; @@ -1402,7 +1402,6 @@ static int add_port(struct ports_device *portdev, u32 id) { char debugfs_name[16]; struct port *port; - struct port_buffer *buf; dev_t devt; unsigned int nr_added_bufs; int err; @@ -1513,8 +1512,6 @@ static int add_port(struct ports_device *portdev, u32 id) return 0; free_inbufs: - while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf, true); free_device: device_destroy(pdrvdata.class, port->dev->devt); free_cdev: @@ -1539,34 +1536,14 @@ static void remove_port(struct kref *kref) static void remove_port_data(struct port *port) { - struct port_buffer *buf; - spin_lock_irq(&port->inbuf_lock); /* Remove unused data this port might have received. */ discard_port_data(port); spin_unlock_irq(&port->inbuf_lock); - /* Remove buffers we queued up for the Host to send us data in. */ - do { - spin_lock_irq(&port->inbuf_lock); - buf = virtqueue_detach_unused_buf(port->in_vq); - spin_unlock_irq(&port->inbuf_lock); - if (buf) - free_buf(buf, true); - } while (buf); - spin_lock_irq(&port->outvq_lock); reclaim_consumed_buffers(port); spin_unlock_irq(&port->outvq_lock); - - /* Free pending buffers from the out-queue. */ - do { - spin_lock_irq(&port->outvq_lock); - buf = virtqueue_detach_unused_buf(port->out_vq); - spin_unlock_irq(&port->outvq_lock); - if (buf) - free_buf(buf, true); - } while (buf); } /* @@ -1791,13 +1768,24 @@ static void control_work_handler(struct work_struct *work) spin_unlock(&portdev->c_ivq_lock); } +static void flush_bufs(struct virtqueue *vq, bool can_sleep) +{ + struct port_buffer *buf; + unsigned int len; + + while ((buf = virtqueue_get_buf(vq, &len))) + free_buf(buf, can_sleep); +} + static void out_intr(struct virtqueue *vq) { struct port *port; port = find_port_by_vq(vq->vdev->priv, vq); - if (!port) + if (!port) { + flush_bufs(vq, false); return; + } wake_up_interruptible(&port->waitqueue); } @@ -1808,8 +1796,10 @@ static void in_intr(struct virtqueue *vq) unsigned long flags; port = find_port_by_vq(vq->vdev->priv, vq); - if (!port) + if (!port) { + flush_bufs(vq, false); return; + } spin_lock_irqsave(&port->inbuf_lock, flags); port->inbuf = get_inbuf(port); @@ -1984,24 +1974,54 @@ static const struct file_operations portdev_fops = { static void remove_vqs(struct ports_device *portdev) { + struct virtqueue *vq; + + virtio_device_for_each_vq(portdev->vdev, vq) { + struct port_buffer *buf; + + flush_bufs(vq, true); + while ((buf = virtqueue_detach_unused_buf(vq))) + free_buf(buf, true); + } portdev->vdev->config->del_vqs(portdev->vdev); kfree(portdev->in_vqs); kfree(portdev->out_vqs); } -static void remove_controlq_data(struct ports_device *portdev) +static void virtcons_remove(struct virtio_device *vdev) { - struct port_buffer *buf; - unsigned int len; + struct ports_device *portdev; + struct port *port, *port2; - if (!use_multiport(portdev)) - return; + portdev = vdev->priv; - while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) - free_buf(buf, true); + spin_lock_irq(&pdrvdata_lock); + list_del(&portdev->list); + spin_unlock_irq(&pdrvdata_lock); - while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) - free_buf(buf, true); + /* Disable interrupts for vqs */ + vdev->config->reset(vdev); + /* Finish up work that's lined up */ + if (use_multiport(portdev)) + cancel_work_sync(&portdev->control_work); + else + cancel_work_sync(&portdev->config_work); + + list_for_each_entry_safe(port, port2, &portdev->ports, list) + unplug_port(port); + + unregister_chrdev(portdev->chr_major, "virtio-portsdev"); + + /* + * When yanking out a device, we immediately lose the + * (device-side) queues. So there's no point in keeping the + * guest side around till we drop our final reference. This + * also means that any ports which are in an open state will + * have to just stop using the port, as the vqs are going + * away. + */ + remove_vqs(portdev); + kfree(portdev); } /* @@ -2070,6 +2090,7 @@ static int virtcons_probe(struct virtio_device *vdev) spin_lock_init(&portdev->ports_lock); INIT_LIST_HEAD(&portdev->ports); + INIT_LIST_HEAD(&portdev->list); virtio_device_ready(portdev->vdev); @@ -2087,8 +2108,15 @@ static int virtcons_probe(struct virtio_device *vdev) if (!nr_added_bufs) { dev_err(&vdev->dev, "Error allocating buffers for control queue\n"); - err = -ENOMEM; - goto free_vqs; + /* + * The host might want to notify mgmt sw about device + * add failure. + */ + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 0); + /* Device was functional: we need full cleanup. */ + virtcons_remove(vdev); + return -ENOMEM; } } else { /* @@ -2119,11 +2147,6 @@ static int virtcons_probe(struct virtio_device *vdev) return 0; -free_vqs: - /* The host might want to notify mgmt sw about device add failure */ - __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, - VIRTIO_CONSOLE_DEVICE_READY, 0); - remove_vqs(portdev); free_chrdev: unregister_chrdev(portdev->chr_major, "virtio-portsdev"); free: @@ -2132,43 +2155,6 @@ static int virtcons_probe(struct virtio_device *vdev) return err; } -static void virtcons_remove(struct virtio_device *vdev) -{ - struct ports_device *portdev; - struct port *port, *port2; - - portdev = vdev->priv; - - spin_lock_irq(&pdrvdata_lock); - list_del(&portdev->list); - spin_unlock_irq(&pdrvdata_lock); - - /* Disable interrupts for vqs */ - vdev->config->reset(vdev); - /* Finish up work that's lined up */ - if (use_multiport(portdev)) - cancel_work_sync(&portdev->control_work); - else - cancel_work_sync(&portdev->config_work); - - list_for_each_entry_safe(port, port2, &portdev->ports, list) - unplug_port(port); - - unregister_chrdev(portdev->chr_major, "virtio-portsdev"); - - /* - * When yanking out a device, we immediately lose the - * (device-side) queues. So there's no point in keeping the - * guest side around till we drop our final reference. This - * also means that any ports which are in an open state will - * have to just stop using the port, as the vqs are going - * away. - */ - remove_controlq_data(portdev); - remove_vqs(portdev); - kfree(portdev); -} - static struct virtio_device_id id_table[] = { { VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -2209,7 +2195,6 @@ static int virtcons_freeze(struct virtio_device *vdev) */ if (use_multiport(portdev)) virtqueue_disable_cb(portdev->c_ivq); - remove_controlq_data(portdev); list_for_each_entry(port, &portdev->ports, list) { virtqueue_disable_cb(port->in_vq); diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index eec52734d6ac0b372fec3ba4ecd338e123cb5b16..5f8082d89131307744cafa84c2167c0a59c4c979 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -632,9 +632,7 @@ static void bcm2835_pll_off(struct clk_hw *hw) const struct bcm2835_pll_data *data = pll->data; spin_lock(&cprman->regs_lock); - cprman_write(cprman, data->cm_ctrl_reg, - cprman_read(cprman, data->cm_ctrl_reg) | - CM_PLL_ANARST); + cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST); cprman_write(cprman, data->a2w_ctrl_reg, cprman_read(cprman, data->a2w_ctrl_reg) | A2W_PLL_CTRL_PWRDN); @@ -670,6 +668,10 @@ static int bcm2835_pll_on(struct clk_hw *hw) cpu_relax(); } + cprman_write(cprman, data->a2w_ctrl_reg, + cprman_read(cprman, data->a2w_ctrl_reg) | + A2W_PLL_CTRL_PRST_DISABLE); + return 0; } diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 1157ea64c02f9787403f0d0e57c716825f565155..d85bd0188effa9deb9237f7e5ff5840549100044 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -118,12 +118,11 @@ static unsigned int _get_val(const struct clk_div_table *table, unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, unsigned int val, const struct clk_div_table *table, - unsigned long flags) + unsigned long flags, unsigned long width) { - struct clk_divider *divider = to_clk_divider(hw); unsigned int div; - div = _get_div(table, val, flags, divider->width); + div = _get_div(table, val, flags, width); if (!div) { WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO), "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", @@ -145,7 +144,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, val &= div_mask(divider->width); return divider_recalc_rate(hw, parent_rate, val, divider->table, - divider->flags); + divider->flags, divider->width); } static bool _is_valid_table_div(const struct clk_div_table *table, diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c index a1c1f684ad585bbc5d64f3b7954473d442197398..9f46cf9dcc6529ea05e1d685825c17c7dac13339 100644 --- a/drivers/clk/hisilicon/clkdivider-hi6220.c +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -56,7 +56,7 @@ static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, val &= div_mask(dclk->width); return divider_recalc_rate(hw, parent_rate, val, dclk->table, - CLK_DIVIDER_ROUND_CLOSEST); + CLK_DIVIDER_ROUND_CLOSEST, dclk->width); } static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c index 9598889f972b0dd7b163354861a508e8ad8e3ce0..ccfe5d30fe10fdd12bab2f8f7e11bcdb6deff3cf 100644 --- a/drivers/clk/mediatek/clk-mt2701.c +++ b/drivers/clk/mediatek/clk-mt2701.c @@ -148,6 +148,7 @@ static const struct mtk_fixed_factor top_fixed_divs[] = { FACTOR(CLK_TOP_CLK26M_D8, "clk26m_d8", "clk26m", 1, 8), FACTOR(CLK_TOP_32K_INTERNAL, "32k_internal", "clk26m", 1, 793), FACTOR(CLK_TOP_32K_EXTERNAL, "32k_external", "rtc32k", 1, 1), + FACTOR(CLK_TOP_AXISEL_D4, "axisel_d4", "axi_sel", 1, 4), }; static const char * const axi_parents[] = { @@ -857,13 +858,13 @@ static const struct mtk_gate peri_clks[] = { GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11), GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10), GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9), - GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8), - GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7), - GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6), - GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5), - GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4), - GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3), - GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2), + GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axisel_d4", 8), + GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axisel_d4", 7), + GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axisel_d4", 6), + GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axisel_d4", 5), + GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axisel_d4", 4), + GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axisel_d4", 3), + GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axisel_d4", 2), GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1), GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "nfi2x_sel", 0), diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c index 44a5a535ca6334ebe17acf74ca38fea45ab86708..5144360e2c804760720783098288dd3f104e5164 100644 --- a/drivers/clk/meson/clk-mpll.c +++ b/drivers/clk/meson/clk-mpll.c @@ -98,7 +98,7 @@ static void params_from_rate(unsigned long requested_rate, *sdm = SDM_DEN - 1; } else { *n2 = div; - *sdm = DIV_ROUND_UP(rem * SDM_DEN, requested_rate); + *sdm = DIV_ROUND_UP_ULL((u64)rem * SDM_DEN, requested_rate); } } diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c index 394aa6f03f01ffee1ed370896dbc2946a06b17df..9ff4ea63932d507c9a5b7289e34eddf125193652 100644 --- a/drivers/clk/mvebu/armada-38x.c +++ b/drivers/clk/mvebu/armada-38x.c @@ -46,11 +46,11 @@ static u32 __init armada_38x_get_tclk_freq(void __iomem *sar) } static const u32 armada_38x_cpu_frequencies[] __initconst = { - 0, 0, 0, 0, - 1066 * 1000 * 1000, 0, 0, 0, + 666 * 1000 * 1000, 0, 800 * 1000 * 1000, 0, + 1066 * 1000 * 1000, 0, 1200 * 1000 * 1000, 0, 1332 * 1000 * 1000, 0, 0, 0, 1600 * 1000 * 1000, 0, 0, 0, - 1866 * 1000 * 1000, + 1866 * 1000 * 1000, 0, 0, 2000 * 1000 * 1000, }; static u32 __init armada_38x_get_cpu_freq(void __iomem *sar) @@ -76,11 +76,11 @@ static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = { }; static const int armada_38x_cpu_l2_ratios[32][2] __initconst = { - {0, 1}, {0, 1}, {0, 1}, {0, 1}, - {1, 2}, {0, 1}, {0, 1}, {0, 1}, - {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {1, 2}, {0, 1}, + {1, 2}, {0, 1}, {1, 2}, {0, 1}, {1, 2}, {0, 1}, {0, 1}, {0, 1}, {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {1, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, @@ -91,7 +91,7 @@ static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = { {1, 2}, {0, 1}, {0, 1}, {0, 1}, {1, 2}, {0, 1}, {0, 1}, {0, 1}, {1, 2}, {0, 1}, {0, 1}, {0, 1}, - {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {7, 15}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c index 7b359afd620ec0ad23bb6bf2e41da28d5fd4e05a..a6438f50e6db94845e53cfc0c4dce70880d749a5 100644 --- a/drivers/clk/nxp/clk-lpc32xx.c +++ b/drivers/clk/nxp/clk-lpc32xx.c @@ -956,7 +956,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, val &= div_mask(divider->width); return divider_recalc_rate(hw, parent_rate, val, divider->table, - divider->flags); + divider->flags, divider->width); } static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index e7b66491406855f598fcf20ed408c8cb247a5cf0..9d5fbacb03e33642c4ff2e20a1cfc45d84c2a3b9 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -315,3 +315,20 @@ config MSM_CAMCC_SDMSHRIKE SDMSHRIKE devices. Say Y if you want to support camera devices and functionality such as capturing pictures. + +config MDM_GCC_QCS405 + tristate "QCS405 Global Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the global clock controller on Qualcomm Technologies, Inc + QCS405 devices. + Say Y if you want to use peripheral devices such as UART, SPI, I2C, + USB, SD/eMMC, PCIe, etc. + +config MDM_DEBUGCC_QCS405 + tristate "QCS405 Debug Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the debug clock controller on Qualcomm Technologies, Inc + QCS405 devices. + Say Y if you want to support the clock measurement functionality. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 597663835aabb77f1f9c41fe26e9a2cacee80b0a..b0026c82358e4b3d8bd05551b8af2e2434cb60d8 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o obj-$(CONFIG_IPQ_GCC_8074) += gcc-ipq8074.o obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o obj-$(CONFIG_MDM_GCC_9615) += gcc-mdm9615.o +obj-$(CONFIG_MDM_GCC_QCS405) += gcc-qcs405.o obj-$(CONFIG_MDM_LCC_9615) += lcc-mdm9615.o obj-$(CONFIG_MSM_CAMCC_SM8150) += camcc-sm8150.o obj-$(CONFIG_MSM_CAMCC_SDMSHRIKE) += camcc-sdmshrike.o @@ -32,6 +33,7 @@ obj-$(CONFIG_MSM_CLK_AOP_QMP) += clk-aop-qmp.o obj-$(CONFIG_MSM_CLK_RPMH) += clk-rpmh.o obj-$(CONFIG_MSM_DEBUGCC_SM8150) += debugcc-sm8150.o obj-$(CONFIG_MSM_DISPCC_SM8150) += dispcc-sm8150.o +obj-$(CONFIG_MDM_DEBUGCC_QCS405) += debugcc-qcs405.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8916) += gcc-msm8916.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 391ca0359c553f78970db1a3af2790ad6632aec6..b3c680049b99986b59266ba1e516cc99e1b9bf97 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -726,6 +726,7 @@ int clk_trion_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, if (trion_pll_is_enabled(pll, regmap)) { pr_warn("PLL is already enabled. Skipping configuration.\n"); + pll->inited = true; return ret; } @@ -1039,6 +1040,7 @@ int clk_regera_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, if (mode_regval & PLL_LOCK_DET) { pr_warn("PLL is already enabled. Skipping configuration.\n"); + pll->inited = true; return 0; } @@ -1496,8 +1498,9 @@ static int clk_alpha_pll_slew_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); unsigned long freq_hz; const struct pll_vco *curr_vco, *vco; - u32 l; + u32 l, ctl; u64 a; + int i = 0; freq_hz = alpha_pll_round_rate(pll, rate, parent_rate, &l, &a); if (freq_hz != rate) { @@ -1505,7 +1508,18 @@ static int clk_alpha_pll_slew_set_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; } - curr_vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); + regmap_read(pll->clkr.regmap, pll->offset + PLL_USER_CTL, &ctl); + ctl >>= PLL_POST_DIV_SHIFT; + ctl &= PLL_POST_DIV_MASK; + + for (i = 0; i < ARRAY_SIZE(clk_alpha_div_table); i++) { + if (clk_alpha_div_table[i].val == ctl) + break; + } + + if (i < ARRAY_SIZE(clk_alpha_div_table)) + curr_vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw) * + clk_alpha_div_table[i].div); if (!curr_vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; @@ -1551,8 +1565,8 @@ static int clk_alpha_pll_calibrate(struct clk_hw *hw) struct clk_hw *parent; const struct pll_vco *vco; u64 a; - u32 l; - int rc; + u32 l, ctl; + int rc, i = 0; parent = clk_hw_get_parent(hw); if (!parent) { @@ -1560,7 +1574,18 @@ static int clk_alpha_pll_calibrate(struct clk_hw *hw) return -EINVAL; } - vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); + regmap_read(pll->clkr.regmap, pll->offset + PLL_USER_CTL, &ctl); + ctl >>= PLL_POST_DIV_SHIFT; + ctl &= PLL_POST_DIV_MASK; + + for (i = 0; i < ARRAY_SIZE(clk_alpha_div_table); i++) { + if (clk_alpha_div_table[i].val == ctl) + break; + } + + if (i < ARRAY_SIZE(clk_alpha_div_table)) + vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw) * + clk_alpha_div_table[i].div); if (!vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index f176a22b01f8b63f34332d17208cefc1d6ae485f..81f6fde79f899521293846afe518161a75469877 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -36,6 +36,7 @@ #include "common.h" #include "clk-regmap.h" #include "clk-voter.h" +#include "clk-debug.h" #define OSM_INIT_RATE 300000000UL #define XO_RATE 19200000UL @@ -169,6 +170,7 @@ static int clk_osm_search_table(struct osm_entry *table, int entries, long rate) const struct clk_ops clk_ops_cpu_osm = { .round_rate = clk_osm_round_rate, .list_rate = clk_osm_list_rate, + .debug_init = clk_debug_measure_add, }; static int clk_core_set_rate(struct clk_hw *hw, unsigned long rate, @@ -264,6 +266,7 @@ const static struct clk_ops clk_ops_l3_osm = { .list_rate = clk_osm_list_rate, .recalc_rate = l3_clk_recalc_rate, .set_rate = l3_clk_set_rate, + .debug_init = clk_debug_measure_add, }; static struct clk_init_data osm_clks_init[] = { @@ -301,6 +304,7 @@ static struct clk_osm l3_clk = { static DEFINE_CLK_VOTER(l3_cluster0_vote_clk, l3_clk, 0); static DEFINE_CLK_VOTER(l3_cluster1_vote_clk, l3_clk, 0); static DEFINE_CLK_VOTER(l3_misc_vote_clk, l3_clk, 0); +static DEFINE_CLK_VOTER(l3_gpu_vote_clk, l3_clk, 0); static struct clk_osm pwrcl_clk = { .cluster_num = 1, @@ -429,6 +433,7 @@ static struct clk_hw *osm_qcom_clk_hws[] = { [L3_CLUSTER0_VOTE_CLK] = &l3_cluster0_vote_clk.hw, [L3_CLUSTER1_VOTE_CLK] = &l3_cluster1_vote_clk.hw, [L3_MISC_VOTE_CLK] = &l3_misc_vote_clk.hw, + [L3_GPU_VOTE_CLK] = &l3_gpu_vote_clk.hw, [L3_CLK] = &l3_clk.hw, [CPU0_PWRCL_CLK] = &cpu0_pwrcl_clk.hw, [CPU1_PWRCL_CLK] = &cpu1_pwrcl_clk.hw, @@ -1116,6 +1121,8 @@ static int clk_cpu_osm_driver_probe(struct platform_device *pdev) "clk: Failed to enable cluster1 clock for L3\n"); WARN(clk_prepare_enable(l3_misc_vote_clk.hw.clk), "clk: Failed to enable misc clock for L3\n"); + WARN(clk_prepare_enable(l3_gpu_vote_clk.hw.clk), + "clk: Failed to enable gpu clock for L3\n"); populate_opp_table(pdev); diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c index ec2a318cebbe6ce76be3556bd942a26d100d0bf5..b9d3d717ebefdcd4040f1a0d296ac81164fdd641 100644 --- a/drivers/clk/qcom/clk-debug.c +++ b/drivers/clk/qcom/clk-debug.c @@ -209,17 +209,19 @@ static void enable_debug_clks(struct clk_debug_mux *meas, u8 index) { int dbg_cc = meas->parent[index].dbg_cc; + meas->en_mask = meas->en_mask ? meas->en_mask : CBCR_ENA; + if (dbg_cc != GCC) { /* Not all recursive muxes have a DEBUG clock. */ if (meas->parent[index].cbcr_offset != U32_MAX) regmap_update_bits(meas->regmap[dbg_cc], meas->parent[index].cbcr_offset, - CBCR_ENA, CBCR_ENA); + meas->en_mask, meas->en_mask); } /* Turn on the GCC_DEBUG_CBCR */ regmap_update_bits(meas->regmap[GCC], meas->cbcr_offset, - CBCR_ENA, CBCR_ENA); + meas->en_mask, meas->en_mask); } @@ -227,15 +229,17 @@ static void disable_debug_clks(struct clk_debug_mux *meas, u8 index) { int dbg_cc = meas->parent[index].dbg_cc; + meas->en_mask = meas->en_mask ? meas->en_mask : CBCR_ENA; + /* Turn off the GCC_DEBUG_CBCR */ regmap_update_bits(meas->regmap[GCC], meas->cbcr_offset, - CBCR_ENA, 0); + meas->en_mask, 0); if (dbg_cc != GCC) { if (meas->parent[index].cbcr_offset != U32_MAX) regmap_update_bits(meas->regmap[dbg_cc], meas->parent[index].cbcr_offset, - CBCR_ENA, 0); + meas->en_mask, 0); } } diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h index 3aea9cde97ac952648d8f78424e1f20cb6b574ea..742c295e43f05995bae0b2b82798e57f2398b774 100644 --- a/drivers/clk/qcom/clk-debug.h +++ b/drivers/clk/qcom/clk-debug.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 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 @@ -98,6 +98,8 @@ struct clk_src { * @num_parents: number of parents * @regmap: regmaps of debug mux * @priv: private measure_clk_data to be used by debug mux + * @en_mask: indicates the enable bit mask at global clock + * controller debug mux. * @debug_offset: debug mux offset. * @post_div_offset: register with post-divider settings for the debug mux. * @cbcr_offset: branch register to turn on debug mux. @@ -116,6 +118,7 @@ struct clk_debug_mux { int num_parents; struct regmap **regmap; void *priv; + u32 en_mask; u32 debug_offset; u32 post_div_offset; u32 cbcr_offset; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index b31ff161d8753c21a995204d294d094da4018eac..866d5aaf84a581be1da15998bc5729f16efb1412 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -177,7 +177,8 @@ static int clk_rcg2_set_force_enable(struct clk_hw *hw) udelay(1); } - WARN(1, "%s: rcg didn't turn on.", clk_hw_get_name(hw)); + WARN_CLK(hw->core, clk_hw_get_name(hw), count == 0, + "rcg didn't turn on."); return ret; } diff --git a/drivers/clk/qcom/clk-regmap-divider.c b/drivers/clk/qcom/clk-regmap-divider.c index c314d2c406b7bd0591b31bb7b954bdac0716c1a2..ea954a29fead1f3ce7d4e48bcbcc6b8c0356d6e7 100644 --- a/drivers/clk/qcom/clk-regmap-divider.c +++ b/drivers/clk/qcom/clk-regmap-divider.c @@ -62,7 +62,8 @@ static unsigned long div_recalc_rate(struct clk_hw *hw, div &= BIT(divider->width) - 1; return divider_recalc_rate(hw, parent_rate, div, divider->table, - CLK_DIVIDER_ROUND_CLOSEST | divider->flags); + CLK_DIVIDER_ROUND_CLOSEST | divider->flags, + divider->width); } const struct clk_ops clk_regmap_div_ops = { diff --git a/drivers/clk/qcom/debugcc-qcs405.c b/drivers/clk/qcom/debugcc-qcs405.c new file mode 100644 index 0000000000000000000000000000000000000000..b231337d2e4f87d768da52366cf564ff5ccc2594 --- /dev/null +++ b/drivers/clk/qcom/debugcc-qcs405.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-debug.h" + +static struct measure_clk_data debug_mux_priv = { + .ctl_reg = 0x74004, + .status_reg = 0x74008, + .xo_div4_cbcr = 0x30034, +}; + +static const char *const debug_mux_parent_names[] = { + "snoc_clk", + "pnoc_clk", + "bimc_clk", + "qpic_clk", + "ce1_clk", + "wcnss_m_clk", + "gcc_apss_ahb_clk", + "gcc_bimc_gfx_clk", + "gcc_blsp1_ahb_clk", + "gcc_blsp1_qup0_i2c_apps_clk", + "gcc_blsp1_qup0_spi_apps_clk", + "gcc_blsp1_qup1_i2c_apps_clk", + "gcc_blsp1_qup1_spi_apps_clk", + "gcc_blsp1_qup2_i2c_apps_clk", + "gcc_blsp1_qup2_spi_apps_clk", + "gcc_blsp1_qup3_i2c_apps_clk", + "gcc_blsp1_qup3_spi_apps_clk", + "gcc_blsp1_qup4_i2c_apps_clk", + "gcc_blsp1_qup4_spi_apps_clk", + "gcc_blsp1_uart0_apps_clk", + "gcc_blsp1_uart1_apps_clk", + "gcc_blsp1_uart2_apps_clk", + "gcc_blsp1_uart3_apps_clk", + "gcc_blsp2_ahb_clk", + "gcc_blsp2_qup0_i2c_apps_clk", + "gcc_blsp2_qup0_spi_apps_clk", + "gcc_blsp2_uart0_apps_clk", + "gcc_boot_rom_ahb_clk", + "gcc_dcc_clk", + "gcc_eth_axi_clk", + "gcc_eth_ptp_clk", + "gcc_eth_rgmii_clk", + "gcc_eth_slave_ahb_clk", + "gcc_geni_ir_s_clk", + "gcc_gp1_clk", + "gcc_gp2_clk", + "gcc_gp3_clk", + "gcc_mdss_ahb_clk", + "gcc_mdss_axi_clk", + "gcc_mdss_byte0_clk", + "gcc_mdss_esc0_clk", + "gcc_mdss_hdmi_app_clk", + "gcc_mdss_hdmi_pclk_clk", + "gcc_mdss_mdp_clk", + "gcc_mdss_pclk0_clk", + "gcc_mdss_vsync_clk", + "gcc_oxili_ahb_clk", + "gcc_oxili_gfx3d_clk", + "gcc_pcie_0_aux_clk", + "gcc_pcie_0_cfg_ahb_clk", + "gcc_pcie_0_mstr_axi_clk", + "gcc_pcie_0_pipe_clk", + "gcc_pcie_0_slv_axi_clk", + "gcc_pcnoc_usb2_clk", + "gcc_pcnoc_usb3_clk", + "gcc_pdm2_clk", + "gcc_pdm_ahb_clk", + "gcc_prng_ahb_clk", + "gcc_pwm0_xo512_clk", + "gcc_pwm1_xo512_clk", + "gcc_pwm2_xo512_clk", + "gcc_sdcc1_ahb_clk", + "gcc_sdcc1_apps_clk", + "gcc_sdcc1_ice_core_clk", + "gcc_sdcc2_ahb_clk", + "gcc_sdcc2_apps_clk", + "gcc_sys_noc_usb3_clk", + "gcc_usb20_mock_utmi_clk", + "gcc_usb2a_phy_sleep_clk", + "gcc_usb30_master_clk", + "gcc_usb30_mock_utmi_clk", + "gcc_usb30_sleep_clk", + "gcc_usb3_phy_aux_clk", + "gcc_usb3_phy_pipe_clk", + "gcc_usb_hs_inactivity_timers_clk", + "gcc_usb_hs_phy_cfg_ahb_clk", + "gcc_usb_hs_system_clk", +}; + +static struct clk_debug_mux gcc_debug_mux = { + .priv = &debug_mux_priv, + .debug_offset = 0x74000, + .post_div_offset = 0x74000, + .cbcr_offset = 0x74000, + .src_sel_mask = 0x1FF, + .src_sel_shift = 0, + .post_div_mask = 0xF000, + .post_div_shift = 12, + .en_mask = BIT(16), + MUX_SRC_LIST( + { "snoc_clk", 0x000, 4, GCC, + 0x000, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "pnoc_clk", 0x008, 4, GCC, + 0x008, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "bimc_clk", 0x15A, 4, GCC, + 0x15A, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "qpic_clk", 0x0C0, 4, GCC, + 0x0C0, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "ce1_clk", 0x138, 4, GCC, + 0x138, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "wcnss_m_clk", 0x08A, 4, GCC, + 0x08A, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_apss_ahb_clk", 0x168, 4, GCC, + 0x168, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_bimc_gfx_clk", 0x2D, 4, GCC, + 0x2D, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_ahb_clk", 0x88, 4, GCC, + 0x88, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup0_i2c_apps_clk", 0x99, 4, GCC, + 0x99, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup0_spi_apps_clk", 0x98, 4, GCC, + 0x98, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup1_i2c_apps_clk", 0x8B, 4, GCC, + 0x8B, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup1_spi_apps_clk", 0x8A, 4, GCC, + 0x8A, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup2_i2c_apps_clk", 0x8F, 4, GCC, + 0x8F, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup2_spi_apps_clk", 0x8E, 4, GCC, + 0x8E, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup3_i2c_apps_clk", 0x93, 4, GCC, + 0x93, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup3_spi_apps_clk", 0x92, 4, GCC, + 0x92, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup4_i2c_apps_clk", 0x95, 4, GCC, + 0x95, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_qup4_spi_apps_clk", 0x94, 4, GCC, + 0x94, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_uart0_apps_clk", 0x9A, 4, GCC, + 0x9A, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_uart1_apps_clk", 0x8C, 4, GCC, + 0x8C, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_uart2_apps_clk", 0x90, 4, GCC, + 0x90, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp1_uart3_apps_clk", 0x96, 4, GCC, + 0x96, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp2_ahb_clk", 0xA0, 4, GCC, + 0xA0, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp2_qup0_i2c_apps_clk", 0xA2, 4, GCC, + 0xA2, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp2_qup0_spi_apps_clk", 0xA3, 4, GCC, + 0xA3, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_blsp2_uart0_apps_clk", 0xA4, 4, GCC, + 0xA4, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_boot_rom_ahb_clk", 0xF8, 4, GCC, + 0xF8, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_dcc_clk", 0x1B9, 4, GCC, + 0x1B9, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_eth_axi_clk", 0x80, 4, GCC, + 0x80, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_eth_ptp_clk", 0x83, 4, GCC, + 0x83, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_eth_rgmii_clk", 0x81, 4, GCC, + 0x81, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_eth_slave_ahb_clk", 0x82, 4, GCC, + 0x82, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_geni_ir_s_clk", 0xEE, 4, GCC, + 0xEE, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_gp1_clk", 0x10, 4, GCC, + 0x10, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_gp2_clk", 0x11, 4, GCC, + 0x11, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_gp3_clk", 0x12, 4, GCC, + 0x12, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_ahb_clk", 0x1F6, 4, GCC, + 0x1F6, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_axi_clk", 0x1F7, 4, GCC, + 0x1F7, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_byte0_clk", 0x1FC, 4, GCC, + 0x1FC, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_esc0_clk", 0x1FD, 4, GCC, + 0x1FD, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_hdmi_app_clk", 0x1F2, 4, GCC, + 0x1F2, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_hdmi_pclk_clk", 0x1F1, 4, GCC, + 0x1F1, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_mdp_clk", 0x1F9, 4, GCC, + 0x1F9, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_pclk0_clk", 0x1F8, 4, GCC, + 0x1F8, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_mdss_vsync_clk", 0x1FB, 4, GCC, + 0x1FB, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_oxili_ahb_clk", 0x1EB, 4, GCC, + 0x1EB, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_oxili_gfx3d_clk", 0x1EA, 4, GCC, + 0x1EA, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pcie_0_aux_clk", 0xAB, 4, GCC, + 0xAB, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pcie_0_cfg_ahb_clk", 0xAA, 4, GCC, + 0xAA, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pcie_0_mstr_axi_clk", 0xA9, 4, GCC, + 0xA9, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pcie_0_pipe_clk", 0xAC, 4, GCC, + 0xAC, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pcie_0_slv_axi_clk", 0xA8, 4, GCC, + 0xA8, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pcnoc_usb2_clk", 0x9, 4, GCC, + 0x9, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pcnoc_usb3_clk", 0xA, 4, GCC, + 0xA, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pdm2_clk", 0xD2, 4, GCC, + 0xD2, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pdm_ahb_clk", 0xD0, 4, GCC, + 0xD0, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_prng_ahb_clk", 0xD8, 4, GCC, + 0xD8, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pwm0_xo512_clk", 0xD3, 4, GCC, + 0xD3, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pwm1_xo512_clk", 0xD4, 4, GCC, + 0xD4, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_pwm2_xo512_clk", 0xD5, 4, GCC, + 0xD5, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_sdcc1_ahb_clk", 0x69, 4, GCC, + 0x69, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_sdcc1_apps_clk", 0x68, 4, GCC, + 0x68, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_sdcc1_ice_core_clk", 0x6A, 4, GCC, + 0x6A, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_sdcc2_ahb_clk", 0x71, 4, GCC, + 0x71, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_sdcc2_apps_clk", 0x70, 4, GCC, + 0x70, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_sys_noc_usb3_clk", 0x1, 4, GCC, + 0x1, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb20_mock_utmi_clk", 0x65, 4, GCC, + 0x65, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb2a_phy_sleep_clk", 0x63, 4, GCC, + 0x63, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb30_master_clk", 0x78, 4, GCC, + 0x78, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb30_mock_utmi_clk", 0x7A, 4, GCC, + 0x7A, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb30_sleep_clk", 0x79, 4, GCC, + 0x79, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb3_phy_aux_clk", 0x7C, 4, GCC, + 0x7C, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb3_phy_pipe_clk", 0x7B, 4, GCC, + 0x7B, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb_hs_inactivity_timers_clk", 0x62, 4, GCC, + 0x62, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb_hs_phy_cfg_ahb_clk", 0x64, 4, GCC, + 0x64, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + { "gcc_usb_hs_system_clk", 0x60, 4, GCC, + 0x60, 0x1FF, 0, 0xF000, 12, 4, 0x74000, 0x74000, 0x74000 }, + ), + .hw.init = &(struct clk_init_data){ + .name = "gcc_debug_mux", + .ops = &clk_debug_mux_ops, + .parent_names = debug_mux_parent_names, + .num_parents = ARRAY_SIZE(debug_mux_parent_names), + .flags = CLK_IS_MEASURE, + }, +}; + +static const struct of_device_id clk_debug_match_table[] = { + { .compatible = "qcom,debugcc-qcs405" }, + { } +}; + +static int map_debug_bases(struct platform_device *pdev, char *base, int cc) +{ + if (!of_get_property(pdev->dev.of_node, base, NULL)) + return -ENODEV; + + gcc_debug_mux.regmap[cc] = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + base); + if (IS_ERR(gcc_debug_mux.regmap[cc])) { + pr_err("Failed to map %s (ret=%ld)\n", base, + PTR_ERR(gcc_debug_mux.regmap[cc])); + return PTR_ERR(gcc_debug_mux.regmap[cc]); + } + return 0; +} + +static int clk_debug_qcs405_probe(struct platform_device *pdev) +{ + struct clk *clk; + int ret = 0; + + clk = devm_clk_get(&pdev->dev, "xo_clk_src"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Unable to get xo clock\n"); + return PTR_ERR(clk); + } + + debug_mux_priv.cxo = clk; + + gcc_debug_mux.regmap = devm_kcalloc(&pdev->dev, MAX_NUM_CC, + sizeof(*gcc_debug_mux.regmap), GFP_KERNEL); + if (!gcc_debug_mux.regmap) + return -ENOMEM; + + ret = map_debug_bases(pdev, "qcom,gcc", GCC); + if (ret) + return ret; + + clk = devm_clk_register(&pdev->dev, &gcc_debug_mux.hw); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Unable to register GCC debug mux\n"); + return PTR_ERR(clk); + } + + ret = clk_debug_measure_register(&gcc_debug_mux.hw); + if (ret) + dev_err(&pdev->dev, "Could not register Measure clock\n"); + + return ret; +} + +static struct platform_driver clk_debug_driver = { + .probe = clk_debug_qcs405_probe, + .driver = { + .name = "debugcc-qcs405", + .of_match_table = clk_debug_match_table, + .owner = THIS_MODULE, + }, +}; + +int __init clk_debug_qcs405_init(void) +{ + return platform_driver_register(&clk_debug_driver); +} +fs_initcall(clk_debug_qcs405_init); + +MODULE_DESCRIPTION("QTI DEBUG CC QCS405 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:debugcc-qcs405"); diff --git a/drivers/clk/qcom/debugcc-sm8150.c b/drivers/clk/qcom/debugcc-sm8150.c index e16102c23b56bf0b503e985ed866e51981411fa8..d30acb6ce87e2f4c5512200132eb12c0ab20391b 100644 --- a/drivers/clk/qcom/debugcc-sm8150.c +++ b/drivers/clk/qcom/debugcc-sm8150.c @@ -53,7 +53,6 @@ static const char *const debug_mux_parent_names[] = { "cam_cc_csiphy3_clk", "cam_cc_fd_core_clk", "cam_cc_fd_core_uar_clk", - "cam_cc_gdsc_clk", "cam_cc_icp_ahb_clk", "cam_cc_icp_clk", "cam_cc_ife_0_axi_clk", @@ -86,8 +85,6 @@ static const char *const debug_mux_parent_names[] = { "cam_cc_mclk1_clk", "cam_cc_mclk2_clk", "cam_cc_mclk3_clk", - "cam_cc_qdss_debug_clk", - "cam_cc_qdss_debug_xo_clk", "disp_cc_mdss_ahb_clk", "disp_cc_mdss_byte0_clk", "disp_cc_mdss_byte0_intf_clk", @@ -130,7 +127,6 @@ static const char *const debug_mux_parent_names[] = { "gcc_aggre_ufs_phy_axi_clk", "gcc_aggre_usb3_prim_axi_clk", "gcc_aggre_usb3_sec_axi_clk", - "gcc_boot_rom_ahb_clk", "gcc_camera_ahb_clk", "gcc_camera_hf_axi_clk", "gcc_camera_sf_axi_clk", @@ -141,8 +137,6 @@ static const char *const debug_mux_parent_names[] = { "gcc_cfg_noc_usb3_prim_axi_clk", "gcc_cfg_noc_usb3_sec_axi_clk", "gcc_cpuss_ahb_clk", - "gcc_cpuss_dvm_bus_clk", - "gcc_cpuss_gnoc_clk", "gcc_cpuss_rbcpr_clk", "gcc_ddrss_gpu_axi_clk", "gcc_disp_ahb_clk", @@ -186,11 +180,6 @@ static const char *const debug_mux_parent_names[] = { "gcc_pdm_ahb_clk", "gcc_pdm_xo4_clk", "gcc_prng_ahb_clk", - "gcc_qmip_camera_nrt_ahb_clk", - "gcc_qmip_camera_rt_ahb_clk", - "gcc_qmip_disp_ahb_clk", - "gcc_qmip_video_cvp_ahb_clk", - "gcc_qmip_video_vcodec_ahb_clk", "gcc_qspi_cnoc_periph_ahb_clk", "gcc_qspi_core_clk", "gcc_qupv3_wrap0_core_2x_clk", @@ -219,19 +208,12 @@ static const char *const debug_mux_parent_names[] = { "gcc_qupv3_wrap2_s3_clk", "gcc_qupv3_wrap2_s4_clk", "gcc_qupv3_wrap2_s5_clk", - "gcc_qupv3_wrap_0_m_ahb_clk", - "gcc_qupv3_wrap_0_s_ahb_clk", - "gcc_qupv3_wrap_1_m_ahb_clk", - "gcc_qupv3_wrap_1_s_ahb_clk", - "gcc_qupv3_wrap_2_m_ahb_clk", - "gcc_qupv3_wrap_2_s_ahb_clk", "gcc_sdcc2_ahb_clk", "gcc_sdcc2_apps_clk", "gcc_sdcc4_ahb_clk", "gcc_sdcc4_apps_clk", "gcc_sys_noc_cpuss_ahb_clk", "gcc_tsif_ahb_clk", - "gcc_tsif_inactivity_timers_clk", "gcc_tsif_ref_clk", "gcc_ufs_card_ahb_clk", "gcc_ufs_card_axi_clk", @@ -251,10 +233,8 @@ static const char *const debug_mux_parent_names[] = { "gcc_ufs_phy_unipro_core_clk", "gcc_usb30_prim_master_clk", "gcc_usb30_prim_mock_utmi_clk", - "gcc_usb30_prim_sleep_clk", "gcc_usb30_sec_master_clk", "gcc_usb30_sec_mock_utmi_clk", - "gcc_usb30_sec_sleep_clk", "gcc_usb3_prim_phy_aux_clk", "gcc_usb3_prim_phy_com_aux_clk", "gcc_usb3_prim_phy_pipe_clk", @@ -266,22 +246,16 @@ static const char *const debug_mux_parent_names[] = { "gcc_video_axi1_clk", "gcc_video_axic_clk", "gcc_video_xo_clk", - "gpu_cc_acd_ahb_clk", - "gpu_cc_acd_cxo_clk", "gpu_cc_ahb_clk", - "gpu_cc_crc_ahb_clk", "gpu_cc_cx_apb_clk", "gpu_cc_cx_gmu_clk", "gpu_cc_cx_qdss_at_clk", "gpu_cc_cx_qdss_trig_clk", - "gpu_cc_cx_qdss_tsctr_clk", "gpu_cc_cx_snoc_dvm_clk", "gpu_cc_cxo_aon_clk", "gpu_cc_cxo_clk", "gpu_cc_gx_gmu_clk", - "gpu_cc_gx_qdss_tsctr_clk", "gpu_cc_gx_vsense_clk", - "gpu_cc_sleep_clk", "measure_only_gpu_cc_cx_gfx3d_clk", "measure_only_gpu_cc_cx_gfx3d_slv_clk", "measure_only_gpu_cc_gx_gfx3d_clk", @@ -297,16 +271,17 @@ static const char *const debug_mux_parent_names[] = { "npu_cc_npu_core_clk", "npu_cc_npu_core_cti_clk", "npu_cc_npu_cpc_clk", - "npu_cc_npu_cpc_timer_clk", "npu_cc_perf_cnt_clk", - "npu_cc_qtimer_core_clk", - "npu_cc_sleep_clk", "npu_cc_xo_clk", "video_cc_iris_ahb_clk", "video_cc_mvs0_core_clk", "video_cc_mvs1_core_clk", "video_cc_mvsc_core_clk", "video_cc_xo_clk", + "l3_clk", + "pwrcl_clk", + "perfcl_clk", + "perfpcl_clk" }; static struct clk_debug_mux gcc_debug_mux = { @@ -359,8 +334,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0x28, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, { "cam_cc_fd_core_uar_clk", 0x55, 1, CAM_CC, 0x29, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, - { "cam_cc_gdsc_clk", 0x55, 1, CAM_CC, - 0x3C, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, { "cam_cc_icp_ahb_clk", 0x55, 1, CAM_CC, 0x37, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, { "cam_cc_icp_clk", 0x55, 1, CAM_CC, @@ -425,10 +398,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0x3, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, { "cam_cc_mclk3_clk", 0x55, 1, CAM_CC, 0x4, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, - { "cam_cc_qdss_debug_clk", 0x55, 1, CAM_CC, - 0x3D, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, - { "cam_cc_qdss_debug_xo_clk", 0x55, 1, CAM_CC, - 0x3E, 0xFF, 0, 0xF, 0, 4, 0xD000, 0xD004, 0xD008 }, { "disp_cc_mdss_ahb_clk", 0x56, 1, DISP_CC, 0x2B, 0xFF, 0, 0x3, 0, 4, 0x7000, 0x5008, 0x500C }, { "disp_cc_mdss_byte0_clk", 0x56, 1, DISP_CC, @@ -513,8 +482,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0x13E, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_aggre_usb3_sec_axi_clk", 0x13F, 1, GCC, 0x13F, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_boot_rom_ahb_clk", 0xA0, 1, GCC, - 0xA0, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_camera_ahb_clk", 0x43, 1, GCC, 0x43, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_camera_hf_axi_clk", 0x4D, 1, GCC, @@ -535,10 +502,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0x23, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_cpuss_ahb_clk", 0xE0, 1, GCC, 0xE0, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_cpuss_dvm_bus_clk", 0xE5, 1, GCC, - 0xE5, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_cpuss_gnoc_clk", 0xE1, 1, GCC, - 0xE1, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_cpuss_rbcpr_clk", 0xE2, 1, GCC, 0xE2, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_ddrss_gpu_axi_clk", 0xC0, 1, GCC, @@ -625,16 +588,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0x99, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_prng_ahb_clk", 0x9B, 1, GCC, 0x9B, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qmip_camera_nrt_ahb_clk", 0x47, 1, GCC, - 0x47, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qmip_camera_rt_ahb_clk", 0x48, 1, GCC, - 0x48, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qmip_disp_ahb_clk", 0x49, 1, GCC, - 0x49, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qmip_video_cvp_ahb_clk", 0x45, 1, GCC, - 0x45, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qmip_video_vcodec_ahb_clk", 0x46, 1, GCC, - 0x46, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_qspi_cnoc_periph_ahb_clk", 0x178, 1, GCC, 0x178, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_qspi_core_clk", 0x179, 1, GCC, @@ -691,18 +644,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0x189, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_qupv3_wrap2_s5_clk", 0x18A, 1, GCC, 0x18A, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qupv3_wrap_0_m_ahb_clk", 0x82, 1, GCC, - 0x82, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qupv3_wrap_0_s_ahb_clk", 0x83, 1, GCC, - 0x83, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qupv3_wrap_1_m_ahb_clk", 0x8E, 1, GCC, - 0x8E, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qupv3_wrap_1_s_ahb_clk", 0x8F, 1, GCC, - 0x8F, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qupv3_wrap_2_m_ahb_clk", 0x181, 1, GCC, - 0x181, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_qupv3_wrap_2_s_ahb_clk", 0x182, 1, GCC, - 0x182, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_sdcc2_ahb_clk", 0x7F, 1, GCC, 0x7F, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_sdcc2_apps_clk", 0x7E, 1, GCC, @@ -715,8 +656,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0xC, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_tsif_ahb_clk", 0x9C, 1, GCC, 0x9C, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_tsif_inactivity_timers_clk", 0x9E, 1, GCC, - 0x9E, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_tsif_ref_clk", 0x9D, 1, GCC, 0x9D, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_ufs_card_ahb_clk", 0x107, 1, GCC, @@ -755,14 +694,10 @@ static struct clk_debug_mux gcc_debug_mux = { 0x6B, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_usb30_prim_mock_utmi_clk", 0x6D, 1, GCC, 0x6D, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_usb30_prim_sleep_clk", 0x6C, 1, GCC, - 0x6C, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_usb30_sec_master_clk", 0x72, 1, GCC, 0x72, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_usb30_sec_mock_utmi_clk", 0x74, 1, GCC, 0x74, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gcc_usb30_sec_sleep_clk", 0x73, 1, GCC, - 0x73, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_usb3_prim_phy_aux_clk", 0x6E, 1, GCC, 0x6E, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_usb3_prim_phy_com_aux_clk", 0x6F, 1, GCC, @@ -785,14 +720,8 @@ static struct clk_debug_mux gcc_debug_mux = { 0x4C, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, { "gcc_video_xo_clk", 0x51, 1, GCC, 0x51, 0x3FF, 0, 0xF, 0, 1, 0x62000, 0x62004, 0x62008 }, - { "gpu_cc_acd_ahb_clk", 0x162, 1, GPU_CC, - 0x20, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, - { "gpu_cc_acd_cxo_clk", 0x162, 1, GPU_CC, - 0x1F, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_ahb_clk", 0x162, 1, GPU_CC, 0x10, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, - { "gpu_cc_crc_ahb_clk", 0x162, 1, GPU_CC, - 0x11, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_cx_apb_clk", 0x162, 1, GPU_CC, 0x14, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_cx_gmu_clk", 0x162, 1, GPU_CC, @@ -801,8 +730,6 @@ static struct clk_debug_mux gcc_debug_mux = { 0x12, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_cx_qdss_trig_clk", 0x162, 1, GPU_CC, 0x17, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, - { "gpu_cc_cx_qdss_tsctr_clk", 0x162, 1, GPU_CC, - 0x13, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_cx_snoc_dvm_clk", 0x162, 1, GPU_CC, 0x15, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_cxo_aon_clk", 0x162, 1, GPU_CC, @@ -811,12 +738,8 @@ static struct clk_debug_mux gcc_debug_mux = { 0x19, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_gx_gmu_clk", 0x162, 1, GPU_CC, 0xF, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, - { "gpu_cc_gx_qdss_tsctr_clk", 0x162, 1, GPU_CC, - 0xD, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "gpu_cc_gx_vsense_clk", 0x162, 1, GPU_CC, 0xC, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, - { "gpu_cc_sleep_clk", 0x162, 1, GPU_CC, - 0x16, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "measure_only_gpu_cc_cx_gfx3d_clk", 0x162, 1, GPU_CC, 0x1A, 0xFF, 0, 0x3, 0, 2, 0x1568, 0x10FC, 0x1100 }, { "measure_only_gpu_cc_cx_gfx3d_slv_clk", 0x162, 1, GPU_CC, @@ -847,14 +770,8 @@ static struct clk_debug_mux gcc_debug_mux = { 0xC, 0xFF, 0, 0x3, 0, 2, 0x4000, 0x3004, 0x3008 }, { "npu_cc_npu_cpc_clk", 0x180, 1, NPU_CC, 0x3, 0xFF, 0, 0x3, 0, 2, 0x4000, 0x3004, 0x3008 }, - { "npu_cc_npu_cpc_timer_clk", 0x180, 1, NPU_CC, - 0x5, 0xFF, 0, 0x3, 0, 2, 0x4000, 0x3004, 0x3008 }, { "npu_cc_perf_cnt_clk", 0x180, 1, NPU_CC, 0x10, 0xFF, 0, 0x3, 0, 2, 0x4000, 0x3004, 0x3008 }, - { "npu_cc_qtimer_core_clk", 0x180, 1, NPU_CC, - 0x6, 0xFF, 0, 0x3, 0, 2, 0x4000, 0x3004, 0x3008 }, - { "npu_cc_sleep_clk", 0x180, 1, NPU_CC, - 0x7, 0xFF, 0, 0x3, 0, 2, 0x4000, 0x3004, 0x3008 }, { "npu_cc_xo_clk", 0x180, 1, NPU_CC, 0x11, 0xFF, 0, 0x3, 0, 2, 0x4000, 0x3004, 0x3008 }, { "video_cc_iris_ahb_clk", 0x57, 1, VIDEO_CC, @@ -867,6 +784,14 @@ static struct clk_debug_mux gcc_debug_mux = { 0x1, 0x3F, 0, 0x7, 0, 5, 0xA4C, 0x938, 0x940 }, { "video_cc_xo_clk", 0x57, 1, VIDEO_CC, 0x8, 0x3F, 0, 0x7, 0, 5, 0xA4C, 0x938, 0x940 }, + { "l3_clk", 0xE8, 4, CPU_CC, + 0x46, 0x7F, 4, 0xf, 11, 1, 0x0, 0x0, U32_MAX, 16 }, + { "pwrcl_clk", 0xE8, 4, CPU_CC, + 0x44, 0x7F, 4, 0xf, 11, 1, 0x0, 0x0, U32_MAX, 16 }, + { "perfcl_clk", 0xE8, 4, CPU_CC, + 0x45, 0x7F, 4, 0xf, 11, 1, 0x0, 0x0, U32_MAX, 16 }, + { "perfpcl_clk", 0xE8, 4, CPU_CC, + 0x47, 0x7F, 4, 0xf, 11, 1, 0x0, 0x0, U32_MAX, 16 }, ), .hw.init = &(struct clk_init_data){ .name = "gcc_debug_mux", @@ -941,6 +866,10 @@ static int clk_debug_sm8150_probe(struct platform_device *pdev) if (ret) return ret; + ret = map_debug_bases(pdev, "qcom,cpucc", CPU_CC); + if (ret) + return ret; + clk = devm_clk_register(&pdev->dev, &gcc_debug_mux.hw); if (IS_ERR(clk)) { dev_err(&pdev->dev, "Unable to register GCC debug mux\n"); diff --git a/drivers/clk/qcom/gcc-qcs405.c b/drivers/clk/qcom/gcc-qcs405.c new file mode 100644 index 0000000000000000000000000000000000000000..eb11ebeae5c8a2274ff691c917ce6a02148b07cb --- /dev/null +++ b/drivers/clk/qcom/gcc-qcs405.c @@ -0,0 +1,2847 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "clk: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "clk-voter.h" +#include "reset.h" +#include "clk-alpha-pll.h" +#include "vdd-level-405.h" + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } +#define F_SLEW(f, s, h, m, n, sf) { (f), (s), (2 * (h) - 1), (m), (n), (sf) } + +static DEFINE_VDD_REGULATORS(vdd_cx, VDD_NUM, 1, vdd_corner); + +enum { + P_CORE_BI_PLL_TEST_SE, + P_DSI0_PHY_PLL_OUT_BYTECLK, + P_DSI0_PHY_PLL_OUT_DSICLK, + P_GPLL0_OUT_AUX, + P_GPLL0_OUT_MAIN, + P_GPLL1_OUT_MAIN, + P_GPLL3_OUT_MAIN, + P_GPLL4_OUT_AUX, + P_GPLL4_OUT_MAIN, + P_GPLL6_OUT_AUX, + P_HDMI_PHY_PLL_CLK, + P_PCIE_0_PIPE_CLK, + P_SLEEP_CLK, + P_XO, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_0[] = { + "cxo", + "gpll0_out_main", + "core_bi_pll_test_se", +}; + +static const char * const gcc_parent_names_ao_0[] = { + "cxo_a", + "gpll0_ao_out_main", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_XO, 0 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_1[] = { + "cxo", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL6_OUT_AUX, 2 }, + { P_SLEEP_CLK, 6 }, +}; + +static const char * const gcc_parent_names_2[] = { + "cxo", + "gpll0_out_main", + "gpll6_out_aux", + "sleep_clk", +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL6_OUT_AUX, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_3[] = { + "cxo", + "gpll0_out_main", + "gpll6_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_XO, 0 }, + { P_GPLL1_OUT_MAIN, 1 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_4[] = { + "cxo", + "gpll1_out_main", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_XO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 1 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_5[] = { + "cxo", + "dsi0_phy_pll_out_byteclk", + "gpll0_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_XO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 2 }, + { P_GPLL0_OUT_AUX, 3 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_6[] = { + "cxo", + "dsi0_phy_pll_out_byteclk", + "gpll0_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_7[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL3_OUT_MAIN, 2 }, + { P_GPLL6_OUT_AUX, 3 }, + { P_GPLL4_OUT_AUX, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_7[] = { + "cxo", + "gpll0_out_main", + "gpll3_out_main", + "gpll6_out_aux", + "gpll4_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_XO, 0 }, + { P_HDMI_PHY_PLL_CLK, 1 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_8[] = { + "cxo", + "hdmi_phy_pll_clk", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_9[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 2 }, + { P_GPLL6_OUT_AUX, 3 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_9[] = { + "cxo", + "gpll0_out_main", + "dsi0_phy_pll_out_dsiclk", + "gpll6_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_10[] = { + { P_XO, 0 }, + { P_SLEEP_CLK, 1 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_10[] = { + "cxo", + "sleep_clk", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_11[] = { + { P_XO, 0 }, + { P_PCIE_0_PIPE_CLK, 1 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_11[] = { + "cxo", + "pcie_0_pipe_clk", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_12[] = { + { P_XO, 0 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_12[] = { + "cxo", + "dsi0_phy_pll_out_dsiclk", + "gpll0_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_13[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, + { P_GPLL6_OUT_AUX, 3 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_13[] = { + "cxo", + "gpll0_out_main", + "gpll4_out_main", + "gpll6_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_14[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_AUX, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_14[] = { + "cxo", + "gpll0_out_main", + "gpll4_out_aux", + "core_bi_pll_test_se", +}; + +static const struct parent_map gcc_parent_map_15[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gcc_parent_names_15[] = { + "cxo", + "gpll0_out_aux", + "core_bi_pll_test_se", +}; + +static unsigned int soft_vote_gpll0; + +static struct clk_alpha_pll gpll0_sleep_clk_src = { + .offset = 0x21000, + .clkr = { + .enable_reg = 0x45008, + .enable_mask = BIT(23), + .enable_is_inverted = true, + .hw.init = &(struct clk_init_data){ + .name = "gpll0_sleep_clk_src", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_pll_sleep_vote_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll0_out_main = { + .offset = 0x21000, + .soft_vote = &soft_vote_gpll0, + .soft_vote_mask = PLL_SOFT_VOTE_PRIMARY, + .flags = SUPPORTS_FSM_MODE, + .clkr = { + .enable_reg = 0x45000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpll0_out_main", + .parent_names = (const char *[]) + { "gpll0_sleep_clk_src" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll0_ao_out_main = { + .offset = 0x21000, + .soft_vote = &soft_vote_gpll0, + .soft_vote_mask = PLL_SOFT_VOTE_CPU, + .flags = SUPPORTS_FSM_MODE, + .clkr = { + .enable_reg = 0x45000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpll0_ao_out_main", + .parent_names = (const char *[]){ "cxo_a" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll1_out_main = { + .offset = 0x20000, + .clkr = { + .enable_reg = 0x45000, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gpll1_out_main", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +/* 930MHz configuration */ +static const struct alpha_pll_config gpll3_config = { + .l = 48, + .alpha = 0x0, + .alpha_u = 0x70, + .alpha_en_mask = BIT(24), + .post_div_mask = 0xf << 8, + .post_div_val = 0x1 << 8, + .vco_mask = 0x3 << 20, + .main_output_mask = 0x1, + .config_ctl_val = 0x4001055b, + .test_ctl_hi_val = 0x40000600, + .test_ctl_hi_mask = 0xffffffff, +}; + +static struct pll_vco gpll3_vco[] = { + { 700000000, 1400000000, 0 }, +}; + +static struct clk_alpha_pll gpll3_out_main = { + .offset = 0x22000, + .flags = SUPPORTS_SLEW, + .vco_table = gpll3_vco, + .num_vco = ARRAY_SIZE(gpll3_vco), + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpll3_out_main", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_slew_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_NOMINAL] = 1400000000}, + }, + }, +}; + +static struct clk_alpha_pll gpll4_out_main = { + .offset = 0x24000, + .clkr = { + .enable_reg = 0x45000, + .enable_mask = BIT(5), + .hw.init = &(struct clk_init_data){ + .name = "gpll4_out_main", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static struct clk_pll gpll6 = { + .l_reg = 0x37004, + .m_reg = 0x37008, + .n_reg = 0x3700C, + .config_reg = 0x37014, + .mode_reg = 0x37000, + .status_reg = 0x3701C, + .status_bit = 17, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll6", + .parent_names = (const char *[]){ "cxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + + +static struct clk_regmap gpll6_out_aux = { + .enable_reg = 0x45000, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "gpll6_out_aux", + .parent_names = (const char *[]){ "gpll6" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static const struct freq_tbl ftbl_apss_ahb_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(133333333, P_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 apss_ahb_clk_src = { + .cmd_rcgr = 0x46000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_apss_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "apss_ahb_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_blsp1_qup0_i2c_apps_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_qup0_i2c_apps_clk_src = { + .cmd_rcgr = 0x602c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup0_i2c_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 50000000}, + }, +}; + +static const struct freq_tbl ftbl_blsp1_qup0_spi_apps_clk_src[] = { + F(960000, P_XO, 10, 1, 2), + F(4800000, P_XO, 4, 0, 0), + F(9600000, P_XO, 2, 0, 0), + F(16000000, P_GPLL0_OUT_MAIN, 10, 1, 5), + F(19200000, P_XO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_qup0_spi_apps_clk_src = { + .cmd_rcgr = 0x6034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup0_spi_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 25000000, + [VDD_NOMINAL] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup1_i2c_apps_clk_src = { + .cmd_rcgr = 0x200c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_i2c_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup1_spi_apps_clk_src = { + .cmd_rcgr = 0x2024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_spi_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 25000000, + [VDD_NOMINAL] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup2_i2c_apps_clk_src = { + .cmd_rcgr = 0x3000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_i2c_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup2_spi_apps_clk_src = { + .cmd_rcgr = 0x3014, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_spi_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 25000000, + [VDD_NOMINAL] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup3_i2c_apps_clk_src = { + .cmd_rcgr = 0x4000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_i2c_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup3_spi_apps_clk_src = { + .cmd_rcgr = 0x4024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_spi_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 25000000, + [VDD_NOMINAL] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup4_i2c_apps_clk_src = { + .cmd_rcgr = 0x5000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_i2c_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp1_qup4_spi_apps_clk_src = { + .cmd_rcgr = 0x5024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_spi_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 25000000, + [VDD_NOMINAL] = 50000000}, + }, +}; + +static const struct freq_tbl ftbl_blsp1_uart0_apps_clk_src[] = { + F(3686400, P_GPLL0_OUT_MAIN, 1, 72, 15625), + F(7372800, P_GPLL0_OUT_MAIN, 1, 144, 15625), + F(14745600, P_GPLL0_OUT_MAIN, 1, 288, 15625), + F(16000000, P_GPLL0_OUT_MAIN, 10, 1, 5), + F(19200000, P_XO, 1, 0, 0), + F(24000000, P_GPLL0_OUT_MAIN, 1, 3, 100), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(32000000, P_GPLL0_OUT_MAIN, 1, 1, 25), + F(40000000, P_GPLL0_OUT_MAIN, 1, 1, 20), + F(46400000, P_GPLL0_OUT_MAIN, 1, 29, 500), + F(48000000, P_GPLL0_OUT_MAIN, 1, 3, 50), + F(51200000, P_GPLL0_OUT_MAIN, 1, 8, 125), + F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 100), + F(58982400, P_GPLL0_OUT_MAIN, 1, 1152, 15625), + F(60000000, P_GPLL0_OUT_MAIN, 1, 3, 40), + F(64000000, P_GPLL0_OUT_MAIN, 1, 2, 25), + { } +}; + +static struct clk_rcg2 blsp1_uart0_apps_clk_src = { + .cmd_rcgr = 0x600c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_uart0_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart0_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 32000000, + [VDD_NOMINAL] = 64000000}, + }, +}; + +static struct clk_rcg2 blsp1_uart1_apps_clk_src = { + .cmd_rcgr = 0x2044, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_uart0_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart1_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 32000000, + [VDD_NOMINAL] = 64000000}, + }, +}; + +static struct clk_rcg2 blsp1_uart2_apps_clk_src = { + .cmd_rcgr = 0x3034, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_uart0_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart2_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 32000000, + [VDD_NOMINAL] = 64000000}, + }, +}; + +static struct clk_rcg2 blsp1_uart3_apps_clk_src = { + .cmd_rcgr = 0x4014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_uart0_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart3_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 32000000, + [VDD_NOMINAL] = 64000000}, + }, +}; + +static struct clk_rcg2 blsp2_qup0_i2c_apps_clk_src = { + .cmd_rcgr = 0xc00c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup0_i2c_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp2_qup0_spi_apps_clk_src = { + .cmd_rcgr = 0xc024, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_qup0_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup0_spi_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 25000000, + [VDD_NOMINAL] = 50000000}, + }, +}; + +static struct clk_rcg2 blsp2_uart0_apps_clk_src = { + .cmd_rcgr = 0xc044, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_blsp1_uart0_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_uart0_apps_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 32000000, + [VDD_NOMINAL] = 64000000}, + }, +}; + +static struct clk_rcg2 byte0_clk_src = { + .cmd_rcgr = 0x4d044, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .clkr.hw.init = &(struct clk_init_data){ + .name = "byte0_clk_src", + .parent_names = gcc_parent_names_5, + .num_parents = 4, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_byte2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 125000000, + [VDD_NOMINAL] = 187500000}, + }, +}; + +static const struct freq_tbl ftbl_emac_clk_src[] = { + F(125000000, P_GPLL1_OUT_MAIN, 4, 0, 0), + F(250000000, P_GPLL1_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 emac_clk_src = { + .cmd_rcgr = 0x4e01c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_emac_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "emac_clk_src", + .parent_names = gcc_parent_names_4, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 125000000, + [VDD_NOMINAL] = 250000000}, + }, +}; + +static struct clk_rcg2 emac_ptp_clk_src = { + .cmd_rcgr = 0x4e014, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_emac_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "emac_ptp_clk_src", + .parent_names = gcc_parent_names_4, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 125000000, + [VDD_NOMINAL] = 250000000}, + }, +}; + +static const struct freq_tbl ftbl_esc0_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 esc0_clk_src = { + .cmd_rcgr = 0x4d05c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "esc0_clk_src", + .parent_names = gcc_parent_names_6, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 19200000}, + }, +}; + +static const struct freq_tbl ftbl_gfx3d_clk_src[] = { + F_SLEW(19200000, P_XO, 1, 0, 0, FIXED_FREQ_SRC), + F_SLEW(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0, FIXED_FREQ_SRC), + F_SLEW(80000000, P_GPLL0_OUT_MAIN, 10, 0, 0, FIXED_FREQ_SRC), + F_SLEW(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0, FIXED_FREQ_SRC), + F_SLEW(160000000, P_GPLL0_OUT_MAIN, 5, 0, 0, FIXED_FREQ_SRC), + F_SLEW(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0, FIXED_FREQ_SRC), + F_SLEW(228571429, P_GPLL0_OUT_MAIN, 3.5, 0, 0, FIXED_FREQ_SRC), + F_SLEW(240000000, P_GPLL6_OUT_AUX, 4.5, 0, 0, FIXED_FREQ_SRC), + F_SLEW(266666667, P_GPLL0_OUT_MAIN, 3, 0, 0, FIXED_FREQ_SRC), + F_SLEW(270000000, P_GPLL6_OUT_AUX, 4, 0, 0, FIXED_FREQ_SRC), + F_SLEW(320000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0, FIXED_FREQ_SRC), + F_SLEW(400000000, P_GPLL0_OUT_MAIN, 2, 0, 0, FIXED_FREQ_SRC), + F_SLEW(484800000, P_GPLL3_OUT_MAIN, 1, 0, 0, 969600000), + F_SLEW(523200000, P_GPLL3_OUT_MAIN, 1, 0, 0, 1046400000), + F_SLEW(550000000, P_GPLL3_OUT_MAIN, 1, 0, 0, 1100000000), + F_SLEW(598000000, P_GPLL3_OUT_MAIN, 1, 0, 0, 1196000000), + { } +}; + +static struct clk_rcg2 gfx3d_clk_src = { + .cmd_rcgr = 0x59000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gfx3d_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gfx3d_clk_src", + .parent_names = gcc_parent_names_7, + .num_parents = 6, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 270000000, + [VDD_NOMINAL] = 484800000, + [VDD_HIGH] = 598000000}, + }, +}; + +static const struct freq_tbl ftbl_gp1_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gp1_clk_src = { + .cmd_rcgr = 0x8004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gp1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp1_clk_src", + .parent_names = gcc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 100000000, + [VDD_NOMINAL] = 200000000}, + }, +}; + +static struct clk_rcg2 gp2_clk_src = { + .cmd_rcgr = 0x9004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gp1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp2_clk_src", + .parent_names = gcc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 100000000, + [VDD_NOMINAL] = 200000000}, + }, +}; + +static struct clk_rcg2 gp3_clk_src = { + .cmd_rcgr = 0xa004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gp1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp3_clk_src", + .parent_names = gcc_parent_names_2, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 100000000, + [VDD_NOMINAL] = 200000000}, + }, +}; + +static struct clk_rcg2 hdmi_app_clk_src = { + .cmd_rcgr = 0x4d0e4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "hdmi_app_clk_src", + .parent_names = gcc_parent_names_1, + .num_parents = 2, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 19200000}, + }, +}; + +static struct clk_rcg2 hdmi_pclk_clk_src = { + .cmd_rcgr = 0x4d0dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "hdmi_pclk_clk_src", + .parent_names = gcc_parent_names_8, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 75000000, + [VDD_NOMINAL] = 150000000}, + }, +}; + +static const struct freq_tbl ftbl_mdp_clk_src[] = { + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(80000000, P_GPLL0_OUT_MAIN, 10, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(145454545, P_GPLL0_OUT_MAIN, 5.5, 0, 0), + F(160000000, P_GPLL0_OUT_MAIN, 5, 0, 0), + F(177777778, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(266666667, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(320000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 mdp_clk_src = { + .cmd_rcgr = 0x4d014, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_9, + .freq_tbl = ftbl_mdp_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mdp_clk_src", + .parent_names = gcc_parent_names_9, + .num_parents = 5, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 160000000, + [VDD_NOMINAL] = 266666667, + [VDD_HIGH] = 320000000}, + }, +}; + +static const struct freq_tbl ftbl_pcie_0_aux_clk_src[] = { + F(1200000, P_XO, 16, 0, 0), + { } +}; + +static struct clk_rcg2 pcie_0_aux_clk_src = { + .cmd_rcgr = 0x3e024, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .freq_tbl = ftbl_pcie_0_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie_0_aux_clk_src", + .parent_names = gcc_parent_names_10, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 1200000}, + }, +}; + +static const struct freq_tbl ftbl_pcie_0_pipe_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(125000000, P_PCIE_0_PIPE_CLK, 2, 0, 0), + F(250000000, P_PCIE_0_PIPE_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 pcie_0_pipe_clk_src = { + .cmd_rcgr = 0x3e01c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_11, + .freq_tbl = ftbl_pcie_0_pipe_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie_0_pipe_clk_src", + .parent_names = gcc_parent_names_11, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 125000000, + [VDD_NOMINAL] = 250000000}, + }, +}; + +static struct clk_rcg2 pclk0_clk_src = { + .cmd_rcgr = 0x4d000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_12, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pclk0_clk_src", + .parent_names = gcc_parent_names_12, + .num_parents = 4, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_pixel_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 166666667, + [VDD_NOMINAL] = 250000000}, + }, +}; + +static const struct freq_tbl ftbl_pdm2_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(64000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0), + { } +}; + +static struct clk_rcg2 pdm2_clk_src = { + .cmd_rcgr = 0x44010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_pdm2_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pdm2_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 64000000}, + }, +}; + +static const struct freq_tbl ftbl_sdcc1_apps_clk_src[] = { + F(144000, P_XO, 16, 3, 25), + F(400000, P_XO, 12, 1, 4), + F(20000000, P_GPLL0_OUT_MAIN, 10, 1, 4), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(177777778, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(192000000, P_GPLL4_OUT_MAIN, 6, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(384000000, P_GPLL4_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 sdcc1_apps_clk_src = { + .cmd_rcgr = 0x42004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_13, + .freq_tbl = ftbl_sdcc1_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc1_apps_clk_src", + .parent_names = gcc_parent_names_13, + .num_parents = 5, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 200000000, + [VDD_NOMINAL] = 400000000}, + }, +}; + +static const struct freq_tbl ftbl_sdcc1_ice_core_clk_src[] = { + F(160000000, P_GPLL0_OUT_MAIN, 5, 0, 0), + F(266666667, P_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x5d000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_sdcc1_ice_core_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc1_ice_core_clk_src", + .parent_names = gcc_parent_names_3, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 160000000, + [VDD_NOMINAL] = 266666667}, + }, +}; + +static const struct freq_tbl ftbl_sdcc2_apps_clk_src[] = { + F(144000, P_XO, 16, 3, 25), + F(400000, P_XO, 12, 1, 4), + F(20000000, P_GPLL0_OUT_MAIN, 10, 1, 4), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(177777778, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 sdcc2_apps_clk_src = { + .cmd_rcgr = 0x43004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_14, + .freq_tbl = ftbl_sdcc2_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc2_apps_clk_src", + .parent_names = gcc_parent_names_14, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 100000000, + [VDD_NOMINAL] = 200000000}, + }, +}; + +static struct clk_rcg2 usb20_mock_utmi_clk_src = { + .cmd_rcgr = 0x41048, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb20_mock_utmi_clk_src", + .parent_names = gcc_parent_names_1, + .num_parents = 2, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 19200000}, + }, +}; + +static const struct freq_tbl ftbl_usb30_master_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(266666667, P_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 usb30_master_clk_src = { + .cmd_rcgr = 0x39028, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_usb30_master_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb30_master_clk_src", + .parent_names = gcc_parent_names_0, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 100000000, + [VDD_NOMINAL] = 200000000, + [VDD_HIGH] = 266666667}, + }, +}; + +static struct clk_rcg2 usb30_mock_utmi_clk_src = { + .cmd_rcgr = 0x3901c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb30_mock_utmi_clk_src", + .parent_names = gcc_parent_names_1, + .num_parents = 2, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 19200000}, + }, +}; + +static struct clk_rcg2 usb3_phy_aux_clk_src = { + .cmd_rcgr = 0x3903c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_pcie_0_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb3_phy_aux_clk_src", + .parent_names = gcc_parent_names_1, + .num_parents = 2, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 1200000}, + }, +}; + +static const struct freq_tbl ftbl_usb_hs_system_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(80000000, P_GPLL0_OUT_MAIN, 10, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(133333333, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(177777778, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + { } +}; + +static struct clk_rcg2 usb_hs_system_clk_src = { + .cmd_rcgr = 0x41010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_usb_hs_system_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb_hs_system_clk_src", + .parent_names = gcc_parent_names_3, + .num_parents = 4, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 80000000, + [VDD_NOMINAL] = 133333333, + [VDD_HIGH] = 177777778}, + }, +}; + +static struct clk_rcg2 vsync_clk_src = { + .cmd_rcgr = 0x4d02c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_15, + .freq_tbl = ftbl_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vsync_clk_src", + .parent_names = gcc_parent_names_15, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .vdd_class = &vdd_cx, + .num_rate_max = VDD_NUM, + .rate_max = (unsigned long[VDD_NUM]) { + [VDD_LOW] = 19200000}, + }, +}; + +static struct clk_branch gcc_apss_ahb_clk = { + .halt_reg = 0x4601c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(14), + .hw.init = &(struct clk_init_data){ + .name = "gcc_apss_ahb_clk", + .parent_names = (const char *[]){ + "apss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_bimc_gfx_clk = { + .halt_reg = 0x59034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x59034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_bimc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_bimc_mdss_clk = { + .halt_reg = 0x31038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x31038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_bimc_mdss_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_ahb_clk = { + .halt_reg = 0x1008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup0_i2c_apps_clk = { + .halt_reg = 0x6028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup0_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup0_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup0_spi_apps_clk = { + .halt_reg = 0x6024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup0_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup0_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = { + .halt_reg = 0x2008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup1_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = { + .halt_reg = 0x2004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup1_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = { + .halt_reg = 0x3010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup2_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = { + .halt_reg = 0x300c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x300c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup2_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = { + .halt_reg = 0x4020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup3_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = { + .halt_reg = 0x401c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x401c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup3_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = { + .halt_reg = 0x5020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup4_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = { + .halt_reg = 0x501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x501c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup4_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart0_apps_clk = { + .halt_reg = 0x6004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart0_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart0_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart1_apps_clk = { + .halt_reg = 0x203c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x203c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart1_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart2_apps_clk = { + .halt_reg = 0x302c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x302c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart2_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart3_apps_clk = { + .halt_reg = 0x400c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x400c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart3_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart3_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_ahb_clk = { + .halt_reg = 0xb008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(20), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup0_i2c_apps_clk = { + .halt_reg = 0xc008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup0_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup0_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup0_spi_apps_clk = { + .halt_reg = 0xc004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup0_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup0_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart0_apps_clk = { + .halt_reg = 0xc03c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xc03c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_uart0_apps_clk", + .parent_names = (const char *[]){ + "blsp2_uart0_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x1300c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eth_axi_clk = { + .halt_reg = 0x4e010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4e010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_eth_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eth_ptp_clk = { + .halt_reg = 0x4e004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4e004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_eth_ptp_clk", + .parent_names = (const char *[]){ + "emac_ptp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eth_rgmii_clk = { + .halt_reg = 0x4e008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4e008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_eth_rgmii_clk", + .parent_names = (const char *[]){ + "emac_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eth_slave_ahb_clk = { + .halt_reg = 0x4e00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4e00c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_eth_slave_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_geni_ir_s_clk = { + .halt_reg = 0xf008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_geni_ir_s_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x8000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp1_clk", + .parent_names = (const char *[]){ + "gp1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x9000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp2_clk", + .parent_names = (const char *[]){ + "gp2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0xa000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp3_clk", + .parent_names = (const char *[]){ + "gp3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_ahb_clk = { + .halt_reg = 0x4d07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d07c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_axi_clk = { + .halt_reg = 0x4d080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_byte0_clk = { + .halt_reg = 0x4d094, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d094, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_byte0_clk", + .parent_names = (const char *[]){ + "byte0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_esc0_clk = { + .halt_reg = 0x4d098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_esc0_clk", + .parent_names = (const char *[]){ + "esc0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_hdmi_app_clk = { + .halt_reg = 0x4d0d8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d0d8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_hdmi_app_clk", + .parent_names = (const char *[]){ + "hdmi_app_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_hdmi_pclk_clk = { + .halt_reg = 0x4d0d4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d0d4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_hdmi_pclk_clk", + .parent_names = (const char *[]){ + "hdmi_pclk_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_mdp_clk = { + .halt_reg = 0x4d088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_mdp_clk", + .parent_names = (const char *[]){ + "mdp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static DEFINE_CLK_VOTER(mdss_mdp_vote_clk, &gcc_mdss_mdp_clk.c, 0); +static DEFINE_CLK_VOTER(mdss_rotator_vote_clk, &gcc_mdss_mdp_clk.c, 0); + +static struct clk_branch gcc_mdss_pclk0_clk = { + .halt_reg = 0x4d084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d084, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_pclk0_clk", + .parent_names = (const char *[]){ + "pclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdss_vsync_clk = { + .halt_reg = 0x4d090, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d090, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdss_vsync_clk", + .parent_names = (const char *[]){ + "vsync_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_oxili_ahb_clk = { + .halt_reg = 0x59028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x59028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_oxili_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_oxili_gfx3d_clk = { + .halt_reg = 0x59020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x59020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_oxili_gfx3d_clk", + .parent_names = (const char *[]){ + "gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_aux_clk = { + .halt_reg = 0x3e014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(27), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_aux_clk", + .parent_names = (const char *[]){ + "pcie_0_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_cfg_ahb_clk = { + .halt_reg = 0x3e008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_mstr_axi_clk = { + .halt_reg = 0x3e018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(18), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_clk = { + .halt_reg = 0x3e00c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(28), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_pipe_clk", + .parent_names = (const char *[]){ + "pcie_0_pipe_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_axi_clk = { + .halt_reg = 0x3e010, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(22), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcnoc_usb2_clk = { + .halt_reg = 0x27008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x27008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcnoc_usb2_clk", + .parent_names = (const char *[]){ + "usb_hs_system_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcnoc_usb3_clk = { + .halt_reg = 0x2700c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2700c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcnoc_usb3_clk", + .parent_names = (const char *[]){ + "usb30_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x4400c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4400c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pdm2_clk", + .parent_names = (const char *[]){ + "pdm2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x44004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x44004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pdm_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_prng_ahb_clk = { + .halt_reg = 0x13004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "gcc_prng_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pwm0_xo512_clk = { + .halt_reg = 0x44018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x44018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pwm0_xo512_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pwm1_xo512_clk = { + .halt_reg = 0x49004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x49004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pwm1_xo512_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pwm2_xo512_clk = { + .halt_reg = 0x4a004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4a004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pwm2_xo512_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x4201c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4201c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x42018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x42018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk", + .parent_names = (const char *[]){ + "sdcc1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0x5d014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5d014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ice_core_clk", + .parent_names = (const char *[]){ + "sdcc1_ice_core_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x4301c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4301c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x43018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x43018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_apps_clk", + .parent_names = (const char *[]){ + "sdcc2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sys_noc_usb3_clk = { + .halt_reg = 0x26014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x26014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_usb3_clk", + .parent_names = (const char *[]){ + "usb30_master_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hs_inactivity_timers_clk = { + .halt_reg = 0x4100C, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4100C, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hs_inactivity_timers_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb20_mock_utmi_clk = { + .halt_reg = 0x41044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x41044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb20_mock_utmi_clk", + .parent_names = (const char *[]){ + "usb20_mock_utmi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb2a_phy_sleep_clk = { + .halt_reg = 0x4102c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4102c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb2a_phy_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_master_clk = { + .halt_reg = 0x3900c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3900c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_master_clk", + .parent_names = (const char *[]){ + "usb30_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_mock_utmi_clk = { + .halt_reg = 0x39014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_mock_utmi_clk", + .parent_names = (const char *[]){ + "usb30_mock_utmi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_sleep_clk = { + .halt_reg = 0x39010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_phy_aux_clk = { + .halt_reg = 0x39044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb3_phy_aux_clk", + .parent_names = (const char *[]){ + "usb3_phy_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_phy_pipe_clk = { + .halt_reg = 0x39018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb3_phy_pipe_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hs_phy_cfg_ahb_clk = { + .halt_reg = 0x41030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x41030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hs_phy_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hs_system_clk = { + .halt_reg = 0x41004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x41004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hs_system_clk", + .parent_names = (const char *[]){ + "usb_hs_system_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_dummy wcnss_m_clk = { + .rrate = 1000, + .hw.init = &(struct clk_init_data){ + .name = "wcss_m_clk", + .ops = &clk_dummy_ops, + }, +}; + +static struct clk_regmap *gcc_qcs405_clocks[] = { + [APSS_AHB_CLK_SRC] = &apss_ahb_clk_src.clkr, + [BLSP1_QUP0_I2C_APPS_CLK_SRC] = &blsp1_qup0_i2c_apps_clk_src.clkr, + [BLSP1_QUP0_SPI_APPS_CLK_SRC] = &blsp1_qup0_spi_apps_clk_src.clkr, + [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr, + [BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr, + [BLSP1_QUP2_I2C_APPS_CLK_SRC] = &blsp1_qup2_i2c_apps_clk_src.clkr, + [BLSP1_QUP2_SPI_APPS_CLK_SRC] = &blsp1_qup2_spi_apps_clk_src.clkr, + [BLSP1_QUP3_I2C_APPS_CLK_SRC] = &blsp1_qup3_i2c_apps_clk_src.clkr, + [BLSP1_QUP3_SPI_APPS_CLK_SRC] = &blsp1_qup3_spi_apps_clk_src.clkr, + [BLSP1_QUP4_I2C_APPS_CLK_SRC] = &blsp1_qup4_i2c_apps_clk_src.clkr, + [BLSP1_QUP4_SPI_APPS_CLK_SRC] = &blsp1_qup4_spi_apps_clk_src.clkr, + [BLSP1_UART0_APPS_CLK_SRC] = &blsp1_uart0_apps_clk_src.clkr, + [BLSP1_UART1_APPS_CLK_SRC] = &blsp1_uart1_apps_clk_src.clkr, + [BLSP1_UART2_APPS_CLK_SRC] = &blsp1_uart2_apps_clk_src.clkr, + [BLSP1_UART3_APPS_CLK_SRC] = &blsp1_uart3_apps_clk_src.clkr, + [BLSP2_QUP0_I2C_APPS_CLK_SRC] = &blsp2_qup0_i2c_apps_clk_src.clkr, + [BLSP2_QUP0_SPI_APPS_CLK_SRC] = &blsp2_qup0_spi_apps_clk_src.clkr, + [BLSP2_UART0_APPS_CLK_SRC] = &blsp2_uart0_apps_clk_src.clkr, + [EMAC_CLK_SRC] = &emac_clk_src.clkr, + [EMAC_PTP_CLK_SRC] = &emac_ptp_clk_src.clkr, + [ESC0_CLK_SRC] = &esc0_clk_src.clkr, + [GCC_APSS_AHB_CLK] = &gcc_apss_ahb_clk.clkr, + [GCC_BIMC_GFX_CLK] = &gcc_bimc_gfx_clk.clkr, + [GCC_BIMC_MDSS_CLK] = &gcc_bimc_mdss_clk.clkr, + [GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr, + [GCC_BLSP1_QUP0_I2C_APPS_CLK] = &gcc_blsp1_qup0_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP0_SPI_APPS_CLK] = &gcc_blsp1_qup0_spi_apps_clk.clkr, + [GCC_BLSP1_QUP1_I2C_APPS_CLK] = &gcc_blsp1_qup1_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP1_SPI_APPS_CLK] = &gcc_blsp1_qup1_spi_apps_clk.clkr, + [GCC_BLSP1_QUP2_I2C_APPS_CLK] = &gcc_blsp1_qup2_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP2_SPI_APPS_CLK] = &gcc_blsp1_qup2_spi_apps_clk.clkr, + [GCC_BLSP1_QUP3_I2C_APPS_CLK] = &gcc_blsp1_qup3_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP3_SPI_APPS_CLK] = &gcc_blsp1_qup3_spi_apps_clk.clkr, + [GCC_BLSP1_QUP4_I2C_APPS_CLK] = &gcc_blsp1_qup4_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP4_SPI_APPS_CLK] = &gcc_blsp1_qup4_spi_apps_clk.clkr, + [GCC_BLSP1_UART0_APPS_CLK] = &gcc_blsp1_uart0_apps_clk.clkr, + [GCC_BLSP1_UART1_APPS_CLK] = &gcc_blsp1_uart1_apps_clk.clkr, + [GCC_BLSP1_UART2_APPS_CLK] = &gcc_blsp1_uart2_apps_clk.clkr, + [GCC_BLSP1_UART3_APPS_CLK] = &gcc_blsp1_uart3_apps_clk.clkr, + [GCC_BLSP2_AHB_CLK] = &gcc_blsp2_ahb_clk.clkr, + [GCC_BLSP2_QUP0_I2C_APPS_CLK] = &gcc_blsp2_qup0_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP0_SPI_APPS_CLK] = &gcc_blsp2_qup0_spi_apps_clk.clkr, + [GCC_BLSP2_UART0_APPS_CLK] = &gcc_blsp2_uart0_apps_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_ETH_AXI_CLK] = &gcc_eth_axi_clk.clkr, + [GCC_ETH_PTP_CLK] = &gcc_eth_ptp_clk.clkr, + [GCC_ETH_RGMII_CLK] = &gcc_eth_rgmii_clk.clkr, + [GCC_ETH_SLAVE_AHB_CLK] = &gcc_eth_slave_ahb_clk.clkr, + [GCC_GENI_IR_S_CLK] = &gcc_geni_ir_s_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_MDSS_AHB_CLK] = &gcc_mdss_ahb_clk.clkr, + [GCC_MDSS_AXI_CLK] = &gcc_mdss_axi_clk.clkr, + [GCC_MDSS_ESC0_CLK] = &gcc_mdss_esc0_clk.clkr, + [GCC_MDSS_HDMI_APP_CLK] = &gcc_mdss_hdmi_app_clk.clkr, + [GCC_MDSS_HDMI_PCLK_CLK] = &gcc_mdss_hdmi_pclk_clk.clkr, + [GCC_MDSS_MDP_CLK] = &gcc_mdss_mdp_clk.clkr, + [GCC_MDSS_VSYNC_CLK] = &gcc_mdss_vsync_clk.clkr, + [GCC_OXILI_AHB_CLK] = &gcc_oxili_ahb_clk.clkr, + [GCC_OXILI_GFX3D_CLK] = &gcc_oxili_gfx3d_clk.clkr, + [GCC_PCIE_0_AUX_CLK] = &gcc_pcie_0_aux_clk.clkr, + [GCC_PCIE_0_CFG_AHB_CLK] = &gcc_pcie_0_cfg_ahb_clk.clkr, + [GCC_PCIE_0_MSTR_AXI_CLK] = &gcc_pcie_0_mstr_axi_clk.clkr, + [GCC_PCIE_0_PIPE_CLK] = &gcc_pcie_0_pipe_clk.clkr, + [GCC_PCIE_0_SLV_AXI_CLK] = &gcc_pcie_0_slv_axi_clk.clkr, + [GCC_PCNOC_USB2_CLK] = &gcc_pcnoc_usb2_clk.clkr, + [GCC_PCNOC_USB3_CLK] = &gcc_pcnoc_usb3_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr, + [GCC_PWM0_XO512_CLK] = &gcc_pwm0_xo512_clk.clkr, + [GCC_PWM1_XO512_CLK] = &gcc_pwm1_xo512_clk.clkr, + [GCC_PWM2_XO512_CLK] = &gcc_pwm2_xo512_clk.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr, + [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SYS_NOC_USB3_CLK] = &gcc_sys_noc_usb3_clk.clkr, + [GCC_USB20_MOCK_UTMI_CLK] = &gcc_usb20_mock_utmi_clk.clkr, + [GCC_USB2A_PHY_SLEEP_CLK] = &gcc_usb2a_phy_sleep_clk.clkr, + [GCC_USB30_MASTER_CLK] = &gcc_usb30_master_clk.clkr, + [GCC_USB30_MOCK_UTMI_CLK] = &gcc_usb30_mock_utmi_clk.clkr, + [GCC_USB30_SLEEP_CLK] = &gcc_usb30_sleep_clk.clkr, + [GCC_USB3_PHY_AUX_CLK] = &gcc_usb3_phy_aux_clk.clkr, + [GCC_USB3_PHY_PIPE_CLK] = &gcc_usb3_phy_pipe_clk.clkr, + [GCC_USB_HS_PHY_CFG_AHB_CLK] = &gcc_usb_hs_phy_cfg_ahb_clk.clkr, + [GCC_USB_HS_SYSTEM_CLK] = &gcc_usb_hs_system_clk.clkr, + [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr, + [GP1_CLK_SRC] = &gp1_clk_src.clkr, + [GP2_CLK_SRC] = &gp2_clk_src.clkr, + [GP3_CLK_SRC] = &gp3_clk_src.clkr, + [GPLL0_OUT_MAIN] = &gpll0_out_main.clkr, + [GPLL0_AO_OUT_MAIN] = &gpll0_ao_out_main.clkr, + [GPLL0_SLEEP_CLK_SRC] = &gpll0_sleep_clk_src.clkr, + [GPLL1_OUT_MAIN] = &gpll1_out_main.clkr, + [GPLL3_OUT_MAIN] = &gpll3_out_main.clkr, + [GPLL4_OUT_MAIN] = &gpll4_out_main.clkr, + [GPLL6] = &gpll6.clkr, + [GPLL6_OUT_AUX] = &gpll6_out_aux, + [HDMI_APP_CLK_SRC] = &hdmi_app_clk_src.clkr, + [HDMI_PCLK_CLK_SRC] = &hdmi_pclk_clk_src.clkr, + [MDP_CLK_SRC] = &mdp_clk_src.clkr, + [PCIE_0_AUX_CLK_SRC] = &pcie_0_aux_clk_src.clkr, + [PCIE_0_PIPE_CLK_SRC] = &pcie_0_pipe_clk_src.clkr, + [PDM2_CLK_SRC] = &pdm2_clk_src.clkr, + [SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr, + [SDCC1_ICE_CORE_CLK_SRC] = &sdcc1_ice_core_clk_src.clkr, + [SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr, + [USB20_MOCK_UTMI_CLK_SRC] = &usb20_mock_utmi_clk_src.clkr, + [USB30_MASTER_CLK_SRC] = &usb30_master_clk_src.clkr, + [USB30_MOCK_UTMI_CLK_SRC] = &usb30_mock_utmi_clk_src.clkr, + [USB3_PHY_AUX_CLK_SRC] = &usb3_phy_aux_clk_src.clkr, + [USB_HS_SYSTEM_CLK_SRC] = &usb_hs_system_clk_src.clkr, + [VSYNC_CLK_SRC] = &vsync_clk_src.clkr, + [GCC_USB_HS_INACTIVITY_TIMERS_CLK] = + &gcc_usb_hs_inactivity_timers_clk.clkr, +}; + +static const struct qcom_reset_map gcc_qcs405_resets[] = { + [GCC_GENI_IR_BCR] = {0x0F000}, + [GCC_USB_HS_BCR] = {0x41000}, + [GCC_USB2_HS_PHY_ONLY_BCR] = {0x41034}, + [GCC_QUSB2_PHY_BCR] = {0x4103C}, + [GCC_USB_HS_PHY_CFG_AHB_BCR] = {0x41038}, + [GCC_USB2A_PHY_BCR] = {0x41028}, + [GCC_USB3_PHY_BCR] = {0x39004}, + [GCC_USB_30_BCR] = {0x39000}, + [GCC_USB3PHY_PHY_BCR] = {0x39008}, + [GCC_PCIE_0_BCR] = {0x3E000}, + [GCC_PCIE_0_PHY_BCR] = {0x3E004}, + [GCC_PCIE_0_LINK_DOWN_BCR] = {0x3E038}, + [GCC_PCIEPHY_0_PHY_BCR] = {0x3E03C}, + [GCC_EMAC_BCR] = {0x4E000}, +}; + +static const struct regmap_config gcc_qcs405_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7f000, + .fast_io = true, +}; + +static const struct qcom_cc_desc gcc_qcs405_desc = { + .config = &gcc_qcs405_regmap_config, + .clks = gcc_qcs405_clocks, + .num_clks = ARRAY_SIZE(gcc_qcs405_clocks), + .resets = gcc_qcs405_resets, + .num_resets = ARRAY_SIZE(gcc_qcs405_resets), +}; + +static const struct of_device_id gcc_qcs405_match_table[] = { + { .compatible = "qcom,gcc-qcs405" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_qcs405_match_table); + +static int gcc_qcs405_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct regmap *regmap; + int ret = 0; + + regmap = qcom_cc_map(pdev, &gcc_qcs405_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = devm_clk_get(&pdev->dev, "cxo"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Unable to get cxo clock\n"); + return PTR_ERR(clk); + } + + vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(vdd_cx.regulator[0])) { + if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_cx regulator\n"); + return PTR_ERR(vdd_cx.regulator[0]); + } + + clk_alpha_pll_configure(&gpll3_out_main, regmap, &gpll3_config); + + clk = devm_clk_register(&pdev->dev, &wcnss_m_clk.hw); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Unable to register wcnss_m_clk\n"); + return PTR_ERR(clk); + } + + ret = qcom_cc_really_probe(pdev, &gcc_qcs405_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register GCC clocks\n"); + return ret; + } + + clk_set_rate(apss_ahb_clk_src.clkr.hw.clk, 19200000); + clk_prepare_enable(apss_ahb_clk_src.clkr.hw.clk); + + dev_info(&pdev->dev, "Registered GCC clocks\n"); + + return ret; +} + +static struct platform_driver gcc_qcs405_driver = { + .probe = gcc_qcs405_probe, + .driver = { + .name = "gcc-qcs405", + .of_match_table = gcc_qcs405_match_table, + }, +}; + +static int __init gcc_qcs405_init(void) +{ + return platform_driver_register(&gcc_qcs405_driver); +} +subsys_initcall(gcc_qcs405_init); + +static void __exit gcc_qcs405_exit(void) +{ + platform_driver_unregister(&gcc_qcs405_driver); +} +module_exit(gcc_qcs405_exit); + +struct clk_hw *mdss_qcs405_hws[] = { + [MDSS_MDP_VOTE_CLK] = &mdss_mdp_vote_clk.hw, + [MDSS_ROTATOR_VOTE_CLK] = &mdss_rotator_vote_clk.hw, +}; + +static struct clk_regmap *mdss_qcs405_clocks[] = { + [GCC_MDSS_BYTE0_CLK] = &gcc_mdss_byte0_clk.clkr, + [GCC_MDSS_PCLK0_CLK] = &gcc_mdss_pclk0_clk.clkr, + [BYTE0_CLK_SRC] = &byte0_clk_src.clkr, + [PCLK0_CLK_SRC] = &pclk0_clk_src.clkr, +}; + +static const struct qcom_cc_desc mdss_qcs405_desc = { + .config = &gcc_qcs405_regmap_config, + .clks = mdss_qcs405_clocks, + .num_clks = ARRAY_SIZE(mdss_qcs405_clocks), +}; + +static const struct of_device_id mdss_qcs405_match_table[] = { + { .compatible = "qcom,gcc-mdss-qcs405" }, + {} +}; + +MODULE_DEVICE_TABLE(of, mdss_qcs405_match_table); + +static int mdss_qcs405_probe(struct platform_device *pdev) +{ + struct clk *clk; + int ret = 0, i; + + clk = devm_clk_get(&pdev->dev, "pclk0_src"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Unable to get pclk0_src clock\n"); + return PTR_ERR(clk); + } + + clk = devm_clk_get(&pdev->dev, "byte0_src"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Unable to get byte0_src clock\n"); + return PTR_ERR(clk); + } + + /* register hardware clocks */ + for (i = 0; i < ARRAY_SIZE(mdss_qcs405_hws); i++) { + clk = devm_clk_register(&pdev->dev, mdss_qcs405_hws[i]); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Unable to register hardware clocks\n"); + return PTR_ERR(clk); + } + } + + ret = qcom_cc_probe(pdev, &mdss_qcs405_desc); + if (ret) { + dev_err(&pdev->dev, "Failed to register MDSS clocks\n"); + return ret; + } + + dev_info(&pdev->dev, "Registered MDSS clocks\n"); + + return ret; +} + +static struct platform_driver mdss_qcs405_driver = { + .probe = mdss_qcs405_probe, + .driver = { + .name = "gcc-mdss-qcs405", + .of_match_table = mdss_qcs405_match_table, + }, +}; + +static int __init mdss_qcs405_init(void) +{ + return platform_driver_register(&mdss_qcs405_driver); +} +subsys_initcall(mdss_qcs405_init); + +static void __exit mdss_qcs405_exit(void) +{ + platform_driver_unregister(&mdss_qcs405_driver); +} +module_exit(mdss_qcs405_exit); + +MODULE_DESCRIPTION("QTI GCC QCS405 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gcc-qcs405"); diff --git a/drivers/clk/qcom/gcc-sdmshrike.c b/drivers/clk/qcom/gcc-sdmshrike.c index 378fcf4628a50c26506d223f5faeae2298f8a13a..481b557d51ee49bd472817da075fb6be46e37750 100644 --- a/drivers/clk/qcom/gcc-sdmshrike.c +++ b/drivers/clk/qcom/gcc-sdmshrike.c @@ -1661,6 +1661,8 @@ static struct clk_rcg2 gcc_usb30_mp_master_clk_src = { }; static const struct freq_tbl ftbl_gcc_usb30_mp_mock_utmi_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(20000000, P_GPLL0_OUT_EVEN, 15, 0, 0), F(40000000, P_GPLL0_OUT_EVEN, 7.5, 0, 0), F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0), { } @@ -1709,19 +1711,12 @@ static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { }, }; -static const struct freq_tbl ftbl_gcc_usb30_prim_mock_utmi_clk_src[] = { - F(20000000, P_GPLL0_OUT_EVEN, 15, 0, 0), - F(40000000, P_GPLL0_OUT_EVEN, 7.5, 0, 0), - F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0), - { } -}; - static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { .cmd_rcgr = 0xf034, .mnd_width = 0, .hid_width = 5, .parent_map = gcc_parent_map_0, - .freq_tbl = ftbl_gcc_usb30_prim_mock_utmi_clk_src, + .freq_tbl = ftbl_gcc_usb30_mp_mock_utmi_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_prim_mock_utmi_clk_src", .parent_names = gcc_parent_names_0, @@ -1764,7 +1759,7 @@ static struct clk_rcg2 gcc_usb30_sec_mock_utmi_clk_src = { .mnd_width = 0, .hid_width = 5, .parent_map = gcc_parent_map_0, - .freq_tbl = ftbl_gcc_usb30_prim_mock_utmi_clk_src, + .freq_tbl = ftbl_gcc_usb30_mp_mock_utmi_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_sec_mock_utmi_clk_src", .parent_names = gcc_parent_names_0, diff --git a/drivers/clk/qcom/gcc-sm8150.c b/drivers/clk/qcom/gcc-sm8150.c index 7ac2b7df1e64fd0da0764747ddc6dd24663d88da..e95de9ac8a9d263ef934585408ca24e729017bfd 100644 --- a/drivers/clk/qcom/gcc-sm8150.c +++ b/drivers/clk/qcom/gcc-sm8150.c @@ -51,10 +51,7 @@ enum { P_CORE_BI_PLL_TEST_SE, P_GPLL0_OUT_EVEN, P_GPLL0_OUT_MAIN, - P_GPLL1_OUT_MAIN, - P_GPLL2_OUT_MAIN, P_GPLL4_OUT_MAIN, - P_GPLL5_OUT_MAIN, P_GPLL7_OUT_MAIN, P_GPLL9_OUT_MAIN, P_SLEEP_CLK, @@ -131,28 +128,6 @@ static const char * const gcc_parent_names_4[] = { }; static const struct parent_map gcc_parent_map_5[] = { - { P_BI_TCXO, 0 }, - { P_GPLL0_OUT_MAIN, 1 }, - { P_GPLL2_OUT_MAIN, 2 }, - { P_GPLL5_OUT_MAIN, 3 }, - { P_GPLL1_OUT_MAIN, 4 }, - { P_GPLL4_OUT_MAIN, 5 }, - { P_GPLL0_OUT_EVEN, 6 }, - { P_CORE_BI_PLL_TEST_SE, 7 }, -}; - -static const char * const gcc_parent_names_5[] = { - "bi_tcxo", - "gpll0", - "gpll2", - "gpll5", - "gpll1", - "gpll4", - "gpll0_out_even", - "core_bi_pll_test_se", -}; - -static const struct parent_map gcc_parent_map_6[] = { { P_BI_TCXO, 0 }, { P_GPLL0_OUT_MAIN, 1 }, { P_GPLL7_OUT_MAIN, 3 }, @@ -160,7 +135,7 @@ static const struct parent_map gcc_parent_map_6[] = { { P_CORE_BI_PLL_TEST_SE, 7 }, }; -static const char * const gcc_parent_names_6[] = { +static const char * const gcc_parent_names_5[] = { "bi_tcxo", "gpll0", "gpll7", @@ -168,7 +143,7 @@ static const char * const gcc_parent_names_6[] = { "core_bi_pll_test_se", }; -static const struct parent_map gcc_parent_map_7[] = { +static const struct parent_map gcc_parent_map_6[] = { { P_BI_TCXO, 0 }, { P_GPLL0_OUT_MAIN, 1 }, { P_GPLL9_OUT_MAIN, 2 }, @@ -177,7 +152,7 @@ static const struct parent_map gcc_parent_map_7[] = { { P_CORE_BI_PLL_TEST_SE, 7 }, }; -static const char * const gcc_parent_names_7[] = { +static const char * const gcc_parent_names_6[] = { "bi_tcxo", "gpll0", "gpll9", @@ -186,7 +161,7 @@ static const char * const gcc_parent_names_7[] = { "core_bi_pll_test_se", }; -static const struct parent_map gcc_parent_map_8[] = { +static const struct parent_map gcc_parent_map_7[] = { { P_BI_TCXO, 0 }, { P_GPLL0_OUT_MAIN, 1 }, { P_AUD_REF_CLK, 2 }, @@ -194,7 +169,7 @@ static const struct parent_map gcc_parent_map_8[] = { { P_CORE_BI_PLL_TEST_SE, 7 }, }; -static const char * const gcc_parent_names_8[] = { +static const char * const gcc_parent_names_7[] = { "bi_tcxo", "gpll0", "aud_ref_clk", @@ -294,54 +269,6 @@ static struct clk_alpha_pll_postdiv gpll0_out_even = { }, }; -static struct clk_alpha_pll gpll1 = { - .offset = 0x1000, - .vco_table = trion_vco, - .num_vco = ARRAY_SIZE(trion_vco), - .type = TRION_PLL, - .clkr = { - .enable_reg = 0x52000, - .enable_mask = BIT(1), - .hw.init = &(struct clk_init_data){ - .name = "gpll1", - .parent_names = (const char *[]){ "bi_tcxo" }, - .num_parents = 1, - .ops = &clk_trion_fixed_pll_ops, - .vdd_class = &vdd_cx, - .num_rate_max = VDD_NUM, - .rate_max = (unsigned long[VDD_NUM]) { - [VDD_MIN] = 615000000, - [VDD_LOW] = 1066000000, - [VDD_LOW_L1] = 1600000000, - [VDD_NOMINAL] = 2000000000}, - }, - }, -}; - -static struct clk_alpha_pll gpll4 = { - .offset = 0x76000, - .vco_table = trion_vco, - .num_vco = ARRAY_SIZE(trion_vco), - .type = TRION_PLL, - .clkr = { - .enable_reg = 0x52000, - .enable_mask = BIT(4), - .hw.init = &(struct clk_init_data){ - .name = "gpll4", - .parent_names = (const char *[]){ "bi_tcxo" }, - .num_parents = 1, - .ops = &clk_trion_fixed_pll_ops, - .vdd_class = &vdd_cx, - .num_rate_max = VDD_NUM, - .rate_max = (unsigned long[VDD_NUM]) { - [VDD_MIN] = 615000000, - [VDD_LOW] = 1066000000, - [VDD_LOW_L1] = 1600000000, - [VDD_NOMINAL] = 2000000000}, - }, - }, -}; - static struct clk_alpha_pll gpll7 = { .offset = 0x1a000, .vco_table = trion_vco, @@ -430,11 +357,11 @@ static struct clk_rcg2 gcc_emac_ptp_clk_src = { .cmd_rcgr = 0x6038, .mnd_width = 0, .hid_width = 5, - .parent_map = gcc_parent_map_6, + .parent_map = gcc_parent_map_5, .freq_tbl = ftbl_gcc_emac_ptp_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_emac_ptp_clk_src", - .parent_names = gcc_parent_names_6, + .parent_names = gcc_parent_names_5, .num_parents = 5, .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, @@ -463,11 +390,11 @@ static struct clk_rcg2 gcc_emac_rgmii_clk_src = { .cmd_rcgr = 0x601c, .mnd_width = 8, .hid_width = 5, - .parent_map = gcc_parent_map_6, + .parent_map = gcc_parent_map_5, .freq_tbl = ftbl_gcc_emac_rgmii_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_emac_rgmii_clk_src", - .parent_names = gcc_parent_names_6, + .parent_names = gcc_parent_names_5, .num_parents = 5, .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, @@ -556,41 +483,6 @@ static struct clk_rcg2 gcc_gp3_clk_src = { }, }; -static const struct freq_tbl ftbl_gcc_npu_axi_clk_src[] = { - F(19200000, P_BI_TCXO, 1, 0, 0), - F(60000000, P_GPLL0_OUT_EVEN, 5, 0, 0), - F(150000000, P_GPLL0_OUT_EVEN, 2, 0, 0), - F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), - F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), - F(403000000, P_GPLL4_OUT_MAIN, 2, 0, 0), - F(533000000, P_GPLL1_OUT_MAIN, 2, 0, 0), - { } -}; - -static struct clk_rcg2 gcc_npu_axi_clk_src = { - .cmd_rcgr = 0x4d014, - .mnd_width = 0, - .hid_width = 5, - .parent_map = gcc_parent_map_5, - .freq_tbl = ftbl_gcc_npu_axi_clk_src, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gcc_npu_axi_clk_src", - .parent_names = gcc_parent_names_5, - .num_parents = 8, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_ops, - .vdd_class = &vdd_cx, - .num_rate_max = VDD_NUM, - .rate_max = (unsigned long[VDD_NUM]) { - [VDD_MIN] = 60000000, - [VDD_LOWER] = 150000000, - [VDD_LOW] = 200000000, - [VDD_LOW_L1] = 300000000, - [VDD_NOMINAL] = 403000000, - [VDD_HIGH] = 533000000}, - }, -}; - static const struct freq_tbl ftbl_gcc_pcie_0_aux_clk_src[] = { F(9600000, P_BI_TCXO, 2, 0, 0), F(19200000, P_BI_TCXO, 1, 0, 0), @@ -1187,7 +1079,7 @@ static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { F(25000000, P_GPLL0_OUT_MAIN, 12, 1, 2), F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0), F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), - F(201600000, P_GPLL9_OUT_MAIN, 4, 0, 0), + F(202000000, P_GPLL9_OUT_MAIN, 4, 0, 0), { } }; @@ -1195,11 +1087,11 @@ static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { .cmd_rcgr = 0x1400c, .mnd_width = 8, .hid_width = 5, - .parent_map = gcc_parent_map_7, + .parent_map = gcc_parent_map_6, .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_sdcc2_apps_clk_src", - .parent_names = gcc_parent_names_7, + .parent_names = gcc_parent_names_6, .num_parents = 6, .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, @@ -1208,7 +1100,7 @@ static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { .rate_max = (unsigned long[VDD_NUM]) { [VDD_MIN] = 19200000, [VDD_LOW] = 100000000, - [VDD_LOW_L1] = 201600000}, + [VDD_LOW_L1] = 202000000}, }, }; @@ -1252,11 +1144,11 @@ static struct clk_rcg2 gcc_tsif_ref_clk_src = { .cmd_rcgr = 0x36010, .mnd_width = 8, .hid_width = 5, - .parent_map = gcc_parent_map_8, + .parent_map = gcc_parent_map_7, .freq_tbl = ftbl_gcc_tsif_ref_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_tsif_ref_clk_src", - .parent_names = gcc_parent_names_8, + .parent_names = gcc_parent_names_7, .num_parents = 5, .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, @@ -1515,6 +1407,7 @@ static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { }; static const struct freq_tbl ftbl_gcc_usb30_prim_mock_utmi_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), F(20000000, P_GPLL0_OUT_EVEN, 15, 0, 0), F(60000000, P_GPLL0_OUT_EVEN, 5, 0, 0), { } @@ -2383,7 +2276,7 @@ static struct clk_branch gcc_pcie_0_mstr_axi_clk = { static struct clk_branch gcc_pcie_0_pipe_clk = { .halt_reg = 0x6b024, - .halt_check = BRANCH_VOTED, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x5200c, .enable_mask = BIT(4), @@ -2483,7 +2376,7 @@ static struct clk_branch gcc_pcie_1_mstr_axi_clk = { static struct clk_branch gcc_pcie_1_pipe_clk = { .halt_reg = 0x8d024, - .halt_check = BRANCH_VOTED, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(30), @@ -4065,7 +3958,6 @@ static struct clk_regmap *gcc_sm8150_clocks[] = { [GCC_GPU_SNOC_DVM_GFX_CLK] = &gcc_gpu_snoc_dvm_gfx_clk.clkr, [GCC_NPU_AT_CLK] = &gcc_npu_at_clk.clkr, [GCC_NPU_AXI_CLK] = &gcc_npu_axi_clk.clkr, - [GCC_NPU_AXI_CLK_SRC] = &gcc_npu_axi_clk_src.clkr, [GCC_NPU_CFG_AHB_CLK] = &gcc_npu_cfg_ahb_clk.clkr, [GCC_NPU_GPLL0_CLK_SRC] = &gcc_npu_gpll0_clk_src.clkr, [GCC_NPU_GPLL0_DIV_CLK_SRC] = &gcc_npu_gpll0_div_clk_src.clkr, @@ -4230,8 +4122,6 @@ static struct clk_regmap *gcc_sm8150_clocks[] = { [GCC_VIDEO_XO_CLK] = &gcc_video_xo_clk.clkr, [GPLL0] = &gpll0.clkr, [GPLL0_OUT_EVEN] = &gpll0_out_even.clkr, - [GPLL1] = &gpll1.clkr, - [GPLL4] = &gpll4.clkr, [GPLL7] = &gpll7.clkr, [GPLL9] = &gpll9.clkr, }; diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c index 986002050a51ebabec3dc1ffa95de05856b4a5ba..714ceead73f435615959da136010af0700fdc953 100644 --- a/drivers/clk/qcom/gdsc-regulator.c +++ b/drivers/clk/qcom/gdsc-regulator.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 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 #include #include +#include #include #include #include @@ -60,7 +61,10 @@ struct gdsc { struct regmap *hw_ctrl; struct regmap *sw_reset; struct clk **clocks; + struct regulator *parent_regulator; struct reset_control **reset_clocks; + struct msm_bus_scale_pdata *bus_pdata; + u32 bus_handle; bool toggle_mem; bool toggle_periph; bool toggle_logic; @@ -71,7 +75,7 @@ struct gdsc { bool is_gdsc_enabled; bool allow_clear; bool reset_aon; - bool vote_supply_voltage; + bool is_bus_enabled; int clock_count; int reset_count; int root_clk_idx; @@ -131,7 +135,7 @@ static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status) * bit in the GDSCR to be set or reset after the GDSC state * changes. Hence, keep on checking for a reasonable number * of times until the bit is set with the least possible delay - * between succeessive tries. + * between successive tries. */ udelay(1); } @@ -143,10 +147,45 @@ static int gdsc_is_enabled(struct regulator_dev *rdev) { struct gdsc *sc = rdev_get_drvdata(rdev); uint32_t regval; + int ret; + bool is_enabled = false; if (!sc->toggle_logic) return !sc->resets_asserted; + if (sc->parent_regulator) { + /* + * The parent regulator for the GDSC is required to be on to + * make any register accesses to the GDSC base. Return false + * if the parent supply is disabled. + */ + if (regulator_is_enabled(sc->parent_regulator) <= 0) + return false; + + /* + * Place an explicit vote on the parent rail to cover cases when + * it might be disabled between this point and reading the GDSC + * registers. + */ + if (regulator_set_voltage(sc->parent_regulator, + RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX)) + return false; + + if (regulator_enable(sc->parent_regulator)) { + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + return false; + } + } + + if (sc->bus_handle && !sc->is_bus_enabled) { + ret = msm_bus_scale_client_update_request(sc->bus_handle, 1); + if (ret) { + dev_err(&rdev->dev, "bus scaling failed, ret=%d\n", + ret); + goto end; + } + } + regmap_read(sc->regmap, REG_OFFSET, ®val); if (regval & PWR_ON_MASK) { @@ -156,10 +195,19 @@ static int gdsc_is_enabled(struct regulator_dev *rdev) * determine if HLOS has voted for it. */ if (!(regval & SW_COLLAPSE_MASK)) - return true; + is_enabled = true; + } + + + if (sc->bus_handle && !sc->is_bus_enabled) + msm_bus_scale_client_update_request(sc->bus_handle, 0); +end: + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); } - return false; + return is_enabled; } static int gdsc_enable(struct regulator_dev *rdev) @@ -170,8 +218,8 @@ static int gdsc_enable(struct regulator_dev *rdev) mutex_lock(&gdsc_seq_lock); - if (sc->vote_supply_voltage) { - ret = regulator_set_voltage(sc->rdev->supply, + if (sc->parent_regulator) { + ret = regulator_set_voltage(sc->parent_regulator, RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX); if (ret) { mutex_unlock(&gdsc_seq_lock); @@ -179,6 +227,16 @@ static int gdsc_enable(struct regulator_dev *rdev) } } + if (sc->bus_handle) { + ret = msm_bus_scale_client_update_request(sc->bus_handle, 1); + if (ret) { + dev_err(&rdev->dev, "bus scaling failed, ret=%d\n", + ret); + goto end; + } + sc->is_bus_enabled = true; + } + if (sc->root_en || sc->force_root_en) clk_prepare_enable(sc->clocks[sc->root_clk_idx]); @@ -316,8 +374,12 @@ static int gdsc_enable(struct regulator_dev *rdev) sc->is_gdsc_enabled = true; end: - if (sc->vote_supply_voltage) - regulator_set_voltage(sc->rdev->supply, 0, INT_MAX); + if (ret && sc->bus_handle) { + msm_bus_scale_client_update_request(sc->bus_handle, 0); + sc->is_bus_enabled = false; + } + if (sc->parent_regulator) + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); mutex_unlock(&gdsc_seq_lock); @@ -332,8 +394,8 @@ static int gdsc_disable(struct regulator_dev *rdev) mutex_lock(&gdsc_seq_lock); - if (sc->vote_supply_voltage) { - ret = regulator_set_voltage(sc->rdev->supply, + if (sc->parent_regulator) { + ret = regulator_set_voltage(sc->parent_regulator, RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX); if (ret) { mutex_unlock(&gdsc_seq_lock); @@ -398,8 +460,16 @@ static int gdsc_disable(struct regulator_dev *rdev) if ((sc->is_gdsc_enabled && sc->root_en) || sc->force_root_en) clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); - if (sc->vote_supply_voltage) - regulator_set_voltage(sc->rdev->supply, 0, INT_MAX); + if (sc->bus_handle) { + ret = msm_bus_scale_client_update_request(sc->bus_handle, 0); + if (ret) + dev_err(&rdev->dev, "bus scaling failed, ret=%d\n", + ret); + sc->is_bus_enabled = false; + } + + if (sc->parent_regulator) + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); sc->is_gdsc_enabled = false; @@ -412,9 +482,51 @@ static unsigned int gdsc_get_mode(struct regulator_dev *rdev) { struct gdsc *sc = rdev_get_drvdata(rdev); uint32_t regval; + int ret; mutex_lock(&gdsc_seq_lock); + + if (sc->parent_regulator) { + ret = regulator_set_voltage(sc->parent_regulator, + RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX); + if (ret) { + mutex_unlock(&gdsc_seq_lock); + return ret; + } + + ret = regulator_enable(sc->parent_regulator); + if (ret) { + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + mutex_unlock(&gdsc_seq_lock); + return ret; + } + } + + if (sc->bus_handle && !sc->is_bus_enabled) { + ret = msm_bus_scale_client_update_request(sc->bus_handle, 1); + if (ret) { + dev_err(&rdev->dev, "bus scaling failed, ret=%d\n", + ret); + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, + INT_MAX); + } + mutex_unlock(&gdsc_seq_lock); + return ret; + } + } + regmap_read(sc->regmap, REG_OFFSET, ®val); + + if (sc->bus_handle && !sc->is_bus_enabled) + msm_bus_scale_client_update_request(sc->bus_handle, 0); + + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + } + mutex_unlock(&gdsc_seq_lock); if (regval & HW_CONTROL_MASK) @@ -431,13 +543,35 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode) mutex_lock(&gdsc_seq_lock); - if (sc->vote_supply_voltage) { - ret = regulator_set_voltage(sc->rdev->supply, + if (sc->parent_regulator) { + ret = regulator_set_voltage(sc->parent_regulator, RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX); if (ret) { mutex_unlock(&gdsc_seq_lock); return ret; } + + ret = regulator_enable(sc->parent_regulator); + if (ret) { + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + mutex_unlock(&gdsc_seq_lock); + return ret; + } + } + + if (sc->bus_handle && !sc->is_bus_enabled) { + ret = msm_bus_scale_client_update_request(sc->bus_handle, 1); + if (ret) { + dev_err(&rdev->dev, "bus scaling failed, ret=%d\n", + ret); + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, + INT_MAX); + } + mutex_unlock(&gdsc_seq_lock); + return ret; + } } regmap_read(sc->regmap, REG_OFFSET, ®val); @@ -483,8 +617,13 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode) break; } - if (sc->vote_supply_voltage) - regulator_set_voltage(sc->rdev->supply, 0, INT_MAX); + if (sc->bus_handle && !sc->is_bus_enabled) + msm_bus_scale_client_update_request(sc->bus_handle, 0); + + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + } mutex_unlock(&gdsc_seq_lock); @@ -602,8 +741,18 @@ static int gdsc_probe(struct platform_device *pdev) sc->force_root_en = of_property_read_bool(pdev->dev.of_node, "qcom,force-enable-root-clk"); - sc->vote_supply_voltage = of_property_read_bool(pdev->dev.of_node, - "qcom,vote-parent-supply-voltage"); + if (of_find_property(pdev->dev.of_node, "vdd_parent-supply", NULL)) { + sc->parent_regulator = devm_regulator_get(&pdev->dev, + "vdd_parent"); + if (IS_ERR(sc->parent_regulator)) { + ret = PTR_ERR(sc->parent_regulator); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Unable to get vdd_parent regulator, err: %d\n", + ret); + return ret; + } + } for (i = 0; i < sc->clock_count; i++) { const char *clock_name; @@ -633,6 +782,34 @@ static int gdsc_probe(struct platform_device *pdev) sc->reset_aon = of_property_read_bool(pdev->dev.of_node, "qcom,reset-aon-logic"); + if (of_find_property(pdev->dev.of_node, "qcom,msm-bus,name", NULL)) { + sc->bus_pdata = msm_bus_cl_get_pdata(pdev); + if (!sc->bus_pdata) { + dev_err(&pdev->dev, "Failed to get bus config data\n"); + return -EINVAL; + } + + sc->bus_handle = msm_bus_scale_register_client(sc->bus_pdata); + if (!sc->bus_handle) { + dev_err(&pdev->dev, "Failed to register bus client\n"); + /* + * msm_bus_scale_register_client() returns 0 for all + * errors including when called before the bus driver + * probes. Therefore, return -EPROBE_DEFER here so that + * probing can be retried and this case handled. + */ + return -EPROBE_DEFER; + } + + ret = msm_bus_scale_client_update_request(sc->bus_handle, 1); + if (ret) { + dev_err(&pdev->dev, "bus scaling failed, ret=%d\n", + ret); + goto err; + } + sc->is_bus_enabled = true; + } + sc->rdesc.id = atomic_inc_return(&gdsc_count); sc->rdesc.ops = &gdsc_ops; sc->rdesc.type = REGULATOR_VOLTAGE; @@ -683,14 +860,17 @@ static int gdsc_probe(struct platform_device *pdev) sc->reset_count = 0; } else if (sc->reset_count < 0) { dev_err(&pdev->dev, "Failed to get reset clock names\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } sc->reset_clocks = devm_kzalloc(&pdev->dev, sizeof(struct reset_control *) * sc->reset_count, GFP_KERNEL); - if (!sc->reset_clocks) - return -ENOMEM; + if (!sc->reset_clocks) { + ret = -ENOMEM; + goto err; + } for (i = 0; i < sc->reset_count; i++) { const char *reset_name; @@ -705,7 +885,8 @@ static int gdsc_probe(struct platform_device *pdev) if (rc != -EPROBE_DEFER) dev_err(&pdev->dev, "Failed to get %s\n", reset_name); - return rc; + ret = rc; + goto err; } } @@ -716,7 +897,19 @@ static int gdsc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "%s enable timed out: 0x%x\n", sc->rdesc.name, regval); - return ret; + goto err; + } + } + + if (sc->bus_handle) { + regmap_read(sc->regmap, REG_OFFSET, ®val); + if (!(regval & PWR_ON_MASK) || (regval & SW_COLLAPSE_MASK)) { + /* + * Software is not enabling the GDSC so remove the + * bus vote. + */ + msm_bus_scale_client_update_request(sc->bus_handle, 0); + sc->is_bus_enabled = false; } } @@ -746,13 +939,20 @@ static int gdsc_probe(struct platform_device *pdev) if (IS_ERR(sc->rdev)) { dev_err(&pdev->dev, "regulator_register(\"%s\") failed.\n", sc->rdesc.name); - return PTR_ERR(sc->rdev); + ret = PTR_ERR(sc->rdev); + goto err; } - if (!sc->rdev->supply) - sc->vote_supply_voltage = false; - return 0; + +err: + if (sc->bus_handle) { + if (sc->is_bus_enabled) + msm_bus_scale_client_update_request(sc->bus_handle, 0); + msm_bus_scale_unregister_client(sc->bus_handle); + } + + return ret; } static int gdsc_remove(struct platform_device *pdev) @@ -761,6 +961,12 @@ static int gdsc_remove(struct platform_device *pdev) regulator_unregister(sc->rdev); + if (sc->bus_handle) { + if (sc->is_bus_enabled) + msm_bus_scale_client_update_request(sc->bus_handle, 0); + msm_bus_scale_unregister_client(sc->bus_handle); + } + return 0; } diff --git a/drivers/clk/qcom/gpucc-sm8150.c b/drivers/clk/qcom/gpucc-sm8150.c index 6e236cc41ceddf89408fd0a67e9da7f8c3a09d7b..a11b2b5856b7f9b12ccadf8f20251322d19a53a3 100644 --- a/drivers/clk/qcom/gpucc-sm8150.c +++ b/drivers/clk/qcom/gpucc-sm8150.c @@ -143,32 +143,6 @@ static struct clk_rcg2 gpu_cc_gmu_clk_src = { }, }; -static struct clk_branch gpu_cc_acd_ahb_clk = { - .halt_reg = 0x1168, - .halt_check = BRANCH_HALT, - .clkr = { - .enable_reg = 0x1168, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gpu_cc_acd_ahb_clk", - .ops = &clk_branch2_ops, - }, - }, -}; - -static struct clk_branch gpu_cc_acd_cxo_clk = { - .halt_reg = 0x1164, - .halt_check = BRANCH_HALT, - .clkr = { - .enable_reg = 0x1164, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gpu_cc_acd_cxo_clk", - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch gpu_cc_ahb_clk = { .halt_reg = 0x1078, .halt_check = BRANCH_HALT, @@ -411,8 +385,6 @@ struct clk_hw *gpu_cc_sm8150_hws[] = { }; static struct clk_regmap *gpu_cc_sm8150_clocks[] = { - [GPU_CC_ACD_AHB_CLK] = &gpu_cc_acd_ahb_clk.clkr, - [GPU_CC_ACD_CXO_CLK] = &gpu_cc_acd_cxo_clk.clkr, [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr, [GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr, [GPU_CC_CX_APB_CLK] = &gpu_cc_cx_apb_clk.clkr, @@ -433,7 +405,6 @@ static struct clk_regmap *gpu_cc_sm8150_clocks[] = { }; static const struct qcom_reset_map gpu_cc_sm8150_resets[] = { - [GPUCC_GPU_CC_ACD_BCR] = { 0x1160 }, [GPUCC_GPU_CC_CX_BCR] = { 0x1068 }, [GPUCC_GPU_CC_GMU_BCR] = { 0x111c }, [GPUCC_GPU_CC_GX_BCR] = { 0x1008 }, diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-7nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-7nm.c index 2166f36eca77488d37ed6c58fd6b24196d3f5d29..0e7d35acee51a8aabdab33d5031992f817e5b0fb 100644 --- a/drivers/clk/qcom/mdss/mdss-dsi-pll-7nm.c +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-7nm.c @@ -191,6 +191,7 @@ #define PHY_CMN_CTRL_0 0x024 #define PHY_CMN_CTRL_3 0x030 #define PHY_CMN_PLL_CNTRL 0x03C +#define PHY_CMN_GLBL_DIGTOP_SPARE4 0x128 /* Bit definition of SSC control registers */ #define SSC_CENTER BIT(0) @@ -253,6 +254,7 @@ static inline int pll_reg_read(void *context, unsigned int reg, unsigned int *val) { int rc = 0; + u32 data; struct mdss_pll_resources *rsc = context; rc = mdss_pll_resource_enable(rsc, true); @@ -261,7 +263,19 @@ static inline int pll_reg_read(void *context, unsigned int reg, return rc; } + /* + * DSI PHY/PLL should be both powered on when reading PLL + * registers. Since PHY power has been enabled in DSI PHY + * driver, only PLL power is needed to enable here. + */ + data = MDSS_PLL_REG_R(rsc->phy_base, PHY_CMN_CTRL_0); + MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data | BIT(5)); + ndelay(250); + *val = MDSS_PLL_REG_R(rsc->pll_base, reg); + + MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data); + (void)mdss_pll_resource_enable(rsc, false); return rc; @@ -880,10 +894,24 @@ static void dsi_pll_enable_global_clk(struct mdss_pll_resources *rsc) u32 data; MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_3, 0x04); + data = MDSS_PLL_REG_R(rsc->phy_base, PHY_CMN_CLK_CFG1); MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CLK_CFG1, (data | BIT(5))); } +static void dsi_pll_phy_dig_reset(struct mdss_pll_resources *rsc) +{ + /* + * Reset the PHY digital domain. This would be needed when + * coming out of a CX or analog rail power collapse while + * ensuring that the pads maintain LP00 or LP11 state + */ + MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_GLBL_DIGTOP_SPARE4, BIT(0)); + wmb(); /* Ensure that the reset is asserted */ + MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_GLBL_DIGTOP_SPARE4, 0x0); + wmb(); /* Ensure that the reset is deasserted */ +} + static int dsi_pll_enable(struct dsi_pll_vco_clk *vco) { int rc; @@ -917,13 +945,18 @@ static int dsi_pll_enable(struct dsi_pll_vco_clk *vco) rsc->pll_on = true; - dsi_pll_enable_global_clk(rsc); + /* + * assert power on reset for PHY digital in case the PLL is + * enabled after CX of analog domain power collapse. This needs + * to be done before enabling the global clk. + */ + dsi_pll_phy_dig_reset(rsc); if (rsc->slave) - dsi_pll_enable_global_clk(rsc->slave); + dsi_pll_phy_dig_reset(rsc->slave); - MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_RBUF_CTRL, 0x01); + dsi_pll_enable_global_clk(rsc); if (rsc->slave) - MDSS_PLL_REG_W(rsc->slave->phy_base, PHY_CMN_RBUF_CTRL, 0x01); + dsi_pll_enable_global_clk(rsc->slave); error: return rc; diff --git a/drivers/clk/qcom/vdd-level-405.h b/drivers/clk/qcom/vdd-level-405.h new file mode 100644 index 0000000000000000000000000000000000000000..be0cf4eded8616e15f457c008962299b820ab229 --- /dev/null +++ b/drivers/clk/qcom/vdd-level-405.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, 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 __DRIVERS_CLK_QCOM_VDD_LEVEL_405_H +#define __DRIVERS_CLK_QCOM_VDD_LEVEL_405_H + +#include +#include + +enum vdd_dig_levels { + VDD_NONE, + VDD_MIN, /* MIN SVS */ + VDD_LOWER, /* SVS2 */ + VDD_LOW, /* SVS */ + VDD_LOW_L1, /* SVSL1 */ + VDD_NOMINAL, /* NOM */ + VDD_NOMINAL_L1, /* NOM */ + VDD_HIGH, /* TURBO */ + VDD_NUM, +}; + +static int vdd_corner[] = { + RPM_REGULATOR_LEVEL_NONE, /* VDD_NONE */ + RPM_REGULATOR_LEVEL_MIN_SVS, /* VDD_MIN */ + RPM_REGULATOR_LEVEL_LOW_SVS, /* VDD_LOWER */ + RPM_REGULATOR_LEVEL_SVS, /* VDD_LOW */ + RPM_REGULATOR_LEVEL_SVS_PLUS, /* VDD_LOW_L1 */ + RPM_REGULATOR_LEVEL_NOM, /* VDD_NOMINAL */ + RPM_REGULATOR_LEVEL_NOM_PLUS, /* VDD_NOMINAL */ + RPM_REGULATOR_LEVEL_TURBO, /* VDD_HIGH */ +}; + +#endif diff --git a/drivers/clk/qcom/videocc-sm8150.c b/drivers/clk/qcom/videocc-sm8150.c index c14b34b28e0eba0e9d9c50603e90a68abda6dc46..c18ee37dc8b9da85eb7bbc1924730c159cb157da 100644 --- a/drivers/clk/qcom/videocc-sm8150.c +++ b/drivers/clk/qcom/videocc-sm8150.c @@ -106,9 +106,9 @@ static const struct freq_tbl ftbl_video_cc_iris_clk_src[] = { F(200000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), F(225000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), F(300000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), - F(365000000, P_VIDEO_PLL0_OUT_MAIN, 3, 0, 0), - F(432000000, P_VIDEO_PLL0_OUT_MAIN, 3, 0, 0), - F(480000000, P_VIDEO_PLL0_OUT_MAIN, 3, 0, 0), + F(365000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(432000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), + F(480000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0), { } }; diff --git a/drivers/clk/renesas/clk-sh73a0.c b/drivers/clk/renesas/clk-sh73a0.c index eea38f6ea77e995d287831a5d6b34bd7663883c0..3892346c4fcc177b811d386d8df41b53503d1f99 100644 --- a/drivers/clk/renesas/clk-sh73a0.c +++ b/drivers/clk/renesas/clk-sh73a0.c @@ -46,7 +46,7 @@ struct div4_clk { unsigned int shift; }; -static struct div4_clk div4_clks[] = { +static const struct div4_clk div4_clks[] = { { "zg", "pll0", CPG_FRQCRA, 16 }, { "m3", "pll1", CPG_FRQCRA, 12 }, { "b", "pll1", CPG_FRQCRA, 8 }, @@ -79,7 +79,7 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, { const struct clk_div_table *table = NULL; unsigned int shift, reg, width; - const char *parent_name; + const char *parent_name = NULL; unsigned int mult = 1; unsigned int div = 1; @@ -135,7 +135,7 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, shift = 24; width = 5; } else { - struct div4_clk *c; + const struct div4_clk *c; for (c = div4_clks; c->name; c++) { if (!strcmp(name, c->name)) { diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c index f8203115a6bcea4dff571a5ed664e2cc12a35b31..c10160d7a556b034223a6923615f857666596165 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c @@ -493,8 +493,8 @@ static SUNXI_CCU_MUX_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents, 0x118, 24, 3, BIT(31), CLK_SET_RATE_PARENT); static const char * const tcon1_parents[] = { "pll-video1" }; -static SUNXI_CCU_MUX_WITH_GATE(tcon1_clk, "tcon1", tcon1_parents, - 0x11c, 24, 3, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_GATE(tcon1_clk, "tcon1", tcon1_parents, + 0x11c, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", 0x130, BIT(16), 0); diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index baa3cf96507b5285f94768cb12e734076e97cf93..302a18efd39fa568b8a7ef362ff194e77823e617 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -71,7 +71,7 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, parent_rate); val = divider_recalc_rate(hw, parent_rate, val, cd->div.table, - cd->div.flags); + cd->div.flags, cd->div.width); if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) val /= cd->fixed_post_div; diff --git a/drivers/clocksource/timer-imx-tpm.c b/drivers/clocksource/timer-imx-tpm.c index 21bffdcb2f202640391fd54e1c5ec1c2c53835e4..557ed25b42e3b77581a6862de92c2eeeb8d2a291 100644 --- a/drivers/clocksource/timer-imx-tpm.c +++ b/drivers/clocksource/timer-imx-tpm.c @@ -105,7 +105,7 @@ static int tpm_set_next_event(unsigned long delta, * of writing CNT registers which may cause the min_delta event got * missed, so we need add a ETIME check here in case it happened. */ - return (int)((next - now) <= 0) ? -ETIME : 0; + return (int)(next - now) <= 0 ? -ETIME : 0; } static int tpm_set_state_oneshot(struct clock_event_device *evt) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 536a23ba5a6a543283142829176757702639da76..6791993dcffab3017a40aeaf1c7763cf5f0e12eb 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -37,6 +37,13 @@ config CPU_FREQ_STAT If in doubt, say N. +config CPU_FREQ_TIMES + bool "CPU frequency time-in-state statistics" + help + Export CPU time-in-state information through procfs. + + If in doubt, say N. + choice prompt "Default CPUFreq governor" default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index a6d4bfe65dda87ea3ede006787dc2495e3873a82..9f24c0a8150d0694eb46b46aab82139b3dd64b94 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -5,7 +5,10 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o -# CPUfreq governors +# CPUfreq times +obj-$(CONFIG_CPU_FREQ_TIMES) += cpufreq_times.o + +# CPUfreq governors obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index a1c3025f9df77b91bfe278ca89ab16ebe3c295eb..dcb1cb9a4572a6220956bc59b3b57be9b752d588 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -162,6 +163,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.max_freq = cppc_dmi_max_khz; policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num); + policy->transition_delay_us = cppc_get_transition_latency(cpu_num) / + NSEC_PER_USEC; policy->shared_type = cpu->shared_type; if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index beb86f7a840533f87d3d9ab7f0f2a345a0d33c0b..55fa8cb1a2f727f30746bea03653018d801d074d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -341,6 +342,7 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, (unsigned long)freqs->new, (unsigned long)freqs->cpu); trace_cpu_frequency(freqs->new, freqs->cpu); cpufreq_stats_record_transition(policy, freqs->new); + cpufreq_times_record_transition(freqs); srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); if (likely(policy) && likely(policy->cpu == freqs->cpu)) @@ -633,8 +635,6 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, *governor = t; err = 0; } - if (t && !try_module_get(t->owner)) - t = NULL; mutex_unlock(&cpufreq_governor_mutex); } @@ -766,10 +766,6 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, return -EINVAL; ret = cpufreq_set_policy(policy, &new_policy); - - if (new_policy.governor) - module_put(new_policy.governor->owner); - return ret ? ret : count; } @@ -1299,6 +1295,7 @@ static int cpufreq_online(unsigned int cpu) goto out_exit_policy; cpufreq_stats_create_table(policy); + cpufreq_times_create_policy(policy); write_lock_irqsave(&cpufreq_driver_lock, flags); list_add(&policy->policy_list, &cpufreq_policy_list); diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c new file mode 100644 index 0000000000000000000000000000000000000000..f560e10ba183bda4ce3bec2d31fbf5f0915ea57f --- /dev/null +++ b/drivers/cpufreq/cpufreq_times.c @@ -0,0 +1,461 @@ +/* drivers/cpufreq/cpufreq_times.c + * + * Copyright (C) 2018 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UID_HASH_BITS 10 + +static DECLARE_HASHTABLE(uid_hash_table, UID_HASH_BITS); + +static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */ +static DEFINE_SPINLOCK(uid_lock); /* uid_hash_table */ + +struct uid_entry { + uid_t uid; + unsigned int max_state; + struct hlist_node hash; + struct rcu_head rcu; + u64 time_in_state[0]; +}; + +/** + * struct cpu_freqs - per-cpu frequency information + * @offset: start of these freqs' stats in task time_in_state array + * @max_state: number of entries in freq_table + * @last_index: index in freq_table of last frequency switched to + * @freq_table: list of available frequencies + */ +struct cpu_freqs { + unsigned int offset; + unsigned int max_state; + unsigned int last_index; + unsigned int freq_table[0]; +}; + +static struct cpu_freqs *all_freqs[NR_CPUS]; + +static unsigned int next_offset; + + +/* Caller must hold rcu_read_lock() */ +static struct uid_entry *find_uid_entry_rcu(uid_t uid) +{ + struct uid_entry *uid_entry; + + hash_for_each_possible_rcu(uid_hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +/* Caller must hold uid lock */ +static struct uid_entry *find_uid_entry_locked(uid_t uid) +{ + struct uid_entry *uid_entry; + + hash_for_each_possible(uid_hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +/* Caller must hold uid lock */ +static struct uid_entry *find_or_register_uid_locked(uid_t uid) +{ + struct uid_entry *uid_entry, *temp; + unsigned int max_state = READ_ONCE(next_offset); + size_t alloc_size = sizeof(*uid_entry) + max_state * + sizeof(uid_entry->time_in_state[0]); + + uid_entry = find_uid_entry_locked(uid); + if (uid_entry) { + if (uid_entry->max_state == max_state) + return uid_entry; + /* uid_entry->time_in_state is too small to track all freqs, so + * expand it. + */ + temp = __krealloc(uid_entry, alloc_size, GFP_ATOMIC); + if (!temp) + return uid_entry; + temp->max_state = max_state; + memset(temp->time_in_state + uid_entry->max_state, 0, + (max_state - uid_entry->max_state) * + sizeof(uid_entry->time_in_state[0])); + if (temp != uid_entry) { + hlist_replace_rcu(&uid_entry->hash, &temp->hash); + kfree_rcu(uid_entry, rcu); + } + return temp; + } + + uid_entry = kzalloc(alloc_size, GFP_ATOMIC); + if (!uid_entry) + return NULL; + + uid_entry->uid = uid; + uid_entry->max_state = max_state; + + hash_add_rcu(uid_hash_table, &uid_entry->hash, uid); + + return uid_entry; +} + +static bool freq_index_invalid(unsigned int index) +{ + unsigned int cpu; + struct cpu_freqs *freqs; + + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || index < freqs->offset || + freqs->offset + freqs->max_state <= index) + continue; + return freqs->freq_table[index - freqs->offset] == + CPUFREQ_ENTRY_INVALID; + } + return true; +} + +static int single_uid_time_in_state_show(struct seq_file *m, void *ptr) +{ + struct uid_entry *uid_entry; + unsigned int i; + u64 time; + uid_t uid = from_kuid_munged(current_user_ns(), *(kuid_t *)m->private); + + if (uid == overflowuid) + return -EINVAL; + + rcu_read_lock(); + + uid_entry = find_uid_entry_rcu(uid); + if (!uid_entry) { + rcu_read_unlock(); + return 0; + } + + for (i = 0; i < uid_entry->max_state; ++i) { + if (freq_index_invalid(i)) + continue; + time = nsec_to_clock_t(uid_entry->time_in_state[i]); + seq_write(m, &time, sizeof(time)); + } + + rcu_read_unlock(); + + return 0; +} + +static void *uid_seq_start(struct seq_file *seq, loff_t *pos) +{ + if (*pos >= HASH_SIZE(uid_hash_table)) + return NULL; + + return &uid_hash_table[*pos]; +} + +static void *uid_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + (*pos)++; + + if (*pos >= HASH_SIZE(uid_hash_table)) + return NULL; + + return &uid_hash_table[*pos]; +} + +static void uid_seq_stop(struct seq_file *seq, void *v) { } + +static int uid_time_in_state_seq_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + struct cpu_freqs *freqs, *last_freqs = NULL; + int i, cpu; + + if (v == uid_hash_table) { + seq_puts(m, "uid:"); + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || freqs == last_freqs) + continue; + last_freqs = freqs; + for (i = 0; i < freqs->max_state; i++) { + if (freqs->freq_table[i] == + CPUFREQ_ENTRY_INVALID) + continue; + seq_printf(m, " %d", freqs->freq_table[i]); + } + } + seq_putc(m, '\n'); + } + + rcu_read_lock(); + + hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) { + if (uid_entry->max_state) + seq_printf(m, "%d:", uid_entry->uid); + for (i = 0; i < uid_entry->max_state; ++i) { + if (freq_index_invalid(i)) + continue; + seq_printf(m, " %lu", (unsigned long)nsec_to_clock_t( + uid_entry->time_in_state[i])); + } + if (uid_entry->max_state) + seq_putc(m, '\n'); + } + + rcu_read_unlock(); + return 0; +} + +void cpufreq_task_times_init(struct task_struct *p) +{ + void *temp; + unsigned long flags; + unsigned int max_state; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + p->time_in_state = NULL; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + p->max_state = 0; + + max_state = READ_ONCE(next_offset); + + /* We use one array to avoid multiple allocs per task */ + temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC); + if (!temp) + return; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + p->time_in_state = temp; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + p->max_state = max_state; +} + +/* Caller must hold task_time_in_state_lock */ +static int cpufreq_task_times_realloc_locked(struct task_struct *p) +{ + void *temp; + unsigned int max_state = READ_ONCE(next_offset); + + temp = krealloc(p->time_in_state, max_state * sizeof(u64), GFP_ATOMIC); + if (!temp) + return -ENOMEM; + p->time_in_state = temp; + memset(p->time_in_state + p->max_state, 0, + (max_state - p->max_state) * sizeof(u64)); + p->max_state = max_state; + return 0; +} + +void cpufreq_task_times_exit(struct task_struct *p) +{ + unsigned long flags; + void *temp; + + if (!p->time_in_state) + return; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + temp = p->time_in_state; + p->time_in_state = NULL; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + kfree(temp); +} + +int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *p) +{ + unsigned int cpu, i; + u64 cputime; + unsigned long flags; + struct cpu_freqs *freqs; + struct cpu_freqs *last_freqs = NULL; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || freqs == last_freqs) + continue; + last_freqs = freqs; + + seq_printf(m, "cpu%u\n", cpu); + for (i = 0; i < freqs->max_state; i++) { + if (freqs->freq_table[i] == CPUFREQ_ENTRY_INVALID) + continue; + cputime = 0; + if (freqs->offset + i < p->max_state && + p->time_in_state) + cputime = p->time_in_state[freqs->offset + i]; + seq_printf(m, "%u %lu\n", freqs->freq_table[i], + (unsigned long)nsec_to_clock_t(cputime)); + } + } + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + return 0; +} + +void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) +{ + unsigned long flags; + unsigned int state; + struct uid_entry *uid_entry; + struct cpu_freqs *freqs = all_freqs[task_cpu(p)]; + uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p)); + + if (!freqs || p->flags & PF_EXITING) + return; + + state = freqs->offset + READ_ONCE(freqs->last_index); + + spin_lock_irqsave(&task_time_in_state_lock, flags); + if ((state < p->max_state || !cpufreq_task_times_realloc_locked(p)) && + p->time_in_state) + p->time_in_state[state] += cputime; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + + spin_lock_irqsave(&uid_lock, flags); + uid_entry = find_or_register_uid_locked(uid); + if (uid_entry && state < uid_entry->max_state) + uid_entry->time_in_state[state] += cputime; + spin_unlock_irqrestore(&uid_lock, flags); +} + +void cpufreq_times_create_policy(struct cpufreq_policy *policy) +{ + int cpu, index; + unsigned int count = 0; + struct cpufreq_frequency_table *pos, *table; + struct cpu_freqs *freqs; + void *tmp; + + if (all_freqs[policy->cpu]) + return; + + table = policy->freq_table; + if (!table) + return; + + cpufreq_for_each_entry(pos, table) + count++; + + tmp = kzalloc(sizeof(*freqs) + sizeof(freqs->freq_table[0]) * count, + GFP_KERNEL); + if (!tmp) + return; + + freqs = tmp; + freqs->max_state = count; + + index = cpufreq_frequency_table_get_index(policy, policy->cur); + if (index >= 0) + WRITE_ONCE(freqs->last_index, index); + + cpufreq_for_each_entry(pos, table) + freqs->freq_table[pos - table] = pos->frequency; + + freqs->offset = next_offset; + WRITE_ONCE(next_offset, freqs->offset + count); + for_each_cpu(cpu, policy->related_cpus) + all_freqs[cpu] = freqs; +} + +void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end) +{ + struct uid_entry *uid_entry; + struct hlist_node *tmp; + unsigned long flags; + + spin_lock_irqsave(&uid_lock, flags); + + for (; uid_start <= uid_end; uid_start++) { + hash_for_each_possible_safe(uid_hash_table, uid_entry, tmp, + hash, uid_start) { + if (uid_start == uid_entry->uid) { + hash_del_rcu(&uid_entry->hash); + kfree_rcu(uid_entry, rcu); + } + } + } + + spin_unlock_irqrestore(&uid_lock, flags); +} + +void cpufreq_times_record_transition(struct cpufreq_freqs *freq) +{ + int index; + struct cpu_freqs *freqs = all_freqs[freq->cpu]; + struct cpufreq_policy *policy; + + if (!freqs) + return; + + policy = cpufreq_cpu_get(freq->cpu); + if (!policy) + return; + + index = cpufreq_frequency_table_get_index(policy, freq->new); + if (index >= 0) + WRITE_ONCE(freqs->last_index, index); + + cpufreq_cpu_put(policy); +} + +static const struct seq_operations uid_time_in_state_seq_ops = { + .start = uid_seq_start, + .next = uid_seq_next, + .stop = uid_seq_stop, + .show = uid_time_in_state_seq_show, +}; + +static int uid_time_in_state_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &uid_time_in_state_seq_ops); +} + +int single_uid_time_in_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, single_uid_time_in_state_show, + &(inode->i_uid)); +} + +static const struct file_operations uid_time_in_state_fops = { + .open = uid_time_in_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init cpufreq_times_init(void) +{ + proc_create_data("uid_time_in_state", 0444, NULL, + &uid_time_in_state_fops, NULL); + + return 0; +} + +early_initcall(cpufreq_times_init); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 93a0e88bef76f4a69a4abc60310e30d6f0549eee..20226d4243f2342dd83dd17ab49fa9a0a72d9e42 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -779,6 +779,8 @@ static int intel_pstate_hwp_save_state(struct cpufreq_policy *policy) return 0; } +static void intel_pstate_hwp_enable(struct cpudata *cpudata); + static int intel_pstate_resume(struct cpufreq_policy *policy) { if (!hwp_active) @@ -786,6 +788,9 @@ static int intel_pstate_resume(struct cpufreq_policy *policy) mutex_lock(&intel_pstate_limits_lock); + if (policy->cpu == 0) + intel_pstate_hwp_enable(all_cpu_data[policy->cpu]); + all_cpu_data[policy->cpu]->epp_policy = 0; intel_pstate_hwp_set(policy->cpu); diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 7e1e5bbcf43042252194cf19b19a2fa77ce02e58..a28bb8f3f3953596890579d6f52e9dce610c20a0 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -41,11 +41,9 @@ #define POWERNV_MAX_PSTATES 256 #define PMSR_PSAFE_ENABLE (1UL << 30) #define PMSR_SPR_EM_DISABLE (1UL << 31) -#define PMSR_MAX(x) ((x >> 32) & 0xFF) +#define MAX_PSTATE_SHIFT 32 #define LPSTATE_SHIFT 48 #define GPSTATE_SHIFT 56 -#define GET_LPSTATE(x) (((x) >> LPSTATE_SHIFT) & 0xFF) -#define GET_GPSTATE(x) (((x) >> GPSTATE_SHIFT) & 0xFF) #define MAX_RAMP_DOWN_TIME 5120 /* @@ -93,6 +91,7 @@ struct global_pstate_info { }; static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; +u32 pstate_sign_prefix; static bool rebooting, throttled, occ_reset; static const char * const throttle_reason[] = { @@ -147,6 +146,20 @@ static struct powernv_pstate_info { bool wof_enabled; } powernv_pstate_info; +static inline int extract_pstate(u64 pmsr_val, unsigned int shift) +{ + int ret = ((pmsr_val >> shift) & 0xFF); + + if (!ret) + return ret; + + return (pstate_sign_prefix | ret); +} + +#define extract_local_pstate(x) extract_pstate(x, LPSTATE_SHIFT) +#define extract_global_pstate(x) extract_pstate(x, GPSTATE_SHIFT) +#define extract_max_pstate(x) extract_pstate(x, MAX_PSTATE_SHIFT) + /* Use following macros for conversions between pstate_id and index */ static inline int idx_to_pstate(unsigned int i) { @@ -277,6 +290,9 @@ static int init_powernv_pstates(void) powernv_pstate_info.nr_pstates = nr_pstates; pr_debug("NR PStates %d\n", nr_pstates); + + pstate_sign_prefix = pstate_min & ~0xFF; + for (i = 0; i < nr_pstates; i++) { u32 id = be32_to_cpu(pstate_ids[i]); u32 freq = be32_to_cpu(pstate_freqs[i]); @@ -437,17 +453,10 @@ struct powernv_smp_call_data { static void powernv_read_cpu_freq(void *arg) { unsigned long pmspr_val; - s8 local_pstate_id; struct powernv_smp_call_data *freq_data = arg; pmspr_val = get_pmspr(SPRN_PMSR); - - /* - * The local pstate id corresponds bits 48..55 in the PMSR. - * Note: Watch out for the sign! - */ - local_pstate_id = (pmspr_val >> 48) & 0xFF; - freq_data->pstate_id = local_pstate_id; + freq_data->pstate_id = extract_local_pstate(pmspr_val); freq_data->freq = pstate_id_to_freq(freq_data->pstate_id); pr_debug("cpu %d pmsr %016lX pstate_id %d frequency %d kHz\n", @@ -521,7 +530,7 @@ static void powernv_cpufreq_throttle_check(void *data) chip = this_cpu_read(chip_info); /* Check for Pmax Capping */ - pmsr_pmax = (s8)PMSR_MAX(pmsr); + pmsr_pmax = extract_max_pstate(pmsr); pmsr_pmax_idx = pstate_to_idx(pmsr_pmax); if (pmsr_pmax_idx != powernv_pstate_info.max) { if (chip->throttled) @@ -637,6 +646,16 @@ void gpstate_timer_handler(unsigned long data) if (!spin_trylock(&gpstates->gpstate_lock)) return; + /* + * If the timer has migrated to the different cpu then bring + * it back to one of the policy->cpus + */ + if (!cpumask_test_cpu(raw_smp_processor_id(), policy->cpus)) { + gpstates->timer.expires = jiffies + msecs_to_jiffies(1); + add_timer_on(&gpstates->timer, cpumask_first(policy->cpus)); + spin_unlock(&gpstates->gpstate_lock); + return; + } /* * If PMCR was last updated was using fast_swtich then @@ -644,8 +663,8 @@ void gpstate_timer_handler(unsigned long data) * value. Hence, read from PMCR to get correct data. */ val = get_pmspr(SPRN_PMCR); - freq_data.gpstate_id = (s8)GET_GPSTATE(val); - freq_data.pstate_id = (s8)GET_LPSTATE(val); + freq_data.gpstate_id = extract_global_pstate(val); + freq_data.pstate_id = extract_local_pstate(val); if (freq_data.gpstate_id == freq_data.pstate_id) { reset_gpstates(policy); spin_unlock(&gpstates->gpstate_lock); @@ -676,10 +695,8 @@ void gpstate_timer_handler(unsigned long data) if (gpstate_idx != gpstates->last_lpstate_idx) queue_gpstate_timer(gpstates); + set_pstate(&freq_data); spin_unlock(&gpstates->gpstate_lock); - - /* Timer may get migrated to a different cpu on cpu hot unplug */ - smp_call_function_any(policy->cpus, set_pstate, &freq_data, 1); } /* diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c index 33ba7bf6ee2e4fc5ee9033c07c50193b301b2dde..7f7626f29a792e4eee4be1076cc15f50ca914fb9 100644 --- a/drivers/cpufreq/qcom-cpufreq.c +++ b/drivers/cpufreq/qcom-cpufreq.c @@ -27,10 +27,13 @@ #include #include #include +#include +#include #include static DEFINE_MUTEX(l2bw_lock); +static struct thermal_cooling_device *cdev[NR_CPUS]; static struct clk *cpu_clk[NR_CPUS]; static struct clk *l2_clk; static DEFINE_PER_CPU(struct cpufreq_frequency_table *, freq_table); @@ -306,6 +309,50 @@ static struct freq_attr *msm_freq_attr[] = { NULL, }; +static void msm_cpufreq_ready(struct cpufreq_policy *policy) +{ + struct device_node *np, *lmh_node; + unsigned int cpu = 0; + + if (cdev[policy->cpu]) + return; + + np = of_cpu_device_node_get(policy->cpu); + if (WARN_ON(!np)) + return; + + /* + * For now, just loading the cooling device; + * thermal DT code takes care of matching them. + */ + if (of_find_property(np, "#cooling-cells", NULL)) { + lmh_node = of_parse_phandle(np, "qcom,lmh-dcvs", 0); + if (lmh_node) { + of_node_put(lmh_node); + goto ready_exit; + } + + for_each_cpu(cpu, policy->related_cpus) { + + of_node_put(np); + np = of_cpu_device_node_get(cpu); + if (WARN_ON(!np)) + return; + + cdev[cpu] = of_cpufreq_cooling_register(np, policy); + if (IS_ERR(cdev[cpu])) { + pr_err( + "running cpufreq for CPU%d without cooling dev: %ld\n", + cpu, PTR_ERR(cdev[cpu])); + cdev[cpu] = NULL; + } + } + } + +ready_exit: + of_node_put(np); +} + static struct cpufreq_driver msm_cpufreq_driver = { /* lps calculations are handled here. */ .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | @@ -317,6 +364,7 @@ static struct cpufreq_driver msm_cpufreq_driver = { .get = msm_cpufreq_get_freq, .name = "msm", .attr = msm_freq_attr, + .ready = msm_cpufreq_ready, }; static struct cpufreq_frequency_table *cpufreq_parse_dt(struct device *dev, diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index e47e1410176837434ab7bb4587fb55cf3e317d32..22f9e87c5073983f96083692bd8509a8aa9d4531 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -634,16 +635,36 @@ EXPORT_SYMBOL_GPL(cpuidle_register); #ifdef CONFIG_SMP +static void wake_up_idle_cpus(void *v) +{ + int cpu; + struct cpumask cpus; + + if (v) { + cpumask_andnot(&cpus, v, cpu_isolated_mask); + cpumask_and(&cpus, &cpus, cpu_online_mask); + } else + cpumask_andnot(&cpus, cpu_online_mask, cpu_isolated_mask); + + preempt_disable(); + for_each_cpu(cpu, &cpus) { + if (cpu == smp_processor_id()) + continue; + wake_up_if_idle(cpu); + } + preempt_enable(); +} + /* * This function gets called when a part of the kernel has a new latency - * requirement. This means we need to get all processors out of their C-state, - * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that - * wakes them all right up. + * requirement. This means we need to get only those processors out of their + * C-state for which qos requirement is changed, and then recalculate a new + * suitable C-state. Just do a cross-cpu IPI; that wakes them all right up. */ static int cpuidle_latency_notify(struct notifier_block *b, unsigned long l, void *v) { - wake_up_all_idle_cpus(); + wake_up_idle_cpus(v); return NOTIFY_OK; } diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 4ea5f896421183e60ca27f17b2f04254b740873c..c3e3a9ce47060602c60e10bd5deaa05b12ed4a38 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -178,8 +178,8 @@ static ssize_t lpm_latency_show(struct kobject *kobj, struct kernel_param kp; struct lpm_level_avail *avail = get_avail_ptr(kobj, attr); - if (!avail) - pr_info("Error\n"); + if (WARN_ON(!avail)) + return -EINVAL; kp.arg = &avail->latency_us; @@ -197,8 +197,15 @@ ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr, { int ret = 0; struct kernel_param kp; + struct lpm_level_avail *avail = get_avail_ptr(kobj, attr); + + if (WARN_ON(!avail)) + return -EINVAL; + + kp.arg = get_enabled_ptr(attr, avail); + if (WARN_ON(!kp.arg)) + return -EINVAL; - kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr)); ret = param_get_bool(buf, &kp); if (ret > 0) { strlcat(buf, "\n", PAGE_SIZE); @@ -410,6 +417,9 @@ bool lpm_cpu_mode_allow(unsigned int cpu, { struct lpm_level_avail *avail = cpu_level_available[cpu]; + if (lpm_pdev && !index) + return 1; + if (!lpm_pdev || !avail) return !from_idle; @@ -450,10 +460,21 @@ static int parse_cluster_params(struct device_node *node, if (ret) goto fail; + key = "qcom,disable-prediction"; + c->lpm_prediction = !(of_property_read_bool(node, key)); + + if (c->lpm_prediction) { + key = "qcom,clstr-tmr-add"; + ret = of_property_read_u32(node, key, &c->tmr_add); + if (ret || c->tmr_add < TIMER_ADD_LOW || + c->tmr_add > TIMER_ADD_HIGH) + c->tmr_add = DEFAULT_TIMER_ADD; + } + /* Set default_level to 0 as default */ c->default_level = 0; - return ret; + return 0; fail: pr_err("Failed to read key: %s ret: %d\n", key, ret); @@ -711,6 +732,29 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) if (ret) goto failed_parse_params; + key = "qcom,disable-prediction"; + cpu->lpm_prediction = !(of_property_read_bool(node, key)); + + if (cpu->lpm_prediction) { + key = "qcom,ref-stddev"; + ret = of_property_read_u32(node, key, &cpu->ref_stddev); + if (ret || cpu->ref_stddev < STDDEV_LOW || + cpu->ref_stddev > STDDEV_HIGH) + cpu->ref_stddev = DEFAULT_STDDEV; + + key = "qcom,tmr-add"; + ret = of_property_read_u32(node, key, &cpu->tmr_add); + if (ret || cpu->tmr_add < TIMER_ADD_LOW || + cpu->tmr_add > TIMER_ADD_HIGH) + cpu->tmr_add = DEFAULT_TIMER_ADD; + + key = "qcom,ref-premature-cnt"; + ret = of_property_read_u32(node, key, &cpu->ref_premature_cnt); + if (ret || cpu->ref_premature_cnt < PREMATURE_CNT_LOW || + cpu->ref_premature_cnt > PREMATURE_CNT_HIGH) + cpu->ref_premature_cnt = DEFAULT_PREMATURE_CNT; + } + key = "parse_cpu"; ret = parse_cpu(node, cpu); if (ret) @@ -734,26 +778,22 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) void free_cluster_node(struct lpm_cluster *cluster) { - struct list_head *list; struct lpm_cpu *cpu, *n; - int i; + struct lpm_cluster *cl, *m; - list_for_each(list, &cluster->child) { - struct lpm_cluster *n; - - n = list_entry(list, typeof(*n), list); - list_del(list); - free_cluster_node(n); + list_for_each_entry_safe(cl, m, &cluster->child, list) { + list_del(&cl->list); + free_cluster_node(cl); }; list_for_each_entry_safe(cpu, n, &cluster->cpu, list) { - struct lpm_cpu *cpu = list_entry(list, typeof(*cpu), list); + int i; + list_del(&cpu->list); for (i = 0; i < cpu->nlevels; i++) { kfree(cpu->levels[i].name); cpu->levels[i].name = NULL; } - list_del(list); } } diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 80ef4f9820aa0c75934c05160d3dfc4aebf1274a..c7865bc273c60b7459e4e2632d8e8ed74b7caa1e 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -36,10 +36,12 @@ #include #include #include +#include #include #include +#include #include -#include +#include #include #include #include @@ -77,6 +79,9 @@ struct lpm_debug { uint32_t arg4; }; +static struct system_pm_ops *sys_pm_ops; + + struct lpm_cluster *lpm_root_node; #define MAXSAMPLES 5 @@ -84,12 +89,6 @@ struct lpm_cluster *lpm_root_node; static bool lpm_prediction = true; module_param_named(lpm_prediction, lpm_prediction, bool, 0664); -static uint32_t ref_stddev = 100; -module_param_named(ref_stddev, ref_stddev, uint, 0664); - -static uint32_t tmr_add = 100; -module_param_named(tmr_add, tmr_add, uint, 0664); - static uint32_t bias_hyst; module_param_named(bias_hyst, bias_hyst, uint, 0664); @@ -108,7 +107,7 @@ static DEFINE_PER_CPU(struct lpm_history, hist); static DEFINE_PER_CPU(struct lpm_cpu*, cpu_lpm); static bool suspend_in_progress; static struct hrtimer lpm_hrtimer; -static struct hrtimer histtimer; +static DEFINE_PER_CPU(struct hrtimer, histtimer); static struct lpm_debug *lpm_debug; static phys_addr_t lpm_debug_phys; static const int num_dbg_elements = 0x100; @@ -120,14 +119,6 @@ static void cluster_prepare(struct lpm_cluster *cluster, const struct cpumask *cpu, int child_idx, bool from_idle, int64_t time); -static bool menu_select; -module_param_named(menu_select, menu_select, bool, 0664); - -static int msm_pm_sleep_time_override; -module_param_named(sleep_time_override, - msm_pm_sleep_time_override, int, 0664); -static uint64_t suspend_wake_time; - static bool print_parsed_dt; module_param_named(print_parsed_dt, print_parsed_dt, bool, 0664); @@ -145,20 +136,15 @@ s32 msm_cpuidle_get_deep_idle_latency(void) } EXPORT_SYMBOL(msm_cpuidle_get_deep_idle_latency); -void lpm_suspend_wake_time(uint64_t wakeup_time) +uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops) { - if (wakeup_time <= 0) { - suspend_wake_time = msm_pm_sleep_time_override; - return; - } + if (sys_pm_ops) + return -EUSERS; - if (msm_pm_sleep_time_override && - (msm_pm_sleep_time_override < wakeup_time)) - suspend_wake_time = msm_pm_sleep_time_override; - else - suspend_wake_time = wakeup_time; + sys_pm_ops = pm_ops; + + return 0; } -EXPORT_SYMBOL(lpm_suspend_wake_time); static uint32_t least_cluster_latency(struct lpm_cluster *cluster, struct latency_level *lat_level) @@ -350,7 +336,10 @@ static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h) static void histtimer_cancel(void) { - hrtimer_try_to_cancel(&histtimer); + unsigned int cpu = raw_smp_processor_id(); + struct hrtimer *cpu_histtimer = &per_cpu(histtimer, cpu); + + hrtimer_try_to_cancel(cpu_histtimer); } static enum hrtimer_restart histtimer_fn(struct hrtimer *h) @@ -366,9 +355,11 @@ static void histtimer_start(uint32_t time_us) { uint64_t time_ns = time_us * NSEC_PER_USEC; ktime_t hist_ktime = ns_to_ktime(time_ns); + unsigned int cpu = raw_smp_processor_id(); + struct hrtimer *cpu_histtimer = &per_cpu(histtimer, cpu); - histtimer.function = histtimer_fn; - hrtimer_start(&histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED); + cpu_histtimer->function = histtimer_fn; + hrtimer_start(cpu_histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED); } static void cluster_timer_init(struct lpm_cluster *cluster) @@ -436,8 +427,9 @@ static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev, int64_t thresh = LLONG_MAX; struct lpm_history *history = &per_cpu(hist, dev->cpu); uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu); + uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu); - if (!lpm_prediction) + if (!lpm_prediction || !cpu->lpm_prediction) return 0; /* @@ -497,7 +489,7 @@ static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev, * ignore one maximum sample and retry */ if (((avg > stddev * 6) && (divisor >= (MAXSAMPLES - 1))) - || stddev <= ref_stddev) { + || stddev <= cpu->ref_stddev) { history->stime = ktime_to_us(ktime_get()) + avg; return avg; } else if (divisor > (MAXSAMPLES - 1)) { @@ -522,9 +514,17 @@ static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev, total += history->resi[i]; } } - if (failed > (MAXSAMPLES/2)) { + if (failed >= cpu->ref_premature_cnt) { *idx_restrict = j; do_div(total, failed); + for (i = 0; i < j; i++) { + if (total < max_residency[i]) { + *idx_restrict = i+1; + total = max_residency[i]; + break; + } + } + *idx_restrict_time = total; history->stime = ktime_to_us(ktime_get()) + *idx_restrict_time; @@ -538,8 +538,9 @@ static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev, static inline void invalidate_predict_history(struct cpuidle_device *dev) { struct lpm_history *history = &per_cpu(hist, dev->cpu); + struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, dev->cpu); - if (!lpm_prediction) + if (!lpm_prediction || !lpm_cpu->lpm_prediction) return; if (history->hinvalid) { @@ -554,8 +555,9 @@ static void clear_predict_history(void) struct lpm_history *history; int i; unsigned int cpu; + struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, raw_smp_processor_id()); - if (!lpm_prediction) + if (!lpm_prediction || !lpm_cpu->lpm_prediction) return; for_each_possible_cpu(cpu) { @@ -574,7 +576,13 @@ static void update_history(struct cpuidle_device *dev, int idx); static inline bool is_cpu_biased(int cpu) { - return false; + u64 now = sched_clock(); + u64 last = sched_get_cpu_last_busy_time(cpu); + + if (!last) + return false; + + return (now - last) < BIAS_HYST; } static int cpu_power_select(struct cpuidle_device *dev, @@ -601,17 +609,15 @@ static int cpu_power_select(struct cpuidle_device *dev, next_event_us = (uint32_t)(ktime_to_us(get_next_event_time(dev->cpu))); - if (is_cpu_biased(dev->cpu)) { - best_level = 0; + if (is_cpu_biased(dev->cpu) && (!cpu_isolated(dev->cpu))) goto done_select; - } for (i = 0; i < cpu->nlevels; i++) { struct lpm_cpu_level *level = &cpu->levels[i]; struct power_params *pwr_params = &level->pwr; bool allow; - allow = lpm_cpu_mode_allow(dev->cpu, i, true); + allow = i ? lpm_cpu_mode_allow(dev->cpu, i, true) : true; if (!allow) continue; @@ -630,7 +636,7 @@ static int cpu_power_select(struct cpuidle_device *dev, next_wakeup_us = next_event_us - lvl_latency_us; } - if (!i) { + if (!i && !cpu_isolated(dev->cpu)) { /* * If the next_wake_us itself is not sufficient for * deeper low power modes than clock gating do not @@ -670,8 +676,8 @@ static int cpu_power_select(struct cpuidle_device *dev, if ((predicted || (idx_restrict != (cpu->nlevels + 1))) && ((best_level >= 0) && (best_level < (cpu->nlevels-1)))) { - htime = predicted + tmr_add; - if (htime == tmr_add) + htime = predicted + cpu->tmr_add; + if (htime == cpu->tmr_add) htime = idx_restrict_time; else if (htime > max_residency[best_level]) htime = max_residency[best_level]; @@ -690,28 +696,40 @@ static int cpu_power_select(struct cpuidle_device *dev, return best_level; } +static unsigned int get_next_online_cpu(bool from_idle) +{ + unsigned int cpu; + ktime_t next_event; + unsigned int next_cpu = raw_smp_processor_id(); + + if (!from_idle) + return next_cpu; + next_event = KTIME_MAX; + for_each_online_cpu(cpu) { + ktime_t *next_event_c; + + next_event_c = get_next_event_cpu(cpu); + if (*next_event_c < next_event) { + next_event = *next_event_c; + next_cpu = cpu; + } + } + return next_cpu; +} + static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster, - struct cpumask *mask, bool from_idle, uint32_t *pred_time) + bool from_idle, uint32_t *pred_time) { int cpu; - int next_cpu = raw_smp_processor_id(); ktime_t next_event; struct cpumask online_cpus_in_cluster; struct lpm_history *history; int64_t prediction = LONG_MAX; - next_event = KTIME_MAX; - if (!suspend_wake_time) - suspend_wake_time = msm_pm_sleep_time_override; - if (!from_idle) { - if (mask) - cpumask_copy(mask, cpumask_of(raw_smp_processor_id())); - if (!suspend_wake_time) - return ~0ULL; - else - return USEC_PER_SEC * suspend_wake_time; - } + if (!from_idle) + return ~0ULL; + next_event = KTIME_MAX; cpumask_and(&online_cpus_in_cluster, &cluster->num_children_in_sync, cpu_online_mask); @@ -719,22 +737,17 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster, ktime_t *next_event_c; next_event_c = get_next_event_cpu(cpu); - if (*next_event_c < next_event) { + if (*next_event_c < next_event) next_event = *next_event_c; - next_cpu = cpu; - } - if (from_idle && lpm_prediction) { + if (from_idle && lpm_prediction && cluster->lpm_prediction) { history = &per_cpu(hist, cpu); if (history->stime && (history->stime < prediction)) prediction = history->stime; } } - if (mask) - cpumask_copy(mask, cpumask_of(next_cpu)); - - if (from_idle && lpm_prediction) { + if (from_idle && lpm_prediction && cluster->lpm_prediction) { if (prediction > ktime_to_us(ktime_get())) *pred_time = prediction - ktime_to_us(ktime_get()); } @@ -753,7 +766,7 @@ static int cluster_predict(struct lpm_cluster *cluster, struct cluster_history *history = &cluster->history; int64_t cur_time = ktime_to_us(ktime_get()); - if (!lpm_prediction) + if (!lpm_prediction || !cluster->lpm_prediction) return 0; if (history->hinvalid) { @@ -828,7 +841,7 @@ static void update_cluster_history(struct cluster_history *history, int idx) struct lpm_cluster *cluster = container_of(history, struct lpm_cluster, history); - if (!lpm_prediction) + if (!lpm_prediction || !cluster->lpm_prediction) return; if ((history->entry_idx == -1) || (history->entry_idx == idx)) { @@ -889,7 +902,7 @@ static void clear_cl_predict_history(void) struct lpm_cluster *cluster = lpm_root_node; struct list_head *list; - if (!lpm_prediction) + if (!lpm_prediction || !cluster->lpm_prediction) return; clear_cl_history_each(&cluster->history); @@ -916,7 +929,7 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle, if (!cluster) return -EINVAL; - sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL, + sleep_us = (uint32_t)get_cluster_sleep_time(cluster, from_idle, &cpupred_us); if (from_idle) { @@ -967,6 +980,13 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle, if (suspend_in_progress && from_idle && level->notify_rpm) continue; + if (level->notify_rpm) { + if (!(sys_pm_ops && sys_pm_ops->sleep_allowed)) + continue; + if (!sys_pm_ops->sleep_allowed()) + continue; + } + best_level = i; if (from_idle && @@ -999,11 +1019,15 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, bool from_idle, int predicted) { struct lpm_cluster_level *level = &cluster->levels[idx]; + struct cpumask online_cpus, cpumask; + unsigned int cpu; + + cpumask_and(&online_cpus, &cluster->num_children_in_sync, + cpu_online_mask); if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus) - || is_IPI_pending(&cluster->num_children_in_sync)) { + || is_IPI_pending(&online_cpus)) return -EPERM; - } if (idx != cluster->default_level) { update_debug_pc_event(CLUSTER_ENTER, idx, @@ -1014,16 +1038,19 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, cluster->child_cpus.bits[0], from_idle); lpm_stats_cluster_enter(cluster->stats, idx); - if (from_idle && lpm_prediction) + if (from_idle && lpm_prediction && cluster->lpm_prediction) update_cluster_history_time(&cluster->history, idx, ktime_to_us(ktime_get())); } if (level->notify_rpm) { + cpu = get_next_online_cpu(from_idle); + cpumask_copy(&cpumask, cpumask_of(cpu)); clear_predict_history(); clear_cl_predict_history(); - if (system_sleep_enter()) - return -EBUSY; + if (sys_pm_ops && sys_pm_ops->enter) + if ((sys_pm_ops->enter(&cpumask))) + return -EBUSY; } /* Notify cluster enter event after successfully config completion */ cluster_notify(cluster, level, true); @@ -1033,7 +1060,8 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, if (predicted && (idx < (cluster->nlevels - 1))) { struct power_params *pwr_params = &cluster->levels[idx].pwr; - clusttimer_start(cluster, pwr_params->max_residency + tmr_add); + clusttimer_start(cluster, pwr_params->max_residency + + cluster->tmr_add); } return 0; @@ -1087,7 +1115,8 @@ static void cluster_prepare(struct lpm_cluster *cluster, &cluster->levels[0].pwr; clusttimer_start(cluster, - pwr_params->max_residency + tmr_add); + pwr_params->max_residency + + cluster->tmr_add); goto failed; } @@ -1154,7 +1183,8 @@ static void cluster_unprepare(struct lpm_cluster *cluster, level = &cluster->levels[cluster->last_level]; if (level->notify_rpm) - system_sleep_exit(); + if (sys_pm_ops && sys_pm_ops->exit) + sys_pm_ops->exit(); update_debug_pc_event(CLUSTER_EXIT, cluster->last_level, cluster->num_children_in_sync.bits[0], @@ -1207,7 +1237,8 @@ static inline void cpu_unprepare(struct lpm_cpu *cpu, int cpu_index, cpu_pm_exit(); } -int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl) +static int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl, + bool from_idle) { int state_id = 0; @@ -1220,7 +1251,7 @@ int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl) &cluster->child_cpus)) goto unlock_and_return; - state_id |= get_cluster_id(cluster->parent, aff_lvl); + state_id |= get_cluster_id(cluster->parent, aff_lvl, from_idle); if (cluster->last_level != cluster->default_level) { struct lpm_cluster_level *level @@ -1228,14 +1259,16 @@ int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl) state_id |= (level->psci_id & cluster->psci_mode_mask) << cluster->psci_mode_shift; - (*aff_lvl)++; /* * We may have updated the broadcast timers, update * the wakeup value by reading the bc timer directly. */ if (level->notify_rpm) - system_sleep_update_wakeup(); + if (sys_pm_ops && sys_pm_ops->update_wakeup) + sys_pm_ops->update_wakeup(from_idle); + if (level->psci_id) + (*aff_lvl)++; } unlock_and_return: spin_unlock(&cluster->sync_lock); @@ -1262,7 +1295,7 @@ static bool psci_enter_sleep(struct lpm_cpu *cpu, int idx, bool from_idle) return success; } - state_id = get_cluster_id(cpu->parent, &affinity_level); + state_id = get_cluster_id(cpu->parent, &affinity_level, from_idle); power_state = PSCI_POWER_STATE(cpu->levels[idx].is_reset); affinity_level = PSCI_AFFINITY_LEVEL(affinity_level); state_id |= power_state | affinity_level | cpu->levels[idx].psci_id; @@ -1298,8 +1331,9 @@ static void update_history(struct cpuidle_device *dev, int idx) { struct lpm_history *history = &per_cpu(hist, dev->cpu); uint32_t tmr = 0; + struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, dev->cpu); - if (!lpm_prediction) + if (!lpm_prediction || !lpm_cpu->lpm_prediction) return; if (history->htmr_wkup) { @@ -1333,10 +1367,8 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, struct lpm_cpu *cpu = per_cpu(cpu_lpm, dev->cpu); bool success = true; const struct cpumask *cpumask = get_cpu_mask(dev->cpu); - int64_t start_time = ktime_to_ns(ktime_get()), end_time; - struct power_params *pwr_params; - - pwr_params = &cpu->levels[idx].pwr; + ktime_t start = ktime_get(); + uint64_t start_time = ktime_to_ns(start), end_time; cpu_prepare(cpu, idx, true); cluster_prepare(cpu->parent, cpumask, idx, true, start_time); @@ -1355,13 +1387,11 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev, cluster_unprepare(cpu->parent, cpumask, idx, true, end_time); cpu_unprepare(cpu, idx, true); - end_time = ktime_to_ns(ktime_get()) - start_time; - do_div(end_time, 1000); - dev->last_residency = end_time; + dev->last_residency = ktime_us_delta(ktime_get(), start); update_history(dev, idx); trace_cpu_idle_exit(idx, success); local_irq_enable(); - if (lpm_prediction) { + if (lpm_prediction && cpu->lpm_prediction) { histtimer_cancel(); clusttimer_cancel(); } @@ -1613,7 +1643,10 @@ static int lpm_probe(struct platform_device *pdev) { int ret; int size; + unsigned int cpu; + struct hrtimer *cpu_histtimer; struct kobject *module_kobj = NULL; + struct md_region md_entry; get_online_cpus(); lpm_root_node = lpm_of_parse_cluster(pdev); @@ -1635,7 +1668,11 @@ static int lpm_probe(struct platform_device *pdev) */ suspend_set_ops(&lpm_suspend_ops); hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer_init(&histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + for_each_possible_cpu(cpu) { + cpu_histtimer = &per_cpu(histtimer, cpu); + hrtimer_init(cpu_histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + } + cluster_timer_init(lpm_root_node); size = num_dbg_elements * sizeof(struct lpm_debug); @@ -1670,6 +1707,14 @@ static int lpm_probe(struct platform_device *pdev) goto failed; } + /* Add lpm_debug to Minidump*/ + strlcpy(md_entry.name, "KLPMDEBUG", sizeof(md_entry.name)); + md_entry.virt_addr = (uintptr_t)lpm_debug; + md_entry.phys_addr = lpm_debug_phys; + md_entry.size = size; + if (msm_minidump_add_region(&md_entry)) + pr_info("Failed to add lpm_debug in Minidump\n"); + return 0; failed: free_cluster_node(lpm_root_node); @@ -1695,13 +1740,23 @@ static int __init lpm_levels_module_init(void) { int rc; - rc = platform_driver_register(&lpm_driver); - if (rc) { - pr_info("Error registering %s\n", lpm_driver.driver.name); - goto fail; +#ifdef CONFIG_ARM + int cpu; + + for_each_possible_cpu(cpu) { + rc = arm_cpuidle_init(cpu); + if (rc) { + pr_err("CPU%d ARM CPUidle init failed (%d)\n", cpu, rc); + return rc; + } } +#endif + + rc = platform_driver_register(&lpm_driver); + if (rc) + pr_info("Error registering %s rc=%d\n", lpm_driver.driver.name, + rc); -fail: return rc; } late_initcall(lpm_levels_module_init); diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index 10567e70ac4e9fca023ed70b534d2a5cb6627215..a6c7c5b5986cdb35f3c74f2d59783552278acd16 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -15,6 +15,15 @@ #define NR_LPM_LEVELS 8 #define MAXSAMPLES 5 #define CLUST_SMPL_INVLD_TIME 40000 +#define DEFAULT_PREMATURE_CNT 3 +#define DEFAULT_STDDEV 100 +#define DEFAULT_TIMER_ADD 100 +#define TIMER_ADD_LOW 100 +#define TIMER_ADD_HIGH 1500 +#define STDDEV_LOW 100 +#define STDDEV_HIGH 1000 +#define PREMATURE_CNT_LOW 1 +#define PREMATURE_CNT_HIGH 5 struct power_params { uint32_t latency_us; /* Enter + Exit latency */ @@ -32,7 +41,6 @@ struct lpm_cpu_level { struct power_params pwr; unsigned int psci_id; bool is_reset; - bool hyp_psci; int reset_level; }; @@ -43,6 +51,10 @@ struct lpm_cpu { int nlevels; unsigned int psci_mode_shift; unsigned int psci_mode_mask; + uint32_t ref_stddev; + uint32_t ref_premature_cnt; + uint32_t tmr_add; + bool lpm_prediction; struct cpuidle_driver *drv; struct lpm_cluster *parent; }; @@ -96,6 +108,8 @@ struct lpm_cluster { int min_child_level; int default_level; int last_level; + uint32_t tmr_add; + bool lpm_prediction; struct list_head cpu; spinlock_t sync_lock; struct cpumask child_cpus; @@ -108,8 +122,6 @@ struct lpm_cluster { struct hrtimer histtimer; }; -void lpm_suspend_wake_time(uint64_t wakeup_time); - struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev); void free_cluster_node(struct lpm_cluster *cluster); void cluster_dt_walkthrough(struct lpm_cluster *cluster); @@ -123,7 +135,7 @@ uint32_t *get_per_cpu_max_residency(int cpu); uint32_t *get_per_cpu_min_residency(int cpu); extern struct lpm_cluster *lpm_root_node; -#if CONFIG_SMP +#if defined(CONFIG_SMP) extern DEFINE_PER_CPU(bool, pending_ipi); static inline bool is_IPI_pending(const struct cpumask *mask) { diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index db10db1028cab7bceba5de6dbce3018878870fcc..8effad0e7d269fcf1835125f8107f0c4d0af3730 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -763,7 +763,6 @@ config CRYPTO_DEV_ARTPEC6 select CRYPTO_HASH select CRYPTO_SHA1 select CRYPTO_SHA256 - select CRYPTO_SHA384 select CRYPTO_SHA512 help Enables the driver for the on-chip crypto accelerator diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index e1d4ae1153c4fb165e16bb32bd7e8e722e2c349b..39f70411f28feb82b5bf7126ed748b9b52b291c4 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -813,9 +813,6 @@ static int caam_probe(struct platform_device *pdev) return 0; caam_remove: -#ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(ctrlpriv->dfs_root); -#endif caam_remove(pdev); return ret; diff --git a/drivers/crypto/ccp/ccp-crypto-rsa.c b/drivers/crypto/ccp/ccp-crypto-rsa.c index e6db8672d89c436f2559522cf14e98fe05159dc0..05850dfd794076b2541c969319479cf93f37dbeb 100644 --- a/drivers/crypto/ccp/ccp-crypto-rsa.c +++ b/drivers/crypto/ccp/ccp-crypto-rsa.c @@ -60,10 +60,9 @@ static int ccp_rsa_complete(struct crypto_async_request *async_req, int ret) static unsigned int ccp_rsa_maxsize(struct crypto_akcipher *tfm) { - if (ccp_version() > CCP_VERSION(3, 0)) - return CCP5_RSA_MAXMOD; - else - return CCP_RSA_MAXMOD; + struct ccp_ctx *ctx = akcipher_tfm_ctx(tfm); + + return ctx->u.rsa.n_len; } static int ccp_rsa_crypt(struct akcipher_request *req, bool encrypt) diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c index 4bcef78a08aad241675e50d959aff2a6c58e126b..d4c81cb73bee6ccfdbea9a67af7237cea86649d2 100644 --- a/drivers/crypto/inside-secure/safexcel.c +++ b/drivers/crypto/inside-secure/safexcel.c @@ -789,7 +789,7 @@ static int safexcel_probe(struct platform_device *pdev) return PTR_ERR(priv->base); } - priv->clk = of_clk_get(dev->of_node, 0); + priv->clk = devm_clk_get(&pdev->dev, NULL); if (!IS_ERR(priv->clk)) { ret = clk_prepare_enable(priv->clk); if (ret) { diff --git a/drivers/crypto/msm/Makefile b/drivers/crypto/msm/Makefile index 9ecb646220f5fafc742db90f582cccbe1cb4a20c..b712fc193f91116901c4412ae6d3cf4c8f8ed289 100644 --- a/drivers/crypto/msm/Makefile +++ b/drivers/crypto/msm/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_CRYPTO_DEV_QCOM_MSM_QCE) += qce50.o obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev.o +obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev_smmu.o obj-$(CONFIG_CRYPTO_DEV_QCRYPTO) += qcrypto.o obj-$(CONFIG_CRYPTO_DEV_OTA_CRYPTO) += ota_crypto.o obj-$(CONFIG_CRYPTO_DEV_QCOM_ICE) += ice.o diff --git a/drivers/crypto/msm/compat_qcedev.c b/drivers/crypto/msm/compat_qcedev.c index c10ea68c8a210df3996aa1da6d7abcc51f56fea7..41b13a7d227b43c35b09f1a25c04602050704e0f 100644 --- a/drivers/crypto/msm/compat_qcedev.c +++ b/drivers/crypto/msm/compat_qcedev.c @@ -254,6 +254,75 @@ static int compat_put_qcedev_cipher_op_req( return err; } +static int compat_xfer_qcedev_map_buf_req( + struct compat_qcedev_map_buf_req __user *data32, + struct qcedev_map_buf_req __user *data, bool to_get) +{ + int rc = 0, i = 0, fd = -1; + uint32_t fd_size, fd_offset, num_fds, buf_vaddr; + + if (to_get) { + /* copy from compat struct */ + for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { + rc |= get_user(fd, &data32->fd[i]); + rc |= put_user(fd, &data->fd[i]); + rc |= get_user(fd_size, &data32->fd_size[i]); + rc |= put_user(fd_size, &data->fd_size[i]); + rc |= get_user(fd_offset, &data32->fd_offset[i]); + rc |= put_user(fd_offset, &data->fd_offset[i]); + rc |= get_user(buf_vaddr, &data32->buf_vaddr[i]); + rc |= put_user(buf_vaddr, &data->buf_vaddr[i]); + } + + rc |= get_user(num_fds, &data32->num_fds); + rc |= put_user(num_fds, &data->num_fds); + } else { + /* copy to compat struct */ + for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { + rc |= get_user(fd, &data->fd[i]); + rc |= put_user(fd, &data32->fd[i]); + rc |= get_user(fd_size, &data->fd_size[i]); + rc |= put_user(fd_size, &data32->fd_size[i]); + rc |= get_user(fd_offset, &data->fd_offset[i]); + rc |= put_user(fd_offset, &data32->fd_offset[i]); + rc |= get_user(buf_vaddr, &data->buf_vaddr[i]); + rc |= put_user(buf_vaddr, &data32->buf_vaddr[i]); + } + rc |= get_user(num_fds, &data->num_fds); + rc |= put_user(num_fds, &data32->num_fds); + } + + return rc; +} + +static int compat_xfer_qcedev_unmap_buf_req( + struct compat_qcedev_unmap_buf_req __user *data32, + struct qcedev_unmap_buf_req __user *data, bool to_get) +{ + int i = 0, rc = 0, fd = -1; + uint32_t num_fds; + + if (to_get) { + /* copy from compat struct */ + for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { + rc |= get_user(fd, &data32->fd[i]); + rc |= put_user(fd, &data->fd[i]); + } + rc |= get_user(num_fds, &data32->num_fds); + rc |= put_user(num_fds, &data->num_fds); + } else { + /* copy to compat struct */ + for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) { + rc |= get_user(fd, &data->fd[i]); + rc |= put_user(fd, &data32->fd[i]); + } + rc |= get_user(num_fds, &data->num_fds); + rc |= put_user(num_fds, &data32->num_fds); + } + return rc; +} + + static int compat_get_qcedev_sha_op_req( struct compat_qcedev_sha_op_req __user *data32, struct qcedev_sha_op_req __user *data) @@ -359,6 +428,10 @@ static unsigned int convert_cmd(unsigned int cmd) return QCEDEV_IOCTL_GET_SHA_REQ; case COMPAT_QCEDEV_IOCTL_GET_CMAC_REQ: return QCEDEV_IOCTL_GET_CMAC_REQ; + case COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ: + return QCEDEV_IOCTL_MAP_BUF_REQ; + case COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ: + return QCEDEV_IOCTL_UNMAP_BUF_REQ; default: return cmd; } @@ -412,6 +485,46 @@ long compat_qcedev_ioctl(struct file *file, err = compat_put_qcedev_sha_op_req(data32, data); return ret ? ret : err; } + case COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ: { + struct compat_qcedev_map_buf_req __user *data32; + struct qcedev_map_buf_req __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (!data) + return -EINVAL; + + err = compat_xfer_qcedev_map_buf_req(data32, data, true); + if (err) + return err; + + ret = qcedev_ioctl(file, convert_cmd(cmd), (unsigned long)data); + err = compat_xfer_qcedev_map_buf_req(data32, data, false); + return ret ? ret : err; + + break; + } + case COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ: { + struct compat_qcedev_unmap_buf_req __user *data32; + struct qcedev_unmap_buf_req __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (!data) + return -EINVAL; + + err = compat_xfer_qcedev_unmap_buf_req(data32, data, true); + if (err) + return err; + + ret = qcedev_ioctl(file, convert_cmd(cmd), (unsigned long)data); + err = compat_xfer_qcedev_unmap_buf_req(data32, data, false); + return ret ? ret : err; + + break; + } default: return -ENOIOCTLCMD; } diff --git a/drivers/crypto/msm/compat_qcedev.h b/drivers/crypto/msm/compat_qcedev.h index 6c041cba6e4c89584f74a0ef832e02076a8b3082..4cefd0ad54a9ce28bfd2c3f92077925f012a7e4e 100644 --- a/drivers/crypto/msm/compat_qcedev.h +++ b/drivers/crypto/msm/compat_qcedev.h @@ -151,6 +151,33 @@ struct compat_qcedev_sha_op_req { enum qcedev_sha_alg_enum alg; }; +/** + * struct compact_qcedev_map_buf_req - Holds the mapping request information + * fd (IN): Array of fds. + * num_fds (IN): Number of fds in fd[]. + * fd_size (IN): Array of sizes corresponding to each fd in fd[]. + * fd_offset (IN): Array of offset corresponding to each fd in fd[]. + * vaddr (OUT): Array of mapped virtual address corresponding to + * each fd in fd[]. + */ +struct compat_qcedev_map_buf_req { + compat_long_t fd[QCEDEV_MAX_BUFFERS]; + compat_ulong_t num_fds; + compat_ulong_t fd_size[QCEDEV_MAX_BUFFERS]; + compat_ulong_t fd_offset[QCEDEV_MAX_BUFFERS]; + compat_u64 buf_vaddr[QCEDEV_MAX_BUFFERS]; +}; + +/** + * struct compat_qcedev_unmap_buf_req - Holds the hashing request information + * fd (IN): Array of fds to unmap + * num_fds (IN): Number of fds in fd[]. + */ +struct compat_qcedev_unmap_buf_req { + compat_long_t fd[QCEDEV_MAX_BUFFERS]; + compat_ulong_t num_fds; +}; + struct file; extern long compat_qcedev_ioctl(struct file *file, unsigned int cmd, unsigned long arg); @@ -173,6 +200,9 @@ extern long compat_qcedev_ioctl(struct file *file, _IO(QCEDEV_IOC_MAGIC, 8) #define COMPAT_QCEDEV_IOCTL_GET_CMAC_REQ \ _IOWR(QCEDEV_IOC_MAGIC, 9, struct compat_qcedev_sha_op_req) - +#define COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ \ + _IOWR(QCEDEV_IOC_MAGIC, 10, struct compat_qcedev_map_buf_req) +#define COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ \ + _IOWR(QCEDEV_IOC_MAGIC, 11, struct compat_qcedev_unmap_buf_req) #endif /* CONFIG_COMPAT */ #endif /* _UAPI_COMPAT_QCEDEV__H */ diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c index d2974068807037a1ef8a80d028f242e3a0d5abe8..668724e698c0aeacba7954e4fa06ca87f8cbab1f 100644 --- a/drivers/crypto/msm/qcedev.c +++ b/drivers/crypto/msm/qcedev.c @@ -36,6 +36,7 @@ #include #include "qcedevi.h" #include "qce.h" +#include "qcedev_smmu.h" #include #include "compat_qcedev.h" @@ -64,6 +65,14 @@ static dev_t qcedev_device_no; static struct class *driver_class; static struct device *class_dev; +MODULE_DEVICE_TABLE(of, qcedev_match); + +static const struct of_device_id qcedev_match[] = { + { .compatible = "qcom,qcedev"}, + { .compatible = "qcom,qcedev,context-bank"}, + {} +}; + static int qcedev_control_clocks(struct qcedev_control *podev, bool enable) { unsigned int control_flag; @@ -259,6 +268,9 @@ static int qcedev_open(struct inode *inode, struct file *file) file->private_data = handle; if (podev->platform_support.bus_scale_table != NULL) qcedev_ce_high_bw_req(podev, true); + + mutex_init(&handle->registeredbufs.lock); + INIT_LIST_HEAD(&handle->registeredbufs.list); return 0; } @@ -1856,6 +1868,62 @@ static inline long qcedev_ioctl(struct file *file, } break; + case QCEDEV_IOCTL_MAP_BUF_REQ: + { + unsigned long long vaddr = 0; + struct qcedev_map_buf_req map_buf = { {0} }; + int i = 0; + + if (copy_from_user(&map_buf, + (void __user *)arg, sizeof(map_buf))) + return -EFAULT; + + for (i = 0; i < map_buf.num_fds; i++) { + err = qcedev_check_and_map_buffer(handle, + map_buf.fd[i], + map_buf.fd_offset[i], + map_buf.fd_size[i], + &vaddr); + if (err) { + pr_err( + "%s: err: failed to map fd(%d) - %d\n", + __func__, map_buf.fd[i], err); + return err; + } + map_buf.buf_vaddr[i] = vaddr; + pr_info("%s: info: vaddr = %llx\n", + __func__, vaddr); + } + + if (copy_to_user((void __user *)arg, &map_buf, + sizeof(map_buf))) + return -EFAULT; + break; + } + + case QCEDEV_IOCTL_UNMAP_BUF_REQ: + { + struct qcedev_unmap_buf_req unmap_buf = { { 0 } }; + int i = 0; + + if (copy_from_user(&unmap_buf, + (void __user *)arg, sizeof(unmap_buf))) + return -EFAULT; + + for (i = 0; i < unmap_buf.num_fds; i++) { + err = qcedev_check_and_unmap_buffer(handle, + unmap_buf.fd[i]); + if (err) { + pr_err( + "%s: err: failed to unmap fd(%d) - %d\n", + __func__, + unmap_buf.fd[i], err); + return err; + } + } + break; + } + default: return -ENOTTY; } @@ -1863,7 +1931,7 @@ static inline long qcedev_ioctl(struct file *file, return err; } -static int qcedev_probe(struct platform_device *pdev) +static int qcedev_probe_device(struct platform_device *pdev) { void *handle = NULL; int rc = 0; @@ -1907,6 +1975,8 @@ static int qcedev_probe(struct platform_device *pdev) INIT_LIST_HEAD(&podev->ready_commands); podev->active_command = NULL; + INIT_LIST_HEAD(&podev->context_banks); + spin_lock_init(&podev->lock); tasklet_init(&podev->done_tasklet, req_done, (unsigned long)podev); @@ -1965,8 +2035,27 @@ static int qcedev_probe(struct platform_device *pdev) podev->platform_support.sha_hmac = platform_support->sha_hmac; } + podev->mem_client = qcedev_mem_new_client(MEM_ION); + if (!podev->mem_client) { + pr_err("%s: err: qcedev_mem_new_client failed\n", __func__); + goto exit_qce_close; + } + + rc = of_platform_populate(pdev->dev.of_node, qcedev_match, + NULL, &pdev->dev); + if (rc) { + pr_err("%s: err: of_platform_populate failed: %d\n", + __func__, rc); + goto exit_mem_new_client; + } + return 0; +exit_mem_new_client: + if (podev->mem_client) + qcedev_mem_delete_client(podev->mem_client); + podev->mem_client = NULL; + exit_qce_close: if (handle) qce_close(handle); @@ -1984,7 +2073,23 @@ static int qcedev_probe(struct platform_device *pdev) exit_unreg_chrdev_region: unregister_chrdev_region(qcedev_device_no, 1); + podev->bus_scale_handle = 0; + platform_set_drvdata(pdev, NULL); + podev->pdev = NULL; + podev->qce = NULL; + return rc; +} + +static int qcedev_probe(struct platform_device *pdev) +{ + if (of_device_is_compatible(pdev->dev.of_node, "qcom,qcedev")) + return qcedev_probe_device(pdev); + else if (of_device_is_compatible(pdev->dev.of_node, + "qcom,qcedev,context-bank")) + return qcedev_parse_context_bank(pdev); + + return -EINVAL; }; static int qcedev_remove(struct platform_device *pdev) @@ -2055,12 +2160,6 @@ static int qcedev_resume(struct platform_device *pdev) return 0; } -static const struct of_device_id qcedev_match[] = { - { .compatible = "qcom,qcedev", - }, - {} -}; - static struct platform_driver qcedev_plat_driver = { .probe = qcedev_probe, .remove = qcedev_remove, diff --git a/drivers/crypto/msm/qcedev_smmu.c b/drivers/crypto/msm/qcedev_smmu.c new file mode 100644 index 0000000000000000000000000000000000000000..2fe9de8114eb7a3e70dc46794945a912cf8c2d01 --- /dev/null +++ b/drivers/crypto/msm/qcedev_smmu.c @@ -0,0 +1,467 @@ +/* Qti (or) Qualcomm Technologies Inc CE device driver. + * + * Copyright (c) 2018, 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 +#include +#include +#include +#include "qcedevi.h" +#include "qcedev_smmu.h" +#include "soc/qcom/secure_buffer.h" + +static int qcedev_setup_context_bank(struct context_bank_info *cb, + struct device *dev) +{ + int rc = 0; + int secure_vmid = VMID_INVAL; + struct bus_type *bus; + + if (!dev || !cb) { + pr_err("%s err: invalid input params\n", __func__); + return -EINVAL; + } + cb->dev = dev; + + bus = cb->dev->bus; + if (IS_ERR_OR_NULL(bus)) { + pr_err("%s err: failed to get bus type\n", __func__); + rc = PTR_ERR(bus) ?: -ENODEV; + goto remove_cb; + } + + cb->mapping = arm_iommu_create_mapping(bus, cb->start_addr, cb->size); + if (IS_ERR_OR_NULL(cb->mapping)) { + pr_err("%s err: failed to create mapping\n", __func__); + rc = PTR_ERR(cb->mapping) ?: -ENODEV; + goto remove_cb; + } + + if (cb->is_secure) { + /* Hardcoded since we only have this vmid.*/ + secure_vmid = VMID_CP_BITSTREAM; + rc = iommu_domain_set_attr(cb->mapping->domain, + DOMAIN_ATTR_SECURE_VMID, &secure_vmid); + if (rc) { + pr_err("%s err: programming secure vmid failed %s %d\n", + __func__, dev_name(dev), rc); + goto release_mapping; + } + } + + rc = arm_iommu_attach_device(cb->dev, cb->mapping); + if (rc) { + pr_err("%s err: Failed to attach %s - %d\n", + __func__, dev_name(dev), rc); + goto release_mapping; + } + + pr_info("%s Attached %s and create mapping\n", __func__, dev_name(dev)); + pr_info("%s Context Bank name:%s, is_secure:%d, start_addr:%#x\n", + __func__, cb->name, cb->is_secure, cb->start_addr); + pr_info("%s size:%#x, dev:%pK, mapping:%pK\n", __func__, cb->size, + cb->dev, cb->mapping); + return rc; + +release_mapping: + arm_iommu_release_mapping(cb->mapping); +remove_cb: + return rc; +} + +int qcedev_parse_context_bank(struct platform_device *pdev) +{ + struct qcedev_control *podev; + struct context_bank_info *cb = NULL; + struct device_node *np = NULL; + int rc = 0; + + if (!pdev) { + pr_err("%s err: invalid platform devices\n", __func__); + return -EINVAL; + } + if (!pdev->dev.parent) { + pr_err("%s err: failed to find a parent for %s\n", + __func__, dev_name(&pdev->dev)); + return -EINVAL; + } + + podev = dev_get_drvdata(pdev->dev.parent); + np = pdev->dev.of_node; + cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL); + if (!cb) { + pr_err("%s ERROR = Failed to allocate cb\n", __func__); + return -ENOMEM; + } + + INIT_LIST_HEAD(&cb->list); + list_add_tail(&cb->list, &podev->context_banks); + + rc = of_property_read_string(np, "label", &cb->name); + if (rc) + pr_debug("%s ERROR = Unable to read label\n", __func__); + + rc = of_property_read_u32(np, "virtual-addr", &cb->start_addr); + if (rc) { + pr_err("%s err: cannot read virtual region addr %d\n", + __func__, rc); + goto err_setup_cb; + } + + rc = of_property_read_u32(np, "virtual-size", &cb->size); + if (rc) { + pr_err("%s err: cannot read virtual region size %d\n", + __func__, rc); + goto err_setup_cb; + } + + cb->is_secure = of_property_read_bool(np, "qcom,secure-context-bank"); + + rc = qcedev_setup_context_bank(cb, &pdev->dev); + if (rc) { + pr_err("%s err: cannot setup context bank %d\n", __func__, rc); + goto err_setup_cb; + } + + return 0; + +err_setup_cb: + devm_kfree(&pdev->dev, cb); + list_del(&cb->list); + return rc; +} + +struct qcedev_mem_client *qcedev_mem_new_client(enum qcedev_mem_type mtype) +{ + struct qcedev_mem_client *mem_client = NULL; + + if (mtype != MEM_ION) { + pr_err("%s: err: Mem type not supported\n", __func__); + goto err; + } + + mem_client = kzalloc(sizeof(*mem_client), GFP_KERNEL); + if (!mem_client) + goto err; + mem_client->mtype = mtype; + + return mem_client; +err: + return NULL; +} + +void qcedev_mem_delete_client(struct qcedev_mem_client *mem_client) +{ + kfree(mem_client); +} + +static bool is_iommu_present(struct qcedev_handle *qce_hndl) +{ + return !list_empty(&qce_hndl->cntl->context_banks); +} + +static struct context_bank_info *get_context_bank( + struct qcedev_handle *qce_hndl, bool is_secure) +{ + struct qcedev_control *podev = qce_hndl->cntl; + struct context_bank_info *cb = NULL, *match = NULL; + + list_for_each_entry(cb, &podev->context_banks, list) { + if (cb->is_secure == is_secure) { + match = cb; + break; + } + } + return match; +} + +static int ion_map_buffer(struct qcedev_handle *qce_hndl, + struct qcedev_mem_client *mem_client, int fd, + unsigned int fd_size, struct qcedev_reg_buf_info *binfo) +{ + unsigned long ion_flags = 0; + int rc = 0; + struct dma_buf *buf = NULL; + struct dma_buf_attachment *attach = NULL; + struct sg_table *table = NULL; + struct context_bank_info *cb = NULL; + + buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(buf)) + return -EINVAL; + + rc = dma_buf_get_flags(buf, &ion_flags); + if (rc) { + pr_err("%s: err: failed to get ion flags: %d\n", __func__, rc); + goto map_err; + } + + if (is_iommu_present(qce_hndl)) { + cb = get_context_bank(qce_hndl, ion_flags & ION_FLAG_SECURE); + if (!cb) { + pr_err("%s: err: failed to get context bank info\n", + __func__); + rc = -EIO; + goto map_err; + } + + /* Prepare a dma buf for dma on the given device */ + attach = dma_buf_attach(buf, cb->dev); + if (IS_ERR_OR_NULL(attach)) { + rc = PTR_ERR(attach) ?: -ENOMEM; + pr_err("%s: err: failed to attach dmabuf\n", __func__); + goto map_err; + } + + /* Get the scatterlist for the given attachment */ + attach->dma_map_attrs |= DMA_ATTR_DELAYED_UNMAP; + table = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(table)) { + rc = PTR_ERR(table) ?: -ENOMEM; + pr_err("%s: err: failed to map table\n", __func__); + goto map_table_err; + } + + if (table->sgl) { + binfo->ion_buf.iova = sg_dma_address(table->sgl); + binfo->ion_buf.mapped_buf_size = sg_dma_len(table->sgl); + if (binfo->ion_buf.mapped_buf_size < fd_size) { + pr_err("%s: err: mapping failed, size mismatch", + __func__); + rc = -ENOMEM; + goto map_sg_err; + } + } else { + pr_err("%s: err: sg list is NULL\n", __func__); + rc = -ENOMEM; + goto map_sg_err; + } + + binfo->ion_buf.mapping_info.dev = cb->dev; + binfo->ion_buf.mapping_info.mapping = cb->mapping; + binfo->ion_buf.mapping_info.table = table; + binfo->ion_buf.mapping_info.attach = attach; + binfo->ion_buf.mapping_info.buf = buf; + binfo->ion_buf.ion_fd = fd; + } else { + pr_err("%s: err: smmu not enabled\n", __func__); + rc = -EIO; + goto map_err; + } + + return 0; + +map_sg_err: + dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL); +map_table_err: + dma_buf_detach(buf, attach); +map_err: + dma_buf_put(buf); + return rc; +} + +static int ion_unmap_buffer(struct qcedev_handle *qce_hndl, + struct qcedev_reg_buf_info *binfo) +{ + struct dma_mapping_info *mapping_info = &binfo->ion_buf.mapping_info; + + if (is_iommu_present(qce_hndl)) { + dma_buf_unmap_attachment(mapping_info->attach, + mapping_info->table, DMA_BIDIRECTIONAL); + dma_buf_detach(mapping_info->buf, mapping_info->attach); + dma_buf_put(mapping_info->buf); + + } + return 0; +} + +static int qcedev_map_buffer(struct qcedev_handle *qce_hndl, + struct qcedev_mem_client *mem_client, int fd, + unsigned int fd_size, struct qcedev_reg_buf_info *binfo) +{ + int rc = -1; + + switch (mem_client->mtype) { + case MEM_ION: + rc = ion_map_buffer(qce_hndl, mem_client, fd, fd_size, binfo); + break; + default: + pr_err("%s: err: Mem type not supported\n", __func__); + break; + } + + if (rc) + pr_err("%s: err: failed to map buffer\n", __func__); + + return rc; +} + +static int qcedev_unmap_buffer(struct qcedev_handle *qce_hndl, + struct qcedev_mem_client *mem_client, + struct qcedev_reg_buf_info *binfo) +{ + int rc = -1; + + switch (mem_client->mtype) { + case MEM_ION: + rc = ion_unmap_buffer(qce_hndl, binfo); + break; + default: + pr_err("%s: err: Mem type not supported\n", __func__); + break; + } + + if (rc) + pr_err("%s: err: failed to unmap buffer\n", __func__); + + return rc; +} + +int qcedev_check_and_map_buffer(void *handle, + int fd, unsigned int offset, unsigned int fd_size, + unsigned long long *vaddr) +{ + bool found = false; + struct qcedev_reg_buf_info *binfo = NULL, *temp = NULL; + struct qcedev_mem_client *mem_client = NULL; + struct qcedev_handle *qce_hndl = handle; + int rc = 0; + unsigned long mapped_size = 0; + + if (!handle || !vaddr || fd < 0 || offset >= fd_size) { + pr_err("%s: err: invalid input arguments\n", __func__); + return -EINVAL; + } + + if (!qce_hndl->cntl || !qce_hndl->cntl->mem_client) { + pr_err("%s: err: invalid qcedev handle\n", __func__); + return -EINVAL; + } + mem_client = qce_hndl->cntl->mem_client; + + if (mem_client->mtype != MEM_ION) + return -EPERM; + + /* Check if the buffer fd is already mapped */ + mutex_lock(&qce_hndl->registeredbufs.lock); + list_for_each_entry(temp, &qce_hndl->registeredbufs.list, list) { + if (temp->ion_buf.ion_fd == fd) { + found = true; + *vaddr = temp->ion_buf.iova; + mapped_size = temp->ion_buf.mapped_buf_size; + atomic_inc(&temp->ref_count); + break; + } + } + mutex_unlock(&qce_hndl->registeredbufs.lock); + + /* If buffer fd is not mapped then create a fresh mapping */ + if (!found) { + pr_debug("%s: info: ion fd not registered with driver\n", + __func__); + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + pr_err("%s: err: failed to allocate binfo\n", + __func__); + rc = -ENOMEM; + goto error; + } + rc = qcedev_map_buffer(qce_hndl, mem_client, fd, + fd_size, binfo); + if (rc) { + pr_err("%s: err: failed to map fd (%d) error = %d\n", + __func__, fd, rc); + goto error; + } + + *vaddr = binfo->ion_buf.iova; + mapped_size = binfo->ion_buf.mapped_buf_size; + atomic_inc(&binfo->ref_count); + + /* Add buffer mapping information to regd buffer list */ + mutex_lock(&qce_hndl->registeredbufs.lock); + list_add_tail(&binfo->list, &qce_hndl->registeredbufs.list); + mutex_unlock(&qce_hndl->registeredbufs.lock); + } + + /* Make sure the offset is within the mapped range */ + if (offset >= mapped_size) { + pr_err( + "%s: err: Offset (%u) exceeds mapped size(%lu) for fd: %d\n", + __func__, offset, mapped_size, fd); + rc = -ERANGE; + goto unmap; + } + + /* return the mapped virtual address adjusted by offset */ + *vaddr += offset; + + return 0; + +unmap: + if (!found) + qcedev_unmap_buffer(handle, mem_client, binfo); + +error: + kfree(binfo); + return rc; +} + +int qcedev_check_and_unmap_buffer(void *handle, int fd) +{ + struct qcedev_reg_buf_info *binfo = NULL, *dummy = NULL; + struct qcedev_mem_client *mem_client = NULL; + struct qcedev_handle *qce_hndl = handle; + bool found = false; + + if (!handle || fd < 0) { + pr_err("%s: err: invalid input arguments\n", __func__); + return -EINVAL; + } + + if (!qce_hndl->cntl || !qce_hndl->cntl->mem_client) { + pr_err("%s: err: invalid qcedev handle\n", __func__); + return -EINVAL; + } + mem_client = qce_hndl->cntl->mem_client; + + if (mem_client->mtype != MEM_ION) + return -EPERM; + + /* Check if the buffer fd is mapped and present in the regd list. */ + mutex_lock(&qce_hndl->registeredbufs.lock); + list_for_each_entry_safe(binfo, dummy, + &qce_hndl->registeredbufs.list, list) { + if (binfo->ion_buf.ion_fd == fd) { + found = true; + atomic_dec(&binfo->ref_count); + + /* Unmap only if there are no more references */ + if (atomic_read(&binfo->ref_count) == 0) { + qcedev_unmap_buffer(qce_hndl, + mem_client, binfo); + list_del(&binfo->list); + kfree(binfo); + } + break; + } + } + mutex_unlock(&qce_hndl->registeredbufs.lock); + + if (!found) { + pr_err("%s: err: calling unmap on unknown fd %d\n", + __func__, fd); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/crypto/msm/qcedev_smmu.h b/drivers/crypto/msm/qcedev_smmu.h new file mode 100644 index 0000000000000000000000000000000000000000..d99ed55da9a9423b8113709d0b7d5b8e5d0b6b27 --- /dev/null +++ b/drivers/crypto/msm/qcedev_smmu.h @@ -0,0 +1,88 @@ +/* Qti (or) Qualcomm Technologies Inc CE device driver. + * + * Copyright (c) 2018, 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 _DRIVERS_CRYPTO_PARSE_H_ +#define _DRIVERS_CRYPTO_PARSE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct context_bank_info { + struct list_head list; + const char *name; + u32 buffer_type; + u32 start_addr; + u32 size; + bool is_secure; + struct device *dev; + struct dma_iommu_mapping *mapping; +}; + +enum qcedev_mem_type { + MEM_ION, +}; + +struct qcedev_mem_client { + enum qcedev_mem_type mtype; +}; + +struct dma_mapping_info { + struct device *dev; + struct dma_iommu_mapping *mapping; + struct sg_table *table; + struct dma_buf_attachment *attach; + struct dma_buf *buf; +}; + +struct qcedev_ion_buf_info { + struct dma_mapping_info mapping_info; + dma_addr_t iova; + unsigned long mapped_buf_size; + int ion_fd; +}; + +struct qcedev_reg_buf_info { + struct list_head list; + union { + struct qcedev_ion_buf_info ion_buf; + }; + atomic_t ref_count; +}; + +struct qcedev_buffer_list { + struct list_head list; + struct mutex lock; +}; + +int qcedev_parse_context_bank(struct platform_device *pdev); +struct qcedev_mem_client *qcedev_mem_new_client(enum qcedev_mem_type mtype); +void qcedev_mem_delete_client(struct qcedev_mem_client *mem_client); +int qcedev_check_and_map_buffer(void *qce_hndl, + int fd, unsigned int offset, unsigned int fd_size, + unsigned long long *vaddr); +int qcedev_check_and_unmap_buffer(void *handle, int fd); + +extern struct qcedev_reg_buf_info *global_binfo_in; +extern struct qcedev_reg_buf_info *global_binfo_out; +extern struct qcedev_reg_buf_info *global_binfo_res; +#endif + diff --git a/drivers/crypto/msm/qcedevi.h b/drivers/crypto/msm/qcedevi.h index 05ea404e0c2720ce56d7d25a942a44da692b218c..8c4bd1c05da3a4dda5f8929f8160679dd6a56677 100644 --- a/drivers/crypto/msm/qcedevi.h +++ b/drivers/crypto/msm/qcedevi.h @@ -21,6 +21,7 @@ #include #include #include "qce.h" +#include "qcedev_smmu.h" #define CACHE_LINE_SIZE 32 #define CE_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE @@ -109,6 +110,8 @@ struct qcedev_control { struct qcedev_async_req *active_command; spinlock_t lock; struct tasklet_struct done_tasklet; + struct list_head context_banks; + struct qcedev_mem_client *mem_client; }; struct qcedev_handle { @@ -116,6 +119,8 @@ struct qcedev_handle { struct qcedev_control *cntl; /* qce internal sha context*/ struct qcedev_sha_ctxt sha_ctxt; + /* qcedev mapped buffer list */ + struct qcedev_buffer_list registeredbufs; }; void qcedev_cipher_req_cb(void *cookie, unsigned char *icv, diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 4d134bad6e50dd306e2c6425e464a97c9880a8df..c5666c870b335df3cd6320e99ecac1bf478c50ee 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -522,6 +522,7 @@ static void devfreq_dev_release(struct device *dev) devfreq->profile->exit(devfreq->dev.parent); mutex_destroy(&devfreq->lock); + mutex_destroy(&devfreq->sysfs_lock); kfree(devfreq); } @@ -564,6 +565,7 @@ struct devfreq *devfreq_add_device(struct device *dev, } mutex_init(&devfreq->lock); + mutex_init(&devfreq->sysfs_lock); mutex_lock(&devfreq->lock); devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; @@ -957,17 +959,19 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, if (df->governor == governor) { ret = 0; goto out; - } else if (df->governor->immutable || governor->immutable) { + } else if ((df->governor && df->governor->immutable) || + governor->immutable) { ret = -EINVAL; goto out; } + mutex_lock(&df->sysfs_lock); if (df->governor) { ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); if (ret) { dev_warn(dev, "%s: Governor %s not stopped(%d)\n", __func__, df->governor->name, ret); - goto out; + goto gov_stop_out; } } prev_gov = df->governor; @@ -985,6 +989,9 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, NULL); } } + +gov_stop_out: + mutex_unlock(&df->sysfs_lock); out: mutex_unlock(&devfreq_list_lock); @@ -1079,8 +1086,10 @@ static ssize_t polling_interval_store(struct device *dev, if (ret != 1) return -EINVAL; + mutex_lock(&df->sysfs_lock); df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value); ret = count; + mutex_unlock(&df->sysfs_lock); return ret; } @@ -1098,6 +1107,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; + mutex_lock(&df->sysfs_lock); mutex_lock(&df->lock); max = df->max_freq; if (value && max && value > max) { @@ -1110,6 +1120,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); + mutex_unlock(&df->sysfs_lock); return ret; } @@ -1125,6 +1136,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; + mutex_lock(&df->sysfs_lock); mutex_lock(&df->lock); min = df->min_freq; if (value && min && value < min) { @@ -1137,6 +1149,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); + mutex_unlock(&df->sysfs_lock); return ret; } diff --git a/drivers/devfreq/devfreq_devbw.c b/drivers/devfreq/devfreq_devbw.c index 5c7959c23ad252f7500ae355030179c53128371c..b17f4e21962e3229041a858c77a8abe943ca9e8b 100644 --- a/drivers/devfreq/devfreq_devbw.c +++ b/drivers/devfreq/devfreq_devbw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2018, 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 @@ -78,33 +78,15 @@ static int set_bw(struct device *dev, int new_ib, int new_ab) return ret; } -static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, - u32 flags) -{ - int i; - unsigned long atmost, atleast, f; - - atmost = p->freq_table[0]; - atleast = p->freq_table[p->max_state-1]; - for (i = 0; i < p->max_state; i++) { - f = p->freq_table[i]; - if (f <= *freq) - atmost = max(f, atmost); - if (f >= *freq) - atleast = min(f, atleast); - } - - if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) - *freq = atmost; - else - *freq = atleast; -} - static int devbw_target(struct device *dev, unsigned long *freq, u32 flags) { struct dev_data *d = dev_get_drvdata(dev); + struct dev_pm_opp *opp; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (!IS_ERR(opp)) + dev_pm_opp_put(opp); - find_freq(&d->dp, freq, flags); return set_bw(dev, *freq, d->gov_ab); } @@ -118,14 +100,13 @@ static int devbw_get_dev_status(struct device *dev, } #define PROP_PORTS "qcom,src-dst-ports" -#define PROP_TBL "qcom,bw-tbl" #define PROP_ACTIVE "qcom,active-only" int devfreq_add_devbw(struct device *dev) { struct dev_data *d; struct devfreq_dev_profile *p; - u32 *data, ports[MAX_PATHS * 2]; + u32 ports[MAX_PATHS * 2]; const char *gov_name; int ret, len, i, num_paths; @@ -174,27 +155,9 @@ int devfreq_add_devbw(struct device *dev) p->target = devbw_target; p->get_dev_status = devbw_get_dev_status; - if (of_find_property(dev->of_node, PROP_TBL, &len)) { - len /= sizeof(*data); - data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - p->freq_table = devm_kzalloc(dev, - len * sizeof(*p->freq_table), - GFP_KERNEL); - if (!p->freq_table) - return -ENOMEM; - - ret = of_property_read_u32_array(dev->of_node, PROP_TBL, - data, len); - if (ret) - return ret; - - for (i = 0; i < len; i++) - p->freq_table[i] = data[i]; - p->max_state = len; - } + ret = dev_pm_opp_of_add_table(dev); + if (ret) + dev_err(dev, "Couldn't parse OPP table:%d\n", ret); d->bus_client = msm_bus_scale_register_client(&d->bw_data); if (!d->bus_client) { diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c index 12a90d41bc37d0a01f3c9eb2799bb26fce15611a..184f33f0ef18b7306944db556a3c640b1dbb5e3e 100644 --- a/drivers/devfreq/governor_memlat.c +++ b/drivers/devfreq/governor_memlat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, 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 @@ -43,6 +43,7 @@ struct memlat_node { struct memlat_hwmon *hw; struct devfreq_governor *gov; struct attribute_group *attr_grp; + unsigned long resume_freq; }; static LIST_HEAD(memlat_list); @@ -212,6 +213,39 @@ static int gov_start(struct devfreq *df) return ret; } +static int gov_suspend(struct devfreq *df) +{ + struct memlat_node *node = df->data; + unsigned long prev_freq = df->previous_freq; + + node->mon_started = false; + devfreq_monitor_suspend(df); + + mutex_lock(&df->lock); + update_devfreq(df); + mutex_unlock(&df->lock); + + node->resume_freq = max(prev_freq, 1UL); + + return 0; +} + +static int gov_resume(struct devfreq *df) +{ + struct memlat_node *node = df->data; + + mutex_lock(&df->lock); + update_devfreq(df); + mutex_unlock(&df->lock); + + node->resume_freq = 0; + + devfreq_monitor_resume(df); + node->mon_started = true; + + return 0; +} + static void gov_stop(struct devfreq *df) { struct memlat_node *node = df->data; @@ -233,6 +267,18 @@ static int devfreq_memlat_get_freq(struct devfreq *df, unsigned long max_freq = 0; unsigned int ratio; + /* + * node->resume_freq is set to 0 at the end of resume (after the update) + * and is set to df->prev_freq at the end of suspend (after the update). + * This function will be called as part of the update_devfreq call in + * both scenarios. As a result, this block will cause a 0 vote during + * suspend and a vote for df->prev_freq during resume. + */ + if (!node->mon_started) { + *freq = node->resume_freq; + return 0; + } + hw->get_cnt(hw); for (i = 0; i < hw->num_cores; i++) { @@ -331,6 +377,30 @@ static int devfreq_memlat_ev_handler(struct devfreq *df, "Disabled Memory Latency governor\n"); break; + case DEVFREQ_GOV_SUSPEND: + ret = gov_suspend(df); + if (ret) { + dev_err(df->dev.parent, + "Unable to suspend memlat governor (%d)\n", + ret); + return ret; + } + + dev_dbg(df->dev.parent, "Suspended memlat governor\n"); + break; + + case DEVFREQ_GOV_RESUME: + ret = gov_resume(df); + if (ret) { + dev_err(df->dev.parent, + "Unable to resume memlat governor (%d)\n", + ret); + return ret; + } + + dev_dbg(df->dev.parent, "Resumed memlat governor\n"); + break; + case DEVFREQ_GOV_INTERVAL: sample_ms = *(unsigned int *)data; sample_ms = max(MIN_MS, sample_ms); diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index c00e3923d7d81154c7b0157491f270e90dd78fdf..94236ec9d4100fd6f1c0673f6c49e1cfd914d234 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1471,10 +1471,10 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) { check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; rmb(); - initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD); - rmb(); cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC); rmb(); + initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD); + rmb(); cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; rmb(); diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index e4fcfa84fbd3503246d144bcbcbb36747ed9171c..c70ea82c815c90966e1cf22c3781d16f5eed6ce6 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -50,7 +50,7 @@ int edac_mc_get_poll_msec(void) return edac_mc_poll_msec; } -static int edac_set_poll_msec(const char *val, struct kernel_param *kp) +static int edac_set_poll_msec(const char *val, const struct kernel_param *kp) { unsigned long l; int ret; diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 172598a27d7d9df8f83dcd37ae6bf0706df8d53a..32a931d0cb71ff1c262c97eb323c8894768ee423 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -19,7 +19,8 @@ #ifdef CONFIG_EDAC_DEBUG -static int edac_set_debug_level(const char *buf, struct kernel_param *kp) +static int edac_set_debug_level(const char *buf, + const struct kernel_param *kp) { unsigned long val; int ret; diff --git a/drivers/edac/kryo_arm64_edac.c b/drivers/edac/kryo_arm64_edac.c index 89cb0658be16d49911386b51860e7aeebfde327e..f7a1863bc7f1181215f450665cac4c6f66006840 100644 --- a/drivers/edac/kryo_arm64_edac.c +++ b/drivers/edac/kryo_arm64_edac.c @@ -46,8 +46,12 @@ module_param(poll_msec, int, 0444); #define L2_SILVER_BIT 0x1 #define L3_BIT 0x2 -#define L1_GOLD_DC_BIT 0x1 -#define L1_GOLD_IC_BIT 0x4 +#define QCOM_CPU_PART_KRYO4XX_GOLD 0x804 +#define QCOM_CPU_PART_KRYO4XX_SILVER_V1 0x803 +#define QCOM_CPU_PART_KRYO4XX_SILVER_V2 0x805 + +#define L1_GOLD_IC_BIT 0x1 +#define L1_GOLD_DC_BIT 0x4 #define L2_GOLD_BIT 0x8 #define L2_GOLD_TLB_BIT 0x2 @@ -258,8 +262,12 @@ static void kryo_parse_l1_l2_cache_error(u64 errxstatus, u64 errxmisc, struct edac_device_ctl_info *edev_ctl, int cpu) { int level = 0; + u32 part_num; - if (cpu <= 3) { + part_num = read_cpuid_part_number(); + switch (part_num) { + case QCOM_CPU_PART_KRYO4XX_SILVER_V1: + case QCOM_CPU_PART_KRYO4XX_SILVER_V2: switch (KRYO_ERRXMISC_LVL(errxmisc)) { case L1_SILVER_BIT: level = L1; @@ -267,8 +275,13 @@ static void kryo_parse_l1_l2_cache_error(u64 errxstatus, u64 errxmisc, case L2_SILVER_BIT: level = L2; break; + default: + edac_printk(KERN_CRIT, EDAC_CPU, + "silver cpu:%d unknown error location:%u\n", + cpu, KRYO_ERRXMISC_LVL(errxmisc)); } - } else { + break; + case QCOM_CPU_PART_KRYO4XX_GOLD: switch (KRYO_ERRXMISC_LVL_GOLD(errxmisc)) { case L1_GOLD_DC_BIT: case L1_GOLD_IC_BIT: @@ -278,8 +291,19 @@ static void kryo_parse_l1_l2_cache_error(u64 errxstatus, u64 errxmisc, case L2_GOLD_TLB_BIT: level = L2; break; + default: + edac_printk(KERN_CRIT, EDAC_CPU, + "gold cpu:%d unknown error location:%u\n", + cpu, KRYO_ERRXMISC_LVL_GOLD(errxmisc)); } + break; + default: + edac_printk(KERN_CRIT, EDAC_CPU, + "Error in matching cpu%d with part num:%u\n", + cpu, part_num); + return; } + switch (level) { case L1: if (KRYO_ERRXSTATUS_UE(errxstatus)) diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index a11a671c7a3866cb416d20eb4f302968028d7080..2ab4d61ee47e86d41a730302632ebb0a7cd4b4be 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -854,21 +854,24 @@ static void decode_mc6_mce(struct mce *m) static void decode_smca_error(struct mce *m) { struct smca_hwid *hwid; - unsigned int bank_type; + enum smca_bank_types bank_type; const char *ip_name; u8 xec = XEC(m->status, xec_mask); if (m->bank >= ARRAY_SIZE(smca_banks)) return; - if (x86_family(m->cpuid) >= 0x17 && m->bank == 4) - pr_emerg(HW_ERR "Bank 4 is reserved on Fam17h.\n"); - hwid = smca_banks[m->bank].hwid; if (!hwid) return; bank_type = hwid->bank_type; + + if (bank_type == SMCA_RESERVED) { + pr_emerg(HW_ERR "Bank %d is reserved.\n", m->bank); + return; + } + ip_name = smca_get_long_name(bank_type); pr_emerg(HW_ERR "%s Extended Error Code: %d\n", ip_name, xec); diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index ec5d695bbb7264d0fffd8d3d8eaf74a0d2a66905..3c68bb525d5da3aa9c41c512e14fde1acf7cf0cd 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -758,7 +758,7 @@ static int mv64x60_mc_err_probe(struct platform_device *pdev) /* Non-ECC RAM? */ printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); res = -ENODEV; - goto err2; + goto err; } edac_dbg(3, "init mci\n"); diff --git a/drivers/edac/qcom_llcc_edac.c b/drivers/edac/qcom_llcc_edac.c index 0b8f5369219d9c299d012b6883699a4824edf115..ef40c22043165ae7e2087fdeffb8d19713078413 100644 --- a/drivers/edac/qcom_llcc_edac.c +++ b/drivers/edac/qcom_llcc_edac.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -295,13 +295,14 @@ static void dump_syn_reg(struct edac_device_ctl_info *edev_ctl, errors[err_type].func(edev_ctl, 0, bank, errors[err_type].msg); } -static void qcom_llcc_check_cache_errors +static irqreturn_t qcom_llcc_check_cache_errors (struct edac_device_ctl_info *edev_ctl) { u32 drp_error; u32 trp_error; struct erp_drvdata *drv = edev_ctl->pvt_info; u32 i; + irqreturn_t irq_rc = IRQ_NONE; for (i = 0; i < drv->num_banks; i++) { /* Look for Data RAM errors */ @@ -312,10 +313,12 @@ static void qcom_llcc_check_cache_errors edac_printk(KERN_CRIT, EDAC_LLCC, "Single Bit Error detected in Data Ram\n"); dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i); + irq_rc = IRQ_HANDLED; } else if (drp_error & DB_ECC_ERROR) { edac_printk(KERN_CRIT, EDAC_LLCC, "Double Bit Error detected in Data Ram\n"); dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i); + irq_rc = IRQ_HANDLED; } /* Look for Tag RAM errors */ @@ -326,12 +329,16 @@ static void qcom_llcc_check_cache_errors edac_printk(KERN_CRIT, EDAC_LLCC, "Single Bit Error detected in Tag Ram\n"); dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i); + irq_rc = IRQ_HANDLED; } else if (trp_error & DB_ECC_ERROR) { edac_printk(KERN_CRIT, EDAC_LLCC, "Double Bit Error detected in Tag Ram\n"); dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i); + irq_rc = IRQ_HANDLED; } } + + return irq_rc; } #ifdef CONFIG_EDAC_LLCC_POLL @@ -344,8 +351,7 @@ static void qcom_llcc_poll_cache_errors(struct edac_device_ctl_info *edev_ctl) static irqreturn_t llcc_ecc_irq_handler (int irq, void *edev_ctl) { - qcom_llcc_check_cache_errors(edev_ctl); - return IRQ_HANDLED; + return qcom_llcc_check_cache_errors(edev_ctl); } static int qcom_llcc_erp_probe(struct platform_device *pdev) @@ -431,7 +437,8 @@ static int qcom_llcc_erp_probe(struct platform_device *pdev) } rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler, - IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl); + IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + "llcc_ecc", edev_ctl); if (rc) { dev_err(dev, "failed to request ecc irq\n"); goto out_dev; diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c index ee07f2d947b7977f10d52178960dda1a6549d9b2..f2e6f7af446f93f7715f1db595fe08cecc914d2f 100644 --- a/drivers/esoc/esoc-mdm-pon.c +++ b/drivers/esoc/esoc-mdm-pon.c @@ -57,13 +57,13 @@ static int sdx50m_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic) * Allow PS hold assert to be detected */ if (!atomic) - usleep_range(203000, 300000); + usleep_range(80000,180000); else /* * The flow falls through this path as a part of the * panic handler, which has to executed atomically. */ - mdelay(203); + mdelay(100); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), soft_reset_direction_de_assert); return 0; @@ -145,7 +145,7 @@ static int sdx50m_power_down(struct mdm_ctrl *mdm) * for the reset to fully take place. Sleep here to ensure the * reset has occurred before the function exits. */ - msleep(406); + msleep(300); return 0; } @@ -175,7 +175,7 @@ static void sdx50m_cold_reset(struct mdm_ctrl *mdm) * The function is executed as a part of the atomic reboot handler. * Hence, go with a busy loop instead of sleep. */ - mdelay(334); + mdelay(600); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !mdm->soft_reset_inverted); diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index 91a0023074af3a3962c75dd631176b2361c29117..60baaf6931032043f9b260919e970bf49ae895b9 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -66,6 +66,8 @@ #define CHT_WC_VBUS_GPIO_CTLO 0x6e2d #define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0) +#define CHT_WC_VBUS_GPIO_CTLO_DRV_OD BIT(4) +#define CHT_WC_VBUS_GPIO_CTLO_DIR_OUT BIT(5) enum cht_wc_usb_id { USB_ID_OTG, @@ -183,14 +185,15 @@ static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext, { int ret, val; - val = enable ? CHT_WC_VBUS_GPIO_CTLO_OUTPUT : 0; - /* * The 5V boost converter is enabled through a gpio on the PMIC, since * there currently is no gpio driver we access the gpio reg directly. */ - ret = regmap_update_bits(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, - CHT_WC_VBUS_GPIO_CTLO_OUTPUT, val); + val = CHT_WC_VBUS_GPIO_CTLO_DRV_OD | CHT_WC_VBUS_GPIO_CTLO_DIR_OUT; + if (enable) + val |= CHT_WC_VBUS_GPIO_CTLO_OUTPUT; + + ret = regmap_write(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, val); if (ret) dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret); } diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 9c925b05b7aaa1a479e9f2a49b0479233fd068be..85c6dee974cc05e073e3edcb6fcb0dc0b6033935 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -36,6 +36,7 @@ struct usb_extcon_info { struct gpio_desc *id_gpiod; struct gpio_desc *vbus_gpiod; + struct gpio_desc *vbus_out_gpiod; int id_irq; int vbus_irq; @@ -80,12 +81,17 @@ static void usb_extcon_detect_cable(struct work_struct *work) gpiod_get_value_cansleep(info->vbus_gpiod) : id; /* at first we clean states which are no longer active */ - if (id) + if (id) { + if (info->vbus_out_gpiod) + gpiod_set_value_cansleep(info->vbus_out_gpiod, 0); extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false); + } if (!vbus) extcon_set_state_sync(info->edev, EXTCON_USB, false); if (!id) { + if (info->vbus_out_gpiod) + gpiod_set_value_cansleep(info->vbus_out_gpiod, 1); extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true); } else { if (vbus) @@ -121,6 +127,8 @@ static int usb_extcon_probe(struct platform_device *pdev) info->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", GPIOD_IN); info->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus", GPIOD_IN); + info->vbus_out_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus-out", + GPIOD_OUT_HIGH); if (!info->id_gpiod && !info->vbus_gpiod) { dev_err(dev, "failed to get gpios\n"); @@ -133,6 +141,9 @@ static int usb_extcon_probe(struct platform_device *pdev) if (IS_ERR(info->vbus_gpiod)) return PTR_ERR(info->vbus_gpiod); + if (IS_ERR(info->vbus_out_gpiod)) + return PTR_ERR(info->vbus_out_gpiod); + info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable); if (IS_ERR(info->edev)) { dev_err(dev, "failed to allocate extcon device\n"); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 8bf89267dc252f260a3fc6e640c0c1809f04ef2d..d731b413cb2ce504c5cb895cdec0d91e4231fe01 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1130,7 +1130,13 @@ static int context_add_buffer(struct context *ctx) return -ENOMEM; offset = (void *)&desc->buffer - (void *)desc; - desc->buffer_size = PAGE_SIZE - offset; + /* + * Some controllers, like JMicron ones, always issue 0x20-byte DMA reads + * for descriptors, even 0x10-byte ones. This can cause page faults when + * an IOMMU is in use and the oversized read crosses a page boundary. + * Work around this by always leaving at least 0x10 bytes of padding. + */ + desc->buffer_size = PAGE_SIZE - offset - 0x10; desc->buffer_bus = bus_addr + offset; desc->used = 0; diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 783041964439acaa60c80eac80df24080579d036..e8db9659a36b2de1662416d901aad9f3352d873a 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -18,7 +18,7 @@ EXPORT_SYMBOL_GPL(dmi_kobj); * of and an antecedent to, SMBIOS, which stands for System * Management BIOS. See further: http://www.dmtf.org/standards */ -static const char dmi_empty_string[] = " "; +static const char dmi_empty_string[] = ""; static u32 dmi_ver __initdata; static u32 dmi_len; @@ -44,25 +44,21 @@ static int dmi_memdev_nr; static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s) { const u8 *bp = ((u8 *) dm) + dm->length; + const u8 *nsp; if (s) { - s--; - while (s > 0 && *bp) { + while (--s > 0 && *bp) bp += strlen(bp) + 1; - s--; - } - - if (*bp != 0) { - size_t len = strlen(bp)+1; - size_t cmp_len = len > 8 ? 8 : len; - if (!memcmp(bp, dmi_empty_string, cmp_len)) - return dmi_empty_string; + /* Strings containing only spaces are considered empty */ + nsp = bp; + while (*nsp == ' ') + nsp++; + if (*nsp != '\0') return bp; - } } - return ""; + return dmi_empty_string; } static const char * __init dmi_string(const struct dmi_header *dm, u8 s) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index c52d0484080c8467966effda48f723f69a7af9a4..8b30d72d779b6d0c6ed05535246acb9906e7c4af 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -23,7 +23,8 @@ cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \ -D__NO_FORTIFY \ $(call cc-option,-ffreestanding) \ - $(call cc-option,-fno-stack-protector) + $(call cc-option,-fno-stack-protector) \ + $(DISABLE_LTO) GCOV_PROFILE := n KASAN_SANITIZE := n diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index 0863996e38df8322c240221fa715982b91415868..5f4a81e7858fab11fa99ed6fed6ffc0cbd26cca2 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -268,8 +268,9 @@ static int __init psci_features(u32 psci_func_id) } #ifdef CONFIG_CPU_IDLE -static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); +static __maybe_unused DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); +#ifdef CONFIG_DT_IDLE_STATES static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) { int i, ret, count = 0; @@ -322,6 +323,10 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) kfree(psci_states); return ret; } +#else +static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) +{ return 0; } +#endif #ifdef CONFIG_ACPI #include diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c index 14f14efdf0d53ca23c24d7c514cdd4c4ad4b8174..06d212a3d49dd6ebdba471aa1814759ff828d1aa 100644 --- a/drivers/fpga/altera-ps-spi.c +++ b/drivers/fpga/altera-ps-spi.c @@ -249,7 +249,7 @@ static int altera_ps_probe(struct spi_device *spi) conf->data = of_id->data; conf->spi = spi; - conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_HIGH); + conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_LOW); if (IS_ERR(conf->config)) { dev_err(&spi->dev, "Failed to get config gpio: %ld\n", PTR_ERR(conf->config)); diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 57efb251f9c462ceb6a4b5015f3658707fa119d0..10523ce00c387e2ffaf5c311452dedd4cdd4120c 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -566,8 +566,10 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, 0, 0, of_node_to_fwnode(dev->of_node), &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) + if (!txgpio->irqd) { + err = -ENOMEM; goto out; + } /* Push on irq_data and the domain for each line. */ for (i = 0; i < ngpio; i++) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a39a537e2c6f7b3359f58bbd9a0b07428b8885e6..bf7a9ba0d461bdfa103cac514eee50e2b23bdf33 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3323,7 +3323,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, return desc; } - status = gpiod_request(desc, con_id); + /* If a connection label was passed use that, else use the device name as label */ + status = gpiod_request(desc, con_id ? con_id : dev_name(dev)); if (status < 0) return ERR_PTR(status); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c index c53095b3b0fb9754bd248113eee2877130c9e066..1ae5ae8c45a45e20b21d42fffcb9e6c6d9ceaf4e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c @@ -569,6 +569,7 @@ static const struct amdgpu_px_quirk amdgpu_px_quirk_list[] = { { 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX }, + { 0x1002, 0x67DF, 0x1028, 0x0774, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0, 0, 0, 0, 0 }, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index 59089e027f4d8ac386f1174fe47eea5622ec182d..92be7f6de197372beb1a12a0ad1816bd62f738a1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -233,8 +233,10 @@ void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, for (i = 0; i < list->num_entries; i++) { unsigned priority = list->array[i].priority; - list_add_tail(&list->array[i].tv.head, - &bucket[priority]); + if (!list->array[i].robj->parent) + list_add_tail(&list->array[i].tv.head, + &bucket[priority]); + list->array[i].user_pages = NULL; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index b5aa8e6f8e0beb9e0af1ad6a4fcce9eec6453bc1..5f892ad6476edcccec4c4e7cb9b05d9b0db303c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -522,7 +522,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, INIT_LIST_HEAD(&duplicates); amdgpu_vm_get_pd_bo(&fpriv->vm, &p->validated, &p->vm_pd); - if (p->uf_entry.robj) + if (p->uf_entry.robj && !p->uf_entry.robj->parent) list_add(&p->uf_entry.tv.head, &p->validated); if (need_mmap_lock) diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index f508f4d01e4a9000f633c85e290964098e8c1b86..11beef7c595f2dc7a67ab97da6064ae00c2ca90e 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -866,7 +866,7 @@ static void cik_sdma_ring_emit_pipeline_sync(struct amdgpu_ring *ring) amdgpu_ring_write(ring, addr & 0xfffffffc); amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff); amdgpu_ring_write(ring, seq); /* reference */ - amdgpu_ring_write(ring, 0xfffffff); /* mask */ + amdgpu_ring_write(ring, 0xffffffff); /* mask */ amdgpu_ring_write(ring, (0xfff << 16) | 4); /* retry count, poll interval */ } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index fc260c13b1da4938443a972aecea21c2250d7e40..a7e54820a330d70fccf53584722f603fd07ea20c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -1398,10 +1398,11 @@ static const u32 sgpr_init_compute_shader[] = static const u32 vgpr_init_regs[] = { mmCOMPUTE_STATIC_THREAD_MGMT_SE0, 0xffffffff, - mmCOMPUTE_RESOURCE_LIMITS, 0, + mmCOMPUTE_RESOURCE_LIMITS, 0x1000000, /* CU_GROUP_COUNT=1 */ mmCOMPUTE_NUM_THREAD_X, 256*4, mmCOMPUTE_NUM_THREAD_Y, 1, mmCOMPUTE_NUM_THREAD_Z, 1, + mmCOMPUTE_PGM_RSRC1, 0x100004f, /* VGPRS=15 (64 logical VGPRs), SGPRS=1 (16 SGPRs), BULKY=1 */ mmCOMPUTE_PGM_RSRC2, 20, mmCOMPUTE_USER_DATA_0, 0xedcedc00, mmCOMPUTE_USER_DATA_1, 0xedcedc01, @@ -1418,10 +1419,11 @@ static const u32 vgpr_init_regs[] = static const u32 sgpr1_init_regs[] = { mmCOMPUTE_STATIC_THREAD_MGMT_SE0, 0x0f, - mmCOMPUTE_RESOURCE_LIMITS, 0x1000000, + mmCOMPUTE_RESOURCE_LIMITS, 0x1000000, /* CU_GROUP_COUNT=1 */ mmCOMPUTE_NUM_THREAD_X, 256*5, mmCOMPUTE_NUM_THREAD_Y, 1, mmCOMPUTE_NUM_THREAD_Z, 1, + mmCOMPUTE_PGM_RSRC1, 0x240, /* SGPRS=9 (80 GPRS) */ mmCOMPUTE_PGM_RSRC2, 20, mmCOMPUTE_USER_DATA_0, 0xedcedc00, mmCOMPUTE_USER_DATA_1, 0xedcedc01, @@ -1442,6 +1444,7 @@ static const u32 sgpr2_init_regs[] = mmCOMPUTE_NUM_THREAD_X, 256*5, mmCOMPUTE_NUM_THREAD_Y, 1, mmCOMPUTE_NUM_THREAD_Z, 1, + mmCOMPUTE_PGM_RSRC1, 0x240, /* SGPRS=9 (80 GPRS) */ mmCOMPUTE_PGM_RSRC2, 20, mmCOMPUTE_USER_DATA_0, 0xedcedc00, mmCOMPUTE_USER_DATA_1, 0xedcedc01, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index f2d0710258cb272552ed48f41d72ffb13d355f3d..9928473234a67b7b45ec3d4b02dd0f102455464e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -856,7 +856,7 @@ static void sdma_v2_4_ring_emit_pipeline_sync(struct amdgpu_ring *ring) amdgpu_ring_write(ring, addr & 0xfffffffc); amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff); amdgpu_ring_write(ring, seq); /* reference */ - amdgpu_ring_write(ring, 0xfffffff); /* mask */ + amdgpu_ring_write(ring, 0xffffffff); /* mask */ amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) | SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */ } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index b1de44f2282490189f0c4024d3d75711512547fe..f5db1fad3f05dd6907c8145b8e395f7dea37a45b 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -1099,7 +1099,7 @@ static void sdma_v3_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring) amdgpu_ring_write(ring, addr & 0xfffffffc); amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff); amdgpu_ring_write(ring, seq); /* reference */ - amdgpu_ring_write(ring, 0xfffffff); /* mask */ + amdgpu_ring_write(ring, 0xffffffff); /* mask */ amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) | SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */ } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index fd7c72aaafa6248a566996140128a1fc22569279..4e5fed7c66bf9f7f2d3cd2423bba5bca405cc73c 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -1136,7 +1136,7 @@ static void sdma_v4_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring) amdgpu_ring_write(ring, addr & 0xfffffffc); amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff); amdgpu_ring_write(ring, seq); /* reference */ - amdgpu_ring_write(ring, 0xfffffff); /* mask */ + amdgpu_ring_write(ring, 0xffffffff); /* mask */ amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) | SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */ } diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c index 4c178feeb4bd1bf90ff4790986b6e06dc3c9210b..40520a968eaca1a5d6d5a211a3b2e5580541ac36 100644 --- a/drivers/gpu/drm/amd/amdgpu/si.c +++ b/drivers/gpu/drm/amd/amdgpu/si.c @@ -1231,6 +1231,71 @@ static void si_detect_hw_virtualization(struct amdgpu_device *adev) adev->virt.caps |= AMDGPU_PASSTHROUGH_MODE; } +static int si_get_pcie_lanes(struct amdgpu_device *adev) +{ + u32 link_width_cntl; + + if (adev->flags & AMD_IS_APU) + return 0; + + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); + + switch ((link_width_cntl & LC_LINK_WIDTH_RD_MASK) >> LC_LINK_WIDTH_RD_SHIFT) { + case LC_LINK_WIDTH_X1: + return 1; + case LC_LINK_WIDTH_X2: + return 2; + case LC_LINK_WIDTH_X4: + return 4; + case LC_LINK_WIDTH_X8: + return 8; + case LC_LINK_WIDTH_X0: + case LC_LINK_WIDTH_X16: + default: + return 16; + } +} + +static void si_set_pcie_lanes(struct amdgpu_device *adev, int lanes) +{ + u32 link_width_cntl, mask; + + if (adev->flags & AMD_IS_APU) + return; + + switch (lanes) { + case 0: + mask = LC_LINK_WIDTH_X0; + break; + case 1: + mask = LC_LINK_WIDTH_X1; + break; + case 2: + mask = LC_LINK_WIDTH_X2; + break; + case 4: + mask = LC_LINK_WIDTH_X4; + break; + case 8: + mask = LC_LINK_WIDTH_X8; + break; + case 16: + mask = LC_LINK_WIDTH_X16; + break; + default: + DRM_ERROR("invalid pcie lane request: %d\n", lanes); + return; + } + + link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); + link_width_cntl &= ~LC_LINK_WIDTH_MASK; + link_width_cntl |= mask << LC_LINK_WIDTH_SHIFT; + link_width_cntl |= (LC_RECONFIG_NOW | + LC_RECONFIG_ARC_MISSING_ESCAPE); + + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); +} + static const struct amdgpu_asic_funcs si_asic_funcs = { .read_disabled_bios = &si_read_disabled_bios, @@ -1241,6 +1306,8 @@ static const struct amdgpu_asic_funcs si_asic_funcs = .get_xclk = &si_get_xclk, .set_uvd_clocks = &si_set_uvd_clocks, .set_vce_clocks = NULL, + .get_pcie_lanes = &si_get_pcie_lanes, + .set_pcie_lanes = &si_set_pcie_lanes, .get_config_memsize = &si_get_config_memsize, }; diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c index abb0a2341a41c5dcb38a5f34301e9f7564eb5964..6f1dea157a7753b70570e4a9d3d7064f1c4aeaa0 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c @@ -6374,9 +6374,9 @@ static void si_set_pcie_lane_width_in_smc(struct amdgpu_device *adev, { u32 lane_width; u32 new_lane_width = - (amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT; + ((amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1; u32 current_lane_width = - (amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT; + ((amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1; if (new_lane_width != current_lane_width) { amdgpu_set_pcie_lanes(adev, new_lane_width); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index bf14214fa4640279fa46b655333198ed5aa1446e..4db31b89507c5f6e5185583725c5bb3a1f641f28 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -1634,6 +1634,8 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) * (and possibly on the platform). So far only i.MX6Q (v1.30a) and * i.MX6DL (v1.31a) have been identified as needing the workaround, with * 4 and 1 iterations respectively. + * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing + * the workaround with a single iteration. */ switch (hdmi->version) { @@ -1641,6 +1643,7 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) count = 4; break; case 0x131a: + case 0x201a: count = 1; break; default: diff --git a/drivers/gpu/drm/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/drm_dp_dual_mode_helper.c index 02a50929af6759ab7769bba100134b2e6f4b0713..e7f4fe2848a542c3e45b55a8c92b258273cd71c8 100644 --- a/drivers/gpu/drm/drm_dp_dual_mode_helper.c +++ b/drivers/gpu/drm/drm_dp_dual_mode_helper.c @@ -350,19 +350,44 @@ int drm_dp_dual_mode_set_tmds_output(enum drm_dp_dual_mode_type type, { uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE; ssize_t ret; + int retry; if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) return 0; - ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN, - &tmds_oen, sizeof(tmds_oen)); - if (ret) { - DRM_DEBUG_KMS("Failed to %s TMDS output buffers\n", - enable ? "enable" : "disable"); - return ret; + /* + * LSPCON adapters in low-power state may ignore the first write, so + * read back and verify the written value a few times. + */ + for (retry = 0; retry < 3; retry++) { + uint8_t tmp; + + ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN, + &tmds_oen, sizeof(tmds_oen)); + if (ret) { + DRM_DEBUG_KMS("Failed to %s TMDS output buffers (%d attempts)\n", + enable ? "enable" : "disable", + retry + 1); + return ret; + } + + ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN, + &tmp, sizeof(tmp)); + if (ret) { + DRM_DEBUG_KMS("I2C read failed during TMDS output buffer %s (%d attempts)\n", + enable ? "enabling" : "disabling", + retry + 1); + return ret; + } + + if (tmp == tmds_oen) + return 0; } - return 0; + DRM_DEBUG_KMS("I2C write value mismatch during TMDS output buffer %s\n", + enable ? "enabling" : "disabling"); + + return -EIO; } EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output); diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c index 83e88c70272a5b0dd3b60134c83b44752dbd1a48..9bf4045cd679ab6598494e13440e3b4f23d60c78 100644 --- a/drivers/gpu/drm/i915/gvt/kvmgt.c +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c @@ -1153,7 +1153,7 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd, return 0; } - return 0; + return -ENOTTY; } static ssize_t diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3f818412765c859612e741f36909f48f7c5bbf9c..51411894d2cd584d5ad583331ea813c0d9eff74d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3995,7 +3995,11 @@ extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e, struct intel_display_error_state *error); int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val); -int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u32 mbox, u32 val); +int sandybridge_pcode_write_timeout(struct drm_i915_private *dev_priv, u32 mbox, + u32 val, int timeout_us); +#define sandybridge_pcode_write(dev_priv, mbox, val) \ + sandybridge_pcode_write_timeout(dev_priv, mbox, val, 500) + int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request, u32 reply_mask, u32 reply, int timeout_base_ms); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index de8ca5f1dd2e67ee544b8cd9780e856901917f0f..4cc9ce4b5b1648315db1ce5f157bb442bf54d8a7 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -722,7 +722,7 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb) err = radix_tree_insert(handles_vma, handle, vma); if (unlikely(err)) { - kfree(lut); + kmem_cache_free(eb->i915->luts, lut); goto err_obj; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 920c8914cec173ac8831627a73d217cc04c11513..cc70e24702721375fe611b3fee1597314113fce4 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -6159,6 +6159,12 @@ enum { #define _SPATILEOFF (VLV_DISPLAY_BASE + 0x721a4) #define _SPACONSTALPHA (VLV_DISPLAY_BASE + 0x721a8) #define SP_CONST_ALPHA_ENABLE (1<<31) +#define _SPACLRC0 (VLV_DISPLAY_BASE + 0x721d0) +#define SP_CONTRAST(x) ((x) << 18) /* u3.6 */ +#define SP_BRIGHTNESS(x) ((x) & 0xff) /* s8 */ +#define _SPACLRC1 (VLV_DISPLAY_BASE + 0x721d4) +#define SP_SH_SIN(x) (((x) & 0x7ff) << 16) /* s4.7 */ +#define SP_SH_COS(x) (x) /* u3.7 */ #define _SPAGAMC (VLV_DISPLAY_BASE + 0x721f4) #define _SPBCNTR (VLV_DISPLAY_BASE + 0x72280) @@ -6172,6 +6178,8 @@ enum { #define _SPBKEYMAXVAL (VLV_DISPLAY_BASE + 0x722a0) #define _SPBTILEOFF (VLV_DISPLAY_BASE + 0x722a4) #define _SPBCONSTALPHA (VLV_DISPLAY_BASE + 0x722a8) +#define _SPBCLRC0 (VLV_DISPLAY_BASE + 0x722d0) +#define _SPBCLRC1 (VLV_DISPLAY_BASE + 0x722d4) #define _SPBGAMC (VLV_DISPLAY_BASE + 0x722f4) #define _MMIO_VLV_SPR(pipe, plane_id, reg_a, reg_b) \ @@ -6188,6 +6196,8 @@ enum { #define SPKEYMAXVAL(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPAKEYMAXVAL, _SPBKEYMAXVAL) #define SPTILEOFF(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPATILEOFF, _SPBTILEOFF) #define SPCONSTALPHA(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPACONSTALPHA, _SPBCONSTALPHA) +#define SPCLRC0(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPACLRC0, _SPBCLRC0) +#define SPCLRC1(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPACLRC1, _SPBCLRC1) #define SPGAMC(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPAGAMC, _SPBGAMC) /* diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 27743be5b768e13c4be9749537b1af76dfb3f478..9240fa79de7ca6a595367e28e8b13e65dff50b04 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -704,7 +704,7 @@ static void i915_audio_component_codec_wake_override(struct device *kdev, struct drm_i915_private *dev_priv = kdev_to_i915(kdev); u32 tmp; - if (!IS_GEN9_BC(dev_priv)) + if (!IS_GEN9(dev_priv)) return; i915_audio_component_get_power(kdev); diff --git a/drivers/gpu/drm/i915/intel_cdclk.c b/drivers/gpu/drm/i915/intel_cdclk.c index 26a8dcd2c54942bccb080c54bbda987f29da83e3..47ad24229c7880a791cbc2105afcb77580c94746 100644 --- a/drivers/gpu/drm/i915/intel_cdclk.c +++ b/drivers/gpu/drm/i915/intel_cdclk.c @@ -1289,10 +1289,15 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv, break; } - /* Inform power controller of upcoming frequency change */ mutex_lock(&dev_priv->rps.hw_lock); - ret = sandybridge_pcode_write(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ, - 0x80000000); + /* + * Inform power controller of upcoming frequency change. BSpec + * requires us to wait up to 150usec, but that leads to timeouts; + * the 2ms used here is based on experiment. + */ + ret = sandybridge_pcode_write_timeout(dev_priv, + HSW_PCODE_DE_WRITE_FREQ_REQ, + 0x80000000, 2000); mutex_unlock(&dev_priv->rps.hw_lock); if (ret) { @@ -1323,8 +1328,15 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv, I915_WRITE(CDCLK_CTL, val); mutex_lock(&dev_priv->rps.hw_lock); - ret = sandybridge_pcode_write(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ, - DIV_ROUND_UP(cdclk, 25000)); + /* + * The timeout isn't specified, the 2ms used here is based on + * experiment. + * FIXME: Waiting for the request completion could be delayed until + * the next PCODE request based on BSpec. + */ + ret = sandybridge_pcode_write_timeout(dev_priv, + HSW_PCODE_DE_WRITE_FREQ_REQ, + DIV_ROUND_UP(cdclk, 25000), 2000); mutex_unlock(&dev_priv->rps.hw_lock); if (ret) { diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 014e5c08571a4d4eebe3fd050428c115521c8524..87cccb5f8c5da5ec6d493ba53968c3f4c0a2980c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -8941,8 +8941,8 @@ int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val return 0; } -int sandybridge_pcode_write(struct drm_i915_private *dev_priv, - u32 mbox, u32 val) +int sandybridge_pcode_write_timeout(struct drm_i915_private *dev_priv, + u32 mbox, u32 val, int timeout_us) { int status; @@ -8965,7 +8965,7 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, if (__intel_wait_for_register_fw(dev_priv, GEN6_PCODE_MAILBOX, GEN6_PCODE_READY, 0, - 500, 0, NULL)) { + timeout_us, 0, NULL)) { DRM_ERROR("timeout waiting for pcode write of 0x%08x to mbox %x to finish for %ps\n", val, mbox, __builtin_return_address(0)); return -ETIMEDOUT; diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index bcccacba1ec68d00faff67f3fc3c41de3ed06a25..bcfc1c235966665e3f30a48eeecb419a760c06ea 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -622,19 +622,18 @@ void skl_enable_dc6(struct drm_i915_private *dev_priv) DRM_DEBUG_KMS("Enabling DC6\n"); - gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); + /* Wa Display #1183: skl,kbl,cfl */ + if (IS_GEN9_BC(dev_priv)) + I915_WRITE(GEN8_CHICKEN_DCPR_1, I915_READ(GEN8_CHICKEN_DCPR_1) | + SKL_SELECT_ALTERNATE_DC_EXIT); + gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); } void skl_disable_dc6(struct drm_i915_private *dev_priv) { DRM_DEBUG_KMS("Disabling DC6\n"); - /* Wa Display #1183: skl,kbl,cfl */ - if (IS_GEN9_BC(dev_priv)) - I915_WRITE(GEN8_CHICKEN_DCPR_1, I915_READ(GEN8_CHICKEN_DCPR_1) | - SKL_SELECT_ALTERNATE_DC_EXIT); - gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index f8ebeb5ffb9608a85ea4c808d339076e0b1afa62..41e31a4546046f74e20d3bcd3a653b3cd0289833 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -345,44 +345,87 @@ skl_plane_get_hw_state(struct intel_plane *plane) } static void -chv_update_csc(struct intel_plane *plane, uint32_t format) +chv_update_csc(const struct intel_plane_state *plane_state) { + struct intel_plane *plane = to_intel_plane(plane_state->base.plane); struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + const struct drm_framebuffer *fb = plane_state->base.fb; enum plane_id plane_id = plane->id; /* Seems RGB data bypasses the CSC always */ - if (!format_is_yuv(format)) + if (!format_is_yuv(fb->format->format)) return; /* - * BT.601 limited range YCbCr -> full range RGB + * BT.601 full range YCbCr -> full range RGB * - * |r| | 6537 4769 0| |cr | - * |g| = |-3330 4769 -1605| x |y-64| - * |b| | 0 4769 8263| |cb | + * |r| | 5743 4096 0| |cr| + * |g| = |-2925 4096 -1410| x |y | + * |b| | 0 4096 7258| |cb| * - * Cb and Cr apparently come in as signed already, so no - * need for any offset. For Y we need to remove the offset. + * Cb and Cr apparently come in as signed already, + * and we get full range data in on account of CLRC0/1 */ - I915_WRITE_FW(SPCSCYGOFF(plane_id), SPCSC_OOFF(0) | SPCSC_IOFF(-64)); + I915_WRITE_FW(SPCSCYGOFF(plane_id), SPCSC_OOFF(0) | SPCSC_IOFF(0)); I915_WRITE_FW(SPCSCCBOFF(plane_id), SPCSC_OOFF(0) | SPCSC_IOFF(0)); I915_WRITE_FW(SPCSCCROFF(plane_id), SPCSC_OOFF(0) | SPCSC_IOFF(0)); - I915_WRITE_FW(SPCSCC01(plane_id), SPCSC_C1(4769) | SPCSC_C0(6537)); - I915_WRITE_FW(SPCSCC23(plane_id), SPCSC_C1(-3330) | SPCSC_C0(0)); - I915_WRITE_FW(SPCSCC45(plane_id), SPCSC_C1(-1605) | SPCSC_C0(4769)); - I915_WRITE_FW(SPCSCC67(plane_id), SPCSC_C1(4769) | SPCSC_C0(0)); - I915_WRITE_FW(SPCSCC8(plane_id), SPCSC_C0(8263)); + I915_WRITE_FW(SPCSCC01(plane_id), SPCSC_C1(4096) | SPCSC_C0(5743)); + I915_WRITE_FW(SPCSCC23(plane_id), SPCSC_C1(-2925) | SPCSC_C0(0)); + I915_WRITE_FW(SPCSCC45(plane_id), SPCSC_C1(-1410) | SPCSC_C0(4096)); + I915_WRITE_FW(SPCSCC67(plane_id), SPCSC_C1(4096) | SPCSC_C0(0)); + I915_WRITE_FW(SPCSCC8(plane_id), SPCSC_C0(7258)); - I915_WRITE_FW(SPCSCYGICLAMP(plane_id), SPCSC_IMAX(940) | SPCSC_IMIN(64)); - I915_WRITE_FW(SPCSCCBICLAMP(plane_id), SPCSC_IMAX(448) | SPCSC_IMIN(-448)); - I915_WRITE_FW(SPCSCCRICLAMP(plane_id), SPCSC_IMAX(448) | SPCSC_IMIN(-448)); + I915_WRITE_FW(SPCSCYGICLAMP(plane_id), SPCSC_IMAX(1023) | SPCSC_IMIN(0)); + I915_WRITE_FW(SPCSCCBICLAMP(plane_id), SPCSC_IMAX(512) | SPCSC_IMIN(-512)); + I915_WRITE_FW(SPCSCCRICLAMP(plane_id), SPCSC_IMAX(512) | SPCSC_IMIN(-512)); I915_WRITE_FW(SPCSCYGOCLAMP(plane_id), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); I915_WRITE_FW(SPCSCCBOCLAMP(plane_id), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); I915_WRITE_FW(SPCSCCROCLAMP(plane_id), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); } +#define SIN_0 0 +#define COS_0 1 + +static void +vlv_update_clrc(const struct intel_plane_state *plane_state) +{ + struct intel_plane *plane = to_intel_plane(plane_state->base.plane); + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + const struct drm_framebuffer *fb = plane_state->base.fb; + enum pipe pipe = plane->pipe; + enum plane_id plane_id = plane->id; + int contrast, brightness, sh_scale, sh_sin, sh_cos; + + if (format_is_yuv(fb->format->format)) { + /* + * Expand limited range to full range: + * Contrast is applied first and is used to expand Y range. + * Brightness is applied second and is used to remove the + * offset from Y. Saturation/hue is used to expand CbCr range. + */ + contrast = DIV_ROUND_CLOSEST(255 << 6, 235 - 16); + brightness = -DIV_ROUND_CLOSEST(16 * 255, 235 - 16); + sh_scale = DIV_ROUND_CLOSEST(128 << 7, 240 - 128); + sh_sin = SIN_0 * sh_scale; + sh_cos = COS_0 * sh_scale; + } else { + /* Pass-through everything. */ + contrast = 1 << 6; + brightness = 0; + sh_scale = 1 << 7; + sh_sin = SIN_0 * sh_scale; + sh_cos = COS_0 * sh_scale; + } + + /* FIXME these register are single buffered :( */ + I915_WRITE_FW(SPCLRC0(pipe, plane_id), + SP_CONTRAST(contrast) | SP_BRIGHTNESS(brightness)); + I915_WRITE_FW(SPCLRC1(pipe, plane_id), + SP_SH_SIN(sh_sin) | SP_SH_COS(sh_cos)); +} + static u32 vlv_sprite_ctl(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { @@ -476,8 +519,10 @@ vlv_update_plane(struct intel_plane *plane, spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + vlv_update_clrc(plane_state); + if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B) - chv_update_csc(plane, fb->format->format); + chv_update_csc(plane_state); if (key->flags) { I915_WRITE_FW(SPKEYMINVAL(pipe, plane_id), key->min_value); diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index eb6929025766c4221e806f9da83762f352ad1dd5..36e0be99cf9a249dd364d77f0e4f48b2d45433c6 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -71,6 +71,16 @@ config DRM_MSM_DSI_STAGING Interface and DSI stands for Display Serial Interface which powers the primary display of your mobile device. +config DSI_PARSER + bool "Enable DSI panel configuration parser" + depends on DYNAMIC_DEBUG + default y + help + Choose this option if you need text parser for a DSI panel + configurations which can parse a given text file and get the + panel configurations. Also, this module provides a set of APIs + which can be used to get the parsed data. + config DRM_MSM_DSI_PLL bool "Enable DSI PLL driver in MSM DRM" depends on DRM_MSM_DSI && COMMON_CLK diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index ce105fb28270572af68500ce0c9a58763b395758..98fdd007c875dec3323f6749403f3b5fb5c29399 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -150,6 +150,8 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \ dsi-staging/dsi_clk_manager.o \ dsi-staging/dsi_display_test.o +msm_drm-$(CONFIG_DSI_PARSER) += dsi-staging/dsi_parser.o + msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \ dsi/pll/dsi_pll_28nm.o diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c index 796f55f90b0867e92c4d69eb9769f420ab024bd2..840e44e8a5e15edb4a833f04da88816656b7d3bf 100644 --- a/drivers/gpu/drm/msm/dp/dp_aux.c +++ b/drivers/gpu/drm/msm/dp/dp_aux.c @@ -14,6 +14,8 @@ #define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ +#include +#include #include #include "dp_aux.h" @@ -29,6 +31,7 @@ struct dp_aux_private { struct dp_aux dp_aux; struct dp_catalog_aux *catalog; struct dp_aux_cfg *cfg; + struct device_node *aux_switch_node; struct mutex mutex; struct completion comp; struct drm_dp_aux drm_aux; @@ -703,14 +706,60 @@ static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en, aux->drm_aux.transfer = dp_aux_transfer; } +static int dp_aux_configure_aux_switch(struct dp_aux *dp_aux, + bool enable, int orientation) +{ + struct dp_aux_private *aux; + int rc = 0; + enum fsa_function event = FSA_USBC_DISPLAYPORT_DISCONNECTED; + + if (!dp_aux) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto end; + } + + aux = container_of(dp_aux, struct dp_aux_private, dp_aux); + + if (!aux->aux_switch_node) { + pr_debug("undefined fsa4480 handle\n"); + rc = -EINVAL; + goto end; + } + + if (enable) { + switch (orientation) { + case ORIENTATION_CC1: + event = FSA_USBC_ORIENTATION_CC1; + break; + case ORIENTATION_CC2: + event = FSA_USBC_ORIENTATION_CC2; + break; + default: + pr_err("invalid orientation\n"); + rc = -EINVAL; + goto end; + } + } + + pr_debug("enable=%d, orientation=%d, event=%d\n", + enable, orientation, event); + + rc = fsa4480_switch_event(aux->aux_switch_node, event); + if (rc) + pr_err("failed to configure fsa4480 i2c device (%d)\n", rc); +end: + return rc; +} + struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog, - struct dp_aux_cfg *aux_cfg) + struct dp_aux_cfg *aux_cfg, struct device_node *aux_switch) { int rc = 0; struct dp_aux_private *aux; struct dp_aux *dp_aux; - if (!catalog || !aux_cfg) { + if (!catalog || !aux_cfg || !aux_switch) { pr_err("invalid input\n"); rc = -ENODEV; goto error; @@ -729,6 +778,7 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog, aux->dev = dev; aux->catalog = catalog; aux->cfg = aux_cfg; + aux->aux_switch_node = aux_switch; dp_aux = &aux->dp_aux; aux->retry_cnt = 0; aux->dp_aux.reg = 0xFFFF; @@ -742,6 +792,7 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog, dp_aux->abort = dp_aux_abort_transaction; dp_aux->dpcd_updated = dp_aux_dpcd_updated; dp_aux->set_sim_mode = dp_aux_set_sim_mode; + dp_aux->aux_switch = dp_aux_configure_aux_switch; return dp_aux; error: diff --git a/drivers/gpu/drm/msm/dp/dp_aux.h b/drivers/gpu/drm/msm/dp/dp_aux.h index bf52d57d606b3b7f9649c7d06448526988cac499..dba1c5a64e599431305fbbad3aac61288d627274 100644 --- a/drivers/gpu/drm/msm/dp/dp_aux.h +++ b/drivers/gpu/drm/msm/dp/dp_aux.h @@ -55,10 +55,11 @@ struct dp_aux { void (*abort)(struct dp_aux *aux); void (*dpcd_updated)(struct dp_aux *aux); void (*set_sim_mode)(struct dp_aux *aux, bool en, u8 *edid, u8 *dpcd); + int (*aux_switch)(struct dp_aux *aux, bool enable, int orientation); }; struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog, - struct dp_aux_cfg *aux_cfg); + struct dp_aux_cfg *aux_cfg, struct device_node *aux_switch); void dp_aux_put(struct dp_aux *aux); #endif /*__DP_AUX_H_*/ diff --git a/drivers/gpu/drm/msm/dp/dp_catalog_v420.c b/drivers/gpu/drm/msm/dp/dp_catalog_v420.c index 53d1eddadf80c998d93d49d2b3383d8a4bbe89f1..54b5f14d965f6a3100a24ff2845e259e152a28cb 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog_v420.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog_v420.c @@ -27,17 +27,17 @@ #define MAX_PRE_EMP_LEVELS 4 static u8 const vm_pre_emphasis[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = { - {0x00, 0x0B, 0x12, 0xFF}, /* pe0, 0 db */ - {0x00, 0x0A, 0x12, 0xFF}, /* pe1, 3.5 db */ - {0x00, 0x0C, 0xFF, 0xFF}, /* pe2, 6.0 db */ + {0x00, 0x0B, 0x14, 0xFF}, /* pe0, 0 db */ + {0x00, 0x0B, 0x12, 0xFF}, /* pe1, 3.5 db */ + {0x00, 0x0B, 0xFF, 0xFF}, /* pe2, 6.0 db */ {0xFF, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */ }; /* voltage swing, 0.2v and 1.0v are not support */ static u8 const vm_voltage_swing[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = { - {0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v */ - {0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */ - {0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */ + {0x07, 0x0F, 0x16, 0xFF}, /* sw0, 0.4v */ + {0x11, 0x1E, 0x1F, 0xFF}, /* sw1, 0.6 v */ + {0x19, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */ {0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ }; @@ -108,13 +108,13 @@ static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux, catalog = dp_catalog_get_priv_v420(aux); io_data = catalog->io->dp_phy; - dp_write(catalog, io_data, DP_PHY_PD_CTL, 0x65); + dp_write(catalog, io_data, DP_PHY_PD_CTL, 0x67); wmb(); /* make sure PD programming happened */ /* Turn on BIAS current for PHY/PLL */ io_data = catalog->io->dp_pll; dp_write(catalog, io_data, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17); - wmb(); /* make sure PD programming happened */ + wmb(); /* make sure BIAS programming happened */ io_data = catalog->io->dp_phy; /* DP AUX CFG register programming */ diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 08a54fc5d86f9de41171d1e5172b8fd8420f61dc..ba9566e9170c528a4d9749ba39e847eadb9c3e37 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -115,7 +115,7 @@ static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl, enum dp_stream_id strm) { int const idle_pattern_completion_timeout_ms = 3 * HZ / 100; struct dp_ctrl_private *ctrl; - u32 state; + u32 state = 0x0; if (!dp_ctrl) { pr_err("Invalid input data\n"); @@ -139,7 +139,7 @@ static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl, enum dp_stream_id strm) return; } - state = (strm == DP_STREAM_0) ? MST_DP0_PUSH_VCPF : MST_DP1_PUSH_VCPF; + state |= (strm == DP_STREAM_0) ? MST_DP0_PUSH_VCPF : MST_DP1_PUSH_VCPF; trigger_idle: reinit_completion(&ctrl->idle_comp); @@ -759,12 +759,16 @@ static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl) * link clocks and core clocks. */ ctrl->dp_ctrl.reset(&ctrl->dp_ctrl); + ctrl->dp_ctrl.stream_off(&ctrl->dp_ctrl, ctrl->panel); ctrl->dp_ctrl.off(&ctrl->dp_ctrl); + ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg); + ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode); if (ret) pr_err("failed to enable DP controller\n"); + ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel); pr_debug("end\n"); } @@ -905,6 +909,11 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) goto end; } + if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { + dp_ctrl_send_phy_test_pattern(ctrl); + return 0; + } + rc = dp_ctrl_mst_stream_setup(ctrl, panel); if (rc) goto end; @@ -991,9 +1000,6 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode) dp_ctrl_enable_mainlink_clocks(ctrl); } - if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) - dp_ctrl_send_phy_test_pattern(ctrl); - ctrl->power_on = true; pr_debug("End-\n"); diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 6dfa7763d6d5f28ea4c38942ef8936fd0315201a..c39051b8bd4a3dfdf930b58e1c6e05d127d188df 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -262,7 +262,10 @@ static ssize_t dp_debug_write_hpd(struct file *file, struct dp_debug_private *debug = file->private_data; char buf[SZ_8]; size_t len = 0; - int hpd; + int const orientation_mask = 0x4; + int const hpd_data_mask = 0x7; + int hpd = 0; + int orientation = 0; if (!debug) return -ENODEV; @@ -280,11 +283,14 @@ static ssize_t dp_debug_write_hpd(struct file *file, if (kstrtoint(buf, 10, &hpd) != 0) goto end; - hpd &= 0x3; + hpd &= hpd_data_mask; + orientation = hpd & orientation_mask ? + ORIENTATION_CC2 : ORIENTATION_CC1; debug->dp_debug.psm_enabled = !!(hpd & BIT(1)); - debug->usbpd->simulate_connect(debug->usbpd, !!(hpd & BIT(0))); + debug->usbpd->simulate_connect(debug->usbpd, !!(hpd & BIT(0)), + orientation); end: return len; } diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 14ed515cc7913e3ddf9092703539753ae679387e..19e4d5244f44f3d03b27a45935f0cee298393840 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -203,12 +203,6 @@ static void dp_display_notify_hdcp_status_cb(void *ptr, queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ/4); } -static void dp_display_destroy_hdcp_workqueue(struct dp_display_private *dp) -{ - if (dp->wq) - destroy_workqueue(dp->wq); -} - static void dp_display_update_hdcp_info(struct dp_display_private *dp) { void *fd = NULL; @@ -270,7 +264,6 @@ static void dp_display_deinitialize_hdcp(struct dp_display_private *dp) } sde_dp_hdcp2p2_deinit(dp->hdcp.data); - dp_display_destroy_hdcp_workqueue(dp); mutex_destroy(&dp->hdcp_mutex); } @@ -660,36 +653,6 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) return rc; } -static int dp_display_configure_aux_switch(struct dp_display_private *dp) -{ - int rc = 0; - enum fsa_function event = FSA_EVENT_MAX; - - if (!dp->aux_switch_node) { - pr_debug("undefined fsa4480 handle\n"); - goto end; - } - - switch (dp->usbpd->orientation) { - case ORIENTATION_CC1: - event = FSA_USBC_ORIENTATION_CC1; - break; - case ORIENTATION_CC2: - event = FSA_USBC_ORIENTATION_CC2; - break; - default: - pr_err("invalid orientation\n"); - rc = -EINVAL; - goto end; - } - - rc = fsa4480_switch_event(dp->aux_switch_node, event); - if (rc) - pr_err("failed to configure fsa4480 i2c device (%d)\n", rc); -end: - return rc; -} - static int dp_display_usbpd_configure_cb(struct device *dev) { int rc = 0; @@ -708,9 +671,11 @@ static int dp_display_usbpd_configure_cb(struct device *dev) goto end; } - rc = dp_display_configure_aux_switch(dp); - if (rc) - goto end; + if (!dp->debug->sim_mode) { + rc = dp->aux->aux_switch(dp->aux, true, dp->usbpd->orientation); + if (rc) + goto end; + } dp_display_host_init(dp); @@ -799,6 +764,10 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) flush_workqueue(dp->wq); dp_display_handle_disconnect(dp); + + if (!dp->debug->sim_mode) + dp->aux->aux_switch(dp->aux, false, ORIENTATION_NONE); + atomic_set(&dp->aborted, 0); end: return rc; @@ -1035,7 +1004,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_aux; } - dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser->aux_cfg); + dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser->aux_cfg, + dp->aux_switch_node); if (IS_ERR(dp->aux)) { rc = PTR_ERR(dp->aux); pr_err("failed to initialize aux, rc = %d\n", rc); @@ -1976,6 +1946,9 @@ static int dp_display_remove(struct platform_device *pdev) dp_display_deinit_sub_modules(dp); + if (dp->wq) + destroy_workqueue(dp->wq); + platform_set_drvdata(pdev, NULL); devm_kfree(&pdev->dev, dp); diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index f2909e6d7dcf1b8c58e148a46aeedd52992312ab..c9726552e216887e07c4bb7f384908e097bd5228 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -366,7 +366,7 @@ static bool _dp_mst_compute_config(struct dp_mst_bridge *dp_bridge) dp_bridge->slots = slots; - DP_MST_DEBUG("mst bridge [%d] pbn: %d slots: %d", dp_bridge->id, + DP_MST_DEBUG("mst bridge [%d] pbn: %d slots: %d\n", dp_bridge->id, mst_pbn, slots); return true; @@ -798,24 +798,35 @@ dp_mst_atomic_best_encoder(struct drm_connector *connector, struct dp_display *dp_display = display; struct dp_mst_private *mst = dp_display->dp_mst_prv_info; struct sde_connector *conn = to_sde_connector(connector); + struct drm_encoder *enc = NULL; u32 i; - DP_MST_DEBUG("mst connector:%d atomic best encoder\n", - connector->base.id); + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (mst->mst_bridge[i].connector == connector) { + enc = mst->mst_bridge[i].encoder; + goto end; + } + } for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { if (!mst->mst_bridge[i].encoder_active_sts) { mst->mst_bridge[i].encoder_active_sts = true; mst->mst_bridge[i].connector = connector; mst->mst_bridge[i].dp_panel = conn->drv_panel; - return mst->mst_bridge[i].encoder; + enc = mst->mst_bridge[i].encoder; + break; } } - DP_MST_DEBUG("mst connector:%d atomic best encoder failed\n", - connector->base.id); +end: + if (enc) + DP_MST_DEBUG("mst connector:%d atomic best encoder:%d\n", + connector->base.id, i); + else + DP_MST_DEBUG("mst connector:%d atomic best encoder failed\n", + connector->base.id); - return NULL; + return enc; } static struct dp_mst_bridge *_dp_mst_get_bridge_from_encoder( diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.c b/drivers/gpu/drm/msm/dp/dp_usbpd.c index 42eb9b053e9958a02b3c2e2a8d5d6c49fe14e562..913d793620732af02d6575ddc1c2b9c706c8596d 100644 --- a/drivers/gpu/drm/msm/dp/dp_usbpd.c +++ b/drivers/gpu/drm/msm/dp/dp_usbpd.c @@ -16,6 +16,7 @@ #include #include +#include #include "dp_usbpd.h" @@ -314,11 +315,44 @@ static int dp_usbpd_validate_callback(u8 cmd, return ret; } + +static int dp_usbpd_get_ss_lanes(struct dp_usbpd_private *pd) +{ + int rc = 0; + int timeout = 250; + + /* + * By default, USB reserves two lanes for Super Speed. + * Which means DP has remaining two lanes to operate on. + * If multi-function is not supported, request USB to + * release the Super Speed lanes so that DP can use + * all four lanes in case DPCD indicates support for + * four lanes. + */ + if (!pd->dp_usbpd.multi_func) { + while (timeout) { + rc = pd->svid_handler.request_usb_ss_lane( + pd->pd, &pd->svid_handler); + if (rc != -EBUSY) + break; + + pr_warn("USB busy, retry\n"); + + /* wait for hw recommended delay for usb */ + msleep(20); + timeout--; + } + } + + return rc; +} + static void dp_usbpd_response_cb(struct usbpd_svid_handler *hdlr, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, const u32 *vdos, int num_vdos) { struct dp_usbpd_private *pd; + int rc = 0; pd = container_of(hdlr, struct dp_usbpd_private, svid_handler); @@ -380,17 +414,11 @@ static void dp_usbpd_response_cb(struct usbpd_svid_handler *hdlr, u8 cmd, pd->dp_usbpd.orientation = usbpd_get_plug_orientation(pd->pd); - /* - * By default, USB reserves two lanes for Super Speed. - * Which means DP has remaining two lanes to operate on. - * If multi-function is not supported, request USB to - * release the Super Speed lanes so that DP can use - * all four lanes in case DPCD indicates support for - * four lanes. - */ - if (!pd->dp_usbpd.multi_func) - pd->svid_handler.request_usb_ss_lane(pd->pd, - &pd->svid_handler); + rc = dp_usbpd_get_ss_lanes(pd); + if (rc) { + pr_err("failed to get SuperSpeed lanes\n"); + break; + } if (pd->dp_cb && pd->dp_cb->configure) pd->dp_cb->configure(pd->dev); @@ -401,7 +429,8 @@ static void dp_usbpd_response_cb(struct usbpd_svid_handler *hdlr, u8 cmd, } } -static int dp_usbpd_simulate_connect(struct dp_usbpd *dp_usbpd, bool hpd) +static int dp_usbpd_simulate_connect(struct dp_usbpd *dp_usbpd, bool hpd, + int orientation) { int rc = 0; struct dp_usbpd_private *pd; @@ -416,7 +445,11 @@ static int dp_usbpd_simulate_connect(struct dp_usbpd *dp_usbpd, bool hpd) dp_usbpd->hpd_high = hpd; pd->forced_disconnect = !hpd; + pd->dp_usbpd.orientation = orientation; + pr_debug("hpd_high=%d, forced_disconnect=%d, orientation=%d\n", + dp_usbpd->hpd_high, pd->forced_disconnect, + pd->dp_usbpd.orientation); if (hpd) pd->dp_cb->configure(pd->dev); else diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.h b/drivers/gpu/drm/msm/dp/dp_usbpd.h index 0a7efd957d84ebe26d895c28c6dee0b0bd77175f..5589af4417cf79aca14a6149d9560e1120a81b70 100644 --- a/drivers/gpu/drm/msm/dp/dp_usbpd.h +++ b/drivers/gpu/drm/msm/dp/dp_usbpd.h @@ -65,7 +65,8 @@ struct dp_usbpd { bool alt_mode_cfg_done; bool debug_en; - int (*simulate_connect)(struct dp_usbpd *dp_usbpd, bool hpd); + int (*simulate_connect)(struct dp_usbpd *dp_usbpd, bool hpd, + int orientation); int (*simulate_attention)(struct dp_usbpd *dp_usbpd, int vdo); }; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c index 2074db0d7691444c80701f07a1a2fa599d6690f9..06b3e48f096261116fd22540ab299ecb2172cb90 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c @@ -67,6 +67,8 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, ctrl->ops.error_intr_ctrl = dsi_ctrl_hw_cmn_error_intr_ctrl; ctrl->ops.get_error_mask = dsi_ctrl_hw_cmn_get_error_mask; ctrl->ops.get_hw_version = dsi_ctrl_hw_cmn_get_hw_version; + ctrl->ops.wait_for_cmd_mode_mdp_idle = + dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle; switch (version) { case DSI_CTRL_VERSION_1_4: diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h index 8fd07375144eb2e5aac9872a1962f73bf15d5b38..03cb251dbe3f159b628ac1524c92f1b779933704 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h @@ -202,6 +202,7 @@ void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx, void dsi_ctrl_hw_cmn_error_intr_ctrl(struct dsi_ctrl_hw *ctrl, bool en); u32 dsi_ctrl_hw_cmn_get_error_mask(struct dsi_ctrl_hw *ctrl); u32 dsi_ctrl_hw_cmn_get_hw_version(struct dsi_ctrl_hw *ctrl); +int dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl_hw *ctrl); /* Definitions specific to 1.4 DSI controller hardware */ int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h index 1fd10d990c03173c8a7b555cbfaad219e00e6a09..d2000f6c0977f6f97119d17a7ce70919829f6472 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -231,6 +231,15 @@ void *dsi_register_clk_handle(void *clk_mngr, char *client); */ int dsi_deregister_clk_handle(void *client); +/** + * dsi_display_link_clk_force_update_ctrl() - force to set link clks + * @handle: Handle of desired DSI clock client. + * + * return: error code in case of failure or 0 for success. + */ + +int dsi_display_link_clk_force_update_ctrl(void *handle); + /** * dsi_display_clk_ctrl() - set frequencies for link clks * @handle: Handle of desired DSI clock client. diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c index 38eba8de48dd45c267f4f5e14698f4b34b8f9bcd..858769682740a15273549e354825f84637c5c4c1 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -1071,6 +1071,69 @@ int dsi_clk_req_state(void *client, enum dsi_clk_type clk, DEFINE_MUTEX(dsi_mngr_clk_mutex); +static int dsi_display_link_clk_force_update(void *client) +{ + int rc = 0; + struct dsi_clk_client_info *c = client; + struct dsi_clk_mngr *mngr; + struct dsi_link_clks *l_clks; + + mngr = c->mngr; + mutex_lock(&mngr->clk_mutex); + + l_clks = mngr->link_clks; + + /* + * When link_clk_state is DSI_CLK_OFF, don't change DSI clock rate + * since it is possible to be overwritten, and return -EAGAIN to + * dynamic DSI writing interface to defer the reenabling to the next + * drm commit. + */ + if (mngr->link_clk_state == DSI_CLK_OFF) { + rc = -EAGAIN; + goto error; + } + + rc = dsi_display_link_clk_disable(l_clks, + mngr->dsi_ctrl_count, mngr->master_ndx); + if (rc) { + pr_err("%s, failed to stop link clk, rc = %d\n", + __func__, rc); + goto error; + } + + rc = dsi_display_link_clk_enable(l_clks, + mngr->dsi_ctrl_count, mngr->master_ndx); + if (rc) { + pr_err("%s, failed to start link clk rc= %d\n", + __func__, rc); + goto error; + } + +error: + mutex_unlock(&mngr->clk_mutex); + return rc; + +} + +int dsi_display_link_clk_force_update_ctrl(void *handle) +{ + int rc = 0; + + if (!handle) { + pr_err("%s: Invalid arg\n", __func__); + return -EINVAL; + } + + mutex_lock(&dsi_mngr_clk_mutex); + + rc = dsi_display_link_clk_force_update(handle); + + mutex_unlock(&dsi_mngr_clk_mutex); + + return rc; +} + int dsi_display_clk_ctrl(void *handle, enum dsi_clk_type clk_type, enum dsi_clk_state clk_state) { diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c index fb2f4b95c3b18db6fda3a8e36fdc93a70899e577..2587a6efd79fe9640d535c6a07f8c9e131c20282 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -921,6 +921,27 @@ static int dsi_ctrl_copy_and_pad_cmd(struct dsi_ctrl *dsi_ctrl, return rc; } +int dsi_ctrl_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl *dsi_ctrl) +{ + int rc = 0; + + if (!dsi_ctrl) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + if (dsi_ctrl->host_config.panel_mode != DSI_OP_CMD_MODE) + return -EINVAL; + + mutex_lock(&dsi_ctrl->ctrl_lock); + + rc = dsi_ctrl->hw.ops.wait_for_cmd_mode_mdp_idle(&dsi_ctrl->hw); + + mutex_unlock(&dsi_ctrl->ctrl_lock); + + return rc; +} + static void dsi_ctrl_wait_for_video_done(struct dsi_ctrl *dsi_ctrl) { u32 v_total = 0, v_blank = 0, sleep_ms = 0, fps = 0, ret; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h index 451ada370bbceace970f34e7916beedf5d3748d8..6ea34689bc3c185f041f1f0db3835bb135af9307 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h @@ -741,4 +741,11 @@ void dsi_ctrl_irq_update(struct dsi_ctrl *dsi_ctrl, bool enable); int dsi_ctrl_get_host_engine_init_state(struct dsi_ctrl *dsi_ctrl, bool *state); +/** + * dsi_ctrl_wait_for_cmd_mode_mdp_idle() - Wait for command mode engine not to + * be busy sending data from display engine. + * @dsi_ctrl: DSI controller handle. + */ +int dsi_ctrl_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl *dsi_ctrl); + #endif /* _DSI_CTRL_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h index 86eed096dd3b103f1a21332920960f939acb5e9d..230772f1bcd1c0300a0a6275015c4fe87c99cca6 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h @@ -801,6 +801,13 @@ struct dsi_ctrl_hw_ops { * @ctrl: Pointer to the controller host hardware. */ u32 (*get_hw_version)(struct dsi_ctrl_hw *ctrl); + + /** + * wait_for_cmd_mode_mdp_idle() - wait for command mode engine not to + * be busy sending data from display engine + * @ctrl: Pointer to the controller host hardware. + */ + int (*wait_for_cmd_mode_mdp_idle)(struct dsi_ctrl_hw *ctrl); }; /* diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c index 0600d1aa381599795ce4555b0ed7160e1e50a395..53717a50ccc8acd7c9c2365d51655eb8dca9955d 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, 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 @@ -1476,3 +1476,18 @@ u32 dsi_ctrl_hw_cmn_get_hw_version(struct dsi_ctrl_hw *ctrl) return reg; } + +int dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl_hw *ctrl) +{ + int rc = 0, val = 0; + u32 cmd_mode_mdp_busy_mask = BIT(2); + u32 const sleep_us = 2 * 1000; + u32 const timeout_us = 200 * 1000; + + rc = readl_poll_timeout(ctrl->base + DSI_STATUS, val, + !(val & cmd_mode_mdp_busy_mask), sleep_us, timeout_us); + if (rc) + pr_err("%s: timed out waiting for idle\n", __func__); + + return rc; +} diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c index 6b8995844d1dbc9db05c23f2a1382c3ac6e3c715..31f94b08c42dc34a11bb70daaab096a1f563e34d 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c @@ -29,6 +29,7 @@ #include "dsi_clk.h" #include "dsi_pwr.h" #include "sde_dbg.h" +#include "dsi_parser.h" #define to_dsi_display(x) container_of(x, struct dsi_display, host) #define INT_BASE_10 10 @@ -38,19 +39,18 @@ #define MAX_NAME_SIZE 64 -static DEFINE_MUTEX(dsi_display_list_lock); -static LIST_HEAD(dsi_display_list); +#define DSI_CLOCK_BITRATE_RADIX 10 + static char dsi_display_primary[MAX_CMDLINE_PARAM_LEN]; static char dsi_display_secondary[MAX_CMDLINE_PARAM_LEN]; static struct dsi_display_boot_param boot_displays[MAX_DSI_ACTIVE_DISPLAY]; -static struct device_node *default_active_node; +static struct dsi_display *default_display; +static bool display_from_cmdline; static const struct of_device_id dsi_display_dt_match[] = { {.compatible = "qcom,dsi-display"}, {} }; -static struct dsi_display *main_display; - static void dsi_display_mask_ctrl_error_interrupts(struct dsi_display *display) { int i; @@ -1146,6 +1146,8 @@ static int dsi_display_debugfs_init(struct dsi_display *display) } display->root = dir; + dsi_parser_dbg_init(display->parser, dir); + return rc; error_remove_dir: debugfs_remove(dir); @@ -1598,6 +1600,7 @@ static void dsi_display_parse_cmdline_topology(struct dsi_display *display, { char *boot_str = NULL; char *str = NULL; + char *sw_te = NULL; unsigned long value; if (display_type >= MAX_DSI_ACTIVE_DISPLAY) { @@ -1610,6 +1613,10 @@ static void dsi_display_parse_cmdline_topology(struct dsi_display *display, else boot_str = dsi_display_secondary; + sw_te = strnstr(boot_str, ":swte", strlen(boot_str)); + if (sw_te) + display->sw_te_using_wd = true; + str = strnstr(boot_str, ":config", strlen(boot_str)); if (!str) return; @@ -1774,20 +1781,6 @@ static bool validate_dsi_display_selection(void) return true; } -struct device_node *dsi_display_get_boot_display(int index) -{ - - pr_err("index = %d\n", index); - - if (boot_displays[index].node) - return boot_displays[index].node; - else if ((index == (MAX_DSI_ACTIVE_DISPLAY - 1)) - && (default_active_node)) - return default_active_node; - else - return NULL; -} - static int dsi_display_phy_power_on(struct dsi_display *display) { int rc = 0; @@ -2463,87 +2456,93 @@ static int dsi_display_clocks_deinit(struct dsi_display *display) return rc; } +static bool dsi_display_check_prefix(const char *clk_prefix, + const char *clk_name) +{ + return !!strnstr(clk_name, clk_prefix, strlen(clk_name)); +} + +static int dsi_display_get_clocks_count(struct dsi_display *display) +{ + if (display->fw) + return dsi_parser_count_strings(display->parser_node, + "qcom,dsi-select-clocks"); + else + return of_property_count_strings(display->disp_node, + "qcom,dsi-select-clocks"); +} + +static void dsi_display_get_clock_name(struct dsi_display *display, + int index, const char **clk_name) +{ + if (display->fw) + dsi_parser_read_string_index(display->parser_node, + "qcom,dsi-select-clocks", index, clk_name); + else + of_property_read_string_index(display->disp_node, + "qcom,dsi-select-clocks", index, clk_name); +} + static int dsi_display_clocks_init(struct dsi_display *display) { - int rc = 0; + int i, rc = 0, num_clk = 0; + const char *clk_name; + const char *src_byte = "src_byte", *src_pixel = "src_pixel"; + const char *mux_byte = "mux_byte", *mux_pixel = "mux_pixel"; + const char *shadow_byte = "shadow_byte", *shadow_pixel = "shadow_pixel"; + struct clk *dsi_clk; struct dsi_clk_link_set *src = &display->clock_info.src_clks; struct dsi_clk_link_set *mux = &display->clock_info.mux_clks; struct dsi_clk_link_set *shadow = &display->clock_info.shadow_clks; - src->byte_clk = devm_clk_get(&display->pdev->dev, "src_byte_clk"); - if (IS_ERR_OR_NULL(src->byte_clk)) { - rc = PTR_ERR(src->byte_clk); - src->byte_clk = NULL; - pr_err("failed to get src_byte_clk, rc=%d\n", rc); - goto error; - } + num_clk = dsi_display_get_clocks_count(display); - src->pixel_clk = devm_clk_get(&display->pdev->dev, "src_pixel_clk"); - if (IS_ERR_OR_NULL(src->pixel_clk)) { - rc = PTR_ERR(src->pixel_clk); - src->pixel_clk = NULL; - pr_err("failed to get src_pixel_clk, rc=%d\n", rc); - goto error; - } + pr_debug("clk count=%d\n", num_clk); - mux->byte_clk = devm_clk_get(&display->pdev->dev, "mux_byte_clk"); - if (IS_ERR_OR_NULL(mux->byte_clk)) { - rc = PTR_ERR(mux->byte_clk); - pr_debug("failed to get mux_byte_clk, rc=%d\n", rc); - mux->byte_clk = NULL; - /* - * Skip getting rest of clocks since one failed. This is a - * non-critical failure since these clocks are requied only for - * dynamic refresh use cases. - */ - rc = 0; - goto done; - }; + for (i = 0; i < num_clk; i++) { + dsi_display_get_clock_name(display, i, &clk_name); - mux->pixel_clk = devm_clk_get(&display->pdev->dev, "mux_pixel_clk"); - if (IS_ERR_OR_NULL(mux->pixel_clk)) { - rc = PTR_ERR(mux->pixel_clk); - mux->pixel_clk = NULL; - pr_debug("failed to get mux_pixel_clk, rc=%d\n", rc); - /* - * Skip getting rest of clocks since one failed. This is a - * non-critical failure since these clocks are requied only for - * dynamic refresh use cases. - */ - rc = 0; - goto done; - }; + pr_debug("clock name:%s\n", clk_name); - shadow->byte_clk = devm_clk_get(&display->pdev->dev, "shadow_byte_clk"); - if (IS_ERR_OR_NULL(shadow->byte_clk)) { - rc = PTR_ERR(shadow->byte_clk); - shadow->byte_clk = NULL; - pr_err("failed to get shadow_byte_clk, rc=%d\n", rc); - /* - * Skip getting rest of clocks since one failed. This is a - * non-critical failure since these clocks are requied only for - * dynamic refresh use cases. - */ - rc = 0; - goto done; - }; + dsi_clk = devm_clk_get(&display->pdev->dev, clk_name); + if (IS_ERR_OR_NULL(dsi_clk)) { + rc = PTR_ERR(dsi_clk); - shadow->pixel_clk = devm_clk_get(&display->pdev->dev, - "shadow_pixel_clk"); - if (IS_ERR_OR_NULL(shadow->pixel_clk)) { - rc = PTR_ERR(shadow->pixel_clk); - shadow->pixel_clk = NULL; - pr_err("failed to get shadow_pixel_clk, rc=%d\n", rc); - /* - * Skip getting rest of clocks since one failed. This is a - * non-critical failure since these clocks are requied only for - * dynamic refresh use cases. - */ - rc = 0; - goto done; - }; + pr_err("failed to get %s, rc=%d\n", clk_name, rc); + goto error; + } + + if (dsi_display_check_prefix(src_byte, clk_name)) { + src->byte_clk = dsi_clk; + continue; + } + + if (dsi_display_check_prefix(src_pixel, clk_name)) { + src->pixel_clk = dsi_clk; + continue; + } + + if (dsi_display_check_prefix(mux_byte, clk_name)) { + mux->byte_clk = dsi_clk; + continue; + } + + if (dsi_display_check_prefix(mux_pixel, clk_name)) { + mux->pixel_clk = dsi_clk; + continue; + } + + if (dsi_display_check_prefix(shadow_byte, clk_name)) { + shadow->byte_clk = dsi_clk; + continue; + } + + if (dsi_display_check_prefix(shadow_pixel, clk_name)) { + shadow->pixel_clk = dsi_clk; + continue; + } + } -done: return 0; error: (void)dsi_display_clocks_deinit(display); @@ -2655,6 +2654,8 @@ int dsi_pre_clkoff_cb(void *priv, pr_err("%s: failed to disable ulps. rc=%d\n", __func__, rc); } + /* dsi will not be able to serve irqs from here on */ + dsi_display_ctrl_irq_update(display, false); } return rc; @@ -2762,9 +2763,6 @@ int dsi_post_clkoff_cb(void *priv, if ((clk_type & DSI_CORE_CLK) && (curr_state == DSI_CLK_OFF)) { - /* dsi will not be able to serve irqs from here */ - dsi_display_ctrl_irq_update(display, false); - rc = dsi_display_phy_power_off(display); if (rc) pr_err("[%s] failed to power off PHY, rc=%d\n", @@ -2927,62 +2925,103 @@ static int dsi_display_parse_lane_map(struct dsi_display *display) return 0; } -static int dsi_display_parse_dt(struct dsi_display *display) +static int dsi_display_get_phandle_index( + struct dsi_display *display, + const char *propname, int count, int index) { + struct device_node *disp_node = display->disp_node; + u32 *val = NULL; int rc = 0; - int i; - u32 phy_count = 0; - struct device_node *of_node; - - /* Parse controllers */ - for (i = 0; i < MAX_DSI_CTRLS_PER_DISPLAY; i++) { - of_node = of_parse_phandle(display->pdev->dev.of_node, - "qcom,dsi-ctrl", i); - if (!of_node) { - if (!i) { - pr_err("No controllers present\n"); - return -ENODEV; - } - break; - } - display->ctrl[i].ctrl_of_node = of_node; - display->ctrl_count++; + val = kzalloc(count, GFP_KERNEL); + if (!val) { + rc = -ENOMEM; + goto end; } - /* Parse Phys */ - for (i = 0; i < MAX_DSI_CTRLS_PER_DISPLAY; i++) { - of_node = of_parse_phandle(display->pdev->dev.of_node, - "qcom,dsi-phy", i); - if (!of_node) { - if (!i) { - pr_err("No PHY devices present\n"); - rc = -ENODEV; - goto error; - } - break; - } + if (index >= count) + goto end; + + if (display->fw) + rc = dsi_parser_read_u32_array(display->parser_node, + propname, val, count); + else + rc = of_property_read_u32_array(disp_node, propname, + val, count); + if (rc) + goto end; + + rc = val[index]; + + pr_debug("%s index=%d\n", propname, rc); +end: + kfree(val); + return rc; +} + +static int dsi_display_get_phandle_count(struct dsi_display *display, + const char *propname) +{ + if (display->fw) + return dsi_parser_count_u32_elems(display->parser_node, + propname); + else + return of_property_count_u32_elems(display->disp_node, + propname); +} + +static int dsi_display_parse_dt(struct dsi_display *display) +{ + int i, rc = 0; + u32 phy_count = 0; + struct device_node *of_node = display->pdev->dev.of_node; + struct device_node *disp_node = display->disp_node; + + display->ctrl_count = dsi_display_get_phandle_count(display, + "qcom,dsi-ctrl-num"); + phy_count = dsi_display_get_phandle_count(display, + "qcom,dsi-ctrl-num"); + + pr_debug("ctrl count=%d, phy count=%d\n", + display->ctrl_count, phy_count); - display->ctrl[i].phy_of_node = of_node; - phy_count++; + if (!phy_count || !display->ctrl_count) { + pr_err("no ctrl/phys found\n"); + rc = -ENODEV; + goto error; } if (phy_count != display->ctrl_count) { - pr_err("Number of controllers does not match PHYs\n"); + pr_err("different ctrl and phy counts\n"); rc = -ENODEV; goto error; } - of_node = of_parse_phandle(display->pdev->dev.of_node, - "qcom,dsi-panel", 0); - if (!of_node) { + for (i = 0; i < display->ctrl_count; i++) { + struct dsi_display_ctrl *ctrl = &display->ctrl[i]; + int index; + + index = dsi_display_get_phandle_index(display, + "qcom,dsi-ctrl-num", display->ctrl_count, i); + ctrl->ctrl_of_node = of_parse_phandle(of_node, + "qcom,dsi-ctrl", index); + of_node_put(ctrl->ctrl_of_node); + + index = dsi_display_get_phandle_index(display, + "qcom,dsi-phy-num", display->ctrl_count, i); + ctrl->phy_of_node = of_parse_phandle(of_node, + "qcom,dsi-phy", index); + of_node_put(ctrl->phy_of_node); + } + + display->panel_of = of_parse_phandle(disp_node, "qcom,dsi-panel", 0); + if (!display->panel_of) { pr_err("No Panel device present\n"); rc = -ENODEV; goto error; - } else { - display->panel_of = of_node; } + pr_debug("success\n"); error: return rc; } @@ -3013,8 +3052,11 @@ static int dsi_display_res_init(struct dsi_display *display) } } - display->panel = dsi_panel_get(&display->pdev->dev, display->panel_of, - display->cmdline_topology); + display->panel = dsi_panel_get(&display->pdev->dev, + display->panel_of, + display->parser_node, + display->root, + display->cmdline_topology); if (IS_ERR_OR_NULL(display->panel)) { rc = PTR_ERR(display->panel); pr_err("failed to get panel, rc=%d\n", rc); @@ -3500,6 +3542,12 @@ static int _dsi_display_dev_init(struct dsi_display *display) mutex_lock(&display->display_lock); + display->parser = dsi_parser_get(&display->pdev->dev); + if (display->fw && display->parser) + display->parser_node = dsi_parser_get_head_node( + display->parser, display->fw->data, + display->fw->size); + rc = dsi_display_parse_dt(display); if (rc) { pr_err("[%s] failed to parse dt, rc=%d\n", display->name, rc); @@ -3648,6 +3696,228 @@ int dsi_display_splash_res_cleanup(struct dsi_display *display) return rc; } +static int dsi_display_force_update_dsi_clk(struct dsi_display *display) +{ + int rc = 0; + + rc = dsi_display_link_clk_force_update_ctrl(display->dsi_clk_handle); + + if (!rc) { + pr_info("dsi bit clk has been configured to %d\n", + display->cached_clk_rate); + + atomic_set(&display->clkrate_change_pending, 0); + } else { + pr_err("Failed to configure dsi bit clock '%d'. rc = %d\n", + display->cached_clk_rate, rc); + } + + return rc; +} + +static int dsi_display_request_update_dsi_bitrate(struct dsi_display *display, + u32 bit_clk_rate) +{ + int rc = 0; + int i; + + pr_debug("%s:bit rate:%d\n", __func__, bit_clk_rate); + if (!display->panel) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + if (bit_clk_rate == 0) { + pr_err("Invalid bit clock rate\n"); + return -EINVAL; + } + + display->config.bit_clk_rate_hz = bit_clk_rate; + + for (i = 0; i < display->ctrl_count; i++) { + struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i]; + struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl; + u32 num_of_lanes = 0; + u32 bpp = 3; + u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate; + struct dsi_host_common_cfg *host_cfg; + + mutex_lock(&ctrl->ctrl_lock); + + host_cfg = &display->panel->host_config; + if (host_cfg->data_lanes & DSI_DATA_LANE_0) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_1) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_2) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_3) + num_of_lanes++; + + if (num_of_lanes == 0) { + pr_err("Invalid lane count\n"); + rc = -EINVAL; + goto error; + } + + bit_rate = display->config.bit_clk_rate_hz * num_of_lanes; + bit_rate_per_lane = bit_rate; + do_div(bit_rate_per_lane, num_of_lanes); + pclk_rate = bit_rate; + do_div(pclk_rate, (8 * bpp)); + byte_clk_rate = bit_rate_per_lane; + do_div(byte_clk_rate, 8); + pr_debug("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n", + bit_rate, bit_rate_per_lane); + pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n", + byte_clk_rate, pclk_rate); + + ctrl->clk_freq.byte_clk_rate = byte_clk_rate; + ctrl->clk_freq.pix_clk_rate = pclk_rate; + rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle, + ctrl->clk_freq, ctrl->cell_index); + if (rc) { + pr_err("Failed to update link frequencies\n"); + goto error; + } + + ctrl->host_config.bit_clk_rate_hz = bit_clk_rate; +error: + mutex_unlock(&ctrl->ctrl_lock); + + /* TODO: recover ctrl->clk_freq in case of failure */ + if (rc) + return rc; + } + + return 0; +} + +static ssize_t sysfs_dynamic_dsi_clk_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc = 0; + struct dsi_display *display; + struct dsi_display_ctrl *m_ctrl; + struct dsi_ctrl *ctrl; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return -EINVAL; + } + + mutex_lock(&display->display_lock); + + m_ctrl = &display->ctrl[display->cmd_master_idx]; + ctrl = m_ctrl->ctrl; + if (ctrl) + display->cached_clk_rate = ctrl->clk_freq.byte_clk_rate + * 8; + + rc = snprintf(buf, PAGE_SIZE, "%d\n", display->cached_clk_rate); + pr_debug("%s: read dsi clk rate %d\n", __func__, + display->cached_clk_rate); + + mutex_unlock(&display->display_lock); + + return rc; +} + +static ssize_t sysfs_dynamic_dsi_clk_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc = 0; + int clk_rate; + struct dsi_display *display; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return -EINVAL; + } + + rc = kstrtoint(buf, DSI_CLOCK_BITRATE_RADIX, &clk_rate); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + return rc; + } + + if (clk_rate <= 0) { + pr_err("%s: bitrate should be greater than 0\n", __func__); + return -EINVAL; + } + + if (clk_rate == display->cached_clk_rate) { + pr_info("%s: ignore duplicated DSI clk setting\n", __func__); + return count; + } + + pr_info("%s: bitrate param value: '%d'\n", __func__, clk_rate); + + mutex_lock(&display->display_lock); + + display->cached_clk_rate = clk_rate; + rc = dsi_display_request_update_dsi_bitrate(display, clk_rate); + if (!rc) { + pr_info("%s: bit clk is ready to be configured to '%d'\n", + __func__, clk_rate); + } else { + pr_err("%s: Failed to prepare to configure '%d'. rc = %d\n", + __func__, clk_rate, rc); + /*Caching clock failed, so don't go on doing so.*/ + atomic_set(&display->clkrate_change_pending, 0); + display->cached_clk_rate = 0; + + mutex_unlock(&display->display_lock); + + return rc; + } + atomic_set(&display->clkrate_change_pending, 1); + + mutex_unlock(&display->display_lock); + + return count; + +} + +static DEVICE_ATTR(dynamic_dsi_clock, 0644, + sysfs_dynamic_dsi_clk_read, + sysfs_dynamic_dsi_clk_write); + +static struct attribute *dynamic_dsi_clock_fs_attrs[] = { + &dev_attr_dynamic_dsi_clock.attr, + NULL, +}; +static struct attribute_group dynamic_dsi_clock_fs_attrs_group = { + .attrs = dynamic_dsi_clock_fs_attrs, +}; + +static int dsi_display_sysfs_init(struct dsi_display *display) +{ + int rc = 0; + struct device *dev = &display->pdev->dev; + + if (display->panel->panel_mode == DSI_OP_CMD_MODE) + rc = sysfs_create_group(&dev->kobj, + &dynamic_dsi_clock_fs_attrs_group); + + return rc; + +} + +static int dsi_display_sysfs_deinit(struct dsi_display *display) +{ + struct device *dev = &display->pdev->dev; + + if (display->panel->panel_mode == DSI_OP_CMD_MODE) + sysfs_remove_group(&dev->kobj, + &dynamic_dsi_clock_fs_attrs_group); + + return 0; + +} + /** * dsi_display_bind - bind dsi device with controlling device * @dev: Pointer to base of platform device @@ -3695,6 +3965,15 @@ static int dsi_display_bind(struct device *dev, goto error; } + atomic_set(&display->clkrate_change_pending, 0); + display->cached_clk_rate = 0; + + rc = dsi_display_sysfs_init(display); + if (rc) { + pr_err("[%s] sysfs init failed, rc=%d\n", display->name, rc); + goto error; + } + memset(&info, 0x0, sizeof(info)); for (i = 0; i < display->ctrl_count; i++) { @@ -3846,6 +4125,7 @@ static int dsi_display_bind(struct device *dev, (void)dsi_phy_drv_deinit(display_ctrl->phy); (void)dsi_ctrl_drv_deinit(display_ctrl->ctrl); } + (void)dsi_display_sysfs_deinit(display); (void)dsi_display_debugfs_deinit(display); error: mutex_unlock(&display->display_lock); @@ -3903,6 +4183,9 @@ static void dsi_display_unbind(struct device *dev, pr_err("[%s] failed to deinit ctrl%d driver, rc=%d\n", display->name, i, rc); } + + atomic_set(&display->clkrate_change_pending, 0); + (void)dsi_display_sysfs_deinit(display); (void)dsi_display_debugfs_deinit(display); mutex_unlock(&display->display_lock); @@ -3922,122 +4205,168 @@ static struct platform_driver dsi_display_driver = { }, }; -int dsi_display_dev_probe(struct platform_device *pdev) +static void dsi_display_setup(struct dsi_display *display) +{ + struct platform_device *pdev = display->pdev; + + /* use default topology of every mode if not overridden */ + display->cmdline_topology = NO_OVERRIDE; + display->cmdline_timing = 0; + + if (boot_displays[DSI_PRIMARY].boot_disp_en) { + dsi_display_name_compare(pdev->dev.of_node, + display->name, DSI_PRIMARY); + + dsi_display_parse_cmdline_topology(display, DSI_PRIMARY); + boot_displays[DSI_PRIMARY].node = pdev->dev.of_node; + boot_displays[DSI_PRIMARY].disp = display; + } + + if (boot_displays[DSI_SECONDARY].boot_disp_en) { + boot_displays[DSI_SECONDARY].node = pdev->dev.of_node; + boot_displays[DSI_SECONDARY].disp = display; + + if (validate_dsi_display_selection()) + dsi_display_parse_cmdline_topology(display, + DSI_SECONDARY); + else + boot_displays[DSI_SECONDARY].boot_disp_en = false; + + } + + display->display_type = of_get_property(display->disp_node, + "qcom,display-type", NULL); + if (!display->display_type) + display->display_type = "unknown"; +} + +static int dsi_display_init(struct dsi_display *display, + struct platform_device *pdev) { int rc = 0; - struct dsi_display *display; - static bool display_from_cmdline, boot_displays_parsed; - static bool comp_add_success; - static struct device_node *primary_np, *secondary_np; + + mutex_init(&display->display_lock); + + rc = _dsi_display_dev_init(display); + if (rc) { + pr_err("device init failed, rc=%d\n", rc); + goto end; + } + + rc = component_add(&pdev->dev, &dsi_display_comp_ops); + if (rc) + pr_err("component add failed, rc=%d\n", rc); + + pr_debug("component add success: %s\n", display->name); +end: + return rc; +} + +static void dsi_display_firmware_display(const struct firmware *fw, + void *context) +{ + struct dsi_display *display = context; + struct platform_device *pdev = display->pdev; + + if (fw) { + pr_debug("reading data from firmware, size=%zd\n", + fw->size); + + display->fw = fw; + display->name = "dsi_firmware_display"; + } + + dsi_display_setup(display); + + if (dsi_display_init(display, pdev)) + return; + + pr_debug("success\n"); +} + +int dsi_display_dev_probe(struct platform_device *pdev) +{ + struct dsi_display *display = NULL; + struct device_node *node = NULL, *disp_node = NULL; + const char *name = NULL; + const char *disp_list = "qcom,dsi-display-list"; + const char *disp_active = "qcom,dsi-display-active"; + int i, count, rc = 0; + bool firm_req = false; if (!pdev || !pdev->dev.of_node) { pr_err("pdev not found\n"); - return -ENODEV; + rc = -ENODEV; + goto end; } display = devm_kzalloc(&pdev->dev, sizeof(*display), GFP_KERNEL); - if (!display) - return -ENOMEM; + if (!display) { + rc = -ENOMEM; + goto end; + } - display->name = of_get_property(pdev->dev.of_node, "label", NULL); - if (!display->name) - display->name = "unknown"; + if (boot_displays[DSI_PRIMARY].boot_disp_en) + display_from_cmdline = true; - if (!boot_displays_parsed) { - boot_displays[DSI_PRIMARY].boot_disp_en = false; - boot_displays[DSI_SECONDARY].boot_disp_en = false; - if (dsi_display_parse_boot_display_selection()) - pr_debug("Display Boot param not valid/available\n"); + node = pdev->dev.of_node; + count = of_count_phandle_with_args(node, disp_list, NULL); - boot_displays_parsed = true; - } + for (i = 0; i < count; i++) { + struct device_node *np; - /* use default topology of every mode if not overridden */ - display->cmdline_topology = NO_OVERRIDE; - display->cmdline_timing = 0; + np = of_parse_phandle(node, disp_list, i); + name = of_get_property(np, "label", NULL); - if ((!display_from_cmdline) && - (boot_displays[DSI_PRIMARY].boot_disp_en)) { - display->is_active = dsi_display_name_compare(pdev->dev.of_node, - display->name, DSI_PRIMARY); - if (display->is_active) { - if (comp_add_success) { - (void)_dsi_display_dev_deinit(main_display); - component_del(&main_display->pdev->dev, - &dsi_display_comp_ops); - mutex_lock(&dsi_display_list_lock); - list_del(&main_display->list); - mutex_unlock(&dsi_display_list_lock); - comp_add_success = false; - default_active_node = NULL; - pr_debug("removed the existing comp ops\n"); + if (display_from_cmdline) { + if (name && !strcmp(boot_displays[0].name, name)) { + disp_node = np; + break; + } + } else { + if (of_property_read_bool(np, disp_active)) { + disp_node = np; + + if (IS_ENABLED(CONFIG_DSI_PARSER)) + firm_req = !request_firmware_nowait( + THIS_MODULE, 1, "dsi_prop", + &pdev->dev, GFP_KERNEL, display, + dsi_display_firmware_display); + break; } - /* - * Need to add component for - * the secondary DSI display - * when more than one DSI display - * is supported. - */ - pr_debug("cmdline primary dsi: %s\n", - display->name); - display_from_cmdline = true; - dsi_display_parse_cmdline_topology(display, - DSI_PRIMARY); - primary_np = pdev->dev.of_node; } + + of_node_put(np); } - if (boot_displays[DSI_SECONDARY].boot_disp_en - && !secondary_np - && dsi_display_name_compare(pdev->dev.of_node, - display->name, DSI_SECONDARY)) { - pr_debug("cmdline secondary dsi: %s\n", - display->name); - secondary_np = pdev->dev.of_node; - if (primary_np) { - if (validate_dsi_display_selection()) { - display->is_active = true; - dsi_display_parse_cmdline_topology( - display, DSI_SECONDARY); - } else { - boot_displays[DSI_SECONDARY] - .boot_disp_en = false; - } - } + if (!name || !disp_node) { + pr_err("display node not found\n"); + rc = -EINVAL; + goto end; } - display->display_type = of_get_property(pdev->dev.of_node, - "qcom,display-type", NULL); - if (!display->display_type) - display->display_type = "unknown"; - mutex_init(&display->display_lock); + /* decrement ref count */ + of_node_put(disp_node); + + display->disp_node = disp_node; + display->name = name; display->pdev = pdev; - platform_set_drvdata(pdev, display); - mutex_lock(&dsi_display_list_lock); - list_add(&display->list, &dsi_display_list); - mutex_unlock(&dsi_display_list_lock); - if (!display_from_cmdline) - display->is_active = of_property_read_bool(pdev->dev.of_node, - "qcom,dsi-display-active"); + platform_set_drvdata(pdev, display); + default_display = display; - if (display->is_active) { - main_display = display; - rc = _dsi_display_dev_init(display); - if (rc) { - pr_err("device init failed, rc=%d\n", rc); - return rc; - } + /* initialize display in firmware callback */ + if (!firm_req) { + dsi_display_setup(display); - rc = component_add(&pdev->dev, &dsi_display_comp_ops); + rc = dsi_display_init(display, pdev); if (rc) - pr_err("component add failed, rc=%d\n", rc); - - comp_add_success = true; - pr_debug("Component_add success: %s\n", display->name); - if (!display_from_cmdline) - default_active_node = pdev->dev.of_node; + goto end; } + + return 0; +end: + devm_kfree(&pdev->dev, display); return rc; } @@ -4045,7 +4374,6 @@ int dsi_display_dev_remove(struct platform_device *pdev) { int rc = 0; struct dsi_display *display; - struct dsi_display *pos, *tmp; if (!pdev) { pr_err("Invalid device\n"); @@ -4056,15 +4384,6 @@ int dsi_display_dev_remove(struct platform_device *pdev) (void)_dsi_display_dev_deinit(display); - mutex_lock(&dsi_display_list_lock); - list_for_each_entry_safe(pos, tmp, &dsi_display_list, list) { - if (pos == display) { - list_del(&display->list); - break; - } - } - mutex_unlock(&dsi_display_list_lock); - platform_set_drvdata(pdev, NULL); devm_kfree(&pdev->dev, display); return rc; @@ -4072,22 +4391,21 @@ int dsi_display_dev_remove(struct platform_device *pdev) int dsi_display_get_num_of_displays(void) { - int count = 0; - struct dsi_display *display; + int count = 0, i; - mutex_lock(&dsi_display_list_lock); + if (!display_from_cmdline) + return 1; - list_for_each_entry(display, &dsi_display_list, list) { - count++; + for (i = 0; i < MAX_DSI_ACTIVE_DISPLAY; i++) { + if (boot_displays[i].boot_disp_en) + count++; } - mutex_unlock(&dsi_display_list_lock); return count; } int dsi_display_get_active_displays(void **display_array, u32 max_display_count) { - struct dsi_display *pos; int i = 0; if (!display_array || !max_display_count) { @@ -4096,42 +4414,16 @@ int dsi_display_get_active_displays(void **display_array, u32 max_display_count) return 0; } - mutex_lock(&dsi_display_list_lock); - - list_for_each_entry(pos, &dsi_display_list, list) { - if (i >= max_display_count) { - pr_err("capping display count to %d\n", i); - break; - } - if (pos->is_active) - display_array[i++] = pos; + if (!display_from_cmdline) { + display_array[0] = default_display; + return 1; } - mutex_unlock(&dsi_display_list_lock); - return i; -} - -struct dsi_display *dsi_display_get_display_by_name(const char *name) -{ - struct dsi_display *display = NULL, *pos; - - mutex_lock(&dsi_display_list_lock); - - list_for_each_entry(pos, &dsi_display_list, list) { - if (!strcmp(name, pos->name)) - display = pos; + for (i = 0; i < max_display_count; i++) { + if (boot_displays[i].boot_disp_en) + display_array[i] = boot_displays[i].disp; } - - mutex_unlock(&dsi_display_list_lock); - - return display; -} - -void dsi_display_set_active_state(struct dsi_display *display, bool is_active) -{ - mutex_lock(&display->display_lock); - display->is_active = is_active; - mutex_unlock(&display->display_lock); + return i; } int dsi_display_drm_bridge_init(struct dsi_display *display, @@ -4239,7 +4531,8 @@ int dsi_display_get_info(struct drm_connector *connector, case DSI_OP_CMD_MODE: info->capabilities |= MSM_DISPLAY_CAP_CMD_MODE; info->is_te_using_watchdog_timer = - display->panel->te_using_watchdog_timer; + display->panel->te_using_watchdog_timer | + display->sw_te_using_wd; break; default: pr_err("unknwown dsi panel mode %d\n", @@ -4634,6 +4927,10 @@ int dsi_display_set_mode(struct dsi_display *display, adj_mode = *mode; adjust_timing_by_ctrl_count(display, &adj_mode); + /*For dynamic DSI setting, use specified clock rate */ + if (display->cached_clk_rate > 0) + adj_mode.priv_info->clk_rate_hz = display->cached_clk_rate; + rc = dsi_display_validate_mode_set(display, &adj_mode, flags); if (rc) { pr_err("[%s] mode cannot be set\n", display->name); @@ -5070,18 +5367,19 @@ int dsi_display_prepare(struct dsi_display *display) goto error_host_engine_off; } - rc = dsi_display_soft_reset(display); - if (rc) { - pr_err("[%s] failed soft reset, rc=%d\n", display->name, rc); - goto error_ctrl_link_off; - } - if (!display->is_cont_splash_enabled) { /* - * For continuous splash usecase we skip panel - * prepare since the pnael is already in - * active state and panel on commands are not needed + * For continuous splash usecase, skip panel prepare and + * ctl reset since the pnael and ctrl is already in active + * state and panel on commands are not needed */ + rc = dsi_display_soft_reset(display); + if (rc) { + pr_err("[%s] failed soft reset, rc=%d\n", + display->name, rc); + goto error_ctrl_link_off; + } + rc = dsi_panel_prepare(display->panel); if (rc) { pr_err("[%s] panel prepare failed, rc=%d\n", @@ -5225,6 +5523,7 @@ int dsi_display_pre_kickoff(struct drm_connector *connector, struct msm_display_kickoff_params *params) { int rc = 0; + int i; /* check and setup MISR */ if (display->misr_enable) @@ -5232,6 +5531,41 @@ int dsi_display_pre_kickoff(struct drm_connector *connector, rc = dsi_display_set_roi(display, params->rois); + /* dynamic DSI clock setting */ + if (atomic_read(&display->clkrate_change_pending)) { + mutex_lock(&display->display_lock); + /* + * acquire panel_lock to make sure no commands are in progress + */ + dsi_panel_acquire_panel_lock(display->panel); + + /* + * Wait for DSI command engine not to be busy sending data + * from display engine. + * If waiting fails, return "rc" instead of below "ret" so as + * not to impact DRM commit. The clock updating would be + * deferred to the next DRM commit. + */ + for (i = 0; i < display->ctrl_count; i++) { + struct dsi_ctrl *ctrl = display->ctrl[i].ctrl; + int ret = 0; + + ret = dsi_ctrl_wait_for_cmd_mode_mdp_idle(ctrl); + if (ret) + goto wait_failure; + } + + /* + * Don't check the return value so as not to impact DRM commit + * when error occurs. + */ + (void)dsi_display_force_update_dsi_clk(display); +wait_failure: + /* release panel_lock */ + dsi_panel_release_panel_lock(display->panel); + mutex_unlock(&display->display_lock); + } + return rc; } @@ -5553,6 +5887,9 @@ static int __init dsi_display_register(void) { dsi_phy_drv_register(); dsi_ctrl_drv_register(); + + dsi_display_parse_boot_display_selection(); + return platform_driver_register(&dsi_display_driver); } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h index 1a184d7ddf64d78383cce1c59fbe5f2ca283002c..faeb58bb96f59f9216d3d503277bec72e02cdbee 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,7 @@ struct dsi_display_boot_param { int length; struct device_node *node; int cmdline_topology; + void *disp; }; /** @@ -131,6 +133,7 @@ struct dsi_display_clk_info { * @list: List pointer. * @is_active: Is display active. * @is_cont_splash_enabled: Is continuous splash enabled + * @sw_te_using_wd: Is software te enabled * @display_lock: Mutex for dsi_display interface. * @ctrl_count: Number of DSI interfaces required by panel. * @ctrl: Controller information for DSI display. @@ -142,6 +145,8 @@ struct dsi_display_clk_info { * index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array. * @cmd_master_idx: The master controller for sending DSI commands to panel. * @video_master_idx: The master controller for enabling video engine. + * @cached_clk_rate: The cached DSI clock rate set dynamically by sysfs. + * @clkrate_change_pending: Flag indicating the pending DSI clock re-enabling. * @clock_info: Clock sourcing for DSI display. * @config: DSI host configuration information. * @lane_map: Lane mapping between DSI host and Panel. @@ -170,8 +175,8 @@ struct dsi_display { const char *name; const char *display_type; struct list_head list; - bool is_active; bool is_cont_splash_enabled; + bool sw_te_using_wd; struct mutex display_lock; u32 ctrl_count; @@ -179,7 +184,9 @@ struct dsi_display { /* panel info */ struct dsi_panel *panel; + struct device_node *disp_node; struct device_node *panel_of; + struct device_node *parser_node; struct dsi_display_mode *modes; @@ -188,6 +195,10 @@ struct dsi_display { u32 cmd_master_idx; u32 video_master_idx; + /* dynamic DSI clock info*/ + u32 cached_clk_rate; + atomic_t clkrate_change_pending; + struct dsi_display_clk_info clock_info; struct dsi_host_config config; struct dsi_lane_map lane_map; @@ -225,6 +236,10 @@ struct dsi_display { struct work_struct fifo_underflow_work; struct work_struct fifo_overflow_work; struct work_struct lp_rx_timeout_work; + + /* firmware panel data */ + const struct firmware *fw; + void *parser; }; int dsi_display_dev_probe(struct platform_device *pdev); @@ -247,14 +262,6 @@ int dsi_display_get_num_of_displays(void); int dsi_display_get_active_displays(void **display_array, u32 max_display_count); -/** - * dsi_display_get_boot_display()- get DSI boot display name - * @index: index of display selection - * - * Return: returns the display node pointer - */ -struct device_node *dsi_display_get_boot_display(int index); - /** * dsi_display_get_display_by_name()- finds display by name * @name: name of the display. diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c index 4021b8be0e12666886071974c7f49708420c3f2b..58e8782ce966a12e7f41712399e6d6299dce0ee4 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c @@ -21,6 +21,7 @@ #include "dsi_panel.h" #include "dsi_ctrl_hw.h" +#include "dsi_parser.h" /** * topology is currently defined by a set of following 3 values: @@ -680,42 +681,45 @@ static int dsi_panel_bl_unregister(struct dsi_panel *panel) error: return rc; } + static int dsi_panel_parse_timing(struct dsi_mode_info *mode, - struct device_node *of_node) + struct dsi_parser_utils *utils) { int rc = 0; - u64 tmp64; + u64 tmp64 = 0; struct dsi_display_mode *display_mode; display_mode = container_of(mode, struct dsi_display_mode, timing); - rc = of_property_read_u64(of_node, + rc = utils->read_u64(utils->data, "qcom,mdss-dsi-panel-clockrate", &tmp64); if (rc == -EOVERFLOW) { tmp64 = 0; - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-panel-clockrate", (u32 *)&tmp64); } mode->clk_rate_hz = !rc ? tmp64 : 0; display_mode->priv_info->clk_rate_hz = mode->clk_rate_hz; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-framerate", - &mode->refresh_rate); + rc = utils->read_u32(utils->data, + "qcom,mdss-dsi-panel-framerate", + &mode->refresh_rate); if (rc) { pr_err("failed to read qcom,mdss-dsi-panel-framerate, rc=%d\n", rc); goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-width", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-panel-width", &mode->h_active); if (rc) { pr_err("failed to read qcom,mdss-dsi-panel-width, rc=%d\n", rc); goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-h-front-porch", + rc = utils->read_u32(utils->data, + "qcom,mdss-dsi-h-front-porch", &mode->h_front_porch); if (rc) { pr_err("failed to read qcom,mdss-dsi-h-front-porch, rc=%d\n", @@ -723,7 +727,8 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-h-back-porch", + rc = utils->read_u32(utils->data, + "qcom,mdss-dsi-h-back-porch", &mode->h_back_porch); if (rc) { pr_err("failed to read qcom,mdss-dsi-h-back-porch, rc=%d\n", @@ -731,7 +736,8 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-h-pulse-width", + rc = utils->read_u32(utils->data, + "qcom,mdss-dsi-h-pulse-width", &mode->h_sync_width); if (rc) { pr_err("failed to read qcom,mdss-dsi-h-pulse-width, rc=%d\n", @@ -739,7 +745,7 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-h-sync-skew", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-h-sync-skew", &mode->h_skew); if (rc) pr_err("qcom,mdss-dsi-h-sync-skew is not defined, rc=%d\n", rc); @@ -748,7 +754,7 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, mode->h_active, mode->h_front_porch, mode->h_back_porch, mode->h_sync_width); - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-height", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-panel-height", &mode->v_active); if (rc) { pr_err("failed to read qcom,mdss-dsi-panel-height, rc=%d\n", @@ -756,7 +762,7 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-v-back-porch", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-v-back-porch", &mode->v_back_porch); if (rc) { pr_err("failed to read qcom,mdss-dsi-v-back-porch, rc=%d\n", @@ -764,7 +770,7 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-v-front-porch", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-v-front-porch", &mode->v_front_porch); if (rc) { pr_err("failed to read qcom,mdss-dsi-v-back-porch, rc=%d\n", @@ -772,7 +778,7 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-v-pulse-width", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-v-pulse-width", &mode->v_sync_width); if (rc) { pr_err("failed to read qcom,mdss-dsi-v-pulse-width, rc=%d\n", @@ -788,7 +794,7 @@ static int dsi_panel_parse_timing(struct dsi_mode_info *mode, } static int dsi_panel_parse_pixel_format(struct dsi_host_common_cfg *host, - struct device_node *of_node, + struct dsi_parser_utils *utils, const char *name) { int rc = 0; @@ -796,7 +802,7 @@ static int dsi_panel_parse_pixel_format(struct dsi_host_common_cfg *host, enum dsi_pixel_format fmt; const char *packing; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-bpp", &bpp); + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-bpp", &bpp); if (rc) { pr_err("[%s] failed to read qcom,mdss-dsi-bpp, rc=%d\n", name, rc); @@ -826,7 +832,7 @@ static int dsi_panel_parse_pixel_format(struct dsi_host_common_cfg *host, } if (fmt == DSI_PIXEL_FORMAT_RGB666) { - packing = of_get_property(of_node, + packing = utils->get_property(utils->data, "qcom,mdss-dsi-pixel-packing", NULL); if (packing && !strcmp(packing, "loose")) @@ -838,25 +844,25 @@ static int dsi_panel_parse_pixel_format(struct dsi_host_common_cfg *host, } static int dsi_panel_parse_lane_states(struct dsi_host_common_cfg *host, - struct device_node *of_node, + struct dsi_parser_utils *utils, const char *name) { int rc = 0; bool lane_enabled; - lane_enabled = of_property_read_bool(of_node, + lane_enabled = utils->read_bool(utils->data, "qcom,mdss-dsi-lane-0-state"); host->data_lanes |= (lane_enabled ? DSI_DATA_LANE_0 : 0); - lane_enabled = of_property_read_bool(of_node, + lane_enabled = utils->read_bool(utils->data, "qcom,mdss-dsi-lane-1-state"); host->data_lanes |= (lane_enabled ? DSI_DATA_LANE_1 : 0); - lane_enabled = of_property_read_bool(of_node, + lane_enabled = utils->read_bool(utils->data, "qcom,mdss-dsi-lane-2-state"); host->data_lanes |= (lane_enabled ? DSI_DATA_LANE_2 : 0); - lane_enabled = of_property_read_bool(of_node, + lane_enabled = utils->read_bool(utils->data, "qcom,mdss-dsi-lane-3-state"); host->data_lanes |= (lane_enabled ? DSI_DATA_LANE_3 : 0); @@ -869,13 +875,14 @@ static int dsi_panel_parse_lane_states(struct dsi_host_common_cfg *host, } static int dsi_panel_parse_color_swap(struct dsi_host_common_cfg *host, - struct device_node *of_node, + struct dsi_parser_utils *utils, const char *name) { int rc = 0; const char *swap_mode; - swap_mode = of_get_property(of_node, "qcom,mdss-dsi-color-order", NULL); + swap_mode = utils->get_property(utils->data, + "qcom,mdss-dsi-color-order", NULL); if (swap_mode) { if (!strcmp(swap_mode, "rgb_swap_rgb")) { host->swap_mode = DSI_COLOR_SWAP_RGB; @@ -905,13 +912,14 @@ static int dsi_panel_parse_color_swap(struct dsi_host_common_cfg *host, } static int dsi_panel_parse_triggers(struct dsi_host_common_cfg *host, - struct device_node *of_node, + struct dsi_parser_utils *utils, const char *name) { const char *trig; int rc = 0; - trig = of_get_property(of_node, "qcom,mdss-dsi-mdp-trigger", NULL); + trig = utils->get_property(utils->data, + "qcom,mdss-dsi-mdp-trigger", NULL); if (trig) { if (!strcmp(trig, "none")) { host->mdp_cmd_trigger = DSI_TRIGGER_NONE; @@ -933,7 +941,8 @@ static int dsi_panel_parse_triggers(struct dsi_host_common_cfg *host, host->mdp_cmd_trigger = DSI_TRIGGER_SW; } - trig = of_get_property(of_node, "qcom,mdss-dsi-dma-trigger", NULL); + trig = utils->get_property(utils->data, + "qcom,mdss-dsi-dma-trigger", NULL); if (trig) { if (!strcmp(trig, "none")) { host->dma_cmd_trigger = DSI_TRIGGER_NONE; @@ -956,7 +965,7 @@ static int dsi_panel_parse_triggers(struct dsi_host_common_cfg *host, host->dma_cmd_trigger = DSI_TRIGGER_SW; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-te-pin-select", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-te-pin-select", &host->te_mode); if (rc) { pr_warn("[%s] fallback to default te-pin-select\n", name); @@ -968,46 +977,40 @@ static int dsi_panel_parse_triggers(struct dsi_host_common_cfg *host, } static int dsi_panel_parse_misc_host_config(struct dsi_host_common_cfg *host, - struct device_node *of_node, + struct dsi_parser_utils *utils, const char *name) { u32 val = 0; int rc = 0; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-t-clk-post", &val); - if (rc) { - pr_debug("[%s] Fallback to default t_clk_post value\n", name); - host->t_clk_post = 0x03; - } else { + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-t-clk-post", &val); + if (!rc) { host->t_clk_post = val; pr_debug("[%s] t_clk_post = %d\n", name, val); } val = 0; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-t-clk-pre", &val); - if (rc) { - pr_debug("[%s] Fallback to default t_clk_pre value\n", name); - host->t_clk_pre = 0x24; - } else { + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-t-clk-pre", &val); + if (!rc) { host->t_clk_pre = val; pr_debug("[%s] t_clk_pre = %d\n", name, val); } - host->ignore_rx_eot = of_property_read_bool(of_node, + host->ignore_rx_eot = utils->read_bool(utils->data, "qcom,mdss-dsi-rx-eot-ignore"); - host->append_tx_eot = of_property_read_bool(of_node, + host->append_tx_eot = utils->read_bool(utils->data, "qcom,mdss-dsi-tx-eot-append"); return 0; } -static int dsi_panel_parse_host_config(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_host_config(struct dsi_panel *panel) { int rc = 0; + struct dsi_parser_utils *utils = &panel->utils; - rc = dsi_panel_parse_pixel_format(&panel->host_config, of_node, + rc = dsi_panel_parse_pixel_format(&panel->host_config, utils, panel->name); if (rc) { pr_err("[%s] failed to get pixel format, rc=%d\n", @@ -1015,7 +1018,7 @@ static int dsi_panel_parse_host_config(struct dsi_panel *panel, goto error; } - rc = dsi_panel_parse_lane_states(&panel->host_config, of_node, + rc = dsi_panel_parse_lane_states(&panel->host_config, utils, panel->name); if (rc) { pr_err("[%s] failed to parse lane states, rc=%d\n", @@ -1023,7 +1026,7 @@ static int dsi_panel_parse_host_config(struct dsi_panel *panel, goto error; } - rc = dsi_panel_parse_color_swap(&panel->host_config, of_node, + rc = dsi_panel_parse_color_swap(&panel->host_config, utils, panel->name); if (rc) { pr_err("[%s] failed to parse color swap config, rc=%d\n", @@ -1031,7 +1034,7 @@ static int dsi_panel_parse_host_config(struct dsi_panel *panel, goto error; } - rc = dsi_panel_parse_triggers(&panel->host_config, of_node, + rc = dsi_panel_parse_triggers(&panel->host_config, utils, panel->name); if (rc) { pr_err("[%s] failed to parse triggers, rc=%d\n", @@ -1039,7 +1042,7 @@ static int dsi_panel_parse_host_config(struct dsi_panel *panel, goto error; } - rc = dsi_panel_parse_misc_host_config(&panel->host_config, of_node, + rc = dsi_panel_parse_misc_host_config(&panel->host_config, utils, panel->name); if (rc) { pr_err("[%s] failed to parse misc host config, rc=%d\n", @@ -1051,24 +1054,25 @@ static int dsi_panel_parse_host_config(struct dsi_panel *panel, return rc; } -static int dsi_panel_parse_dfps_caps(struct dsi_dfps_capabilities *dfps_caps, - struct device_node *of_node, - const char *name) +static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel) { int rc = 0; bool supported = false; + struct dsi_dfps_capabilities *dfps_caps = &panel->dfps_caps; + struct dsi_parser_utils *utils = &panel->utils; + const char *name = panel->name; const char *type; u32 val = 0; - supported = of_property_read_bool(of_node, - "qcom,mdss-dsi-pan-enable-dynamic-fps"); + supported = utils->read_bool(utils->data, + "qcom,mdss-dsi-pan-enable-dynamic-fps"); if (!supported) { pr_debug("[%s] DFPS is not supported\n", name); dfps_caps->dfps_support = false; } else { - type = of_get_property(of_node, + type = utils->get_property(utils->data, "qcom,mdss-dsi-pan-fps-update", NULL); if (!type) { @@ -1089,7 +1093,7 @@ static int dsi_panel_parse_dfps_caps(struct dsi_dfps_capabilities *dfps_caps, goto error; } - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-min-refresh-rate", &val); if (rc) { @@ -1099,12 +1103,12 @@ static int dsi_panel_parse_dfps_caps(struct dsi_dfps_capabilities *dfps_caps, } dfps_caps->min_refresh_rate = val; - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-max-refresh-rate", &val); if (rc) { pr_debug("[%s] Using default refresh rate\n", name); - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-panel-framerate", &val); if (rc) { @@ -1133,7 +1137,7 @@ static int dsi_panel_parse_dfps_caps(struct dsi_dfps_capabilities *dfps_caps, } static int dsi_panel_parse_video_host_config(struct dsi_video_engine_cfg *cfg, - struct device_node *of_node, + struct dsi_parser_utils *utils, const char *name) { int rc = 0; @@ -1141,7 +1145,7 @@ static int dsi_panel_parse_video_host_config(struct dsi_video_engine_cfg *cfg, u32 vc_id = 0; u32 val = 0; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-h-sync-pulse", &val); + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-h-sync-pulse", &val); if (rc) { pr_debug("[%s] fallback to default h-sync-pulse\n", name); cfg->pulse_mode_hsa_he = false; @@ -1156,25 +1160,25 @@ static int dsi_panel_parse_video_host_config(struct dsi_video_engine_cfg *cfg, goto error; } - cfg->hfp_lp11_en = of_property_read_bool(of_node, + cfg->hfp_lp11_en = utils->read_bool(utils->data, "qcom,mdss-dsi-hfp-power-mode"); - cfg->hbp_lp11_en = of_property_read_bool(of_node, + cfg->hbp_lp11_en = utils->read_bool(utils->data, "qcom,mdss-dsi-hbp-power-mode"); - cfg->hsa_lp11_en = of_property_read_bool(of_node, + cfg->hsa_lp11_en = utils->read_bool(utils->data, "qcom,mdss-dsi-hsa-power-mode"); - cfg->last_line_interleave_en = of_property_read_bool(of_node, + cfg->last_line_interleave_en = utils->read_bool(utils->data, "qcom,mdss-dsi-last-line-interleave"); - cfg->eof_bllp_lp11_en = of_property_read_bool(of_node, + cfg->eof_bllp_lp11_en = utils->read_bool(utils->data, "qcom,mdss-dsi-bllp-eof-power-mode"); - cfg->bllp_lp11_en = of_property_read_bool(of_node, + cfg->bllp_lp11_en = utils->read_bool(utils->data, "qcom,mdss-dsi-bllp-power-mode"); - traffic_mode = of_get_property(of_node, + traffic_mode = utils->get_property(utils->data, "qcom,mdss-dsi-traffic-mode", NULL); if (!traffic_mode) { @@ -1193,7 +1197,7 @@ static int dsi_panel_parse_video_host_config(struct dsi_video_engine_cfg *cfg, goto error; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-virtual-channel-id", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-virtual-channel-id", &vc_id); if (rc) { pr_debug("[%s] Fallback to default vc id\n", name); @@ -1207,13 +1211,13 @@ static int dsi_panel_parse_video_host_config(struct dsi_video_engine_cfg *cfg, } static int dsi_panel_parse_cmd_host_config(struct dsi_cmd_engine_cfg *cfg, - struct device_node *of_node, + struct dsi_parser_utils *utils, const char *name) { u32 val = 0; int rc = 0; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-wr-mem-start", &val); + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-wr-mem-start", &val); if (rc) { pr_debug("[%s] Fallback to default wr-mem-start\n", name); cfg->wr_mem_start = 0x2C; @@ -1222,7 +1226,7 @@ static int dsi_panel_parse_cmd_host_config(struct dsi_cmd_engine_cfg *cfg, } val = 0; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-wr-mem-continue", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-wr-mem-continue", &val); if (rc) { pr_debug("[%s] Fallback to default wr-mem-continue\n", name); @@ -1235,7 +1239,7 @@ static int dsi_panel_parse_cmd_host_config(struct dsi_cmd_engine_cfg *cfg, cfg->max_cmd_packets_interleave = 0; val = 0; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-te-dcs-command", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-te-dcs-command", &val); if (rc) { pr_debug("[%s] fallback to default te-dcs-cmd\n", name); @@ -1251,7 +1255,7 @@ static int dsi_panel_parse_cmd_host_config(struct dsi_cmd_engine_cfg *cfg, goto error; } - if (of_property_read_u32(of_node, "qcom,mdss-mdp-transfer-time-us", + if (utils->read_u32(utils->data, "qcom,mdss-mdp-transfer-time-us", &val)) { pr_debug("[%s] Fallback to default transfer-time-us\n", name); cfg->mdp_transfer_time_us = DEFAULT_MDP_TRANSFER_TIME; @@ -1263,14 +1267,15 @@ static int dsi_panel_parse_cmd_host_config(struct dsi_cmd_engine_cfg *cfg, return rc; } -static int dsi_panel_parse_panel_mode(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_panel_mode(struct dsi_panel *panel) { int rc = 0; + struct dsi_parser_utils *utils = &panel->utils; enum dsi_op_mode panel_mode; const char *mode; - mode = of_get_property(of_node, "qcom,mdss-dsi-panel-type", NULL); + mode = utils->get_property(utils->data, + "qcom,mdss-dsi-panel-type", NULL); if (!mode) { pr_debug("[%s] Fallback to default panel mode\n", panel->name); panel_mode = DSI_OP_VIDEO_MODE; @@ -1286,7 +1291,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel, if (panel_mode == DSI_OP_VIDEO_MODE) { rc = dsi_panel_parse_video_host_config(&panel->video_config, - of_node, + utils, panel->name); if (rc) { pr_err("[%s] Failed to parse video host cfg, rc=%d\n", @@ -1297,7 +1302,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel, if (panel_mode == DSI_OP_CMD_MODE) { rc = dsi_panel_parse_cmd_host_config(&panel->cmd_config, - of_node, + utils, panel->name); if (rc) { pr_err("[%s] Failed to parse cmd host config, rc=%d\n", @@ -1311,17 +1316,17 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel, return rc; } -static int dsi_panel_parse_phy_props(struct dsi_panel_phy_props *props, - struct device_node *of_node, - const char *name) +static int dsi_panel_parse_phy_props(struct dsi_panel *panel) { int rc = 0; u32 val = 0; const char *str; + struct dsi_panel_phy_props *props = &panel->phy_props; + struct dsi_parser_utils *utils = &panel->utils; + const char *name = panel->name; - rc = of_property_read_u32(of_node, - "qcom,mdss-pan-physical-width-dimension", - &val); + rc = utils->read_u32(utils->data, + "qcom,mdss-pan-physical-width-dimension", &val); if (rc) { pr_debug("[%s] Physical panel width is not defined\n", name); props->panel_width_mm = 0; @@ -1330,7 +1335,7 @@ static int dsi_panel_parse_phy_props(struct dsi_panel_phy_props *props, props->panel_width_mm = val; } - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,mdss-pan-physical-height-dimension", &val); if (rc) { @@ -1341,7 +1346,8 @@ static int dsi_panel_parse_phy_props(struct dsi_panel_phy_props *props, props->panel_height_mm = val; } - str = of_get_property(of_node, "qcom,mdss-dsi-panel-orientation", NULL); + str = utils->get_property(utils->data, + "qcom,mdss-dsi-panel-orientation", NULL); if (!str) { props->rotation = DSI_PANEL_ROTATE_NONE; } else if (!strcmp(str, "180")) { @@ -1504,7 +1510,7 @@ static int dsi_panel_alloc_cmd_packets(struct dsi_panel_cmd_set *cmd, static int dsi_panel_parse_cmd_sets_sub(struct dsi_panel_cmd_set *cmd, enum dsi_cmd_set_type type, - struct device_node *of_node) + struct dsi_parser_utils *utils) { int rc = 0; u32 length = 0; @@ -1512,13 +1518,20 @@ static int dsi_panel_parse_cmd_sets_sub(struct dsi_panel_cmd_set *cmd, const char *state; u32 packet_count = 0; - data = of_get_property(of_node, cmd_set_prop_map[type], &length); + data = utils->get_property(utils->data, cmd_set_prop_map[type], + &length); if (!data) { pr_debug("%s commands not defined\n", cmd_set_prop_map[type]); rc = -ENOTSUPP; goto error; } + pr_debug("type=%d, name=%s, length=%d\n", type, + cmd_set_prop_map[type], length); + + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, + 8, 1, data, length, false); + rc = dsi_panel_get_cmd_pkt_count(data, length, &packet_count); if (rc) { pr_err("commands failed, rc=%d\n", rc); @@ -1540,7 +1553,7 @@ static int dsi_panel_parse_cmd_sets_sub(struct dsi_panel_cmd_set *cmd, goto error_free_mem; } - state = of_get_property(of_node, cmd_set_state_map[type], NULL); + state = utils->get_property(utils->data, cmd_set_state_map[type], NULL); if (!state || !strcmp(state, "dsi_lp_mode")) { cmd->state = DSI_CMD_SET_STATE_LP; } else if (!strcmp(state, "dsi_hs_mode")) { @@ -1562,7 +1575,7 @@ static int dsi_panel_parse_cmd_sets_sub(struct dsi_panel_cmd_set *cmd, static int dsi_panel_parse_cmd_sets( struct dsi_display_mode_priv_info *priv_info, - struct device_node *of_node) + struct dsi_parser_utils *utils) { int rc = 0; struct dsi_panel_cmd_set *set; @@ -1585,7 +1598,7 @@ static int dsi_panel_parse_cmd_sets( i, rc); set->state = DSI_CMD_SET_STATE_LP; } else { - rc = dsi_panel_parse_cmd_sets_sub(set, i, of_node); + rc = dsi_panel_parse_cmd_sets_sub(set, i, utils); if (rc) pr_debug("failed to parse set %d\n", i); } @@ -1595,8 +1608,7 @@ static int dsi_panel_parse_cmd_sets( return rc; } -static int dsi_panel_parse_reset_sequence(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_reset_sequence(struct dsi_panel *panel) { int rc = 0; int i; @@ -1605,9 +1617,11 @@ static int dsi_panel_parse_reset_sequence(struct dsi_panel *panel, u32 size = 0; u32 *arr_32 = NULL; const u32 *arr; + struct dsi_parser_utils *utils = &panel->utils; struct dsi_reset_seq *seq; - arr = of_get_property(of_node, "qcom,mdss-dsi-reset-sequence", &length); + arr = utils->get_property(utils->data, + "qcom,mdss-dsi-reset-sequence", &length); if (!arr) { pr_err("[%s] dsi-reset-sequence not found\n", panel->name); rc = -EINVAL; @@ -1631,7 +1645,7 @@ static int dsi_panel_parse_reset_sequence(struct dsi_panel *panel, goto error; } - rc = of_property_read_u32_array(of_node, "qcom,mdss-dsi-reset-sequence", + rc = utils->read_u32_array(utils->data, "qcom,mdss-dsi-reset-sequence", arr_32, length); if (rc) { pr_err("[%s] cannot read dso-reset-seqience\n", panel->name); @@ -1662,32 +1676,33 @@ static int dsi_panel_parse_reset_sequence(struct dsi_panel *panel, return rc; } -static int dsi_panel_parse_misc_features(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_misc_features(struct dsi_panel *panel) { + struct dsi_parser_utils *utils = &panel->utils; + panel->ulps_enabled = - of_property_read_bool(of_node, "qcom,ulps-enabled"); + utils->read_bool(utils->data, "qcom,ulps-enabled"); pr_info("%s: ulps feature %s\n", __func__, (panel->ulps_enabled ? "enabled" : "disabled")); panel->ulps_suspend_enabled = - of_property_read_bool(of_node, "qcom,suspend-ulps-enabled"); + utils->read_bool(utils->data, "qcom,suspend-ulps-enabled"); pr_info("%s: ulps during suspend feature %s", __func__, (panel->ulps_suspend_enabled ? "enabled" : "disabled")); - panel->te_using_watchdog_timer = of_property_read_bool(of_node, + panel->te_using_watchdog_timer = utils->read_bool(utils->data, "qcom,mdss-dsi-te-using-wd"); - panel->sync_broadcast_en = of_property_read_bool(of_node, + panel->sync_broadcast_en = utils->read_bool(utils->data, "qcom,cmd-sync-wait-broadcast"); return 0; } static int dsi_panel_parse_jitter_config( struct dsi_display_mode *mode, - struct device_node *of_node) + struct dsi_parser_utils *utils) { int rc; struct dsi_display_mode_priv_info *priv_info; @@ -1696,7 +1711,7 @@ static int dsi_panel_parse_jitter_config( priv_info = mode->priv_info; - rc = of_property_read_u32_array(of_node, "qcom,mdss-dsi-panel-jitter", + rc = utils->read_u32_array(utils->data, "qcom,mdss-dsi-panel-jitter", jitter, DEFAULT_PANEL_JITTER_ARRAY_SIZE); if (rc) { pr_debug("panel jitter not defined rc=%d\n", rc); @@ -1714,7 +1729,7 @@ static int dsi_panel_parse_jitter_config( priv_info->panel_jitter_denom = jitter[1]; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-prefill-lines", + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-panel-prefill-lines", &priv_info->panel_prefill_lines); if (rc) { pr_debug("panel prefill lines are not defined rc=%d\n", rc); @@ -1730,15 +1745,13 @@ static int dsi_panel_parse_jitter_config( return 0; } -static int dsi_panel_parse_power_cfg(struct device *parent, - struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_power_cfg(struct dsi_panel *panel) { int rc = 0; - rc = dsi_pwr_of_get_vreg_data(of_node, - &panel->power_info, - "qcom,panel-supply-entries"); + rc = dsi_pwr_of_get_vreg_data(&panel->utils, + &panel->power_info, + "qcom,panel-supply-entries"); if (rc) { pr_err("[%s] failed to parse vregs\n", panel->name); goto error; @@ -1748,42 +1761,43 @@ static int dsi_panel_parse_power_cfg(struct device *parent, return rc; } -static int dsi_panel_parse_gpios(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_gpios(struct dsi_panel *panel) { int rc = 0; const char *data; + struct dsi_parser_utils *utils = &panel->utils; - panel->reset_config.reset_gpio = of_get_named_gpio(of_node, - "qcom,platform-reset-gpio", - 0); + panel->reset_config.reset_gpio = utils->get_named_gpio(utils->data, + "qcom,platform-reset-gpio", 0); if (!gpio_is_valid(panel->reset_config.reset_gpio)) { pr_err("[%s] failed get reset gpio, rc=%d\n", panel->name, rc); rc = -EINVAL; goto error; } - panel->reset_config.disp_en_gpio = of_get_named_gpio(of_node, + panel->reset_config.disp_en_gpio = utils->get_named_gpio(utils->data, "qcom,5v-boost-gpio", 0); if (!gpio_is_valid(panel->reset_config.disp_en_gpio)) { pr_debug("[%s] 5v-boot-gpio is not set, rc=%d\n", panel->name, rc); - panel->reset_config.disp_en_gpio = of_get_named_gpio(of_node, - "qcom,platform-en-gpio", - 0); + panel->reset_config.disp_en_gpio = + utils->get_named_gpio(utils->data, + "qcom,platform-en-gpio", 0); if (!gpio_is_valid(panel->reset_config.disp_en_gpio)) { pr_debug("[%s] platform-en-gpio is not set, rc=%d\n", panel->name, rc); } } - panel->reset_config.lcd_mode_sel_gpio = of_get_named_gpio(of_node, - "qcom,panel-mode-gpio", 0); + panel->reset_config.lcd_mode_sel_gpio = utils->get_named_gpio( + utils->data, "qcom,panel-mode-gpio", 0); if (!gpio_is_valid(panel->reset_config.lcd_mode_sel_gpio)) pr_debug("%s:%d mode gpio not specified\n", __func__, __LINE__); - data = of_get_property(of_node, + pr_err("mode gpio=%d\n", panel->reset_config.lcd_mode_sel_gpio); + + data = utils->get_property(utils->data, "qcom,mdss-dsi-mode-sel-gpio-state", NULL); if (data) { if (!strcmp(data, "single_port")) @@ -1804,7 +1818,7 @@ static int dsi_panel_parse_gpios(struct dsi_panel *panel, } /* TODO: release memory */ - rc = dsi_panel_parse_reset_sequence(panel, of_node); + rc = dsi_panel_parse_reset_sequence(panel); if (rc) { pr_err("[%s] failed to parse reset sequence, rc=%d\n", panel->name, rc); @@ -1815,13 +1829,14 @@ static int dsi_panel_parse_gpios(struct dsi_panel *panel, return rc; } -static int dsi_panel_parse_bl_pwm_config(struct dsi_backlight_config *config, - struct device_node *of_node) +static int dsi_panel_parse_bl_pwm_config(struct dsi_panel *panel) { int rc = 0; u32 val; + struct dsi_backlight_config *config = &panel->bl_config; + struct dsi_parser_utils *utils = &panel->utils; - rc = of_property_read_u32(of_node, "qcom,dsi-bl-pmic-bank-select", + rc = utils->read_u32(utils->data, "qcom,dsi-bl-pmic-bank-select", &val); if (rc) { pr_err("bl-pmic-bank-select is not defined, rc=%d\n", rc); @@ -1829,7 +1844,7 @@ static int dsi_panel_parse_bl_pwm_config(struct dsi_backlight_config *config, } config->pwm_pmic_bank = val; - rc = of_property_read_u32(of_node, "qcom,dsi-bl-pmic-pwm-frequency", + rc = utils->read_u32(utils->data, "qcom,dsi-bl-pmic-pwm-frequency", &val); if (rc) { pr_err("bl-pmic-bank-select is not defined, rc=%d\n", rc); @@ -1837,10 +1852,10 @@ static int dsi_panel_parse_bl_pwm_config(struct dsi_backlight_config *config, } config->pwm_period_usecs = val; - config->pwm_pmi_control = of_property_read_bool(of_node, + config->pwm_pmi_control = utils->read_bool(utils->data, "qcom,mdss-dsi-bl-pwm-pmi"); - config->pwm_gpio = of_get_named_gpio(of_node, + config->pwm_gpio = utils->get_named_gpio(utils->data, "qcom,mdss-dsi-pwm-gpio", 0); if (!gpio_is_valid(config->pwm_gpio)) { @@ -1853,14 +1868,14 @@ static int dsi_panel_parse_bl_pwm_config(struct dsi_backlight_config *config, return rc; } -static int dsi_panel_parse_bl_config(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_bl_config(struct dsi_panel *panel) { int rc = 0; - const char *bl_type; u32 val = 0; + const char *bl_type; + struct dsi_parser_utils *utils = &panel->utils; - bl_type = of_get_property(of_node, + bl_type = utils->get_property(utils->data, "qcom,mdss-dsi-bl-pmic-control-type", NULL); if (!bl_type) { @@ -1880,7 +1895,7 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel, panel->bl_config.bl_scale = MAX_BL_SCALE_LEVEL; panel->bl_config.bl_scale_ad = MAX_AD_BL_SCALE_LEVEL; - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-bl-min-level", &val); + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-bl-min-level", &val); if (rc) { pr_debug("[%s] bl-min-level unspecified, defaulting to zero\n", panel->name); @@ -1889,7 +1904,7 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel, panel->bl_config.bl_min_level = val; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsi-bl-max-level", &val); + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-bl-max-level", &val); if (rc) { pr_debug("[%s] bl-max-level unspecified, defaulting to max level\n", panel->name); @@ -1898,7 +1913,7 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel, panel->bl_config.bl_max_level = val; } - rc = of_property_read_u32(of_node, "qcom,mdss-brightness-max-level", + rc = utils->read_u32(utils->data, "qcom,mdss-brightness-max-level", &val); if (rc) { pr_debug("[%s] brigheness-max-level unspecified, defaulting to 255\n", @@ -1909,7 +1924,7 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel, } if (panel->bl_config.type == DSI_BACKLIGHT_PWM) { - rc = dsi_panel_parse_bl_pwm_config(&panel->bl_config, of_node); + rc = dsi_panel_parse_bl_pwm_config(panel); if (rc) { pr_err("[%s] failed to parse pwm config, rc=%d\n", panel->name, rc); @@ -1917,7 +1932,7 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel, } } - panel->bl_config.en_gpio = of_get_named_gpio(of_node, + panel->bl_config.en_gpio = utils->get_named_gpio(utils->data, "qcom,platform-bklight-en-gpio", 0); if (!gpio_is_valid(panel->bl_config.en_gpio)) { @@ -2116,7 +2131,7 @@ int dsi_dsc_populate_static_param(struct msm_display_dsc_info *dsc) static int dsi_panel_parse_phy_timing(struct dsi_display_mode *mode, - struct device_node *of_node) + struct dsi_parser_utils *utils) { const char *data; u32 len, i; @@ -2125,7 +2140,7 @@ static int dsi_panel_parse_phy_timing(struct dsi_display_mode *mode, priv_info = mode->priv_info; - data = of_get_property(of_node, + data = utils->get_property(utils->data, "qcom,mdss-dsi-panel-phy-timings", &len); if (!data) { pr_debug("Unable to read Phy timing settings"); @@ -2148,7 +2163,7 @@ static int dsi_panel_parse_phy_timing(struct dsi_display_mode *mode, } static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, - struct device_node *of_node) + struct dsi_parser_utils *utils) { u32 data; int rc = -EINVAL; @@ -2162,7 +2177,8 @@ static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, priv_info = mode->priv_info; priv_info->dsc_enabled = false; - compression = of_get_property(of_node, "qcom,compression-mode", NULL); + compression = utils->get_property(utils->data, + "qcom,compression-mode", NULL); if (compression && !strcmp(compression, "dsc")) priv_info->dsc_enabled = true; @@ -2171,14 +2187,14 @@ static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, return 0; } - rc = of_property_read_u32(of_node, "qcom,mdss-dsc-slice-height", &data); + rc = utils->read_u32(utils->data, "qcom,mdss-dsc-slice-height", &data); if (rc) { pr_err("failed to parse qcom,mdss-dsc-slice-height\n"); goto error; } priv_info->dsc.slice_height = data; - rc = of_property_read_u32(of_node, "qcom,mdss-dsc-slice-width", &data); + rc = utils->read_u32(utils->data, "qcom,mdss-dsc-slice-width", &data); if (rc) { pr_err("failed to parse qcom,mdss-dsc-slice-width\n"); goto error; @@ -2196,7 +2212,7 @@ static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, priv_info->dsc.pic_width = mode->timing.h_active; priv_info->dsc.pic_height = mode->timing.v_active; - rc = of_property_read_u32(of_node, "qcom,mdss-dsc-slice-per-pkt", + rc = utils->read_u32(utils->data, "qcom,mdss-dsc-slice-per-pkt", &data); if (rc) { pr_err("failed to parse qcom,mdss-dsc-slice-per-pkt\n"); @@ -2204,7 +2220,7 @@ static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, } priv_info->dsc.slice_per_pkt = data; - rc = of_property_read_u32(of_node, "qcom,mdss-dsc-bit-per-component", + rc = utils->read_u32(utils->data, "qcom,mdss-dsc-bit-per-component", &data); if (rc) { pr_err("failed to parse qcom,mdss-dsc-bit-per-component\n"); @@ -2212,7 +2228,7 @@ static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, } priv_info->dsc.bpc = data; - rc = of_property_read_u32(of_node, "qcom,mdss-dsc-bit-per-pixel", + rc = utils->read_u32(utils->data, "qcom,mdss-dsc-bit-per-pixel", &data); if (rc) { pr_err("failed to parse qcom,mdss-dsc-bit-per-pixel\n"); @@ -2220,7 +2236,7 @@ static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, } priv_info->dsc.bpp = data; - priv_info->dsc.block_pred_enable = of_property_read_bool(of_node, + priv_info->dsc.block_pred_enable = utils->read_bool(utils->data, "qcom,mdss-dsc-block-prediction-enable"); priv_info->dsc.full_frame_slices = DIV_ROUND_UP(intf_width, @@ -2233,19 +2249,18 @@ static int dsi_panel_parse_dsc_params(struct dsi_display_mode *mode, return rc; } -static int dsi_panel_parse_hdr_config(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_hdr_config(struct dsi_panel *panel) { - int rc = 0; struct drm_panel_hdr_properties *hdr_prop; + struct dsi_parser_utils *utils = &panel->utils; hdr_prop = &panel->hdr_props; - hdr_prop->hdr_enabled = of_property_read_bool(of_node, + hdr_prop->hdr_enabled = utils->read_bool(utils->data, "qcom,mdss-dsi-panel-hdr-enabled"); if (hdr_prop->hdr_enabled) { - rc = of_property_read_u32_array(of_node, + rc = utils->read_u32_array(utils->data, "qcom,mdss-dsi-panel-hdr-color-primaries", hdr_prop->display_primaries, DISPLAY_PRIMARIES_MAX); @@ -2256,7 +2271,7 @@ static int dsi_panel_parse_hdr_config(struct dsi_panel *panel, return rc; } - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-panel-peak-brightness", &(hdr_prop->peak_brightness)); if (rc) { @@ -2266,7 +2281,7 @@ static int dsi_panel_parse_hdr_config(struct dsi_panel *panel, return rc; } - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-panel-blackness-level", &(hdr_prop->blackness_level)); if (rc) { @@ -2281,14 +2296,15 @@ static int dsi_panel_parse_hdr_config(struct dsi_panel *panel, static int dsi_panel_parse_topology( struct dsi_display_mode_priv_info *priv_info, - struct device_node *of_node, int topology_override) + struct dsi_parser_utils *utils, + int topology_override) { struct msm_display_topology *topology; u32 top_count, top_sel, *array = NULL; int i, len = 0; int rc = -EINVAL; - len = of_property_count_u32_elems(of_node, "qcom,display-topology"); + len = utils->count_u32_elems(utils->data, "qcom,display-topology"); if (len <= 0 || len % TOPOLOGY_SET_LEN || len > (TOPOLOGY_SET_LEN * MAX_TOPOLOGY)) { pr_err("invalid topology list for the panel, rc = %d\n", rc); @@ -2301,7 +2317,7 @@ static int dsi_panel_parse_topology( if (!array) return -ENOMEM; - rc = of_property_read_u32_array(of_node, + rc = utils->read_u32_array(utils->data, "qcom,display-topology", array, len); if (rc) { pr_err("unable to read the display topologies, rc = %d\n", rc); @@ -2332,7 +2348,7 @@ static int dsi_panel_parse_topology( goto parse_done; } - rc = of_property_read_u32(of_node, + rc = utils->read_u32(utils->data, "qcom,default-topology-index", &top_sel); if (rc) { pr_err("no default topology selected, rc = %d\n", rc); @@ -2362,19 +2378,20 @@ static int dsi_panel_parse_topology( return rc; } -static int dsi_panel_parse_roi_alignment(struct device_node *of_node, +static int dsi_panel_parse_roi_alignment(struct dsi_parser_utils *utils, struct msm_roi_alignment *align) { int len = 0, rc = 0; u32 value[6]; struct property *data; - if (!align || !of_node) + if (!align) return -EINVAL; memset(align, 0, sizeof(*align)); - data = of_find_property(of_node, "qcom,panel-roi-alignment", &len); + data = utils->find_property(utils->data, + "qcom,panel-roi-alignment", &len); len /= sizeof(u32); if (!data) { pr_err("panel roi alignment not found\n"); @@ -2383,7 +2400,7 @@ static int dsi_panel_parse_roi_alignment(struct device_node *of_node, pr_err("incorrect roi alignment len %d\n", len); rc = -EINVAL; } else { - rc = of_property_read_u32_array(of_node, + rc = utils->read_u32_array(utils->data, "qcom,panel-roi-alignment", value, len); if (rc) pr_debug("error reading panel roi alignment values\n"); @@ -2409,7 +2426,7 @@ static int dsi_panel_parse_roi_alignment(struct device_node *of_node, } static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode, - struct device_node *of_node) + struct dsi_parser_utils *utils) { struct msm_roi_caps *roi_caps = NULL; const char *data; @@ -2424,7 +2441,8 @@ static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode, memset(roi_caps, 0, sizeof(*roi_caps)); - data = of_get_property(of_node, "qcom,partial-update-enabled", NULL); + data = utils->get_property(utils->data, + "qcom,partial-update-enabled", NULL); if (data) { if (!strcmp(data, "dual_roi")) roi_caps->num_roi = 2; @@ -2441,7 +2459,7 @@ static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode, return 0; } - roi_caps->merge_rois = of_property_read_bool(of_node, + roi_caps->merge_rois = utils->read_bool(utils->data, "qcom,partial-update-roi-merge"); roi_caps->enabled = roi_caps->num_roi > 0; @@ -2450,7 +2468,7 @@ static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode, roi_caps->enabled); if (roi_caps->enabled) - rc = dsi_panel_parse_roi_alignment(of_node, + rc = dsi_panel_parse_roi_alignment(utils, &roi_caps->align); if (rc) @@ -2459,24 +2477,20 @@ static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode, return rc; } -static int dsi_panel_parse_dms_info(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_dms_info(struct dsi_panel *panel) { int dms_enabled; const char *data; - - if (!of_node || !panel) { - pr_err("invalid params\n"); - return -EINVAL; - } + struct dsi_parser_utils *utils = &panel->utils; panel->dms_mode = DSI_DMS_MODE_DISABLED; - dms_enabled = of_property_read_bool(of_node, + dms_enabled = utils->read_bool(utils->data, "qcom,dynamic-mode-switch-enabled"); if (!dms_enabled) return 0; - data = of_get_property(of_node, "qcom,dynamic-mode-switch-type", NULL); + data = utils->get_property(utils->data, + "qcom,dynamic-mode-switch-type", NULL); if (data && !strcmp(data, "dynamic-resolution-switch-immediate")) { panel->dms_mode = DSI_DMS_MODE_RES_SWITCH_IMMEDIATE; } else { @@ -2509,12 +2523,12 @@ dsi_panel_parse_esd_check_valid_params(struct dsi_panel *panel, u32 count) return true; } -static bool dsi_panel_parse_esd_status_len(struct device_node *np, +static bool dsi_panel_parse_esd_status_len(struct dsi_parser_utils *utils, char *prop_key, u32 **target, u32 cmd_cnt) { int tmp; - if (!of_find_property(np, prop_key, &tmp)) + if (!utils->find_property(utils->data, prop_key, &tmp)) return false; tmp /= sizeof(u32); @@ -2530,7 +2544,7 @@ static bool dsi_panel_parse_esd_status_len(struct device_node *np, return false; } - if (of_property_read_u32_array(np, prop_key, *target, tmp)) { + if (utils->read_u32_array(utils->data, prop_key, *target, tmp)) { pr_err("cannot get values from dts\n"); kfree(*target); *target = NULL; @@ -2550,8 +2564,7 @@ static void dsi_panel_esd_config_deinit(struct drm_panel_esd_config *esd_config) kfree(esd_config->status_cmd.cmds); } -static int dsi_panel_parse_esd_config(struct dsi_panel *panel, - struct device_node *of_node) +static int dsi_panel_parse_esd_config(struct dsi_panel *panel) { int rc = 0; u32 tmp; @@ -2559,17 +2572,18 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel, struct property *data; const char *string; struct drm_panel_esd_config *esd_config; + struct dsi_parser_utils *utils = &panel->utils; u8 *esd_mode = NULL; esd_config = &panel->esd_config; esd_config->status_mode = ESD_MODE_MAX; - esd_config->esd_enabled = of_property_read_bool(of_node, - "qcom,esd-check-enabled"); + esd_config->esd_enabled = utils->read_bool(utils->data, + "qcom,esd-check-enabled"); if (!esd_config->esd_enabled) return 0; - rc = of_property_read_string(of_node, + rc = utils->read_string(utils->data, "qcom,mdss-dsi-panel-status-check-mode", &string); if (!rc) { if (!strcmp(string, "bta_check")) { @@ -2600,14 +2614,14 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel, return 0; dsi_panel_parse_cmd_sets_sub(&esd_config->status_cmd, - DSI_CMD_SET_PANEL_STATUS, of_node); + DSI_CMD_SET_PANEL_STATUS, utils); if (!esd_config->status_cmd.count) { pr_err("panel status command parsing failed\n"); rc = -EINVAL; goto error; } - if (!dsi_panel_parse_esd_status_len(of_node, + if (!dsi_panel_parse_esd_status_len(utils, "qcom,mdss-dsi-panel-status-read-length", &panel->esd_config.status_cmds_rlen, esd_config->status_cmd.count)) { @@ -2616,7 +2630,7 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel, goto error1; } - if (dsi_panel_parse_esd_status_len(of_node, + if (dsi_panel_parse_esd_status_len(utils, "qcom,mdss-dsi-panel-status-valid-params", &panel->esd_config.status_valid_params, esd_config->status_cmd.count)) { @@ -2645,7 +2659,7 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel, * commands are there then, there should be corresponding * status check values for each read command. */ - data = of_find_property(of_node, + data = utils->find_property(utils->data, "qcom,mdss-dsi-panel-status-value", &tmp); tmp /= sizeof(u32); if (!IS_ERR_OR_NULL(data) && tmp != 0 && (tmp % status_len) == 0) { @@ -2675,7 +2689,7 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel, if (!esd_config->status_buf) goto error4; - rc = of_property_read_u32_array(of_node, + rc = utils->read_u32_array(utils->data, "qcom,mdss-dsi-panel-status-value", esd_config->status_value, esd_config->groups * status_len); if (rc) { @@ -2709,83 +2723,112 @@ static int dsi_panel_parse_esd_config(struct dsi_panel *panel, return rc; } +static void dsi_panel_update_util(struct dsi_panel *panel, + struct device_node *parser_node) +{ + struct dsi_parser_utils *utils = &panel->utils; + + if (parser_node) { + *utils = *dsi_parser_get_parser_utils(); + utils->data = parser_node; + + pr_debug("switching to parser APIs\n"); + + goto end; + } + + *utils = *dsi_parser_get_of_utils(); + utils->data = panel->panel_of_node; +end: + utils->node = panel->panel_of_node; +} + struct dsi_panel *dsi_panel_get(struct device *parent, struct device_node *of_node, + struct device_node *parser_node, + struct dentry *root, int topology_override) { struct dsi_panel *panel; + struct dsi_parser_utils *utils; int rc = 0; panel = kzalloc(sizeof(*panel), GFP_KERNEL); if (!panel) return ERR_PTR(-ENOMEM); - panel->name = of_get_property(of_node, "qcom,mdss-dsi-panel-name", - NULL); + panel->panel_of_node = of_node; + panel->parent = parent; + panel->root = root; + + dsi_panel_update_util(panel, parser_node); + utils = &panel->utils; + + panel->name = utils->get_property(utils->data, + "qcom,mdss-dsi-panel-name", NULL); if (!panel->name) panel->name = DSI_PANEL_DEFAULT_LABEL; - rc = dsi_panel_parse_host_config(panel, of_node); + rc = dsi_panel_parse_host_config(panel); if (rc) { pr_err("failed to parse host configuration, rc=%d\n", rc); goto error; } - rc = dsi_panel_parse_panel_mode(panel, of_node); + rc = dsi_panel_parse_panel_mode(panel); if (rc) { pr_err("failed to parse panel mode configuration, rc=%d\n", rc); goto error; } - rc = dsi_panel_parse_dfps_caps(&panel->dfps_caps, of_node, panel->name); + rc = dsi_panel_parse_dfps_caps(panel); if (rc) pr_err("failed to parse dfps configuration, rc=%d\n", rc); - rc = dsi_panel_parse_phy_props(&panel->phy_props, of_node, panel->name); + rc = dsi_panel_parse_phy_props(panel); if (rc) { pr_err("failed to parse panel physical dimension, rc=%d\n", rc); goto error; } - rc = dsi_panel_parse_power_cfg(parent, panel, of_node); + rc = dsi_panel_parse_power_cfg(panel); if (rc) pr_err("failed to parse power config, rc=%d\n", rc); - rc = dsi_panel_parse_gpios(panel, of_node); + rc = dsi_panel_parse_gpios(panel); if (rc) pr_err("failed to parse panel gpios, rc=%d\n", rc); - rc = dsi_panel_parse_bl_config(panel, of_node); + rc = dsi_panel_parse_bl_config(panel); if (rc) pr_err("failed to parse backlight config, rc=%d\n", rc); - rc = dsi_panel_parse_misc_features(panel, of_node); + rc = dsi_panel_parse_misc_features(panel); if (rc) pr_err("failed to parse misc features, rc=%d\n", rc); - rc = dsi_panel_parse_hdr_config(panel, of_node); + rc = dsi_panel_parse_hdr_config(panel); if (rc) pr_err("failed to parse hdr config, rc=%d\n", rc); - rc = dsi_panel_get_mode_count(panel, of_node); + rc = dsi_panel_get_mode_count(panel); if (rc) { pr_err("failed to get mode count, rc=%d\n", rc); goto error; } - rc = dsi_panel_parse_dms_info(panel, of_node); + rc = dsi_panel_parse_dms_info(panel); if (rc) pr_debug("failed to get dms info, rc=%d\n", rc); - rc = dsi_panel_parse_esd_config(panel, of_node); + rc = dsi_panel_parse_esd_config(panel); if (rc) pr_debug("failed to parse esd config, rc=%d\n", rc); - panel->panel_of_node = of_node; drm_panel_init(&panel->drm_panel); mutex_init(&panel->panel_lock); - panel->parent = parent; + return panel; error: kfree(panel); @@ -2909,21 +2952,23 @@ int dsi_panel_validate_mode(struct dsi_panel *panel, return 0; } -int dsi_panel_get_mode_count(struct dsi_panel *panel, - struct device_node *of_node) +int dsi_panel_get_mode_count(struct dsi_panel *panel) { const u32 SINGLE_MODE_SUPPORT = 1; + struct dsi_parser_utils *utils; struct device_node *timings_np; int count, rc = 0; - if (!of_node || !panel) { + if (!panel) { pr_err("invalid params\n"); return -EINVAL; } + utils = &panel->utils; + panel->num_timing_nodes = 0; - timings_np = of_get_child_by_name(of_node, + timings_np = utils->get_child_by_name(utils->data, "qcom,mdss-dsi-display-timings"); if (!timings_np) { pr_err("no display timing nodes defined\n"); @@ -2931,7 +2976,7 @@ int dsi_panel_get_mode_count(struct dsi_panel *panel, goto error; } - count = of_get_child_count(timings_np); + count = utils->get_child_count(timings_np); if (!count || count > DSI_MODE_MAX) { pr_err("invalid count of timing nodes: %d\n", count); rc = -EINVAL; @@ -3002,9 +3047,11 @@ int dsi_panel_get_mode(struct dsi_panel *panel, int topology_override) { struct device_node *timings_np, *child_np; + struct dsi_parser_utils *utils; struct dsi_display_mode_priv_info *prv_info; u32 child_idx = 0; int rc = 0, num_timings; + void *utils_data = NULL; if (!panel || !mode) { pr_err("invalid params\n"); @@ -3012,6 +3059,7 @@ int dsi_panel_get_mode(struct dsi_panel *panel, } mutex_lock(&panel->panel_lock); + utils = &panel->utils; mode->priv_info = kzalloc(sizeof(*mode->priv_info), GFP_KERNEL); if (!mode->priv_info) { @@ -3021,7 +3069,7 @@ int dsi_panel_get_mode(struct dsi_panel *panel, prv_info = mode->priv_info; - timings_np = of_get_child_by_name(panel->panel_of_node, + timings_np = utils->get_child_by_name(utils->data, "qcom,mdss-dsi-display-timings"); if (!timings_np) { pr_err("no display timing nodes defined\n"); @@ -3029,55 +3077,59 @@ int dsi_panel_get_mode(struct dsi_panel *panel, goto parse_fail; } - num_timings = of_get_child_count(timings_np); + num_timings = utils->get_child_count(timings_np); if (!num_timings || num_timings > DSI_MODE_MAX) { pr_err("invalid count of timing nodes: %d\n", num_timings); rc = -EINVAL; goto parse_fail; } - for_each_child_of_node(timings_np, child_np) { + utils_data = utils->data; + + dsi_for_each_child_node(timings_np, child_np) { if (index != child_idx++) continue; - rc = dsi_panel_parse_timing(&mode->timing, child_np); + utils->data = child_np; + + rc = dsi_panel_parse_timing(&mode->timing, utils); if (rc) { pr_err("failed to parse panel timing, rc=%d\n", rc); goto parse_fail; } - rc = dsi_panel_parse_dsc_params(mode, child_np); + rc = dsi_panel_parse_dsc_params(mode, utils); if (rc) { pr_err("failed to parse dsc params, rc=%d\n", rc); goto parse_fail; } - rc = dsi_panel_parse_topology(prv_info, child_np, + rc = dsi_panel_parse_topology(prv_info, utils, topology_override); if (rc) { pr_err("failed to parse panel topology, rc=%d\n", rc); goto parse_fail; } - rc = dsi_panel_parse_cmd_sets(prv_info, child_np); + rc = dsi_panel_parse_cmd_sets(prv_info, utils); if (rc) { pr_err("failed to parse command sets, rc=%d\n", rc); goto parse_fail; } - rc = dsi_panel_parse_jitter_config(mode, child_np); + rc = dsi_panel_parse_jitter_config(mode, utils); if (rc) pr_err( "failed to parse panel jitter config, rc=%d\n", rc); - rc = dsi_panel_parse_phy_timing(mode, child_np); + rc = dsi_panel_parse_phy_timing(mode, utils); if (rc) { pr_err( "failed to parse panel phy timings, rc=%d\n", rc); goto parse_fail; } - rc = dsi_panel_parse_partial_update_caps(mode, child_np); + rc = dsi_panel_parse_partial_update_caps(mode, utils); if (rc) pr_err("failed to partial update caps, rc=%d\n", rc); } @@ -3087,6 +3139,7 @@ int dsi_panel_get_mode(struct dsi_panel *panel, kfree(mode->priv_info); mode->priv_info = NULL; done: + utils->data = utils_data; mutex_unlock(&panel->panel_lock); return rc; } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h index c6b09531aa685c3244148dd9124debf26376dc78..4d3d23e7fddc5ab9fc5e372cc6651609cc08a66e 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h @@ -27,6 +27,7 @@ #include "dsi_ctrl_hw.h" #include "dsi_clk.h" #include "dsi_pwr.h" +#include "dsi_parser.h" #include "msm_drv.h" #define MAX_BL_LEVEL 4096 @@ -149,6 +150,7 @@ struct dsi_panel { struct drm_panel drm_panel; struct mipi_dsi_host *host; struct device *parent; + struct dentry *root; struct dsi_host_common_cfg host_config; struct dsi_video_engine_cfg video_config; @@ -168,6 +170,8 @@ struct dsi_panel { struct drm_panel_hdr_properties hdr_props; struct drm_panel_esd_config esd_config; + struct dsi_parser_utils utils; + bool lp11_init; bool ulps_enabled; bool ulps_suspend_enabled; @@ -204,6 +208,8 @@ static inline void dsi_panel_release_panel_lock(struct dsi_panel *panel) struct dsi_panel *dsi_panel_get(struct device *parent, struct device_node *of_node, + struct device_node *parser_node, + struct dentry *root, int topology_override); int dsi_panel_trigger_esd_attack(struct dsi_panel *panel); @@ -214,8 +220,7 @@ int dsi_panel_drv_init(struct dsi_panel *panel, struct mipi_dsi_host *host); int dsi_panel_drv_deinit(struct dsi_panel *panel); -int dsi_panel_get_mode_count(struct dsi_panel *panel, - struct device_node *of_node); +int dsi_panel_get_mode_count(struct dsi_panel *panel); void dsi_panel_put_mode(struct dsi_display_mode *mode); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_parser.c b/drivers/gpu/drm/msm/dsi-staging/dsi_parser.c new file mode 100644 index 0000000000000000000000000000000000000000..e5ca15156bbcbc21c1224a90f21f582d4db941e2 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_parser.c @@ -0,0 +1,1268 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "[dsi-parser] %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include + +#include "dsi_parser.h" + +#define DSI_PARSER_MAX_NODES 20 + +enum dsi_parser_prop_type { + DSI_PROP_TYPE_STR, + DSI_PROP_TYPE_STR_ARRAY, + DSI_PROP_TYPE_INT_SET, + DSI_PROP_TYPE_INT_SET_ARRAY, + DSI_PROP_TYPE_INT_ARRAY, +}; + +struct dsi_parser_prop { + char *name; + char *raw; + char *value; + char **items; + enum dsi_parser_prop_type type; + int len; +}; + +struct dsi_parser_node { + char *name; + char *data; + + struct dsi_parser_prop *prop; + int prop_count; + + struct dsi_parser_node *child[DSI_PARSER_MAX_NODES]; + int children_count; +}; + +struct dsi_parser { + const struct firmware *fw; + struct dsi_parser_node *head_node; + struct dsi_parser_node *current_node; + struct device *dev; + char *buf; + char file_name[SZ_32]; +}; + +static int dsi_parser_count(char *buf, int item) +{ + int count = 0; + + do { + buf = strnchr(buf, strlen(buf), item); + if (buf) + count++; + } while (buf++); + + return count; +} + +static char *dsi_parser_clear_tail(char *buf) +{ + int size = strlen(buf); + char *end; + + if (!size) + goto exit; + + end = buf + size - 1; + while (end >= buf && *end == '*') + end--; + + *(end + 1) = '\0'; +exit: + return buf; +} + +static char *dsi_parser_strim(char *buf) +{ + strreplace(buf, '*', ' '); + + return strim(buf); +} + +static char *dsi_parser_get_data(char *start, char *end, char *str) +{ + strsep(&str, start); + if (str) + return dsi_parser_clear_tail(strsep(&str, end)); + + return NULL; +} + +static bool dsi_parser_get_tuplets_data( + struct dsi_parser_prop *prop, char *str) +{ + bool middle_of_tx = false; + + while (str) { + char *out = strsep(&str, " "); + + if (str || middle_of_tx) { + middle_of_tx = true; + + prop->items[prop->len++] = dsi_parser_strim(out); + } + } + + return middle_of_tx; +} + +static bool dsi_parser_get_strings(struct device *dev, + struct dsi_parser_prop *prop, char *str) +{ + bool middle_of_tx = false; + int i = 0; + int count = 0; + + if (!dsi_parser_count(str, '"')) + goto end; + + count = dsi_parser_count(str, ','); + pr_debug("count=%d\n", count); + + if (!count) { + prop->value = dsi_parser_get_data("\"", "\"", str); + prop->type = DSI_PROP_TYPE_STR; + middle_of_tx = prop->value ? true : false; + + goto end; + } + + /* number of items are 1 more than separator */ + count++; + prop->items = devm_kzalloc(dev, count, GFP_KERNEL); + if (!prop->items) + goto end; + + prop->type = DSI_PROP_TYPE_STR_ARRAY; + + while (str) { + char *out = strsep(&str, ","); + + if ((str || middle_of_tx) && (i < count)) { + prop->items[i++] = + dsi_parser_get_data("\"", "\"", out); + prop->len++; + + middle_of_tx = true; + } + } +end: + return middle_of_tx; +} + +static bool dsi_parser_get_tuplets(struct device *dev, + struct dsi_parser_prop *prop, char *str) +{ + bool middle_of_tx = false; + char *data = NULL; + + while (str) { + char *out = strsep(&str, ","); + + if (str || middle_of_tx) { + data = dsi_parser_get_data("<", ">", out); + middle_of_tx = true; + + dsi_parser_get_tuplets_data(prop, data); + } + } + + return middle_of_tx; +} + +static void dsi_parser_get_int_value(struct dsi_parser_prop *prop, + int forced_base) +{ + int i; + + for (i = 0; i < prop->len; i++) { + int base, val; + char *to_int, *tmp; + char item[SZ_128]; + + strlcpy(item, prop->items[i], SZ_128); + + tmp = item; + + if (forced_base) { + base = forced_base; + } else { + to_int = strsep(&tmp, "x"); + + if (!tmp) { + tmp = to_int; + base = 10; + } else { + base = 16; + } + } + + if (kstrtoint(tmp, base, &val)) { + pr_err("error converting %s at %d\n", + tmp, i); + + continue; + } + + prop->value[i] = val & 0xFF; + } +} + +static bool dsi_parser_parse_prop(struct device *dev, + struct dsi_parser_prop *prop, char *buf) +{ + bool found = false; + char *out = strsep(&buf, "="); + + if (!out || !buf) + goto end; + + prop->raw = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + if (!prop->raw) + goto end; + + strlcpy(prop->raw, buf, strlen(buf) + 1); + + found = true; + + prop->name = dsi_parser_strim(out); + pr_debug("RAW: %s: %s\n", prop->name, prop->raw); + + prop->len = 0; + + if (dsi_parser_get_strings(dev, prop, buf)) + goto end; + + prop->items = devm_kzalloc(dev, strlen(buf) * 2, GFP_KERNEL); + if (!prop->items) + goto end; + + if (dsi_parser_get_tuplets(dev, prop, buf)) { + prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL); + if (prop->value) { + prop->type = DSI_PROP_TYPE_INT_SET_ARRAY; + dsi_parser_get_int_value(prop, 0); + } + goto end; + } + + prop->value = dsi_parser_get_data("<", ">", buf); + if (prop->value) { + if (dsi_parser_get_tuplets_data(prop, prop->value)) { + prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL); + if (prop->value) { + prop->type = DSI_PROP_TYPE_INT_SET; + dsi_parser_get_int_value(prop, 0); + } + goto end; + } else { + prop->items[prop->len++] = prop->value; + } + + goto end; + } + + prop->value = dsi_parser_get_data("[", "]", buf); + if (prop->value) { + char *out5; + + if (!prop->items) + goto end; + + out5 = prop->value; + while (out5 && strlen(out5)) { + char *out6 = strsep(&out5, " "); + + out6 = dsi_parser_strim(out6); + if (out6 && strlen(out6)) + prop->items[prop->len++] = out6; + }; + + prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL); + if (prop->value) { + prop->type = DSI_PROP_TYPE_INT_ARRAY; + + dsi_parser_get_int_value(prop, 16); + } + } else { + found = false; + } +end: + return found; +} + +static char *dsi_parser_clean_name(char *name) +{ + char *clean_name = name; + + while (name) + clean_name = strsep(&name, ";"); + + return dsi_parser_strim(clean_name); +} + +static char *dsi_parser_get_blob(char **buf, bool *has_child) +{ + char *data = NULL; + char *start = *buf; + + data = strpbrk(*buf, "{}"); + if (!data) + goto end; + + if (*data == '{') + *has_child = true; + + if (*has_child) { + while (data != *buf) { + data--; + if (*data == ';') { + data++; + *data = '\0'; + *buf = data + 1; + break; + } + } + } else { + *data = '\0'; + *buf = data + 1; + } +end: + return start; +} + +static struct dsi_parser_node *dsi_parser_find_nodes(struct device *dev, + char **buf) +{ + struct dsi_parser_node *node = NULL, *cnode = NULL; + char *name, *data; + bool has_child = false; + + if (!buf) + goto end; + + data = strpbrk(*buf, "{}"); + if (!data) { + pr_debug("{} not found\n"); + goto end; + } + + if (*data == '}') { + *buf = data + 1; + goto end; + } + + name = strsep(buf, "{"); + + if (*buf && name) { + node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL); + if (!node) + goto end; + + node->name = dsi_parser_clean_name(name); + node->data = dsi_parser_get_blob(buf, &has_child); + + if (!has_child) + goto end; + + do { + cnode = dsi_parser_find_nodes(dev, buf); + if (cnode && + (node->children_count < DSI_PARSER_MAX_NODES)) + node->child[node->children_count++] = cnode; + } while (cnode); + } +end: + return node; +} + +static void dsi_parser_count_properties(struct dsi_parser_node *node) +{ + int count; + + if (node && strlen(node->data)) { + node->prop_count = dsi_parser_count(node->data, ';'); + + for (count = 0; count < node->children_count; count++) + dsi_parser_count_properties(node->child[count]); + } +} + +static void dsi_parser_get_properties(struct device *dev, + struct dsi_parser_node *node) +{ + int count; + + if (!node) + return; + + if (node->prop_count) { + int i = 0; + char *buf = node->data; + + node->prop = devm_kcalloc(dev, node->prop_count, + sizeof(struct dsi_parser_prop), + GFP_KERNEL); + if (!node->prop) + return; + + for (i = 0; i < node->prop_count; i++) { + char *out = strsep(&buf, ";"); + struct dsi_parser_prop *prop = &node->prop[i]; + + if (!out || !prop) + continue; + + if (!dsi_parser_parse_prop(dev, prop, out)) { + char *out1 = strsep(&out, "}"); + + if (!out1) + continue; + + out1 = dsi_parser_strim(out1); + + if (!out && strlen(out1)) { + prop->name = out1; + prop->value = "1"; + } + } + } + } + + for (count = 0; count < node->children_count; count++) + dsi_parser_get_properties(dev, node->child[count]); +} + +static struct dsi_parser_prop *dsi_parser_search_property( + struct dsi_parser_node *node, + const char *name) +{ + int i = 0; + struct dsi_parser_prop *prop = node->prop; + + for (i = 0; i < node->prop_count; i++) { + if (prop[i].name && !strcmp(prop[i].name, name)) + return &prop[i]; + } + + return NULL; +} + +/* APIs for the clients */ +struct property *dsi_parser_find_property(const struct device_node *np, + const char *name, + int *lenp) +{ + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop = NULL; + + if (!node || !name || !lenp) + goto end; + + prop = dsi_parser_search_property(node, name); + if (!prop) { + pr_debug("%s not found\n", name); + goto end; + } + + if (lenp) { + if (prop->type == DSI_PROP_TYPE_INT_ARRAY) + *lenp = prop->len; + else if (prop->type == DSI_PROP_TYPE_INT_SET_ARRAY || + prop->type == DSI_PROP_TYPE_INT_SET) + *lenp = prop->len * sizeof(u32); + else + *lenp = strlen(prop->raw) + 1; + + pr_debug("%s len=%d\n", name, *lenp); + } +end: + return (struct property *)prop; +} + +bool dsi_parser_read_bool(const struct device_node *np, + const char *propname) +{ + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + bool prop_set; + + prop_set = dsi_parser_search_property(node, propname) ? true : false; + + pr_debug("%s=%s\n", propname, prop_set ? "set" : "not set"); + + return prop_set; +} + +int dsi_parser_read_string(const struct device_node *np, + const char *propname, const char **out_string) +{ + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop; + char *property = NULL; + int rc = 0; + + prop = dsi_parser_search_property(node, propname); + if (!prop) { + pr_debug("%s not found\n", propname); + rc = -EINVAL; + } else { + property = prop->value; + } + + *out_string = property; + + pr_debug("%s=%s\n", propname, *out_string); + return rc; +} + +int dsi_parser_read_u64(const struct device_node *np, const char *propname, + u64 *out_value) +{ + return -EINVAL; +} + +int dsi_parser_read_u32(const struct device_node *np, + const char *propname, u32 *out_value) +{ + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop; + char *property, *to_int, item[SZ_128]; + int rc = 0, base; + + prop = dsi_parser_search_property(node, propname); + if (!prop) { + pr_debug("%s not found\n", propname); + rc = -EINVAL; + goto end; + } + + if (!prop->value) + goto end; + + strlcpy(item, prop->value, SZ_128); + property = item; + to_int = strsep(&property, "x"); + + if (!property) { + property = to_int; + base = 10; + } else { + base = 16; + } + + rc = kstrtoint(property, base, out_value); + if (rc) { + pr_err("prop=%s error(%d) converting %s, base=%d\n", + propname, rc, property, base); + goto end; + } + + pr_debug("%s=%d\n", propname, *out_value); +end: + return rc; +} + +int dsi_parser_read_u32_array(const struct device_node *np, + const char *propname, + u32 *out_values, size_t sz) +{ + int i, rc = 0; + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop; + + prop = dsi_parser_search_property(node, propname); + if (!prop) { + pr_debug("%s not found\n", propname); + rc = -EINVAL; + goto end; + } + + for (i = 0; i < prop->len; i++) { + int base, val; + char item[SZ_128]; + char *to_int, *tmp; + + strlcpy(item, prop->items[i], SZ_128); + + tmp = item; + + to_int = strsep(&tmp, "x"); + + if (!tmp) { + tmp = to_int; + base = 10; + } else { + base = 16; + } + + rc = kstrtoint(tmp, base, &val); + if (rc) { + pr_err("prop=%s error(%d) converting %s(%d), base=%d\n", + propname, rc, tmp, i, base); + continue; + } + + *out_values++ = val; + + pr_debug("%s: [%d]=%d\n", propname, i, *(out_values - 1)); + } +end: + return rc; +} + +const void *dsi_parser_get_property(const struct device_node *np, + const char *name, int *lenp) +{ + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop; + char *property = NULL; + + prop = dsi_parser_search_property(node, name); + if (!prop) { + pr_debug("%s not found\n", name); + goto end; + } + + property = prop->value; + + if (prop->type == DSI_PROP_TYPE_STR) + pr_debug("%s=%s\n", name, property); + + if (lenp) { + if (prop->type == DSI_PROP_TYPE_INT_ARRAY) + *lenp = prop->len; + else if (prop->type == DSI_PROP_TYPE_INT_SET_ARRAY || + prop->type == DSI_PROP_TYPE_INT_SET) + *lenp = prop->len * sizeof(u32); + else + *lenp = strlen(prop->raw) + 1; + + pr_debug("%s len=%d\n", name, *lenp); + } +end: + return property; +} + +struct device_node *dsi_parser_get_child_by_name(const struct device_node *np, + const char *name) +{ + int index = 0; + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_node *matched_node = NULL; + + if (!node || !node->children_count) + goto end; + + do { + struct dsi_parser_node *child_node = node->child[index++]; + + if (!child_node) + goto end; + + if (!strcmp(child_node->name, name)) { + matched_node = child_node; + break; + } + } while (index < node->children_count); +end: + pr_debug("%s: %s\n", name, matched_node ? "found" : "not found"); + + return (struct device_node *)matched_node; +} + +struct dsi_parser_node *dsi_parser_get_node_by_name( + struct dsi_parser_node *node, + char *name) +{ + int count = 0; + struct dsi_parser_node *matched_node = NULL; + + if (!node) { + pr_err("node is null\n"); + goto end; + } + + if (!strcmp(node->name, name)) { + matched_node = node; + goto end; + } + + for (count = 0; count < node->children_count; count++) { + matched_node = dsi_parser_get_node_by_name( + node->child[count], name); + if (matched_node) + break; + } +end: + pr_debug("%s: %s\n", name, matched_node ? "found" : "not found"); + + return matched_node; +} + +int dsi_parser_get_child_count(const struct device_node *np) +{ + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + int count = 0; + + if (node) { + count = node->children_count; + pr_debug("node %s child count=%d\n", node->name, count); + } + + return count; +} + +struct device_node *dsi_parser_get_next_child(const struct device_node *np, + struct device_node *prev) +{ + int index = 0; + struct dsi_parser_node *parent = (struct dsi_parser_node *)np; + struct dsi_parser_node *prev_child = (struct dsi_parser_node *)prev; + struct dsi_parser_node *matched_node = NULL; + + if (!parent || !parent->children_count) + goto end; + + do { + struct dsi_parser_node *child_node = parent->child[index++]; + + if (!child_node) + goto end; + + if (!prev) { + matched_node = child_node; + goto end; + } + + if (!strcmp(child_node->name, prev_child->name)) { + if (index < parent->children_count) + matched_node = parent->child[index]; + break; + } + } while (index < parent->children_count); +end: + if (matched_node) + pr_debug("next child: %s\n", matched_node->name); + + return (struct device_node *)matched_node; +} + +int dsi_parser_count_u32_elems(const struct device_node *np, + const char *propname) +{ + int count = 0; + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop; + + prop = dsi_parser_search_property(node, propname); + if (!prop) { + pr_debug("%s not found\n", propname); + goto end; + } + + count = prop->len; + + pr_debug("prop %s has %d items\n", prop->name, count); +end: + return count; +} + +int dsi_parser_count_strings(const struct device_node *np, + const char *propname) +{ + int count = 0; + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop; + + prop = dsi_parser_search_property(node, propname); + if (!prop) { + pr_debug("%s not found\n", propname); + goto end; + } + + if (prop->type == DSI_PROP_TYPE_STR_ARRAY) + count = prop->len; + else if (prop->type == DSI_PROP_TYPE_STR) + count = 1; + + pr_debug("prop %s has %d items\n", prop->name, count); +end: + return count; +} + +int dsi_parser_read_string_index(const struct device_node *np, + const char *propname, + int index, const char **output) +{ + struct dsi_parser_node *node = (struct dsi_parser_node *)np; + struct dsi_parser_prop *prop; + + prop = dsi_parser_search_property(node, propname); + if (!prop) { + pr_debug("%s not found\n", propname); + goto end; + } + + if (prop->type != DSI_PROP_TYPE_STR_ARRAY) { + pr_err("not a string array property\n"); + goto end; + } + + if (index >= prop->len) { + pr_err("out of bond index %d\n", index); + goto end; + } + + *output = prop->items[index]; + + return 0; +end: + return -EINVAL; +} + +int dsi_parser_get_named_gpio(struct device_node *np, + const char *propname, int index) +{ + int gpio = -EINVAL; + + dsi_parser_read_u32(np, propname, &gpio); + + return gpio; +} + +void *dsi_parser_get_head_node(void *in, + const u8 *data, u32 size) +{ + struct dsi_parser *parser = in; + char *buf; + + if (!parser || !data || !size) { + pr_err("invalid input\n"); + goto err; + } + + parser->buf = devm_kzalloc(parser->dev, size, GFP_KERNEL); + if (!parser->buf) + goto err; + + buf = parser->buf; + + memcpy(buf, data, size); + + strreplace(buf, '\n', ' '); + strreplace(buf, '\t', '*'); + + parser->head_node = dsi_parser_find_nodes(parser->dev, &buf); + if (!parser->head_node) { + pr_err("could not get head node\n"); + devm_kfree(parser->dev, parser->buf); + goto err; + } + + dsi_parser_count_properties(parser->head_node); + dsi_parser_get_properties(parser->dev, parser->head_node); + + parser->current_node = parser->head_node; + + return parser->head_node; +err: + return NULL; +} + +static int dsi_parser_check_buffer_overflow(int rc, int *max_size, int *len) +{ + if (rc >= *max_size) { + pr_err("buffer overflow, rc=%d, max_size=%d\n", + rc, *max_size); + return -ENOMEM; + } + *len += rc; + *max_size = SZ_4K - *len; + + return 0; +} + +static int dsi_parser_read_file(struct dsi_parser *parser, + const u8 **buf, u32 *size) +{ + int rc = 0; + + release_firmware(parser->fw); + + rc = request_firmware(&parser->fw, parser->file_name, parser->dev); + if (rc || !parser->fw) { + pr_err("couldn't read firmware\n"); + goto end; + } + + *buf = parser->fw->data; + *size = parser->fw->size; + + pr_debug("file %s: size %zd\n", + parser->file_name, parser->fw->size); +end: + return rc; +} + +static void dsi_parser_free_mem(struct device *dev, + struct dsi_parser_node *node) +{ + int i = 0; + + if (!node) + return; + + pr_debug("node=%s, prop_count=%d\n", node->name, node->prop_count); + + for (i = 0; i < node->prop_count; i++) { + struct dsi_parser_prop *prop = &node->prop[i]; + + if (!prop) + continue; + + pr_debug("deleting prop=%s\n", prop->name); + + if (prop->items) + devm_kfree(dev, prop->items); + + if (prop->raw) + devm_kfree(dev, prop->raw); + + if ((prop->type == DSI_PROP_TYPE_INT_SET_ARRAY || + prop->type == DSI_PROP_TYPE_INT_SET || + prop->type == DSI_PROP_TYPE_INT_ARRAY) && prop->value) + devm_kfree(dev, prop->value); + } + + if (node->prop) + devm_kfree(dev, node->prop); + + for (i = 0; i < node->children_count; i++) + dsi_parser_free_mem(dev, node->child[i]); + + devm_kfree(dev, node); +} + +static ssize_t dsi_parser_write_init(struct file *file, + const char __user *user_buff, size_t count, loff_t *ppos) +{ + struct dsi_parser *parser = file->private_data; + const u8 *data = NULL; + u32 size = 0; + char buf[SZ_32]; + size_t len = 0; + + if (!parser) + return -ENODEV; + + if (*ppos) + return 0; + + /* Leave room for termination char */ + len = min_t(size_t, count, SZ_32 - 1); + if (copy_from_user(buf, user_buff, len)) + goto end; + + buf[len] = '\0'; + + if (sscanf(buf, "%31s", parser->file_name) != 1) { + pr_err("failed to get val\n"); + goto end; + } + + if (dsi_parser_read_file(parser, &data, &size)) { + pr_err("failed to read file\n"); + goto end; + } + + dsi_parser_free_mem(parser->dev, parser->head_node); + + if (parser->buf) { + devm_kfree(parser->dev, parser->buf); + parser->buf = NULL; + } + + parser->head_node = dsi_parser_get_head_node(parser, data, size); + if (!parser->head_node) { + pr_err("failed to parse data\n"); + goto end; + } +end: + return len; +} + +static ssize_t dsi_parser_read_node(struct file *file, + char __user *user_buff, size_t count, loff_t *ppos) +{ + char *buf = NULL; + int i, j, len = 0, rc = 0, max_size = SZ_4K; + struct dsi_parser *parser = file->private_data; + struct dsi_parser_node *node; + struct dsi_parser_prop *prop; + + if (!parser) { + len = -ENODEV; + goto error; + } + + if (*ppos) + goto error; + + buf = devm_kzalloc(parser->dev, SZ_4K, GFP_KERNEL); + if (!buf) { + len = -ENOMEM; + goto error; + } + + node = parser->current_node; + if (!node) { + len = -EINVAL; + goto error; + } + + prop = node->prop; + + rc = snprintf(buf + len, max_size, "node name=%s\n", node->name); + rc = dsi_parser_check_buffer_overflow(rc, &max_size, &len); + if (rc) + goto error; + + rc = snprintf(buf + len, max_size, "children count=%d\n", + node->children_count); + rc = dsi_parser_check_buffer_overflow(rc, &max_size, &len); + if (rc) + goto error; + + for (i = 0; i < node->children_count; i++) { + rc = snprintf(buf + len, max_size, "child[%d]=%s\n", + i, node->child[i]->name); + rc = dsi_parser_check_buffer_overflow(rc, &max_size, &len); + if (rc) + goto error; + } + + for (i = 0; i < node->prop_count; i++) { + if (!prop[i].name) + continue; + + rc = snprintf(buf + len, max_size, + "property=%s\n", prop[i].name); + rc = dsi_parser_check_buffer_overflow(rc, &max_size, &len); + if (rc) + goto error; + + if (prop[i].value) { + if (prop[i].type == DSI_PROP_TYPE_STR) { + rc = snprintf(buf + len, max_size, + "value=%s\n", prop[i].value); + rc = dsi_parser_check_buffer_overflow( + rc, &max_size, &len); + if (rc) + goto error; + } else { + for (j = 0; j < prop[i].len; j++) { + rc = snprintf(buf + len, max_size, + "%x", prop[i].value[j]); + rc = dsi_parser_check_buffer_overflow( + rc, &max_size, &len); + if (rc) + goto error; + } + + rc = snprintf(buf + len, max_size, "\n"); + rc = dsi_parser_check_buffer_overflow( + rc, &max_size, &len); + if (rc) + goto error; + + } + } + + if (prop[i].len) { + rc = snprintf(buf + len, max_size, "items:\n"); + rc = dsi_parser_check_buffer_overflow(rc, &max_size, + &len); + if (rc) + goto error; + } + + for (j = 0; j < prop[i].len; j++) { + char delim; + + if (j && !(j % 10)) + delim = '\n'; + else + delim = ' '; + + rc = snprintf(buf + len, max_size, "%s%c", + prop[i].items[j], delim); + rc = dsi_parser_check_buffer_overflow(rc, &max_size, + &len); + if (rc) + goto error; + } + + rc = snprintf(buf + len, max_size, "\n\n"); + rc = dsi_parser_check_buffer_overflow(rc, &max_size, &len); + if (rc) + goto error; + } +error: + if (copy_to_user(user_buff, buf, len)) { + len = -EFAULT; + goto error; + } + *ppos += len; + + if (buf) + devm_kfree(parser->dev, buf); + + return len; +} + +static ssize_t dsi_parser_write_node(struct file *file, + const char __user *user_buff, size_t count, loff_t *ppos) +{ + struct dsi_parser *parser = file->private_data; + char buf[SZ_512]; + size_t len = 0; + + if (!parser) + return -ENODEV; + + if (*ppos) + return 0; + + /* Leave room for termination char */ + len = min_t(size_t, count, SZ_512 - 1); + if (copy_from_user(buf, user_buff, len)) + goto end; + + buf[len] = '\0'; + + strreplace(buf, '\n', ' '); + + if (!strcmp(strim(buf), "head_node")) + parser->current_node = parser->head_node; + else + parser->current_node = dsi_parser_get_node_by_name( + parser->head_node, strim(buf)); +end: + return len; +} + +static const struct file_operations dsi_parser_init_fops = { + .open = simple_open, + .write = dsi_parser_write_init, +}; + +static const struct file_operations dsi_parser_node_fops = { + .open = simple_open, + .read = dsi_parser_read_node, + .write = dsi_parser_write_node, +}; + +int dsi_parser_dbg_init(void *parser, struct dentry *parent_dir) +{ + int rc = 0; + struct dentry *dir, *file; + + if (!parser || !parent_dir) { + pr_err("invalid input\n"); + goto end; + } + + dir = debugfs_create_dir("parser", parent_dir); + if (IS_ERR_OR_NULL(dir)) { + rc = PTR_ERR(dir); + + pr_err("failed to create parser debugfs\n"); + goto end; + } + + file = debugfs_create_file("init", 0644, dir, + parser, &dsi_parser_init_fops); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + + pr_err("failed to create init debugfs\n"); + goto dbg; + } + + file = debugfs_create_file("node", 0644, dir, + parser, &dsi_parser_node_fops); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + + pr_err("failed to create init debugfs\n"); + goto dbg; + } + + pr_debug("success\n"); + return 0; +dbg: + debugfs_remove_recursive(dir); +end: + return rc; +} + +void *dsi_parser_get(struct device *dev) +{ + int rc = 0; + struct dsi_parser *parser = NULL; + + if (!dev) { + pr_err("invalid data\n"); + rc = -EINVAL; + goto end; + } + + parser = devm_kzalloc(dev, sizeof(*parser), GFP_KERNEL); + if (!parser) { + rc = -ENOMEM; + goto end; + } + + parser->dev = dev; + + strlcpy(parser->file_name, "dsi_prop", sizeof(parser->file_name)); + + return parser; +end: + return ERR_PTR(rc); +} + +void dsi_parser_put(void *data) +{ + struct dsi_parser *parser = data; + + if (!parser) + return; + + dsi_parser_free_mem(parser->dev, parser->head_node); + + devm_kfree(parser->dev, parser->buf); + devm_kfree(parser->dev, parser); +} + diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_parser.h b/drivers/gpu/drm/msm/dsi-staging/dsi_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..273a180a53da321bf1b6ca7ab0f74ebea273cdaf --- /dev/null +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_parser.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2018, 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 _DSI_PARSER_H_ +#define _DSI_PARSER_H_ + +#include +#include + +#ifdef CONFIG_DSI_PARSER +void *dsi_parser_get(struct device *dev); +void dsi_parser_put(void *data); +int dsi_parser_dbg_init(void *parser, struct dentry *dir); +void *dsi_parser_get_head_node(void *parser, + const u8 *data, u32 size); + +const void *dsi_parser_get_property(const struct device_node *np, + const char *name, int *lenp); +bool dsi_parser_read_bool(const struct device_node *np, + const char *propname); +int dsi_parser_read_u64(const struct device_node *np, const char *propname, + u64 *out_value); +int dsi_parser_read_u32(const struct device_node *np, + const char *propname, u32 *out_value); +int dsi_parser_read_u32_array(const struct device_node *np, + const char *propname, + u32 *out_values, size_t sz); +int dsi_parser_read_string(const struct device_node *np, + const char *propname, const char **out_string); +struct device_node *dsi_parser_get_child_by_name(const struct device_node *node, + const char *name); +int dsi_parser_get_child_count(const struct device_node *np); +struct property *dsi_parser_find_property(const struct device_node *np, + const char *name, int *lenp); +struct device_node *dsi_parser_get_next_child(const struct device_node *np, + struct device_node *prev); +int dsi_parser_count_u32_elems(const struct device_node *np, + const char *propname); +int dsi_parser_count_strings(const struct device_node *np, + const char *propname); +int dsi_parser_read_string_index(const struct device_node *np, + const char *propname, + int index, const char **output); +int dsi_parser_get_named_gpio(struct device_node *np, + const char *propname, int index); +#else /* CONFIG_DSI_PARSER */ +static inline void *dsi_parser_get(struct device *dev) +{ + return NULL; +} + +static inline void dsi_parser_put(void *data) +{ +} + +static inline int dsi_parser_dbg_init(void *parser, struct dentry *dir) +{ + return -ENODEV; +} + +static inline void *dsi_parser_get_head_node(void *parser, + const u8 *data, u32 size) +{ + return NULL; +} + +static inline const void *dsi_parser_get_property(const struct device_node *np, + const char *name, int *lenp) +{ + return NULL; +} + +static inline bool dsi_parser_read_bool(const struct device_node *np, + const char *propname) +{ + return false; +} + +static inline int dsi_parser_read_u64(const struct device_node *np, + const char *propname, u64 *out_value) +{ + return -ENODEV; +} + +static inline int dsi_parser_read_u32(const struct device_node *np, + const char *propname, u32 *out_value) +{ + return -ENODEV; +} + +static inline int dsi_parser_read_u32_array(const struct device_node *np, + const char *propname, u32 *out_values, size_t sz) +{ + return -ENODEV; +} + +static inline int dsi_parser_read_string(const struct device_node *np, + const char *propname, const char **out_string) +{ + return -ENODEV; +} + +static inline struct device_node *dsi_parser_get_child_by_name( + const struct device_node *node, + const char *name) +{ + return NULL; +} + +static inline int dsi_parser_get_child_count(const struct device_node *np) +{ + return -ENODEV; +} + +static inline struct property *dsi_parser_find_property( + const struct device_node *np, + const char *name, int *lenp) +{ + return NULL; +} + +static inline struct device_node *dsi_parser_get_next_child( + const struct device_node *np, + struct device_node *prev) +{ + return NULL; +} + +static inline int dsi_parser_count_u32_elems(const struct device_node *np, + const char *propname) +{ + return -ENODEV; +} + +static inline int dsi_parser_count_strings(const struct device_node *np, + const char *propname) +{ + return -ENODEV; +} + +static inline int dsi_parser_read_string_index(const struct device_node *np, + const char *propname, + int index, const char **output) +{ + return -ENODEV; +} + +static inline int dsi_parser_get_named_gpio(struct device_node *np, + const char *propname, int index) +{ + return -ENODEV; +} + +#endif /* CONFIG_DSI_PARSER */ + +#define dsi_for_each_child_node(parent, child) \ + for (child = utils->get_next_child(parent, NULL); \ + child != NULL; \ + child = utils->get_next_child(parent, child)) + +struct dsi_parser_utils { + void *data; + struct device_node *node; + + const void *(*get_property)(const struct device_node *np, + const char *name, int *lenp); + int (*read_u64)(const struct device_node *np, + const char *propname, u64 *out_value); + int (*read_u32)(const struct device_node *np, + const char *propname, u32 *out_value); + bool (*read_bool)(const struct device_node *np, + const char *propname); + int (*read_u32_array)(const struct device_node *np, + const char *propname, u32 *out_values, size_t sz); + int (*read_string)(const struct device_node *np, const char *propname, + const char **out_string); + struct device_node *(*get_child_by_name)( + const struct device_node *node, + const char *name); + int (*get_child_count)(const struct device_node *np); + struct property *(*find_property)(const struct device_node *np, + const char *name, int *lenp); + struct device_node *(*get_next_child)(const struct device_node *np, + struct device_node *prev); + int (*count_u32_elems)(const struct device_node *np, + const char *propname); + int (*get_named_gpio)(struct device_node *np, + const char *propname, int index); + int (*get_available_child_count)(const struct device_node *np); +}; + +static inline struct dsi_parser_utils *dsi_parser_get_of_utils(void) +{ + static struct dsi_parser_utils of_utils = { + .get_property = of_get_property, + .read_bool = of_property_read_bool, + .read_u64 = of_property_read_u64, + .read_u32 = of_property_read_u32, + .read_u32_array = of_property_read_u32_array, + .read_string = of_property_read_string, + .get_child_by_name = of_get_child_by_name, + .get_child_count = of_get_child_count, + .get_available_child_count = of_get_available_child_count, + .find_property = of_find_property, + .get_next_child = of_get_next_child, + .count_u32_elems = of_property_count_u32_elems, + .get_named_gpio = of_get_named_gpio, + }; + + return &of_utils; +} + +static inline struct dsi_parser_utils *dsi_parser_get_parser_utils(void) +{ + static struct dsi_parser_utils parser_utils = { + .get_property = dsi_parser_get_property, + .read_bool = dsi_parser_read_bool, + .read_u64 = dsi_parser_read_u64, + .read_u32 = dsi_parser_read_u32, + .read_u32_array = dsi_parser_read_u32_array, + .read_string = dsi_parser_read_string, + .get_child_by_name = dsi_parser_get_child_by_name, + .get_child_count = dsi_parser_get_child_count, + .get_available_child_count = dsi_parser_get_child_count, + .find_property = dsi_parser_find_property, + .get_next_child = dsi_parser_get_next_child, + .count_u32_elems = dsi_parser_count_u32_elems, + .get_named_gpio = dsi_parser_get_named_gpio, + }; + + return &parser_utils; +} +#endif diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c index 60deeeee3fe3056a75a9056ecfbece6ae0563778..3ba54ccc821205b329444dd72efb54dd0405d07a 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c @@ -17,11 +17,13 @@ #include #include "dsi_pwr.h" +#include "dsi_parser.h" /* * dsi_pwr_parse_supply_node() - parse power supply node from root device node */ -static int dsi_pwr_parse_supply_node(struct device_node *root, +static int dsi_pwr_parse_supply_node(struct dsi_parser_utils *utils, + struct device_node *root, struct dsi_regulator_info *regs) { int rc = 0; @@ -29,10 +31,10 @@ static int dsi_pwr_parse_supply_node(struct device_node *root, u32 tmp = 0; struct device_node *node = NULL; - for_each_child_of_node(root, node) { + dsi_for_each_child_node(root, node) { const char *st = NULL; - rc = of_property_read_string(node, "qcom,supply-name", &st); + rc = utils->read_string(node, "qcom,supply-name", &st); if (rc) { pr_err("failed to read name, rc = %d\n", rc); goto error; @@ -42,32 +44,28 @@ static int dsi_pwr_parse_supply_node(struct device_node *root, ARRAY_SIZE(regs->vregs[i].vreg_name), "%s", st); - rc = of_property_read_u32(node, "qcom,supply-min-voltage", - &tmp); + rc = utils->read_u32(node, "qcom,supply-min-voltage", &tmp); if (rc) { pr_err("failed to read min voltage, rc = %d\n", rc); goto error; } regs->vregs[i].min_voltage = tmp; - rc = of_property_read_u32(node, "qcom,supply-max-voltage", - &tmp); + rc = utils->read_u32(node, "qcom,supply-max-voltage", &tmp); if (rc) { pr_err("failed to read max voltage, rc = %d\n", rc); goto error; } regs->vregs[i].max_voltage = tmp; - rc = of_property_read_u32(node, "qcom,supply-enable-load", - &tmp); + rc = utils->read_u32(node, "qcom,supply-enable-load", &tmp); if (rc) { pr_err("failed to read enable load, rc = %d\n", rc); goto error; } regs->vregs[i].enable_load = tmp; - rc = of_property_read_u32(node, "qcom,supply-disable-load", - &tmp); + rc = utils->read_u32(node, "qcom,supply-disable-load", &tmp); if (rc) { pr_err("failed to read disable load, rc = %d\n", rc); goto error; @@ -75,8 +73,7 @@ static int dsi_pwr_parse_supply_node(struct device_node *root, regs->vregs[i].disable_load = tmp; /* Optional values */ - rc = of_property_read_u32(node, "qcom,supply-pre-on-sleep", - &tmp); + rc = utils->read_u32(node, "qcom,supply-pre-on-sleep", &tmp); if (rc) { pr_debug("pre-on-sleep not specified\n"); rc = 0; @@ -84,8 +81,7 @@ static int dsi_pwr_parse_supply_node(struct device_node *root, regs->vregs[i].pre_on_sleep = tmp; } - rc = of_property_read_u32(node, "qcom,supply-pre-off-sleep", - &tmp); + rc = utils->read_u32(node, "qcom,supply-pre-off-sleep", &tmp); if (rc) { pr_debug("pre-off-sleep not specified\n"); rc = 0; @@ -93,8 +89,7 @@ static int dsi_pwr_parse_supply_node(struct device_node *root, regs->vregs[i].pre_off_sleep = tmp; } - rc = of_property_read_u32(node, "qcom,supply-post-on-sleep", - &tmp); + rc = utils->read_u32(node, "qcom,supply-post-on-sleep", &tmp); if (rc) { pr_debug("post-on-sleep not specified\n"); rc = 0; @@ -102,8 +97,7 @@ static int dsi_pwr_parse_supply_node(struct device_node *root, regs->vregs[i].post_on_sleep = tmp; } - rc = of_property_read_u32(node, "qcom,supply-post-off-sleep", - &tmp); + rc = utils->read_u32(node, "qcom,supply-post-off-sleep", &tmp); if (rc) { pr_debug("post-off-sleep not specified\n"); rc = 0; @@ -221,22 +215,23 @@ static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable) * * return: error code in case of failure or 0 for success. */ -int dsi_pwr_of_get_vreg_data(struct device_node *of_node, +int dsi_pwr_of_get_vreg_data(struct dsi_parser_utils *utils, struct dsi_regulator_info *regs, char *supply_name) { int rc = 0; struct device_node *supply_root_node = NULL; - if (!of_node || !regs) { + if (!utils || !regs) { pr_err("Bad params\n"); return -EINVAL; } regs->count = 0; - supply_root_node = of_get_child_by_name(of_node, supply_name); + supply_root_node = utils->get_child_by_name(utils->data, supply_name); if (!supply_root_node) { - supply_root_node = of_parse_phandle(of_node, supply_name, 0); + supply_root_node = of_parse_phandle(utils->node, + supply_name, 0); if (!supply_root_node) { pr_debug("No supply entry present for %s\n", supply_name); @@ -244,7 +239,7 @@ int dsi_pwr_of_get_vreg_data(struct device_node *of_node, } } - regs->count = of_get_available_child_count(supply_root_node); + regs->count = utils->get_available_child_count(supply_root_node); if (regs->count == 0) { pr_err("No vregs defined for %s\n", supply_name); return -EINVAL; @@ -256,7 +251,7 @@ int dsi_pwr_of_get_vreg_data(struct device_node *of_node, return -ENOMEM; } - rc = dsi_pwr_parse_supply_node(supply_root_node, regs); + rc = dsi_pwr_parse_supply_node(utils, supply_root_node, regs); if (rc) { pr_err("failed to parse supply node for %s, rc = %d\n", supply_name, rc); @@ -285,6 +280,7 @@ int dsi_pwr_get_dt_vreg_data(struct device *dev, struct device_node *of_node = NULL; struct device_node *supply_node = NULL; struct device_node *supply_root_node = NULL; + struct dsi_parser_utils utils = *dsi_parser_get_of_utils(); if (!dev || !regs) { pr_err("Bad params\n"); @@ -318,7 +314,10 @@ int dsi_pwr_get_dt_vreg_data(struct device *dev, return -ENOMEM; } - rc = dsi_pwr_parse_supply_node(supply_root_node, regs); + utils.data = of_node; + utils.node = of_node; + + rc = dsi_pwr_parse_supply_node(&utils, supply_root_node, regs); if (rc) { pr_err("failed to parse supply node for %s, rc = %d\n", supply_name, rc); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h index 4d85244fc5cc1494b93ec5b7d94a79d9ef78e8b8..35b0601d182388f1350e551607f34aecbb237419 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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,8 @@ #include #include +struct dsi_parser_utils; + /** * struct dsi_vreg - regulator information for DSI regulators * @vreg: Handle to the regulator. @@ -66,7 +68,7 @@ struct dsi_regulator_info { * * return: error code in case of failure or 0 for success. */ -int dsi_pwr_of_get_vreg_data(struct device_node *of_node, +int dsi_pwr_of_get_vreg_data(struct dsi_parser_utils *utils, struct dsi_regulator_info *regs, char *supply_name); diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_14nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_14nm.c index fe15aa64086f213a642d244f78a08c1bb657098c..71fe60e5f01f1e05e99b45d35db3e47e3dba0bf6 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_14nm.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_14nm.c @@ -698,7 +698,7 @@ static unsigned long dsi_pll_14nm_postdiv_recalc_rate(struct clk_hw *hw, val &= div_mask(width); return divider_recalc_rate(hw, parent_rate, val, NULL, - postdiv->flags); + postdiv->flags, width); } static long dsi_pll_14nm_postdiv_round_rate(struct clk_hw *hw, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 5fff7522101891e8558a61a8eebe86513380d206..9aef53dc65ea50415bff9f8b4bd3d58cff0e7b0c 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -47,7 +47,6 @@ #include "msm_gpu.h" #include "msm_kms.h" #include "sde_wb.h" -#include "dsi_display.h" /* * MSM driver version: @@ -991,7 +990,7 @@ static void msm_lastclose(struct drm_device *dev) struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; struct drm_modeset_acquire_ctx ctx; - int i; + int i, rc; /* * clean up vblank disable immediately as this is the last close. @@ -1009,15 +1008,31 @@ static void msm_lastclose(struct drm_device *dev) if (priv->fbdev) { drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); - } else { - drm_modeset_acquire_init(&ctx, 0); - drm_modeset_lock_all_ctx(dev, &ctx); - msm_disable_all_modes(dev, &ctx); - if (kms && kms->funcs && kms->funcs->lastclose) - kms->funcs->lastclose(kms, &ctx); - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); + return; } + + drm_modeset_acquire_init(&ctx, 0); +retry: + rc = drm_modeset_lock_all_ctx(dev, &ctx); + if (rc) + goto fail; + + rc = msm_disable_all_modes(dev, &ctx); + if (rc) + goto fail; + + if (kms && kms->funcs && kms->funcs->lastclose) + kms->funcs->lastclose(kms, &ctx); + +fail: + if (rc == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } else if (rc) { + pr_err("last close failed: %d\n", rc); + } + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } static irqreturn_t msm_irq(int irq, void *arg) @@ -1824,7 +1839,6 @@ static int add_display_components(struct device *dev, { struct device *mdp_dev = NULL; struct device_node *node; - const char *name; int ret; if (of_device_is_compatible(dev->of_node, "qcom,sde-kms")) { @@ -1839,17 +1853,6 @@ static int add_display_components(struct device *dev, component_match_add(dev, matchptr, compare_of, node); } - for (i = 0; i < MAX_DSI_ACTIVE_DISPLAY; i++) { - node = dsi_display_get_boot_display(i); - - if (node != NULL) { - name = of_get_property(node, "label", NULL); - component_match_add(dev, matchptr, compare_of, - node); - pr_debug("Added component = %s\n", name); - } - } - return 0; } diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 8f246aad61fc5a3267e998ed4d1d4fd918ce3885..0df1b3eee15b93545a79986f25224ca9c89c9954 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -668,6 +668,8 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, int npages, unsigned int flags); +struct device *msm_gem_get_aspace_device(struct msm_gem_address_space *aspace); + void msm_gem_address_space_put(struct msm_gem_address_space *aspace); struct msm_gem_address_space * diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 48ce0e5ead54633b41995dca7a703af8c71b58f1..ba2b637989731ee84cb5d1e9dd6b9699ac967191 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -26,6 +26,7 @@ #include "msm_gem.h" #include "msm_gpu.h" #include "msm_mmu.h" +#include "sde_dbg.h" static void msm_gem_vunmap_locked(struct drm_gem_object *obj); @@ -322,14 +323,20 @@ uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj) dma_addr_t msm_gem_get_dma_addr(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); - struct drm_device *dev = obj->dev; + struct sg_table *sgt; - if (IS_ERR_OR_NULL(msm_obj->sgt)) { - dev_err(dev->dev, "invalid scatter/gather table\n"); - return 0; + if (!msm_obj->sgt) { + sgt = dma_buf_map_attachment(obj->import_attach, + DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sgt)) { + DRM_ERROR("dma_buf_map_attachment failure, err=%d\n", + PTR_ERR(sgt)); + return 0; + } + msm_obj->sgt = sgt; } - return sg_dma_address(msm_obj->sgt->sgl); + return sg_phys(msm_obj->sgt->sgl); } static struct msm_gem_vma *add_vma(struct drm_gem_object *obj, @@ -416,9 +423,44 @@ int msm_gem_get_iova(struct drm_gem_object *obj, if (!vma) { struct page **pages; + struct device *dev; + struct dma_buf *dmabuf; + bool reattach = false; + + /* + * both secure/non-secure domains are attached with the default + * devive (non-sec) with dma_buf_attach during + * msm_gem_prime_import. detach and attach the correct device + * to the dma_buf based on the aspace domain. + */ + dev = msm_gem_get_aspace_device(aspace); + if (dev && obj->import_attach && + (dev != obj->import_attach->dev)) { + dmabuf = obj->import_attach->dmabuf; + + DRM_DEBUG("detach nsec-dev:%pK attach sec-dev:%pK\n", + obj->import_attach->dev, dev); + SDE_EVT32(obj->import_attach->dev, dev, msm_obj->sgt); + + + if (msm_obj->sgt) + dma_buf_unmap_attachment(obj->import_attach, + msm_obj->sgt, + DMA_BIDIRECTIONAL); + dma_buf_detach(dmabuf, obj->import_attach); + + obj->import_attach = dma_buf_attach(dmabuf, dev); + if (IS_ERR(obj->import_attach)) { + DRM_ERROR("dma_buf_attach failure, err=%ld\n", + PTR_ERR(obj->import_attach)); + goto unlock; + } + reattach = true; + } /* perform delayed import for buffers without existing sgt */ - if ((msm_obj->flags & MSM_BO_EXTBUF) && !(msm_obj->sgt)) { + if (((msm_obj->flags & MSM_BO_EXTBUF) && !(msm_obj->sgt)) + || reattach) { ret = msm_gem_delayed_import(obj); if (ret) { DRM_ERROR("delayed dma-buf import failed %d\n", diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index ec007fd7167412191472943418a0384a4d628412..58fe9bc328d6e488ca10ae605fce9eb9cf676749 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -318,6 +318,16 @@ msm_gem_map_vma(struct msm_gem_address_space *aspace, return -EINVAL; } +struct device *msm_gem_get_aspace_device(struct msm_gem_address_space *aspace) +{ + struct device *client_dev = NULL; + + if (aspace && aspace->mmu && aspace->mmu->funcs->get_dev) + client_dev = aspace->mmu->funcs->get_dev(aspace->mmu); + + return client_dev; +} + void msm_gem_add_obj_to_aspace_active_list( struct msm_gem_address_space *aspace, struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index e76d42c5748e87d002339992f75c45d6e1f089b8..ee6cbcd580794c477adeb61766b13ac4914f90f0 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -53,6 +53,7 @@ struct msm_mmu_funcs { uint32_t dest_address, uint32_t size, int prot); int (*one_to_one_unmap)(struct msm_mmu *mmu, uint32_t dest_address, uint32_t size); + struct device *(*get_dev)(struct msm_mmu *mmu); }; struct msm_mmu { diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c index 72c7caab4643e9f579e1d0ca3ca32a4e13bba717..adc440cee46b26207d3c0b4c32b282a30af152e5 100644 --- a/drivers/gpu/drm/msm/msm_smmu.c +++ b/drivers/gpu/drm/msm/msm_smmu.c @@ -211,6 +211,13 @@ static void msm_smmu_destroy(struct msm_mmu *mmu) kfree(smmu); } +struct device *msm_smmu_get_dev(struct msm_mmu *mmu) +{ + struct msm_smmu *smmu = to_msm_smmu(mmu); + + return smmu->client_dev; +} + static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, int dir, u32 flags) { @@ -242,7 +249,7 @@ static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, &sgt->sgl->dma_address, sgt->sgl->dma_length, dir, attrs); SDE_EVT32(sgt->sgl->dma_address, sgt->sgl->dma_length, - dir, attrs); + dir, attrs, client->secure); } return 0; @@ -265,7 +272,7 @@ static void msm_smmu_unmap_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, &sgt->sgl->dma_address, sgt->sgl->dma_length, dir); SDE_EVT32(sgt->sgl->dma_address, sgt->sgl->dma_length, - dir); + dir, client->secure); } if (!(flags & MSM_BO_EXTBUF)) @@ -292,6 +299,7 @@ static const struct msm_mmu_funcs funcs = { .set_attribute = msm_smmu_set_attribute, .one_to_one_map = msm_smmu_one_to_one_map, .one_to_one_unmap = msm_smmu_one_to_one_unmap, + .get_dev = msm_smmu_get_dev, }; static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = { diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 6ee35ca379b06deca1cb268b34aca2b8975a3ca9..82e70699c1a54baf720bdecd30f8b48ff621002a 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -889,7 +889,7 @@ int sde_connector_roi_v1_check_roi(struct drm_connector_state *conn_state) static int _sde_connector_set_roi_v1( struct sde_connector *c_conn, struct sde_connector_state *c_state, - void *usr_ptr) + void __user *usr_ptr) { struct sde_drm_roi_v1 roi_v1; int i; @@ -940,7 +940,7 @@ static int _sde_connector_set_roi_v1( static int _sde_connector_set_ext_hdr_info( struct sde_connector *c_conn, struct sde_connector_state *c_state, - void *usr_ptr) + void __user *usr_ptr) { int rc = 0; struct drm_connector *connector; @@ -1054,7 +1054,10 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, if (!val) goto end; - rc = sde_fence_create(&c_conn->retire_fence, &fence_fd, 0); + /* + * update the the offset to a timeline for commit completion + */ + rc = sde_fence_create(&c_conn->retire_fence, &fence_fd, 1); if (rc) { SDE_ERROR("fence create failed rc:%d\n", rc); goto end; @@ -1071,7 +1074,8 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, } break; case CONNECTOR_PROP_ROI_V1: - rc = _sde_connector_set_roi_v1(c_conn, c_state, (void *)val); + rc = _sde_connector_set_roi_v1(c_conn, + c_state, (void __user *)val); if (rc) SDE_ERROR_CONN(c_conn, "invalid roi_v1, rc: %d\n", rc); break; @@ -1094,7 +1098,7 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, if (idx == CONNECTOR_PROP_HDR_METADATA) { rc = _sde_connector_set_ext_hdr_info(c_conn, - c_state, (void *)val); + c_state, (void __user *)val); if (rc) SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc); } diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index a7c21c111c1c1db9de778dd2670c839c88ef2fbe..a5e293b5d7dd6dd45cebd456f331d732d5cb91d1 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -2453,6 +2453,23 @@ static void _sde_crtc_set_input_fence_timeout(struct sde_crtc_state *cstate) cstate->input_fence_timeout_ns *= NSEC_PER_MSEC; } +/** + * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings + * @cstate: Pointer to sde crtc state + */ +static void _sde_crtc_clear_dim_layers_v1(struct sde_crtc_state *cstate) +{ + u32 i; + + if (!cstate) + return; + + for (i = 0; i < cstate->num_dim_layers; i++) + memset(&cstate->dim_layer[i], 0, sizeof(cstate->dim_layer[i])); + + cstate->num_dim_layers = 0; +} + /** * _sde_crtc_set_dim_layer_v1 - copy dim layer settings from userspace * @cstate: Pointer to sde crtc state @@ -2473,6 +2490,8 @@ static void _sde_crtc_set_dim_layer_v1(struct sde_crtc_state *cstate, dim_layer = cstate->dim_layer; if (!usr_ptr) { + /* usr_ptr is null when setting the default property value */ + _sde_crtc_clear_dim_layers_v1(cstate); SDE_DEBUG("dim_layer data removed\n"); return; } @@ -4094,6 +4113,7 @@ static void sde_crtc_handle_power_event(u32 event_type, void *arg) static void sde_crtc_disable(struct drm_crtc *crtc) { + struct sde_kms *sde_kms; struct sde_crtc *sde_crtc; struct sde_crtc_state *cstate; struct drm_encoder *encoder; @@ -4109,6 +4129,12 @@ static void sde_crtc_disable(struct drm_crtc *crtc) return; } + sde_kms = _sde_crtc_get_kms(crtc); + if (!sde_kms) { + SDE_ERROR("invalid kms\n"); + return; + } + if (!sde_kms_power_resource_is_enabled(crtc->dev)) { SDE_ERROR("power resource is not enabled\n"); return; @@ -4174,7 +4200,9 @@ static void sde_crtc_disable(struct drm_crtc *crtc) } spin_unlock_irqrestore(&sde_crtc->spin_lock, flags); - sde_core_perf_crtc_update(crtc, 0, true); + /* avoid clk/bw downvote if cont-splash is enabled */ + if (!sde_kms->splash_data.cont_splash_en) + sde_core_perf_crtc_update(crtc, 0, true); drm_for_each_encoder(encoder, crtc->dev) { if (encoder->crtc != crtc) @@ -4329,46 +4357,6 @@ static int pstate_cmp(const void *a, const void *b) return rc; } -static int _sde_crtc_excl_rect_overlap_check(struct plane_state pstates[], - int cnt, int curr_cnt, struct sde_rect *excl_rect) -{ - struct sde_rect dst_rect, intersect; - int i, rc = -EINVAL; - const struct drm_plane_state *pstate; - - for (i = 0; i < cnt; i++) { - if (i == curr_cnt) - continue; - - pstate = pstates[i].drm_pstate; - POPULATE_RECT(&dst_rect, pstate->crtc_x, pstate->crtc_y, - pstate->crtc_w, pstate->crtc_h, false); - sde_kms_rect_intersect(&dst_rect, excl_rect, &intersect); - - /* complete intersection of excl_rect is required */ - if (intersect.w == excl_rect->w && intersect.h == excl_rect->h - /* intersecting rect should be in another z_order */ - && pstates[curr_cnt].stage != pstates[i].stage) { - rc = 0; - goto end; - } - } - - SDE_ERROR( - "no overlapping rect for [%d] z_pos:%d, excl_rect:{%d,%d,%d,%d}\n", - i, pstates[curr_cnt].stage, - excl_rect->x, excl_rect->y, excl_rect->w, excl_rect->h); - for (i = 0; i < cnt; i++) { - pstate = pstates[i].drm_pstate; - SDE_ERROR("[%d] p:%d, z_pos:%d, src:{%d,%d,%d,%d}\n", - i, pstate->plane->base.id, pstates[i].stage, - pstate->crtc_x, pstate->crtc_y, - pstate->crtc_w, pstate->crtc_h); - } -end: - return rc; -} - /* no input validation - caller API has all the checks */ static int _sde_crtc_excl_dim_layer_check(struct drm_crtc_state *state, struct plane_state pstates[], int cnt) @@ -4401,17 +4389,16 @@ static int _sde_crtc_excl_dim_layer_check(struct drm_crtc_state *state, } } - /* this is traversing on sorted z-order pstates */ + /* log all src and excl_rect, useful for debugging */ for (i = 0; i < cnt; i++) { pstate = pstates[i].drm_pstate; sde_pstate = to_sde_plane_state(pstate); - if (sde_pstate->excl_rect.w && sde_pstate->excl_rect.h) { - /* check overlap on any other z-order */ - rc = _sde_crtc_excl_rect_overlap_check(pstates, cnt, - i, &sde_pstate->excl_rect); - if (rc) - goto end; - } + SDE_DEBUG("p %d z %d src{%d,%d,%d,%d} excl_rect{%d,%d,%d,%d}\n", + pstate->plane->base.id, pstates[i].stage, + pstate->crtc_x, pstate->crtc_y, + pstate->crtc_w, pstate->crtc_h, + sde_pstate->excl_rect.x, sde_pstate->excl_rect.y, + sde_pstate->excl_rect.w, sde_pstate->excl_rect.h); } end: @@ -5209,6 +5196,13 @@ static int _sde_crtc_get_output_fence(struct drm_crtc *crtc, */ offset = is_cmd ? 0 : (offset + conn_offset); + /* + * Hwcomposer now queries the fences using the commit list in atomic + * commit ioctl. The offset should be set to next timeline + * which will be incremented during the prepare commit phase + */ + offset++; + return sde_fence_create(&sde_crtc->output_fence, val, offset); } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 65162c67d316e8243157a0e2d49d34221d702515..239afa447f70bf0687beda5fc0b6b5f6f44ef1cc 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -205,6 +205,7 @@ enum sde_enc_rc_states { * @input_handler: handler for input device events * @topology: topology of the display * @vblank_enabled: boolean to track userspace vblank vote + * @idle_pc_restore: flag to indicate idle_pc_restore happened * @rsc_config: rsc configuration for display vtotal, fps, etc. * @cur_conn_roi: current connector roi * @prv_conn_roi: previous connector roi to optimize if unchanged @@ -251,6 +252,7 @@ struct sde_encoder_virt { struct input_handler *input_handler; struct msm_display_topology topology; bool vblank_enabled; + bool idle_pc_restore; struct sde_rsc_cmd_config rsc_config; struct sde_rect cur_conn_roi; @@ -2688,7 +2690,8 @@ static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc) sde_kms->catalog); if (sde_enc->cur_master->hw_ctl && - sde_enc->cur_master->hw_ctl->ops.setup_intf_cfg_v1) + sde_enc->cur_master->hw_ctl->ops.setup_intf_cfg_v1 && + !sde_kms->splash_data.cont_splash_en) sde_enc->cur_master->hw_ctl->ops.setup_intf_cfg_v1( sde_enc->cur_master->hw_ctl, &sde_enc->cur_master->intf_cfg_v1); @@ -2712,11 +2715,18 @@ void sde_encoder_virt_restore(struct drm_encoder *drm_enc) sde_enc = to_sde_encoder_virt(drm_enc); memset(&sde_enc->cur_master->intf_cfg_v1, 0, sizeof(sde_enc->cur_master->intf_cfg_v1)); + sde_enc->idle_pc_restore = true; for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; - if (phys && (phys != sde_enc->cur_master) && phys->ops.restore) + if (!phys) + continue; + + if (phys->hw_ctl && phys->hw_ctl->ops.clear_pending_flush) + phys->hw_ctl->ops.clear_pending_flush(phys->hw_ctl); + + if ((phys != sde_enc->cur_master) && phys->ops.restore) phys->ops.restore(phys); } @@ -2901,8 +2911,11 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_STOP); for (i = 0; i < sde_enc->num_phys_encs; i++) { - if (sde_enc->phys_encs[i]) + if (sde_enc->phys_encs[i]) { + sde_enc->phys_encs[i]->cont_splash_settings = false; + sde_enc->phys_encs[i]->cont_splash_single_flush = 0; sde_enc->phys_encs[i]->connector = NULL; + } } sde_enc->cur_master = NULL; @@ -3399,12 +3412,8 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) if (!phys->ops.needs_single_flush || !phys->ops.needs_single_flush(phys)) _sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0); - else if (ctl->ops.get_pending_flush) { - pending_kickoff_cnt = - sde_encoder_phys_inc_pending(phys); + else if (ctl->ops.get_pending_flush) ctl->ops.get_pending_flush(ctl, &pending_flush); - SDE_EVT32(pending_kickoff_cnt); - } } /* for split flush, combine pending flush masks and send to master */ @@ -3417,6 +3426,26 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) _sde_encoder_trigger_start(sde_enc->cur_master); + /* update pending_kickoff_cnt AFTER next frame is queued in HW */ + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + if (!phys || phys->enable_state == SDE_ENC_DISABLED) + continue; + + if (!phys->ops.needs_single_flush || + !phys->ops.needs_single_flush(phys)) { + pending_kickoff_cnt = + sde_encoder_phys_inc_pending(phys); + SDE_EVT32(pending_kickoff_cnt, SDE_EVTLOG_FUNC_CASE1); + } else { + pending_kickoff_cnt = + sde_encoder_phys_inc_pending(phys); + SDE_EVT32(pending_kickoff_cnt, + pending_flush.pending_flush_mask, + SDE_EVTLOG_FUNC_CASE2); + } + } } static void _sde_encoder_ppsplit_swap_intf_for_right_only_update( @@ -3577,7 +3606,13 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc) if (phys && phys->hw_ctl) { ctl = phys->hw_ctl; - if (ctl->ops.clear_pending_flush) + /* + * avoid clearing the pending flush during the first + * frame update after idle power collpase as the + * restore path would have updated the pending flush + */ + if (!sde_enc->idle_pc_restore && + ctl->ops.clear_pending_flush) ctl->ops.clear_pending_flush(ctl); /* update only for command mode primary ctl */ @@ -3587,6 +3622,7 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc) ctl->ops.trigger_pending(ctl); } } + sde_enc->idle_pc_restore = false; } static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) @@ -4828,8 +4864,9 @@ int sde_encoder_update_caps_for_cont_splash(struct drm_encoder *encoder) struct sde_connector *sde_conn = NULL; struct sde_connector_state *sde_conn_state = NULL; struct drm_display_mode *drm_mode = NULL; - struct sde_rm_hw_iter dsc_iter, pp_iter, ctl_iter; - int ret = 0, i; + struct sde_rm_hw_iter dsc_iter, pp_iter, ctl_iter, intf_iter; + struct sde_encoder_phys *phys_enc; + int ret = 0, i, idx; if (!encoder) { SDE_ERROR("invalid drm enc\n"); @@ -4955,14 +4992,34 @@ int sde_encoder_update_caps_for_cont_splash(struct drm_encoder *encoder) sde_enc->hw_dsc[i] = (struct sde_hw_dsc *) dsc_iter.hw; } - sde_rm_init_hw_iter(&ctl_iter, encoder->base.id, SDE_HW_BLK_CTL); + /* + * If we have multiple phys encoders with one controller, make + * sure to populate the controller pointer in both phys encoders. + */ + for (idx = 0; idx < sde_enc->num_phys_encs; idx++) { + phys_enc = sde_enc->phys_encs[idx]; + phys_enc->hw_ctl = NULL; + + sde_rm_init_hw_iter(&ctl_iter, encoder->base.id, + SDE_HW_BLK_CTL); + for (i = 0; i < sde_enc->num_phys_encs; i++) { + if (sde_rm_get_hw(&sde_kms->rm, &ctl_iter)) { + phys_enc->hw_ctl = + (struct sde_hw_ctl *) ctl_iter.hw; + pr_debug("HW CTL intf_idx:%d hw_ctl:[0x%pK]\n", + phys_enc->intf_idx, phys_enc->hw_ctl); + } + } + } + + sde_rm_init_hw_iter(&intf_iter, encoder->base.id, SDE_HW_BLK_INTF); for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; - phys->hw_ctl = NULL; - if (!sde_rm_get_hw(&sde_kms->rm, &ctl_iter)) + phys->hw_intf = NULL; + if (!sde_rm_get_hw(&sde_kms->rm, &intf_iter)) break; - phys->hw_ctl = (struct sde_hw_ctl *) ctl_iter.hw; + phys->hw_intf = (struct sde_hw_intf *) intf_iter.hw; } for (i = 0; i < sde_enc->num_phys_encs; i++) { @@ -4974,14 +5031,18 @@ int sde_encoder_update_caps_for_cont_splash(struct drm_encoder *encoder) return -EINVAL; } + /* update connector for master and slave phys encoders */ + phys->connector = conn; + phys->cont_splash_single_flush = + sde_kms->splash_data.single_flush_en; + phys->cont_splash_settings = true; + phys->hw_pp = sde_enc->hw_pp[i]; if (phys->ops.cont_splash_mode_set) phys->ops.cont_splash_mode_set(phys, drm_mode); - if (phys->ops.is_master && phys->ops.is_master(phys)) { - phys->connector = conn; + if (phys->ops.is_master && phys->ops.is_master(phys)) sde_enc->cur_master = phys; - } } return ret; diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h index 3b13289a1829ef3cf3adeb518ebab4c843aa6c12..488a36fe7899b20b461f5a21cf4e5a24bd86ee8b 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -269,6 +269,8 @@ struct sde_encoder_irq { * @pending_kickoff_wq: Wait queue for blocking until kickoff completes * @irq: IRQ tracking structures * @has_intf_te: Interface TE configuration support + * @cont_splash_single_flush Variable to check if single flush is enabled. + * @cont_splash_settings Variable to store continuous splash settings. */ struct sde_encoder_phys { struct drm_encoder *parent; @@ -300,6 +302,8 @@ struct sde_encoder_phys { wait_queue_head_t pending_kickoff_wq; struct sde_encoder_irq irq[INTR_IDX_MAX]; bool has_intf_te; + u32 cont_splash_single_flush; + bool cont_splash_settings; }; static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys) @@ -665,7 +669,9 @@ static inline bool sde_encoder_phys_needs_single_flush( if (!phys_enc) return false; - return phys_enc && (_sde_encoder_phys_is_ppsplit(phys_enc) || - _sde_encoder_phys_is_dual_ctl(phys_enc)); + return phys_enc->cont_splash_settings ? + phys_enc->cont_splash_single_flush : + (_sde_encoder_phys_is_ppsplit(phys_enc) || + _sde_encoder_phys_is_dual_ctl(phys_enc)); } #endif /* __sde_encoder_phys_H__ */ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c index 248d54ddcb920d2eb45a2a9860f4d955ceaabca3..5c4d9f25c935dbbb32842e0621f469b99baf3440 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c @@ -254,7 +254,9 @@ static void sde_encoder_phys_cmd_autorefresh_done_irq(void *arg, int irq_idx) spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); SDE_EVT32_IRQ(DRMID(phys_enc->parent), - phys_enc->hw_pp->idx - PINGPONG_0, new_cnt); + phys_enc->hw_pp->idx - PINGPONG_0, + phys_enc->hw_intf->idx - INTF_0, + new_cnt); /* Signal any waiting atomic commit thread */ wake_up_all(&cmd_enc->autorefresh.kickoff_wq); @@ -376,8 +378,16 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx( { struct sde_encoder_irq *irq; - if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_intf) + if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_ctl) { + SDE_ERROR("invalid args %d %d\n", !phys_enc, + phys_enc ? !phys_enc->hw_pp : 0); + return; + } + + if (phys_enc->has_intf_te && !phys_enc->hw_intf) { + SDE_ERROR("invalid intf configuration\n"); return; + } irq = &phys_enc->irq[INTR_IDX_CTL_START]; irq->hw_idx = phys_enc->hw_ctl->idx; @@ -418,6 +428,13 @@ static void sde_encoder_phys_cmd_cont_splash_mode_set( phys_enc->cached_mode = *adj_mode; phys_enc->enable_state = SDE_ENC_ENABLED; + if (!phys_enc->hw_ctl || !phys_enc->hw_pp) { + SDE_DEBUG("invalid ctl:%d pp:%d\n", + (phys_enc->hw_ctl == NULL), + (phys_enc->hw_pp == NULL)); + return; + } + _sde_encoder_phys_cmd_setup_irq_hw_idx(phys_enc); } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c index 514c3a72ede43a8329960766cf1559a98e2083f3..8d6787fb90f469e0cdd2d0731e31cdf83f0572a0 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -475,8 +475,6 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) SDE_EVT32_IRQ(DRMID(phys_enc->parent), phys_enc->hw_intf->idx - INTF_0, old_cnt, new_cnt, reset_status ? SDE_EVTLOG_ERROR : 0, flush_register, event); - SDE_EVT32_IRQ(DRMID(phys_enc->parent), phys_enc->hw_intf->idx - INTF_0, - old_cnt, new_cnt, flush_register, event); /* Signal any waiting atomic commit thread */ wake_up_all(&phys_enc->pending_kickoff_wq); diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c index 729d14047c9d204263256303dd1b2c8e1511b813..59acecf73550ad45a04ca44173ba0d4c8c0063ae 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c @@ -153,6 +153,71 @@ static void sde_encoder_phys_wb_set_qos_remap( sde_vbif_set_qos_remap(phys_enc->sde_kms, &qos_params); } +static u64 _sde_encoder_phys_wb_get_qos_lut(const struct sde_qos_lut_tbl *tbl, + u32 total_fl) +{ + int i; + + if (!tbl || !tbl->nentry || !tbl->entries) + return 0; + + for (i = 0; i < tbl->nentry; i++) + if (total_fl <= tbl->entries[i].fl) + return tbl->entries[i].lut; + + /* if last fl is zero, use as default */ + if (!tbl->entries[i-1].fl) + return tbl->entries[i-1].lut; + + return 0; +} + +/** + * sde_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for writeback + * @phys_enc: Pointer to physical encoder + */ +static void sde_encoder_phys_wb_set_qos(struct sde_encoder_phys *phys_enc) +{ + struct sde_encoder_phys_wb *wb_enc; + struct sde_hw_wb *hw_wb; + struct sde_hw_wb_qos_cfg qos_cfg; + struct sde_mdss_cfg *catalog; + + if (!phys_enc || !phys_enc->sde_kms || !phys_enc->sde_kms->catalog) { + SDE_ERROR("invalid parameter(s)\n"); + return; + } + catalog = phys_enc->sde_kms->catalog; + + wb_enc = to_sde_encoder_phys_wb(phys_enc); + if (!wb_enc->hw_wb) { + SDE_ERROR("invalid writeback hardware\n"); + return; + } + + hw_wb = wb_enc->hw_wb; + + memset(&qos_cfg, 0, sizeof(struct sde_hw_wb_qos_cfg)); + qos_cfg.danger_safe_en = true; + qos_cfg.danger_lut = + catalog->perf.danger_lut_tbl[SDE_QOS_LUT_USAGE_NRT]; + qos_cfg.safe_lut = + (u32) _sde_encoder_phys_wb_get_qos_lut( + &catalog->perf.sfe_lut_tbl[SDE_QOS_LUT_USAGE_NRT], 0); + qos_cfg.creq_lut = + _sde_encoder_phys_wb_get_qos_lut( + &catalog->perf.qos_lut_tbl[SDE_QOS_LUT_USAGE_NRT], 0); + + if (hw_wb->ops.setup_danger_safe_lut) + hw_wb->ops.setup_danger_safe_lut(hw_wb, &qos_cfg); + + if (hw_wb->ops.setup_creq_lut) + hw_wb->ops.setup_creq_lut(hw_wb, &qos_cfg); + + if (hw_wb->ops.setup_qos_ctrl) + hw_wb->ops.setup_qos_ctrl(hw_wb, &qos_cfg); +} + /** * sde_encoder_phys_setup_cdm - setup chroma down block * @phys_enc: Pointer to physical encoder @@ -710,6 +775,8 @@ static void sde_encoder_phys_wb_setup( sde_encoder_phys_wb_set_qos_remap(phys_enc); + sde_encoder_phys_wb_set_qos(phys_enc); + sde_encoder_phys_setup_cdm(phys_enc, fb, wb_enc->wb_fmt, wb_roi); sde_encoder_phys_wb_setup_fb(phys_enc, fb, wb_roi); diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c index a819a6d1a0ee089927281edd17f827994251d8fc..741f7e00e903a221e8bf731e9c13f56832694dd2 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.c +++ b/drivers/gpu/drm/msm/sde/sde_formats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -275,97 +275,97 @@ static const struct sde_format sde_format_map[] = { INTERLEAVED_RGB_FMT(ARGB1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ARGB4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 5fcde3dae405682119d42ebb355842aa59aa538e..78889591f583153753b3043051457fae46a131c7 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -201,6 +201,8 @@ enum { PERF_CDP_SETTING, PERF_CPU_MASK, PERF_CPU_DMA_LATENCY, + PERF_QOS_LUT_MACROTILE_QSEED, + PERF_SAFE_LUT_MACROTILE_QSEED, PERF_PROP_MAX, }; @@ -497,6 +499,10 @@ static struct sde_prop_type sde_perf_prop[] = { {PERF_CPU_MASK, "qcom,sde-qos-cpu-mask", false, PROP_TYPE_U32}, {PERF_CPU_DMA_LATENCY, "qcom,sde-qos-cpu-dma-latency", false, PROP_TYPE_U32}, + {PERF_QOS_LUT_MACROTILE_QSEED, "qcom,sde-qos-lut-macrotile-qseed", + false, PROP_TYPE_U32_ARRAY}, + {PERF_SAFE_LUT_MACROTILE_QSEED, "qcom,sde-safe-lut-macrotile-qseed", + false, PROP_TYPE_U32_ARRAY}, }; static struct sde_prop_type sspp_prop[] = { @@ -1768,7 +1774,7 @@ static int sde_intf_parse_dt(struct device_node *np, if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev)) set_bit(SDE_INTF_INPUT_CTRL, &intf->features); - if (IS_SDE_MAJOR_MINOR_SAME((sde_cfg->hwversion), + if (IS_SDE_MAJOR_SAME((sde_cfg->hwversion), SDE_HW_VER_500)) set_bit(SDE_INTF_TE, &intf->features); } @@ -3133,6 +3139,18 @@ static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) if (rc) goto freeprop; + rc = _validate_dt_entry(np, + &sde_perf_prop[PERF_QOS_LUT_MACROTILE_QSEED], 1, + &prop_count[PERF_QOS_LUT_MACROTILE_QSEED], NULL); + if (rc) + goto freeprop; + + rc = _validate_dt_entry(np, + &sde_perf_prop[PERF_SAFE_LUT_MACROTILE_QSEED], 1, + &prop_count[PERF_SAFE_LUT_MACROTILE_QSEED], NULL); + if (rc) + goto freeprop; + rc = _read_dt_entry(np, sde_perf_prop, ARRAY_SIZE(sde_perf_prop), prop_count, prop_exists, prop_value); if (rc) @@ -3239,6 +3257,8 @@ static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) PERF_SAFE_LUT_NRT, [SDE_QOS_LUT_USAGE_CWB] = PERF_SAFE_LUT_CWB, + [SDE_QOS_LUT_USAGE_MACROTILE_QSEED] = + PERF_SAFE_LUT_MACROTILE_QSEED, }; const u32 entry_size = 2; int m, count; @@ -3281,6 +3301,8 @@ static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) PERF_QOS_LUT_NRT, [SDE_QOS_LUT_USAGE_CWB] = PERF_QOS_LUT_CWB, + [SDE_QOS_LUT_USAGE_MACROTILE_QSEED] = + PERF_QOS_LUT_MACROTILE_QSEED, }; const u32 entry_size = 3; int m, count; @@ -3430,7 +3452,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, virt_vig_list_size += ARRAY_SIZE(rgb_10bit_formats); if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400) || (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_410)) || - (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_500))) + (IS_SDE_MAJOR_SAME((hw_rev), SDE_HW_VER_500))) vig_list_size += ARRAY_SIZE(p010_ubwc_formats); wb2_list_size += ARRAY_SIZE(rgb_10bit_formats) @@ -3468,7 +3490,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300) || IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400) || IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_410) || - IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_500)) + IS_SDE_MAJOR_SAME((hw_rev), SDE_HW_VER_500)) sde_cfg->has_hdr = true; index = sde_copy_formats(sde_cfg->dma_formats, dma_list_size, @@ -3486,7 +3508,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, index, p010_formats, ARRAY_SIZE(p010_formats)); if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400) || (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_410)) || - (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_500))) + (IS_SDE_MAJOR_SAME((hw_rev), SDE_HW_VER_500))) index += sde_copy_formats(sde_cfg->vig_formats, vig_list_size, index, p010_ubwc_formats, ARRAY_SIZE(p010_ubwc_formats)); @@ -3522,16 +3544,20 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) rc = sde_hardware_format_caps(sde_cfg, hw_rev); if (IS_MSM8996_TARGET(hw_rev)) { - /* update msm8996 target here */ sde_cfg->perf.min_prefill_lines = 21; } else if (IS_MSM8998_TARGET(hw_rev)) { - /* update msm8998 target here */ sde_cfg->has_wb_ubwc = true; sde_cfg->perf.min_prefill_lines = 25; sde_cfg->vbif_qos_nlvl = 4; sde_cfg->ts_prefill_rev = 1; - } else if (IS_SDM845_TARGET(hw_rev) || IS_SDM670_TARGET(hw_rev)) { - /* update sdm845 target here */ + } else if (IS_SDM845_TARGET(hw_rev)) { + sde_cfg->has_wb_ubwc = true; + sde_cfg->perf.min_prefill_lines = 24; + sde_cfg->vbif_qos_nlvl = 8; + sde_cfg->ts_prefill_rev = 2; + sde_cfg->sui_misr_supported = true; + sde_cfg->sui_block_xin_mask = 0x3F71; + } else if (IS_SDM670_TARGET(hw_rev)) { sde_cfg->has_wb_ubwc = true; sde_cfg->perf.min_prefill_lines = 24; sde_cfg->vbif_qos_nlvl = 8; @@ -3544,6 +3570,15 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) sde_cfg->ctl_rev = SDE_CTL_CFG_VERSION_1_0_0; sde_cfg->delay_prg_fetch_start = true; sde_cfg->sui_ns_allowed = true; + sde_cfg->sui_misr_supported = true; + sde_cfg->sui_block_xin_mask = 0x3F71; + } else if (IS_SDMSHRIKE_TARGET(hw_rev)) { + sde_cfg->has_wb_ubwc = true; + sde_cfg->perf.min_prefill_lines = 24; + sde_cfg->vbif_qos_nlvl = 8; + sde_cfg->ts_prefill_rev = 2; + sde_cfg->ctl_rev = SDE_CTL_CFG_VERSION_1_0_0; + sde_cfg->delay_prg_fetch_start = true; } else { SDE_ERROR("unsupported chipset id:%X\n", hw_rev); sde_cfg->perf.min_prefill_lines = 0xffff; @@ -3562,6 +3597,15 @@ static int _sde_hardware_post_caps(struct sde_mdss_cfg *sde_cfg, if (!sde_cfg) return -EINVAL; + if (IS_SM8150_TARGET(hw_rev)) { + sde_cfg->sui_supported_blendstage = + sde_cfg->max_mixer_blendstages - SDE_STAGE_0; + + for (i = 0; i < sde_cfg->sspp_count; i++) + set_bit(SDE_SSPP_QOS_FL_NOCALC, + &sde_cfg->sspp[i].features); + } + for (i = 0; i < sde_cfg->sspp_count; i++) { if (sde_cfg->sspp[i].sblk) { max_horz_deci = max(max_horz_deci, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index 57f0dd342d4d9c489d885d0a01388c140c1d0880..dee834c05895ae82b415b5b6366c3d3fffa327de 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -36,6 +36,9 @@ #define SDE_HW_STEP(rev) ((rev) & 0xFFFF) #define SDE_HW_MAJOR_MINOR(rev) ((rev) >> 16) +#define IS_SDE_MAJOR_SAME(rev1, rev2) \ + (SDE_HW_MAJOR((rev1)) == SDE_HW_MAJOR((rev2))) + #define IS_SDE_MAJOR_MINOR_SAME(rev1, rev2) \ (SDE_HW_MAJOR_MINOR((rev1)) == SDE_HW_MAJOR_MINOR((rev2))) @@ -49,12 +52,14 @@ #define SDE_HW_VER_410 SDE_HW_VER(4, 1, 0) /* sdm670 v1.0 */ #define SDE_HW_VER_500 SDE_HW_VER(5, 0, 0) /* sm8150 v1.0 */ #define SDE_HW_VER_501 SDE_HW_VER(5, 0, 1) /* sm8150 v2.0 */ +#define SDE_HW_VER_510 SDE_HW_VER(5, 1, 0) /* sdmshrike v1.0 */ #define IS_MSM8996_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_170) #define IS_MSM8998_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_300) #define IS_SDM845_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400) #define IS_SDM670_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_410) #define IS_SM8150_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_500) +#define IS_SDMSHRIKE_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_510) #define SDE_HW_BLK_NAME_LEN 16 @@ -151,6 +156,7 @@ enum { * @SDE_SSPP_DGM_CSC Support of color space conversion in DGM block * @SDE_SSPP_SEC_UI_ALLOWED Allows secure-ui layers * @SDE_SSPP_BLOCK_SEC_UI Blocks secure-ui layers + * @SDE_SSPP_QOS_FL_NOCALC Avoid fill level calculation for QoS/danger/safe * @SDE_SSPP_MAX maximum value */ enum { @@ -183,6 +189,7 @@ enum { SDE_SSPP_DGM_CSC, SDE_SSPP_SEC_UI_ALLOWED, SDE_SSPP_BLOCK_SEC_UI, + SDE_SSPP_QOS_FL_NOCALC, SDE_SSPP_MAX }; @@ -445,6 +452,7 @@ enum sde_qos_lut_usage { SDE_QOS_LUT_USAGE_MACROTILE, SDE_QOS_LUT_USAGE_NRT, SDE_QOS_LUT_USAGE_CWB, + SDE_QOS_LUT_USAGE_MACROTILE_QSEED, SDE_QOS_LUT_USAGE_MAX, }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c index 43890964e8109841d40f4af8f90b56fbd5ba293e..4ab6941697a3d71dfdf2ec9db8045555fb51aefd 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c @@ -576,6 +576,42 @@ static inline int sde_hw_ctl_trigger_flush_v1(struct sde_hw_ctl *ctx) return 0; } +static inline u32 sde_hw_ctl_get_intf_v1(struct sde_hw_ctl *ctx) +{ + struct sde_hw_blk_reg_map *c; + u32 intf_active; + + if (!ctx) { + pr_err("Invalid input argument\n"); + return 0; + } + + c = &ctx->hw; + intf_active = SDE_REG_READ(c, CTL_INTF_ACTIVE); + + return intf_active; +} + +static inline u32 sde_hw_ctl_get_intf(struct sde_hw_ctl *ctx) +{ + struct sde_hw_blk_reg_map *c; + u32 ctl_top; + u32 intf_active = 0; + + if (!ctx) { + pr_err("Invalid input argument\n"); + return 0; + } + + c = &ctx->hw; + ctl_top = SDE_REG_READ(c, CTL_TOP); + + intf_active = (ctl_top > 0) ? + BIT(ctl_top - 1) : 0; + + return intf_active; +} + static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 timeout_us) { struct sde_hw_blk_reg_map *c; @@ -1011,6 +1047,7 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, ops->update_bitmask_merge3d = sde_hw_ctl_update_bitmask_merge3d_v1; ops->update_bitmask_cwb = sde_hw_ctl_update_bitmask_cwb_v1; + ops->get_ctl_intf = sde_hw_ctl_get_intf_v1; } else { ops->update_pending_flush = sde_hw_ctl_update_pending_flush; ops->trigger_flush = sde_hw_ctl_trigger_flush; @@ -1020,6 +1057,7 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, ops->update_bitmask_cdm = sde_hw_ctl_update_bitmask_cdm; ops->update_bitmask_wb = sde_hw_ctl_update_bitmask_wb; ops->update_bitmask_intf = sde_hw_ctl_update_bitmask_intf; + ops->get_ctl_intf = sde_hw_ctl_get_intf; } ops->clear_pending_flush = sde_hw_ctl_clear_pending_flush; ops->get_pending_flush = sde_hw_ctl_get_pending_flush; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h index a2db1a8eb5340ba2fa46705bfc931c9549d453f6..08167a400953c6e9da2a6bdcf3197dfaa26c7616 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h @@ -380,6 +380,13 @@ struct sde_hw_ctl_ops { */ u32 (*read_ctl_top)(struct sde_hw_ctl *ctx); + /** + * get interfaces for the active CTL . + * @ctx : ctl path ctx pointer + * @return : bit mask with the active interfaces for the CTL + */ + u32 (*get_ctl_intf)(struct sde_hw_ctl *ctx); + /** * read CTL layers register value and return * the data. diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c index b9350ff9f65db41cc558d1cd797bf63f54bd17ac..eb2ccb1acb12f8d77ba5069a6c2f8825b6d58288 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c @@ -45,7 +45,7 @@ static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp, static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) { - int i = 0, ret; + int i = 0, ret = 0; if (!c || !c->cap || !c->cap->sblk) return; @@ -71,7 +71,7 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) break; case SDE_DSPP_HSIC: if (c->cap->sblk->hsic.version == - SDE_COLOR_PROCESS_VER(0x1, 0x7)) + SDE_COLOR_PROCESS_VER(0x1, 0x7)) { ret = reg_dmav1_init_dspp_op_v4(i, c->idx); if (!ret) c->ops.setup_pa_hsic = @@ -79,6 +79,7 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) else c->ops.setup_pa_hsic = sde_setup_dspp_pa_hsic_v17; + } break; case SDE_DSPP_MEMCOLOR: if (c->cap->sblk->memcolor.version == @@ -107,7 +108,7 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) break; case SDE_DSPP_SIXZONE: if (c->cap->sblk->sixzone.version == - SDE_COLOR_PROCESS_VER(0x1, 0x7)) + SDE_COLOR_PROCESS_VER(0x1, 0x7)) { ret = reg_dmav1_init_dspp_op_v4(i, c->idx); if (!ret) c->ops.setup_sixzone = @@ -115,6 +116,7 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) else c->ops.setup_sixzone = sde_setup_dspp_sixzone_v17; + } break; case SDE_DSPP_DITHER: if (c->cap->sblk->dither.version == diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c index 9b4e07df3e451c617112a20d85c6820347c48080..ecf4a4ff99c1e8067676d7bd9453868898b09a21 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c @@ -805,8 +805,6 @@ static const struct sde_irq_type sde_irq_map[] = { SDE_INTR_INTF_TEAR_WR_PTR, 10}, { SDE_IRQ_TYPE_INTF_TEAR_RD_PTR, INTF_1, SDE_INTR_INTF_TEAR_RD_PTR, 10}, - { SDE_IRQ_TYPE_INTF_TEAR_TE_CHECK, INTF_1, - SDE_INTR_INTF_TEAR_TE_DETECTED, 10}, /* irq_idx: 323 */ { SDE_IRQ_TYPE_INTF_TEAR_TEAR_CHECK, INTF_1, SDE_INTR_INTF_TEAR_TEAR_DETECTED, 10}, @@ -849,13 +847,11 @@ static const struct sde_irq_type sde_irq_map[] = { /* BEGIN MAP_RANGE: 352-383 INTF_2_TEAR INTR */ /* irq_idx: 352-354 */ { SDE_IRQ_TYPE_INTF_TEAR_AUTO_REF, INTF_2, - SDE_INTR_INTF_TEAR_AUTOREFRESH_DONE, 10}, + SDE_INTR_INTF_TEAR_AUTOREFRESH_DONE, 11}, { SDE_IRQ_TYPE_INTF_TEAR_WR_PTR, INTF_2, - SDE_INTR_INTF_TEAR_WR_PTR, 10}, + SDE_INTR_INTF_TEAR_WR_PTR, 11}, { SDE_IRQ_TYPE_INTF_TEAR_RD_PTR, INTF_2, - SDE_INTR_INTF_TEAR_RD_PTR, 10}, - { SDE_IRQ_TYPE_INTF_TEAR_TE_CHECK, INTF_2, - SDE_INTR_INTF_TEAR_TE_DETECTED, 10}, + SDE_INTR_INTF_TEAR_RD_PTR, 11}, /* irq_idx: 355 */ { SDE_IRQ_TYPE_INTF_TEAR_TEAR_CHECK, INTF_2, SDE_INTR_INTF_TEAR_TEAR_DETECTED, 11}, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c index 2da2171364d5046088832a0d6d762aa47a54f016..cf77df03507350a39f13e4a191539844aa613139 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c @@ -263,7 +263,8 @@ static void _setup_mixer_ops(struct sde_mdss_cfg *m, { ops->setup_mixer_out = sde_hw_lm_setup_out; if (IS_SDM845_TARGET(m->hwversion) || IS_SDM670_TARGET(m->hwversion) || - IS_SM8150_TARGET(m->hwversion)) + IS_SM8150_TARGET(m->hwversion) || + IS_SDMSHRIKE_TARGET(m->hwversion)) ops->setup_blend_config = sde_hw_lm_setup_blend_config_sdm845; else ops->setup_blend_config = sde_hw_lm_setup_blend_config; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h index 852c20693077d8e74d2f766e6f1767813b12508f..25e53edd5f3afa57e00c1cec3a75e282a5dcca97 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h @@ -561,19 +561,11 @@ struct sde_splash_lm_hw { /** * struct ctl_top - Struct contains CTL block properties - * @value: Store the CTL block register value - * @mode_sel: stores the mode selected in the CTL block - * @dspp_sel: stores the dspp selected in the CTL block - * @pp_sel: stores the pp selected in the CTL block * @intf_sel: stores the intf selected in the CTL block * @lm: Pointer to store the list of LMs in the CTL block * @ctl_lm_cnt: stores the active number of MDSS "LM" blocks in the CTL block */ struct ctl_top { - u32 value; - u8 mode_sel; - u8 dspp_sel; - u8 pp_sel; u8 intf_sel; struct sde_splash_lm_hw lm[LM_MAX - LM_0]; u8 ctl_lm_cnt; @@ -587,6 +579,9 @@ struct ctl_top { * @splash_base: Base address of continuous splash region reserved * by bootloader * @splash_size: Size of continuous splash region + * @ramdump_base: Base address of ramdump display region reserved + * by bootloader + * @ramdump_size: Size of ramdump buffer region * @top: struct ctl_top objects * @ctl_ids: stores the valid MDSS ctl block ids for the current mode * @lm_ids: stores the valid MDSS layer mixer block ids for the current mode @@ -595,11 +590,14 @@ struct ctl_top { * @lm_cnt: stores the active number of MDSS "LM" blks for the current mode * @dsc_cnt: stores the active number of MDSS "dsc" blks for the current mode * @cont_splash_en: Stores the cont_splash status (enabled/disabled) + * @single_flush_en: Stores if the single flush is enabled. */ struct sde_splash_data { bool resource_handoff_pending; unsigned long splash_base; u32 splash_size; + unsigned long ramdump_base; + u32 ramdump_size; struct ctl_top top[CTL_MAX - CTL_0]; u8 ctl_ids[CTL_MAX - CTL_0]; u8 lm_ids[LM_MAX - LM_0]; @@ -608,6 +606,7 @@ struct sde_splash_data { u8 lm_cnt; u8 dsc_cnt; bool cont_splash_en; + bool single_flush_en; }; /** diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c index 333a7fda464d395dbbc0a391d866b9119e2d210f..9f2073cb9ca89b71152fabff130efda3539c2fda 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c @@ -68,6 +68,7 @@ static uint32_t reg_dma_intr_4_status_offset; static uint32_t reg_dma_intr_clear_offset; static uint32_t reg_dma_ctl_trigger_offset; static uint32_t reg_dma_ctl0_reset_offset; +static uint32_t reg_dma_error_clear_mask; typedef int (*reg_dma_internal_ops) (struct sde_reg_dma_setup_ops_cfg *cfg); @@ -530,8 +531,13 @@ static int write_kick_off_v1(struct sde_reg_dma_kickoff_cfg *cfg) SET_UP_REG_DMA_REG(hw, reg_dma); SDE_REG_WRITE(&hw, reg_dma_opmode_offset, BIT(0)); val = SDE_REG_READ(&hw, reg_dma_intr_4_status_offset); - if (val) - SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic"); + if (val) { + DRM_DEBUG("LUT dma status %x\n", val); + mask = reg_dma_error_clear_mask; + SDE_REG_WRITE(&hw, reg_dma_intr_clear_offset + sizeof(u32) * 4, + mask); + SDE_EVT32(val); + } SDE_REG_WRITE(&hw, reg_dma_ctl_queue_off[cfg->ctl->idx], cfg->dma_buf->iova); @@ -599,6 +605,7 @@ int init_v1(struct sde_hw_reg_dma *cfg) reg_dma_intr_clear_offset = 0xb0; reg_dma_ctl_trigger_offset = 0xd4; reg_dma_ctl0_reset_offset = 0xe4; + reg_dma_error_clear_mask = BIT(0) | BIT(1) | BIT(2) | BIT(16); reg_dma_ctl_queue_off[CTL_0] = reg_dma_ctl0_queue0_cmd0_offset; for (i = CTL_1; i < ARRAY_SIZE(reg_dma_ctl_queue_off); i++) @@ -628,6 +635,8 @@ int init_v11(struct sde_hw_reg_dma *cfg) reg_dma_intr_clear_offset = 0x1a0; reg_dma_ctl_trigger_offset = 0xd4; reg_dma_ctl0_reset_offset = 0x200; + reg_dma_error_clear_mask = BIT(0) | BIT(1) | BIT(2) | BIT(16) | + BIT(17) | BIT(18); reg_dma_ctl_queue_off[CTL_0] = reg_dma_ctl0_queue0_cmd0_offset; for (i = CTL_1; i < ARRAY_SIZE(reg_dma_ctl_queue_off); i++) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c index 2b8295d03427ede7a3bc80ca9fc4021049bcb091..b03fc9ea1360eacc9277c277c40b4c7ba7528ed9 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c @@ -92,7 +92,8 @@ #define QSEED3_COEF_LUT_CTRL_OFF 0x4C static struct sde_reg_dma_buffer *dspp_buf[REG_DMA_FEATURES_MAX][DSPP_MAX]; -static struct sde_reg_dma_buffer *sspp_buf[REG_DMA_FEATURES_MAX][SSPP_MAX]; +static struct sde_reg_dma_buffer + *sspp_buf[SDE_SSPP_RECT_MAX][REG_DMA_FEATURES_MAX][SSPP_MAX]; static u32 feature_map[SDE_DSPP_MAX] = { [SDE_DSPP_VLUT] = VLUT, @@ -189,7 +190,8 @@ static int reg_dma_buf_init(struct sde_reg_dma_buffer **buf, u32 sz); static int reg_dma_dspp_check(struct sde_hw_dspp *ctx, void *cfg, enum sde_reg_dma_features feature); static int reg_dma_sspp_check(struct sde_hw_pipe *ctx, void *cfg, - enum sde_reg_dma_features feature); + enum sde_reg_dma_features feature, + enum sde_sspp_multirect_index idx); static int reg_dma_buf_init(struct sde_reg_dma_buffer **buf, u32 size) { @@ -1576,7 +1578,7 @@ int reg_dmav1_init_sspp_op_v4(int feature, enum sde_sspp idx) int rc = -ENOTSUPP; struct sde_hw_reg_dma_ops *dma_ops; bool is_supported = false; - u32 blk; + u32 blk, i = 0; if (feature >= SDE_SSPP_MAX || idx >= SSPP_MAX) { DRM_ERROR("invalid feature %x max %x sspp idx %x max %xd\n", @@ -1600,15 +1602,25 @@ int reg_dmav1_init_sspp_op_v4(int feature, enum sde_sspp idx) if (!rc) rc = (is_supported) ? 0 : -ENOTSUPP; - if (!rc) - rc = reg_dma_buf_init(&sspp_buf[sspp_feature_map[feature]][idx], + if (!rc) { + for (i = SDE_SSPP_RECT_0; i < SDE_SSPP_RECT_MAX; i++) { + rc = reg_dma_buf_init( + &sspp_buf[i][sspp_feature_map[feature]][idx], sspp_feature_reg_dma_sz[feature]); + if (rc) { + DRM_ERROR("rect %d buf init failed\n", i); + break; + } + } + + } return rc; } static int reg_dma_sspp_check(struct sde_hw_pipe *ctx, void *cfg, - enum sde_reg_dma_features feature) + enum sde_reg_dma_features feature, + enum sde_sspp_multirect_index idx) { struct sde_hw_reg_dma_ops *dma_ops; struct sde_hw_cp_cfg *hw_cfg = cfg; @@ -1618,6 +1630,11 @@ static int reg_dma_sspp_check(struct sde_hw_pipe *ctx, void *cfg, return -EINVAL; } + if (idx >= SDE_SSPP_RECT_MAX) { + DRM_ERROR("invalid multirect idx %d\n", idx); + return -EINVAL; + } + dma_ops = sde_reg_dma_get_ops(); if (IS_ERR_OR_NULL(dma_ops)) return -EINVAL; @@ -1629,8 +1646,9 @@ static int reg_dma_sspp_check(struct sde_hw_pipe *ctx, void *cfg, return -EINVAL; } - if (!sspp_buf[feature][ctx->idx]) { - DRM_ERROR("invalid dma_buf for sspp idx %d\n", ctx->idx); + if (!sspp_buf[idx][feature][ctx->idx]) { + DRM_ERROR("invalid dma_buf for rect idx %d sspp idx %d\n", idx, + ctx->idx); return -EINVAL; } @@ -1646,12 +1664,13 @@ static void vig_gamutv5_off(struct sde_hw_pipe *ctx, void *cfg) struct sde_reg_dma_setup_ops_cfg dma_write_cfg; struct sde_reg_dma_kickoff_cfg kick_off; u32 gamut_base = ctx->cap->sblk->gamut_blk.base - REG_DMA_VIG_SWI_DIFF; + enum sde_sspp_multirect_index idx = SDE_SSPP_RECT_0; dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[GAMUT][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][GAMUT][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], GAMUT, - sspp_buf[GAMUT][ctx->idx]); + sspp_buf[idx][GAMUT][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -1669,8 +1688,9 @@ static void vig_gamutv5_off(struct sde_hw_pipe *ctx, void *cfg) return; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[GAMUT][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][GAMUT][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); @@ -1688,8 +1708,9 @@ void reg_dmav1_setup_vig_gamutv5(struct sde_hw_pipe *ctx, void *cfg) struct sde_reg_dma_kickoff_cfg kick_off; u32 gamut_base = ctx->cap->sblk->gamut_blk.base - REG_DMA_VIG_SWI_DIFF; bool use_2nd_memory = false; + enum sde_sspp_multirect_index idx = SDE_SSPP_RECT_0; - rc = reg_dma_sspp_check(ctx, cfg, GAMUT); + rc = reg_dma_sspp_check(ctx, cfg, GAMUT, idx); if (rc) return; @@ -1714,10 +1735,10 @@ void reg_dmav1_setup_vig_gamutv5(struct sde_hw_pipe *ctx, void *cfg) } dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[GAMUT][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][GAMUT][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], GAMUT, - sspp_buf[GAMUT][ctx->idx]); + sspp_buf[idx][GAMUT][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -1778,8 +1799,9 @@ void reg_dmav1_setup_vig_gamutv5(struct sde_hw_pipe *ctx, void *cfg) return; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[GAMUT][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][GAMUT][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); @@ -1794,12 +1816,13 @@ static void vig_igcv5_off(struct sde_hw_pipe *ctx, void *cfg) struct sde_reg_dma_setup_ops_cfg dma_write_cfg; struct sde_reg_dma_kickoff_cfg kick_off; u32 igc_base = ctx->cap->sblk->igc_blk[0].base - REG_DMA_VIG_SWI_DIFF; + enum sde_sspp_multirect_index idx = SDE_SSPP_RECT_0; dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[IGC][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][IGC][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], IGC, - sspp_buf[IGC][ctx->idx]); + sspp_buf[idx][IGC][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -1816,8 +1839,9 @@ static void vig_igcv5_off(struct sde_hw_pipe *ctx, void *cfg) return; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[IGC][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][IGC][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); @@ -1836,8 +1860,9 @@ void reg_dmav1_setup_vig_igcv5(struct sde_hw_pipe *ctx, void *cfg) struct sde_reg_dma_setup_ops_cfg dma_write_cfg; struct sde_reg_dma_kickoff_cfg kick_off; u32 igc_base = ctx->cap->sblk->igc_blk[0].base - REG_DMA_VIG_SWI_DIFF; + enum sde_sspp_multirect_index idx = SDE_SSPP_RECT_0; - rc = reg_dma_sspp_check(ctx, cfg, IGC); + rc = reg_dma_sspp_check(ctx, cfg, IGC, idx); if (rc) return; @@ -1855,10 +1880,10 @@ void reg_dmav1_setup_vig_igcv5(struct sde_hw_pipe *ctx, void *cfg) } dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[IGC][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][IGC][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], IGC, - sspp_buf[IGC][ctx->idx]); + sspp_buf[idx][IGC][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -1927,8 +1952,9 @@ void reg_dmav1_setup_vig_igcv5(struct sde_hw_pipe *ctx, void *cfg) goto exit; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[IGC][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][IGC][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); @@ -1948,10 +1974,10 @@ static void dma_igcv5_off(struct sde_hw_pipe *ctx, void *cfg, u32 igc_opmode_off; dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[IGC][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][IGC][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], IGC, - sspp_buf[IGC][ctx->idx]); + sspp_buf[idx][IGC][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -1974,8 +2000,9 @@ static void dma_igcv5_off(struct sde_hw_pipe *ctx, void *cfg, return; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[IGC][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][IGC][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); @@ -1994,15 +2021,10 @@ void reg_dmav1_setup_dma_igcv5(struct sde_hw_pipe *ctx, void *cfg, struct sde_reg_dma_kickoff_cfg kick_off; u32 igc_base, igc_dither_off, igc_opmode_off; - rc = reg_dma_sspp_check(ctx, cfg, IGC); + rc = reg_dma_sspp_check(ctx, cfg, IGC, idx); if (rc) return; - if (idx > SDE_SSPP_RECT_1) { - DRM_ERROR("invalid multirect idx %d\n", idx); - return; - } - igc_lut = hw_cfg->payload; if (!igc_lut) { DRM_DEBUG_DRIVER("disable igc feature\n"); @@ -2017,10 +2039,10 @@ void reg_dmav1_setup_dma_igcv5(struct sde_hw_pipe *ctx, void *cfg, } dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[IGC][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][IGC][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], IGC, - sspp_buf[IGC][ctx->idx]); + sspp_buf[idx][IGC][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -2035,9 +2057,10 @@ void reg_dmav1_setup_dma_igcv5(struct sde_hw_pipe *ctx, void *cfg, return; } + /* client packs the 1D LUT data in c2 instead of c0 */ for (i = 0; i < DMA_1D_LUT_IGC_LEN; i++) - data[i] = (igc_lut->c0[2 * i] & IGC_DATA_MASK) | - ((igc_lut->c0[2 * i + 1] & IGC_DATA_MASK) << 16); + data[i] = (igc_lut->c2[2 * i] & IGC_DATA_MASK) | + ((igc_lut->c2[2 * i + 1] & IGC_DATA_MASK) << 16); if (idx == SDE_SSPP_RECT_SOLO || idx == SDE_SSPP_RECT_0) { igc_base = ctx->cap->sblk->igc_blk[0].base - @@ -2082,8 +2105,9 @@ void reg_dmav1_setup_dma_igcv5(struct sde_hw_pipe *ctx, void *cfg, goto igc_exit; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[IGC][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][IGC][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); @@ -2103,10 +2127,10 @@ static void dma_gcv5_off(struct sde_hw_pipe *ctx, void *cfg, u32 gc_opmode_off; dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[GC][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][GC][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], GC, - sspp_buf[GC][ctx->idx]); + sspp_buf[idx][GC][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -2129,8 +2153,9 @@ static void dma_gcv5_off(struct sde_hw_pipe *ctx, void *cfg, return; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[GC][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][GC][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); @@ -2140,8 +2165,7 @@ void reg_dmav1_setup_dma_gcv5(struct sde_hw_pipe *ctx, void *cfg, enum sde_sspp_multirect_index idx) { int rc; - u32 i = 0, reg = 0; - u32 *data = NULL; + u32 reg = 0; struct drm_msm_pgc_lut *gc_lut; struct sde_hw_cp_cfg *hw_cfg = cfg; struct sde_hw_reg_dma_ops *dma_ops; @@ -2149,15 +2173,10 @@ void reg_dmav1_setup_dma_gcv5(struct sde_hw_pipe *ctx, void *cfg, struct sde_reg_dma_kickoff_cfg kick_off; u32 gc_base, gc_opmode_off; - rc = reg_dma_sspp_check(ctx, cfg, GC); + rc = reg_dma_sspp_check(ctx, cfg, GC, idx); if (rc) return; - if (idx > SDE_SSPP_RECT_1) { - DRM_ERROR("invalid multirect idx %d\n", idx); - return; - } - gc_lut = hw_cfg->payload; if (!gc_lut) { DRM_DEBUG_DRIVER("disable gc feature\n"); @@ -2172,10 +2191,10 @@ void reg_dmav1_setup_dma_gcv5(struct sde_hw_pipe *ctx, void *cfg, } dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[GC][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][GC][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], GC, - sspp_buf[GC][ctx->idx]); + sspp_buf[idx][GC][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -2184,15 +2203,6 @@ void reg_dmav1_setup_dma_gcv5(struct sde_hw_pipe *ctx, void *cfg, return; } - data = kzalloc(DMA_1D_LUT_GC_LEN * sizeof(u32), GFP_KERNEL); - if (!data) { - DRM_ERROR("failed to allocate memory for igc\n"); - return; - } - - for (i = 0; i < DMA_1D_LUT_GC_LEN; i++) - data[i] = gc_lut->c0[2 * i] | (gc_lut->c0[2 * i + 1] << 16); - if (idx == SDE_SSPP_RECT_SOLO || idx == SDE_SSPP_RECT_0) { gc_base = ctx->cap->sblk->gc_blk[0].base - REG_DMA_DMA_SWI_DIFF; gc_opmode_off = DMA_DGM_0_OP_MODE_OFF; @@ -2201,13 +2211,16 @@ void reg_dmav1_setup_dma_gcv5(struct sde_hw_pipe *ctx, void *cfg, gc_opmode_off = DMA_DGM_1_OP_MODE_OFF; } - REG_DMA_SETUP_OPS(dma_write_cfg, gc_base, data, + /* client packs the 1D LUT data in c2 instead of c0, + * and even & odd values are already stacked in register foramt + */ + REG_DMA_SETUP_OPS(dma_write_cfg, gc_base, gc_lut->c2, DMA_1D_LUT_GC_LEN * sizeof(u32), REG_BLK_WRITE_SINGLE, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); if (rc) { DRM_ERROR("lut write failed ret %d\n", rc); - goto gc_exit; + return; } reg = BIT(2); REG_DMA_SETUP_OPS(dma_write_cfg, gc_opmode_off, ®, @@ -2216,21 +2229,20 @@ void reg_dmav1_setup_dma_gcv5(struct sde_hw_pipe *ctx, void *cfg, rc = dma_ops->setup_payload(&dma_write_cfg); if (rc) { DRM_ERROR("setting opcode failed ret %d\n", rc); - goto gc_exit; + return; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, sspp_buf[GC][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + sspp_buf[idx][GC][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); -gc_exit: - kfree(data); } int reg_dmav1_deinit_sspp_ops(enum sde_sspp idx) { - int i; + u32 i, j; struct sde_hw_reg_dma_ops *dma_ops; dma_ops = sde_reg_dma_get_ops(); @@ -2242,11 +2254,13 @@ int reg_dmav1_deinit_sspp_ops(enum sde_sspp idx) return -EINVAL; } - for (i = 0; i < REG_DMA_FEATURES_MAX; i++) { - if (!sspp_buf[i][idx]) - continue; - dma_ops->dealloc_reg_dma(sspp_buf[i][idx]); - sspp_buf[i][idx] = NULL; + for (i = SDE_SSPP_RECT_0; i < SDE_SSPP_RECT_MAX; i++) { + for (j = 0; j < REG_DMA_FEATURES_MAX; j++) { + if (!sspp_buf[i][j][idx]) + continue; + dma_ops->dealloc_reg_dma(sspp_buf[i][j][idx]); + sspp_buf[i][j][idx] = NULL; + } } return 0; } @@ -2392,6 +2406,7 @@ void reg_dmav1_setup_vig_qseed3(struct sde_hw_pipe *ctx, u32 op_mode = 0, offset; u32 preload, src_y_rgb, src_uv, dst; u32 cache[4]; + enum sde_sspp_multirect_index idx = SDE_SSPP_RECT_0; if (!ctx || !pe || !scaler_cfg) { DRM_ERROR("invalid params ctx %pK pe %pK scaler_cfg %pK", @@ -2402,7 +2417,7 @@ void reg_dmav1_setup_vig_qseed3(struct sde_hw_pipe *ctx, hw_cfg.ctl = ctx->ctl; hw_cfg.payload = scaler_cfg; hw_cfg.len = sizeof(*scaler3_cfg); - rc = reg_dma_sspp_check(ctx, &hw_cfg, QSEED); + rc = reg_dma_sspp_check(ctx, &hw_cfg, QSEED, idx); if (rc || !sspp) { DRM_ERROR("invalid params rc %d sspp %pK\n", rc, sspp); return; @@ -2410,10 +2425,10 @@ void reg_dmav1_setup_vig_qseed3(struct sde_hw_pipe *ctx, offset = ctx->cap->sblk->scaler_blk.base - REG_DMA_VIG_SWI_DIFF; dma_ops = sde_reg_dma_get_ops(); - dma_ops->reset_reg_dma_buf(sspp_buf[QSEED][ctx->idx]); + dma_ops->reset_reg_dma_buf(sspp_buf[idx][QSEED][ctx->idx]); REG_DMA_INIT_OPS(dma_write_cfg, sspp_mapping[ctx->idx], QSEED, - sspp_buf[QSEED][ctx->idx]); + sspp_buf[idx][QSEED][ctx->idx]); REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); rc = dma_ops->setup_payload(&dma_write_cfg); @@ -2529,8 +2544,9 @@ void reg_dmav1_setup_vig_qseed3(struct sde_hw_pipe *ctx, return; } - REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg.ctl, sspp_buf[QSEED][ctx->idx], - REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE); + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg.ctl, + sspp_buf[idx][QSEED][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_IMMEDIATE); rc = dma_ops->kick_off(&kick_off); if (rc) DRM_ERROR("failed to kick off ret %d\n", rc); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c index 12b05e628c6f48004bbfde709c1e7732236ced0a..8bf8e17331d9e56952df9d01c623d87eb484b3cf 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c @@ -150,6 +150,7 @@ #define VIG_CSC_10_SRC_DATAFMT BIT(1) #define VIG_CSC_10_EN BIT(0) #define CSC_10BIT_OFFSET 4 +#define DGM_CSC_MATRIX_SHIFT 0 /* traffic shaper clock in Hz */ #define TS_CLK 19200000 @@ -388,19 +389,24 @@ static void sde_hw_sspp_setup_secure(struct sde_hw_pipe *ctx, c = &ctx->hw; - if (enable) { - if ((rect_mode == SDE_SSPP_RECT_SOLO) - || (rect_mode == SDE_SSPP_RECT_0)) - secure_bit_mask = - (rect_mode == SDE_SSPP_RECT_SOLO) ? 0xF : 0x5; - else - secure_bit_mask = 0xA; + if ((rect_mode == SDE_SSPP_RECT_SOLO) + || (rect_mode == SDE_SSPP_RECT_0)) + secure_bit_mask = + (rect_mode == SDE_SSPP_RECT_SOLO) ? 0xF : 0x5; + else + secure_bit_mask = 0xA; - secure = SDE_REG_READ(c, SSPP_SRC_ADDR_SW_STATUS + idx); + secure = SDE_REG_READ(c, SSPP_SRC_ADDR_SW_STATUS + idx); + + if (enable) secure |= secure_bit_mask; - } + else + secure &= ~secure_bit_mask; SDE_REG_WRITE(c, SSPP_SRC_ADDR_SW_STATUS + idx, secure); + + /* multiple planes share same sw_status register */ + wmb(); } @@ -1053,7 +1059,7 @@ static void sde_hw_sspp_setup_dgm_csc(struct sde_hw_pipe *ctx, if (data) { op_mode |= BIT(0); sde_hw_csc_matrix_coeff_setup(&ctx->hw, - offset + CSC_10BIT_OFFSET, data); + offset + CSC_10BIT_OFFSET, data, DGM_CSC_MATRIX_SHIFT); } SDE_REG_WRITE(&ctx->hw, offset, op_mode); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h index 51595036ce5050d715f88d09c215b928fd4e90e9..e493bb77ca53e0ebb6349e72cf1d8043c16da268 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h @@ -56,6 +56,7 @@ enum { * SDE_SSPP_RECT_SOLO - multirect disabled * SDE_SSPP_RECT_0 - rect0 of a multirect pipe * SDE_SSPP_RECT_1 - rect1 of a multirect pipe + * SDE_SSPP_RECT_MAX - max enum of multirect pipe * * Note: HW supports multirect with either RECT0 or * RECT1. Considering no benefit of such configs over @@ -66,6 +67,7 @@ enum sde_sspp_multirect_index { SDE_SSPP_RECT_SOLO = 0, SDE_SSPP_RECT_0, SDE_SSPP_RECT_1, + SDE_SSPP_RECT_MAX, }; enum sde_sspp_multirect_mode { diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c index 0302a3e551944c43044f88e5533f944d8f53d354..82840e64d12f55626db2906296262fe8bbccaf83 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_top.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c @@ -107,6 +107,18 @@ static void sde_hw_setup_split_pipe(struct sde_hw_mdp *mdp, SDE_REG_WRITE(c, SPLIT_DISPLAY_EN, cfg->en & 0x1); } +static u32 sde_hw_get_split_flush(struct sde_hw_mdp *mdp) +{ + struct sde_hw_blk_reg_map *c; + + if (!mdp) + return 0; + + c = &mdp->hw; + + return (SDE_REG_READ(c, SSPP_SPARE) & 0x1); +} + static void sde_hw_setup_pp_split(struct sde_hw_mdp *mdp, struct split_pipe_cfg *cfg) { @@ -411,6 +423,7 @@ static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops, ops->get_danger_status = sde_hw_get_danger_status; ops->setup_vsync_source = sde_hw_setup_vsync_source; ops->get_safe_status = sde_hw_get_safe_status; + ops->get_split_flush_status = sde_hw_get_split_flush; ops->setup_dce = sde_hw_setup_dce; ops->reset_ubwc = sde_hw_reset_ubwc; ops->intf_audio_select = sde_hw_intf_audio_select; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.h b/drivers/gpu/drm/msm/sde/sde_hw_top.h index 0ca5af9359baba195644892c1fc05213e9b731d2..950a62cee8ef1683cc2394a0df68b8206adc6216 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_top.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_top.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -176,6 +176,12 @@ struct sde_hw_mdp_ops { void (*get_safe_status)(struct sde_hw_mdp *mdp, struct sde_danger_safe_status *status); + /** + * get_split_flush_status - get split flush status + * @mdp: mdp top context driver + */ + u32 (*get_split_flush_status)(struct sde_hw_mdp *mdp); + /** * reset_ubwc - reset top level UBWC configuration * @mdp: mdp top context driver diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.c b/drivers/gpu/drm/msm/sde/sde_hw_util.c index 84ce6df218834d5202c1d630bf873c1c6f6ad84b..fab9e349b28a84f8f63ce0cc8a7289a255ea116b 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_util.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_util.c @@ -373,27 +373,27 @@ u32 sde_hw_get_scaler3_ver(struct sde_hw_blk_reg_map *c, } void sde_hw_csc_matrix_coeff_setup(struct sde_hw_blk_reg_map *c, - u32 csc_reg_off, struct sde_csc_cfg *data) + u32 csc_reg_off, struct sde_csc_cfg *data, + u32 shift_bit) { u32 val; if (!c || !data) return; - /* matrix coeff - convert S15.16 to S4.9 */ - val = ((data->csc_mv[0] >> CSC_MATRIX_SHIFT) & 0x1FFF) | - (((data->csc_mv[1] >> CSC_MATRIX_SHIFT) & 0x1FFF) << 16); + val = ((data->csc_mv[0] >> shift_bit) & 0x1FFF) | + (((data->csc_mv[1] >> shift_bit) & 0x1FFF) << 16); SDE_REG_WRITE(c, csc_reg_off, val); - val = ((data->csc_mv[2] >> CSC_MATRIX_SHIFT) & 0x1FFF) | - (((data->csc_mv[3] >> CSC_MATRIX_SHIFT) & 0x1FFF) << 16); + val = ((data->csc_mv[2] >> shift_bit) & 0x1FFF) | + (((data->csc_mv[3] >> shift_bit) & 0x1FFF) << 16); SDE_REG_WRITE(c, csc_reg_off + 0x4, val); - val = ((data->csc_mv[4] >> CSC_MATRIX_SHIFT) & 0x1FFF) | - (((data->csc_mv[5] >> CSC_MATRIX_SHIFT) & 0x1FFF) << 16); + val = ((data->csc_mv[4] >> shift_bit) & 0x1FFF) | + (((data->csc_mv[5] >> shift_bit) & 0x1FFF) << 16); SDE_REG_WRITE(c, csc_reg_off + 0x8, val); - val = ((data->csc_mv[6] >> CSC_MATRIX_SHIFT) & 0x1FFF) | - (((data->csc_mv[7] >> CSC_MATRIX_SHIFT) & 0x1FFF) << 16); + val = ((data->csc_mv[6] >> shift_bit) & 0x1FFF) | + (((data->csc_mv[7] >> shift_bit) & 0x1FFF) << 16); SDE_REG_WRITE(c, csc_reg_off + 0xc, val); - val = (data->csc_mv[8] >> CSC_MATRIX_SHIFT) & 0x1FFF; + val = (data->csc_mv[8] >> shift_bit) & 0x1FFF; SDE_REG_WRITE(c, csc_reg_off + 0x10, val); } @@ -407,7 +407,8 @@ void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, if (!c || !data) return; - sde_hw_csc_matrix_coeff_setup(c, csc_reg_off, data); + /* matrix coeff - convert S15.16 to S4.9 */ + sde_hw_csc_matrix_coeff_setup(c, csc_reg_off, data, CSC_MATRIX_SHIFT); /* Pre clamp */ val = (data->csc_pre_lv[0] << clamp_shift) | data->csc_pre_lv[1]; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h index bdcc25307ad8b75720b57188f7ca2884e4759456..d07a15b5e30b9bb17c1348daa1098c5dd3c710f2 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_util.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h @@ -189,7 +189,8 @@ u32 sde_hw_get_scaler3_ver(struct sde_hw_blk_reg_map *c, u32 scaler_offset); void sde_hw_csc_matrix_coeff_setup(struct sde_hw_blk_reg_map *c, - u32 csc_reg_off, struct sde_csc_cfg *data); + u32 csc_reg_off, struct sde_csc_cfg *data, + u32 shift_bit); void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, u32 csc_reg_off, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c index 6e6a542d4e98f6b6569c0d9e7a615c3f90022cdd..837a7c4fd0cc47fce6019fe89c3f561774712fe5 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -33,6 +33,8 @@ #define VBIF_OUT_WR_LIM_CONF0 0x00D4 #define VBIF_OUT_AXI_AMEMTYPE_CONF0 0x0160 #define VBIF_OUT_AXI_AMEMTYPE_CONF1 0x0164 +#define VBIF_OUT_AXI_ASHARED 0x0170 +#define VBIF_OUT_AXI_AINNERSHARED 0x0174 #define VBIF_XIN_PND_ERR 0x0190 #define VBIF_XIN_SRC_ERR 0x0194 #define VBIF_XIN_CLR_ERR 0x019C @@ -73,11 +75,12 @@ static void sde_hw_set_mem_type(struct sde_hw_vbif *vbif, * Assume 4 bits per bit field, 8 fields per 32-bit register so * 16 bit fields maximum across two registers */ - if (!vbif || xin_id >= MAX_XIN_COUNT || xin_id >= 16) + if (!vbif || xin_id >= MAX_XIN_COUNT) return; c = &vbif->hw; + /* enable cacheable */ if (xin_id >= 8) { xin_id -= 8; reg_off = VBIF_OUT_AXI_AMEMTYPE_CONF1; @@ -91,6 +94,30 @@ static void sde_hw_set_mem_type(struct sde_hw_vbif *vbif, SDE_REG_WRITE(c, reg_off, reg_val); } +static void sde_hw_set_mem_type_v1(struct sde_hw_vbif *vbif, + u32 xin_id, u32 value) +{ + struct sde_hw_blk_reg_map *c; + u32 reg_val; + + if (!vbif || xin_id >= MAX_XIN_COUNT) + return; + + sde_hw_set_mem_type(vbif, xin_id, value); + + c = &vbif->hw; + + /* disable outer shareable */ + reg_val = SDE_REG_READ(c, VBIF_OUT_AXI_ASHARED); + reg_val &= ~BIT(xin_id); + SDE_REG_WRITE(c, VBIF_OUT_AXI_ASHARED, 0); + + /* disable inner shareable */ + reg_val = SDE_REG_READ(c, VBIF_OUT_AXI_AINNERSHARED); + reg_val &= ~BIT(xin_id); + SDE_REG_WRITE(c, VBIF_OUT_AXI_AINNERSHARED, 0); +} + static void sde_hw_set_limit_conf(struct sde_hw_vbif *vbif, u32 xin_id, bool rd, u32 limit) { @@ -205,8 +232,8 @@ static void sde_hw_set_write_gather_en(struct sde_hw_vbif *vbif, u32 xin_id) SDE_REG_WRITE(c, VBIF_WRITE_GATHER_EN, reg_val); } -static void _setup_vbif_ops(struct sde_hw_vbif_ops *ops, - unsigned long cap) +static void _setup_vbif_ops(const struct sde_mdss_cfg *m, + struct sde_hw_vbif_ops *ops, unsigned long cap) { ops->set_limit_conf = sde_hw_set_limit_conf; ops->get_limit_conf = sde_hw_get_limit_conf; @@ -214,7 +241,10 @@ static void _setup_vbif_ops(struct sde_hw_vbif_ops *ops, ops->get_halt_ctrl = sde_hw_get_halt_ctrl; if (test_bit(SDE_VBIF_QOS_REMAP, &cap)) ops->set_qos_remap = sde_hw_set_qos_remap; - ops->set_mem_type = sde_hw_set_mem_type; + if (IS_SM8150_TARGET(m->hwversion)) + ops->set_mem_type = sde_hw_set_mem_type_v1; + else + ops->set_mem_type = sde_hw_set_mem_type; ops->clear_errors = sde_hw_clear_errors; ops->set_write_gather_en = sde_hw_set_write_gather_en; } @@ -262,7 +292,7 @@ struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx, */ c->idx = idx; c->cap = cfg; - _setup_vbif_ops(&c->ops, c->cap->features); + _setup_vbif_ops(m, &c->ops, c->cap->features); /* no need to register sub-range in sde dbg, dump entire vbif io base */ diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index eea767450709750432bef6f70e06bfb8a9997127..cad89d399a87ccc221ad91b75f6f7fba1b7c2c75 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -801,18 +801,22 @@ static int sde_kms_prepare_secure_transition(struct msm_kms *kms, } static int _sde_kms_release_splash_buffer(unsigned int mem_addr, - unsigned int size) + unsigned int splash_buffer_size, + unsigned int ramdump_buffer_size) { unsigned long pfn_start, pfn_end, pfn_idx; int ret = 0; - if (!mem_addr || !size) + if (!mem_addr || !splash_buffer_size) SDE_ERROR("invalid params\n"); + mem_addr += ramdump_buffer_size; + splash_buffer_size -= ramdump_buffer_size; + pfn_start = mem_addr >> PAGE_SHIFT; - pfn_end = (mem_addr + size) >> PAGE_SHIFT; + pfn_end = (mem_addr + splash_buffer_size) >> PAGE_SHIFT; - ret = memblock_free(mem_addr, size); + ret = memblock_free(mem_addr, splash_buffer_size); if (ret) { SDE_ERROR("continuous splash memory free failed:%d\n", ret); return ret; @@ -993,7 +997,8 @@ static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms, rc = _sde_kms_release_splash_buffer( sde_kms->splash_data.splash_base, - sde_kms->splash_data.splash_size); + sde_kms->splash_data.splash_size, + sde_kms->splash_data.ramdump_size); if (rc) pr_err("failed to release splash memory\n"); sde_kms->splash_data.splash_base = 0; @@ -1691,7 +1696,8 @@ static void _sde_kms_hw_destroy(struct sde_kms *sde_kms, _sde_kms_release_displays(sde_kms); (void)_sde_kms_release_splash_buffer( sde_kms->splash_data.splash_base, - sde_kms->splash_data.splash_size); + sde_kms->splash_data.splash_size, + sde_kms->splash_data.ramdump_size); /* safe to call these more than once during shutdown */ _sde_debugfs_destroy(sde_kms); @@ -2803,8 +2809,8 @@ static int sde_kms_pd_disable(struct generic_pm_domain *genpd) static int _sde_kms_get_splash_data(struct sde_splash_data *data) { int ret = 0; - struct device_node *parent, *node; - struct resource r; + struct device_node *parent, *node, *node1; + struct resource r, r1; if (!data) return -EINVAL; @@ -2829,9 +2835,29 @@ static int _sde_kms_get_splash_data(struct sde_splash_data *data) data->splash_base = (unsigned long)r.start; data->splash_size = (r.end - r.start) + 1; - pr_info("found continuous splash base address:%lx size:%x\n", - data->splash_base, - data->splash_size); + node1 = of_find_node_by_name(parent, "disp_rdump_region"); + if (!node1) + SDE_DEBUG("failed to find disp ramdump memory reservation\n"); + + if (!node1 || of_address_to_resource(node1, 0, &r1)) { + SDE_DEBUG("failed to find data for disp ramdump memory\n"); + data->ramdump_base = 0; + data->ramdump_size = 0; + } else { + data->ramdump_base = (unsigned long)r1.start; + data->ramdump_size = (r1.end - r1.start) + 1; + } + + if ((data->ramdump_base && data->ramdump_base != data->splash_base) || + (data->ramdump_size > data->splash_size)) { + SDE_ERROR("ramdump/splash buffer addr/size mismatched\n"); + data->ramdump_base = 0; + data->ramdump_size = 0; + } + + pr_info("cont spla base adds:%lx size:%x rdump adds=:%lx size:%x\n", + data->splash_base, data->splash_size, + data->ramdump_base, data->ramdump_size); return ret; } diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c index 819c17f36a9d8c44e4594944ded6c89c99233b2f..5f8cb4c125c102e68c916819b9e08c112ff2ecb9 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.c +++ b/drivers/gpu/drm/msm/sde/sde_plane.c @@ -287,6 +287,9 @@ static inline int _sde_plane_calc_fill_level(struct drm_plane *plane, } psde = to_sde_plane(plane); + if (psde->features & BIT(SDE_SSPP_QOS_FL_NOCALC)) + return 0; + pstate = to_sde_plane_state(plane->state); rstate = &pstate->rot; fixed_buff_size = psde->pipe_sblk->pixel_ram_size; @@ -405,6 +408,8 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, if (fmt && SDE_FORMAT_IS_LINEAR(fmt)) lut_usage = SDE_QOS_LUT_USAGE_LINEAR; + else if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) + lut_usage = SDE_QOS_LUT_USAGE_MACROTILE_QSEED; else lut_usage = SDE_QOS_LUT_USAGE_MACROTILE; } @@ -469,6 +474,10 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane, danger_lut = psde->catalog->perf.danger_lut_tbl [SDE_QOS_LUT_USAGE_LINEAR]; lut_usage = SDE_QOS_LUT_USAGE_LINEAR; + } else if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + danger_lut = psde->catalog->perf.danger_lut_tbl + [SDE_QOS_LUT_USAGE_MACROTILE_QSEED]; + lut_usage = SDE_QOS_LUT_USAGE_MACROTILE_QSEED; } else { danger_lut = psde->catalog->perf.danger_lut_tbl [SDE_QOS_LUT_USAGE_MACROTILE]; @@ -4460,7 +4469,8 @@ static void _sde_plane_install_properties(struct drm_plane *plane, PLANE_PROP_FB_TRANSLATION_MODE); } -static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) +static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, + void __user *usr_ptr) { struct sde_drm_csc_v1 csc_v1; int i; @@ -4496,7 +4506,7 @@ static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) } static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, - struct sde_plane_state *pstate, void *usr) + struct sde_plane_state *pstate, void __user *usr) { struct sde_drm_scaler_v1 scale_v1; struct sde_hw_pixel_ext *pe; @@ -4555,7 +4565,7 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, } static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, - struct sde_plane_state *pstate, void *usr) + struct sde_plane_state *pstate, void __user *usr) { struct sde_drm_scaler_v2 scale_v2; struct sde_hw_pixel_ext *pe; @@ -4617,17 +4627,18 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, } static void _sde_plane_set_excl_rect_v1(struct sde_plane *psde, - struct sde_plane_state *pstate, void *usr_ptr) + struct sde_plane_state *pstate, void __user *usr_ptr) { struct drm_clip_rect excl_rect_v1; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!psde || !pstate) { + SDE_ERROR("invalid argument(s)\n"); return; } if (!usr_ptr) { - SDE_DEBUG_PLANE(psde, "invalid excl_rect user data\n"); + memset(&pstate->excl_rect, 0, sizeof(pstate->excl_rect)); + SDE_DEBUG_PLANE(psde, "excl_rect data cleared\n"); return; } @@ -4674,19 +4685,19 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, break; case PLANE_PROP_CSC_V1: case PLANE_PROP_CSC_DMA_V1: - _sde_plane_set_csc_v1(psde, (void *)val); + _sde_plane_set_csc_v1(psde, (void __user *)val); break; case PLANE_PROP_SCALER_V1: _sde_plane_set_scaler_v1(psde, pstate, - (void *)val); + (void __user *)val); break; case PLANE_PROP_SCALER_V2: _sde_plane_set_scaler_v2(psde, pstate, - (void *)val); + (void __user *)val); break; case PLANE_PROP_EXCL_RECT_V1: _sde_plane_set_excl_rect_v1(psde, pstate, - (void *)val); + (void __user *)val); break; default: /* nothing to do */ diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h index acd7770524a2b1050aef0793cbe8e7c4bce125ae..5810cc44ef331dfc0c4b1da6ba0320cc84b534c0 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.h +++ b/drivers/gpu/drm/msm/sde/sde_plane.h @@ -97,7 +97,10 @@ struct sde_plane_rot_state { #define SDE_PLANE_DIRTY_VIG_IGC 0x40 #define SDE_PLANE_DIRTY_DMA_IGC 0x80 #define SDE_PLANE_DIRTY_DMA_GC 0x100 -#define SDE_PLANE_DIRTY_ALL 0xFFFFFFFF +#define SDE_PLANE_DIRTY_CP (SDE_PLANE_DIRTY_VIG_GAMUT |\ + SDE_PLANE_DIRTY_VIG_IGC | SDE_PLANE_DIRTY_DMA_IGC |\ + SDE_PLANE_DIRTY_DMA_GC) +#define SDE_PLANE_DIRTY_ALL (0xFFFFFFFF & ~(SDE_PLANE_DIRTY_CP)) /** * enum sde_plane_sclcheck_state - User scaler data status diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index 33990b33c5df2d316dd72bed948c4b90724cc2ef..92734565d80b23b8b296ecc78ac759742d857756 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -1211,51 +1211,136 @@ static u32 _sde_rm_poll_intr_status_for_cont_splash(struct sde_hw_intr *intr, } SDE_EVT32(status, irq_idx_pp_done, SDE_EVTLOG_ERROR); - SDE_ERROR("polling timed out. status = 0x%x\n", status); + SDE_DEBUG("polling timed out. status = 0x%x\n", status); return -ETIMEDOUT; } +static inline bool _sde_rm_autorefresh_validate(struct sde_hw_pingpong *pp, + struct sde_hw_intf *intf, + bool hw_intf_te) +{ + + if ((hw_intf_te && !intf) || + (!hw_intf_te && !pp)) { + SDE_ERROR("autorefresh wrong params!\n"); + return true; + } + + if (hw_intf_te) { + if (!intf->ops.get_autorefresh || + !intf->ops.setup_autorefresh || + !intf->ops.connect_external_te || + !intf->ops.get_vsync_info) { + SDE_ERROR("intf autorefresh apis not supported\n"); + return true; + } + } else { + if (!pp->ops.get_autorefresh || + !pp->ops.setup_autorefresh || + !pp->ops.connect_external_te || + !pp->ops.get_vsync_info) { + SDE_ERROR("pp autorefresh apis not supported\n"); + return true; + } + } + + return false; +} + +static inline void _sde_rm_autorefresh_get_cfg( + struct sde_hw_pingpong *pp, + struct sde_hw_intf *intf, + struct sde_hw_autorefresh *cfg, + bool hw_intf_te) +{ + if (hw_intf_te) + intf->ops.get_autorefresh(intf, cfg); + else + pp->ops.get_autorefresh(pp, cfg); +} + +static inline void _sde_rm_autorefresh_connect_external_te( + struct sde_hw_pingpong *pp, + struct sde_hw_intf *intf, + bool hw_intf_te, + bool enable) +{ + if (hw_intf_te) + intf->ops.connect_external_te(intf, enable); + else + pp->ops.connect_external_te(pp, enable); +} + +static inline void _sde_rm_autorefresh_setup(struct sde_hw_pingpong *pp, + struct sde_hw_intf *intf, + struct sde_hw_autorefresh *cfg, + bool hw_intf_te) +{ + if (hw_intf_te) + intf->ops.setup_autorefresh(intf, cfg); + else + pp->ops.setup_autorefresh(pp, cfg); +} + +static inline void _sde_rm_autorefresh_get_vsync_info( + struct sde_hw_pingpong *pp, + struct sde_hw_intf *intf, + struct sde_hw_pp_vsync_info *info, + bool hw_intf_te) +{ + if (hw_intf_te) + intf->ops.get_vsync_info(intf, info); + else + pp->ops.get_vsync_info(pp, info); +} + static int _sde_rm_autorefresh_disable(struct sde_hw_pingpong *pp, - struct sde_hw_intr *hw_intr) + struct sde_hw_intf *intf, + struct sde_hw_intr *hw_intr, + bool hw_intf_te) { u32 const timeout_ms = 35; /* Max two vsyncs delay */ int rc = 0, i, loop = 3; struct sde_hw_pp_vsync_info info; int irq_idx_pp_done = -1, irq_idx_autorefresh = -1; struct sde_hw_autorefresh cfg = {0}; + int dbg_idx; + int te_irq_idx; - if (!pp->ops.get_autorefresh || !pp->ops.setup_autorefresh || - !pp->ops.connect_external_te || !pp->ops.get_vsync_info) { - SDE_ERROR("autorefresh update api not supported\n"); + if (_sde_rm_autorefresh_validate(pp, intf, hw_intf_te)) return 0; - } + + dbg_idx = hw_intf_te ? intf->idx - INTF_0 : pp->idx - PINGPONG_0; + te_irq_idx = hw_intf_te ? intf->idx : pp->idx; /* read default autorefresh configuration */ - pp->ops.get_autorefresh(pp, &cfg); + _sde_rm_autorefresh_get_cfg(pp, intf, &cfg, hw_intf_te); + if (!cfg.enable) { - SDE_DEBUG("autorefresh already disabled\n"); - SDE_EVT32(pp->idx - PINGPONG_0, SDE_EVTLOG_FUNC_CASE1); + SDE_DEBUG("autorefresh already disabled idx:%d\n", + dbg_idx); + SDE_EVT32(dbg_idx, SDE_EVTLOG_FUNC_CASE1); return 0; } /* disable external TE first */ - pp->ops.connect_external_te(pp, false); + _sde_rm_autorefresh_connect_external_te(pp, intf, hw_intf_te, false); /* get all IRQ indexes */ if (hw_intr->ops.irq_idx_lookup) { irq_idx_pp_done = hw_intr->ops.irq_idx_lookup( - SDE_IRQ_TYPE_PING_PONG_COMP, pp->idx); + SDE_IRQ_TYPE_PING_PONG_COMP, te_irq_idx); irq_idx_autorefresh = hw_intr->ops.irq_idx_lookup( - SDE_IRQ_TYPE_PING_PONG_AUTO_REF, pp->idx); - SDE_DEBUG("pp_done itr_idx = %d autorefresh irq_idx:%d\n", + SDE_IRQ_TYPE_PING_PONG_AUTO_REF, te_irq_idx); + SDE_DEBUG("pp_done irq_idx = %d autorefresh irq_idx:%d\n", irq_idx_pp_done, irq_idx_autorefresh); } /* disable autorefresh */ cfg.enable = false; - pp->ops.setup_autorefresh(pp, &cfg); + _sde_rm_autorefresh_setup(pp, intf, &cfg, hw_intf_te); - SDE_EVT32(pp->idx - PINGPONG_0, irq_idx_pp_done, irq_idx_autorefresh); + SDE_EVT32(dbg_idx, irq_idx_pp_done, irq_idx_autorefresh); _sde_rm_clear_irq_status(hw_intr, irq_idx_pp_done, irq_idx_autorefresh); /* @@ -1267,9 +1352,9 @@ static int _sde_rm_autorefresh_disable(struct sde_hw_pingpong *pp, for (i = 0; i < loop; i++) { info.wr_ptr_line_count = 0; info.rd_ptr_init_val = 0; - pp->ops.get_vsync_info(pp, &info); + _sde_rm_autorefresh_get_vsync_info(pp, intf, &info, hw_intf_te); - SDE_EVT32(pp->idx - PINGPONG_0, info.wr_ptr_line_count, + SDE_EVT32(dbg_idx, info.wr_ptr_line_count, info.rd_ptr_init_val, SDE_EVTLOG_FUNC_CASE1); /* wait for read ptr intr */ @@ -1278,10 +1363,11 @@ static int _sde_rm_autorefresh_disable(struct sde_hw_pingpong *pp, info.wr_ptr_line_count = 0; info.rd_ptr_init_val = 0; - pp->ops.get_vsync_info(pp, &info); - SDE_DEBUG("i=%d, line count=%d\n", i, info.wr_ptr_line_count); - SDE_EVT32(pp->idx - PINGPONG_0, info.wr_ptr_line_count, + _sde_rm_autorefresh_get_vsync_info(pp, intf, &info, hw_intf_te); + + SDE_DEBUG("i=%d, line count=%d\n", i, info.wr_ptr_line_count); + SDE_EVT32(dbg_idx, info.wr_ptr_line_count, info.rd_ptr_init_val, SDE_EVTLOG_FUNC_CASE2); /* log line count and return */ @@ -1295,7 +1381,7 @@ static int _sde_rm_autorefresh_disable(struct sde_hw_pingpong *pp, usleep_range(3000, 4000); } - pp->ops.connect_external_te(pp, true); + _sde_rm_autorefresh_connect_external_te(pp, intf, hw_intf_te, true); return rc; } @@ -1315,29 +1401,33 @@ static int _sde_rm_get_pp_dsc_for_cont_splash(struct sde_rm *rm, { int index = 0; int value, dsc_cnt = 0; - struct sde_rm_hw_iter iter_pp; + struct sde_rm_hw_iter iter_pp, intf_iter; + bool hw_intf_te_supported; + struct sde_hw_intr *hw_intr = NULL; if (!rm || !sde_kms || !dsc_ids) { SDE_ERROR("invalid input parameters\n"); return 0; } + hw_intf_te_supported = sde_hw_intf_te_supported(sde_kms->catalog); + hw_intr = sde_kms->hw_intr; + if (!hw_intr) { + SDE_ERROR("hw_intr handler not initialized\n"); + return 0; + } + SDE_DEBUG("max_dsc_cnt = %d\n", max_dsc_cnt); sde_rm_init_hw_iter(&iter_pp, 0, SDE_HW_BLK_PINGPONG); while (_sde_rm_get_hw_locked(rm, &iter_pp)) { struct sde_hw_pingpong *pp = to_sde_hw_pingpong(iter_pp.blk->hw); - struct sde_hw_intr *hw_intr = NULL; if (!pp->ops.get_dsc_status) { SDE_ERROR("get_dsc_status ops not initialized\n"); return 0; } - hw_intr = sde_kms->hw_intr; - if (!hw_intr) { - SDE_ERROR("hw_intr handler not initialized\n"); - return 0; - } + value = pp->ops.get_dsc_status(pp); SDE_DEBUG("DSC[%d]=0x%x, dsc_cnt = %d\n", index, value, dsc_cnt); @@ -1347,7 +1437,19 @@ static int _sde_rm_get_pp_dsc_for_cont_splash(struct sde_rm *rm, } index++; - _sde_rm_autorefresh_disable(pp, hw_intr); + if (!hw_intf_te_supported) + _sde_rm_autorefresh_disable(pp, NULL, hw_intr, + hw_intf_te_supported); + } + + sde_rm_init_hw_iter(&intf_iter, 0, SDE_HW_BLK_INTF); + while (_sde_rm_get_hw_locked(rm, &intf_iter)) { + struct sde_hw_intf *intf = + to_sde_hw_intf(intf_iter.blk->hw); + + if (hw_intf_te_supported) + _sde_rm_autorefresh_disable(NULL, intf, hw_intr, + hw_intf_te_supported); } return dsc_cnt; @@ -1412,20 +1514,14 @@ static void _sde_rm_get_ctl_top_for_cont_splash(struct sde_hw_ctl *ctl, return; } - if (!ctl->ops.read_ctl_top) { - SDE_ERROR("read_ctl_top not initialized\n"); + if (!ctl->ops.get_ctl_intf) { + SDE_ERROR("get_ctl_intf not initialized\n"); return; } - top->value = ctl->ops.read_ctl_top(ctl); - top->intf_sel = (top->value >> 4) & 0xf; - top->pp_sel = (top->value >> 8) & 0x7; - top->dspp_sel = (top->value >> 11) & 0x3; - top->mode_sel = (top->value >> 17) & 0x1; + top->intf_sel = ctl->ops.get_ctl_intf(ctl); - SDE_DEBUG("id=%d,top->0x%x,pp_sel=0x%x,dspp_sel=0x%x,intf_sel=%d\n", - ctl->idx, top->value, top->pp_sel, - top->dspp_sel, top->intf_sel); + SDE_DEBUG("id=%d intf_sel=%d\n", ctl->idx, top->intf_sel); } int sde_rm_cont_splash_res_init(struct msm_drm_private *priv, @@ -1436,6 +1532,7 @@ int sde_rm_cont_splash_res_init(struct msm_drm_private *priv, struct sde_rm_hw_iter iter_c; int index = 0, ctl_top_cnt; struct sde_kms *sde_kms = NULL; + struct sde_hw_mdp *hw_mdp; if (!priv || !rm || !cat || !splash_data) { SDE_ERROR("invalid input parameters\n"); @@ -1491,9 +1588,16 @@ int sde_rm_cont_splash_res_init(struct msm_drm_private *priv, sde_kms, cat->dsc_count, splash_data->dsc_ids); - SDE_DEBUG("splash_data: ctl_top_cnt=%d, lm_cnt=%d, dsc_cnt=%d\n", + + hw_mdp = sde_rm_get_mdp(rm); + if (hw_mdp && hw_mdp->ops.get_split_flush_status) { + splash_data->single_flush_en = + hw_mdp->ops.get_split_flush_status(hw_mdp); + } + + SDE_DEBUG("splash_data: ctl_top_cnt=%d, lm_cnt=%d, dsc_cnt=%d sf=%d\n", splash_data->ctl_top_cnt, splash_data->lm_cnt, - splash_data->dsc_cnt); + splash_data->dsc_cnt, splash_data->single_flush_en); return 0; } diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c index c92af97d6a1fc826ca407576642eb540bcdcb7b8..2e50a097de4f323d4259da57b7f3793bb1d4f557 100644 --- a/drivers/gpu/drm/msm/sde_dbg.c +++ b/drivers/gpu/drm/msm/sde_dbg.c @@ -4046,6 +4046,11 @@ static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf, SDE_EVTLOG_BUF_MAX, true); + if (len < 0 || len > count) { + pr_err("len is more than user buffer size"); + return 0; + } + if (copy_to_user(buff, evtlog_buf, len)) return -EFAULT; *ppos += len; diff --git a/drivers/gpu/drm/msm/sde_io_util.c b/drivers/gpu/drm/msm/sde_io_util.c index d5a438e5f26cd6f599f790b010319cce921a125f..685ca5458a602621af62b3b948142df5bae756e0 100644 --- a/drivers/gpu/drm/msm/sde_io_util.c +++ b/drivers/gpu/drm/msm/sde_io_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 2017-2018 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 @@ -217,6 +217,7 @@ int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable) { int i = 0, rc = 0; bool need_sleep; + int reg_mode; if (enable) { for (i = 0; i < num_vreg; i++) { @@ -227,6 +228,17 @@ int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable) in_vreg[i].vreg_name, rc); goto vreg_set_opt_mode_fail; } + reg_mode = regulator_get_mode(in_vreg[i].vreg); + if (reg_mode == REGULATOR_MODE_FAST) { + DEV_DBG("%pS->%s: %s operation not allowed\n", + __builtin_return_address(0), __func__, + in_vreg[i].vreg_name); + /* + * This regulator is controlled by Hw cannot be + * controlled by Sw vote + */ + continue; + } need_sleep = !regulator_is_enabled(in_vreg[i].vreg); if (in_vreg[i].pre_on_sleep && need_sleep) usleep_range(in_vreg[i].pre_on_sleep * 1000, @@ -252,6 +264,17 @@ int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable) } } else { for (i = num_vreg-1; i >= 0; i--) { + reg_mode = regulator_get_mode(in_vreg[i].vreg); + if (reg_mode == REGULATOR_MODE_FAST) { + DEV_DBG("%pS->%s: %s operation not allowed\n", + __builtin_return_address(0), __func__, + in_vreg[i].vreg_name); + /* + * This regulator is controlled by Hw cannot be + * controlled by Sw vote + */ + continue; + } if (in_vreg[i].pre_off_sleep) usleep_range(in_vreg[i].pre_off_sleep * 1000, in_vreg[i].pre_off_sleep * 1000); diff --git a/drivers/gpu/drm/msm/sde_power_handle.c b/drivers/gpu/drm/msm/sde_power_handle.c index 40542abf4e0c9a27e44db4d87bcabd7bdf7ac803..29c868eebecf4bca2217561d471f23b882d60f58 100644 --- a/drivers/gpu/drm/msm/sde_power_handle.c +++ b/drivers/gpu/drm/msm/sde_power_handle.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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,12 +56,9 @@ static void sde_power_event_trigger_locked(struct sde_power_handle *phandle, } } -static int sde_power_rsc_update(struct sde_power_handle *phandle, bool enable) +static inline void sde_power_rsc_client_init(struct sde_power_handle *phandle) { - u32 rsc_state; - int ret = 0; - - /* creates the rsc client on the first enable */ + /* creates the rsc client */ if (!phandle->rsc_client_init) { phandle->rsc_client = sde_rsc_client_create(SDE_RSC_INDEX, "sde_power_handle", false); @@ -72,6 +69,12 @@ static int sde_power_rsc_update(struct sde_power_handle *phandle, bool enable) } phandle->rsc_client_init = true; } +} + +static int sde_power_rsc_update(struct sde_power_handle *phandle, bool enable) +{ + u32 rsc_state; + int ret = 0; rsc_state = enable ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE; @@ -890,6 +893,9 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, if (!changed) goto end; + /* RSC client init */ + sde_power_rsc_client_init(phandle); + if (enable) { sde_power_event_trigger_locked(phandle, SDE_POWER_EVENT_PRE_ENABLE); @@ -903,21 +909,11 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, goto data_bus_hdl_err; } } - /* - * - When the target is RSCC enabled, regulator should - * be enabled by the s/w only for the first time during - * bootup. After that, RSCC hardware takes care of enabling/ - * disabling it. - * - When the target is not RSCC enabled, regulator should - * be totally handled by the software. - */ - if (!phandle->rsc_client) { - rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, - enable); - if (rc) { - pr_err("failed to enable vregs rc=%d\n", rc); - goto vreg_err; - } + rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, + enable); + if (rc) { + pr_err("failed to enable vregs rc=%d\n", rc); + goto vreg_err; } rc = sde_power_reg_bus_update(phandle->reg_bus_hdl, @@ -927,13 +923,13 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, goto reg_bus_hdl_err; } + SDE_EVT32_VERBOSE(enable, SDE_EVTLOG_FUNC_CASE1); rc = sde_power_rsc_update(phandle, true); if (rc) { pr_err("failed to update rsc\n"); goto rsc_err; } - SDE_EVT32_VERBOSE(enable, SDE_EVTLOG_FUNC_CASE1); rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); if (rc) { pr_err("clock enable failed rc:%d\n", rc); @@ -948,16 +944,15 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, SDE_POWER_EVENT_PRE_DISABLE); SDE_EVT32_VERBOSE(enable, SDE_EVTLOG_FUNC_CASE2); - msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); - sde_power_rsc_update(phandle, false); + msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); + sde_power_reg_bus_update(phandle->reg_bus_hdl, max_usecase_ndx); - if (!phandle->rsc_client) - msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, - enable); + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable); + for (i = 0 ; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) sde_power_data_bus_update(&phandle->data_bus_handle[i], enable); @@ -977,8 +972,7 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, rsc_err: sde_power_reg_bus_update(phandle->reg_bus_hdl, prev_usecase_ndx); reg_bus_hdl_err: - if (!phandle->rsc_client) - msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0); + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0); vreg_err: for (i = 0 ; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) sde_power_data_bus_update(&phandle->data_bus_handle[i], 0); diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c index 3a70746fae1ee2508fc0414519d8908f5bdab05d..f811ca149b36e28ca28ad520633cb6c7d8617b47 100644 --- a/drivers/gpu/drm/msm/sde_rsc.c +++ b/drivers/gpu/drm/msm/sde_rsc.c @@ -898,23 +898,22 @@ EXPORT_SYMBOL(sde_rsc_client_state_update); int sde_rsc_client_vote(struct sde_rsc_client *caller_client, u32 bus_id, u64 ab_vote, u64 ib_vote) { - int rc = 0; + int rc = 0, rsc_index; struct sde_rsc_priv *rsc; - if (!caller_client) { - pr_err("invalid client for ab/ib vote\n"); - return -EINVAL; - } else if (caller_client->rsc_index >= MAX_RSC_COUNT) { + if (caller_client && caller_client->rsc_index >= MAX_RSC_COUNT) { pr_err("invalid rsc index\n"); return -EINVAL; } - rsc = rsc_prv_list[caller_client->rsc_index]; + rsc_index = caller_client ? caller_client->rsc_index : SDE_RSC_INDEX; + rsc = rsc_prv_list[rsc_index]; if (!rsc) return -EINVAL; pr_debug("client:%s ab:%llu ib:%llu\n", - caller_client->name, ab_vote, ib_vote); + caller_client ? caller_client->name : "unknown", + ab_vote, ib_vote); mutex_lock(&rsc->client_lock); rc = sde_rsc_clk_enable(&rsc->phandle, rsc->pclient, true); diff --git a/drivers/gpu/drm/msm/sde_rsc_hw.c b/drivers/gpu/drm/msm/sde_rsc_hw.c index 35f1d55e1777f64f901cb2c6379cbbe567eb3a63..509ae503eaf93300410a191a7cfa49dbdd65f81c 100644 --- a/drivers/gpu/drm/msm/sde_rsc_hw.c +++ b/drivers/gpu/drm/msm/sde_rsc_hw.c @@ -99,6 +99,13 @@ #define MAX_CHECK_LOOPS 500 #define POWER_CTRL_BIT_12 12 +#define SDE_RSC_MODE_0_VAL 0 +#define SDE_RSC_MODE_1_VAL 1 +#define MAX_MODE2_ENTRY_TRY 3 + +#define SDE_RSC_REV_1 0x1 +#define SDE_RSC_REV_2 0x2 + static void rsc_event_trigger(struct sde_rsc_priv *rsc, uint32_t event_type) { struct sde_rsc_event *event; @@ -191,39 +198,40 @@ static int rsc_hw_seq_memory_init_v2(struct sde_rsc_priv *rsc) 0x38bb9ebe, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x10, 0xbeff39e0, rsc->debug_mode); - - /* Mode - 2 sequence */ dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x14, 0x20209b9e, rsc->debug_mode); + + /* Mode - 2 sequence */ dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x18, - 0xfab9baa0, rsc->debug_mode); + 0xb9bae5a0, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x1c, - 0xfebdbbf9, rsc->debug_mode); + 0xbdbbf9fa, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x20, - 0xa138999a, rsc->debug_mode); + 0x38999afe, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x24, - 0xa2e081e1, rsc->debug_mode); + 0xac81e1a1, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x28, - 0x9d3982e2, rsc->debug_mode); - - /* tcs sleep & wake sequence */ + 0x82e2a2e0, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x2c, - 0x20209bfd, rsc->debug_mode); + 0x8cfd9d39, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x30, - 0x01a6fcbc, rsc->debug_mode); + 0xbc20209b, rsc->debug_mode); + + /* tcs sleep & wake sequence */ dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x34, - 0x20209ce6, rsc->debug_mode); + 0xe601a6fc, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x38, - 0x01a7fcbc, rsc->debug_mode); + 0xbc20209c, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x3c, - 0x00209ce7, rsc->debug_mode); - + 0xe701a7fc, rsc->debug_mode); + dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x40, + 0x0000209c, rsc->debug_mode); /* branch address */ dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_CFG_BR_ADDR_0_DRV0, - 0x30, rsc->debug_mode); + 0x33, rsc->debug_mode); dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_CFG_BR_ADDR_1_DRV0, - 0x38, rsc->debug_mode); + 0x3b, rsc->debug_mode); /* start address */ dss_reg_w(&rsc->drv_io, SDE_RSC_SOLVER_OVERRIDE_CTRL_DRV0, @@ -452,11 +460,13 @@ static int sde_rsc_mode2_exit(struct sde_rsc_priv *rsc, dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_CTRL, reg, rsc->debug_mode); - reg = dss_reg_r(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, + if (rsc->version < SDE_RSC_REV_2) { + reg = dss_reg_r(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, rsc->debug_mode); - reg |= BIT(13); - dss_reg_w(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, + reg |= BIT(13); + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, reg, rsc->debug_mode); + } /* make sure that mode-2 exit before wait*/ wmb(); @@ -475,36 +485,31 @@ static int sde_rsc_mode2_exit(struct sde_rsc_priv *rsc, usleep_range(10, 100); } - reg = dss_reg_r(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, + if (rsc->version < SDE_RSC_REV_2) { + reg = dss_reg_r(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, rsc->debug_mode); - reg &= ~BIT(13); - dss_reg_w(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, + reg &= ~BIT(13); + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_SPARE_PWR_EVENT, reg, rsc->debug_mode); + } + if (rc) pr_err("vdd reg is not enabled yet\n"); + dss_reg_w(&rsc->drv_io, SDE_RSC_SOLVER_SOLVER_MODES_ENABLED_DRV0, + 0x3, rsc->debug_mode); + rsc_event_trigger(rsc, SDE_RSC_EVENT_POST_CORE_RESTORE); return rc; } -static int sde_rsc_mode2_entry(struct sde_rsc_priv *rsc) +static int sde_rsc_mode2_entry_trigger(struct sde_rsc_priv *rsc) { int rc; int count, wrapper_status; unsigned long reg; - if (rsc->power_collapse_block) - return -EINVAL; - - rc = regulator_set_mode(rsc->fs, REGULATOR_MODE_FAST); - if (rc) { - pr_err("vdd reg fast mode set failed rc:%d\n", rc); - return rc; - } - - rsc_event_trigger(rsc, SDE_RSC_EVENT_PRE_CORE_PC); - /* update qtimers to high during clk & video mode state */ if ((rsc->current_state == SDE_RSC_VID_STATE) || (rsc->current_state == SDE_RSC_CLK_STATE)) { @@ -549,12 +554,90 @@ static int sde_rsc_mode2_entry(struct sde_rsc_priv *rsc) usleep_range(10, 100); } + return rc; +} + +static void sde_rsc_reset_mode_0_1(struct sde_rsc_priv *rsc) +{ + u32 seq_busy, current_mode, curr_inst_addr; + + seq_busy = dss_reg_r(&rsc->drv_io, SDE_RSCC_SEQ_BUSY_DRV0, + rsc->debug_mode); + current_mode = dss_reg_r(&rsc->drv_io, SDE_RSCC_SOLVER_STATUS2_DRV0, + rsc->debug_mode); + curr_inst_addr = dss_reg_r(&rsc->drv_io, SDE_RSCC_SEQ_PROGRAM_COUNTER, + rsc->debug_mode); + SDE_EVT32(seq_busy, current_mode, curr_inst_addr); + + if (seq_busy && (current_mode == SDE_RSC_MODE_0_VAL || + current_mode == SDE_RSC_MODE_1_VAL)) { + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F1_QTMR_V1_CNTP_CVAL_HI, + 0xffffff, rsc->debug_mode); + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F1_QTMR_V1_CNTP_CVAL_LO, + 0xffffffff, rsc->debug_mode); + /* unstick f1 qtimer */ + wmb(); + + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F1_QTMR_V1_CNTP_CVAL_HI, + 0x0, rsc->debug_mode); + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F1_QTMR_V1_CNTP_CVAL_LO, + 0x0, rsc->debug_mode); + /* manually trigger f1 qtimer interrupt */ + wmb(); + + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F0_QTMR_V1_CNTP_CVAL_HI, + 0xffffff, rsc->debug_mode); + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F0_QTMR_V1_CNTP_CVAL_LO, + 0xffffffff, rsc->debug_mode); + /* unstick f0 qtimer */ + wmb(); + + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F0_QTMR_V1_CNTP_CVAL_HI, + 0x0, rsc->debug_mode); + dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F0_QTMR_V1_CNTP_CVAL_LO, + 0x0, rsc->debug_mode); + /* manually trigger f0 qtimer interrupt */ + wmb(); + } +} + +static int sde_rsc_mode2_entry(struct sde_rsc_priv *rsc) +{ + int rc = 0, i; + u32 reg; + + if (rsc->power_collapse_block) + return -EINVAL; + + rc = regulator_set_mode(rsc->fs, REGULATOR_MODE_FAST); if (rc) { - pr_err("mdss gdsc power down failed rc:%d\n", rc); - SDE_EVT32(rc, SDE_EVTLOG_ERROR); - goto end; + pr_err("vdd reg fast mode set failed rc:%d\n", rc); + return rc; } + dss_reg_w(&rsc->drv_io, SDE_RSC_SOLVER_SOLVER_MODES_ENABLED_DRV0, + 0x7, rsc->debug_mode); + rsc_event_trigger(rsc, SDE_RSC_EVENT_PRE_CORE_PC); + + for (i = 0; i <= MAX_MODE2_ENTRY_TRY; i++) { + rc = sde_rsc_mode2_entry_trigger(rsc); + if (!rc) + break; + + reg = dss_reg_r(&rsc->drv_io, + SDE_RSCC_SEQ_PROGRAM_COUNTER, rsc->debug_mode); + pr_err("mdss gdsc power down failed, instruction:0x%x, rc:%d\n", + reg, rc); + SDE_EVT32(rc, reg, SDE_EVTLOG_ERROR); + + /* avoid touching f1 qtimer for last try */ + if (i != MAX_MODE2_ENTRY_TRY) + sde_rsc_reset_mode_0_1(rsc); + } + + if (rc) + goto end; + if ((rsc->current_state == SDE_RSC_VID_STATE) || (rsc->current_state == SDE_RSC_CLK_STATE)) { dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL, @@ -628,7 +711,7 @@ static int sde_rsc_state_update(struct sde_rsc_priv *rsc, reg = dss_reg_r(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL, rsc->debug_mode); - reg &= ~(BIT(8) | BIT(0)); + reg &= ~BIT(0); dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL, reg, rsc->debug_mode); /* make sure that solver mode is disabled */ @@ -667,7 +750,7 @@ int rsc_hw_init(struct sde_rsc_priv *rsc) goto end; } - if (rsc->version == 2) + if (rsc->version == SDE_RSC_REV_2) rc = rsc_hw_seq_memory_init_v2(rsc); else rc = rsc_hw_seq_memory_init(rsc); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h index 53d01fb00a8b66b189856d3d24a511f08101aa73..1dbe593e596093e18f30be0cfc38b764909fd984 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gf100.fuc3.h @@ -47,8 +47,8 @@ static uint32_t gf100_pmu_data[] = { 0x00000000, 0x00000000, 0x584d454d, - 0x00000756, - 0x00000748, + 0x00000754, + 0x00000746, 0x00000000, 0x00000000, 0x00000000, @@ -69,8 +69,8 @@ static uint32_t gf100_pmu_data[] = { 0x00000000, 0x00000000, 0x46524550, - 0x0000075a, 0x00000758, + 0x00000756, 0x00000000, 0x00000000, 0x00000000, @@ -91,8 +91,8 @@ static uint32_t gf100_pmu_data[] = { 0x00000000, 0x00000000, 0x5f433249, - 0x00000b8a, - 0x00000a2d, + 0x00000b88, + 0x00000a2b, 0x00000000, 0x00000000, 0x00000000, @@ -113,8 +113,8 @@ static uint32_t gf100_pmu_data[] = { 0x00000000, 0x00000000, 0x54534554, - 0x00000bb3, - 0x00000b8c, + 0x00000bb1, + 0x00000b8a, 0x00000000, 0x00000000, 0x00000000, @@ -135,8 +135,8 @@ static uint32_t gf100_pmu_data[] = { 0x00000000, 0x00000000, 0x454c4449, - 0x00000bbf, 0x00000bbd, + 0x00000bbb, 0x00000000, 0x00000000, 0x00000000, @@ -237,19 +237,19 @@ static uint32_t gf100_pmu_data[] = { 0x000005d3, 0x00000003, 0x00000002, - 0x0000069d, + 0x0000069b, 0x00040004, 0x00000000, - 0x000006b9, + 0x000006b7, 0x00010005, 0x00000000, - 0x000006d6, + 0x000006d4, 0x00010006, 0x00000000, 0x0000065b, 0x00000007, 0x00000000, - 0x000006e1, + 0x000006df, /* 0x03c4: memx_func_tail */ /* 0x03c4: memx_ts_start */ 0x00000000, @@ -1373,432 +1373,432 @@ static uint32_t gf100_pmu_code[] = { /* 0x065b: memx_func_wait_vblank */ 0x9800f840, 0x66b00016, - 0x130bf400, + 0x120bf400, 0xf40166b0, 0x0ef4060b, /* 0x066d: memx_func_wait_vblank_head1 */ - 0x2077f12e, - 0x070ef400, -/* 0x0674: memx_func_wait_vblank_head0 */ - 0x000877f1, -/* 0x0678: memx_func_wait_vblank_0 */ - 0x07c467f1, - 0xcf0664b6, - 0x67fd0066, - 0xf31bf404, -/* 0x0688: memx_func_wait_vblank_1 */ - 0x07c467f1, - 0xcf0664b6, - 0x67fd0066, - 0xf30bf404, -/* 0x0698: memx_func_wait_vblank_fini */ - 0xf80410b6, -/* 0x069d: memx_func_wr32 */ - 0x00169800, - 0xb6011598, - 0x60f90810, - 0xd0fc50f9, - 0x21f4e0fc, - 0x0242b640, - 0xf8e91bf4, -/* 0x06b9: memx_func_wait */ - 0x2c87f000, - 0xcf0684b6, - 0x1e980088, - 0x011d9800, - 0x98021c98, - 0x10b6031b, - 0xa321f410, -/* 0x06d6: memx_func_delay */ - 0x1e9800f8, - 0x0410b600, - 0xf87e21f4, -/* 0x06e1: memx_func_train */ -/* 0x06e3: memx_exec */ - 0xf900f800, - 0xb9d0f9e0, - 0xb2b902c1, -/* 0x06ed: memx_exec_next */ - 0x00139802, - 0xe70410b6, - 0xe701f034, - 0xb601e033, - 0x30f00132, - 0xde35980c, - 0x12b855f9, - 0xe41ef406, - 0x98f10b98, - 0xcbbbf20c, - 0xc4b7f102, - 0x06b4b607, - 0xfc00bbcf, - 0xf5e0fcd0, - 0xf8033621, -/* 0x0729: memx_info */ - 0x01c67000, -/* 0x072f: memx_info_data */ - 0xf10e0bf4, - 0xf103ccc7, - 0xf40800b7, -/* 0x073a: memx_info_train */ - 0xc7f10b0e, - 0xb7f10bcc, -/* 0x0742: memx_info_send */ - 0x21f50100, - 0x00f80336, -/* 0x0748: memx_recv */ - 0xf401d6b0, - 0xd6b0980b, - 0xd80bf400, -/* 0x0756: memx_init */ - 0x00f800f8, -/* 0x0758: perf_recv */ -/* 0x075a: perf_init */ + 0x2077f02c, +/* 0x0673: memx_func_wait_vblank_head0 */ + 0xf0060ef4, +/* 0x0676: memx_func_wait_vblank_0 */ + 0x67f10877, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x0686: memx_func_wait_vblank_1 */ + 0x67f1f31b, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x0696: memx_func_wait_vblank_fini */ + 0x10b6f30b, +/* 0x069b: memx_func_wr32 */ + 0x9800f804, + 0x15980016, + 0x0810b601, + 0x50f960f9, + 0xe0fcd0fc, + 0xb64021f4, + 0x1bf40242, +/* 0x06b7: memx_func_wait */ + 0xf000f8e9, + 0x84b62c87, + 0x0088cf06, + 0x98001e98, + 0x1c98011d, + 0x031b9802, + 0xf41010b6, + 0x00f8a321, +/* 0x06d4: memx_func_delay */ + 0xb6001e98, + 0x21f40410, +/* 0x06df: memx_func_train */ + 0xf800f87e, +/* 0x06e1: memx_exec */ + 0xf9e0f900, + 0x02c1b9d0, +/* 0x06eb: memx_exec_next */ + 0x9802b2b9, + 0x10b60013, + 0xf034e704, + 0xe033e701, + 0x0132b601, + 0x980c30f0, + 0x55f9de35, + 0xf40612b8, + 0x0b98e41e, + 0xf20c98f1, + 0xf102cbbb, + 0xb607c4b7, + 0xbbcf06b4, + 0xfcd0fc00, + 0x3621f5e0, +/* 0x0727: memx_info */ + 0x7000f803, + 0x0bf401c6, +/* 0x072d: memx_info_data */ + 0xccc7f10e, + 0x00b7f103, + 0x0b0ef408, +/* 0x0738: memx_info_train */ + 0x0bccc7f1, + 0x0100b7f1, +/* 0x0740: memx_info_send */ + 0x033621f5, +/* 0x0746: memx_recv */ + 0xd6b000f8, + 0x980bf401, + 0xf400d6b0, + 0x00f8d80b, +/* 0x0754: memx_init */ +/* 0x0756: perf_recv */ 0x00f800f8, -/* 0x075c: i2c_drive_scl */ - 0xf40036b0, - 0x07f1110b, - 0x04b607e0, - 0x0001d006, - 0x00f804bd, -/* 0x0770: i2c_drive_scl_lo */ - 0x07e407f1, - 0xd00604b6, - 0x04bd0001, -/* 0x077e: i2c_drive_sda */ +/* 0x0758: perf_init */ +/* 0x075a: i2c_drive_scl */ 0x36b000f8, 0x110bf400, 0x07e007f1, 0xd00604b6, - 0x04bd0002, -/* 0x0792: i2c_drive_sda_lo */ + 0x04bd0001, +/* 0x076e: i2c_drive_scl_lo */ 0x07f100f8, 0x04b607e4, + 0x0001d006, + 0x00f804bd, +/* 0x077c: i2c_drive_sda */ + 0xf40036b0, + 0x07f1110b, + 0x04b607e0, 0x0002d006, 0x00f804bd, -/* 0x07a0: i2c_sense_scl */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0431fd00, - 0xf4060bf4, -/* 0x07b6: i2c_sense_scl_done */ - 0x00f80131, -/* 0x07b8: i2c_sense_sda */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0432fd00, - 0xf4060bf4, -/* 0x07ce: i2c_sense_sda_done */ - 0x00f80131, -/* 0x07d0: i2c_raise_scl */ - 0x47f140f9, - 0x37f00898, - 0x5c21f501, -/* 0x07dd: i2c_raise_scl_wait */ - 0xe8e7f107, - 0x7e21f403, - 0x07a021f5, - 0xb60901f4, - 0x1bf40142, -/* 0x07f1: i2c_raise_scl_done */ - 0xf840fcef, -/* 0x07f5: i2c_start */ - 0xa021f500, - 0x0d11f407, - 0x07b821f5, - 0xf40611f4, -/* 0x0806: i2c_start_rep */ - 0x37f0300e, - 0x5c21f500, - 0x0137f007, - 0x077e21f5, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0xd021f550, - 0x0464b607, -/* 0x0833: i2c_start_send */ - 0xf01f11f4, +/* 0x0790: i2c_drive_sda_lo */ + 0x07e407f1, + 0xd00604b6, + 0x04bd0002, +/* 0x079e: i2c_sense_scl */ + 0x32f400f8, + 0xc437f101, + 0x0634b607, + 0xfd0033cf, + 0x0bf40431, + 0x0131f406, +/* 0x07b4: i2c_sense_scl_done */ +/* 0x07b6: i2c_sense_sda */ + 0x32f400f8, + 0xc437f101, + 0x0634b607, + 0xfd0033cf, + 0x0bf40432, + 0x0131f406, +/* 0x07cc: i2c_sense_sda_done */ +/* 0x07ce: i2c_raise_scl */ + 0x40f900f8, + 0x089847f1, + 0xf50137f0, +/* 0x07db: i2c_raise_scl_wait */ + 0xf1075a21, + 0xf403e8e7, + 0x21f57e21, + 0x01f4079e, + 0x0142b609, +/* 0x07ef: i2c_raise_scl_done */ + 0xfcef1bf4, +/* 0x07f3: i2c_start */ + 0xf500f840, + 0xf4079e21, + 0x21f50d11, + 0x11f407b6, + 0x300ef406, +/* 0x0804: i2c_start_rep */ + 0xf50037f0, + 0xf0075a21, + 0x21f50137, + 0x76bb077c, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb607ce21, + 0x11f40464, +/* 0x0831: i2c_start_send */ + 0x0037f01f, + 0x077c21f5, + 0x1388e7f1, + 0xf07e21f4, 0x21f50037, - 0xe7f1077e, + 0xe7f1075a, 0x21f41388, - 0x0037f07e, - 0x075c21f5, - 0x1388e7f1, -/* 0x084f: i2c_start_out */ - 0xf87e21f4, -/* 0x0851: i2c_stop */ - 0x0037f000, - 0x075c21f5, - 0xf50037f0, - 0xf1077e21, - 0xf403e8e7, - 0x37f07e21, - 0x5c21f501, - 0x88e7f107, - 0x7e21f413, +/* 0x084d: i2c_start_out */ +/* 0x084f: i2c_stop */ + 0xf000f87e, + 0x21f50037, + 0x37f0075a, + 0x7c21f500, + 0xe8e7f107, + 0x7e21f403, 0xf50137f0, - 0xf1077e21, + 0xf1075a21, 0xf41388e7, - 0x00f87e21, -/* 0x0884: i2c_bitw */ - 0x077e21f5, - 0x03e8e7f1, - 0xbb7e21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x07d021f5, - 0xf40464b6, - 0xe7f11811, - 0x21f41388, - 0x0037f07e, - 0x075c21f5, - 0x1388e7f1, -/* 0x08c3: i2c_bitw_out */ - 0xf87e21f4, -/* 0x08c5: i2c_bitr */ - 0x0137f000, - 0x077e21f5, - 0x03e8e7f1, - 0xbb7e21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x07d021f5, - 0xf40464b6, - 0x21f51b11, - 0x37f007b8, - 0x5c21f500, + 0x37f07e21, + 0x7c21f501, 0x88e7f107, 0x7e21f413, - 0xf4013cf0, -/* 0x090a: i2c_bitr_done */ - 0x00f80131, -/* 0x090c: i2c_get_byte */ - 0xf00057f0, -/* 0x0912: i2c_get_byte_next */ - 0x54b60847, - 0x0076bb01, +/* 0x0882: i2c_bitw */ + 0x21f500f8, + 0xe7f1077c, + 0x21f403e8, + 0x0076bb7e, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b608c5, - 0x2b11f404, - 0xb60553fd, - 0x1bf40142, - 0x0137f0d8, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0x8421f550, - 0x0464b608, -/* 0x095c: i2c_get_byte_done */ -/* 0x095e: i2c_put_byte */ - 0x47f000f8, -/* 0x0961: i2c_put_byte_next */ - 0x0142b608, - 0xbb3854ff, + 0x64b607ce, + 0x1811f404, + 0x1388e7f1, + 0xf07e21f4, + 0x21f50037, + 0xe7f1075a, + 0x21f41388, +/* 0x08c1: i2c_bitw_out */ +/* 0x08c3: i2c_bitr */ + 0xf000f87e, + 0x21f50137, + 0xe7f1077c, + 0x21f403e8, + 0x0076bb7e, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b607ce, + 0x1b11f404, + 0x07b621f5, + 0xf50037f0, + 0xf1075a21, + 0xf41388e7, + 0x3cf07e21, + 0x0131f401, +/* 0x0908: i2c_bitr_done */ +/* 0x090a: i2c_get_byte */ + 0x57f000f8, + 0x0847f000, +/* 0x0910: i2c_get_byte_next */ + 0xbb0154b6, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x088421f5, + 0x08c321f5, 0xf40464b6, - 0x46b03411, - 0xd81bf400, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0xc521f550, - 0x0464b608, - 0xbb0f11f4, - 0x36b00076, - 0x061bf401, -/* 0x09b7: i2c_put_byte_done */ - 0xf80132f4, -/* 0x09b9: i2c_addr */ - 0x0076bb00, + 0x53fd2b11, + 0x0142b605, + 0xf0d81bf4, + 0x76bb0137, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6088221, +/* 0x095a: i2c_get_byte_done */ + 0x00f80464, +/* 0x095c: i2c_put_byte */ +/* 0x095f: i2c_put_byte_next */ + 0xb60847f0, + 0x54ff0142, + 0x0076bb38, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b607f5, - 0x2911f404, - 0x012ec3e7, - 0xfd0134b6, - 0x76bb0553, + 0x64b60882, + 0x3411f404, + 0xf40046b0, + 0x76bbd81b, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb6095e21, -/* 0x09fe: i2c_addr_done */ - 0x00f80464, -/* 0x0a00: i2c_acquire_addr */ - 0xb6f8cec7, - 0xe0b702e4, - 0xee980d1c, -/* 0x0a0f: i2c_acquire */ - 0xf500f800, - 0xf40a0021, - 0xd9f00421, - 0x4021f403, -/* 0x0a1e: i2c_release */ - 0x21f500f8, - 0x21f40a00, - 0x03daf004, - 0xf84021f4, -/* 0x0a2d: i2c_recv */ - 0x0132f400, - 0xb6f8c1c7, - 0x16b00214, - 0x3a1ff528, - 0xf413a001, - 0x0032980c, - 0x0ccc13a0, - 0xf4003198, - 0xd0f90231, - 0xd0f9e0f9, - 0x000067f1, - 0x100063f1, - 0xbb016792, + 0xb608c321, + 0x11f40464, + 0x0076bb0f, + 0xf40136b0, + 0x32f4061b, +/* 0x09b5: i2c_put_byte_done */ +/* 0x09b7: i2c_addr */ + 0xbb00f801, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0a0f21f5, - 0xfc0464b6, - 0x00d6b0d0, - 0x00b31bf5, - 0xbb0057f0, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x09b921f5, - 0xf50464b6, - 0xc700d011, - 0x76bbe0c5, - 0x0465b600, - 0x659450f9, - 0x0256bb04, - 0x75fd50bd, - 0xf550fc04, - 0xb6095e21, - 0x11f50464, - 0x57f000ad, + 0x07f321f5, + 0xf40464b6, + 0xc3e72911, + 0x34b6012e, + 0x0553fd01, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x5c21f550, + 0x0464b609, +/* 0x09fc: i2c_addr_done */ +/* 0x09fe: i2c_acquire_addr */ + 0xcec700f8, + 0x02e4b6f8, + 0x0d1ce0b7, + 0xf800ee98, +/* 0x0a0d: i2c_acquire */ + 0xfe21f500, + 0x0421f409, + 0xf403d9f0, + 0x00f84021, +/* 0x0a1c: i2c_release */ + 0x09fe21f5, + 0xf00421f4, + 0x21f403da, +/* 0x0a2b: i2c_recv */ + 0xf400f840, + 0xc1c70132, + 0x0214b6f8, + 0xf52816b0, + 0xa0013a1f, + 0x980cf413, + 0x13a00032, + 0x31980ccc, + 0x0231f400, + 0xe0f9d0f9, + 0x67f1d0f9, + 0x63f10000, + 0x67921000, 0x0076bb01, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b609b9, - 0x8a11f504, + 0x64b60a0d, + 0xb0d0fc04, + 0x1bf500d6, + 0x57f000b3, 0x0076bb00, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b6090c, - 0x6a11f404, - 0xbbe05bcb, + 0x64b609b7, + 0xd011f504, + 0xe0c5c700, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x5c21f550, + 0x0464b609, + 0x00ad11f5, + 0xbb0157f0, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x085121f5, - 0xb90464b6, - 0x74bd025b, -/* 0x0b33: i2c_recv_not_rd08 */ - 0xb0430ef4, - 0x1bf401d6, - 0x0057f03d, - 0x09b921f5, - 0xc73311f4, - 0x21f5e0c5, - 0x11f4095e, - 0x0057f029, - 0x09b921f5, - 0xc71f11f4, - 0x21f5e0b5, - 0x11f4095e, - 0x5121f515, - 0xc774bd08, - 0x1bf408c5, - 0x0232f409, -/* 0x0b73: i2c_recv_not_wr08 */ -/* 0x0b73: i2c_recv_done */ - 0xc7030ef4, - 0x21f5f8ce, - 0xe0fc0a1e, - 0x12f4d0fc, - 0x027cb90a, - 0x033621f5, -/* 0x0b88: i2c_recv_exit */ -/* 0x0b8a: i2c_init */ - 0x00f800f8, -/* 0x0b8c: test_recv */ - 0x05d817f1, + 0x09b721f5, + 0xf50464b6, + 0xbb008a11, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x090a21f5, + 0xf40464b6, + 0x5bcb6a11, + 0x0076bbe0, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b6084f, + 0x025bb904, + 0x0ef474bd, +/* 0x0b31: i2c_recv_not_rd08 */ + 0x01d6b043, + 0xf03d1bf4, + 0x21f50057, + 0x11f409b7, + 0xe0c5c733, + 0x095c21f5, + 0xf02911f4, + 0x21f50057, + 0x11f409b7, + 0xe0b5c71f, + 0x095c21f5, + 0xf51511f4, + 0xbd084f21, + 0x08c5c774, + 0xf4091bf4, + 0x0ef40232, +/* 0x0b71: i2c_recv_not_wr08 */ +/* 0x0b71: i2c_recv_done */ + 0xf8cec703, + 0x0a1c21f5, + 0xd0fce0fc, + 0xb90a12f4, + 0x21f5027c, +/* 0x0b86: i2c_recv_exit */ + 0x00f80336, +/* 0x0b88: i2c_init */ +/* 0x0b8a: test_recv */ + 0x17f100f8, + 0x14b605d8, + 0x0011cf06, + 0xf10110b6, + 0xb605d807, + 0x01d00604, + 0xf104bd00, + 0xf1d900e7, + 0xf5134fe3, + 0xf8025621, +/* 0x0bb1: test_init */ + 0x00e7f100, + 0x5621f508, +/* 0x0bbb: idle_recv */ + 0xf800f802, +/* 0x0bbd: idle */ + 0x0031f400, + 0x05d417f1, 0xcf0614b6, 0x10b60011, - 0xd807f101, + 0xd407f101, 0x0604b605, 0xbd0001d0, - 0x00e7f104, - 0x4fe3f1d9, - 0x5621f513, -/* 0x0bb3: test_init */ - 0xf100f802, - 0xf50800e7, - 0xf8025621, -/* 0x0bbd: idle_recv */ -/* 0x0bbf: idle */ - 0xf400f800, - 0x17f10031, - 0x14b605d4, - 0x0011cf06, - 0xf10110b6, - 0xb605d407, - 0x01d00604, -/* 0x0bdb: idle_loop */ - 0xf004bd00, - 0x32f45817, -/* 0x0be1: idle_proc */ -/* 0x0be1: idle_proc_exec */ - 0xb910f902, - 0x21f5021e, - 0x10fc033f, - 0xf40911f4, - 0x0ef40231, -/* 0x0bf5: idle_proc_next */ - 0x5810b6ef, - 0xf4061fb8, - 0x02f4e61b, - 0x0028f4dd, - 0x00bb0ef4, +/* 0x0bd9: idle_loop */ + 0x5817f004, +/* 0x0bdf: idle_proc */ +/* 0x0bdf: idle_proc_exec */ + 0xf90232f4, + 0x021eb910, + 0x033f21f5, + 0x11f410fc, + 0x0231f409, +/* 0x0bf3: idle_proc_next */ + 0xb6ef0ef4, + 0x1fb85810, + 0xe61bf406, + 0xf4dd02f4, + 0x0ef40028, + 0x000000bb, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h index c4edbc79e41a25303732d3982068f1710dba1200..e0222cb832fbc6be08b213812cdf990ef470ee2a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gk208.fuc5.h @@ -47,8 +47,8 @@ static uint32_t gk208_pmu_data[] = { 0x00000000, 0x00000000, 0x584d454d, - 0x000005f3, - 0x000005e5, + 0x000005ee, + 0x000005e0, 0x00000000, 0x00000000, 0x00000000, @@ -69,8 +69,8 @@ static uint32_t gk208_pmu_data[] = { 0x00000000, 0x00000000, 0x46524550, - 0x000005f7, - 0x000005f5, + 0x000005f2, + 0x000005f0, 0x00000000, 0x00000000, 0x00000000, @@ -91,8 +91,8 @@ static uint32_t gk208_pmu_data[] = { 0x00000000, 0x00000000, 0x5f433249, - 0x000009f8, - 0x000008a2, + 0x000009f3, + 0x0000089d, 0x00000000, 0x00000000, 0x00000000, @@ -113,8 +113,8 @@ static uint32_t gk208_pmu_data[] = { 0x00000000, 0x00000000, 0x54534554, - 0x00000a16, - 0x000009fa, + 0x00000a11, + 0x000009f5, 0x00000000, 0x00000000, 0x00000000, @@ -135,8 +135,8 @@ static uint32_t gk208_pmu_data[] = { 0x00000000, 0x00000000, 0x454c4449, - 0x00000a21, - 0x00000a1f, + 0x00000a1c, + 0x00000a1a, 0x00000000, 0x00000000, 0x00000000, @@ -234,22 +234,22 @@ static uint32_t gk208_pmu_data[] = { /* 0x037c: memx_func_next */ 0x00000002, 0x00000000, - 0x000004cf, + 0x000004cc, 0x00000003, 0x00000002, - 0x00000546, + 0x00000541, 0x00040004, 0x00000000, - 0x00000563, + 0x0000055e, 0x00010005, 0x00000000, - 0x0000057d, + 0x00000578, 0x00010006, 0x00000000, - 0x00000541, + 0x0000053c, 0x00000007, 0x00000000, - 0x00000589, + 0x00000584, /* 0x03c4: memx_func_tail */ /* 0x03c4: memx_ts_start */ 0x00000000, @@ -1239,454 +1239,454 @@ static uint32_t gk208_pmu_code[] = { 0x0001f604, 0x00f804bd, /* 0x045c: memx_func_enter */ - 0x162067f1, - 0xf55d77f1, - 0x047e6eb2, - 0xd8b20000, - 0xf90487fd, - 0xfc80f960, - 0x7ee0fcd0, - 0x0700002d, - 0x7e6eb2fe, + 0x47162046, + 0x6eb2f55d, + 0x0000047e, + 0x87fdd8b2, + 0xf960f904, + 0xfcd0fc80, + 0x002d7ee0, + 0xb2fe0700, + 0x00047e6e, + 0xfdd8b200, + 0x60f90487, + 0xd0fc80f9, + 0x2d7ee0fc, + 0xf0460000, + 0x7e6eb226, 0xb2000004, 0x0487fdd8, 0x80f960f9, 0xe0fcd0fc, 0x00002d7e, - 0x26f067f1, - 0x047e6eb2, - 0xd8b20000, - 0xf90487fd, - 0xfc80f960, - 0x7ee0fcd0, - 0x0600002d, - 0x07e04004, - 0xbd0006f6, -/* 0x04b9: memx_func_enter_wait */ - 0x07c04604, - 0xf00066cf, - 0x0bf40464, - 0xcf2c06f7, - 0x06b50066, -/* 0x04cf: memx_func_leave */ - 0x0600f8f1, - 0x0066cf2c, - 0x06f206b5, - 0x07e44004, - 0xbd0006f6, -/* 0x04e1: memx_func_leave_wait */ - 0x07c04604, - 0xf00066cf, - 0x1bf40464, - 0xf067f1f7, + 0xe0400406, + 0x0006f607, +/* 0x04b6: memx_func_enter_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0x2c06f70b, + 0xb50066cf, + 0x00f8f106, +/* 0x04cc: memx_func_leave */ + 0x66cf2c06, + 0xf206b500, + 0xe4400406, + 0x0006f607, +/* 0x04de: memx_func_leave_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0xf046f71b, 0xb2010726, 0x00047e6e, 0xfdd8b200, 0x60f90587, 0xd0fc80f9, 0x2d7ee0fc, - 0x67f10000, - 0x6eb21620, - 0x0000047e, - 0x87fdd8b2, - 0xf960f905, - 0xfcd0fc80, - 0x002d7ee0, - 0x0aa24700, - 0x047e6eb2, - 0xd8b20000, - 0xf90587fd, - 0xfc80f960, - 0x7ee0fcd0, - 0xf800002d, -/* 0x0541: memx_func_wait_vblank */ + 0x20460000, + 0x7e6eb216, + 0xb2000004, + 0x0587fdd8, + 0x80f960f9, + 0xe0fcd0fc, + 0x00002d7e, + 0xb20aa247, + 0x00047e6e, + 0xfdd8b200, + 0x60f90587, + 0xd0fc80f9, + 0x2d7ee0fc, + 0x00f80000, +/* 0x053c: memx_func_wait_vblank */ + 0xf80410b6, +/* 0x0541: memx_func_wr32 */ + 0x00169800, + 0xb6011598, + 0x60f90810, + 0xd0fc50f9, + 0x2d7ee0fc, + 0x42b60000, + 0xe81bf402, +/* 0x055e: memx_func_wait */ + 0x2c0800f8, + 0x980088cf, + 0x1d98001e, + 0x021c9801, + 0xb6031b98, + 0x747e1010, + 0x00f80000, +/* 0x0578: memx_func_delay */ + 0xb6001e98, + 0x587e0410, + 0x00f80000, +/* 0x0584: memx_func_train */ +/* 0x0586: memx_exec */ + 0xe0f900f8, + 0xc1b2d0f9, +/* 0x058e: memx_exec_next */ + 0x1398b2b2, 0x0410b600, -/* 0x0546: memx_func_wr32 */ - 0x169800f8, - 0x01159800, - 0xf90810b6, - 0xfc50f960, + 0x01f034e7, + 0x01e033e7, + 0xf00132b6, + 0x35980c30, + 0xa655f9de, + 0xe51ef412, + 0x98f10b98, + 0xcbbbf20c, + 0x07c44b02, + 0xfc00bbcf, 0x7ee0fcd0, - 0xb600002d, - 0x1bf40242, -/* 0x0563: memx_func_wait */ - 0x0800f8e8, - 0x0088cf2c, - 0x98001e98, - 0x1c98011d, - 0x031b9802, - 0x7e1010b6, - 0xf8000074, -/* 0x057d: memx_func_delay */ - 0x001e9800, - 0x7e0410b6, - 0xf8000058, -/* 0x0589: memx_func_train */ -/* 0x058b: memx_exec */ - 0xf900f800, - 0xb2d0f9e0, -/* 0x0593: memx_exec_next */ - 0x98b2b2c1, - 0x10b60013, - 0xf034e704, - 0xe033e701, - 0x0132b601, - 0x980c30f0, - 0x55f9de35, - 0x1ef412a6, - 0xf10b98e5, - 0xbbf20c98, - 0xc44b02cb, - 0x00bbcf07, - 0xe0fcd0fc, - 0x00029f7e, -/* 0x05ca: memx_info */ - 0xc67000f8, - 0x0c0bf401, -/* 0x05d0: memx_info_data */ - 0x4b03cc4c, - 0x0ef40800, -/* 0x05d9: memx_info_train */ - 0x0bcc4c09, -/* 0x05df: memx_info_send */ - 0x7e01004b, 0xf800029f, -/* 0x05e5: memx_recv */ - 0x01d6b000, - 0xb0a30bf4, - 0x0bf400d6, -/* 0x05f3: memx_init */ - 0xf800f8dc, -/* 0x05f5: perf_recv */ -/* 0x05f7: perf_init */ - 0xf800f800, -/* 0x05f9: i2c_drive_scl */ - 0x0036b000, - 0x400d0bf4, - 0x01f607e0, - 0xf804bd00, -/* 0x0609: i2c_drive_scl_lo */ - 0x07e44000, - 0xbd0001f6, -/* 0x0613: i2c_drive_sda */ - 0xb000f804, - 0x0bf40036, - 0x07e0400d, - 0xbd0002f6, -/* 0x0623: i2c_drive_sda_lo */ - 0x4000f804, - 0x02f607e4, - 0xf804bd00, -/* 0x062d: i2c_sense_scl */ - 0x0132f400, - 0xcf07c443, - 0x31fd0033, - 0x060bf404, -/* 0x063f: i2c_sense_scl_done */ - 0xf80131f4, -/* 0x0641: i2c_sense_sda */ - 0x0132f400, - 0xcf07c443, - 0x32fd0033, - 0x060bf404, -/* 0x0653: i2c_sense_sda_done */ - 0xf80131f4, -/* 0x0655: i2c_raise_scl */ - 0x4440f900, - 0x01030898, - 0x0005f97e, -/* 0x0660: i2c_raise_scl_wait */ - 0x7e03e84e, - 0x7e000058, - 0xf400062d, - 0x42b60901, - 0xef1bf401, -/* 0x0674: i2c_raise_scl_done */ - 0x00f840fc, -/* 0x0678: i2c_start */ - 0x00062d7e, - 0x7e0d11f4, - 0xf4000641, - 0x0ef40611, -/* 0x0689: i2c_start_rep */ - 0x7e00032e, - 0x030005f9, - 0x06137e01, +/* 0x05c5: memx_info */ + 0x01c67000, +/* 0x05cb: memx_info_data */ + 0x4c0c0bf4, + 0x004b03cc, + 0x090ef408, +/* 0x05d4: memx_info_train */ + 0x4b0bcc4c, +/* 0x05da: memx_info_send */ + 0x9f7e0100, + 0x00f80002, +/* 0x05e0: memx_recv */ + 0xf401d6b0, + 0xd6b0a30b, + 0xdc0bf400, +/* 0x05ee: memx_init */ + 0x00f800f8, +/* 0x05f0: perf_recv */ +/* 0x05f2: perf_init */ + 0x00f800f8, +/* 0x05f4: i2c_drive_scl */ + 0xf40036b0, + 0xe0400d0b, + 0x0001f607, + 0x00f804bd, +/* 0x0604: i2c_drive_scl_lo */ + 0xf607e440, + 0x04bd0001, +/* 0x060e: i2c_drive_sda */ + 0x36b000f8, + 0x0d0bf400, + 0xf607e040, + 0x04bd0002, +/* 0x061e: i2c_drive_sda_lo */ + 0xe44000f8, + 0x0002f607, + 0x00f804bd, +/* 0x0628: i2c_sense_scl */ + 0x430132f4, + 0x33cf07c4, + 0x0431fd00, + 0xf4060bf4, +/* 0x063a: i2c_sense_scl_done */ + 0x00f80131, +/* 0x063c: i2c_sense_sda */ + 0x430132f4, + 0x33cf07c4, + 0x0432fd00, + 0xf4060bf4, +/* 0x064e: i2c_sense_sda_done */ + 0x00f80131, +/* 0x0650: i2c_raise_scl */ + 0x984440f9, + 0x7e010308, +/* 0x065b: i2c_raise_scl_wait */ + 0x4e0005f4, + 0x587e03e8, + 0x287e0000, + 0x01f40006, + 0x0142b609, +/* 0x066f: i2c_raise_scl_done */ + 0xfcef1bf4, +/* 0x0673: i2c_start */ + 0x7e00f840, + 0xf4000628, + 0x3c7e0d11, + 0x11f40006, + 0x2e0ef406, +/* 0x0684: i2c_start_rep */ + 0xf47e0003, + 0x01030005, + 0x00060e7e, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x06507e50, + 0x0464b600, +/* 0x06af: i2c_start_send */ + 0x031d11f4, + 0x060e7e00, + 0x13884e00, + 0x0000587e, + 0xf47e0003, + 0x884e0005, + 0x00587e13, +/* 0x06c9: i2c_start_out */ +/* 0x06cb: i2c_stop */ + 0x0300f800, + 0x05f47e00, + 0x7e000300, + 0x4e00060e, + 0x587e03e8, + 0x01030000, + 0x0005f47e, + 0x7e13884e, + 0x03000058, + 0x060e7e01, + 0x13884e00, + 0x0000587e, +/* 0x06fa: i2c_bitw */ + 0x0e7e00f8, + 0xe84e0006, + 0x00587e03, 0x0076bb00, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, - 0x557e50fc, + 0x507e50fc, 0x64b60006, - 0x1d11f404, -/* 0x06b4: i2c_start_send */ - 0x137e0003, - 0x884e0006, - 0x00587e13, - 0x7e000300, - 0x4e0005f9, - 0x587e1388, -/* 0x06ce: i2c_start_out */ - 0x00f80000, -/* 0x06d0: i2c_stop */ - 0xf97e0003, - 0x00030005, - 0x0006137e, - 0x7e03e84e, + 0x1711f404, + 0x7e13884e, 0x03000058, - 0x05f97e01, + 0x05f47e00, 0x13884e00, 0x0000587e, - 0x137e0103, - 0x884e0006, - 0x00587e13, -/* 0x06ff: i2c_bitw */ - 0x7e00f800, - 0x4e000613, - 0x587e03e8, - 0x76bb0000, +/* 0x0738: i2c_bitw_out */ +/* 0x073a: i2c_bitr */ + 0x010300f8, + 0x00060e7e, + 0x7e03e84e, + 0xbb000058, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x0006507e, + 0xf40464b6, + 0x3c7e1a11, + 0x00030006, + 0x0005f47e, + 0x7e13884e, + 0xf0000058, + 0x31f4013c, +/* 0x077d: i2c_bitr_done */ +/* 0x077f: i2c_get_byte */ + 0x0500f801, +/* 0x0783: i2c_get_byte_next */ + 0xb6080400, + 0x76bb0154, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0x7e50fc04, - 0xb6000655, + 0xb600073a, 0x11f40464, - 0x13884e17, - 0x0000587e, - 0xf97e0003, - 0x884e0005, - 0x00587e13, -/* 0x073d: i2c_bitw_out */ -/* 0x073f: i2c_bitr */ - 0x0300f800, - 0x06137e01, - 0x03e84e00, - 0x0000587e, + 0x0553fd2a, + 0xf40142b6, + 0x0103d81b, 0xb60076bb, 0x50f90465, 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x06557e50, + 0x06fa7e50, 0x0464b600, - 0x7e1a11f4, - 0x03000641, - 0x05f97e00, - 0x13884e00, - 0x0000587e, - 0xf4013cf0, -/* 0x0782: i2c_bitr_done */ - 0x00f80131, -/* 0x0784: i2c_get_byte */ - 0x08040005, -/* 0x0788: i2c_get_byte_next */ - 0xbb0154b6, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x00073f7e, - 0xf40464b6, - 0x53fd2a11, - 0x0142b605, - 0x03d81bf4, - 0x0076bb01, - 0xf90465b6, - 0x04659450, - 0xbd0256bb, - 0x0475fd50, - 0xff7e50fc, - 0x64b60006, -/* 0x07d1: i2c_get_byte_done */ -/* 0x07d3: i2c_put_byte */ - 0x0400f804, -/* 0x07d5: i2c_put_byte_next */ - 0x0142b608, - 0xbb3854ff, +/* 0x07cc: i2c_get_byte_done */ +/* 0x07ce: i2c_put_byte */ + 0x080400f8, +/* 0x07d0: i2c_put_byte_next */ + 0xff0142b6, + 0x76bb3854, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb60006fa, + 0x11f40464, + 0x0046b034, + 0xbbd81bf4, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0006ff7e, + 0x00073a7e, 0xf40464b6, - 0x46b03411, - 0xd81bf400, + 0x76bb0f11, + 0x0136b000, + 0xf4061bf4, +/* 0x0826: i2c_put_byte_done */ + 0x00f80132, +/* 0x0828: i2c_addr */ 0xb60076bb, 0x50f90465, 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x073f7e50, + 0x06737e50, 0x0464b600, - 0xbb0f11f4, - 0x36b00076, - 0x061bf401, -/* 0x082b: i2c_put_byte_done */ - 0xf80132f4, -/* 0x082d: i2c_addr */ - 0x0076bb00, + 0xe72911f4, + 0xb6012ec3, + 0x53fd0134, + 0x0076bb05, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, - 0x787e50fc, - 0x64b60006, - 0x2911f404, - 0x012ec3e7, - 0xfd0134b6, - 0x76bb0553, - 0x0465b600, - 0x659450f9, - 0x0256bb04, - 0x75fd50bd, - 0x7e50fc04, - 0xb60007d3, -/* 0x0872: i2c_addr_done */ - 0x00f80464, -/* 0x0874: i2c_acquire_addr */ - 0xb6f8cec7, - 0xe0b705e4, - 0x00f8d014, -/* 0x0880: i2c_acquire */ - 0x0008747e, + 0xce7e50fc, + 0x64b60007, +/* 0x086d: i2c_addr_done */ +/* 0x086f: i2c_acquire_addr */ + 0xc700f804, + 0xe4b6f8ce, + 0x14e0b705, +/* 0x087b: i2c_acquire */ + 0x7e00f8d0, + 0x7e00086f, + 0xf0000004, + 0x2d7e03d9, + 0x00f80000, +/* 0x088c: i2c_release */ + 0x00086f7e, 0x0000047e, - 0x7e03d9f0, + 0x7e03daf0, 0xf800002d, -/* 0x0891: i2c_release */ - 0x08747e00, - 0x00047e00, - 0x03daf000, - 0x00002d7e, -/* 0x08a2: i2c_recv */ - 0x32f400f8, - 0xf8c1c701, - 0xb00214b6, - 0x1ff52816, - 0x13b80134, - 0x98000cf4, - 0x13b80032, - 0x98000ccc, - 0x31f40031, - 0xf9d0f902, - 0xd6d0f9e0, - 0x10000000, - 0xbb016792, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x0008807e, - 0xfc0464b6, - 0x00d6b0d0, - 0x00b01bf5, - 0x76bb0005, +/* 0x089d: i2c_recv */ + 0x0132f400, + 0xb6f8c1c7, + 0x16b00214, + 0x341ff528, + 0xf413b801, + 0x3298000c, + 0xcc13b800, + 0x3198000c, + 0x0231f400, + 0xe0f9d0f9, + 0x00d6d0f9, + 0x92100000, + 0x76bb0167, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0x7e50fc04, - 0xb600082d, - 0x11f50464, - 0xc5c700cc, - 0x0076bbe0, - 0xf90465b6, - 0x04659450, - 0xbd0256bb, - 0x0475fd50, - 0xd37e50fc, - 0x64b60007, - 0xa911f504, - 0xbb010500, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x00082d7e, - 0xf50464b6, - 0xbb008711, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x0007847e, - 0xf40464b6, - 0x5bcb6711, - 0x0076bbe0, + 0xb600087b, + 0xd0fc0464, + 0xf500d6b0, + 0x0500b01b, + 0x0076bb00, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, - 0xd07e50fc, - 0x64b60006, - 0xbd5bb204, - 0x410ef474, -/* 0x09a4: i2c_recv_not_rd08 */ - 0xf401d6b0, - 0x00053b1b, - 0x00082d7e, - 0xc73211f4, - 0xd37ee0c5, - 0x11f40007, - 0x7e000528, - 0xf400082d, - 0xb5c71f11, - 0x07d37ee0, - 0x1511f400, - 0x0006d07e, - 0xc5c774bd, - 0x091bf408, - 0xf40232f4, -/* 0x09e2: i2c_recv_not_wr08 */ -/* 0x09e2: i2c_recv_done */ - 0xcec7030e, - 0x08917ef8, - 0xfce0fc00, - 0x0912f4d0, - 0x9f7e7cb2, -/* 0x09f6: i2c_recv_exit */ - 0x00f80002, -/* 0x09f8: i2c_init */ -/* 0x09fa: test_recv */ - 0x584100f8, - 0x0011cf04, - 0x400110b6, - 0x01f60458, - 0xde04bd00, - 0x134fd900, - 0x0001de7e, -/* 0x0a16: test_init */ - 0x004e00f8, - 0x01de7e08, -/* 0x0a1f: idle_recv */ + 0x287e50fc, + 0x64b60008, + 0xcc11f504, + 0xe0c5c700, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x07ce7e50, + 0x0464b600, + 0x00a911f5, + 0x76bb0105, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb6000828, + 0x11f50464, + 0x76bb0087, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0x7e50fc04, + 0xb600077f, + 0x11f40464, + 0xe05bcb67, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x06cb7e50, + 0x0464b600, + 0x74bd5bb2, +/* 0x099f: i2c_recv_not_rd08 */ + 0xb0410ef4, + 0x1bf401d6, + 0x7e00053b, + 0xf4000828, + 0xc5c73211, + 0x07ce7ee0, + 0x2811f400, + 0x287e0005, + 0x11f40008, + 0xe0b5c71f, + 0x0007ce7e, + 0x7e1511f4, + 0xbd0006cb, + 0x08c5c774, + 0xf4091bf4, + 0x0ef40232, +/* 0x09dd: i2c_recv_not_wr08 */ +/* 0x09dd: i2c_recv_done */ + 0xf8cec703, + 0x00088c7e, + 0xd0fce0fc, + 0xb20912f4, + 0x029f7e7c, +/* 0x09f1: i2c_recv_exit */ +/* 0x09f3: i2c_init */ 0xf800f800, -/* 0x0a21: idle */ - 0x0031f400, - 0xcf045441, - 0x10b60011, - 0x04544001, - 0xbd0001f6, -/* 0x0a35: idle_loop */ - 0xf4580104, -/* 0x0a3a: idle_proc */ -/* 0x0a3a: idle_proc_exec */ - 0x10f90232, - 0xa87e1eb2, - 0x10fc0002, - 0xf40911f4, - 0x0ef40231, -/* 0x0a4d: idle_proc_next */ - 0x5810b6f0, - 0x1bf41fa6, - 0xe002f4e8, - 0xf40028f4, - 0x0000c60e, +/* 0x09f5: test_recv */ + 0x04584100, + 0xb60011cf, + 0x58400110, + 0x0001f604, + 0x00de04bd, + 0x7e134fd9, + 0xf80001de, +/* 0x0a11: test_init */ + 0x08004e00, + 0x0001de7e, +/* 0x0a1a: idle_recv */ + 0x00f800f8, +/* 0x0a1c: idle */ + 0x410031f4, + 0x11cf0454, + 0x0110b600, + 0xf6045440, + 0x04bd0001, +/* 0x0a30: idle_loop */ + 0x32f45801, +/* 0x0a35: idle_proc */ +/* 0x0a35: idle_proc_exec */ + 0xb210f902, + 0x02a87e1e, + 0xf410fc00, + 0x31f40911, + 0xf00ef402, +/* 0x0a48: idle_proc_next */ + 0xa65810b6, + 0xe81bf41f, + 0xf4e002f4, + 0x0ef40028, + 0x000000c6, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h index 6a2572e8945ad7767b5756f6ea28a216a2c3aa56..defddf5957eee02b55b27b6119523998bf24ae51 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/gt215.fuc3.h @@ -47,8 +47,8 @@ static uint32_t gt215_pmu_data[] = { 0x00000000, 0x00000000, 0x584d454d, - 0x0000083a, - 0x0000082c, + 0x00000833, + 0x00000825, 0x00000000, 0x00000000, 0x00000000, @@ -69,8 +69,8 @@ static uint32_t gt215_pmu_data[] = { 0x00000000, 0x00000000, 0x46524550, - 0x0000083e, - 0x0000083c, + 0x00000837, + 0x00000835, 0x00000000, 0x00000000, 0x00000000, @@ -91,8 +91,8 @@ static uint32_t gt215_pmu_data[] = { 0x00000000, 0x00000000, 0x5f433249, - 0x00000c6e, - 0x00000b11, + 0x00000c67, + 0x00000b0a, 0x00000000, 0x00000000, 0x00000000, @@ -113,8 +113,8 @@ static uint32_t gt215_pmu_data[] = { 0x00000000, 0x00000000, 0x54534554, - 0x00000c97, - 0x00000c70, + 0x00000c90, + 0x00000c69, 0x00000000, 0x00000000, 0x00000000, @@ -135,8 +135,8 @@ static uint32_t gt215_pmu_data[] = { 0x00000000, 0x00000000, 0x454c4449, - 0x00000ca3, - 0x00000ca1, + 0x00000c9c, + 0x00000c9a, 0x00000000, 0x00000000, 0x00000000, @@ -234,22 +234,22 @@ static uint32_t gt215_pmu_data[] = { /* 0x037c: memx_func_next */ 0x00000002, 0x00000000, - 0x000005a0, + 0x0000059f, 0x00000003, 0x00000002, - 0x00000632, + 0x0000062f, 0x00040004, 0x00000000, - 0x0000064e, + 0x0000064b, 0x00010005, 0x00000000, - 0x0000066b, + 0x00000668, 0x00010006, 0x00000000, - 0x000005f0, + 0x000005ef, 0x00000007, 0x00000000, - 0x00000676, + 0x00000673, /* 0x03c4: memx_func_tail */ /* 0x03c4: memx_ts_start */ 0x00000000, @@ -1305,560 +1305,560 @@ static uint32_t gt215_pmu_code[] = { 0x67f102d7, 0x63f1fffc, 0x76fdffff, - 0x0267f104, - 0x0576fd00, - 0x70f980f9, - 0xe0fcd0fc, - 0xf04021f4, + 0x0267f004, + 0xf90576fd, + 0xfc70f980, + 0xf4e0fcd0, + 0x67f04021, + 0xe007f104, + 0x0604b607, + 0xbd0006d0, +/* 0x0581: memx_func_enter_wait */ + 0xc067f104, + 0x0664b607, + 0xf00066cf, + 0x0bf40464, + 0x2c67f0f3, + 0xcf0664b6, + 0x06800066, +/* 0x059f: memx_func_leave */ + 0xf000f8f1, + 0x64b62c67, + 0x0066cf06, + 0xf0f20680, 0x07f10467, - 0x04b607e0, + 0x04b607e4, 0x0006d006, -/* 0x0582: memx_func_enter_wait */ +/* 0x05ba: memx_func_leave_wait */ 0x67f104bd, 0x64b607c0, 0x0066cf06, 0xf40464f0, - 0x67f0f30b, - 0x0664b62c, - 0x800066cf, - 0x00f8f106, -/* 0x05a0: memx_func_leave */ - 0xb62c67f0, - 0x66cf0664, - 0xf2068000, - 0xf10467f0, - 0xb607e407, - 0x06d00604, -/* 0x05bb: memx_func_leave_wait */ - 0xf104bd00, - 0xb607c067, - 0x66cf0664, - 0x0464f000, - 0xf1f31bf4, - 0xb9161087, - 0x21f4028e, - 0x02d7b904, - 0xffcc67f1, - 0xffff63f1, - 0xf90476fd, - 0xfc70f980, - 0xf4e0fcd0, - 0x00f84021, -/* 0x05f0: memx_func_wait_vblank */ - 0xb0001698, - 0x0bf40066, - 0x0166b013, - 0xf4060bf4, -/* 0x0602: memx_func_wait_vblank_head1 */ - 0x77f12e0e, - 0x0ef40020, -/* 0x0609: memx_func_wait_vblank_head0 */ - 0x0877f107, -/* 0x060d: memx_func_wait_vblank_0 */ - 0xc467f100, - 0x0664b607, - 0xfd0066cf, - 0x1bf40467, -/* 0x061d: memx_func_wait_vblank_1 */ - 0xc467f1f3, - 0x0664b607, - 0xfd0066cf, - 0x0bf40467, -/* 0x062d: memx_func_wait_vblank_fini */ - 0x0410b6f3, -/* 0x0632: memx_func_wr32 */ - 0x169800f8, - 0x01159800, - 0xf90810b6, - 0xfc50f960, - 0xf4e0fcd0, - 0x42b64021, - 0xe91bf402, -/* 0x064e: memx_func_wait */ - 0x87f000f8, - 0x0684b62c, - 0x980088cf, - 0x1d98001e, - 0x021c9801, - 0xb6031b98, - 0x21f41010, -/* 0x066b: memx_func_delay */ - 0x9800f8a3, - 0x10b6001e, - 0x7e21f404, -/* 0x0676: memx_func_train */ - 0x57f100f8, - 0x77f10003, - 0x97f10000, - 0x93f00000, - 0x029eb970, - 0xb90421f4, - 0xe7f102d8, - 0x21f42710, -/* 0x0695: memx_func_train_loop_outer */ - 0x0158e07e, - 0x0083f101, - 0xe097f102, - 0x1193f011, - 0x80f990f9, + 0x87f1f31b, + 0x8eb91610, + 0x0421f402, + 0xf102d7b9, + 0xf1ffcc67, + 0xfdffff63, + 0x80f90476, + 0xd0fc70f9, + 0x21f4e0fc, +/* 0x05ef: memx_func_wait_vblank */ + 0x9800f840, + 0x66b00016, + 0x120bf400, + 0xf40166b0, + 0x0ef4060b, +/* 0x0601: memx_func_wait_vblank_head1 */ + 0x2077f02c, +/* 0x0607: memx_func_wait_vblank_head0 */ + 0xf0060ef4, +/* 0x060a: memx_func_wait_vblank_0 */ + 0x67f10877, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x061a: memx_func_wait_vblank_1 */ + 0x67f1f31b, + 0x64b607c4, + 0x0066cf06, + 0xf40467fd, +/* 0x062a: memx_func_wait_vblank_fini */ + 0x10b6f30b, +/* 0x062f: memx_func_wr32 */ + 0x9800f804, + 0x15980016, + 0x0810b601, + 0x50f960f9, 0xe0fcd0fc, - 0xf94021f4, - 0x0067f150, -/* 0x06b5: memx_func_train_loop_inner */ - 0x1187f100, - 0x9068ff11, - 0xfd109894, - 0x97f10589, - 0x93f00720, - 0xf990f910, - 0xfcd0fc80, - 0x4021f4e0, - 0x008097f1, - 0xb91093f0, - 0x21f4029e, - 0x02d8b904, - 0xf92088c5, + 0xb64021f4, + 0x1bf40242, +/* 0x064b: memx_func_wait */ + 0xf000f8e9, + 0x84b62c87, + 0x0088cf06, + 0x98001e98, + 0x1c98011d, + 0x031b9802, + 0xf41010b6, + 0x00f8a321, +/* 0x0668: memx_func_delay */ + 0xb6001e98, + 0x21f40410, +/* 0x0673: memx_func_train */ + 0xf000f87e, + 0x77f00357, + 0x0097f100, + 0x7093f000, + 0xf4029eb9, + 0xd8b90421, + 0x10e7f102, + 0x7e21f427, +/* 0x0690: memx_func_train_loop_outer */ + 0x010158e0, + 0x020083f1, + 0x11e097f1, + 0xf91193f0, + 0xfc80f990, + 0xf4e0fcd0, + 0x50f94021, +/* 0x06af: memx_func_train_loop_inner */ + 0xf10067f0, + 0xff111187, + 0x98949068, + 0x0589fd10, + 0x072097f1, + 0xf91093f0, 0xfc80f990, 0xf4e0fcd0, 0x97f14021, - 0x93f0053c, - 0x0287f110, - 0x0083f130, - 0xf990f980, + 0x93f00080, + 0x029eb910, + 0xb90421f4, + 0x88c502d8, + 0xf990f920, 0xfcd0fc80, 0x4021f4e0, - 0x0560e7f1, - 0xf110e3f0, - 0xf10000d7, - 0x908000d3, - 0xb7f100dc, - 0xb3f08480, - 0xa321f41e, - 0x000057f1, - 0xffff97f1, - 0x830093f1, -/* 0x0734: memx_func_train_loop_4x */ - 0x0080a7f1, - 0xb910a3f0, - 0x21f402ae, - 0x02d8b904, - 0xffdfb7f1, - 0xffffb3f1, - 0xf9048bfd, - 0xfc80f9a0, + 0x053c97f1, + 0xf11093f0, + 0xf1300287, + 0xf9800083, + 0xfc80f990, 0xf4e0fcd0, - 0xa7f14021, - 0xa3f0053c, - 0x0287f110, - 0x0083f130, - 0xf9a0f980, - 0xfcd0fc80, - 0x4021f4e0, - 0x0560e7f1, - 0xf110e3f0, - 0xf10000d7, - 0xb98000d3, - 0xb7f102dc, - 0xb3f02710, - 0xa321f400, - 0xf402eeb9, - 0xddb90421, - 0x949dff02, + 0xe7f14021, + 0xe3f00560, + 0x00d7f110, + 0x00d3f100, + 0x00dc9080, + 0x8480b7f1, + 0xf41eb3f0, + 0x57f0a321, + 0xff97f100, + 0x0093f1ff, +/* 0x072d: memx_func_train_loop_4x */ + 0x80a7f183, + 0x10a3f000, + 0xf402aeb9, + 0xd8b90421, + 0xdfb7f102, + 0xffb3f1ff, + 0x048bfdff, + 0x80f9a0f9, + 0xe0fcd0fc, + 0xf14021f4, + 0xf0053ca7, + 0x87f110a3, + 0x83f13002, + 0xa0f98000, + 0xd0fc80f9, + 0x21f4e0fc, + 0x60e7f140, + 0x10e3f005, + 0x0000d7f1, + 0x8000d3f1, + 0xf102dcb9, + 0xf02710b7, + 0x21f400b3, + 0x02eeb9a3, + 0xb90421f4, + 0x9dff02dd, + 0x0150b694, + 0xf4045670, + 0x7aa0921e, + 0xa9800bcc, + 0x0160b600, + 0x700470b6, + 0x1ef51066, + 0x50fcff01, 0x700150b6, - 0x1ef40456, - 0xcc7aa092, - 0x00a9800b, - 0xb60160b6, - 0x66700470, - 0x001ef510, - 0xb650fcff, - 0x56700150, - 0xd41ef507, -/* 0x07c7: memx_exec */ - 0xf900f8fe, - 0xb9d0f9e0, - 0xb2b902c1, -/* 0x07d1: memx_exec_next */ - 0x00139802, - 0xe70410b6, - 0xe701f034, - 0xb601e033, - 0x30f00132, - 0xde35980c, - 0x12b855f9, - 0xe41ef406, - 0x98f10b98, - 0xcbbbf20c, - 0xc4b7f102, - 0x06b4b607, - 0xfc00bbcf, - 0xf5e0fcd0, + 0x1ef50756, + 0x00f8fed6, +/* 0x07c0: memx_exec */ + 0xd0f9e0f9, + 0xb902c1b9, +/* 0x07ca: memx_exec_next */ + 0x139802b2, + 0x0410b600, + 0x01f034e7, + 0x01e033e7, + 0xf00132b6, + 0x35980c30, + 0xb855f9de, + 0x1ef40612, + 0xf10b98e4, + 0xbbf20c98, + 0xb7f102cb, + 0xb4b607c4, + 0x00bbcf06, + 0xe0fcd0fc, + 0x033621f5, +/* 0x0806: memx_info */ + 0xc67000f8, + 0x0e0bf401, +/* 0x080c: memx_info_data */ + 0x03ccc7f1, + 0x0800b7f1, +/* 0x0817: memx_info_train */ + 0xf10b0ef4, + 0xf10bccc7, +/* 0x081f: memx_info_send */ + 0xf50100b7, 0xf8033621, -/* 0x080d: memx_info */ - 0x01c67000, -/* 0x0813: memx_info_data */ - 0xf10e0bf4, - 0xf103ccc7, - 0xf40800b7, -/* 0x081e: memx_info_train */ - 0xc7f10b0e, - 0xb7f10bcc, -/* 0x0826: memx_info_send */ - 0x21f50100, - 0x00f80336, -/* 0x082c: memx_recv */ - 0xf401d6b0, - 0xd6b0980b, - 0xd80bf400, -/* 0x083a: memx_init */ - 0x00f800f8, -/* 0x083c: perf_recv */ -/* 0x083e: perf_init */ - 0x00f800f8, -/* 0x0840: i2c_drive_scl */ - 0xf40036b0, - 0x07f1110b, - 0x04b607e0, - 0x0001d006, - 0x00f804bd, -/* 0x0854: i2c_drive_scl_lo */ - 0x07e407f1, - 0xd00604b6, - 0x04bd0001, -/* 0x0862: i2c_drive_sda */ - 0x36b000f8, - 0x110bf400, - 0x07e007f1, - 0xd00604b6, - 0x04bd0002, -/* 0x0876: i2c_drive_sda_lo */ - 0x07f100f8, - 0x04b607e4, - 0x0002d006, - 0x00f804bd, -/* 0x0884: i2c_sense_scl */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0431fd00, - 0xf4060bf4, -/* 0x089a: i2c_sense_scl_done */ - 0x00f80131, -/* 0x089c: i2c_sense_sda */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0432fd00, - 0xf4060bf4, -/* 0x08b2: i2c_sense_sda_done */ - 0x00f80131, -/* 0x08b4: i2c_raise_scl */ - 0x47f140f9, - 0x37f00898, - 0x4021f501, -/* 0x08c1: i2c_raise_scl_wait */ +/* 0x0825: memx_recv */ + 0x01d6b000, + 0xb0980bf4, + 0x0bf400d6, +/* 0x0833: memx_init */ + 0xf800f8d8, +/* 0x0835: perf_recv */ +/* 0x0837: perf_init */ + 0xf800f800, +/* 0x0839: i2c_drive_scl */ + 0x0036b000, + 0xf1110bf4, + 0xb607e007, + 0x01d00604, + 0xf804bd00, +/* 0x084d: i2c_drive_scl_lo */ + 0xe407f100, + 0x0604b607, + 0xbd0001d0, +/* 0x085b: i2c_drive_sda */ + 0xb000f804, + 0x0bf40036, + 0xe007f111, + 0x0604b607, + 0xbd0002d0, +/* 0x086f: i2c_drive_sda_lo */ + 0xf100f804, + 0xb607e407, + 0x02d00604, + 0xf804bd00, +/* 0x087d: i2c_sense_scl */ + 0x0132f400, + 0x07c437f1, + 0xcf0634b6, + 0x31fd0033, + 0x060bf404, +/* 0x0893: i2c_sense_scl_done */ + 0xf80131f4, +/* 0x0895: i2c_sense_sda */ + 0x0132f400, + 0x07c437f1, + 0xcf0634b6, + 0x32fd0033, + 0x060bf404, +/* 0x08ab: i2c_sense_sda_done */ + 0xf80131f4, +/* 0x08ad: i2c_raise_scl */ + 0xf140f900, + 0xf0089847, + 0x21f50137, +/* 0x08ba: i2c_raise_scl_wait */ + 0xe7f10839, + 0x21f403e8, + 0x7d21f57e, + 0x0901f408, + 0xf40142b6, +/* 0x08ce: i2c_raise_scl_done */ + 0x40fcef1b, +/* 0x08d2: i2c_start */ + 0x21f500f8, + 0x11f4087d, + 0x9521f50d, + 0x0611f408, +/* 0x08e3: i2c_start_rep */ + 0xf0300ef4, + 0x21f50037, + 0x37f00839, + 0x5b21f501, + 0x0076bb08, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b608ad, + 0x1f11f404, +/* 0x0910: i2c_start_send */ + 0xf50037f0, + 0xf1085b21, + 0xf41388e7, + 0x37f07e21, + 0x3921f500, + 0x88e7f108, + 0x7e21f413, +/* 0x092c: i2c_start_out */ +/* 0x092e: i2c_stop */ + 0x37f000f8, + 0x3921f500, + 0x0037f008, + 0x085b21f5, + 0x03e8e7f1, + 0xf07e21f4, + 0x21f50137, + 0xe7f10839, + 0x21f41388, + 0x0137f07e, + 0x085b21f5, + 0x1388e7f1, + 0xf87e21f4, +/* 0x0961: i2c_bitw */ + 0x5b21f500, 0xe8e7f108, 0x7e21f403, - 0x088421f5, - 0xb60901f4, - 0x1bf40142, -/* 0x08d5: i2c_raise_scl_done */ - 0xf840fcef, -/* 0x08d9: i2c_start */ - 0x8421f500, - 0x0d11f408, - 0x089c21f5, - 0xf40611f4, -/* 0x08ea: i2c_start_rep */ - 0x37f0300e, - 0x4021f500, - 0x0137f008, - 0x086221f5, 0xb60076bb, 0x50f90465, 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0xb421f550, + 0xad21f550, 0x0464b608, -/* 0x0917: i2c_start_send */ - 0xf01f11f4, - 0x21f50037, - 0xe7f10862, - 0x21f41388, - 0x0037f07e, - 0x084021f5, - 0x1388e7f1, -/* 0x0933: i2c_start_out */ - 0xf87e21f4, -/* 0x0935: i2c_stop */ - 0x0037f000, - 0x084021f5, - 0xf50037f0, - 0xf1086221, - 0xf403e8e7, + 0xf11811f4, + 0xf41388e7, 0x37f07e21, - 0x4021f501, + 0x3921f500, 0x88e7f108, 0x7e21f413, - 0xf50137f0, - 0xf1086221, - 0xf41388e7, - 0x00f87e21, -/* 0x0968: i2c_bitw */ - 0x086221f5, - 0x03e8e7f1, - 0xbb7e21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x08b421f5, - 0xf40464b6, - 0xe7f11811, +/* 0x09a0: i2c_bitw_out */ +/* 0x09a2: i2c_bitr */ + 0x37f000f8, + 0x5b21f501, + 0xe8e7f108, + 0x7e21f403, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xad21f550, + 0x0464b608, + 0xf51b11f4, + 0xf0089521, + 0x21f50037, + 0xe7f10839, 0x21f41388, - 0x0037f07e, - 0x084021f5, - 0x1388e7f1, -/* 0x09a7: i2c_bitw_out */ - 0xf87e21f4, -/* 0x09a9: i2c_bitr */ - 0x0137f000, - 0x086221f5, - 0x03e8e7f1, - 0xbb7e21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x08b421f5, - 0xf40464b6, - 0x21f51b11, - 0x37f0089c, - 0x4021f500, - 0x88e7f108, - 0x7e21f413, - 0xf4013cf0, -/* 0x09ee: i2c_bitr_done */ - 0x00f80131, -/* 0x09f0: i2c_get_byte */ - 0xf00057f0, -/* 0x09f6: i2c_get_byte_next */ - 0x54b60847, + 0x013cf07e, +/* 0x09e7: i2c_bitr_done */ + 0xf80131f4, +/* 0x09e9: i2c_get_byte */ + 0x0057f000, +/* 0x09ef: i2c_get_byte_next */ + 0xb60847f0, + 0x76bb0154, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb609a221, + 0x11f40464, + 0x0553fd2b, + 0xf40142b6, + 0x37f0d81b, 0x0076bb01, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b609a9, - 0x2b11f404, - 0xb60553fd, - 0x1bf40142, - 0x0137f0d8, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0x6821f550, - 0x0464b609, -/* 0x0a40: i2c_get_byte_done */ -/* 0x0a42: i2c_put_byte */ - 0x47f000f8, -/* 0x0a45: i2c_put_byte_next */ - 0x0142b608, - 0xbb3854ff, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x096821f5, - 0xf40464b6, - 0x46b03411, - 0xd81bf400, + 0x64b60961, +/* 0x0a39: i2c_get_byte_done */ +/* 0x0a3b: i2c_put_byte */ + 0xf000f804, +/* 0x0a3e: i2c_put_byte_next */ + 0x42b60847, + 0x3854ff01, 0xb60076bb, 0x50f90465, 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0xa921f550, + 0x6121f550, 0x0464b609, - 0xbb0f11f4, - 0x36b00076, - 0x061bf401, -/* 0x0a9b: i2c_put_byte_done */ - 0xf80132f4, -/* 0x0a9d: i2c_addr */ - 0x0076bb00, + 0xb03411f4, + 0x1bf40046, + 0x0076bbd8, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b608d9, - 0x2911f404, - 0x012ec3e7, - 0xfd0134b6, - 0x76bb0553, + 0x64b609a2, + 0x0f11f404, + 0xb00076bb, + 0x1bf40136, + 0x0132f406, +/* 0x0a94: i2c_put_byte_done */ +/* 0x0a96: i2c_addr */ + 0x76bb00f8, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb60a4221, -/* 0x0ae2: i2c_addr_done */ - 0x00f80464, -/* 0x0ae4: i2c_acquire_addr */ - 0xb6f8cec7, - 0xe0b702e4, - 0xee980d1c, -/* 0x0af3: i2c_acquire */ - 0xf500f800, - 0xf40ae421, - 0xd9f00421, - 0x4021f403, -/* 0x0b02: i2c_release */ - 0x21f500f8, - 0x21f40ae4, - 0x03daf004, - 0xf84021f4, -/* 0x0b11: i2c_recv */ - 0x0132f400, - 0xb6f8c1c7, - 0x16b00214, - 0x3a1ff528, - 0xf413a001, - 0x0032980c, - 0x0ccc13a0, - 0xf4003198, - 0xd0f90231, - 0xd0f9e0f9, - 0x000067f1, - 0x100063f1, - 0xbb016792, + 0xb608d221, + 0x11f40464, + 0x2ec3e729, + 0x0134b601, + 0xbb0553fd, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0af321f5, - 0xfc0464b6, - 0x00d6b0d0, - 0x00b31bf5, - 0xbb0057f0, + 0x0a3b21f5, +/* 0x0adb: i2c_addr_done */ + 0xf80464b6, +/* 0x0add: i2c_acquire_addr */ + 0xf8cec700, + 0xb702e4b6, + 0x980d1ce0, + 0x00f800ee, +/* 0x0aec: i2c_acquire */ + 0x0add21f5, + 0xf00421f4, + 0x21f403d9, +/* 0x0afb: i2c_release */ + 0xf500f840, + 0xf40add21, + 0xdaf00421, + 0x4021f403, +/* 0x0b0a: i2c_recv */ + 0x32f400f8, + 0xf8c1c701, + 0xb00214b6, + 0x1ff52816, + 0x13a0013a, + 0x32980cf4, + 0xcc13a000, + 0x0031980c, + 0xf90231f4, + 0xf9e0f9d0, + 0x0067f1d0, + 0x0063f100, + 0x01679210, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xec21f550, + 0x0464b60a, + 0xd6b0d0fc, + 0xb31bf500, + 0x0057f000, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x9621f550, + 0x0464b60a, + 0x00d011f5, + 0xbbe0c5c7, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0a9d21f5, + 0x0a3b21f5, 0xf50464b6, - 0xc700d011, - 0x76bbe0c5, + 0xf000ad11, + 0x76bb0157, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb60a4221, + 0xb60a9621, 0x11f50464, - 0x57f000ad, - 0x0076bb01, - 0xf90465b6, - 0x04659450, - 0xbd0256bb, - 0x0475fd50, - 0x21f550fc, - 0x64b60a9d, - 0x8a11f504, - 0x0076bb00, - 0xf90465b6, - 0x04659450, - 0xbd0256bb, - 0x0475fd50, - 0x21f550fc, - 0x64b609f0, - 0x6a11f404, - 0xbbe05bcb, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x093521f5, - 0xb90464b6, - 0x74bd025b, -/* 0x0c17: i2c_recv_not_rd08 */ - 0xb0430ef4, - 0x1bf401d6, - 0x0057f03d, - 0x0a9d21f5, - 0xc73311f4, - 0x21f5e0c5, - 0x11f40a42, - 0x0057f029, - 0x0a9d21f5, - 0xc71f11f4, - 0x21f5e0b5, - 0x11f40a42, - 0x3521f515, - 0xc774bd09, - 0x1bf408c5, - 0x0232f409, -/* 0x0c57: i2c_recv_not_wr08 */ -/* 0x0c57: i2c_recv_done */ - 0xc7030ef4, - 0x21f5f8ce, - 0xe0fc0b02, - 0x12f4d0fc, - 0x027cb90a, - 0x033621f5, -/* 0x0c6c: i2c_recv_exit */ -/* 0x0c6e: i2c_init */ + 0x76bb008a, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb609e921, + 0x11f40464, + 0xe05bcb6a, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x2e21f550, + 0x0464b609, + 0xbd025bb9, + 0x430ef474, +/* 0x0c10: i2c_recv_not_rd08 */ + 0xf401d6b0, + 0x57f03d1b, + 0x9621f500, + 0x3311f40a, + 0xf5e0c5c7, + 0xf40a3b21, + 0x57f02911, + 0x9621f500, + 0x1f11f40a, + 0xf5e0b5c7, + 0xf40a3b21, + 0x21f51511, + 0x74bd092e, + 0xf408c5c7, + 0x32f4091b, + 0x030ef402, +/* 0x0c50: i2c_recv_not_wr08 */ +/* 0x0c50: i2c_recv_done */ + 0xf5f8cec7, + 0xfc0afb21, + 0xf4d0fce0, + 0x7cb90a12, + 0x3621f502, +/* 0x0c65: i2c_recv_exit */ +/* 0x0c67: i2c_init */ + 0xf800f803, +/* 0x0c69: test_recv */ + 0xd817f100, + 0x0614b605, + 0xb60011cf, + 0x07f10110, + 0x04b605d8, + 0x0001d006, + 0xe7f104bd, + 0xe3f1d900, + 0x21f5134f, + 0x00f80256, +/* 0x0c90: test_init */ + 0x0800e7f1, + 0x025621f5, +/* 0x0c9a: idle_recv */ 0x00f800f8, -/* 0x0c70: test_recv */ - 0x05d817f1, - 0xcf0614b6, - 0x10b60011, - 0xd807f101, - 0x0604b605, - 0xbd0001d0, - 0x00e7f104, - 0x4fe3f1d9, - 0x5621f513, -/* 0x0c97: test_init */ - 0xf100f802, - 0xf50800e7, - 0xf8025621, -/* 0x0ca1: idle_recv */ -/* 0x0ca3: idle */ - 0xf400f800, - 0x17f10031, - 0x14b605d4, - 0x0011cf06, - 0xf10110b6, - 0xb605d407, - 0x01d00604, -/* 0x0cbf: idle_loop */ - 0xf004bd00, - 0x32f45817, -/* 0x0cc5: idle_proc */ -/* 0x0cc5: idle_proc_exec */ - 0xb910f902, - 0x21f5021e, - 0x10fc033f, - 0xf40911f4, - 0x0ef40231, -/* 0x0cd9: idle_proc_next */ - 0x5810b6ef, - 0xf4061fb8, - 0x02f4e61b, - 0x0028f4dd, - 0x00bb0ef4, +/* 0x0c9c: idle */ + 0xf10031f4, + 0xb605d417, + 0x11cf0614, + 0x0110b600, + 0x05d407f1, + 0xd00604b6, + 0x04bd0001, +/* 0x0cb8: idle_loop */ + 0xf45817f0, +/* 0x0cbe: idle_proc */ +/* 0x0cbe: idle_proc_exec */ + 0x10f90232, + 0xf5021eb9, + 0xfc033f21, + 0x0911f410, + 0xf40231f4, +/* 0x0cd2: idle_proc_next */ + 0x10b6ef0e, + 0x061fb858, + 0xf4e61bf4, + 0x28f4dd02, + 0xbb0ef400, + 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc index ec03f9a4290b4d72bb796655061f9fe12b011d68..1663bf943d771a5ca58c9d8f6e2c2fbedf5a3a13 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/fuc/memx.fuc @@ -82,15 +82,15 @@ memx_train_tail: // $r0 - zero memx_func_enter: #if NVKM_PPWR_CHIPSET == GT215 - movw $r8 0x1610 + mov $r8 0x1610 nv_rd32($r7, $r8) imm32($r6, 0xfffffffc) and $r7 $r6 - movw $r6 0x2 + mov $r6 0x2 or $r7 $r6 nv_wr32($r8, $r7) #else - movw $r6 0x001620 + mov $r6 0x001620 imm32($r7, ~0x00000aa2); nv_rd32($r8, $r6) and $r8 $r7 @@ -101,7 +101,7 @@ memx_func_enter: and $r8 $r7 nv_wr32($r6, $r8) - movw $r6 0x0026f0 + mov $r6 0x0026f0 nv_rd32($r8, $r6) and $r8 $r7 nv_wr32($r6, $r8) @@ -136,19 +136,19 @@ memx_func_leave: bra nz #memx_func_leave_wait #if NVKM_PPWR_CHIPSET == GT215 - movw $r8 0x1610 + mov $r8 0x1610 nv_rd32($r7, $r8) imm32($r6, 0xffffffcc) and $r7 $r6 nv_wr32($r8, $r7) #else - movw $r6 0x0026f0 + mov $r6 0x0026f0 imm32($r7, 0x00000001) nv_rd32($r8, $r6) or $r8 $r7 nv_wr32($r6, $r8) - movw $r6 0x001620 + mov $r6 0x001620 nv_rd32($r8, $r6) or $r8 $r7 nv_wr32($r6, $r8) @@ -177,11 +177,11 @@ memx_func_wait_vblank: bra #memx_func_wait_vblank_fini memx_func_wait_vblank_head1: - movw $r7 0x20 + mov $r7 0x20 bra #memx_func_wait_vblank_0 memx_func_wait_vblank_head0: - movw $r7 0x8 + mov $r7 0x8 memx_func_wait_vblank_0: nv_iord($r6, NV_PPWR_INPUT) @@ -273,13 +273,13 @@ memx_func_train: // $r5 - outer loop counter // $r6 - inner loop counter // $r7 - entry counter (#memx_train_head + $r7) - movw $r5 0x3 - movw $r7 0x0 + mov $r5 0x3 + mov $r7 0x0 // Read random memory to wake up... things imm32($r9, 0x700000) nv_rd32($r8,$r9) - movw $r14 0x2710 + mov $r14 0x2710 call(nsec) memx_func_train_loop_outer: @@ -289,9 +289,9 @@ memx_func_train: nv_wr32($r9, $r8) push $r5 - movw $r6 0x0 + mov $r6 0x0 memx_func_train_loop_inner: - movw $r8 0x1111 + mov $r8 0x1111 mulu $r9 $r6 $r8 shl b32 $r8 $r9 0x10 or $r8 $r9 @@ -315,7 +315,7 @@ memx_func_train: // $r5 - inner inner loop counter // $r9 - result - movw $r5 0 + mov $r5 0 imm32($r9, 0x8300ffff) memx_func_train_loop_4x: imm32($r10, 0x100080) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 32b577c776b981b2982837e8ff9f6cdf1d91c77f..58488eac846275f00b2ba82cd01e5571df49f79d 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -139,6 +139,10 @@ static struct radeon_px_quirk radeon_px_quirk_list[] = { * https://bugs.freedesktop.org/show_bug.cgi?id=101491 */ { PCI_VENDOR_ID_ATI, 0x6741, 0x1043, 0x2122, RADEON_PX_QUIRK_DISABLE_PX }, + /* Asus K73TK laptop with AMD A6-3420M APU and Radeon 7670m GPU + * https://bugzilla.kernel.org/show_bug.cgi?id=51381#c52 + */ + { PCI_VENDOR_ID_ATI, 0x6840, 0x1043, 0x2123, RADEON_PX_QUIRK_DISABLE_PX }, { 0, 0, 0, 0, 0 }, }; diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index baadb706c2767d13949be02c00407ce7479d0d19..b19a54dd18de5ec65661c8c8b1fda50048e3329a 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -240,9 +240,10 @@ int radeon_bo_create(struct radeon_device *rdev, * may be slow * See https://bugs.freedesktop.org/show_bug.cgi?id=88758 */ - +#ifndef CONFIG_COMPILE_TEST #warning Please enable CONFIG_MTRR and CONFIG_X86_PAT for better performance \ thanks to write-combining +#endif if (bo->flags & RADEON_GEM_GTT_WC) DRM_INFO_ONCE("Please enable CONFIG_MTRR and CONFIG_X86_PAT for " diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 97a0a639dad90b082d5c8856f042a00932fcabd0..90d5b41007bfd295eb531faa96da3fd99dde2693 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -5912,9 +5912,9 @@ static void si_set_pcie_lane_width_in_smc(struct radeon_device *rdev, { u32 lane_width; u32 new_lane_width = - (radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT; + ((radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1; u32 current_lane_width = - (radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT; + ((radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1; if (new_lane_width != current_lane_width) { radeon_set_pcie_lanes(rdev, new_lane_width); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index bf9ed0e639731cf15d0847674856a7082986ac76..f1fa8d5c9b52464f0c372763d8abbd4110c6dd01 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -1413,6 +1413,9 @@ static int vop_initial(struct vop *vop) usleep_range(10, 20); reset_control_deassert(ahb_rst); + VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1); + VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0); + memcpy(vop->regsbak, vop->regs, vop->len); VOP_REG_SET(vop, misc, global_regdone_en, 1); @@ -1568,17 +1571,9 @@ static int vop_bind(struct device *dev, struct device *master, void *data) mutex_init(&vop->vsync_mutex); - ret = devm_request_irq(dev, vop->irq, vop_isr, - IRQF_SHARED, dev_name(dev), vop); - if (ret) - return ret; - - /* IRQ is initially disabled; it gets enabled in power_on */ - disable_irq(vop->irq); - ret = vop_create_crtc(vop); if (ret) - goto err_enable_irq; + return ret; pm_runtime_enable(&pdev->dev); @@ -1588,13 +1583,19 @@ static int vop_bind(struct device *dev, struct device *master, void *data) goto err_disable_pm_runtime; } + ret = devm_request_irq(dev, vop->irq, vop_isr, + IRQF_SHARED, dev_name(dev), vop); + if (ret) + goto err_disable_pm_runtime; + + /* IRQ is initially disabled; it gets enabled in power_on */ + disable_irq(vop->irq); + return 0; err_disable_pm_runtime: pm_runtime_disable(&pdev->dev); vop_destroy_crtc(vop); -err_enable_irq: - enable_irq(vop->irq); /* To balance out the disable_irq above */ return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 3afdbf4bc10b37fcc1a21fc62b21aaa974405490..eff0a8ece8bcfdee6fd189251ead02727c8f0e11 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -173,6 +173,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo) vc4_bo_set_label(obj, -1); if (bo->validated_shader) { + kfree(bo->validated_shader->uniform_addr_offsets); kfree(bo->validated_shader->texture_samples); kfree(bo->validated_shader); bo->validated_shader = NULL; @@ -432,6 +433,7 @@ void vc4_free_object(struct drm_gem_object *gem_bo) } if (bo->validated_shader) { + kfree(bo->validated_shader->uniform_addr_offsets); kfree(bo->validated_shader->texture_samples); kfree(bo->validated_shader); bo->validated_shader = NULL; diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index d3f15bf609008f869f11bb132a1a9f0b2f457739..7cf82b071de2904d2169e67d9cde40df00ce5900 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -942,6 +942,7 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) fail: kfree(validation_state.branch_targets); if (validated_shader) { + kfree(validated_shader->uniform_addr_offsets); kfree(validated_shader->texture_samples); kfree(validated_shader); } diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 9eb96fb2c147940365918b9fa6ea6edeb51e976c..26a2da1f712d9e395dd9ff80369f3182e67df4b4 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -291,7 +291,7 @@ static int virtio_gpu_queue_ctrl_buffer_locked(struct virtio_gpu_device *vgdev, ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC); if (ret == -ENOSPC) { spin_unlock(&vgdev->ctrlq.qlock); - wait_event(vgdev->ctrlq.ack_queue, vq->num_free); + wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= outcnt + incnt); spin_lock(&vgdev->ctrlq.qlock); goto retry; } else { @@ -366,7 +366,7 @@ static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev, ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC); if (ret == -ENOSPC) { spin_unlock(&vgdev->cursorq.qlock); - wait_event(vgdev->cursorq.ack_queue, vq->num_free); + wait_event(vgdev->cursorq.ack_queue, vq->num_free >= outcnt); spin_lock(&vgdev->cursorq.qlock); goto retry; } else { diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index 00582267210123c1d479cbcf39ca13302d930cee..54350e72a9f2fa7b9f156969d8ebf94f393f46bb 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -12,6 +12,7 @@ msm_kgsl_core-y = \ kgsl_snapshot.o \ kgsl_events.o \ kgsl_pool.o \ + kgsl_gmu_core.o \ kgsl_gmu.o \ kgsl_hfi.o @@ -39,6 +40,7 @@ msm_adreno-y += \ adreno_a4xx_preempt.o \ adreno_a5xx_preempt.o \ adreno_a6xx_preempt.o \ + adreno_a6xx_gmu.o \ adreno_sysfs.o \ adreno.o \ adreno_cp_parser.o \ diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h index ab3280512697449f46222d20db78b697ab525d66..cfbc753e7178fc23796dbab60c86ead123451dc4 100644 --- a/drivers/gpu/msm/a6xx_reg.h +++ b/drivers/gpu/msm/a6xx_reg.h @@ -814,6 +814,7 @@ #define GBIF_AXI1_WRITE_DATA_TOTAL_BEATS 47 /* GBIF registers */ +#define A6XX_GBIF_SCACHE_CNTL1 0x3c02 #define A6XX_GBIF_QSB_SIDE0 0x3c03 #define A6XX_GBIF_QSB_SIDE1 0x3c04 #define A6XX_GBIF_QSB_SIDE2 0x3c05 diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h index 79fa645537adf562ae08e3d54cdd9ad313823fbe..9e58cb4510a8a0aa1bdb290855c3cdd532bd61f9 100644 --- a/drivers/gpu/msm/adreno-gpulist.h +++ b/drivers/gpu/msm/adreno-gpulist.h @@ -385,7 +385,8 @@ static const struct adreno_gpu_core adreno_gpulist[] = { .major = 4, .minor = 0, .patchid = ANY_ID, - .features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_GPMU, + .features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_GPMU | + ADRENO_IFPC, .sqefw_name = "a630_sqe.fw", .zap_name = "a640_zap", .gpudev = &adreno_a6xx_gpudev, @@ -397,5 +398,6 @@ static const struct adreno_gpu_core adreno_gpulist[] = { .gpmu_minor = 0x000, .gpmu_tsens = 0x000C000D, .max_power = 5448, + .va_padding = SZ_64K, }, }; diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 0763926c13783940db6f0b277787141af9f3c488..3e636d926a8f55342cc96040a9ca6d62cf6e44f7 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -24,6 +24,7 @@ #include #include "kgsl.h" +#include "kgsl_gmu_core.h" #include "kgsl_pwrscale.h" #include "kgsl_sharedmem.h" #include "kgsl_iommu.h" @@ -79,9 +80,6 @@ static struct adreno_device device_3d0 = { .pwrscale = KGSL_PWRSCALE_INIT(&adreno_tz_data), .name = DEVICE_3D0_NAME, .id = KGSL_DEVICE_3D0, - .gmu = { - .load_mode = TCM_BOOT, - }, .pwrctrl = { .irq_name = "kgsl_3d0_irq", }, @@ -107,13 +105,13 @@ static struct adreno_device device_3d0 = { .input_work = __WORK_INITIALIZER(device_3d0.input_work, adreno_input_work), .pwrctrl_flag = BIT(ADRENO_SPTP_PC_CTRL) | BIT(ADRENO_PPD_CTRL) | - BIT(ADRENO_LM_CTRL) | + BIT(ADRENO_LM_CTRL) | BIT(ADRENO_HWCG_CTRL) | BIT(ADRENO_THROTTLING_CTRL), .profile.enabled = false, .active_list = LIST_HEAD_INIT(device_3d0.active_list), .active_list_lock = __SPIN_LOCK_UNLOCKED(device_3d0.active_list_lock), - .gpu_llc_slice_enable = false, - .gpuhtw_llc_slice_enable = false, + .gpu_llc_slice_enable = true, + .gpuhtw_llc_slice_enable = true, .preempt = { .preempt_level = 1, .skipsaverestore = 1, @@ -613,6 +611,7 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) struct adreno_irq *irq_params = gpudev->irq; irqreturn_t ret = IRQ_NONE; unsigned int status = 0, fence = 0, fence_retries = 0, tmp, int_bit; + unsigned int shadow_status = 0; int i; atomic_inc(&adreno_dev->pending_irq_refcnt); @@ -634,19 +633,30 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) * This is usually harmless because the GMU will abort power collapse * and change the fence back to ALLOW. Poll so that this can happen. */ - if (kgsl_gmu_isenabled(device)) { - do { + if (gmu_core_isenabled(device)) { + adreno_readreg(adreno_dev, + ADRENO_REG_GMU_AO_AHB_FENCE_CTRL, + &fence); + + while (fence != 0) { + /* Wait for small time before trying again */ + udelay(1); adreno_readreg(adreno_dev, ADRENO_REG_GMU_AO_AHB_FENCE_CTRL, &fence); - if (fence_retries == FENCE_RETRY_MAX) { + if (fence_retries == FENCE_RETRY_MAX && fence != 0) { + adreno_readreg(adreno_dev, + ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS, + &shadow_status); + KGSL_DRV_CRIT_RATELIMIT(device, - "AHB fence stuck in ISR\n"); - return ret; + "AHB fence stuck in ISR: Shadow INT status=%8.8X\n", + shadow_status & irq_params->mask); + goto done; } fence_retries++; - } while (fence != 0); + } } adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status); @@ -687,6 +697,7 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD, int_bit); +done: /* Turn off the KEEPALIVE vote from earlier unless hard fault set */ if (gpudev->gpu_keepalive) { /* If hard fault, then let snapshot turn off the keepalive */ @@ -848,6 +859,58 @@ static const struct of_device_id adreno_match_table[] = { {} }; +static void adreno_of_get_ca_target_pwrlevel(struct adreno_device *adreno_dev, + struct device_node *node) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int ca_target_pwrlevel = 1; + + of_property_read_u32(node, "qcom,ca-target-pwrlevel", + &ca_target_pwrlevel); + + if (ca_target_pwrlevel > device->pwrctrl.num_pwrlevels - 2) + ca_target_pwrlevel = 1; + + device->pwrscale.ctxt_aware_target_pwrlevel = ca_target_pwrlevel; +} + +static void adreno_of_get_ca_aware_properties(struct adreno_device *adreno_dev, + struct device_node *parent) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_pwrscale *pwrscale = &device->pwrscale; + struct device_node *node, *child; + unsigned int bin = 0; + + pwrscale->ctxt_aware_enable = + of_property_read_bool(parent, "qcom,enable-ca-jump"); + + if (pwrscale->ctxt_aware_enable) { + if (of_property_read_u32(parent, "qcom,ca-busy-penalty", + &pwrscale->ctxt_aware_busy_penalty)) + pwrscale->ctxt_aware_busy_penalty = 12000; + + node = of_find_node_by_name(parent, "qcom,gpu-pwrlevel-bins"); + if (node == NULL) { + adreno_of_get_ca_target_pwrlevel(adreno_dev, parent); + return; + } + + for_each_child_of_node(node, child) { + if (of_property_read_u32(child, "qcom,speed-bin", &bin)) + continue; + + if (bin == adreno_dev->speed_bin) { + adreno_of_get_ca_target_pwrlevel(adreno_dev, + child); + return; + } + } + + pwrscale->ctxt_aware_target_pwrlevel = 1; + } +} + static int adreno_of_parse_pwrlevels(struct adreno_device *adreno_dev, struct device_node *node) { @@ -1017,6 +1080,9 @@ static int adreno_of_get_power(struct adreno_device *adreno_dev, if (adreno_of_get_pwrlevels(adreno_dev, node)) return -EINVAL; + /* Get context aware DCVS properties */ + adreno_of_get_ca_aware_properties(adreno_dev, node); + /* get pm-qos-active-latency, set it to default if not found */ if (of_property_read_u32(node, "qcom,pm-qos-active-latency", &device->pwrctrl.pm_qos_active_latency)) @@ -1035,8 +1101,6 @@ static int adreno_of_get_power(struct adreno_device *adreno_dev, if (of_property_read_u32(node, "qcom,idle-timeout", &timeout)) timeout = 80; - /* Force disable slumber */ - timeout = 10000000; device->pwrctrl.interval_timeout = msecs_to_jiffies(timeout); device->pwrctrl.bus_control = of_property_read_bool(node, @@ -1175,7 +1239,6 @@ static int adreno_probe(struct platform_device *pdev) struct kgsl_device *device; struct adreno_device *adreno_dev; int status; - unsigned long flags; adreno_dev = adreno_get_dev(pdev); @@ -1211,9 +1274,7 @@ static int adreno_probe(struct platform_device *pdev) * Another part of GPU power probe in platform_probe * needs GMU initialized. */ - flags = ADRENO_FEATURE(adreno_dev, ADRENO_GPMU) ? BIT(GMU_GPMU) : 0; - - status = gmu_probe(device, flags); + status = gmu_core_probe(device); if (status) { device->pdev = NULL; return status; @@ -1227,6 +1288,14 @@ static int adreno_probe(struct platform_device *pdev) if (adreno_support_64bit(adreno_dev)) device->mmu.features |= KGSL_MMU_64BIT; + /* Default to 4K alignment (in other words, no additional padding) */ + device->mmu.va_padding = PAGE_SIZE; + + if (adreno_dev->gpucore->va_padding) { + device->mmu.features |= KGSL_MMU_PAD_VA; + device->mmu.va_padding = adreno_dev->gpucore->va_padding; + } + status = kgsl_device_platform_probe(device); if (status) { device->pdev = NULL; @@ -1263,9 +1332,29 @@ static int adreno_probe(struct platform_device *pdev) adreno_sysfs_init(adreno_dev); + kgsl_pwrscale_init(&pdev->dev, CONFIG_QCOM_ADRENO_DEFAULT_GOVERNOR); + /* Initialize coresight for the target */ adreno_coresight_init(adreno_dev); + /* Get the system cache slice descriptor for GPU */ + adreno_dev->gpu_llc_slice = adreno_llc_getd(&pdev->dev, "gpu"); + if (IS_ERR(adreno_dev->gpu_llc_slice)) { + KGSL_DRV_WARN(device, + "Failed to get GPU LLC slice descriptor %ld\n", + PTR_ERR(adreno_dev->gpu_llc_slice)); + adreno_dev->gpu_llc_slice = NULL; + } + + /* Get the system cache slice descriptor for GPU pagetables */ + adreno_dev->gpuhtw_llc_slice = adreno_llc_getd(&pdev->dev, "gpuhtw"); + if (IS_ERR(adreno_dev->gpuhtw_llc_slice)) { + KGSL_DRV_WARN(device, + "Failed to get gpuhtw LLC slice descriptor %ld\n", + PTR_ERR(adreno_dev->gpuhtw_llc_slice)); + adreno_dev->gpuhtw_llc_slice = NULL; + } + #ifdef CONFIG_INPUT if (!device->pwrctrl.input_disable) { adreno_input_handler.private = device; @@ -1365,7 +1454,7 @@ static int adreno_remove(struct platform_device *pdev) adreno_perfcounter_close(adreno_dev); kgsl_device_platform_remove(device); - gmu_remove(device); + gmu_core_remove(device); if (test_bit(ADRENO_DEVICE_PWRON_FIXUP, &adreno_dev->priv)) { kgsl_free_global(device, &adreno_dev->pwron_fixup); @@ -1566,7 +1655,7 @@ static bool regulators_left_on(struct kgsl_device *device) { int i; - if (kgsl_gmu_gpmu_isenabled(device)) + if (gmu_core_gpmu_isenabled(device)) return false; for (i = 0; i < KGSL_MAX_REGULATORS; i++) { @@ -1682,6 +1771,7 @@ static int _adreno_start(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); int status = -EINVAL, ret; unsigned int state = device->state; bool regulator_left_on; @@ -1742,17 +1832,14 @@ static int _adreno_start(struct adreno_device *adreno_dev) } /* Send OOB request to turn on the GX */ - if (gpudev->oob_set) { - status = gpudev->oob_set(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_set) { + status = gmu_dev_ops->oob_set(adreno_dev, oob_gpu); if (status) goto error_mmu_off; } - if (adreno_is_a640(adreno_dev)) { - struct hfi_start_cmd req; - - /* Send hfi start msg */ - status = hfi_send_req(&device->gmu, H2F_MSG_START, &req); + if (gmu_dev_ops->hfi_start_msg) { + status = gmu_dev_ops->hfi_start_msg(adreno_dev); if (status) goto error_mmu_off; } @@ -1895,6 +1982,14 @@ static int _adreno_start(struct adreno_device *adreno_dev) /* Start the GPU */ gpudev->start(adreno_dev); + /* + * The system cache control registers + * live on the CX/GX rail. Hence need + * reprogramming everytime the GPU + * comes out of power collapse. + */ + adreno_llc_setup(device); + /* Re-initialize the coresight registers if applicable */ adreno_coresight_start(adreno_dev); @@ -1921,19 +2016,19 @@ static int _adreno_start(struct adreno_device *adreno_dev) pmqos_active_vote); /* Send OOB request to allow IFPC */ - if (gpudev->oob_clear) { - gpudev->oob_clear(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_clear) { + gmu_dev_ops->oob_clear(adreno_dev, oob_gpu); /* If we made it this far, the BOOT OOB was sent to the GMU */ if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) - gpudev->oob_clear(adreno_dev, oob_boot_slumber); + gmu_dev_ops->oob_clear(adreno_dev, oob_boot_slumber); } return 0; error_oob_clear: - if (gpudev->oob_clear) - gpudev->oob_clear(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_clear) + gmu_dev_ops->oob_clear(adreno_dev, oob_gpu); error_mmu_off: kgsl_mmu_stop(&device->mmu); @@ -1978,24 +2073,21 @@ int adreno_start(struct kgsl_device *device, int priority) static int adreno_stop(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); int error = 0; if (!test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)) return 0; /* Turn the power on one last time before stopping */ - if (gpudev->oob_set) { - error = gpudev->oob_set(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_set) { + error = gmu_dev_ops->oob_set(adreno_dev, oob_gpu); if (error) { - struct gmu_device *gmu = &device->gmu; - - gpudev->oob_clear(adreno_dev, oob_gpu); - if (gmu->gx_gdsc && - regulator_is_enabled(gmu->gx_gdsc)) { + gmu_dev_ops->oob_clear(adreno_dev, oob_gpu); + if (gmu_core_regulator_isenabled(device)) { /* GPU is on. Try recovery */ - set_bit(GMU_FAULT, &gmu->flags); - gmu_snapshot(device); + gmu_core_setbit(device, GMU_FAULT); + gmu_core_snapshot(device); error = -EINVAL; } else { return error; @@ -2024,8 +2116,8 @@ static int adreno_stop(struct kgsl_device *device) /* Save physical performance counter values before GPU power down*/ adreno_perfcounter_save(adreno_dev); - if (gpudev->oob_clear) - gpudev->oob_clear(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_clear) + gmu_dev_ops->oob_clear(adreno_dev, oob_gpu); /* * Saving perfcounters will use an OOB to put the GMU into @@ -2033,12 +2125,11 @@ static int adreno_stop(struct kgsl_device *device) * GMU to return to the lowest idle level. This is * because some idle level transitions require VBIF and MMU. */ - if (!error && gpudev->wait_for_lowest_idle && - gpudev->wait_for_lowest_idle(adreno_dev)) { - struct gmu_device *gmu = &device->gmu; + if (!error && gmu_dev_ops->wait_for_lowest_idle && + gmu_dev_ops->wait_for_lowest_idle(adreno_dev)) { - set_bit(GMU_FAULT, &gmu->flags); - gmu_snapshot(device); + gmu_core_setbit(device, GMU_FAULT); + gmu_core_snapshot(device); /* * Assume GMU hang after 10ms without responding. * It shall be relative safe to clear vbif and stop @@ -2683,10 +2774,11 @@ int adreno_soft_reset(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); int ret; - if (gpudev->oob_set) { - ret = gpudev->oob_set(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_set) { + ret = gmu_dev_ops->oob_set(adreno_dev, oob_gpu); if (ret) return ret; } @@ -2709,8 +2801,8 @@ int adreno_soft_reset(struct kgsl_device *device) else ret = _soft_reset(adreno_dev); if (ret) { - if (gpudev->oob_clear) - gpudev->oob_clear(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_clear) + gmu_dev_ops->oob_clear(adreno_dev, oob_gpu); return ret; } @@ -2763,8 +2855,8 @@ int adreno_soft_reset(struct kgsl_device *device) /* Restore physical performance counter values after soft reset */ adreno_perfcounter_restore(adreno_dev); - if (gpudev->oob_clear) - gpudev->oob_clear(adreno_dev, oob_gpu); + if (gmu_dev_ops->oob_clear) + gmu_dev_ops->oob_clear(adreno_dev, oob_gpu); return ret; } @@ -3019,44 +3111,72 @@ static void adreno_regwrite(struct kgsl_device *device, __raw_writel(value, reg); } -static void adreno_gmu_regwrite(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) +/* + * adreno_gmu_fenced_write() - Check if there is a GMU and it is enabled + * @adreno_dev: Pointer to the Adreno device device that owns the GMU + * @offset: 32bit register enum that is to be written + * @val: The value to be written to the register + * @fence_mask: The value to poll the fence status register + * + * Check the WRITEDROPPED0/1 bit in the FENCE_STATUS register to check if + * the write to the fenced register went through. If it didn't then we retry + * the write until it goes through or we time out. + */ +int adreno_gmu_fenced_write(struct adreno_device *adreno_dev, + enum adreno_regs offset, unsigned int val, + unsigned int fence_mask) { - void __iomem *reg; - struct gmu_device *gmu = &device->gmu; + unsigned int status, i; + struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + unsigned int reg_offset = gpudev->reg_offsets->offsets[offset]; - trace_kgsl_regwrite(device, offsetwords, value); + adreno_writereg(adreno_dev, offset, val); - offsetwords -= gmu->gmu2gpu_offset; - reg = gmu->reg_virt + (offsetwords << 2); + if (!gmu_core_isenabled(KGSL_DEVICE(adreno_dev))) + return 0; - /* - * ensure previous writes post before this one, - * i.e. act like normal writel() - */ - wmb(); - __raw_writel(value, reg); + for (i = 0; i < GMU_CORE_WAKEUP_RETRY_MAX; i++) { + adreno_read_gmureg(adreno_dev, ADRENO_REG_GMU_AHB_FENCE_STATUS, + &status); + + /* + * If !writedropped0/1, then the write to fenced register + * was successful + */ + if (!(status & fence_mask)) + return 0; + /* Wait a small amount of time before trying again */ + udelay(GMU_CORE_WAKEUP_DELAY_US); + + /* Try to write the fenced register again */ + adreno_writereg(adreno_dev, offset, val); + } + + dev_err(adreno_dev->dev.dev, + "GMU fenced register write timed out: reg 0x%x\n", reg_offset); + return -ETIMEDOUT; } -static void adreno_gmu_regread(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) +unsigned int adreno_gmu_ifpc_show(struct adreno_device *adreno_dev) { - void __iomem *reg; - struct gmu_device *gmu = &device->gmu; + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS( + KGSL_DEVICE(adreno_dev)); - offsetwords -= gmu->gmu2gpu_offset; + if (gmu_dev_ops->ifpc_show) + return gmu_dev_ops->ifpc_show(adreno_dev); - reg = gmu->reg_virt + (offsetwords << 2); + return 0; +} - *value = __raw_readl(reg); +int adreno_gmu_ifpc_store(struct adreno_device *adreno_dev, unsigned int val) +{ + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS( + KGSL_DEVICE(adreno_dev)); - /* - * ensure this read finishes before the next one. - * i.e. act like normal readl() - */ - rmb(); + if (gmu_dev_ops->ifpc_store) + return gmu_dev_ops->ifpc_store(adreno_dev, val); + + return -EINVAL; } bool adreno_is_cx_dbgc_register(struct kgsl_device *device, @@ -3510,8 +3630,6 @@ static const struct kgsl_functable adreno_functable = { /* Mandatory functions */ .regread = adreno_regread, .regwrite = adreno_regwrite, - .gmu_regread = adreno_gmu_regread, - .gmu_regwrite = adreno_gmu_regwrite, .idle = adreno_idle, .isidle = adreno_isidle, .suspend_context = adreno_suspend_context, @@ -3548,6 +3666,8 @@ static const struct kgsl_functable adreno_functable = { .clk_set_options = adreno_clk_set_options, .gpu_model = adreno_gpu_model, .stop_fault_timer = adreno_dispatcher_stop_fault_timer, + .dispatcher_halt = adreno_dispatcher_halt, + .dispatcher_unhalt = adreno_dispatcher_unhalt, }; static struct platform_driver adreno_platform_driver = { diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 4437ef2ea9c6ed1ab531e2a0a07e651bb981a06d..e2b848753e56c7fed89a641f0f5f4e0f82e5134d 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -23,7 +23,7 @@ #include "adreno_perfcounter.h" #include #include -#include "kgsl_gmu.h" +#include "kgsl_gmu_core.h" #include "a4xx_reg.h" @@ -355,6 +355,7 @@ struct adreno_firmware { * @regfw_name: Filename for the register sequence firmware * @gpmu_tsens: ID for the temporature sensor used by the GPMU * @max_power: Max possible power draw of a core, units elephant tail hairs + * @va_padding: Size to pad allocations to, zero if not required */ struct adreno_gpu_core { enum adreno_gpurev gpurev; @@ -385,6 +386,7 @@ struct adreno_gpu_core { const char *regfw_name; unsigned int gpmu_tsens; unsigned int max_power; + uint64_t va_padding; }; @@ -721,6 +723,7 @@ enum adreno_regs { ADRENO_REG_GMU_HOST2GMU_INTR_RAW_INFO, ADRENO_REG_GMU_NMI_CONTROL_STATUS, ADRENO_REG_GMU_CM3_CFG, + ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS, ADRENO_REG_GPMU_POWER_COUNTER_ENABLE, ADRENO_REG_REGISTER_MAX, }; @@ -910,7 +913,8 @@ struct adreno_gpudev { /* GPU specific function hooks */ void (*irq_trace)(struct adreno_device *, unsigned int status); void (*snapshot)(struct adreno_device *, struct kgsl_snapshot *); - void (*snapshot_gmu)(struct adreno_device *, struct kgsl_snapshot *); + void (*snapshot_debugbus)(struct adreno_device *adreno_dev, + struct kgsl_snapshot *snapshot); void (*platform_setup)(struct adreno_device *); void (*init)(struct adreno_device *); void (*remove)(struct adreno_device *); @@ -951,17 +955,9 @@ struct adreno_gpudev { void (*llc_configure_gpuhtw_scid)(struct adreno_device *adreno_dev); void (*llc_enable_overrides)(struct adreno_device *adreno_dev); void (*pre_reset)(struct adreno_device *); - int (*oob_set)(struct adreno_device *adreno_dev, - enum oob_request req); - void (*oob_clear)(struct adreno_device *adreno_dev, - enum oob_request req); void (*gpu_keepalive)(struct adreno_device *adreno_dev, bool state); - int (*rpmh_gpu_pwrctrl)(struct adreno_device *, unsigned int ops, - unsigned int arg1, unsigned int arg2); bool (*hw_isidle)(struct adreno_device *); - int (*wait_for_lowest_idle)(struct adreno_device *); - int (*wait_for_gmu_idle)(struct adreno_device *); const char *(*iommu_fault_block)(struct adreno_device *adreno_dev, unsigned int fsynr1); int (*reset)(struct kgsl_device *, int fault); @@ -1367,7 +1363,7 @@ static inline void adreno_read_gmureg(struct adreno_device *adreno_dev, struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); if (adreno_checkreg_off(adreno_dev, offset_name)) - kgsl_gmu_regread(KGSL_DEVICE(adreno_dev), + gmu_core_regread(KGSL_DEVICE(adreno_dev), gpudev->reg_offsets->offsets[offset_name], val); else *val = 0; @@ -1386,7 +1382,7 @@ static inline void adreno_write_gmureg(struct adreno_device *adreno_dev, struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); if (adreno_checkreg_off(adreno_dev, offset_name)) - kgsl_gmu_regwrite(KGSL_DEVICE(adreno_dev), + gmu_core_regwrite(KGSL_DEVICE(adreno_dev), gpudev->reg_offsets->offsets[offset_name], val); } @@ -1856,15 +1852,16 @@ static inline unsigned int counter_delta(struct kgsl_device *device, static inline int adreno_perfcntr_active_oob_get( struct adreno_device *adreno_dev) { - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS( + KGSL_DEVICE(adreno_dev)); int ret; ret = kgsl_active_count_get(KGSL_DEVICE(adreno_dev)); if (ret) return ret; - if (gpudev->oob_set) { - ret = gpudev->oob_set(adreno_dev, oob_perfcntr); + if (gmu_dev_ops->oob_set) { + ret = gmu_dev_ops->oob_set(adreno_dev, oob_perfcntr); if (ret) kgsl_active_count_put(KGSL_DEVICE(adreno_dev)); } @@ -1875,10 +1872,11 @@ static inline int adreno_perfcntr_active_oob_get( static inline void adreno_perfcntr_active_oob_put( struct adreno_device *adreno_dev) { - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS( + KGSL_DEVICE(adreno_dev)); - if (gpudev->oob_clear) - gpudev->oob_clear(adreno_dev, oob_perfcntr); + if (gmu_dev_ops->oob_clear) + gmu_dev_ops->oob_clear(adreno_dev, oob_perfcntr); kgsl_active_count_put(KGSL_DEVICE(adreno_dev)); } @@ -1939,6 +1937,8 @@ static inline void adreno_deassert_gbif_halt(struct adreno_device *adreno_dev) int adreno_gmu_fenced_write(struct adreno_device *adreno_dev, enum adreno_regs offset, unsigned int val, unsigned int fence_mask); +unsigned int adreno_gmu_ifpc_show(struct adreno_device *adreno_dev); +int adreno_gmu_ifpc_store(struct adreno_device *adreno_dev, unsigned int val); int adreno_clear_pending_transactions(struct kgsl_device *device); #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c index 78e1557ccfc4437f9adddcf824d038d5abf0d2d0..9fb1d8de59c5bc6524a5a62fc2599c2befde12d8 100644 --- a/drivers/gpu/msm/adreno_a6xx.c +++ b/drivers/gpu/msm/adreno_a6xx.c @@ -27,7 +27,6 @@ #include "kgsl_sharedmem.h" #include "kgsl_log.h" #include "kgsl.h" -#include "kgsl_gmu.h" #include "kgsl_hfi.h" #include "kgsl_trace.h" @@ -44,9 +43,6 @@ #define A6XX_GPU_CX_REG_BASE 0x509E000 #define A6XX_GPU_CX_REG_SIZE 0x1000 -#define GPU_LIMIT_THRESHOLD_ENABLE BIT(31) - -static int _load_gmu_firmware(struct kgsl_device *device); static const struct adreno_vbif_data a630_vbif[] = { {A6XX_VBIF_GATE_OFF_WRREQ_EN, 0x00000009}, @@ -440,26 +436,6 @@ static void _update_always_on_regs(struct adreno_device *adreno_dev) A6XX_CP_ALWAYS_ON_COUNTER_HI; } -static uint64_t read_AO_counter(struct kgsl_device *device) -{ - unsigned int l, h, h1; - - kgsl_gmu_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_H, &h); - kgsl_gmu_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_L, &l); - kgsl_gmu_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_H, &h1); - - /* - * If there's no change in COUNTER_H we have no overflow so return, - * otherwise read COUNTER_L again - */ - - if (h == h1) - return (uint64_t) l | ((uint64_t) h << 32); - - kgsl_gmu_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_L, &l); - return (uint64_t) l | ((uint64_t) h1 << 32); -} - static void a6xx_pwrup_reglist_init(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -483,7 +459,7 @@ static void a6xx_init(struct adreno_device *adreno_dev) * If the GMU is not enabled, rewrite the offset for the always on * counters to point to the CP always on instead of GMU always on */ - if (!kgsl_gmu_isenabled(KGSL_DEVICE(adreno_dev))) + if (!gmu_core_isenabled(KGSL_DEVICE(adreno_dev))) _update_always_on_regs(adreno_dev); a6xx_pwrup_reglist_init(adreno_dev); @@ -616,12 +592,12 @@ static void a6xx_hwcg_set(struct adreno_device *adreno_dev, bool on) if (!test_bit(ADRENO_HWCG_CTRL, &adreno_dev->pwrctrl_flag)) on = false; - if (kgsl_gmu_isenabled(device)) { - kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_MODE_CNTL, + if (gmu_core_isenabled(device)) { + gmu_core_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_MODE_CNTL, on ? __get_gmu_ao_cgc_mode_cntl(adreno_dev) : 0); - kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_DELAY_CNTL, + gmu_core_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_DELAY_CNTL, on ? __get_gmu_ao_cgc_delay_cntl(adreno_dev) : 0); - kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_HYST_CNTL, + gmu_core_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_HYST_CNTL, on ? __get_gmu_ao_cgc_hyst_cntl(adreno_dev) : 0); } @@ -644,35 +620,19 @@ static void a6xx_hwcg_set(struct adreno_device *adreno_dev, bool on) regs = a6xx_hwcg_registers[i].regs; /* Disable SP clock before programming HWCG registers */ - kgsl_gmu_regrmw(device, A6XX_GPU_GMU_GX_SPTPRAC_CLOCK_CONTROL, 1, 0); + gmu_core_regrmw(device, A6XX_GPU_GMU_GX_SPTPRAC_CLOCK_CONTROL, 1, 0); for (j = 0; j < a6xx_hwcg_registers[i].count; j++) kgsl_regwrite(device, regs[j].off, on ? regs[j].val : 0); /* Enable SP clock */ - kgsl_gmu_regrmw(device, A6XX_GPU_GMU_GX_SPTPRAC_CLOCK_CONTROL, 0, 1); + gmu_core_regrmw(device, A6XX_GPU_GMU_GX_SPTPRAC_CLOCK_CONTROL, 0, 1); /* enable top level HWCG */ kgsl_regwrite(device, A6XX_RBBM_CLOCK_CNTL, on ? __get_rbbm_clock_cntl_on(adreno_dev) : 0); } -#define LM_DEFAULT_LIMIT 6000 - -static uint32_t lm_limit(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - - if (adreno_dev->lm_limit) - return adreno_dev->lm_limit; - - if (of_property_read_u32(device->pdev->dev.of_node, "qcom,lm-limit", - &adreno_dev->lm_limit)) - adreno_dev->lm_limit = LM_DEFAULT_LIMIT; - - return adreno_dev->lm_limit; -} - static void a6xx_patch_pwrup_reglist(struct adreno_device *adreno_dev) { uint32_t i; @@ -733,13 +693,6 @@ static void a6xx_patch_pwrup_reglist(struct adreno_device *adreno_dev) } } -#define LIMITS_CONFIG(t, s, c, i, a) ( \ - (t & 0xF) | \ - ((s & 0xF) << 4) | \ - ((c & 0xF) << 8) | \ - ((i & 0xF) << 12) | \ - ((a & 0xF) << 16)) - /* * a6xx_start() - Device start * @adreno_dev: Pointer to adreno device @@ -749,12 +702,13 @@ static void a6xx_patch_pwrup_reglist(struct adreno_device *adreno_dev) static void a6xx_start(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); unsigned int bit, mal, mode, glbl_inv; unsigned int amsbc = 0; static bool patch_reglist; /* runtime adjust callbacks based on feature sets */ - if (!kgsl_gmu_isenabled(device)) + if (!gmu_core_isenabled(device)) /* Legacy idle management if gmu is disabled */ ADRENO_GPU_DEVICE(adreno_dev)->hw_isidle = NULL; /* enable hardware clockgating */ @@ -801,8 +755,11 @@ static void a6xx_start(struct adreno_device *adreno_dev) /* Setting the mem pool size */ kgsl_regwrite(device, A6XX_CP_MEM_POOL_SIZE, 128); - /* Setting the primFifo thresholds default values */ - kgsl_regwrite(device, A6XX_PC_DBG_ECO_CNTL, (0x300 << 11)); + /* Setting the primFifo thresholds values */ + if (adreno_is_a640(adreno_dev)) + kgsl_regwrite(device, A6XX_PC_DBG_ECO_CNTL, (0x400 << 11)); + else + kgsl_regwrite(device, A6XX_PC_DBG_ECO_CNTL, (0x300 << 11)); /* Set the AHB default slave response to "ERROR" */ kgsl_regwrite(device, A6XX_CP_AHB_CNTL, 0x1); @@ -888,35 +845,8 @@ static void a6xx_start(struct adreno_device *adreno_dev) * 3. HFI * At this point, we are guaranteed all. */ - if (ADRENO_FEATURE(adreno_dev, ADRENO_LM) && - test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) { - int result; - struct gmu_device *gmu = &device->gmu; - struct device *dev = &gmu->pdev->dev; - struct hfi_lmconfig_cmd cmd; - - kgsl_gmu_regwrite(device, A6XX_GPU_GMU_CX_GMU_PWR_THRESHOLD, - GPU_LIMIT_THRESHOLD_ENABLE | lm_limit(adreno_dev)); - kgsl_gmu_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 1); - kgsl_gmu_regwrite(device, A6XX_GPU_GMU_CX_GMU_ISENSE_CTRL, 0x1); - - gmu->lm_config = LIMITS_CONFIG(1, 1, 1, 0, 0); - gmu->bcl_config = 0; - gmu->lm_dcvs_level = 0; - - cmd.limit_conf = gmu->lm_config; - cmd.bcl_conf = gmu->bcl_config; - cmd.lm_enable_bitmask = 0; - - if (gmu->lm_dcvs_level <= MAX_GX_LEVELS) - cmd.lm_enable_bitmask = - (1 << (gmu->lm_dcvs_level + 1)) - 1; - - result = hfi_send_req(gmu, H2F_MSG_LM_CFG, &cmd); - if (result) - dev_err(dev, "Failure enabling limits management (%d)\n", - result); - } + if (gmu_dev_ops->enable_lm) + gmu_dev_ops->enable_lm(device); } /* @@ -1253,361 +1183,6 @@ static int _load_firmware(struct kgsl_device *device, const char *fwfile, return ret; } -#define RSC_CMD_OFFSET 2 -#define PDC_CMD_OFFSET 4 - -static void _regwrite(void __iomem *regbase, - unsigned int offsetwords, unsigned int value) -{ - void __iomem *reg; - - reg = regbase + (offsetwords << 2); - __raw_writel(value, reg); -} - -/* - * _load_gmu_rpmh_ucode() - Load the ucode into the GPU PDC/RSC blocks - * PDC and RSC execute GPU power on/off RPMh sequence - * @device: Pointer to KGSL device - */ -static void _load_gmu_rpmh_ucode(struct kgsl_device *device) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; - - /* Disable SDE clock gating */ - kgsl_gmu_regwrite(device, A6XX_GPU_RSCC_RSC_STATUS0_DRV0, BIT(24)); - - /* Setup RSC PDC handshake for sleep and wakeup */ - kgsl_gmu_regwrite(device, A6XX_RSCC_PDC_SLAVE_ID_DRV0, 1); - kgsl_gmu_regwrite(device, A6XX_RSCC_HIDDEN_TCS_CMD0_DATA, 0); - kgsl_gmu_regwrite(device, A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR, 0); - kgsl_gmu_regwrite(device, - A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + RSC_CMD_OFFSET, 0); - kgsl_gmu_regwrite(device, - A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + RSC_CMD_OFFSET, 0); - kgsl_gmu_regwrite(device, - A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + RSC_CMD_OFFSET * 2, - 0x80000000); - kgsl_gmu_regwrite(device, - A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + RSC_CMD_OFFSET * 2, - 0); - kgsl_gmu_regwrite(device, A6XX_RSCC_OVERRIDE_START_ADDR, 0); - kgsl_gmu_regwrite(device, A6XX_RSCC_PDC_SEQ_START_ADDR, 0x4520); - kgsl_gmu_regwrite(device, A6XX_RSCC_PDC_MATCH_VALUE_LO, 0x4510); - kgsl_gmu_regwrite(device, A6XX_RSCC_PDC_MATCH_VALUE_HI, 0x4514); - - /* Enable timestamp event for v1 only */ - if (adreno_is_a630v1(adreno_dev)) - kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1); - - /* Load RSC sequencer uCode for sleep and wakeup */ - kgsl_gmu_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0, 0xA7A506A0); - kgsl_gmu_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 1, 0xA1E6A6E7); - kgsl_gmu_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 2, 0xA2E081E1); - kgsl_gmu_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 3, 0xE9A982E2); - kgsl_gmu_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 4, 0x0020E8A8); - - /* Load PDC sequencer uCode for power up and power down sequence */ - _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0, 0xFEBEA1E1); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 1, 0xA5A4A3A2); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 2, 0x8382A6E0); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 3, 0xBCE3E284); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 4, 0x002081FC); - - /* Set TCS commands used by PDC sequence for low power modes */ - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_ENABLE_BANK, 7); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_WAIT_FOR_CMPL_BANK, 0); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CONTROL, 0); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_MSGID, 0x10108); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_ADDR, 0x30010); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_DATA, 1); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET, 0x0); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x0); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_ENABLE_BANK, 7); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_WAIT_FOR_CMPL_BANK, 0); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CONTROL, 0); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_MSGID, 0x10108); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_ADDR, 0x30010); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_DATA, 2); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET, 0x3); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080); - _regwrite(gmu->pdc_reg_virt, - PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x3); - - /* Setup GPU PDC */ - _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_START_ADDR, 0); - _regwrite(gmu->pdc_reg_virt, PDC_GPU_ENABLE_PDC, 0x80000001); - - /* ensure no writes happen before the uCode is fully written */ - wmb(); -} - -#define GMU_START_TIMEOUT 100 /* ms */ -#define GPU_START_TIMEOUT 100 /* ms */ -#define GPU_RESET_TIMEOUT 1 /* ms */ -#define GPU_RESET_TIMEOUT_US 10 /* us */ - -/* - * timed_poll_check() - polling *gmu* register at given offset until - * its value changed to match expected value. The function times - * out and returns after given duration if register is not updated - * as expected. - * - * @device: Pointer to KGSL device - * @offset: Register offset - * @expected_ret: expected register value that stops polling - * @timout: number of jiffies to abort the polling - * @mask: bitmask to filter register value to match expected_ret - */ -static int timed_poll_check(struct kgsl_device *device, - unsigned int offset, unsigned int expected_ret, - unsigned int timeout, unsigned int mask) -{ - unsigned long t; - unsigned int value; - - t = jiffies + msecs_to_jiffies(timeout); - - do { - kgsl_gmu_regread(device, offset, &value); - if ((value & mask) == expected_ret) - return 0; - /* Wait 100us to reduce unnecessary AHB bus traffic */ - usleep_range(10, 100); - } while (!time_after(jiffies, t)); - - /* Double check one last time */ - kgsl_gmu_regread(device, offset, &value); - if ((value & mask) == expected_ret) - return 0; - - return -EINVAL; -} - -/* - * The lowest 16 bits of this value are the number of XO clock cycles - * for main hysteresis. This is the first hysteresis. Here we set it - * to 0x1680 cycles, or 300 us. The highest 16 bits of this value are - * the number of XO clock cycles for short hysteresis. This happens - * after main hysteresis. Here we set it to 0xA cycles, or 0.5 us. - */ -#define GMU_PWR_COL_HYST 0x000A1680 - -/* - * a6xx_gmu_power_config() - Configure and enable GMU's low power mode - * setting based on ADRENO feature flags. - * @device: Pointer to KGSL device - */ -static void a6xx_gmu_power_config(struct kgsl_device *device) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; - - /* Configure registers for idle setting. The setting is cumulative */ - - /* Disable GMU WB/RB buffer and caches at boot */ - kgsl_gmu_regwrite(device, A6XX_GMU_SYS_BUS_CONFIG, 0x1); - kgsl_gmu_regwrite(device, A6XX_GMU_ICACHE_CONFIG, 0x1); - kgsl_gmu_regwrite(device, A6XX_GMU_DCACHE_CONFIG, 0x1); - - kgsl_gmu_regwrite(device, - A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0x9C40400); - - switch (gmu->idle_level) { - case GPU_HW_MIN_VOLT: - kgsl_gmu_regrmw(device, A6XX_GMU_RPMH_CTRL, 0, - MIN_BW_ENABLE_MASK); - kgsl_gmu_regrmw(device, A6XX_GMU_RPMH_HYST_CTRL, 0, - MIN_BW_HYST); - /* fall through */ - case GPU_HW_NAP: - kgsl_gmu_regrmw(device, A6XX_GMU_GPU_NAP_CTRL, 0, - HW_NAP_ENABLE_MASK); - /* fall through */ - case GPU_HW_IFPC: - kgsl_gmu_regwrite(device, A6XX_GMU_PWR_COL_INTER_FRAME_HYST, - GMU_PWR_COL_HYST); - kgsl_gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0, - IFPC_ENABLE_MASK); - /* fall through */ - case GPU_HW_SPTP_PC: - kgsl_gmu_regwrite(device, A6XX_GMU_PWR_COL_SPTPRAC_HYST, - GMU_PWR_COL_HYST); - kgsl_gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0, - SPTP_ENABLE_MASK); - /* fall through */ - default: - break; - } - - /* ACD feature enablement */ - if (ADRENO_FEATURE(adreno_dev, ADRENO_LM) && - test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) - kgsl_gmu_regrmw(device, A6XX_GMU_BOOT_KMD_LM_HANDSHAKE, 0, - BIT(10)); - - /* Enable RPMh GPU client */ - if (ADRENO_FEATURE(adreno_dev, ADRENO_RPMH)) - kgsl_gmu_regrmw(device, A6XX_GMU_RPMH_CTRL, 0, - RPMH_ENABLE_MASK); -} - -/* - * a6xx_gmu_start() - Start GMU and wait until FW boot up. - * @device: Pointer to KGSL device - */ -static int a6xx_gmu_start(struct kgsl_device *device) -{ - struct gmu_device *gmu = &device->gmu; - - kgsl_regwrite(device, A6XX_GMU_CX_GMU_WFI_CONFIG, 0x0); - /* Write 1 first to make sure the GMU is reset */ - kgsl_gmu_regwrite(device, A6XX_GMU_CM3_SYSRESET, 1); - - /* Make sure putting in reset doesn't happen after clearing */ - wmb(); - - /* Bring GMU out of reset */ - kgsl_gmu_regwrite(device, A6XX_GMU_CM3_SYSRESET, 0); - if (timed_poll_check(device, - A6XX_GMU_CM3_FW_INIT_RESULT, - 0xBABEFACE, - GMU_START_TIMEOUT, - 0xFFFFFFFF)) { - dev_err(&gmu->pdev->dev, "GMU doesn't boot\n"); - return -ETIMEDOUT; - } - - return 0; -} - -/* - * a6xx_gmu_hfi_start() - Write registers and start HFI. - * @device: Pointer to KGSL device - */ -static int a6xx_gmu_hfi_start(struct kgsl_device *device) -{ - struct gmu_device *gmu = &device->gmu; - - kgsl_gmu_regrmw(device, A6XX_GMU_GMU2HOST_INTR_MASK, - HFI_IRQ_MSGQ_MASK, 0); - kgsl_gmu_regwrite(device, A6XX_GMU_HFI_CTRL_INIT, 1); - - if (timed_poll_check(device, - A6XX_GMU_HFI_CTRL_STATUS, - BIT(0), - GMU_START_TIMEOUT, - BIT(0))) { - dev_err(&gmu->pdev->dev, "GMU HFI init failed\n"); - return -ETIMEDOUT; - } - - return 0; -} - -/* - * a6xx_oob_set() - Set OOB interrupt to GMU. - * @adreno_dev: Pointer to adreno device - * @req: Which of the OOB bits to request - */ -static int a6xx_oob_set(struct adreno_device *adreno_dev, - enum oob_request req) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - int ret = 0; - int set, check; - - if (!kgsl_gmu_isenabled(device)) - return 0; - - if (adreno_is_a640(adreno_dev)) { - set = BIT(30 - req * 2); - check = BIT(31 - req); - - if ((device->gmu.hfi.version & 0x1F) == 0) { - /* LEGACY for intermediate oobs */ - set = BIT(req + 16); - check = BIT(req + 16); - } - - if (req >= 6) { - dev_err(&device->gmu.pdev->dev, - "OOB_set(0x%x) invalid\n", set); - return -EINVAL; - } - } else { - set = BIT(req + 16); - check = BIT(req + 24); - } - - kgsl_gmu_regwrite(device, A6XX_GMU_HOST2GMU_INTR_SET, set); - - if (timed_poll_check(device, - A6XX_GMU_GMU2HOST_INTR_INFO, - check, - GPU_START_TIMEOUT, - check)) { - ret = -ETIMEDOUT; - dev_err(&device->gmu.pdev->dev, - "OOB_set(0x%x) timed out\n", set); - } - - kgsl_gmu_regwrite(device, A6XX_GMU_GMU2HOST_INTR_CLR, check); - - trace_kgsl_gmu_oob_set(set); - return ret; -} - -/* - * a6xx_oob_clear() - Clear a previously set OOB request. - * @adreno_dev: Pointer to the adreno device that has the GMU - * @req: Which of the OOB bits to clear - */ -static inline void a6xx_oob_clear(struct adreno_device *adreno_dev, - enum oob_request req) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - int clear; - - if (!kgsl_gmu_isenabled(device)) - return; - - if (adreno_is_a640(adreno_dev)) { - clear = BIT(31 - req * 2); - if (req >= 6) { - dev_err(&device->gmu.pdev->dev, - "OOB_clear(0x%x) invalid\n", clear); - return; - } - /* LEGACY for intermediate oobs */ - if ((device->gmu.hfi.version & 0x1F) == 0) - clear = BIT(req + 24); - } else - clear = BIT(req + 24); - - kgsl_gmu_regwrite(device, A6XX_GMU_HOST2GMU_INTR_SET, clear); - trace_kgsl_gmu_oob_clear(clear); -} - /* * a6xx_gpu_keepalive() - GMU reg write to request GPU stays on * @adreno_dev: Pointer to the adreno device that has the GMU @@ -1620,638 +1195,19 @@ static inline void a6xx_gpu_keepalive(struct adreno_device *adreno_dev, ADRENO_REG_GMU_PWR_COL_KEEPALIVE, state); } -#define SPTPRAC_POWERON_CTRL_MASK 0x00778000 -#define SPTPRAC_POWEROFF_CTRL_MASK 0x00778001 -#define SPTPRAC_POWEROFF_STATUS_MASK BIT(2) -#define SPTPRAC_POWERON_STATUS_MASK BIT(3) -#define SPTPRAC_CTRL_TIMEOUT 10 /* ms */ -#define A6XX_RETAIN_FF_ENABLE_ENABLE_MASK BIT(11) - -/* - * a6xx_sptprac_enable() - Power on SPTPRAC - * @adreno_dev: Pointer to Adreno device - */ -static int a6xx_sptprac_enable(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - - if (!kgsl_gmu_gpmu_isenabled(device)) - return -EINVAL; - - if (!adreno_has_sptprac_gdsc(adreno_dev)) - return 0; - - kgsl_gmu_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, - SPTPRAC_POWERON_CTRL_MASK); - - if (timed_poll_check(device, - A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, - SPTPRAC_POWERON_STATUS_MASK, - SPTPRAC_CTRL_TIMEOUT, - SPTPRAC_POWERON_STATUS_MASK)) { - dev_err(&device->gmu.pdev->dev, "power on SPTPRAC fail\n"); - return -EINVAL; - } - - return 0; -} - -/* - * a6xx_sptprac_disable() - Power of SPTPRAC - * @adreno_dev: Pointer to Adreno device - */ -static void a6xx_sptprac_disable(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - - if (!adreno_has_sptprac_gdsc(adreno_dev)) - return; - - if (!kgsl_gmu_gpmu_isenabled(device)) - return; - - /* Ensure that retention is on */ - kgsl_gmu_regrmw(device, A6XX_GPU_CC_GX_GDSCR, 0, - A6XX_RETAIN_FF_ENABLE_ENABLE_MASK); - - kgsl_gmu_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, - SPTPRAC_POWEROFF_CTRL_MASK); - - if (timed_poll_check(device, - A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, - SPTPRAC_POWEROFF_STATUS_MASK, - SPTPRAC_CTRL_TIMEOUT, - SPTPRAC_POWEROFF_STATUS_MASK)) - dev_err(&device->gmu.pdev->dev, "power off SPTPRAC fail\n"); -} - -#define SPTPRAC_POWER_OFF BIT(2) -#define SP_CLK_OFF BIT(4) -#define GX_GDSC_POWER_OFF BIT(6) -#define GX_CLK_OFF BIT(7) -#define is_on(val) (!(val & (GX_GDSC_POWER_OFF | GX_CLK_OFF))) -/* - * a6xx_gx_is_on() - Check if GX is on using pwr status register - * @adreno_dev - Pointer to adreno_device - * This check should only be performed if the keepalive bit is set or it - * can be guaranteed that the power state of the GPU will remain unchanged - */ -static bool a6xx_gx_is_on(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int val; - - if (!kgsl_gmu_isenabled(device)) - return true; - - kgsl_gmu_regread(device, A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, &val); - return is_on(val); -} - -/* - * a6xx_sptprac_is_on() - Check if SPTP is on using pwr status register - * @adreno_dev - Pointer to adreno_device - * This check should only be performed if the keepalive bit is set or it - * can be guaranteed that the power state of the GPU will remain unchanged - */ -static bool a6xx_sptprac_is_on(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int val; - - if (!kgsl_gmu_isenabled(device) || !adreno_has_sptprac_gdsc(adreno_dev)) - return true; - - kgsl_gmu_regread(device, A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, &val); - return !(val & (SPTPRAC_POWER_OFF | SP_CLK_OFF)); -} - -/* - * a6xx_gfx_rail_on() - request GMU to power GPU at given OPP. - * @device: Pointer to KGSL device - * - */ -static int a6xx_gfx_rail_on(struct kgsl_device *device) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - struct gmu_device *gmu = &device->gmu; - unsigned int perf_idx = pwr->num_pwrlevels - pwr->default_pwrlevel - 1; - uint32_t default_opp = gmu->rpmh_votes.gx_votes[perf_idx]; - - kgsl_gmu_regwrite(device, A6XX_GMU_BOOT_SLUMBER_OPTION, - OOB_BOOT_OPTION); - kgsl_gmu_regwrite(device, A6XX_GMU_GX_VOTE_IDX, - ARC_VOTE_GET_PRI(default_opp)); - kgsl_gmu_regwrite(device, A6XX_GMU_MX_VOTE_IDX, - ARC_VOTE_GET_SEC(default_opp)); - - return a6xx_oob_set(adreno_dev, oob_boot_slumber); -} - -#define GMU_POWER_STATE_SLUMBER 15 - -/* - * a6xx_notify_slumber() - initiate request to GMU to prepare to slumber - * @device: Pointer to KGSL device - */ -static int a6xx_notify_slumber(struct kgsl_device *device) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - struct gmu_device *gmu = &device->gmu; - int bus_level = pwr->pwrlevels[pwr->default_pwrlevel].bus_freq; - int perf_idx = gmu->num_gpupwrlevels - pwr->default_pwrlevel - 1; - int ret, state; - - /* Disable the power counter so that the GMU is not busy */ - kgsl_gmu_regwrite(device, A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0); - - /* Turn off SPTPRAC if we own it */ - if (gmu->idle_level < GPU_HW_SPTP_PC) - a6xx_sptprac_disable(adreno_dev); - - if (!ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) { - struct hfi_prep_slumber_cmd req = { - .freq = perf_idx, - .bw = bus_level, - }; - - ret = hfi_send_req(gmu, H2F_MSG_PREPARE_SLUMBER, &req); - goto out; - } - - kgsl_gmu_regwrite(device, A6XX_GMU_BOOT_SLUMBER_OPTION, - OOB_SLUMBER_OPTION); - kgsl_gmu_regwrite(device, A6XX_GMU_GX_VOTE_IDX, perf_idx); - kgsl_gmu_regwrite(device, A6XX_GMU_MX_VOTE_IDX, bus_level); - - ret = a6xx_oob_set(adreno_dev, oob_boot_slumber); - a6xx_oob_clear(adreno_dev, oob_boot_slumber); - - if (!ret) { - kgsl_gmu_regread(device, - A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE, &state); - if (state != GPU_HW_SLUMBER) { - dev_err(&gmu->pdev->dev, - "Failed to prepare for slumber: 0x%x\n", - state); - ret = -EINVAL; - } - } - -out: - /* Make sure the fence is in ALLOW mode */ - kgsl_gmu_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0); - return ret; -} - -static int a6xx_rpmh_power_on_gpu(struct kgsl_device *device) -{ - struct gmu_device *gmu = &device->gmu; - struct device *dev = &gmu->pdev->dev; - int val; - - kgsl_gmu_regread(device, A6XX_GPU_CC_GX_DOMAIN_MISC, &val); - if (!(val & 0x1)) - dev_err_ratelimited(&gmu->pdev->dev, - "GMEM CLAMP IO not set while GFX rail off\n"); - - /* RSC wake sequence */ - kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, BIT(1)); - - /* Write request before polling */ - wmb(); - - if (timed_poll_check(device, - A6XX_GMU_RSCC_CONTROL_ACK, - BIT(1), - GPU_START_TIMEOUT, - BIT(1))) { - dev_err(dev, "Failed to do GPU RSC power on\n"); - return -EINVAL; - } - - if (timed_poll_check(device, - A6XX_RSCC_SEQ_BUSY_DRV0, - 0, - GPU_START_TIMEOUT, - 0xFFFFFFFF)) - goto error_rsc; - - kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0); - - /* Enable the power counter because it was disabled before slumber */ - kgsl_gmu_regwrite(device, A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 1); - - return 0; -error_rsc: - dev_err(dev, "GPU RSC sequence stuck in waking up GPU\n"); - return -EINVAL; -} - -static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device) -{ - struct gmu_device *gmu = &device->gmu; - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - int ret; - - /* RSC sleep sequence is different on v1 */ - if (adreno_is_a630v1(adreno_dev)) - kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1); - - kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 1); - /* Make sure the request completes before continuing */ - wmb(); - - if (adreno_is_a630v1(adreno_dev)) - ret = timed_poll_check(device, - A6XX_RSCC_TIMESTAMP_UNIT1_OUTPUT_DRV0, - BIT(0), - GPU_START_TIMEOUT, - BIT(0)); - else - ret = timed_poll_check(device, - A6XX_GPU_RSCC_RSC_STATUS0_DRV0, - BIT(16), - GPU_START_TIMEOUT, - BIT(16)); - - if (ret) { - dev_err(&gmu->pdev->dev, "GPU RSC power off fail\n"); - return -ETIMEDOUT; - } - - /* Read to clear the timestamp valid signal. Don't care what we read. */ - if (adreno_is_a630v1(adreno_dev)) { - kgsl_gmu_regread(device, - A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_L_DRV0, - &ret); - kgsl_gmu_regread(device, - A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_H_DRV0, - &ret); - } - - kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0); - - if (ADRENO_FEATURE(adreno_dev, ADRENO_LM) && - test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) - kgsl_gmu_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 0); - - return 0; -} - -/* - * Gmu FW header format: - * <32-bit start addr> <32-bit size> <32-bit pad0> <32-bit pad1> - */ -#define GMU_FW_HEADER_SIZE 4 - -#define GMU_ITCM_VA_START 0x0 -#define GMU_ITCM_VA_END (GMU_ITCM_VA_START + 0x4000) /* 16 KB */ - -#define GMU_DTCM_VA_START 0x40000 -#define GMU_DTCM_VA_END (GMU_DTCM_VA_START + 0x4000) /* 16 KB */ - -#define GMU_ICACHE_VA_START 0x4000 -#define GMU_ICACHE_VA_END (GMU_ICACHE_VA_START + 0x3C000) /* 240 KB */ - -static int load_gmu_fw(struct kgsl_device *device) -{ - struct gmu_device *gmu = &device->gmu; - uint32_t *fwptr = gmu->fw_image->hostptr; - int i, j, ret; - int start_addr, size_in_bytes, num_dwords, tcm_slot, num_records; - - /* Allocates & maps memory for GMU cached instructions range */ - ret = allocate_gmu_cached_fw(gmu); - if (ret) - return ret; - - /* - * Read first record. pad0 field of first record contains - * number of records in the image. - */ - num_records = fwptr[2]; - for (i = 0; i < num_records; i++) { - start_addr = fwptr[0]; - size_in_bytes = fwptr[1]; - num_dwords = size_in_bytes / sizeof(uint32_t); - fwptr += GMU_FW_HEADER_SIZE; - - if ((start_addr >= GMU_ITCM_VA_START) && - (start_addr < GMU_ITCM_VA_END)) { - tcm_slot = start_addr / sizeof(uint32_t); - - for (j = 0; j < num_dwords; j++) - kgsl_gmu_regwrite(device, - A6XX_GMU_CM3_ITCM_START + tcm_slot + j, - fwptr[j]); - } else if ((start_addr >= GMU_DTCM_VA_START) && - (start_addr < GMU_DTCM_VA_END)) { - tcm_slot = (start_addr - GMU_DTCM_VA_START) - / sizeof(uint32_t); - - for (j = 0; j < num_dwords; j++) - kgsl_gmu_regwrite(device, - A6XX_GMU_CM3_DTCM_START + tcm_slot + j, - fwptr[j]); - } else if ((start_addr >= GMU_ICACHE_VA_START) && - (start_addr < GMU_ICACHE_VA_END)) { - if (!is_cached_fw_size_valid(size_in_bytes)) { - dev_err(&gmu->pdev->dev, - "GMU firmware size too big\n"); - return -EINVAL; - - } - memcpy(gmu->cached_fw_image.hostptr, - fwptr, size_in_bytes); - } - - fwptr += num_dwords; - } - - /* Proceed only after the FW is written */ - wmb(); - return 0; -} - - -/* - * a6xx_gmu_fw_start() - set up GMU and start FW - * @device: Pointer to KGSL device - * @boot_state: State of the GMU being started - */ -static int a6xx_gmu_fw_start(struct kgsl_device *device, - unsigned int boot_state) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; - struct gmu_memdesc *mem_addr = gmu->hfi_mem; - int ret; - unsigned int chipid = 0; - - switch (boot_state) { - case GMU_RESET: - /* fall through */ - case GMU_COLD_BOOT: - /* Turn on TCM retention */ - kgsl_gmu_regwrite(device, A6XX_GMU_GENERAL_7, 1); - - if (!test_and_set_bit(GMU_BOOT_INIT_DONE, &gmu->flags)) - _load_gmu_rpmh_ucode(device); - else if (boot_state != GMU_RESET) { - ret = a6xx_rpmh_power_on_gpu(device); - if (ret) - return ret; - } - - if (gmu->load_mode == TCM_BOOT) { - /* Load GMU image via AHB bus */ - ret = load_gmu_fw(device); - if (ret) - return ret; - } else { - dev_err(&gmu->pdev->dev, "Unsupported GMU load mode %d\n", - gmu->load_mode); - return -EINVAL; - } - break; - case GMU_WARM_BOOT: - ret = a6xx_rpmh_power_on_gpu(device); - if (ret) - return ret; - break; - default: - break; - } - - /* Clear init result to make sure we are getting fresh value */ - kgsl_gmu_regwrite(device, A6XX_GMU_CM3_FW_INIT_RESULT, 0); - kgsl_gmu_regwrite(device, A6XX_GMU_CM3_BOOT_CONFIG, gmu->load_mode); - - kgsl_gmu_regwrite(device, A6XX_GMU_HFI_QTBL_ADDR, - mem_addr->gmuaddr); - kgsl_gmu_regwrite(device, A6XX_GMU_HFI_QTBL_INFO, 1); - - kgsl_gmu_regwrite(device, A6XX_GMU_AHB_FENCE_RANGE_0, - FENCE_RANGE_MASK); - - /* Pass chipid to GMU FW, must happen before starting GMU */ - - /* Keep Core and Major bitfields unchanged */ - chipid = adreno_dev->chipid & 0xFFFF0000; - - /* - * Compress minor and patch version into 8 bits - * Bit 15-12: minor version - * Bit 11-8: patch version - */ - chipid = chipid | (ADRENO_CHIPID_MINOR(adreno_dev->chipid) << 12) - | (ADRENO_CHIPID_PATCH(adreno_dev->chipid) << 8); - - kgsl_gmu_regwrite(device, A6XX_GMU_HFI_SFR_ADDR, chipid); - init_gmu_log_base(device); - - /* Configure power control and bring the GMU out of reset */ - a6xx_gmu_power_config(device); - ret = a6xx_gmu_start(device); - if (ret) - return ret; - - if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) { - ret = a6xx_gfx_rail_on(device); - if (ret) { - a6xx_oob_clear(adreno_dev, oob_boot_slumber); - return ret; - } - } - - if (gmu->idle_level < GPU_HW_SPTP_PC) { - ret = a6xx_sptprac_enable(adreno_dev); - if (ret) - return ret; - } - - ret = a6xx_gmu_hfi_start(device); - if (ret) - return ret; - - /* Make sure the write to start HFI happens before sending a message */ - wmb(); - return ret; -} - -#define FREQ_VOTE(idx, ack) (((idx) & 0xFF) | (((ack) & 0xF) << 28)) -#define BW_VOTE(idx) ((((idx) & 0xFFF) << 12) | ((idx) & 0xFFF)) - -/* - * a6xx_gmu_dcvs_nohfi() - request GMU to do DCVS without using HFI - * @device: Pointer to KGSL device - * @perf_idx: Index into GPU performance level table defined in - * HFI DCVS table message - * @bw_idx: Index into GPU b/w table defined in HFI b/w table message - * - */ -static int a6xx_gmu_dcvs_nohfi(struct kgsl_device *device, - unsigned int perf_idx, unsigned int bw_idx) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - int ret; - - kgsl_gmu_regwrite(device, A6XX_GMU_DCVS_ACK_OPTION, DCVS_ACK_NONBLOCK); - - kgsl_gmu_regwrite(device, A6XX_GMU_DCVS_PERF_SETTING, - FREQ_VOTE(perf_idx, CLKSET_OPTION_ATLEAST)); - - kgsl_gmu_regwrite(device, A6XX_GMU_DCVS_BW_SETTING, BW_VOTE(bw_idx)); - - ret = a6xx_oob_set(adreno_dev, oob_dcvs); - if (ret == 0) - kgsl_gmu_regread(device, A6XX_GMU_DCVS_RETURN, &ret); - - a6xx_oob_clear(adreno_dev, oob_dcvs); - - return ret; -} - +/* Bitmask for GPU idle status check */ +#define GPUBUSYIGNAHB BIT(23) static bool a6xx_hw_isidle(struct adreno_device *adreno_dev) { unsigned int reg; - kgsl_gmu_regread(KGSL_DEVICE(adreno_dev), + gmu_core_regread(KGSL_DEVICE(adreno_dev), A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, ®); if (reg & GPUBUSYIGNAHB) return false; return true; } -static bool idle_trandition_complete(unsigned int idle_level, - unsigned int gmu_power_reg, - unsigned int sptprac_clk_reg) -{ - if (idle_level != gmu_power_reg) - return false; - - switch (idle_level) { - case GPU_HW_IFPC: - if (is_on(sptprac_clk_reg)) - return false; - break; - /* other GMU idle levels can be added here */ - case GPU_HW_ACTIVE: - default: - break; - } - return true; -} - -static int a6xx_wait_for_lowest_idle(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct gmu_device *gmu = &device->gmu; - unsigned int reg, reg1; - unsigned long t; - uint64_t ts1, ts2, ts3; - - if (!kgsl_gmu_isenabled(device)) - return 0; - - ts1 = read_AO_counter(device); - - t = jiffies + msecs_to_jiffies(GMU_IDLE_TIMEOUT); - do { - kgsl_gmu_regread(device, - A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE, ®); - kgsl_gmu_regread(device, - A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, ®1); - - if (idle_trandition_complete(gmu->idle_level, reg, reg1)) - return 0; - /* Wait 100us to reduce unnecessary AHB bus traffic */ - usleep_range(10, 100); - } while (!time_after(jiffies, t)); - - ts2 = read_AO_counter(device); - /* Check one last time */ - - kgsl_gmu_regread(device, A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE, ®); - kgsl_gmu_regread(device, A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, ®1); - - if (idle_trandition_complete(gmu->idle_level, reg, reg1)) - return 0; - - ts3 = read_AO_counter(device); - WARN(1, "Timeout waiting for lowest idle: %08x %llx %llx %llx %x\n", - reg, ts1, ts2, ts3, reg1); - - return -ETIMEDOUT; -} - -static int a6xx_wait_for_gmu_idle(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct gmu_device *gmu = &device->gmu; - unsigned int status2; - uint64_t ts1; - - ts1 = read_AO_counter(device); - if (timed_poll_check(device, A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, - 0, GMU_START_TIMEOUT, CXGXCPUBUSYIGNAHB)) { - kgsl_gmu_regread(device, - A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS2, &status2); - dev_err(&gmu->pdev->dev, - "GMU not idling: status2=0x%x %llx %llx\n", - status2, ts1, read_AO_counter(device)); - return -ETIMEDOUT; - } - - return 0; -} - -/* - * _load_gmu_firmware() - Load the ucode into the GPMU RAM & PDC/RSC - * @device: Pointer to KGSL device - */ -static int _load_gmu_firmware(struct kgsl_device *device) -{ - const struct firmware *fw = NULL; - const struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; - const struct adreno_gpu_core *gpucore = adreno_dev->gpucore; - int image_size, ret = -EINVAL; - - /* there is no GMU */ - if (!kgsl_gmu_isenabled(device)) - return 0; - - /* GMU fw already saved and verified so do nothing new */ - if (gmu->fw_image) - return 0; - - if (gpucore->gpmufw_name == NULL) - return -EINVAL; - - ret = request_firmware(&fw, gpucore->gpmufw_name, device->dev); - if (ret || fw == NULL) { - KGSL_CORE_ERR("request_firmware (%s) failed: %d\n", - gpucore->gpmufw_name, ret); - return ret; - } - - image_size = PAGE_ALIGN(fw->size); - - ret = allocate_gmu_image(gmu, image_size); - - /* load into shared memory with GMU */ - if (!ret) - memcpy(gmu->fw_image->hostptr, fw->data, fw->size); - - release_firmware(fw); - - return ret; -} - /* * a6xx_microcode_read() - Read microcode * @adreno_dev: Pointer to adreno device @@ -2260,6 +1216,7 @@ static int a6xx_microcode_read(struct adreno_device *adreno_dev) { int ret; struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); struct adreno_firmware *sqe_fw = ADRENO_FW(adreno_dev, ADRENO_FW_SQE); if (sqe_fw->memdesc.hostptr == NULL) { @@ -2269,7 +1226,7 @@ static int a6xx_microcode_read(struct adreno_device *adreno_dev) return ret; } - return _load_gmu_firmware(device); + return gmu_dev_ops->load_firmware(device); } static int a6xx_soft_reset(struct adreno_device *adreno_dev) @@ -2283,7 +1240,7 @@ static int a6xx_soft_reset(struct adreno_device *adreno_dev) * For the soft reset case with GMU enabled this part is done * by the GMU firmware */ - if (kgsl_gmu_isenabled(device) && + if (gmu_core_isenabled(device) && !test_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv)) return 0; @@ -2314,60 +1271,11 @@ static int a6xx_soft_reset(struct adreno_device *adreno_dev) /* Clear GBIF client halt and CX arbiter halt */ adreno_deassert_gbif_halt(adreno_dev); - a6xx_sptprac_enable(adreno_dev); - - return 0; -} - -#define A6XX_STATE_OF_CHILD (BIT(4) | BIT(5)) -#define A6XX_IDLE_FULL_LLM BIT(0) -#define A6XX_WAKEUP_ACK BIT(1) -#define A6XX_IDLE_FULL_ACK BIT(0) -#define A6XX_VBIF_XIN_HALT_CTRL1_ACKS (BIT(0) | BIT(1) | BIT(2) | BIT(3)) - -static void a6xx_isense_disable(struct kgsl_device *device) -{ - unsigned int val; - const struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - - if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM) || - !test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) - return; - - kgsl_gmu_regread(device, A6XX_GPU_CS_ENABLE_REG, &val); - if (val) { - kgsl_gmu_regwrite(device, A6XX_GPU_CS_ENABLE_REG, 0); - kgsl_gmu_regwrite(device, A6XX_GMU_ISENSE_CTRL, 0); - } -} - -static int a6xx_llm_glm_handshake(struct kgsl_device *device) -{ - unsigned int val; - const struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; - - if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM) || - !test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) - return 0; - - kgsl_gmu_regread(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, &val); - if (!(val & A6XX_STATE_OF_CHILD)) { - kgsl_gmu_regrmw(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, 0, BIT(4)); - kgsl_gmu_regrmw(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, 0, - A6XX_IDLE_FULL_LLM); - if (timed_poll_check(device, A6XX_GMU_LLM_GLM_SLEEP_STATUS, - A6XX_IDLE_FULL_ACK, GPU_RESET_TIMEOUT, - A6XX_IDLE_FULL_ACK)) { - dev_err(&gmu->pdev->dev, "LLM-GLM handshake failed\n"); - return -EINVAL; - } - } + a6xx_gmu_sptprac_enable(adreno_dev); return 0; } - static void a6xx_count_throttles(struct adreno_device *adreno_dev, uint64_t adj) { @@ -2375,122 +1283,11 @@ static void a6xx_count_throttles(struct adreno_device *adreno_dev, !test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) return; - kgsl_gmu_regread(KGSL_DEVICE(adreno_dev), + gmu_core_regread(KGSL_DEVICE(adreno_dev), adreno_dev->lm_threshold_count, &adreno_dev->lm_threshold_cross); } -static int a6xx_complete_rpmh_votes(struct kgsl_device *device) -{ - int ret = 0; - - if (!kgsl_gmu_isenabled(device)) - return ret; - - ret |= timed_poll_check(device, A6XX_RSCC_TCS0_DRV0_STATUS, BIT(0), - GPU_RESET_TIMEOUT, BIT(0)); - ret |= timed_poll_check(device, A6XX_RSCC_TCS1_DRV0_STATUS, BIT(0), - GPU_RESET_TIMEOUT, BIT(0)); - ret |= timed_poll_check(device, A6XX_RSCC_TCS2_DRV0_STATUS, BIT(0), - GPU_RESET_TIMEOUT, BIT(0)); - ret |= timed_poll_check(device, A6XX_RSCC_TCS3_DRV0_STATUS, BIT(0), - GPU_RESET_TIMEOUT, BIT(0)); - - return ret; -} - -static int a6xx_gmu_suspend(struct kgsl_device *device) -{ - /* Max GX clients on A6xx is 2: GMU and KMD */ - int ret = 0, max_client_num = 2; - struct gmu_device *gmu = &device->gmu; - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - - /* do it only if LM feature is enabled */ - /* Disable ISENSE if it's on */ - a6xx_isense_disable(device); - - /* LLM-GLM handshake sequence */ - a6xx_llm_glm_handshake(device); - - /* If SPTP_RAC is on, turn off SPTP_RAC HS */ - a6xx_sptprac_disable(adreno_dev); - - /* Disconnect GPU from BUS is not needed if CX GDSC goes off later */ - - /* Check no outstanding RPMh voting */ - a6xx_complete_rpmh_votes(device); - - if (gmu->gx_gdsc) { - if (regulator_is_enabled(gmu->gx_gdsc)) { - /* Switch gx gdsc control from GMU to CPU - * force non-zero reference count in clk driver - * so next disable call will turn - * off the GDSC - */ - ret = regulator_enable(gmu->gx_gdsc); - if (ret) - dev_err(&gmu->pdev->dev, - "suspend fail: gx enable\n"); - - while ((max_client_num)) { - ret = regulator_disable(gmu->gx_gdsc); - if (!regulator_is_enabled(gmu->gx_gdsc)) - break; - max_client_num -= 1; - } - - if (!max_client_num) - dev_err(&gmu->pdev->dev, - "suspend fail: cannot disable gx\n"); - } - } - - return ret; -} - -/* - * a6xx_rpmh_gpu_pwrctrl() - GPU power control via RPMh/GMU interface - * @adreno_dev: Pointer to adreno device - * @mode: requested power mode - * @arg1: first argument for mode control - * @arg2: second argument for mode control - */ -static int a6xx_rpmh_gpu_pwrctrl(struct adreno_device *adreno_dev, - unsigned int mode, unsigned int arg1, unsigned int arg2) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct gmu_device *gmu = &device->gmu; - int ret; - - switch (mode) { - case GMU_FW_START: - ret = a6xx_gmu_fw_start(device, arg1); - break; - case GMU_SUSPEND: - ret = a6xx_gmu_suspend(device); - break; - case GMU_FW_STOP: - if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) - a6xx_oob_clear(adreno_dev, oob_boot_slumber); - ret = a6xx_rpmh_power_off_gpu(device); - break; - case GMU_DCVS_NOHFI: - ret = a6xx_gmu_dcvs_nohfi(device, arg1, arg2); - break; - case GMU_NOTIFY_SLUMBER: - ret = a6xx_notify_slumber(device); - break; - default: - dev_err(&gmu->pdev->dev, - "unsupported GMU power ctrl mode:%d\n", mode); - ret = -EINVAL; - break; - } - - return ret; -} - /** * a6xx_reset() - Helper function to reset the GPU * @device: Pointer to the KGSL device structure for the GPU @@ -2507,7 +1304,7 @@ static int a6xx_reset(struct kgsl_device *device, int fault) int i = 0; /* Use the regular reset sequence for No GMU */ - if (!kgsl_gmu_isenabled(device)) + if (!gmu_core_isenabled(device)) return adreno_reset(device, fault); /* Transition from ACTIVE to RESET state */ @@ -2645,17 +1442,24 @@ static void a6xx_llc_configure_gpu_scid(struct adreno_device *adreno_dev) uint32_t gpu_scid; uint32_t gpu_cntl1_val = 0; int i; - void __iomem *gpu_cx_reg; gpu_scid = adreno_llc_get_scid(adreno_dev->gpu_llc_slice); for (i = 0; i < A6XX_LLC_NUM_GPU_SCIDS; i++) gpu_cntl1_val = (gpu_cntl1_val << A6XX_GPU_LLC_SCID_NUM_BITS) | gpu_scid; - gpu_cx_reg = ioremap(A6XX_GPU_CX_REG_BASE, A6XX_GPU_CX_REG_SIZE); - _reg_rmw(gpu_cx_reg + A6XX_GPU_CX_MISC_SYSTEM_CACHE_CNTL_1, + if (adreno_is_a640(adreno_dev)) { + kgsl_regrmw(KGSL_DEVICE(adreno_dev), A6XX_GBIF_SCACHE_CNTL1, A6XX_GPU_LLC_SCID_MASK, gpu_cntl1_val); - iounmap(gpu_cx_reg); + } else { + void __iomem *gpu_cx_reg; + + gpu_cx_reg = ioremap(A6XX_GPU_CX_REG_BASE, + A6XX_GPU_CX_REG_SIZE); + _reg_rmw(gpu_cx_reg + A6XX_GPU_CX_MISC_SYSTEM_CACHE_CNTL_1, + A6XX_GPU_LLC_SCID_MASK, gpu_cntl1_val); + iounmap(gpu_cx_reg); + } } /* @@ -2667,6 +1471,13 @@ static void a6xx_llc_configure_gpuhtw_scid(struct adreno_device *adreno_dev) uint32_t gpuhtw_scid; void __iomem *gpu_cx_reg; + /* + * On A640, the GPUHTW SCID is configured via a NoC override in the + * XBL image. + */ + if (adreno_is_a640(adreno_dev)) + return; + gpuhtw_scid = adreno_llc_get_scid(adreno_dev->gpuhtw_llc_slice); gpu_cx_reg = ioremap(A6XX_GPU_CX_REG_BASE, A6XX_GPU_CX_REG_SIZE); @@ -2684,6 +1495,13 @@ static void a6xx_llc_enable_overrides(struct adreno_device *adreno_dev) { void __iomem *gpu_cx_reg; + /* + * Attributes override through GBIF is not supported with MMU-500. + * Attributes are used as configured through SMMU pagetable entries. + */ + if (adreno_is_a640(adreno_dev)) + return; + /* * 0x3: readnoallocoverrideen=0 * read-no-alloc=0 - Allocate lines on read miss @@ -3605,11 +2423,17 @@ static struct adreno_perfcount_register a6xx_pwrcounters_gpmu[] = { A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_3_L, A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_3_H, -1, A6XX_GMU_CX_GMU_POWER_COUNTER_SELECT_0, }, - { KGSL_PERFCOUNTER_NOT_USED, 0, 0, + /* + * Both A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4 and + * A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_5 are owned + * by the GMU. Mark them as broken so there is no + * dual ownership. + */ + { KGSL_PERFCOUNTER_BROKEN, 0, 0, A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L, A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_H, -1, A6XX_GMU_CX_GMU_POWER_COUNTER_SELECT_1, }, - { KGSL_PERFCOUNTER_NOT_USED, 0, 0, + { KGSL_PERFCOUNTER_BROKEN, 0, 0, A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_5_L, A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_5_H, -1, A6XX_GMU_CX_GMU_POWER_COUNTER_SELECT_1, }, @@ -3678,7 +2502,7 @@ static int a6xx_enable_pwr_counters(struct adreno_device *adreno_dev, if (counter == 0) return -EINVAL; - if (!device->gmu.pdev) + if (!gmu_core_isenabled(device)) return -ENODEV; kgsl_regwrite(device, A6XX_GPU_GMU_AO_GPU_CX_BUSY_MASK, 0xFF000000); @@ -3895,6 +2719,8 @@ static unsigned int a6xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { A6XX_GMU_NMI_CONTROL_STATUS), ADRENO_REG_DEFINE(ADRENO_REG_GMU_CM3_CFG, A6XX_GMU_CM3_CFG), + ADRENO_REG_DEFINE(ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS, + A6XX_GMU_RBBM_INT_UNMASKED_STATUS), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TRUST_CONTROL, A6XX_RBBM_SECVID_TRUST_CNTL), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE, @@ -3979,7 +2805,7 @@ struct adreno_gpudev adreno_a6xx_gpudev = { .reg_offsets = &a6xx_reg_offsets, .start = a6xx_start, .snapshot = a6xx_snapshot, - .snapshot_gmu = a6xx_snapshot_gmu, + .snapshot_debugbus = a6xx_snapshot_debugbus, .irq = &a6xx_irq, .snapshot_data = &a6xx_snapshot_data, .irq_trace = trace_kgsl_a5xx_irq_status, @@ -3987,8 +2813,8 @@ struct adreno_gpudev adreno_a6xx_gpudev = { .platform_setup = a6xx_platform_setup, .init = a6xx_init, .rb_start = a6xx_rb_start, - .regulator_enable = a6xx_sptprac_enable, - .regulator_disable = a6xx_sptprac_disable, + .regulator_enable = a6xx_gmu_sptprac_enable, + .regulator_disable = a6xx_gmu_sptprac_disable, .perfcounters = &a6xx_perfcounters, .enable_pwr_counters = a6xx_enable_pwr_counters, .count_throttles = a6xx_count_throttles, @@ -3997,13 +2823,8 @@ struct adreno_gpudev adreno_a6xx_gpudev = { .llc_configure_gpu_scid = a6xx_llc_configure_gpu_scid, .llc_configure_gpuhtw_scid = a6xx_llc_configure_gpuhtw_scid, .llc_enable_overrides = a6xx_llc_enable_overrides, - .oob_set = a6xx_oob_set, - .oob_clear = a6xx_oob_clear, .gpu_keepalive = a6xx_gpu_keepalive, - .rpmh_gpu_pwrctrl = a6xx_rpmh_gpu_pwrctrl, .hw_isidle = a6xx_hw_isidle, /* Replaced by NULL if GMU is disabled */ - .wait_for_lowest_idle = a6xx_wait_for_lowest_idle, - .wait_for_gmu_idle = a6xx_wait_for_gmu_idle, .iommu_fault_block = a6xx_iommu_fault_block, .reset = a6xx_reset, .soft_reset = a6xx_soft_reset, @@ -4014,8 +2835,8 @@ struct adreno_gpudev adreno_a6xx_gpudev = { .set_marker = a6xx_set_marker, .preemption_context_init = a6xx_preemption_context_init, .preemption_context_destroy = a6xx_preemption_context_destroy, - .gx_is_on = a6xx_gx_is_on, - .sptprac_is_on = a6xx_sptprac_is_on, + .gx_is_on = a6xx_gmu_gx_is_on, + .sptprac_is_on = a6xx_gmu_sptprac_is_on, .ccu_invalidate = a6xx_ccu_invalidate, .perfcounter_update = a6xx_perfcounter_update, .coresight = {&a6xx_coresight, &a6xx_coresight_cx}, diff --git a/drivers/gpu/msm/adreno_a6xx.h b/drivers/gpu/msm/adreno_a6xx.h index bf1111c6204cf16022cc5edec5f5233945006530..c40dcccf0bf8981ebc023198b5ba4acce92d916d 100644 --- a/drivers/gpu/msm/adreno_a6xx.h +++ b/drivers/gpu/msm/adreno_a6xx.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -129,8 +129,13 @@ void a6xx_preemption_context_destroy(struct kgsl_context *context); void a6xx_snapshot(struct adreno_device *adreno_dev, struct kgsl_snapshot *snapshot); -void a6xx_snapshot_gmu(struct adreno_device *adreno_dev, +void a6xx_snapshot_debugbus(struct adreno_device *adreno_dev, + struct kgsl_snapshot *snapshot); +void a6xx_gmu_snapshot(struct adreno_device *adreno_dev, struct kgsl_snapshot *snapshot); - void a6xx_crashdump_init(struct adreno_device *adreno_dev); +int a6xx_gmu_sptprac_enable(struct adreno_device *adreno_dev); +void a6xx_gmu_sptprac_disable(struct adreno_device *adreno_dev); +bool a6xx_gmu_gx_is_on(struct adreno_device *adreno_dev); +bool a6xx_gmu_sptprac_is_on(struct adreno_device *adreno_dev); #endif diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c new file mode 100644 index 0000000000000000000000000000000000000000..e12a8b96258d784cf6709cef19c2689788cd60f5 --- /dev/null +++ b/drivers/gpu/msm/adreno_a6xx_gmu.c @@ -0,0 +1,1444 @@ +/* Copyright (c) 2018, 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 +#include +#include +#include + +#include "kgsl_gmu_core.h" +#include "kgsl_gmu.h" +#include "kgsl_trace.h" + +#include "adreno.h" +#include "a6xx_reg.h" +#include "adreno_a6xx.h" +#include "adreno_snapshot.h" +#include "adreno_trace.h" + +static const unsigned int a6xx_gmu_gx_registers[] = { + /* GMU GX */ + 0x1A800, 0x1A800, 0x1A810, 0x1A813, 0x1A816, 0x1A816, 0x1A818, 0x1A81B, + 0x1A81E, 0x1A81E, 0x1A820, 0x1A823, 0x1A826, 0x1A826, 0x1A828, 0x1A82B, + 0x1A82E, 0x1A82E, 0x1A830, 0x1A833, 0x1A836, 0x1A836, 0x1A838, 0x1A83B, + 0x1A83E, 0x1A83E, 0x1A840, 0x1A843, 0x1A846, 0x1A846, 0x1A880, 0x1A884, + 0x1A900, 0x1A92B, 0x1A940, 0x1A940, +}; + +static const unsigned int a6xx_gmu_registers[] = { + /* GMU TCM */ + 0x1B400, 0x1C3FF, 0x1C400, 0x1D3FF, + /* GMU CX */ + 0x1F400, 0x1F407, 0x1F410, 0x1F412, 0x1F500, 0x1F500, 0x1F507, 0x1F50A, + 0x1F800, 0x1F804, 0x1F807, 0x1F808, 0x1F80B, 0x1F80C, 0x1F80F, 0x1F81C, + 0x1F824, 0x1F82A, 0x1F82D, 0x1F830, 0x1F840, 0x1F853, 0x1F887, 0x1F889, + 0x1F8A0, 0x1F8A2, 0x1F8A4, 0x1F8AF, 0x1F8C0, 0x1F8C3, 0x1F8D0, 0x1F8D0, + 0x1F8E4, 0x1F8E4, 0x1F8E8, 0x1F8EC, 0x1F900, 0x1F903, 0x1F940, 0x1F940, + 0x1F942, 0x1F944, 0x1F94C, 0x1F94D, 0x1F94F, 0x1F951, 0x1F954, 0x1F954, + 0x1F957, 0x1F958, 0x1F95D, 0x1F95D, 0x1F962, 0x1F962, 0x1F964, 0x1F965, + 0x1F980, 0x1F986, 0x1F990, 0x1F99E, 0x1F9C0, 0x1F9C0, 0x1F9C5, 0x1F9CC, + 0x1F9E0, 0x1F9E2, 0x1F9F0, 0x1F9F0, 0x1FA00, 0x1FA01, + /* GPU RSCC */ + 0x2348C, 0x2348C, 0x23501, 0x23502, 0x23740, 0x23742, 0x23744, 0x23747, + 0x2374C, 0x23787, 0x237EC, 0x237EF, 0x237F4, 0x2382F, 0x23894, 0x23897, + 0x2389C, 0x238D7, 0x2393C, 0x2393F, 0x23944, 0x2397F, + /* GMU AO */ + 0x23B00, 0x23B16, 0x23C00, 0x23C00, + /* GPU CC */ + 0x24000, 0x24012, 0x24040, 0x24052, 0x24400, 0x24404, 0x24407, 0x2440B, + 0x24415, 0x2441C, 0x2441E, 0x2442D, 0x2443C, 0x2443D, 0x2443F, 0x24440, + 0x24442, 0x24449, 0x24458, 0x2445A, 0x24540, 0x2455E, 0x24800, 0x24802, + 0x24C00, 0x24C02, 0x25400, 0x25402, 0x25800, 0x25802, 0x25C00, 0x25C02, + 0x26000, 0x26002, + /* GPU CC ACD */ + 0x26400, 0x26416, 0x26420, 0x26427, +}; + +#define RSC_CMD_OFFSET 2 +#define PDC_CMD_OFFSET 4 + +static void _regwrite(void __iomem *regbase, + unsigned int offsetwords, unsigned int value) +{ + void __iomem *reg; + + reg = regbase + (offsetwords << 2); + __raw_writel(value, reg); +} + +/* + * _load_gmu_rpmh_ucode() - Load the ucode into the GPU PDC/RSC blocks + * PDC and RSC execute GPU power on/off RPMh sequence + * @device: Pointer to KGSL device + */ +static void _load_gmu_rpmh_ucode(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + /* Disable SDE clock gating */ + gmu_core_regwrite(device, A6XX_GPU_RSCC_RSC_STATUS0_DRV0, BIT(24)); + + /* Setup RSC PDC handshake for sleep and wakeup */ + gmu_core_regwrite(device, A6XX_RSCC_PDC_SLAVE_ID_DRV0, 1); + gmu_core_regwrite(device, A6XX_RSCC_HIDDEN_TCS_CMD0_DATA, 0); + gmu_core_regwrite(device, A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR, 0); + gmu_core_regwrite(device, + A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + RSC_CMD_OFFSET, 0); + gmu_core_regwrite(device, + A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + RSC_CMD_OFFSET, 0); + gmu_core_regwrite(device, + A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + RSC_CMD_OFFSET * 2, + 0x80000000); + gmu_core_regwrite(device, + A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + RSC_CMD_OFFSET * 2, + 0); + gmu_core_regwrite(device, A6XX_RSCC_OVERRIDE_START_ADDR, 0); + gmu_core_regwrite(device, A6XX_RSCC_PDC_SEQ_START_ADDR, 0x4520); + gmu_core_regwrite(device, A6XX_RSCC_PDC_MATCH_VALUE_LO, 0x4510); + gmu_core_regwrite(device, A6XX_RSCC_PDC_MATCH_VALUE_HI, 0x4514); + + /* Enable timestamp event for v1 only */ + if (adreno_is_a630v1(adreno_dev)) + gmu_core_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1); + + /* Load RSC sequencer uCode for sleep and wakeup */ + gmu_core_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0, 0xA7A506A0); + gmu_core_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 1, 0xA1E6A6E7); + gmu_core_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 2, 0xA2E081E1); + gmu_core_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 3, 0xE9A982E2); + gmu_core_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 4, 0x0020E8A8); + + /* Load PDC sequencer uCode for power up and power down sequence */ + _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0, 0xFEBEA1E1); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 1, 0xA5A4A3A2); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 2, 0x8382A6E0); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 3, 0xBCE3E284); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 4, 0x002081FC); + + /* Set TCS commands used by PDC sequence for low power modes */ + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_ENABLE_BANK, 7); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_WAIT_FOR_CMPL_BANK, 0); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CONTROL, 0); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_MSGID, 0x10108); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_ADDR, 0x30010); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_DATA, 1); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET, 0x0); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x0); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_ENABLE_BANK, 7); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_WAIT_FOR_CMPL_BANK, 0); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CONTROL, 0); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_MSGID, 0x10108); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_ADDR, 0x30010); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_DATA, 2); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET, 0x3); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080); + _regwrite(gmu->pdc_reg_virt, + PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x3); + + /* Setup GPU PDC */ + _regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_START_ADDR, 0); + _regwrite(gmu->pdc_reg_virt, PDC_GPU_ENABLE_PDC, 0x80000001); + + /* ensure no writes happen before the uCode is fully written */ + wmb(); +} + +#define GMU_START_TIMEOUT 100 /* ms */ +#define GPU_START_TIMEOUT 100 /* ms */ +#define GPU_RESET_TIMEOUT 1 /* ms */ +#define GPU_RESET_TIMEOUT_US 10 /* us */ + +/* + * timed_poll_check() - polling *gmu* register at given offset until + * its value changed to match expected value. The function times + * out and returns after given duration if register is not updated + * as expected. + * + * @device: Pointer to KGSL device + * @offset: Register offset + * @expected_ret: expected register value that stops polling + * @timout: number of jiffies to abort the polling + * @mask: bitmask to filter register value to match expected_ret + */ +static int timed_poll_check(struct kgsl_device *device, + unsigned int offset, unsigned int expected_ret, + unsigned int timeout, unsigned int mask) +{ + unsigned long t; + unsigned int value; + + t = jiffies + msecs_to_jiffies(timeout); + + do { + gmu_core_regread(device, offset, &value); + if ((value & mask) == expected_ret) + return 0; + /* Wait 100us to reduce unnecessary AHB bus traffic */ + usleep_range(10, 100); + } while (!time_after(jiffies, t)); + + /* Double check one last time */ + gmu_core_regread(device, offset, &value); + if ((value & mask) == expected_ret) + return 0; + + return -EINVAL; +} + +/* + * The lowest 16 bits of this value are the number of XO clock cycles + * for main hysteresis. This is the first hysteresis. Here we set it + * to 0x1680 cycles, or 300 us. The highest 16 bits of this value are + * the number of XO clock cycles for short hysteresis. This happens + * after main hysteresis. Here we set it to 0xA cycles, or 0.5 us. + */ +#define GMU_PWR_COL_HYST 0x000A1680 + +/* + * a6xx_gmu_power_config() - Configure and enable GMU's low power mode + * setting based on ADRENO feature flags. + * @device: Pointer to KGSL device + */ +static void a6xx_gmu_power_config(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + /* Configure registers for idle setting. The setting is cumulative */ + + /* Disable GMU WB/RB buffer and caches at boot */ + gmu_core_regwrite(device, A6XX_GMU_SYS_BUS_CONFIG, 0x1); + gmu_core_regwrite(device, A6XX_GMU_ICACHE_CONFIG, 0x1); + gmu_core_regwrite(device, A6XX_GMU_DCACHE_CONFIG, 0x1); + + gmu_core_regwrite(device, + A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0x9C40400); + + switch (gmu->idle_level) { + case GPU_HW_MIN_VOLT: + gmu_core_regrmw(device, A6XX_GMU_RPMH_CTRL, 0, + MIN_BW_ENABLE_MASK); + gmu_core_regrmw(device, A6XX_GMU_RPMH_HYST_CTRL, 0, + MIN_BW_HYST); + /* fall through */ + case GPU_HW_NAP: + gmu_core_regrmw(device, A6XX_GMU_GPU_NAP_CTRL, 0, + HW_NAP_ENABLE_MASK); + /* fall through */ + case GPU_HW_IFPC: + gmu_core_regwrite(device, A6XX_GMU_PWR_COL_INTER_FRAME_HYST, + GMU_PWR_COL_HYST); + gmu_core_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0, + IFPC_ENABLE_MASK); + /* fall through */ + case GPU_HW_SPTP_PC: + gmu_core_regwrite(device, A6XX_GMU_PWR_COL_SPTPRAC_HYST, + GMU_PWR_COL_HYST); + gmu_core_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0, + SPTP_ENABLE_MASK); + /* fall through */ + default: + break; + } + + /* ACD feature enablement */ + if (ADRENO_FEATURE(adreno_dev, ADRENO_LM) && + test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) + gmu_core_regrmw(device, A6XX_GMU_BOOT_KMD_LM_HANDSHAKE, 0, + BIT(10)); + + /* Enable RPMh GPU client */ + if (ADRENO_FEATURE(adreno_dev, ADRENO_RPMH)) + gmu_core_regrmw(device, A6XX_GMU_RPMH_CTRL, 0, + RPMH_ENABLE_MASK); +} + +/* + * a6xx_gmu_start() - Start GMU and wait until FW boot up. + * @device: Pointer to KGSL device + */ +static int a6xx_gmu_start(struct kgsl_device *device) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + kgsl_regwrite(device, A6XX_GMU_CX_GMU_WFI_CONFIG, 0x0); + /* Write 1 first to make sure the GMU is reset */ + gmu_core_regwrite(device, A6XX_GMU_CM3_SYSRESET, 1); + + /* Make sure putting in reset doesn't happen after clearing */ + wmb(); + + /* Bring GMU out of reset */ + gmu_core_regwrite(device, A6XX_GMU_CM3_SYSRESET, 0); + if (timed_poll_check(device, + A6XX_GMU_CM3_FW_INIT_RESULT, + 0xBABEFACE, + GMU_START_TIMEOUT, + 0xFFFFFFFF)) { + dev_err(&gmu->pdev->dev, "GMU doesn't boot\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * a6xx_gmu_hfi_start() - Write registers and start HFI. + * @device: Pointer to KGSL device + */ +static int a6xx_gmu_hfi_start(struct kgsl_device *device) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + gmu_core_regrmw(device, A6XX_GMU_GMU2HOST_INTR_MASK, + HFI_IRQ_MSGQ_MASK, 0); + gmu_core_regwrite(device, A6XX_GMU_HFI_CTRL_INIT, 1); + + if (timed_poll_check(device, + A6XX_GMU_HFI_CTRL_STATUS, + BIT(0), + GMU_START_TIMEOUT, + BIT(0))) { + dev_err(&gmu->pdev->dev, "GMU HFI init failed\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static uint64_t read_AO_counter(struct kgsl_device *device) +{ + unsigned int l, h, h1; + + gmu_core_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_H, &h); + gmu_core_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_L, &l); + gmu_core_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_H, &h1); + + /* + * If there's no change in COUNTER_H we have no overflow so return, + * otherwise read COUNTER_L again + */ + + if (h == h1) + return (uint64_t) l | ((uint64_t) h << 32); + + gmu_core_regread(device, A6XX_GMU_CX_GMU_ALWAYS_ON_COUNTER_L, &l); + return (uint64_t) l | ((uint64_t) h1 << 32); +} + +static int a6xx_rpmh_power_on_gpu(struct kgsl_device *device) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct device *dev = &gmu->pdev->dev; + int val; + + /* Only trigger wakeup sequence if sleep sequence was done earlier */ + if (!test_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags)) + return 0; + + gmu_core_regread(device, A6XX_GPU_CC_GX_DOMAIN_MISC, &val); + if (!(val & 0x1)) + dev_err_ratelimited(&gmu->pdev->dev, + "GMEM CLAMP IO not set while GFX rail off\n"); + + /* RSC wake sequence */ + gmu_core_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, BIT(1)); + + /* Write request before polling */ + wmb(); + + if (timed_poll_check(device, + A6XX_GMU_RSCC_CONTROL_ACK, + BIT(1), + GPU_START_TIMEOUT, + BIT(1))) { + dev_err(dev, "Failed to do GPU RSC power on\n"); + return -EINVAL; + } + + if (timed_poll_check(device, + A6XX_RSCC_SEQ_BUSY_DRV0, + 0, + GPU_START_TIMEOUT, + 0xFFFFFFFF)) + goto error_rsc; + + gmu_core_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0); + + /* Clear sleep sequence flag as wakeup sequence is successful */ + clear_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags); + + /* Enable the power counter because it was disabled before slumber */ + gmu_core_regwrite(device, A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 1); + + return 0; +error_rsc: + dev_err(dev, "GPU RSC sequence stuck in waking up GPU\n"); + return -EINVAL; +} + +static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + int ret; + + if (test_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags)) + return 0; + + /* RSC sleep sequence is different on v1 */ + if (adreno_is_a630v1(adreno_dev)) + gmu_core_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1); + + gmu_core_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 1); + /* Make sure the request completes before continuing */ + wmb(); + + if (adreno_is_a630v1(adreno_dev)) + ret = timed_poll_check(device, + A6XX_RSCC_TIMESTAMP_UNIT1_OUTPUT_DRV0, + BIT(0), + GPU_START_TIMEOUT, + BIT(0)); + else + ret = timed_poll_check(device, + A6XX_GPU_RSCC_RSC_STATUS0_DRV0, + BIT(16), + GPU_START_TIMEOUT, + BIT(16)); + + if (ret) { + dev_err(&gmu->pdev->dev, "GPU RSC power off fail\n"); + return -ETIMEDOUT; + } + + /* Read to clear the timestamp valid signal. Don't care what we read. */ + if (adreno_is_a630v1(adreno_dev)) { + gmu_core_regread(device, + A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_L_DRV0, + &ret); + gmu_core_regread(device, + A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_H_DRV0, + &ret); + } + + gmu_core_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0); + + if (ADRENO_FEATURE(adreno_dev, ADRENO_LM) && + test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) + gmu_core_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 0); + + set_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags); + return 0; +} + +/* + * Gmu FW header format: + * <32-bit start addr> <32-bit size> <32-bit pad0> <32-bit pad1> + */ +#define GMU_FW_HEADER_SIZE 4 + +#define GMU_ITCM_VA_START 0x0 +#define GMU_ITCM_VA_END (GMU_ITCM_VA_START + 0x4000) /* 16 KB */ + +#define GMU_DTCM_VA_START 0x40000 +#define GMU_DTCM_VA_END (GMU_DTCM_VA_START + 0x4000) /* 16 KB */ + +#define GMU_ICACHE_VA_START 0x4000 +#define GMU_ICACHE_VA_END (GMU_ICACHE_VA_START + 0x3C000) /* 240 KB */ + +static int load_gmu_fw(struct kgsl_device *device) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + uint32_t *fwptr = gmu->fw_image->hostptr; + int i, j, ret; + int start_addr, size_in_bytes, num_dwords, tcm_slot, num_records; + + /* Allocates & maps memory for GMU cached instructions range */ + ret = allocate_gmu_cached_fw(gmu); + if (ret) + return ret; + + /* + * Read first record. pad0 field of first record contains + * number of records in the image. + */ + num_records = fwptr[2]; + for (i = 0; i < num_records; i++) { + start_addr = fwptr[0]; + size_in_bytes = fwptr[1]; + num_dwords = size_in_bytes / sizeof(uint32_t); + fwptr += GMU_FW_HEADER_SIZE; + + if ((start_addr >= GMU_ITCM_VA_START) && + (start_addr < GMU_ITCM_VA_END)) { + tcm_slot = start_addr / sizeof(uint32_t); + + for (j = 0; j < num_dwords; j++) + gmu_core_regwrite(device, + A6XX_GMU_CM3_ITCM_START + tcm_slot + j, + fwptr[j]); + } else if ((start_addr >= GMU_DTCM_VA_START) && + (start_addr < GMU_DTCM_VA_END)) { + tcm_slot = (start_addr - GMU_DTCM_VA_START) + / sizeof(uint32_t); + + for (j = 0; j < num_dwords; j++) + gmu_core_regwrite(device, + A6XX_GMU_CM3_DTCM_START + tcm_slot + j, + fwptr[j]); + } else if ((start_addr >= GMU_ICACHE_VA_START) && + (start_addr < GMU_ICACHE_VA_END)) { + if (!is_cached_fw_size_valid(size_in_bytes)) { + dev_err(&gmu->pdev->dev, + "GMU firmware size too big\n"); + return -EINVAL; + + } + memcpy(gmu->cached_fw_image.hostptr, + fwptr, size_in_bytes); + } + + fwptr += num_dwords; + } + + /* Proceed only after the FW is written */ + wmb(); + return 0; +} + +/* + * a6xx_gmu_oob_set() - Set OOB interrupt to GMU. + * @adreno_dev: Pointer to adreno device + * @req: Which of the OOB bits to request + */ +static int a6xx_gmu_oob_set(struct adreno_device *adreno_dev, + enum oob_request req) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + int ret = 0; + int set, check; + + if (!gmu_core_isenabled(device)) + return 0; + + if (adreno_is_a640(adreno_dev)) { + set = BIT(30 - req * 2); + check = BIT(31 - req); + + if ((gmu->hfi.version & 0x1F) == 0) { + /* LEGACY for intermediate oobs */ + set = BIT(req + 16); + check = BIT(req + 16); + } + + if (req >= 6) { + dev_err(&gmu->pdev->dev, + "OOB_set(0x%x) invalid\n", set); + return -EINVAL; + } + } else { + set = BIT(req + 16); + check = BIT(req + 24); + } + + gmu_core_regwrite(device, A6XX_GMU_HOST2GMU_INTR_SET, set); + + if (timed_poll_check(device, + A6XX_GMU_GMU2HOST_INTR_INFO, + check, + GPU_START_TIMEOUT, + check)) { + ret = -ETIMEDOUT; + dev_err(&gmu->pdev->dev, + "OOB_set(0x%x) timed out\n", set); + } + + gmu_core_regwrite(device, A6XX_GMU_GMU2HOST_INTR_CLR, check); + + trace_kgsl_gmu_oob_set(set); + return ret; +} + +/* + * a6xx_gmu_oob_clear() - Clear a previously set OOB request. + * @adreno_dev: Pointer to the adreno device that has the GMU + * @req: Which of the OOB bits to clear + */ +static inline void a6xx_gmu_oob_clear(struct adreno_device *adreno_dev, + enum oob_request req) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + int clear; + + if (!gmu_core_isenabled(device)) + return; + + if (adreno_is_a640(adreno_dev)) { + clear = BIT(31 - req * 2); + if (req >= 6) { + dev_err(&gmu->pdev->dev, + "OOB_clear(0x%x) invalid\n", clear); + return; + } + /* LEGACY for intermediate oobs */ + if ((gmu->hfi.version & 0x1F) == 0) + clear = BIT(req + 24); + } else + clear = BIT(req + 24); + + gmu_core_regwrite(device, A6XX_GMU_HOST2GMU_INTR_SET, clear); + trace_kgsl_gmu_oob_clear(clear); +} + +static int a6xx_gmu_hfi_start_msg(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct hfi_start_cmd req; + + if (!adreno_is_a640(adreno_dev)) + return 0; + + /* Send hfi start msg */ + return hfi_send_req(KGSL_GMU_DEVICE(device), H2F_MSG_START, &req); +} + +#define FREQ_VOTE(idx, ack) (((idx) & 0xFF) | (((ack) & 0xF) << 28)) +#define BW_VOTE(idx) ((((idx) & 0xFFF) << 12) | ((idx) & 0xFFF)) + +/* + * a6xx_gmu_dcvs_nohfi() - request GMU to do DCVS without using HFI + * @device: Pointer to KGSL device + * @perf_idx: Index into GPU performance level table defined in + * HFI DCVS table message + * @bw_idx: Index into GPU b/w table defined in HFI b/w table message + * + */ +static int a6xx_gmu_dcvs_nohfi(struct kgsl_device *device, + unsigned int perf_idx, unsigned int bw_idx) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + int ret; + + gmu_core_regwrite(device, A6XX_GMU_DCVS_ACK_OPTION, DCVS_ACK_NONBLOCK); + + gmu_core_regwrite(device, A6XX_GMU_DCVS_PERF_SETTING, + FREQ_VOTE(perf_idx, CLKSET_OPTION_ATLEAST)); + + gmu_core_regwrite(device, A6XX_GMU_DCVS_BW_SETTING, BW_VOTE(bw_idx)); + + ret = a6xx_gmu_oob_set(adreno_dev, oob_dcvs); + if (ret == 0) + gmu_core_regread(device, A6XX_GMU_DCVS_RETURN, &ret); + + a6xx_gmu_oob_clear(adreno_dev, oob_dcvs); + + return ret; +} +static int a6xx_complete_rpmh_votes(struct kgsl_device *device) +{ + int ret = 0; + + if (!gmu_core_isenabled(device)) + return ret; + + ret |= timed_poll_check(device, A6XX_RSCC_TCS0_DRV0_STATUS, BIT(0), + GPU_RESET_TIMEOUT, BIT(0)); + ret |= timed_poll_check(device, A6XX_RSCC_TCS1_DRV0_STATUS, BIT(0), + GPU_RESET_TIMEOUT, BIT(0)); + ret |= timed_poll_check(device, A6XX_RSCC_TCS2_DRV0_STATUS, BIT(0), + GPU_RESET_TIMEOUT, BIT(0)); + ret |= timed_poll_check(device, A6XX_RSCC_TCS3_DRV0_STATUS, BIT(0), + GPU_RESET_TIMEOUT, BIT(0)); + + return ret; +} + +#define SPTPRAC_POWERON_CTRL_MASK 0x00778000 +#define SPTPRAC_POWEROFF_CTRL_MASK 0x00778001 +#define SPTPRAC_POWEROFF_STATUS_MASK BIT(2) +#define SPTPRAC_POWERON_STATUS_MASK BIT(3) +#define SPTPRAC_CTRL_TIMEOUT 10 /* ms */ +#define A6XX_RETAIN_FF_ENABLE_ENABLE_MASK BIT(11) + +/* + * a6xx_gmu_sptprac_enable() - Power on SPTPRAC + * @adreno_dev: Pointer to Adreno device + */ +int a6xx_gmu_sptprac_enable(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + if (!gmu_core_gpmu_isenabled(device)) + return -EINVAL; + + if (!adreno_has_sptprac_gdsc(adreno_dev)) + return 0; + + gmu_core_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, + SPTPRAC_POWERON_CTRL_MASK); + + if (timed_poll_check(device, + A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, + SPTPRAC_POWERON_STATUS_MASK, + SPTPRAC_CTRL_TIMEOUT, + SPTPRAC_POWERON_STATUS_MASK)) { + dev_err(&gmu->pdev->dev, "power on SPTPRAC fail\n"); + return -EINVAL; + } + + return 0; +} + +/* + * a6xx_gmu_sptprac_disable() - Power of SPTPRAC + * @adreno_dev: Pointer to Adreno device + */ +void a6xx_gmu_sptprac_disable(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + if (!adreno_has_sptprac_gdsc(adreno_dev)) + return; + + if (!gmu_core_gpmu_isenabled(device)) + return; + + /* Ensure that retention is on */ + gmu_core_regrmw(device, A6XX_GPU_CC_GX_GDSCR, 0, + A6XX_RETAIN_FF_ENABLE_ENABLE_MASK); + + gmu_core_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, + SPTPRAC_POWEROFF_CTRL_MASK); + + if (timed_poll_check(device, + A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, + SPTPRAC_POWEROFF_STATUS_MASK, + SPTPRAC_CTRL_TIMEOUT, + SPTPRAC_POWEROFF_STATUS_MASK)) + dev_err(&gmu->pdev->dev, "power off SPTPRAC fail\n"); +} + +#define SPTPRAC_POWER_OFF BIT(2) +#define SP_CLK_OFF BIT(4) +#define GX_GDSC_POWER_OFF BIT(6) +#define GX_CLK_OFF BIT(7) +#define is_on(val) (!(val & (GX_GDSC_POWER_OFF | GX_CLK_OFF))) +/* + * a6xx_gx_is_on() - Check if GX is on using pwr status register + * @adreno_dev - Pointer to adreno_device + * This check should only be performed if the keepalive bit is set or it + * can be guaranteed that the power state of the GPU will remain unchanged + */ +bool a6xx_gmu_gx_is_on(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int val; + + if (!gmu_core_isenabled(device)) + return true; + + gmu_core_regread(device, A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, &val); + return is_on(val); +} + +/* + * a6xx_gmu_sptprac_is_on() - Check if SPTP is on using pwr status register + * @adreno_dev - Pointer to adreno_device + * This check should only be performed if the keepalive bit is set or it + * can be guaranteed that the power state of the GPU will remain unchanged + */ +bool a6xx_gmu_sptprac_is_on(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int val; + + if (!gmu_core_isenabled(device) || !adreno_has_sptprac_gdsc(adreno_dev)) + return true; + + gmu_core_regread(device, A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, &val); + return !(val & (SPTPRAC_POWER_OFF | SP_CLK_OFF)); +} + +/* + * a6xx_gmu_gfx_rail_on() - request GMU to power GPU at given OPP. + * @device: Pointer to KGSL device + * + */ +static int a6xx_gmu_gfx_rail_on(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + unsigned int perf_idx = pwr->num_pwrlevels - pwr->default_pwrlevel - 1; + uint32_t default_opp = gmu->rpmh_votes.gx_votes[perf_idx]; + + gmu_core_regwrite(device, A6XX_GMU_BOOT_SLUMBER_OPTION, + OOB_BOOT_OPTION); + gmu_core_regwrite(device, A6XX_GMU_GX_VOTE_IDX, + ARC_VOTE_GET_PRI(default_opp)); + gmu_core_regwrite(device, A6XX_GMU_MX_VOTE_IDX, + ARC_VOTE_GET_SEC(default_opp)); + + return a6xx_gmu_oob_set(adreno_dev, oob_boot_slumber); +} + +static bool idle_trandition_complete(unsigned int idle_level, + unsigned int gmu_power_reg, + unsigned int sptprac_clk_reg) +{ + if (idle_level != gmu_power_reg) + return false; + + switch (idle_level) { + case GPU_HW_IFPC: + if (is_on(sptprac_clk_reg)) + return false; + break; + /* other GMU idle levels can be added here */ + case GPU_HW_ACTIVE: + default: + break; + } + return true; +} + +static int a6xx_gmu_wait_for_lowest_idle(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + unsigned int reg, reg1; + unsigned long t; + uint64_t ts1, ts2, ts3; + + if (!gmu_core_isenabled(device)) + return 0; + + ts1 = read_AO_counter(device); + + t = jiffies + msecs_to_jiffies(GMU_IDLE_TIMEOUT); + do { + gmu_core_regread(device, + A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE, ®); + gmu_core_regread(device, + A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, ®1); + + if (idle_trandition_complete(gmu->idle_level, reg, reg1)) + return 0; + /* Wait 100us to reduce unnecessary AHB bus traffic */ + usleep_range(10, 100); + } while (!time_after(jiffies, t)); + + ts2 = read_AO_counter(device); + /* Check one last time */ + + gmu_core_regread(device, A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE, ®); + gmu_core_regread(device, A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, ®1); + + if (idle_trandition_complete(gmu->idle_level, reg, reg1)) + return 0; + + ts3 = read_AO_counter(device); + WARN(1, "Timeout waiting for lowest idle: %08x %llx %llx %llx %x\n", + reg, ts1, ts2, ts3, reg1); + + return -ETIMEDOUT; +} + +/* Bitmask for GPU idle status check */ +#define CXGXCPUBUSYIGNAHB BIT(30) +static int a6xx_gmu_wait_for_idle(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + unsigned int status2; + uint64_t ts1; + + ts1 = read_AO_counter(device); + if (timed_poll_check(device, A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, + 0, GMU_START_TIMEOUT, CXGXCPUBUSYIGNAHB)) { + gmu_core_regread(device, + A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS2, &status2); + dev_err(&gmu->pdev->dev, + "GMU not idling: status2=0x%x %llx %llx\n", + status2, ts1, read_AO_counter(device)); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * a6xx_gmu_fw_start() - set up GMU and start FW + * @device: Pointer to KGSL device + * @boot_state: State of the GMU being started + */ +static int a6xx_gmu_fw_start(struct kgsl_device *device, + unsigned int boot_state) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct gmu_memdesc *mem_addr = gmu->hfi_mem; + uint32_t gmu_log_info; + int ret; + unsigned int chipid = 0; + + switch (boot_state) { + case GMU_COLD_BOOT: + /* Turn on TCM retention */ + gmu_core_regwrite(device, A6XX_GMU_GENERAL_7, 1); + + if (!test_and_set_bit(GMU_BOOT_INIT_DONE, &gmu->flags)) + _load_gmu_rpmh_ucode(device); + else { + ret = a6xx_rpmh_power_on_gpu(device); + if (ret) + return ret; + } + + if (gmu->load_mode == TCM_BOOT) { + /* Load GMU image via AHB bus */ + ret = load_gmu_fw(device); + if (ret) + return ret; + } else { + dev_err(&gmu->pdev->dev, "Unsupported GMU load mode %d\n", + gmu->load_mode); + return -EINVAL; + } + break; + case GMU_WARM_BOOT: + ret = a6xx_rpmh_power_on_gpu(device); + if (ret) + return ret; + break; + default: + break; + } + + /* Clear init result to make sure we are getting fresh value */ + gmu_core_regwrite(device, A6XX_GMU_CM3_FW_INIT_RESULT, 0); + gmu_core_regwrite(device, A6XX_GMU_CM3_BOOT_CONFIG, gmu->load_mode); + + gmu_core_regwrite(device, A6XX_GMU_HFI_QTBL_ADDR, + mem_addr->gmuaddr); + gmu_core_regwrite(device, A6XX_GMU_HFI_QTBL_INFO, 1); + + gmu_core_regwrite(device, A6XX_GMU_AHB_FENCE_RANGE_0, + FENCE_RANGE_MASK); + + /* Pass chipid to GMU FW, must happen before starting GMU */ + + /* Keep Core and Major bitfields unchanged */ + chipid = adreno_dev->chipid & 0xFFFF0000; + + /* + * Compress minor and patch version into 8 bits + * Bit 15-12: minor version + * Bit 11-8: patch version + */ + chipid = chipid | (ADRENO_CHIPID_MINOR(adreno_dev->chipid) << 12) + | (ADRENO_CHIPID_PATCH(adreno_dev->chipid) << 8); + + gmu_core_regwrite(device, A6XX_GMU_HFI_SFR_ADDR, chipid); + + /* Log size is encoded in (number of 4K units - 1) */ + gmu_log_info = (gmu->gmu_log->gmuaddr & 0xFFFFF000) | + ((LOGMEM_SIZE/SZ_4K - 1) & 0xFF); + gmu_core_regwrite(device, A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_MSG, + gmu_log_info); + + /* Configure power control and bring the GMU out of reset */ + a6xx_gmu_power_config(device); + ret = a6xx_gmu_start(device); + if (ret) + return ret; + + if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) { + ret = a6xx_gmu_gfx_rail_on(device); + if (ret) { + a6xx_gmu_oob_clear(adreno_dev, oob_boot_slumber); + return ret; + } + } + + if (gmu->idle_level < GPU_HW_SPTP_PC) { + ret = a6xx_gmu_sptprac_enable(adreno_dev); + if (ret) + return ret; + } + + ret = a6xx_gmu_hfi_start(device); + if (ret) + return ret; + + /* Make sure the write to start HFI happens before sending a message */ + wmb(); + return ret; +} + +/* + * a6xx_gmu_load_firmware() - Load the ucode into the GPMU RAM & PDC/RSC + * @device: Pointer to KGSL device + */ +static int a6xx_gmu_load_firmware(struct kgsl_device *device) +{ + const struct firmware *fw = NULL; + const struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + const struct adreno_gpu_core *gpucore = adreno_dev->gpucore; + int image_size, ret = -EINVAL; + + /* there is no GMU */ + if (!gmu_core_isenabled(device)) + return 0; + + /* GMU fw already saved and verified so do nothing new */ + if (gmu->fw_image) + return 0; + + if (gpucore->gpmufw_name == NULL) + return -EINVAL; + + ret = request_firmware(&fw, gpucore->gpmufw_name, device->dev); + if (ret || fw == NULL) { + KGSL_CORE_ERR("request_firmware (%s) failed: %d\n", + gpucore->gpmufw_name, ret); + return ret; + } + + image_size = PAGE_ALIGN(fw->size); + + ret = allocate_gmu_image(gmu, image_size); + + /* load into shared memory with GMU */ + if (!ret) + memcpy(gmu->fw_image->hostptr, fw->data, fw->size); + + release_firmware(fw); + + return ret; +} + +#define A6XX_STATE_OF_CHILD (BIT(4) | BIT(5)) +#define A6XX_IDLE_FULL_LLM BIT(0) +#define A6XX_WAKEUP_ACK BIT(1) +#define A6XX_IDLE_FULL_ACK BIT(0) +#define A6XX_VBIF_XIN_HALT_CTRL1_ACKS (BIT(0) | BIT(1) | BIT(2) | BIT(3)) + +static int a6xx_llm_glm_handshake(struct kgsl_device *device) +{ + unsigned int val; + const struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM) || + !test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) + return 0; + + gmu_core_regread(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, &val); + if (!(val & A6XX_STATE_OF_CHILD)) { + gmu_core_regrmw(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, 0, BIT(4)); + gmu_core_regrmw(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, 0, + A6XX_IDLE_FULL_LLM); + if (timed_poll_check(device, A6XX_GMU_LLM_GLM_SLEEP_STATUS, + A6XX_IDLE_FULL_ACK, GPU_RESET_TIMEOUT, + A6XX_IDLE_FULL_ACK)) { + dev_err(&gmu->pdev->dev, "LLM-GLM handshake failed\n"); + return -EINVAL; + } + } + + return 0; +} + +static void a6xx_isense_disable(struct kgsl_device *device) +{ + unsigned int val; + const struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM) || + !test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) + return; + + gmu_core_regread(device, A6XX_GPU_CS_ENABLE_REG, &val); + if (val) { + gmu_core_regwrite(device, A6XX_GPU_CS_ENABLE_REG, 0); + gmu_core_regwrite(device, A6XX_GMU_ISENSE_CTRL, 0); + } +} + +static int a6xx_gmu_suspend(struct kgsl_device *device) +{ + /* Max GX clients on A6xx is 2: GMU and KMD */ + int ret = 0, max_client_num = 2; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + /* do it only if LM feature is enabled */ + /* Disable ISENSE if it's on */ + a6xx_isense_disable(device); + + /* LLM-GLM handshake sequence */ + a6xx_llm_glm_handshake(device); + + /* If SPTP_RAC is on, turn off SPTP_RAC HS */ + a6xx_gmu_sptprac_disable(adreno_dev); + + /* Disconnect GPU from BUS is not needed if CX GDSC goes off later */ + + /* Check no outstanding RPMh voting */ + a6xx_complete_rpmh_votes(device); + + if (gmu->gx_gdsc) { + if (regulator_is_enabled(gmu->gx_gdsc)) { + /* Switch gx gdsc control from GMU to CPU + * force non-zero reference count in clk driver + * so next disable call will turn + * off the GDSC + */ + ret = regulator_enable(gmu->gx_gdsc); + if (ret) + dev_err(&gmu->pdev->dev, + "suspend fail: gx enable\n"); + + while ((max_client_num)) { + ret = regulator_disable(gmu->gx_gdsc); + if (!regulator_is_enabled(gmu->gx_gdsc)) + break; + max_client_num -= 1; + } + + if (!max_client_num) + dev_err(&gmu->pdev->dev, + "suspend fail: cannot disable gx\n"); + } + } + + return ret; +} + +/* + * a6xx_gmu_notify_slumber() - initiate request to GMU to prepare to slumber + * @device: Pointer to KGSL device + */ +static int a6xx_gmu_notify_slumber(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + int bus_level = pwr->pwrlevels[pwr->default_pwrlevel].bus_freq; + int perf_idx = gmu->num_gpupwrlevels - pwr->default_pwrlevel - 1; + int ret, state; + + /* Disable the power counter so that the GMU is not busy */ + gmu_core_regwrite(device, A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0); + + /* Turn off SPTPRAC if we own it */ + if (gmu->idle_level < GPU_HW_SPTP_PC) + a6xx_gmu_sptprac_disable(adreno_dev); + + if (!ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) { + struct hfi_prep_slumber_cmd req = { + .freq = perf_idx, + .bw = bus_level, + }; + + ret = hfi_send_req(gmu, H2F_MSG_PREPARE_SLUMBER, &req); + goto out; + } + + gmu_core_regwrite(device, A6XX_GMU_BOOT_SLUMBER_OPTION, + OOB_SLUMBER_OPTION); + gmu_core_regwrite(device, A6XX_GMU_GX_VOTE_IDX, perf_idx); + gmu_core_regwrite(device, A6XX_GMU_MX_VOTE_IDX, bus_level); + + ret = a6xx_gmu_oob_set(adreno_dev, oob_boot_slumber); + a6xx_gmu_oob_clear(adreno_dev, oob_boot_slumber); + + if (!ret) { + gmu_core_regread(device, + A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE, &state); + if (state != GPU_HW_SLUMBER) { + dev_err(&gmu->pdev->dev, + "Failed to prepare for slumber: 0x%x\n", + state); + ret = -EINVAL; + } + } + +out: + /* Make sure the fence is in ALLOW mode */ + gmu_core_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0); + return ret; +} + +/* + * a6xx_rpmh_gpu_pwrctrl() - GPU power control via RPMh/GMU interface + * @adreno_dev: Pointer to adreno device + * @mode: requested power mode + * @arg1: first argument for mode control + * @arg2: second argument for mode control + */ +static int a6xx_gmu_rpmh_gpu_pwrctrl(struct adreno_device *adreno_dev, + unsigned int mode, unsigned int arg1, unsigned int arg2) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + int ret; + + switch (mode) { + case GMU_FW_START: + ret = a6xx_gmu_fw_start(device, arg1); + break; + case GMU_SUSPEND: + ret = a6xx_gmu_suspend(device); + break; + case GMU_FW_STOP: + if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) + a6xx_gmu_oob_clear(adreno_dev, oob_boot_slumber); + ret = a6xx_rpmh_power_off_gpu(device); + break; + case GMU_DCVS_NOHFI: + ret = a6xx_gmu_dcvs_nohfi(device, arg1, arg2); + break; + case GMU_NOTIFY_SLUMBER: + ret = a6xx_gmu_notify_slumber(device); + break; + default: + dev_err(&gmu->pdev->dev, + "unsupported GMU power ctrl mode:%d\n", mode); + ret = -EINVAL; + break; + } + + return ret; +} + +#define LM_DEFAULT_LIMIT 6000 +#define GPU_LIMIT_THRESHOLD_ENABLE BIT(31) + +static uint32_t lm_limit(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + if (adreno_dev->lm_limit) + return adreno_dev->lm_limit; + + if (of_property_read_u32(device->pdev->dev.of_node, "qcom,lm-limit", + &adreno_dev->lm_limit)) + adreno_dev->lm_limit = LM_DEFAULT_LIMIT; + + return adreno_dev->lm_limit; +} + +#define LIMITS_CONFIG(t, s, c, i, a) ( \ + (t & 0xF) | \ + ((s & 0xF) << 4) | \ + ((c & 0xF) << 8) | \ + ((i & 0xF) << 12) | \ + ((a & 0xF) << 16)) + +void a6xx_gmu_enable_lm(struct kgsl_device *device) +{ + int result; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct device *dev = &gmu->pdev->dev; + struct hfi_lmconfig_cmd cmd; + + if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM) || + !test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) + return; + + gmu_core_regwrite(device, A6XX_GPU_GMU_CX_GMU_PWR_THRESHOLD, + GPU_LIMIT_THRESHOLD_ENABLE | lm_limit(adreno_dev)); + gmu_core_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 1); + gmu_core_regwrite(device, A6XX_GPU_GMU_CX_GMU_ISENSE_CTRL, 0x1); + + gmu->lm_config = LIMITS_CONFIG(1, 1, 1, 0, 0); + gmu->bcl_config = 0; + gmu->lm_dcvs_level = 0; + + cmd.limit_conf = gmu->lm_config; + cmd.bcl_conf = gmu->bcl_config; + cmd.lm_enable_bitmask = 0; + + if (gmu->lm_dcvs_level <= MAX_GX_LEVELS) + cmd.lm_enable_bitmask = + (1 << (gmu->lm_dcvs_level + 1)) - 1; + + result = hfi_send_req(gmu, H2F_MSG_LM_CFG, &cmd); + if (result) + dev_err(dev, "Failure enabling limits management:%d\n", result); +} + +static int a6xx_gmu_ifpc_store(struct adreno_device *adreno_dev, + unsigned int val) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + unsigned int requested_idle_level; + + if (!gmu_core_isenabled(device) || + !ADRENO_FEATURE(adreno_dev, ADRENO_IFPC)) + return -EINVAL; + + if ((val && gmu->idle_level >= GPU_HW_IFPC) || + (!val && gmu->idle_level < GPU_HW_IFPC)) + return 0; + + if (val) + requested_idle_level = GPU_HW_IFPC; + else { + if (ADRENO_FEATURE(adreno_dev, ADRENO_SPTP_PC)) + requested_idle_level = GPU_HW_SPTP_PC; + else + requested_idle_level = GPU_HW_ACTIVE; + } + + mutex_lock(&device->mutex); + + /* Power down the GPU before changing the idle level */ + kgsl_pwrctrl_change_state(device, KGSL_STATE_SUSPEND); + gmu->idle_level = requested_idle_level; + kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER); + + mutex_unlock(&device->mutex); + + return 0; +} + +static unsigned int a6xx_gmu_ifpc_show(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + return gmu_core_isenabled(device) && gmu->idle_level >= GPU_HW_IFPC; +} + +struct gmu_mem_type_desc { + struct gmu_memdesc *memdesc; + uint32_t type; +}; + +static size_t a6xx_snapshot_gmu_mem(struct kgsl_device *device, + u8 *buf, size_t remain, void *priv) +{ + struct kgsl_snapshot_gmu *header = (struct kgsl_snapshot_gmu *)buf; + struct gmu_mem_type_desc *desc = priv; + unsigned int *data = (unsigned int *)(buf + sizeof(*header)); + + if (priv == NULL) + return 0; + + if (remain < desc->memdesc->size + sizeof(*header)) { + KGSL_CORE_ERR( + "snapshot: Not enough memory for the gmu section %d\n", + desc->type); + return 0; + } + + header->type = desc->type; + header->size = desc->memdesc->size; + + /* Just copy the ringbuffer, there are no active IBs */ + memcpy(data, desc->memdesc->hostptr, desc->memdesc->size); + + return desc->memdesc->size + sizeof(*header); +} + +/* + * a6xx_gmu_snapshot() - A6XX GMU snapshot function + * @adreno_dev: Device being snapshotted + * @snapshot: Pointer to the snapshot instance + * + * This is where all of the A6XX GMU specific bits and pieces are grabbed + * into the snapshot memory + */ +void a6xx_gmu_snapshot(struct adreno_device *adreno_dev, + struct kgsl_snapshot *snapshot) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct gmu_mem_type_desc desc[] = { + {gmu->hfi_mem, SNAPSHOT_GMU_HFIMEM}, + {gmu->gmu_log, SNAPSHOT_GMU_LOG}, + {gmu->bw_mem, SNAPSHOT_GMU_BWMEM}, + {gmu->dump_mem, SNAPSHOT_GMU_DUMPMEM} }; + struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + unsigned int val, i; + + if (!gmu_core_isenabled(device)) + return; + + for (i = 0; i < ARRAY_SIZE(desc); i++) { + if (desc[i].memdesc) + kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_GMU, + snapshot, a6xx_snapshot_gmu_mem, + &desc[i]); + } + + adreno_snapshot_registers(device, snapshot, a6xx_gmu_registers, + ARRAY_SIZE(a6xx_gmu_registers) / 2); + + if (gpudev->gx_is_on(adreno_dev)) { + /* Set fence to ALLOW mode so registers can be read */ + kgsl_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0); + kgsl_regread(device, A6XX_GMU_AO_AHB_FENCE_CTRL, &val); + + KGSL_DRV_ERR(device, "set FENCE to ALLOW mode:%x\n", val); + adreno_snapshot_registers(device, snapshot, + a6xx_gmu_gx_registers, + ARRAY_SIZE(a6xx_gmu_gx_registers) / 2); + } +} + +struct gmu_dev_ops adreno_a6xx_gmudev = { + .load_firmware = a6xx_gmu_load_firmware, + .oob_set = a6xx_gmu_oob_set, + .oob_clear = a6xx_gmu_oob_clear, + .hfi_start_msg = a6xx_gmu_hfi_start_msg, + .enable_lm = a6xx_gmu_enable_lm, + .rpmh_gpu_pwrctrl = a6xx_gmu_rpmh_gpu_pwrctrl, + .wait_for_lowest_idle = a6xx_gmu_wait_for_lowest_idle, + .wait_for_gmu_idle = a6xx_gmu_wait_for_idle, + .sptprac_enable = a6xx_gmu_sptprac_enable, + .sptprac_disable = a6xx_gmu_sptprac_disable, + .ifpc_store = a6xx_gmu_ifpc_store, + .ifpc_show = a6xx_gmu_ifpc_show, + .snapshot = a6xx_gmu_snapshot, +}; diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c index 8d57da1a80ecf99cd92f0c0a332d53e86148de98..0c308edea824e363e3e96422ba5b10d817932d33 100644 --- a/drivers/gpu/msm/adreno_a6xx_preempt.c +++ b/drivers/gpu/msm/adreno_a6xx_preempt.c @@ -15,6 +15,7 @@ #include "a6xx_reg.h" #include "adreno_trace.h" #include "adreno_pm4types.h" +#include "kgsl_gmu_core.h" #define PREEMPT_RECORD(_field) \ offsetof(struct a6xx_cp_preemption_record, _field) @@ -35,7 +36,8 @@ static void _update_wptr(struct adreno_device *adreno_dev, bool reset_timer) struct adreno_ringbuffer *rb = adreno_dev->cur_rb; unsigned int wptr; unsigned long flags; - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS( + KGSL_DEVICE(adreno_dev)); /* * Need to make sure GPU is up before we read the @@ -44,8 +46,8 @@ static void _update_wptr(struct adreno_device *adreno_dev, bool reset_timer) if (in_interrupt() == 0) { int status; - if (gpudev->oob_set) { - status = gpudev->oob_set(adreno_dev, oob_preempt); + if (gmu_dev_ops->oob_set) { + status = gmu_dev_ops->oob_set(adreno_dev, oob_preempt); if (status) return; } @@ -73,8 +75,8 @@ static void _update_wptr(struct adreno_device *adreno_dev, bool reset_timer) spin_unlock_irqrestore(&rb->preempt_lock, flags); if (in_interrupt() == 0) { - if (gpudev->oob_clear) - gpudev->oob_clear(adreno_dev, oob_preempt); + if (gmu_dev_ops->oob_clear) + gmu_dev_ops->oob_clear(adreno_dev, oob_preempt); } } @@ -316,7 +318,7 @@ void a6xx_preemption_trigger(struct adreno_device *adreno_dev) * free when the GPU is already powered on, whereas an OOB requires an * unconditional handshake with the GMU. */ - kgsl_gmu_regrmw(device, A6XX_GMU_AO_SPARE_CNTL, 0x0, 0x2); + gmu_core_regrmw(device, A6XX_GMU_AO_SPARE_CNTL, 0x0, 0x2); /* * Fenced writes on this path will make sure the GPU is woken up @@ -397,7 +399,7 @@ void a6xx_preemption_callback(struct adreno_device *adreno_dev, int bit) * We can now safely clear the preemption keepalive bit, allowing * power collapse to resume its regular activity. */ - kgsl_gmu_regrmw(KGSL_DEVICE(adreno_dev), A6XX_GMU_AO_SPARE_CNTL, 0x2, + gmu_core_regrmw(KGSL_DEVICE(adreno_dev), A6XX_GMU_AO_SPARE_CNTL, 0x2, 0x0); del_timer(&adreno_dev->preempt.timer); @@ -745,6 +747,9 @@ int a6xx_preemption_context_init(struct kgsl_context *context) if (context->flags & KGSL_CONTEXT_SECURE) flags |= KGSL_MEMFLAGS_SECURE; + if (kgsl_is_compat_task()) + flags |= KGSL_MEMFLAGS_FORCE_32BIT; + /* * gpumem_alloc_entry takes an extra refcount. Put it only when * destroying the context to keep the context record valid diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c index afd1be551417e9a4ad79c404a4bc29aa9a8ebbb3..6b92c077367ffe09a917756d99c28467386ed831 100644 --- a/drivers/gpu/msm/adreno_a6xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c @@ -18,7 +18,6 @@ #include "adreno_snapshot.h" #include "a6xx_reg.h" #include "adreno_a6xx.h" -#include "kgsl_gmu.h" #define A6XX_NUM_CTXTS 2 #define A6XX_NUM_AXI_ARB_BLOCKS 2 @@ -244,44 +243,6 @@ static const unsigned int a6xx_gbif_registers[] = { 0x3C00, 0X3C0B, 0X3C40, 0X3C47, 0X3CC0, 0X3CD1, 0xE3A, 0xE3A, }; -static const unsigned int a6xx_gmu_gx_registers[] = { - /* GMU GX */ - 0x1A800, 0x1A800, 0x1A810, 0x1A813, 0x1A816, 0x1A816, 0x1A818, 0x1A81B, - 0x1A81E, 0x1A81E, 0x1A820, 0x1A823, 0x1A826, 0x1A826, 0x1A828, 0x1A82B, - 0x1A82E, 0x1A82E, 0x1A830, 0x1A833, 0x1A836, 0x1A836, 0x1A838, 0x1A83B, - 0x1A83E, 0x1A83E, 0x1A840, 0x1A843, 0x1A846, 0x1A846, 0x1A880, 0x1A884, - 0x1A900, 0x1A92B, 0x1A940, 0x1A940, -}; - -static const unsigned int a6xx_gmu_registers[] = { - /* GMU TCM */ - 0x1B400, 0x1C3FF, 0x1C400, 0x1D3FF, - /* GMU CX */ - 0x1F400, 0x1F407, 0x1F410, 0x1F412, 0x1F500, 0x1F500, 0x1F507, 0x1F50A, - 0x1F800, 0x1F804, 0x1F807, 0x1F808, 0x1F80B, 0x1F80C, 0x1F80F, 0x1F81C, - 0x1F824, 0x1F82A, 0x1F82D, 0x1F830, 0x1F840, 0x1F853, 0x1F887, 0x1F889, - 0x1F8A0, 0x1F8A2, 0x1F8A4, 0x1F8AF, 0x1F8C0, 0x1F8C3, 0x1F8D0, 0x1F8D0, - 0x1F8E4, 0x1F8E4, 0x1F8E8, 0x1F8EC, 0x1F900, 0x1F903, 0x1F940, 0x1F940, - 0x1F942, 0x1F944, 0x1F94C, 0x1F94D, 0x1F94F, 0x1F951, 0x1F954, 0x1F954, - 0x1F957, 0x1F958, 0x1F95D, 0x1F95D, 0x1F962, 0x1F962, 0x1F964, 0x1F965, - 0x1F980, 0x1F986, 0x1F990, 0x1F99E, 0x1F9C0, 0x1F9C0, 0x1F9C5, 0x1F9CC, - 0x1F9E0, 0x1F9E2, 0x1F9F0, 0x1F9F0, 0x1FA00, 0x1FA01, - /* GPU RSCC */ - 0x2348C, 0x2348C, 0x23501, 0x23502, 0x23740, 0x23742, 0x23744, 0x23747, - 0x2374C, 0x23787, 0x237EC, 0x237EF, 0x237F4, 0x2382F, 0x23894, 0x23897, - 0x2389C, 0x238D7, 0x2393C, 0x2393F, 0x23944, 0x2397F, - /* GMU AO */ - 0x23B00, 0x23B16, 0x23C00, 0x23C00, - /* GPU CC */ - 0x24000, 0x24012, 0x24040, 0x24052, 0x24400, 0x24404, 0x24407, 0x2440B, - 0x24415, 0x2441C, 0x2441E, 0x2442D, 0x2443C, 0x2443D, 0x2443F, 0x24440, - 0x24442, 0x24449, 0x24458, 0x2445A, 0x24540, 0x2455E, 0x24800, 0x24802, - 0x24C00, 0x24C02, 0x25400, 0x25402, 0x25800, 0x25802, 0x25C00, 0x25C02, - 0x26000, 0x26002, - /* GPU CC ACD */ - 0x26400, 0x26416, 0x26420, 0x26427, -}; - static const unsigned int a6xx_rb_rac_registers[] = { 0x8E04, 0x8E05, 0x8E07, 0x8E08, 0x8E10, 0x8E1C, 0x8E20, 0x8E25, 0x8E28, 0x8E28, 0x8E2C, 0x8E2F, 0x8E50, 0x8E52, @@ -1323,11 +1284,11 @@ static size_t a6xx_snapshot_cx_dbgc_debugbus_block(struct kgsl_device *device, } /* a6xx_snapshot_debugbus() - Capture debug bus data */ -static void a6xx_snapshot_debugbus(struct kgsl_device *device, +void a6xx_snapshot_debugbus(struct adreno_device *adreno_dev, struct kgsl_snapshot *snapshot) { int i; - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); kgsl_regwrite(device, A6XX_DBGC_CFG_DBGBUS_CNTLT, (0xf << A6XX_DBGC_CFG_DBGBUS_CNTLT_SEGT_SHIFT) | @@ -1447,40 +1408,7 @@ static void a6xx_snapshot_debugbus(struct kgsl_device *device, } } -/* - * a6xx_snapshot_gmu() - A6XX GMU snapshot function - * @adreno_dev: Device being snapshotted - * @snapshot: Pointer to the snapshot instance - * - * This is where all of the A6XX GMU specific bits and pieces are grabbed - * into the snapshot memory - */ -void a6xx_snapshot_gmu(struct adreno_device *adreno_dev, - struct kgsl_snapshot *snapshot) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - unsigned int val; - - if (!kgsl_gmu_isenabled(device)) - return; - adreno_snapshot_registers(device, snapshot, a6xx_gmu_registers, - ARRAY_SIZE(a6xx_gmu_registers) / 2); - - if (gpudev->gx_is_on(adreno_dev)) { - /* Set fence to ALLOW mode so registers can be read */ - kgsl_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0); - kgsl_regread(device, A6XX_GMU_AO_AHB_FENCE_CTRL, &val); - - KGSL_DRV_ERR(device, "set FENCE to ALLOW mode:%x\n", val); - adreno_snapshot_registers(device, snapshot, - a6xx_gmu_gx_registers, - ARRAY_SIZE(a6xx_gmu_gx_registers) / 2); - } - - a6xx_snapshot_debugbus(device, snapshot); -} /* a6xx_snapshot_sqe() - Dump SQE data in snapshot */ static size_t a6xx_snapshot_sqe(struct kgsl_device *device, u8 *buf, @@ -1568,7 +1496,7 @@ void a6xx_snapshot(struct adreno_device *adreno_dev, unsigned int i; /* GMU TCM data dumped through AHB */ - a6xx_snapshot_gmu(adreno_dev, snapshot); + a6xx_gmu_snapshot(adreno_dev, snapshot); sptprac_on = gpudev->sptprac_is_on(adreno_dev); diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 0f372114fcf1546e4c5810c04fb2a03408e301ee..c042d4b410396390ac24bdd85739c8a2b11fb466 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -208,8 +208,8 @@ static inline bool _isidle(struct adreno_device *adreno_dev) if (!kgsl_state_is_awake(KGSL_DEVICE(adreno_dev))) goto ret; - if (adreno_rb_empty(adreno_dev->cur_rb)) - goto ret; + if (!adreno_rb_empty(adreno_dev->cur_rb)) + return false; /* only check rbbm status to determine if GPU is idle */ adreno_readreg(adreno_dev, ADRENO_REG_RBBM_STATUS, ®_rbbm_status); @@ -2094,7 +2094,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) return 0; /* Mask all GMU interrupts */ - if (kgsl_gmu_isenabled(device)) { + if (gmu_core_isenabled(device)) { adreno_write_gmureg(adreno_dev, ADRENO_REG_GMU_AO_HOST_INTERRUPT_MASK, 0xFFFFFFFF); @@ -2857,6 +2857,16 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) return ret; } +void adreno_dispatcher_halt(struct kgsl_device *device) +{ + adreno_get_gpu_halt(ADRENO_DEVICE(device)); +} + +void adreno_dispatcher_unhalt(struct kgsl_device *device) +{ + adreno_put_gpu_halt(ADRENO_DEVICE(device)); +} + /* * adreno_dispatcher_idle() - Wait for dispatcher to idle * @adreno_dev: Adreno device whose dispatcher needs to idle @@ -2887,6 +2897,13 @@ int adreno_dispatcher_idle(struct adreno_device *adreno_dev) mutex_unlock(&device->mutex); + /* + * Flush the worker to make sure all executing + * or pending dispatcher works on worker are + * finished + */ + kthread_flush_worker(&kgsl_driver.worker); + ret = wait_for_completion_timeout(&dispatcher->idle_gate, msecs_to_jiffies(ADRENO_IDLE_TIMEOUT)); if (ret == 0) { diff --git a/drivers/gpu/msm/adreno_dispatch.h b/drivers/gpu/msm/adreno_dispatch.h index 48f0cdc546ff7cc5e6bdfb73de66798ffe586e3c..61bd06f4d37326cb2756207b411e8afe941dcbaf 100644 --- a/drivers/gpu/msm/adreno_dispatch.h +++ b/drivers/gpu/msm/adreno_dispatch.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2018, 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 @@ -103,6 +103,8 @@ enum adreno_dispatcher_flags { }; void adreno_dispatcher_start(struct kgsl_device *device); +void adreno_dispatcher_halt(struct kgsl_device *device); +void adreno_dispatcher_unhalt(struct kgsl_device *device); int adreno_dispatcher_init(struct adreno_device *adreno_dev); void adreno_dispatcher_close(struct adreno_device *adreno_dev); int adreno_dispatcher_idle(struct adreno_device *adreno_dev); diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c index e54a5e6857aee6ec92fc525e6ad99aa98e2ced03..066423c1265c77741bcbd8c6155009c26b3765e4 100644 --- a/drivers/gpu/msm/adreno_perfcounter.c +++ b/drivers/gpu/msm/adreno_perfcounter.c @@ -171,8 +171,9 @@ void adreno_perfcounter_restore(struct adreno_device *adreno_dev) */ inline void adreno_perfcounter_save(struct adreno_device *adreno_dev) { - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); struct adreno_perfcounters *counters = ADRENO_PERFCOUNTERS(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS( + KGSL_DEVICE(adreno_dev)); struct adreno_perfcount_group *group; unsigned int counter, groupid; int ret = 0; @@ -180,8 +181,8 @@ inline void adreno_perfcounter_save(struct adreno_device *adreno_dev) if (counters == NULL) return; - if (gpudev->oob_set) - ret = gpudev->oob_set(adreno_dev, oob_perfcntr); + if (gmu_dev_ops->oob_set) + ret = gmu_dev_ops->oob_set(adreno_dev, oob_perfcntr); /* if oob_set timeout, clear the mask and return */ if (ret) @@ -208,8 +209,8 @@ inline void adreno_perfcounter_save(struct adreno_device *adreno_dev) } done: - if (gpudev->oob_clear) - gpudev->oob_clear(adreno_dev, oob_perfcntr); + if (gmu_dev_ops->oob_clear) + gmu_dev_ops->oob_clear(adreno_dev, oob_perfcntr); } static int adreno_perfcounter_enable(struct adreno_device *adreno_dev, diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index b5999e6fb6a255976c7e0d3c7af74ee726278ecc..df55407564f90d7451c518b2712164e04af16b88 100644 --- a/drivers/gpu/msm/adreno_snapshot.c +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -13,6 +13,7 @@ #include "kgsl.h" #include "kgsl_sharedmem.h" #include "kgsl_snapshot.h" +#include "kgsl_gmu_core.h" #include "adreno.h" #include "adreno_pm4types.h" @@ -956,11 +957,16 @@ void adreno_snapshot_gmu(struct kgsl_device *device, struct kgsl_snapshot *snapshot) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); /* Add GMU specific sections */ - if (gpudev->snapshot_gmu) - gpudev->snapshot_gmu(adreno_dev, snapshot); + if (gmu_dev_ops && gmu_dev_ops->snapshot) + gmu_dev_ops->snapshot(adreno_dev, snapshot); + + if (gpudev->snapshot_debugbus) + gpudev->snapshot_debugbus(adreno_dev, snapshot); + } /* diff --git a/drivers/gpu/msm/adreno_sysfs.c b/drivers/gpu/msm/adreno_sysfs.c index 022aa9f1f99494e3ebfba408e0e2aaf2cf80b7d4..232770e347e55a7acb743028e5d18ce85976cb52 100644 --- a/drivers/gpu/msm/adreno_sysfs.c +++ b/drivers/gpu/msm/adreno_sysfs.c @@ -242,23 +242,6 @@ static int _preemption_store(struct adreno_device *adreno_dev, return 0; } -static int _gmu_idle_level_store(struct adreno_device *adreno_dev, - unsigned int val) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct gmu_device *gmu = &device->gmu; - - mutex_lock(&device->mutex); - - /* Power down the GPU before changing the idle level */ - kgsl_pwrctrl_change_state(device, KGSL_STATE_SUSPEND); - gmu->idle_level = val; - kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER); - - mutex_unlock(&device->mutex); - return 0; -} - static unsigned int _preemption_show(struct adreno_device *adreno_dev) { return adreno_is_preemption_enabled(adreno_dev); @@ -309,36 +292,12 @@ static unsigned int _lm_show(struct adreno_device *adreno_dev) static int _ifpc_store(struct adreno_device *adreno_dev, unsigned int val) { - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct gmu_device *gmu = &device->gmu; - unsigned int requested_idle_level; - - if (!kgsl_gmu_isenabled(device) || - !ADRENO_FEATURE(adreno_dev, ADRENO_IFPC)) - return -EINVAL; - - if ((val && gmu->idle_level >= GPU_HW_IFPC) || - (!val && gmu->idle_level < GPU_HW_IFPC)) - return 0; - - if (val) - requested_idle_level = GPU_HW_IFPC; - else { - if (ADRENO_FEATURE(adreno_dev, ADRENO_SPTP_PC)) - requested_idle_level = GPU_HW_SPTP_PC; - else - requested_idle_level = GPU_HW_ACTIVE; - } - - return _gmu_idle_level_store(adreno_dev, requested_idle_level); + return adreno_gmu_ifpc_store(adreno_dev, val); } static unsigned int _ifpc_show(struct adreno_device *adreno_dev) { - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct gmu_device *gmu = &device->gmu; - - return kgsl_gmu_isenabled(device) && gmu->idle_level >= GPU_HW_IFPC; + return adreno_gmu_ifpc_show(adreno_dev); } static unsigned int _preempt_count_show(struct adreno_device *adreno_dev) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 0bd2ee2c22325d6ade15bc0bfa29a66a691ae4eb..7d68c7c18902c92c4e937629a627448aba1b2b8b 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -532,14 +532,21 @@ int kgsl_context_init(struct kgsl_device_private *dev_priv, int ret = 0, id; struct kgsl_process_private *proc_priv = dev_priv->process_priv; + /* + * Read and increment the context count under lock to make sure + * no process goes beyond the specified context limit. + */ + spin_lock(&proc_priv->ctxt_count_lock); if (atomic_read(&proc_priv->ctxt_count) > KGSL_MAX_CONTEXTS_PER_PROC) { KGSL_DRV_ERR(device, "Per process context limit reached for pid %u", dev_priv->process_priv->pid); + spin_unlock(&proc_priv->ctxt_count_lock); return -ENOSPC; } atomic_inc(&proc_priv->ctxt_count); + spin_unlock(&proc_priv->ctxt_count_lock); id = _kgsl_get_context_id(device); if (id == -ENOSPC) { @@ -754,6 +761,8 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) mutex_lock(&device->mutex); status = kgsl_pwrctrl_change_state(device, KGSL_STATE_SUSPEND); + if (status == 0) + device->ftbl->dispatcher_halt(device); mutex_unlock(&device->mutex); KGSL_PWR_WARN(device, "suspend end\n"); @@ -768,6 +777,7 @@ static int kgsl_resume_device(struct kgsl_device *device) KGSL_PWR_WARN(device, "resume start\n"); mutex_lock(&device->mutex); if (device->state == KGSL_STATE_SUSPEND) { + device->ftbl->dispatcher_unhalt(device); kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER); } else if (device->state != KGSL_STATE_INIT) { /* @@ -919,6 +929,7 @@ static struct kgsl_process_private *kgsl_process_private_new( spin_lock_init(&private->mem_lock); spin_lock_init(&private->syncsource_lock); + spin_lock_init(&private->ctxt_count_lock); idr_init(&private->mem_idr); idr_init(&private->syncsource_idr); @@ -1059,6 +1070,7 @@ static int kgsl_close_device(struct kgsl_device *device) int result = 0; mutex_lock(&device->mutex); + device->open_count--; if (device->open_count == 0) { /* Wait for the active count to go to 0 */ @@ -1158,9 +1170,6 @@ static int kgsl_open_device(struct kgsl_device *device) complete_all(&device->hwaccess_gate); kgsl_pwrctrl_change_state(device, KGSL_STATE_ACTIVE); kgsl_active_count_put(device); - - /* Do not let the device close */ - device->open_count = 1; } device->open_count++; err: @@ -2396,7 +2405,6 @@ long kgsl_ioctl_gpuobj_import(struct kgsl_device_private *dev_priv, struct kgsl_gpuobj_import *param = data; struct kgsl_mem_entry *entry; int ret, fd = -1; - struct kgsl_mmu *mmu = &dev_priv->device->mmu; entry = kgsl_mem_entry_create(); if (entry == NULL) @@ -2410,15 +2418,10 @@ long kgsl_ioctl_gpuobj_import(struct kgsl_device_private *dev_priv, | KGSL_MEMFLAGS_FORCE_32BIT | KGSL_MEMFLAGS_IOCOHERENT; - /* Disable IO coherence if it is not supported on the chip */ - if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT)) - param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT); - - entry->memdesc.flags = param->flags; - - if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE)) - entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE; + if (kgsl_is_compat_task()) + param->flags |= KGSL_MEMFLAGS_FORCE_32BIT; + kgsl_memdesc_init(dev_priv->device, &entry->memdesc, param->flags); if (param->type == KGSL_USER_MEM_TYPE_ADDR) ret = _gpuobj_map_useraddr(dev_priv->device, private->pagetable, entry, param); @@ -2657,6 +2660,7 @@ long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_mmu *mmu = &dev_priv->device->mmu; unsigned int memtype; + uint64_t flags; /* * If content protection is not enabled and secure buffer @@ -2693,28 +2697,17 @@ long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, * Note: CACHEMODE is ignored for this call. Caching should be * determined by type of allocation being mapped. */ - param->flags &= KGSL_MEMFLAGS_GPUREADONLY - | KGSL_MEMTYPE_MASK - | KGSL_MEMALIGN_MASK - | KGSL_MEMFLAGS_USE_CPU_MAP - | KGSL_MEMFLAGS_SECURE - | KGSL_MEMFLAGS_IOCOHERENT; - - /* Disable IO coherence if it is not supported on the chip */ - if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT)) - param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT); + flags = param->flags & (KGSL_MEMFLAGS_GPUREADONLY + | KGSL_MEMTYPE_MASK + | KGSL_MEMALIGN_MASK + | KGSL_MEMFLAGS_USE_CPU_MAP + | KGSL_MEMFLAGS_SECURE + | KGSL_MEMFLAGS_IOCOHERENT); - entry->memdesc.flags = ((uint64_t) param->flags) - | KGSL_MEMFLAGS_FORCE_32BIT; + if (kgsl_is_compat_task()) + flags |= KGSL_MEMFLAGS_FORCE_32BIT; - if (!kgsl_mmu_use_cpu_map(mmu)) - entry->memdesc.flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP); - - if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE)) - entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE; - - if (param->flags & KGSL_MEMFLAGS_SECURE) - entry->memdesc.priv |= KGSL_MEMDESC_SECURE; + kgsl_memdesc_init(dev_priv->device, &entry->memdesc, flags); switch (memtype) { case KGSL_MEM_ENTRY_USER: @@ -3110,10 +3103,6 @@ struct kgsl_mem_entry *gpumem_alloc_entry( | KGSL_MEMFLAGS_FORCE_32BIT | KGSL_MEMFLAGS_IOCOHERENT; - /* Turn off SVM if the system doesn't support it */ - if (!kgsl_mmu_use_cpu_map(mmu)) - flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP); - /* Return not supported error if secure memory isn't enabled */ if (!kgsl_mmu_is_secured(mmu) && (flags & KGSL_MEMFLAGS_SECURE)) { @@ -3122,10 +3111,6 @@ struct kgsl_mem_entry *gpumem_alloc_entry( return ERR_PTR(-EOPNOTSUPP); } - /* Secure memory disables advanced addressing modes */ - if (flags & KGSL_MEMFLAGS_SECURE) - flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP); - /* Cap the alignment bits to the highest number we can handle */ align = MEMFLAGS(flags, KGSL_MEMALIGN_MASK, KGSL_MEMALIGN_SHIFT); if (align >= ilog2(KGSL_MAX_ALIGN)) { @@ -3144,20 +3129,10 @@ struct kgsl_mem_entry *gpumem_alloc_entry( flags = kgsl_filter_cachemode(flags); - /* Disable IO coherence if it is not supported on the chip */ - if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT)) - flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT); - entry = kgsl_mem_entry_create(); if (entry == NULL) return ERR_PTR(-ENOMEM); - if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE)) - entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE; - - if (flags & KGSL_MEMFLAGS_SECURE) - entry->memdesc.priv |= KGSL_MEMDESC_SECURE; - ret = kgsl_allocate_user(dev_priv->device, &entry->memdesc, size, flags); if (ret != 0) @@ -3209,6 +3184,9 @@ long kgsl_ioctl_gpuobj_alloc(struct kgsl_device_private *dev_priv, struct kgsl_gpuobj_alloc *param = data; struct kgsl_mem_entry *entry; + if (kgsl_is_compat_task()) + param->flags |= KGSL_MEMFLAGS_FORCE_32BIT; + entry = gpumem_alloc_entry(dev_priv, param->size, param->flags); if (IS_ERR(entry)) @@ -3236,7 +3214,9 @@ long kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, /* Legacy functions doesn't support these advanced features */ flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP); - flags |= KGSL_MEMFLAGS_FORCE_32BIT; + + if (kgsl_is_compat_task()) + flags |= KGSL_MEMFLAGS_FORCE_32BIT; entry = gpumem_alloc_entry(dev_priv, (uint64_t) param->size, flags); @@ -3260,7 +3240,8 @@ long kgsl_ioctl_gpumem_alloc_id(struct kgsl_device_private *dev_priv, struct kgsl_mem_entry *entry; uint64_t flags = param->flags; - flags |= KGSL_MEMFLAGS_FORCE_32BIT; + if (kgsl_is_compat_task()) + flags |= KGSL_MEMFLAGS_FORCE_32BIT; entry = gpumem_alloc_entry(dev_priv, (uint64_t) param->size, flags); @@ -3335,6 +3316,7 @@ long kgsl_ioctl_sparse_phys_alloc(struct kgsl_device_private *dev_priv, struct kgsl_process_private *process = dev_priv->process_priv; struct kgsl_sparse_phys_alloc *param = data; struct kgsl_mem_entry *entry; + uint64_t flags; int ret; int id; @@ -3367,11 +3349,12 @@ long kgsl_ioctl_sparse_phys_alloc(struct kgsl_device_private *dev_priv, entry->id = id; entry->priv = process; - entry->memdesc.flags = KGSL_MEMFLAGS_SPARSE_PHYS; - kgsl_memdesc_set_align(&entry->memdesc, ilog2(param->pagesize)); + flags = KGSL_MEMFLAGS_SPARSE_PHYS | + ((ilog2(param->pagesize) << KGSL_MEMALIGN_SHIFT) & + KGSL_MEMALIGN_MASK); ret = kgsl_allocate_user(dev_priv->device, &entry->memdesc, - param->size, entry->memdesc.flags); + param->size, flags); if (ret) goto err_remove_idr; @@ -3460,7 +3443,8 @@ long kgsl_ioctl_sparse_virt_alloc(struct kgsl_device_private *dev_priv, if (entry == NULL) return -ENOMEM; - entry->memdesc.flags = KGSL_MEMFLAGS_SPARSE_VIRT; + kgsl_memdesc_init(dev_priv->device, &entry->memdesc, + KGSL_MEMFLAGS_SPARSE_VIRT); entry->memdesc.size = param->size; entry->memdesc.cur_bindings = 0; kgsl_memdesc_set_align(&entry->memdesc, ilog2(param->pagesize)); @@ -4046,6 +4030,7 @@ long kgsl_ioctl_gpuobj_set_info(struct kgsl_device_private *dev_priv, struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_gpuobj_set_info *param = data; struct kgsl_mem_entry *entry; + int ret = 0; if (param->id == 0) return -EINVAL; @@ -4058,13 +4043,16 @@ long kgsl_ioctl_gpuobj_set_info(struct kgsl_device_private *dev_priv, copy_metadata(entry, param->metadata, param->metadata_len); if (param->flags & KGSL_GPUOBJ_SET_INFO_TYPE) { - entry->memdesc.flags &= ~((uint64_t) KGSL_MEMTYPE_MASK); - entry->memdesc.flags |= (uint64_t)(param->type << - KGSL_MEMTYPE_SHIFT); + if (param->type <= (KGSL_MEMTYPE_MASK >> KGSL_MEMTYPE_SHIFT)) { + entry->memdesc.flags &= ~((uint64_t) KGSL_MEMTYPE_MASK); + entry->memdesc.flags |= (uint64_t)((param->type << + KGSL_MEMTYPE_SHIFT) & KGSL_MEMTYPE_MASK); + } else + ret = -EINVAL; } kgsl_mem_entry_put(entry); - return 0; + return ret; } /** @@ -4373,6 +4361,10 @@ static unsigned long _get_svm_area(struct kgsl_process_private *private, return -ERANGE; if (flags & MAP_FIXED) { + /* We must honor alignment requirements */ + if (!IS_ALIGNED(hint, align)) + return -EINVAL; + /* we must use addr 'hint' or fail */ return _gpu_set_svm_region(private, entry, hint, len); } else if (hint != 0) { diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 60aa76a3639c0a6b490626387b8f921e73d52b65..f495515dad6959560521bf91ae962dfec2652325 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -27,6 +27,7 @@ #include #include #include +#include /* * --- kgsl drawobj flags --- @@ -209,6 +210,7 @@ struct kgsl_memdesc_ops { * @physaddr: Physical address of the memory object * @size: Size of the memory object * @mapsize: Size of memory mapped in userspace + * @pad_to: Size that we pad the memdesc to * @priv: Internal flags and settings * @sgt: Scatter gather table for allocated pages * @ops: Function hooks for the memdesc memory type @@ -228,6 +230,7 @@ struct kgsl_memdesc { phys_addr_t physaddr; uint64_t size; uint64_t mapsize; + uint64_t pad_to; unsigned int priv; struct sg_table *sgt; struct kgsl_memdesc_ops *ops; @@ -630,4 +633,9 @@ static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst, kernfs_create_link(dst->sd, dst_name, old); } + +static inline bool kgsl_is_compat_task(void) +{ + return (BITS_PER_LONG == 32) || is_compat_task(); +} #endif /* __KGSL_H */ diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 6767c712577209ea2003788689817f08ea525fbf..704671f5685c373729f7cc9189b827adf6bb55f1 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -26,7 +26,7 @@ #include "kgsl_snapshot.h" #include "kgsl_sharedmem.h" #include "kgsl_drawobj.h" -#include "kgsl_gmu.h" +#include "kgsl_gmu_core.h" #define KGSL_IOCTL_FUNC(_cmd, _func) \ [_IOC_NR((_cmd))] = \ @@ -128,10 +128,6 @@ struct kgsl_functable { int (*init)(struct kgsl_device *device); int (*start)(struct kgsl_device *device, int priority); int (*stop)(struct kgsl_device *device); - void (*gmu_regread)(struct kgsl_device *device, - unsigned int offsetwords, unsigned int *value); - void (*gmu_regwrite)(struct kgsl_device *device, - unsigned int offsetwords, unsigned int value); int (*getproperty)(struct kgsl_device *device, unsigned int type, void __user *value, size_t sizebytes); @@ -190,6 +186,8 @@ struct kgsl_functable { void (*gpu_model)(struct kgsl_device *device, char *str, size_t bufsz); void (*stop_fault_timer)(struct kgsl_device *device); + void (*dispatcher_halt)(struct kgsl_device *device); + void (*dispatcher_unhalt)(struct kgsl_device *device); }; struct kgsl_ioctl { @@ -267,7 +265,7 @@ struct kgsl_device { const char *shadermemname; struct kgsl_mmu mmu; - struct gmu_device gmu; + struct gmu_core_device gmu_core; struct completion hwaccess_gate; struct completion halt_gate; const struct kgsl_functable *ftbl; @@ -443,6 +441,7 @@ struct kgsl_context { * @syncsource_lock: Spinlock to protect the syncsource idr * @fd_count: Counter for the number of FDs for this process * @ctxt_count: Count for the number of contexts for this process + * @ctxt_count_lock: Spinlock to protect ctxt_count */ struct kgsl_process_private { unsigned long priv; @@ -463,6 +462,7 @@ struct kgsl_process_private { spinlock_t syncsource_lock; int fd_count; atomic_t ctxt_count; + spinlock_t ctxt_count_lock; }; /** @@ -562,26 +562,14 @@ static inline bool kgsl_is_register_offset(struct kgsl_device *device, return ((offsetwords * sizeof(uint32_t)) < device->reg_len); } -static inline bool kgsl_is_gmu_offset(struct kgsl_device *device, - unsigned int offsetwords) -{ - struct gmu_device *gmu = &device->gmu; - - return (gmu->pdev && - (offsetwords >= gmu->gmu2gpu_offset) && - ((offsetwords - gmu->gmu2gpu_offset) * sizeof(uint32_t) < - gmu->reg_len)); -} - static inline void kgsl_regread(struct kgsl_device *device, unsigned int offsetwords, unsigned int *value) { if (kgsl_is_register_offset(device, offsetwords)) device->ftbl->regread(device, offsetwords, value); - else if (device->ftbl->gmu_regread && - kgsl_is_gmu_offset(device, offsetwords)) - device->ftbl->gmu_regread(device, offsetwords, value); + else if (gmu_core_is_register_offset(device, offsetwords)) + gmu_core_regread(device, offsetwords, value); else { WARN(1, "Out of bounds register read: 0x%x\n", offsetwords); *value = 0; @@ -594,31 +582,12 @@ static inline void kgsl_regwrite(struct kgsl_device *device, { if (kgsl_is_register_offset(device, offsetwords)) device->ftbl->regwrite(device, offsetwords, value); - else if (device->ftbl->gmu_regwrite && - kgsl_is_gmu_offset(device, offsetwords)) - device->ftbl->gmu_regwrite(device, offsetwords, value); + else if (gmu_core_is_register_offset(device, offsetwords)) + gmu_core_regwrite(device, offsetwords, value); else WARN(1, "Out of bounds register write: 0x%x\n", offsetwords); } -static inline void kgsl_gmu_regread(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) -{ - if (device->ftbl->gmu_regread) - device->ftbl->gmu_regread(device, offsetwords, value); - else - *value = 0; -} - -static inline void kgsl_gmu_regwrite(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) -{ - if (device->ftbl->gmu_regwrite) - device->ftbl->gmu_regwrite(device, offsetwords, value); -} - static inline void kgsl_regrmw(struct kgsl_device *device, unsigned int offsetwords, unsigned int mask, unsigned int bits) @@ -630,17 +599,6 @@ static inline void kgsl_regrmw(struct kgsl_device *device, kgsl_regwrite(device, offsetwords, val | bits); } -static inline void kgsl_gmu_regrmw(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int mask, unsigned int bits) -{ - unsigned int val = 0; - - kgsl_gmu_regread(device, offsetwords, &val); - val &= ~mask; - kgsl_gmu_regwrite(device, offsetwords, val | bits); -} - static inline int kgsl_idle(struct kgsl_device *device) { return device->ftbl->idle(device); @@ -685,13 +643,11 @@ static inline struct kgsl_device *kgsl_device_from_dev(struct device *dev) static inline int kgsl_state_is_awake(struct kgsl_device *device) { - struct gmu_device *gmu = &device->gmu; - if (device->state == KGSL_STATE_ACTIVE || device->state == KGSL_STATE_AWARE) return true; - else if (kgsl_gmu_isenabled(device) && - test_bit(GMU_CLK_ON, &gmu->flags)) + else if (gmu_core_isenabled(device) && + gmu_core_testbit(device, GMU_CLK_ON)) return true; else return false; diff --git a/drivers/gpu/msm/kgsl_drawobj.c b/drivers/gpu/msm/kgsl_drawobj.c index 527c68f74344148c5fa6fd2f8891eb4dd54187a1..b69a2f03896e00829359bdef192ba86b8f92214f 100644 --- a/drivers/gpu/msm/kgsl_drawobj.c +++ b/drivers/gpu/msm/kgsl_drawobj.c @@ -180,7 +180,7 @@ static bool drawobj_sync_expire(struct kgsl_device *device, * for dispatch */ if (!kgsl_drawobj_events_pending(event->syncobj)) { - del_timer_sync(&syncobj->timer); + del_timer(&syncobj->timer); if (device->ftbl->drawctxt_sched) device->ftbl->drawctxt_sched(device, diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c index b2dafe820e50c27ab6b4e6c409e44cea0a6a5239..3a5122953d0bd40404119335ed23841f53cad276 100644 --- a/drivers/gpu/msm/kgsl_gmu.c +++ b/drivers/gpu/msm/kgsl_gmu.c @@ -28,13 +28,7 @@ #include "kgsl_hfi.h" #include "a6xx_reg.h" #include "adreno.h" - -#undef MODULE_PARAM_PREFIX -#define MODULE_PARAM_PREFIX "kgsl_gmu." - -static bool nogmu; -module_param(nogmu, bool, 0444); -MODULE_PARM_DESC(nogmu, "Disable the GMU"); +#include "kgsl_trace.h" #define GMU_CONTEXT_USER 0 #define GMU_CONTEXT_KERNEL 1 @@ -68,14 +62,10 @@ struct gmu_iommu_context { struct iommu_domain *domain; }; -#define HFIMEM_SIZE (HFI_QUEUE_SIZE * (HFI_QUEUE_MAX + 1)) - #define DUMPMEM_SIZE SZ_16K #define DUMMY_SIZE SZ_4K -#define LOGMEM_SIZE SZ_4K - #define GMU_DCACHE_CHUNK_SIZE (60 * SZ_4K) /* GMU DCache VA size - 240KB */ /* Define target specific GMU VMA configurations */ @@ -111,18 +101,7 @@ struct gmu_iommu_context gmu_ctx[] = { static struct gmu_memdesc gmu_kmem_entries[GMU_KERNEL_ENTRIES]; static unsigned long gmu_kmem_bitmap; static unsigned int num_uncached_entries; - -void init_gmu_log_base(struct kgsl_device *device) -{ - uint32_t gmu_log_info; - struct gmu_device *gmu = &device->gmu; - - /* Log size is encoded in (number of 4K units - 1) */ - gmu_log_info = (gmu->gmu_log->gmuaddr & 0xFFFFF000) | - ((LOGMEM_SIZE/SZ_4K - 1) & 0xFF); - kgsl_gmu_regwrite(device, A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_MSG, - gmu_log_info); -} +static void gmu_remove(struct kgsl_device *device); static int _gmu_iommu_fault_handler(struct device *dev, unsigned long addr, int flags, const char *name) @@ -133,6 +112,10 @@ static int _gmu_iommu_fault_handler(struct device *dev, fault_type = "translation"; else if (flags & IOMMU_FAULT_PERMISSION) fault_type = "permission"; + else if (flags & IOMMU_FAULT_EXTERNAL) + fault_type = "external"; + else if (flags & IOMMU_FAULT_TRANSACTION_STALLED) + fault_type = "transaction stalled"; dev_err(dev, "GMU fault addr = %lX, context=%s (%s %s fault)\n", addr, name, @@ -550,11 +533,14 @@ static void gmu_memory_close(struct gmu_device *gmu) /* * gmu_memory_probe() - probe GMU IOMMU context banks and allocate memory * to share with GMU in kernel mode. + * @device: Pointer to KGSL device * @gmu: Pointer to GMU device * @node: Pointer to GMU device node */ -static int gmu_memory_probe(struct gmu_device *gmu, struct device_node *node) +static int gmu_memory_probe(struct kgsl_device *device, + struct gmu_device *gmu, struct device_node *node) { + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int ret; ret = gmu_iommu_init(gmu, node); @@ -582,11 +568,13 @@ static int gmu_memory_probe(struct gmu_device *gmu, struct device_node *node) } /* Allocates & maps GMU crash dump memory */ - gmu->dump_mem = allocate_gmu_kmem(gmu, GMU_NONCACHED_KERNEL, - DUMPMEM_SIZE, (IOMMU_READ | IOMMU_WRITE)); - if (IS_ERR(gmu->dump_mem)) { - ret = PTR_ERR(gmu->dump_mem); - goto err_ret; + if (adreno_is_a630(adreno_dev)) { + gmu->dump_mem = allocate_gmu_kmem(gmu, GMU_NONCACHED_KERNEL, + DUMPMEM_SIZE, (IOMMU_READ | IOMMU_WRITE)); + if (IS_ERR(gmu->dump_mem)) { + ret = PTR_ERR(gmu->dump_mem); + goto err_ret; + } } /* GMU master log */ @@ -612,12 +600,12 @@ static int gmu_memory_probe(struct gmu_device *gmu, struct device_node *node) * The function converts GPU power level and bus level index used by KGSL * to index being used by GMU/RPMh. */ -int gmu_dcvs_set(struct gmu_device *gmu, +static int gmu_dcvs_set(struct kgsl_device *device, unsigned int gpu_pwrlevel, unsigned int bus_level) { - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); struct hfi_gx_bw_perf_vote_cmd req = { .ack_type = DCVS_ACK_BLOCK, .freq = INVALID_DCVS_IDX, @@ -635,7 +623,7 @@ int gmu_dcvs_set(struct gmu_device *gmu, return -EINVAL; if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) { - int ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, + int ret = gmu_dev_ops->rpmh_gpu_pwrctrl(adreno_dev, GMU_DCVS_NOHFI, req.freq, req.bw); if (ret) { @@ -778,19 +766,18 @@ static int setup_volt_dependency_tbl(uint32_t *votes, /* * rpmh_arc_votes_init() - initialized RPMh votes needed for rails voltage * scaling by GMU. + * @device: Pointer to KGSL device * @gmu: Pointer to GMU device * @pri_rail: Pointer to primary power rail VLVL table * @sec_rail: Pointer to second/dependent power rail VLVL table * of pri_rail VLVL table * @type: the type of the primary rail, GPU or GMU */ -static int rpmh_arc_votes_init(struct gmu_device *gmu, - struct rpmh_arc_vals *pri_rail, - struct rpmh_arc_vals *sec_rail, - unsigned int type) +static int rpmh_arc_votes_init(struct kgsl_device *device, + struct gmu_device *gmu, struct rpmh_arc_vals *pri_rail, + struct rpmh_arc_vals *sec_rail, unsigned int type) { struct device *dev; - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); unsigned int num_freqs; uint32_t *votes; unsigned int vlvl_tbl[MAX_GX_LEVELS]; @@ -965,7 +952,8 @@ static int gmu_bus_vote_init(struct gmu_device *gmu, struct kgsl_pwrctrl *pwr) return ret; } -static int gmu_rpmh_init(struct gmu_device *gmu, struct kgsl_pwrctrl *pwr) +static int gmu_rpmh_init(struct kgsl_device *device, + struct gmu_device *gmu, struct kgsl_pwrctrl *pwr) { struct rpmh_arc_vals gfx_arc, cx_arc, mx_arc; int ret; @@ -988,19 +976,19 @@ static int gmu_rpmh_init(struct gmu_device *gmu, struct kgsl_pwrctrl *pwr) if (ret) return ret; - ret = rpmh_arc_votes_init(gmu, &gfx_arc, &mx_arc, GPU_ARC_VOTE); + ret = rpmh_arc_votes_init(device, gmu, &gfx_arc, &mx_arc, GPU_ARC_VOTE); if (ret) return ret; - return rpmh_arc_votes_init(gmu, &cx_arc, &mx_arc, GMU_ARC_VOTE); + return rpmh_arc_votes_init(device, gmu, &cx_arc, &mx_arc, GMU_ARC_VOTE); } static irqreturn_t gmu_irq_handler(int irq, void *data) { - struct gmu_device *gmu = data; - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); + struct kgsl_device *device = data; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - unsigned int status = 0; + unsigned int mask, status = 0; adreno_read_gmureg(ADRENO_DEVICE(device), ADRENO_REG_GMU_AO_HOST_INTERRUPT_STATUS, &status); @@ -1009,6 +997,13 @@ static irqreturn_t gmu_irq_handler(int irq, void *data) /* Ignore GMU_INT_RSCC_COMP and GMU_INT_DBD WAKEUP interrupts */ if (status & GMU_INT_WDOG_BITE) { + /* Temporarily mask the watchdog interrupt to prevent a storm */ + adreno_read_gmureg(adreno_dev, + ADRENO_REG_GMU_AO_HOST_INTERRUPT_MASK, &mask); + adreno_write_gmureg(adreno_dev, + ADRENO_REG_GMU_AO_HOST_INTERRUPT_MASK, + (mask | GMU_INT_WDOG_BITE)); + dev_err_ratelimited(&gmu->pdev->dev, "GMU watchdog expired interrupt received\n"); adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT); @@ -1036,9 +1031,9 @@ static irqreturn_t gmu_irq_handler(int irq, void *data) static irqreturn_t hfi_irq_handler(int irq, void *data) { - struct kgsl_hfi *hfi = data; - struct gmu_device *gmu = container_of(hfi, struct gmu_device, hfi); - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); + struct kgsl_device *device = data; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct kgsl_hfi *hfi = &gmu->hfi; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); unsigned int status = 0; @@ -1180,9 +1175,8 @@ static int gmu_clocks_probe(struct gmu_device *gmu, struct device_node *node) return 0; } -static int gmu_gpu_bw_probe(struct gmu_device *gmu) +static int gmu_gpu_bw_probe(struct kgsl_device *device, struct gmu_device *gmu) { - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); struct msm_bus_scale_pdata *bus_scale_table; struct msm_bus_paths *usecase; struct msm_bus_vectors *vector; @@ -1278,7 +1272,7 @@ static int gmu_regulators_probe(struct gmu_device *gmu, return 0; } -static int gmu_irq_probe(struct gmu_device *gmu) +static int gmu_irq_probe(struct kgsl_device *device, struct gmu_device *gmu) { int ret; struct kgsl_hfi *hfi = &gmu->hfi; @@ -1288,7 +1282,7 @@ static int gmu_irq_probe(struct gmu_device *gmu) ret = devm_request_irq(&gmu->pdev->dev, hfi->hfi_interrupt_num, hfi_irq_handler, IRQF_TRIGGER_HIGH, - "HFI", hfi); + "HFI", device); if (ret) { dev_err(&gmu->pdev->dev, "request_irq(%d) failed: %d\n", hfi->hfi_interrupt_num, ret); @@ -1300,7 +1294,7 @@ static int gmu_irq_probe(struct gmu_device *gmu) ret = devm_request_irq(&gmu->pdev->dev, gmu->gmu_interrupt_num, gmu_irq_handler, IRQF_TRIGGER_HIGH, - "GMU", gmu); + "GMU", device); if (ret) dev_err(&gmu->pdev->dev, "request_irq(%d) failed: %d\n", gmu->gmu_interrupt_num, ret); @@ -1311,7 +1305,7 @@ static int gmu_irq_probe(struct gmu_device *gmu) static void gmu_irq_enable(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); struct kgsl_hfi *hfi = &gmu->hfi; /* Clear any pending IRQs before unmasking on GMU */ @@ -1334,7 +1328,7 @@ static void gmu_irq_enable(struct kgsl_device *device) static void gmu_irq_disable(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); struct kgsl_hfi *hfi = &gmu->hfi; /* Disable all IRQs on host */ @@ -1355,38 +1349,28 @@ static void gmu_irq_disable(struct kgsl_device *device) } /* Do not access any GMU registers in GMU probe function */ -int gmu_probe(struct kgsl_device *device, unsigned long flags) +static int gmu_probe(struct kgsl_device *device, + struct device_node *node, unsigned long flags) { - struct device_node *node; - struct gmu_device *gmu = &device->gmu; + struct gmu_device *gmu; struct gmu_memdesc *mem_addr = NULL; - struct kgsl_hfi *hfi = &gmu->hfi; + struct kgsl_hfi *hfi; struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int i = 0, ret = -ENXIO; - /* Make sure no flags enabled by default, probably don't need */ - gmu->flags = 0; - gmu->ver = ~0U; + gmu = kzalloc(sizeof(struct gmu_device), GFP_KERNEL); - /* Normalize flags for input feature requests */ - flags &= BIT(GMU_GPMU); - - node = of_find_compatible_node(device->pdev->dev.of_node, - NULL, "qcom,gpu-gmu"); - /* No GMU in dt, no worries...hopefully */ - if (node == NULL) { - /* If we are trying to use GPMU and no GMU, that's bad */ - if (flags & BIT(GMU_GPMU)) - return -ENXIO; - /* Otherwise it's ok and nothing to do */ - return 0; - } + if (gmu == NULL) + return -ENOMEM; + + hfi = &gmu->hfi; + gmu->load_mode = TCM_BOOT; - /* Ok, now say the flags are enabled */ + gmu->ver = ~0U; gmu->flags = flags; - device->gmu.pdev = of_find_device_by_node(node); + gmu->pdev = of_find_device_by_node(node); of_dma_configure(&gmu->pdev->dev, node); /* Set up GMU regulators */ @@ -1400,7 +1384,7 @@ int gmu_probe(struct kgsl_device *device, unsigned long flags) goto error; /* Set up GMU IOMMU and shared memory with GMU */ - ret = gmu_memory_probe(&device->gmu, node); + ret = gmu_memory_probe(device, gmu, node); if (ret) goto error; mem_addr = gmu->hfi_mem; @@ -1414,10 +1398,12 @@ int gmu_probe(struct kgsl_device *device, unsigned long flags) if (ret) goto error; - gmu->gmu2gpu_offset = (gmu->reg_phys - device->reg_phys) >> 2; + device->gmu_core.gmu2gpu_offset = + (gmu->reg_phys - device->reg_phys) >> 2; + device->gmu_core.reg_len = gmu->reg_len; /* Initialize HFI and GMU interrupts */ - ret = gmu_irq_probe(gmu); + ret = gmu_irq_probe(device, gmu); if (ret) goto error; @@ -1444,7 +1430,7 @@ int gmu_probe(struct kgsl_device *device, unsigned long flags) } /* Initializes GPU b/w levels configuration */ - ret = gmu_gpu_bw_probe(gmu); + ret = gmu_gpu_bw_probe(device, gmu); if (ret) goto error; @@ -1454,7 +1440,7 @@ int gmu_probe(struct kgsl_device *device, unsigned long flags) goto error; /* Populates RPMh configurations */ - ret = gmu_rpmh_init(gmu, pwr); + ret = gmu_rpmh_init(device, gmu, pwr); if (ret) goto error; @@ -1476,6 +1462,9 @@ int gmu_probe(struct kgsl_device *device, unsigned long flags) clear_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag); set_bit(GMU_ENABLED, &gmu->flags); + device->gmu_core.ptr = (void *)gmu; + device->gmu_core.dev_ops = &adreno_a6xx_gmudev; + return 0; error: @@ -1483,8 +1472,6 @@ int gmu_probe(struct kgsl_device *device, unsigned long flags) return ret; } - - static int gmu_enable_clks(struct gmu_device *gmu) { int ret, j = 0; @@ -1586,8 +1573,8 @@ static int gmu_disable_gdsc(struct gmu_device *gmu) static int gmu_suspend(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - struct gmu_device *gmu = &device->gmu; + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); if (!test_bit(GMU_CLK_ON, &gmu->flags)) return 0; @@ -1597,7 +1584,7 @@ static int gmu_suspend(struct kgsl_device *device) clear_bit(GMU_HFI_ON, &gmu->flags); gmu_irq_disable(device); - if (gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_SUSPEND, 0, 0)) + if (gmu_dev_ops->rpmh_gpu_pwrctrl(adreno_dev, GMU_SUSPEND, 0, 0)) return -EINVAL; gmu_disable_clks(gmu); @@ -1606,10 +1593,10 @@ static int gmu_suspend(struct kgsl_device *device) return 0; } -void gmu_snapshot(struct kgsl_device *device) +static void gmu_snapshot(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct gmu_device *gmu = &device->gmu; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); /* Mask so there's no interrupt caused by NMI */ adreno_write_gmureg(adreno_dev, @@ -1663,13 +1650,13 @@ static void gmu_change_gpu_pwrlevel(struct kgsl_device *device, } /* To be called to power on both GPU and GMU */ -int gmu_start(struct kgsl_device *device) +static int gmu_start(struct kgsl_device *device) { int ret = 0; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); struct kgsl_pwrctrl *pwr = &device->pwrctrl; - struct gmu_device *gmu = &device->gmu; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); switch (device->state) { case KGSL_STATE_INIT: @@ -1686,12 +1673,12 @@ int gmu_start(struct kgsl_device *device) dev_err(&gmu->pdev->dev, "Failed to allocate gmu b/w: %d\n", ret); - ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START, + ret = gmu_dev_ops->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START, GMU_COLD_BOOT, 0); if (ret) goto error_gmu; - ret = hfi_start(gmu, GMU_COLD_BOOT); + ret = hfi_start(device, gmu, GMU_COLD_BOOT); if (ret) goto error_gmu; @@ -1706,12 +1693,12 @@ int gmu_start(struct kgsl_device *device) gmu_enable_clks(gmu); gmu_irq_enable(device); - ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START, + ret = gmu_dev_ops->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START, GMU_COLD_BOOT, 0); if (ret) goto error_gmu; - ret = hfi_start(gmu, GMU_COLD_BOOT); + ret = hfi_start(device, gmu, GMU_COLD_BOOT); if (ret) goto error_gmu; @@ -1726,13 +1713,13 @@ int gmu_start(struct kgsl_device *device) gmu_enable_clks(gmu); gmu_irq_enable(device); - ret = gpudev->rpmh_gpu_pwrctrl( - adreno_dev, GMU_FW_START, GMU_RESET, 0); + ret = gmu_dev_ops->rpmh_gpu_pwrctrl( + adreno_dev, GMU_FW_START, GMU_COLD_BOOT, 0); if (ret) goto error_gmu; - ret = hfi_start(gmu, GMU_COLD_BOOT); + ret = hfi_start(device, gmu, GMU_COLD_BOOT); if (ret) goto error_gmu; @@ -1743,12 +1730,12 @@ int gmu_start(struct kgsl_device *device) /* GMU fast boot */ hfi_stop(gmu); - ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START, - GMU_RESET, 0); + ret = gmu_dev_ops->rpmh_gpu_pwrctrl(adreno_dev, + GMU_FW_START, GMU_COLD_BOOT, 0); if (ret) goto error_gmu; - ret = hfi_start(gmu, GMU_COLD_BOOT); + ret = hfi_start(device, gmu, GMU_COLD_BOOT); if (ret) goto error_gmu; } @@ -1761,33 +1748,34 @@ int gmu_start(struct kgsl_device *device) error_gmu: if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) - gpudev->oob_clear(adreno_dev, oob_boot_slumber); - gmu_snapshot(device); + gmu_dev_ops->oob_clear(adreno_dev, oob_boot_slumber); + gmu_core_snapshot(device); return ret; } /* Caller shall ensure GPU is ready for SLUMBER */ -void gmu_stop(struct kgsl_device *device) +static void gmu_stop(struct kgsl_device *device) { - struct gmu_device *gmu = &device->gmu; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + struct gmu_dev_ops *gmu_dev_ops = GMU_DEVICE_OPS(device); int ret = 0; if (!test_bit(GMU_CLK_ON, &gmu->flags)) return; /* Wait for the lowest idle level we requested */ - if (gpudev->wait_for_lowest_idle && - gpudev->wait_for_lowest_idle(adreno_dev)) + if (gmu_dev_ops->wait_for_lowest_idle && + gmu_dev_ops->wait_for_lowest_idle(adreno_dev)) goto error; - ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0); + ret = gmu_dev_ops->rpmh_gpu_pwrctrl(adreno_dev, + GMU_NOTIFY_SLUMBER, 0, 0); if (ret) goto error; - if (gpudev->wait_for_gmu_idle && - gpudev->wait_for_gmu_idle(adreno_dev)) + if (gmu_dev_ops->wait_for_gmu_idle && + gmu_dev_ops->wait_for_gmu_idle(adreno_dev)) goto error; /* Pending message in all queues are abandoned */ @@ -1795,7 +1783,7 @@ void gmu_stop(struct kgsl_device *device) clear_bit(GMU_HFI_ON, &gmu->flags); gmu_irq_disable(device); - gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0); + gmu_dev_ops->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0); gmu_disable_clks(gmu); gmu_disable_gdsc(gmu); @@ -1810,18 +1798,20 @@ void gmu_stop(struct kgsl_device *device) */ set_bit(GMU_FAULT, &gmu->flags); dev_err(&gmu->pdev->dev, "Failed to stop GMU\n"); - gmu_snapshot(device); + gmu_core_snapshot(device); } -void gmu_remove(struct kgsl_device *device) +static void gmu_remove(struct kgsl_device *device) { - struct gmu_device *gmu = &device->gmu; - struct kgsl_hfi *hfi = &gmu->hfi; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + struct kgsl_hfi *hfi; int i = 0; - if (!device->gmu.pdev) + if (gmu == NULL || gmu->pdev == NULL) return; + hfi = &gmu->hfi; + tasklet_kill(&hfi->tasklet); gmu_stop(device); @@ -1863,7 +1853,7 @@ void gmu_remove(struct kgsl_device *device) gmu->reg_virt = NULL; } - gmu_memory_close(&device->gmu); + gmu_memory_close(gmu); for (i = 0; i < MAX_GMU_CLKS; i++) { if (gmu->clks[i]) { @@ -1882,64 +1872,107 @@ void gmu_remove(struct kgsl_device *device) gmu->cx_gdsc = NULL; } - device->gmu.flags = 0; - device->gmu.pdev = NULL; + gmu->flags = 0; + gmu->pdev = NULL; + kfree(gmu); +} + +static void gmu_regwrite(struct kgsl_device *device, + unsigned int offsetwords, unsigned int value) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + void __iomem *reg; + + trace_kgsl_regwrite(device, offsetwords, value); + + offsetwords -= device->gmu_core.gmu2gpu_offset; + reg = gmu->reg_virt + (offsetwords << 2); + + /* + * ensure previous writes post before this one, + * i.e. act like normal writel() + */ + wmb(); + __raw_writel(value, reg); +} + +static void gmu_regread(struct kgsl_device *device, + unsigned int offsetwords, unsigned int *value) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + void __iomem *reg; + + offsetwords -= device->gmu_core.gmu2gpu_offset; + + reg = gmu->reg_virt + (offsetwords << 2); + + *value = __raw_readl(reg); + + /* + * ensure this read finishes before the next one. + * i.e. act like normal readl() + */ + rmb(); } /* Check if GPMU is in charge of power features */ -bool kgsl_gmu_gpmu_isenabled(struct kgsl_device *device) +static bool gmu_gpmu_isenabled(struct kgsl_device *device) { - return test_bit(GMU_GPMU, &(device->gmu.flags)); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + return test_bit(GMU_GPMU, &gmu->flags); } /* Check if GMU is enabled. Only set once GMU is fully initialized */ -bool kgsl_gmu_isenabled(struct kgsl_device *device) +static bool gmu_isenabled(struct kgsl_device *device) { - return !nogmu && test_bit(GMU_ENABLED, &device->gmu.flags); + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + return test_bit(GMU_ENABLED, &gmu->flags); } -/* - * adreno_gmu_fenced_write() - Check if there is a GMU and it is enabled - * @adreno_dev: Pointer to the Adreno device device that owns the GMU - * @offset: 32bit register enum that is to be written - * @val: The value to be written to the register - * @fence_mask: The value to poll the fence status register - * - * Check the WRITEDROPPED0/1 bit in the FENCE_STATUS register to check if - * the write to the fenced register went through. If it didn't then we retry - * the write until it goes through or we time out. - */ -int adreno_gmu_fenced_write(struct adreno_device *adreno_dev, - enum adreno_regs offset, unsigned int val, - unsigned int fence_mask) +static void gmu_set_bit(struct kgsl_device *device, enum gmu_core_flags flag) { - unsigned int status, i; - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - unsigned int reg_offset = gpudev->reg_offsets->offsets[offset]; + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); - adreno_writereg(adreno_dev, offset, val); + set_bit(flag, &gmu->flags); +} - if (!kgsl_gmu_isenabled(KGSL_DEVICE(adreno_dev))) - return 0; +static void gmu_clear_bit(struct kgsl_device *device, enum gmu_core_flags flag) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); - for (i = 0; i < GMU_WAKEUP_RETRY_MAX; i++) { - adreno_read_gmureg(adreno_dev, ADRENO_REG_GMU_AHB_FENCE_STATUS, - &status); + clear_bit(flag, &gmu->flags); +} - /* - * If !writedropped0/1, then the write to fenced register - * was successful - */ - if (!(status & fence_mask)) - return 0; - /* Wait a small amount of time before trying again */ - udelay(GMU_WAKEUP_DELAY_US); +static int gmu_test_bit(struct kgsl_device *device, enum gmu_core_flags flag) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); - /* Try to write the fenced register again */ - adreno_writereg(adreno_dev, offset, val); - } + return test_bit(flag, &gmu->flags); +} - dev_err(adreno_dev->dev.dev, - "GMU fenced register write timed out: reg 0x%x\n", reg_offset); - return -ETIMEDOUT; +static bool gmu_regulator_isenabled(struct kgsl_device *device) +{ + struct gmu_device *gmu = KGSL_GMU_DEVICE(device); + + return (gmu->gx_gdsc && regulator_is_enabled(gmu->gx_gdsc)); } + + +struct gmu_core_ops gmu_ops = { + .probe = gmu_probe, + .remove = gmu_remove, + .regread = gmu_regread, + .regwrite = gmu_regwrite, + .isenabled = gmu_isenabled, + .gpmu_isenabled = gmu_gpmu_isenabled, + .start = gmu_start, + .stop = gmu_stop, + .set_bit = gmu_set_bit, + .clear_bit = gmu_clear_bit, + .test_bit = gmu_test_bit, + .dcvs_set = gmu_dcvs_set, + .snapshot = gmu_snapshot, + .regulator_isenabled = gmu_regulator_isenabled, +}; diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h index 39d249403bb9c96532e0a2bd8d80dfb0985278cc..33605c8a04322a4a5926cef6cc7f5633a17e72cf 100644 --- a/drivers/gpu/msm/kgsl_gmu.h +++ b/drivers/gpu/msm/kgsl_gmu.h @@ -15,22 +15,9 @@ #include "kgsl_hfi.h" -#define GMU_INT_WDOG_BITE BIT(0) -#define GMU_INT_RSCC_COMP BIT(1) -#define GMU_INT_FENCE_ERR BIT(3) -#define GMU_INT_DBD_WAKEUP BIT(4) -#define GMU_INT_HOST_AHB_BUS_ERR BIT(5) -#define GMU_AO_INT_MASK \ - (GMU_INT_WDOG_BITE | \ - GMU_INT_HOST_AHB_BUS_ERR | \ - GMU_INT_FENCE_ERR) - #define MAX_GMUFW_SIZE 0x2000 /* in bytes */ #define FENCE_RANGE_MASK ((0x1 << 31) | ((0xA << 2) << 18) | (0x8A0)) -#define FENCE_STATUS_WRITEDROPPED0_MASK 0x1 -#define FENCE_STATUS_WRITEDROPPED1_MASK 0x2 - #define BWMEM_SIZE (12 + (4 * NUM_BW_LEVELS)) /*in bytes*/ /* Bitmask for GPU low power mode enabling and hysterisis*/ @@ -54,10 +41,6 @@ CX_VOTE_ENABLE | \ GFX_VOTE_ENABLE) -/* Bitmask for GPU idle status check */ -#define GPUBUSYIGNAHB BIT(23) -#define CXGXCPUBUSYIGNAHB BIT(30) - /* GMU timeouts */ #define GMU_IDLE_TIMEOUT 100 /* ms */ @@ -65,41 +48,11 @@ #define OOB_BOOT_OPTION 0 #define OOB_SLUMBER_OPTION 1 -/* - * OOB requests values. These range from 0 to 7 and then - * the BIT() offset into the actual value is calculated - * later based on the request. This keeps the math clean - * and easy to ensure not reaching over/under the range - * of 8 bits. - */ -enum oob_request { - oob_gpu = 0, - oob_perfcntr = 1, - oob_preempt = 2, - oob_boot_slumber = 6, /* reserved special case */ - oob_dcvs = 7, /* reserved special case */ -}; - -/* - * Wait time before trying to write the register again. - * Hopefully the GMU has finished waking up during this delay. - * This delay must be less than the IFPC main hysteresis or - * the GMU will start shutting down before we try again. - */ -#define GMU_WAKEUP_DELAY_US 10 -/* Max amount of tries to wake up the GMU. */ -#define GMU_WAKEUP_RETRY_MAX 60 +/* For GMU Logs*/ +#define LOGMEM_SIZE SZ_4K -/* Bits for the flags field in the gmu structure */ -enum gmu_flags { - GMU_BOOT_INIT_DONE = 0, - GMU_CLK_ON, - GMU_HFI_ON, - GMU_FAULT, - GMU_DCVS_REPLAY, - GMU_GPMU, - GMU_ENABLED, -}; +extern struct gmu_dev_ops adreno_a6xx_gmudev; +#define KGSL_GMU_DEVICE(_a) ((struct gmu_device *)((_a)->gmu_core.ptr)) /** * struct gmu_memdesc - Gmu shared memory object descriptor @@ -131,20 +84,6 @@ struct rpmh_votes_t { struct gmu_bw_votes cnoc_votes; }; -#define MAX_GMU_CLKS 6 -#define DEFAULT_GMU_FREQ_IDX 1 - -/* - * These are the different ways the GMU can boot. GMU_WARM_BOOT is waking up - * from slumber. GMU_COLD_BOOT is booting for the first time. GMU_RESET - * is a soft reset of the GMU. - */ -enum gmu_boot { - GMU_WARM_BOOT = 0, - GMU_COLD_BOOT = 1, - GMU_RESET = 2 -}; - enum gmu_load_mode { CACHED_LOAD_BOOT, CACHED_BOOT, @@ -178,9 +117,6 @@ enum gpu_idle_level { * @reg_phys: GMU CSR physical address * @reg_virt: GMU CSR virtual address * @reg_len: GMU CSR range - * @gmu2gpu_offset: address difference between GMU register set - * and GPU register set, the offset will be used when accessing - * gmu registers using offset defined in GPU register space. * @pdc_reg_virt: starting kernel virtual address for RPMh PDC registers * @gmu_interrupt_num: GMU interrupt number * @fw_image: descriptor of GMU memory that has GMU image in it @@ -218,7 +154,6 @@ struct gmu_device { unsigned long reg_phys; void __iomem *reg_virt; unsigned int reg_len; - unsigned int gmu2gpu_offset; void __iomem *pdc_reg_virt; unsigned int gmu_interrupt_num; struct gmu_memdesc cached_fw_image; @@ -252,20 +187,8 @@ struct gmu_device { unsigned int fault_count; }; -struct kgsl_device; -void gmu_snapshot(struct kgsl_device *device); - -bool kgsl_gmu_gpmu_isenabled(struct kgsl_device *device); -bool kgsl_gmu_isenabled(struct kgsl_device *device); - -int gmu_probe(struct kgsl_device *device, unsigned long flags); -void gmu_remove(struct kgsl_device *device); -int allocate_gmu_image(struct gmu_device *gmu, unsigned int size); -int gmu_start(struct kgsl_device *device); -void gmu_stop(struct kgsl_device *device); -int gmu_dcvs_set(struct gmu_device *gmu, unsigned int gpu_pwrlevel, - unsigned int bus_level); -int allocate_gmu_cached_fw(struct gmu_device *gmu); bool is_cached_fw_size_valid(uint32_t size_in_bytes); -void init_gmu_log_base(struct kgsl_device *device); +int allocate_gmu_cached_fw(struct gmu_device *gmu); +int allocate_gmu_image(struct gmu_device *gmu, unsigned int size); + #endif /* __KGSL_GMU_H */ diff --git a/drivers/gpu/msm/kgsl_gmu_core.c b/drivers/gpu/msm/kgsl_gmu_core.c new file mode 100644 index 0000000000000000000000000000000000000000..3a60a53dc3e2d787a42ff9699ae875baf82ee24a --- /dev/null +++ b/drivers/gpu/msm/kgsl_gmu_core.c @@ -0,0 +1,219 @@ +/* Copyright (c) 2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kgsl_device.h" +#include "kgsl_gmu_core.h" +#include "a6xx_reg.h" +#include "adreno.h" + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "kgsl_gmu." + +static bool nogmu; +module_param(nogmu, bool, 0444); +MODULE_PARM_DESC(nogmu, "Disable the GMU"); + +static const struct { + char *compat; + struct gmu_core_ops *core_ops; +} gmu_subtypes[] = { + {"qcom,gpu-gmu", &gmu_ops}, +}; + +int gmu_core_probe(struct kgsl_device *device) +{ + struct device_node *node; + struct gmu_core_ops *gmu_core_ops; + unsigned long flags; + int i = 0, ret = -ENXIO; + + flags = ADRENO_FEATURE(ADRENO_DEVICE(device), ADRENO_GPMU) ? + BIT(GMU_GPMU) : 0; + + for (i = 0; i < ARRAY_SIZE(gmu_subtypes); i++) { + node = of_find_compatible_node(device->pdev->dev.of_node, + NULL, gmu_subtypes[i].compat); + + if (node != NULL) + gmu_core_ops = gmu_subtypes[i].core_ops; + } + + /* No GMU in dt, no worries...hopefully */ + if (node == NULL) { + /* If we are trying to use GPMU and no GMU, that's bad */ + if (flags & BIT(GMU_GPMU)) + return ret; + /* Otherwise it's ok and nothing to do */ + return 0; + } + + if (gmu_core_ops && gmu_core_ops->probe) { + ret = gmu_core_ops->probe(device, node, flags); + if (ret == 0) + device->gmu_core.core_ops = gmu_core_ops; + } + + return ret; +} + +void gmu_core_remove(struct kgsl_device *device) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->remove) + gmu_core_ops->remove(device); +} + +bool gmu_core_isenabled(struct kgsl_device *device) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->isenabled) + return !nogmu && gmu_core_ops->isenabled(device); + + return false; +} + +bool gmu_core_gpmu_isenabled(struct kgsl_device *device) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->gpmu_isenabled) + return gmu_core_ops->gpmu_isenabled(device); + + return false; +} + +int gmu_core_start(struct kgsl_device *device) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->start) + return gmu_core_ops->start(device); + + return -EINVAL; +} + +void gmu_core_stop(struct kgsl_device *device) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->stop) + gmu_core_ops->stop(device); +} + +void gmu_core_snapshot(struct kgsl_device *device) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->snapshot) + gmu_core_ops->snapshot(device); +} + +int gmu_core_dcvs_set(struct kgsl_device *device, unsigned int gpu_pwrlevel, + unsigned int bus_level) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->dcvs_set) + return gmu_core_ops->dcvs_set(device, gpu_pwrlevel, bus_level); + + return -EINVAL; +} + +void gmu_core_setbit(struct kgsl_device *device, enum gmu_core_flags flag) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->set_bit) + return gmu_core_ops->set_bit(device, flag); +} + +void gmu_core_clearbit(struct kgsl_device *device, enum gmu_core_flags flag) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->clear_bit) + return gmu_core_ops->clear_bit(device, flag); +} + +int gmu_core_testbit(struct kgsl_device *device, enum gmu_core_flags flag) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->test_bit) + return gmu_core_ops->test_bit(device, flag); + + return -EINVAL; +} + +bool gmu_core_regulator_isenabled(struct kgsl_device *device) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->regulator_isenabled) + return gmu_core_ops->regulator_isenabled(device); + + return false; +} + +bool gmu_core_is_register_offset(struct kgsl_device *device, + unsigned int offsetwords) +{ + return (gmu_core_isenabled(device) && + (offsetwords >= device->gmu_core.gmu2gpu_offset) && + ((offsetwords - device->gmu_core.gmu2gpu_offset) * + sizeof(uint32_t) < device->gmu_core.reg_len)); +} + +void gmu_core_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->regread) + gmu_core_ops->regread(device, offsetwords, value); + else + *value = 0; +} + +void gmu_core_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value) +{ + struct gmu_core_ops *gmu_core_ops = GMU_CORE_OPS(device); + + if (gmu_core_ops && gmu_core_ops->regwrite) + gmu_core_ops->regwrite(device, offsetwords, value); +} + +void gmu_core_regrmw(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int mask, unsigned int bits) +{ + unsigned int val = 0; + + gmu_core_regread(device, offsetwords, &val); + val &= ~mask; + gmu_core_regwrite(device, offsetwords, val | bits); +} diff --git a/drivers/gpu/msm/kgsl_gmu_core.h b/drivers/gpu/msm/kgsl_gmu_core.h new file mode 100644 index 0000000000000000000000000000000000000000..40a9eddb604fcfd353f756ce7feb2c56687eaa45 --- /dev/null +++ b/drivers/gpu/msm/kgsl_gmu_core.h @@ -0,0 +1,185 @@ +/* Copyright (c) 2018, 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 __KGSL_GMU_CORE_H +#define __KGSL_GMU_CORE_H + +#define GMU_INT_WDOG_BITE BIT(0) +#define GMU_INT_RSCC_COMP BIT(1) +#define GMU_INT_FENCE_ERR BIT(3) +#define GMU_INT_DBD_WAKEUP BIT(4) +#define GMU_INT_HOST_AHB_BUS_ERR BIT(5) +#define GMU_AO_INT_MASK \ + (GMU_INT_WDOG_BITE | \ + GMU_INT_HOST_AHB_BUS_ERR | \ + GMU_INT_FENCE_ERR) + +/* GMU_DEVICE - Given an KGSL device return the GMU specific struct */ +#define GMU_DEVICE_OPS(_a) ((_a)->gmu_core.dev_ops) +#define GMU_CORE_OPS(_a) ((_a)->gmu_core.core_ops) + +#define NUM_BW_LEVELS 100 +#define MAX_GX_LEVELS 16 +#define MAX_CX_LEVELS 4 +#define MAX_CNOC_LEVELS 2 +#define MAX_CNOC_CMDS 6 +#define MAX_BW_CMDS 8 +#define INVALID_DCVS_IDX 0xFF + +#if MAX_CNOC_LEVELS > MAX_GX_LEVELS +#error "CNOC levels cannot exceed GX levels" +#endif + +#define MAX_GMU_CLKS 6 +#define DEFAULT_GMU_FREQ_IDX 1 + +/* + * These are the different ways the GMU can boot. GMU_WARM_BOOT is waking up + * from slumber. GMU_COLD_BOOT is booting for the first time. GMU_RESET + * is a soft reset of the GMU. + */ +enum gmu_core_boot { + GMU_WARM_BOOT = 0, + GMU_COLD_BOOT = 1, + GMU_RESET = 2 +}; + +/* Bits for the flags field in the gmu structure */ +enum gmu_core_flags { + GMU_BOOT_INIT_DONE = 0, + GMU_CLK_ON, + GMU_HFI_ON, + GMU_FAULT, + GMU_DCVS_REPLAY, + GMU_GPMU, + GMU_ENABLED, + GMU_RSCC_SLEEP_SEQ_DONE, +}; + +/* + * OOB requests values. These range from 0 to 7 and then + * the BIT() offset into the actual value is calculated + * later based on the request. This keeps the math clean + * and easy to ensure not reaching over/under the range + * of 8 bits. + */ +enum oob_request { + oob_gpu = 0, + oob_perfcntr = 1, + oob_preempt = 2, + oob_boot_slumber = 6, /* reserved special case */ + oob_dcvs = 7, /* reserved special case */ +}; + +/* + * Wait time before trying to write the register again. + * Hopefully the GMU has finished waking up during this delay. + * This delay must be less than the IFPC main hysteresis or + * the GMU will start shutting down before we try again. + */ +#define GMU_CORE_WAKEUP_DELAY_US 10 +/* Max amount of tries to wake up the GMU. */ +#define GMU_CORE_WAKEUP_RETRY_MAX 60 + +#define FENCE_STATUS_WRITEDROPPED0_MASK 0x1 +#define FENCE_STATUS_WRITEDROPPED1_MASK 0x2 + +struct kgsl_device; +struct adreno_device; +struct kgsl_snapshot; + +struct gmu_core_ops { + int (*probe)(struct kgsl_device *device, struct device_node *node, + unsigned long flags); + void (*remove)(struct kgsl_device *device); + void (*regread)(struct kgsl_device *device, + unsigned int offsetwords, unsigned int *value); + void (*regwrite)(struct kgsl_device *device, + unsigned int offsetwords, unsigned int value); + bool (*isenabled)(struct kgsl_device *device); + bool (*gpmu_isenabled)(struct kgsl_device *device); + int (*dcvs_set)(struct kgsl_device *device, + unsigned int gpu_pwrlevel, unsigned int bus_level); + void (*set_bit)(struct kgsl_device *device, enum gmu_core_flags flag); + void (*clear_bit)(struct kgsl_device *device, enum gmu_core_flags flag); + int (*test_bit)(struct kgsl_device *device, enum gmu_core_flags flag); + int (*start)(struct kgsl_device *device); + void (*stop)(struct kgsl_device *device); + void (*snapshot)(struct kgsl_device *device); + int (*get_idle_level)(struct kgsl_device *device); + void (*set_idle_level)(struct kgsl_device *device, unsigned int val); + bool (*regulator_isenabled)(struct kgsl_device *device); +}; + +struct gmu_dev_ops { + int (*load_firmware)(struct kgsl_device *device); + int (*oob_set)(struct adreno_device *adreno_dev, + enum oob_request req); + void (*oob_clear)(struct adreno_device *adreno_dev, + enum oob_request req); + int (*hfi_start_msg)(struct adreno_device *adreno_dev); + void (*enable_lm)(struct kgsl_device *device); + int (*rpmh_gpu_pwrctrl)(struct adreno_device *, unsigned int ops, + unsigned int arg1, unsigned int arg2); + int (*wait_for_lowest_idle)(struct adreno_device *); + int (*wait_for_gmu_idle)(struct adreno_device *); + int (*sptprac_enable)(struct adreno_device *adreno_dev); + void (*sptprac_disable)(struct adreno_device *adreno_dev); + int (*ifpc_store)(struct adreno_device *adreno_dev, + unsigned int val); + unsigned int (*ifpc_show)(struct adreno_device *adreno_dev); + void (*snapshot)(struct adreno_device *, struct kgsl_snapshot *); +}; + +/** + * struct gmu_core_device - GMU Core device structure + * @ptr: Pointer to GMU device structure + * @gmu2gpu_offset: address difference between GMU register set + * and GPU register set, the offset will be used when accessing + * gmu registers using offset defined in GPU register space. + * @reg_len: GMU registers length + * @core_ops: Pointer to gmu core operations + * @dev_ops: Pointer to gmu device operations + */ +struct gmu_core_device { + void *ptr; + unsigned int gmu2gpu_offset; + unsigned int reg_len; + struct gmu_core_ops *core_ops; + struct gmu_dev_ops *dev_ops; +}; + +/* GMU core functions */ +extern struct gmu_core_ops gmu_ops; + +int gmu_core_probe(struct kgsl_device *device); +void gmu_core_remove(struct kgsl_device *device); +int gmu_core_start(struct kgsl_device *device); +void gmu_core_stop(struct kgsl_device *device); +void gmu_core_snapshot(struct kgsl_device *device); +bool gmu_core_gpmu_isenabled(struct kgsl_device *device); +bool gmu_core_isenabled(struct kgsl_device *device); +int gmu_core_dcvs_set(struct kgsl_device *device, unsigned int gpu_pwrlevel, + unsigned int bus_level); +void gmu_core_setbit(struct kgsl_device *device, enum gmu_core_flags flag); +void gmu_core_clearbit(struct kgsl_device *device, enum gmu_core_flags flag); +int gmu_core_testbit(struct kgsl_device *device, enum gmu_core_flags flag); +bool gmu_core_regulator_isenabled(struct kgsl_device *device); +bool gmu_core_is_register_offset(struct kgsl_device *device, + unsigned int offsetwords); +void gmu_core_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value); +void gmu_core_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value); +void gmu_core_regrmw(struct kgsl_device *device, unsigned int offsetwords, + unsigned int mask, unsigned int bits); +#endif /* __KGSL_GMU_CORE_H */ diff --git a/drivers/gpu/msm/kgsl_hfi.c b/drivers/gpu/msm/kgsl_hfi.c index 83364bcc75baec4d001789fd3f4b1c2751cb2fdc..69bb16c1db331cdca6f375b6c01e3a4dfb620c8e 100644 --- a/drivers/gpu/msm/kgsl_hfi.c +++ b/drivers/gpu/msm/kgsl_hfi.c @@ -45,22 +45,6 @@ (((minor) & 0x7FFFFF) << 5) | \ ((branch) & 0x1F)) -/* - * hfi_align_idx() - Update index based on HFI version - * @gmu: Pointer to a GMU device - * @idx: Integer that is either a read or write index - * @boundary: boundary of adjusted ptr value. Exceeding the boundary - * will reset ptr to 0 - */ -static int hfi_align_idx(struct gmu_device *gmu, int idx, int boundary) -{ - if (HFI_VER_MAJOR(&gmu->hfi) >= 2) { - idx = ALIGN(idx, SZ_4); - return (idx < boundary) ? idx : 0; - } - return idx; -} - /* Size in below functions are in unit of dwords */ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx, unsigned int *output, unsigned int max_size) @@ -109,7 +93,10 @@ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx, result = -ENODATA; } - hdr->read_index = hfi_align_idx(gmu, read, hdr->queue_size); + if (HFI_VER_MAJOR(&gmu->hfi) >= 2) + read = ALIGN(read, SZ_4) % hdr->queue_size; + + hdr->read_index = read; return result; } @@ -118,7 +105,7 @@ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx, static int hfi_queue_write(struct gmu_device *gmu, uint32_t queue_idx, uint32_t *msg) { - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); + struct kgsl_device *device = kgsl_get_device(KGSL_DEVICE_3D0); struct hfi_queue_table *tbl = gmu->hfi_mem->hostptr; struct hfi_queue_header *hdr = &tbl->qhdr[queue_idx]; uint32_t *queue; @@ -164,7 +151,13 @@ static int hfi_queue_write(struct gmu_device *gmu, uint32_t queue_idx, write = (write + 1) % hdr->queue_size; } - hdr->write_index = hfi_align_idx(gmu, write, hdr->queue_size); + /* Cookify any non used data at the end of the write buffer */ + if (HFI_VER_MAJOR(&gmu->hfi) >= 2) { + for (; write % 4; write = (write + 1) % hdr->queue_size) + queue[write] = 0xFAFAFAFA; + } + + hdr->write_index = write; mutex_unlock(&hfi->cmdq_mutex); @@ -404,17 +397,6 @@ static int hfi_send_feature_ctrls(struct gmu_device *gmu) return 0; } -static int hfi_send_init_perf_vote(struct gmu_device *gmu) -{ - struct hfi_gx_bw_perf_vote_cmd req = { - .ack_type = DCVS_ACK_BLOCK, - .freq = 2, - .bw = 2, - }; - - return hfi_send_req(gmu, H2F_MSG_GX_BW_PERF_VOTE, &req); -} - static int hfi_send_dcvstbl(struct gmu_device *gmu) { struct hfi_dcvstable_cmd cmd = { @@ -485,8 +467,10 @@ static void receive_err_req(struct gmu_device *gmu, void *rcvd) { struct hfi_err_cmd *cmd = rcvd; - dev_err(&gmu->pdev->dev, "HFI Error Received: %d %d %d\n", - cmd->error_code, cmd->data[0], cmd->data[1]); + dev_err(&gmu->pdev->dev, "HFI Error Received: %d %d %s\n", + ((cmd->error_code >> 16) & 0xFFFF), + (cmd->error_code & 0xFFFF), + (char *) cmd->data); } static void receive_debug_req(struct gmu_device *gmu, void *rcvd) @@ -525,40 +509,49 @@ void hfi_receiver(unsigned long data) { struct gmu_device *gmu; uint32_t rcvd[MAX_RCVD_SIZE]; + int read_queue[] = { + HFI_MSG_IDX, + HFI_DBG_IDX, + }; + int q; if (!data) return; gmu = (struct gmu_device *)data; - while (hfi_queue_read(gmu, HFI_MSG_IDX, rcvd, sizeof(rcvd)) > 0) { - /* Special case if we're v1 */ - if (HFI_VER_MAJOR(&gmu->hfi) < 2) { - hfi_v1_receiver(gmu, rcvd); - continue; - } - - /* V2 ACK Handler */ - if (MSG_HDR_GET_TYPE(rcvd[0]) == HFI_MSG_ACK) { - receive_ack_cmd(gmu, rcvd); - continue; - } - - /* V2 Request Handler */ - switch (MSG_HDR_GET_ID(rcvd[0])) { - case F2H_MSG_ERR: /* No Reply */ - receive_err_req(gmu, rcvd); - break; - case F2H_MSG_DEBUG: /* No Reply */ - receive_debug_req(gmu, rcvd); - break; - default: /* No Reply */ - dev_err(&gmu->pdev->dev, - "HFI request %d not supported\n", - MSG_HDR_GET_ID(rcvd[0])); - break; - } - }; + /* While we are here, check all of the queues for messages */ + for (q = 0; q < ARRAY_SIZE(read_queue); q++) { + while (hfi_queue_read(gmu, read_queue[q], + rcvd, sizeof(rcvd)) > 0) { + /* Special case if we're v1 */ + if (HFI_VER_MAJOR(&gmu->hfi) < 2) { + hfi_v1_receiver(gmu, rcvd); + continue; + } + + /* V2 ACK Handler */ + if (MSG_HDR_GET_TYPE(rcvd[0]) == HFI_MSG_ACK) { + receive_ack_cmd(gmu, rcvd); + continue; + } + + /* V2 Request Handler */ + switch (MSG_HDR_GET_ID(rcvd[0])) { + case F2H_MSG_ERR: /* No Reply */ + receive_err_req(gmu, rcvd); + break; + case F2H_MSG_DEBUG: /* No Reply */ + receive_debug_req(gmu, rcvd); + break; + default: /* No Reply */ + dev_err(&gmu->pdev->dev, + "HFI request %d not supported\n", + MSG_HDR_GET_ID(rcvd[0])); + break; + } + }; + } } #define GMU_VER_MAJOR(ver) (((ver) >> 28) & 0xF) @@ -566,9 +559,9 @@ void hfi_receiver(unsigned long data) #define GMU_VERSION(major, minor) \ ((((major) & 0xF) << 28) | (((minor) & 0xFFF) << 16)) -static int hfi_verify_fw_version(struct gmu_device *gmu) +static int hfi_verify_fw_version(struct kgsl_device *device, + struct gmu_device *gmu) { - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int result; unsigned int ver, major, minor; @@ -608,9 +601,9 @@ static int hfi_verify_fw_version(struct gmu_device *gmu) return 0; } -int hfi_start(struct gmu_device *gmu, uint32_t boot_state) +int hfi_start(struct kgsl_device *device, + struct gmu_device *gmu, uint32_t boot_state) { - struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int result; @@ -623,7 +616,7 @@ int hfi_start(struct gmu_device *gmu, uint32_t boot_state) return result; } - result = hfi_verify_fw_version(gmu); + result = hfi_verify_fw_version(device, gmu); if (result) return result; @@ -647,11 +640,6 @@ int hfi_start(struct gmu_device *gmu, uint32_t boot_state) result = hfi_send_core_fw_start(gmu); if (result) return result; - - /* Force a vote with initial values */ - result = hfi_send_init_perf_vote(gmu); - if (result) - return result; } else { /* * Tell the GMU we are sending no more HFIs diff --git a/drivers/gpu/msm/kgsl_hfi.h b/drivers/gpu/msm/kgsl_hfi.h index e358f5c63915b8dceaa53bedc49c3b97543925a0..d1100e05ebaa4d9f5e2fc76074d9f637a902b47a 100644 --- a/drivers/gpu/msm/kgsl_hfi.h +++ b/drivers/gpu/msm/kgsl_hfi.h @@ -25,6 +25,8 @@ #define HFI_QUEUE_DISPATCH_CNT 1 #define HFI_QUEUE_MAX (HFI_QUEUE_DEFAULT_CNT + HFI_QUEUE_DISPATCH_CNT) +#define HFIMEM_SIZE (HFI_QUEUE_SIZE * (HFI_QUEUE_MAX + 1)) + #define HFI_CMD_ID 0 #define HFI_MSG_ID 1 #define HFI_DBG_ID 2 @@ -61,6 +63,8 @@ #define HFI_IRQ_CM3_FAULT_MASK BIT(15) #define HFI_IRQ_OOB_MASK GENMASK(31, 16) #define HFI_IRQ_MASK (HFI_IRQ_MSGQ_MASK |\ + HFI_IRQ_SIDEMSGQ_MASK |\ + HFI_IRQ_DBGQ_MASK |\ HFI_IRQ_CM3_FAULT_MASK) #define CLKSET_OPTION_DEFAULT 0 @@ -196,18 +200,6 @@ enum hfi_msg_type { #define H2F_MSG_CONTEXT_RULE 140 /* AKA constraint */ #define F2H_MSG_CONTEXT_BAD 150 -#define NUM_BW_LEVELS 100 -#define MAX_GX_LEVELS 16 -#define MAX_CX_LEVELS 4 -#define MAX_CNOC_LEVELS 2 -#define MAX_CNOC_CMDS 6 -#define MAX_BW_CMDS 8 -#define INVALID_DCVS_IDX 0xFF - -#if MAX_CNOC_LEVELS > MAX_GX_LEVELS -#error "CNOC levels cannot exceed GX levels" -#endif - /* H2F */ struct hfi_gmu_init_cmd { uint32_t hdr; @@ -621,7 +613,8 @@ struct kgsl_hfi { struct gmu_device; struct gmu_memdesc; -int hfi_start(struct gmu_device *gmu, uint32_t boot_state); +int hfi_start(struct kgsl_device *device, struct gmu_device *gmu, + uint32_t boot_state); void hfi_stop(struct gmu_device *gmu); void hfi_receiver(unsigned long data); void hfi_init(struct kgsl_hfi *hfi, struct gmu_memdesc *mem_addr, diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index ded7dde1a5a399083761c950ea5b419c5fc69aff..439e3d24a32dc0f2289fc4949cf05c4e0677bf3e 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -232,7 +232,7 @@ static void kgsl_iommu_add_global(struct kgsl_mmu *mmu, memdesc->gpuaddr = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu) + global_pt_alloc; memdesc->priv |= KGSL_MEMDESC_GLOBAL; - global_pt_alloc += memdesc->size; + global_pt_alloc += kgsl_memdesc_footprint(memdesc); global_pt_entries[global_pt_count].memdesc = memdesc; strlcpy(global_pt_entries[global_pt_count].name, name, @@ -260,13 +260,12 @@ static void kgsl_setup_qdss_desc(struct kgsl_device *device) return; } - gpu_qdss_desc.flags = 0; + kgsl_memdesc_init(device, &gpu_qdss_desc, 0); gpu_qdss_desc.priv = 0; gpu_qdss_desc.physaddr = gpu_qdss_entry[0]; gpu_qdss_desc.size = gpu_qdss_entry[1]; gpu_qdss_desc.pagetable = NULL; gpu_qdss_desc.ops = NULL; - gpu_qdss_desc.dev = device->dev->parent; gpu_qdss_desc.hostptr = NULL; result = memdesc_sg_dma(&gpu_qdss_desc, gpu_qdss_desc.physaddr, @@ -305,13 +304,12 @@ static void kgsl_setup_qtimer_desc(struct kgsl_device *device) return; } - gpu_qtimer_desc.flags = 0; + kgsl_memdesc_init(device, &gpu_qtimer_desc, 0); gpu_qtimer_desc.priv = 0; gpu_qtimer_desc.physaddr = gpu_qtimer_entry[0]; gpu_qtimer_desc.size = gpu_qtimer_entry[1]; gpu_qtimer_desc.pagetable = NULL; gpu_qtimer_desc.ops = NULL; - gpu_qtimer_desc.dev = device->dev->parent; gpu_qtimer_desc.hostptr = NULL; result = memdesc_sg_dma(&gpu_qtimer_desc, gpu_qtimer_desc.physaddr, @@ -370,22 +368,33 @@ static int _attach_pt(struct kgsl_iommu_pt *iommu_pt, return ret; } -static int _iommu_map_sync_pc(struct kgsl_pagetable *pt, - uint64_t gpuaddr, phys_addr_t physaddr, - uint64_t size, unsigned int flags) +static int _iommu_map_single_page_sync_pc(struct kgsl_pagetable *pt, + uint64_t gpuaddr, phys_addr_t physaddr, int times, + unsigned int flags) { struct kgsl_iommu_pt *iommu_pt = pt->priv; - int ret; + size_t mapped = 0; + int i; + int ret = 0; _iommu_sync_mmu_pc(true); - ret = iommu_map(iommu_pt->domain, gpuaddr, physaddr, size, flags); + for (i = 0; i < times; i++) { + ret = iommu_map(iommu_pt->domain, gpuaddr + mapped, + physaddr, PAGE_SIZE, flags); + if (ret) + break; + mapped += PAGE_SIZE; + } + + if (ret) + iommu_unmap(iommu_pt->domain, gpuaddr, mapped); _iommu_sync_mmu_pc(false); if (ret) { - KGSL_CORE_ERR("map err: 0x%016llX, 0x%llx, 0x%x, %d\n", - gpuaddr, size, flags, ret); + KGSL_CORE_ERR("map err: 0x%016llX, 0x%lx, 0x%x, %d\n", + gpuaddr, PAGE_SIZE * times, flags, ret); return -ENODEV; } @@ -505,7 +514,7 @@ static int _iommu_map_sg_sync_pc(struct kgsl_pagetable *pt, */ static struct page *kgsl_guard_page; -static struct kgsl_memdesc kgsl_secure_guard_page_memdesc; +static struct page *kgsl_secure_guard_page; /* * The dummy page is a placeholder/extra page to be used for sparse mappings. @@ -787,6 +796,10 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain, fault_type = "translation"; else if (flags & IOMMU_FAULT_PERMISSION) fault_type = "permission"; + else if (flags & IOMMU_FAULT_EXTERNAL) + fault_type = "external"; + else if (flags & IOMMU_FAULT_TRANSACTION_STALLED) + fault_type = "transaction stalled"; if (kgsl_iommu_suppress_pagefault(addr, write, context)) { iommu->pagefault_suppression_count++; @@ -1052,7 +1065,7 @@ static void setup_64bit_pagetable(struct kgsl_mmu *mmu, if (pagetable->name != KGSL_MMU_GLOBAL_PT && pagetable->name != KGSL_MMU_SECURE_PT) { - if ((BITS_PER_LONG == 32) || is_compat_task()) { + if (kgsl_is_compat_task()) { pt->svm_start = KGSL_IOMMU_SVM_BASE32; pt->svm_end = KGSL_IOMMU_SECURE_BASE(mmu); } else { @@ -1155,8 +1168,13 @@ void _enable_gpuhtw_llc(struct kgsl_mmu *mmu, struct kgsl_iommu_pt *iommu_pt) return; /* Domain attribute to enable system cache for GPU pagetable walks */ - ret = iommu_domain_set_attr(iommu_pt->domain, + if (adreno_is_a640(adreno_dev)) + ret = iommu_domain_set_attr(iommu_pt->domain, + DOMAIN_ATTR_USE_LLC_NWA, &gpuhtw_llc_enable); + else + ret = iommu_domain_set_attr(iommu_pt->domain, DOMAIN_ATTR_USE_UPSTREAM_HINT, &gpuhtw_llc_enable); + /* * Warn that the system cache will not be used for GPU * pagetable walks. This is not a fatal error. @@ -1460,7 +1478,8 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu) if (iommu->regbase != NULL) iounmap(iommu->regbase); - kgsl_sharedmem_free(&kgsl_secure_guard_page_memdesc); + kgsl_free_secure_page(kgsl_secure_guard_page); + kgsl_secure_guard_page = NULL; if (kgsl_guard_page != NULL) { __free_page(kgsl_guard_page); @@ -1483,6 +1502,7 @@ static int _setstate_alloc(struct kgsl_device *device, { int ret; + kgsl_memdesc_init(device, &iommu->setstate, 0); ret = kgsl_sharedmem_alloc_contig(device, &iommu->setstate, PAGE_SIZE); if (!ret) { @@ -1731,9 +1751,11 @@ static int _iommu_map_guard_page(struct kgsl_pagetable *pt, uint64_t gpuaddr, unsigned int protflags) { + uint64_t pad_size; phys_addr_t physaddr; - if (!kgsl_memdesc_has_guard_page(memdesc)) + pad_size = kgsl_memdesc_footprint(memdesc) - memdesc->size; + if (!pad_size) return 0; /* @@ -1743,21 +1765,16 @@ static int _iommu_map_guard_page(struct kgsl_pagetable *pt, * mapped to save 1MB of memory if CPZ is not used. */ if (kgsl_memdesc_is_secured(memdesc)) { - struct scatterlist *sg; - unsigned int sgp_size = pt->mmu->secure_align_mask + 1; - - if (!kgsl_secure_guard_page_memdesc.sgt) { - if (kgsl_allocate_user(KGSL_MMU_DEVICE(pt->mmu), - &kgsl_secure_guard_page_memdesc, - sgp_size, KGSL_MEMFLAGS_SECURE)) { + if (!kgsl_secure_guard_page) { + kgsl_secure_guard_page = kgsl_alloc_secure_page(); + if (!kgsl_secure_guard_page) { KGSL_CORE_ERR( "Secure guard page alloc failed\n"); return -ENOMEM; } } - sg = kgsl_secure_guard_page_memdesc.sgt->sgl; - physaddr = page_to_phys(sg_page(sg)); + physaddr = page_to_phys(kgsl_secure_guard_page); } else { if (kgsl_guard_page == NULL) { kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO | @@ -1769,15 +1786,28 @@ static int _iommu_map_guard_page(struct kgsl_pagetable *pt, physaddr = page_to_phys(kgsl_guard_page); } - return _iommu_map_sync_pc(pt, gpuaddr, physaddr, - kgsl_memdesc_guard_page_size(memdesc), - protflags & ~IOMMU_WRITE); + if (!MMU_FEATURE(pt->mmu, KGSL_MMU_PAD_VA)) + protflags &= ~IOMMU_WRITE; + + return _iommu_map_single_page_sync_pc(pt, gpuaddr, physaddr, + pad_size >> PAGE_SHIFT, protflags); } -static unsigned int _get_protection_flags(struct kgsl_memdesc *memdesc) +static unsigned int _get_protection_flags(struct kgsl_pagetable *pt, + struct kgsl_memdesc *memdesc) { unsigned int flags = IOMMU_READ | IOMMU_WRITE | - IOMMU_NOEXEC | IOMMU_USE_UPSTREAM_HINT; + IOMMU_NOEXEC; + int ret, llc_nwa = 0; + struct kgsl_iommu_pt *iommu_pt = pt->priv; + + ret = iommu_domain_get_attr(iommu_pt->domain, + DOMAIN_ATTR_USE_LLC_NWA, &llc_nwa); + + if (ret || (llc_nwa == 0)) + flags |= IOMMU_USE_UPSTREAM_HINT; + else + flags |= IOMMU_USE_LLC_NWA; if (memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY) flags &= ~IOMMU_WRITE; @@ -1801,7 +1831,7 @@ kgsl_iommu_map(struct kgsl_pagetable *pt, int ret; uint64_t addr = memdesc->gpuaddr; uint64_t size = memdesc->size; - unsigned int flags = _get_protection_flags(memdesc); + unsigned int flags = _get_protection_flags(pt, memdesc); struct sg_table *sgt = NULL; /* @@ -1931,7 +1961,7 @@ static int kgsl_iommu_map_offset(struct kgsl_pagetable *pt, uint64_t size, uint64_t feature_flag) { int pg_sz; - unsigned int protflags = _get_protection_flags(memdesc); + unsigned int protflags = _get_protection_flags(pt, memdesc); int ret; struct sg_table *sgt = NULL; diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 430a14081d35fbc04677d41ab2adf8776e05455c..4b06654fc21ae3079f7edc294373fb835866c3f4 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2018, 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 @@ -140,6 +140,8 @@ struct kgsl_mmu_pt_ops { #define KGSL_MMU_NEED_GUARD_PAGE BIT(9) /* The device supports IO coherency */ #define KGSL_MMU_IO_COHERENT BIT(10) +/* The device requires VA mappings padded up to a given size */ +#define KGSL_MMU_PAD_VA BIT(11) /** * struct kgsl_mmu - Master definition for KGSL MMU devices @@ -151,6 +153,7 @@ struct kgsl_mmu_pt_ops { * @secured: True if the MMU needs to be secured * @feature: Static list of MMU features * @secure_aligned_mask: Mask that secure buffers need to be aligned to + * @va_padding: Size to pad VA mappings to * @priv: Union of sub-device specific members */ struct kgsl_mmu { @@ -162,6 +165,7 @@ struct kgsl_mmu { bool secured; unsigned long features; unsigned int secure_align_mask; + uint64_t va_padding; union { struct kgsl_iommu iommu; } priv; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index c77bb17e68bd63199b7a6309d75b62b70fd1a9fb..fd53cc13e7368b11ecde4679fbc85c8a6af4b004 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -26,7 +26,6 @@ #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_device.h" -#include "kgsl_gmu.h" #include "kgsl_trace.h" #define KGSL_PWRFLAGS_POWER_ON 0 @@ -226,7 +225,7 @@ static int kgsl_bus_scale_request(struct kgsl_device *device, int ret = 0; /* GMU scales BW */ - if (kgsl_gmu_gpmu_isenabled(device)) + if (gmu_core_gpmu_isenabled(device)) return 0; if (pwr->pcl) { @@ -248,28 +247,32 @@ static int kgsl_bus_scale_request(struct kgsl_device *device, int kgsl_clk_set_rate(struct kgsl_device *device, unsigned int pwrlevel) { - struct gmu_device *gmu = &device->gmu; struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwrlevel *pl = &pwr->pwrlevels[pwrlevel]; int ret = 0; /* GMU scales GPU freq */ - if (kgsl_gmu_gpmu_isenabled(device)) { + if (gmu_core_gpmu_isenabled(device)) { + int num_gpupwrlevels = pwr->num_pwrlevels; + /* If GMU has not been started, save it */ - if (!test_bit(GMU_HFI_ON, &gmu->flags)) { + if (!gmu_core_testbit(device, GMU_HFI_ON)) { /* store clock change request */ - set_bit(GMU_DCVS_REPLAY, &gmu->flags); + gmu_core_setbit(device, GMU_DCVS_REPLAY); return 0; } + if (num_gpupwrlevels < 0) + return -EINVAL; + /* If the GMU is on we cannot vote for the lowest level */ - if (pwrlevel == (gmu->num_gpupwrlevels - 1)) { + if (pwrlevel == (num_gpupwrlevels - 1)) { WARN(1, "Cannot set 0 GPU frequency with GMU\n"); return -EINVAL; } - ret = gmu_dcvs_set(gmu, pwrlevel, INVALID_DCVS_IDX); + ret = gmu_core_dcvs_set(device, pwrlevel, INVALID_DCVS_IDX); /* indicate actual clock change */ - clear_bit(GMU_DCVS_REPLAY, &gmu->flags); + gmu_core_clearbit(device, GMU_DCVS_REPLAY); } else /* Linux clock driver scales GPU freq */ ret = kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0], @@ -442,7 +445,7 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, kgsl_pwrctrl_set_thermal_cycle(device, new_level); if (new_level == old_level && - !test_bit(GMU_DCVS_REPLAY, &device->gmu.flags)) + !gmu_core_testbit(device, GMU_DCVS_REPLAY)) return; kgsl_pwrscale_update_stats(device); @@ -1701,7 +1704,7 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, struct kgsl_pwrctrl *pwr = &device->pwrctrl; int i = 0; - if (kgsl_gmu_gpmu_isenabled(device)) + if (gmu_core_gpmu_isenabled(device)) return; if (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->ctrl_flags)) return; @@ -1811,9 +1814,6 @@ static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; - if (kgsl_gmu_gpmu_isenabled(device)) - return; - if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->ctrl_flags)) return; @@ -1880,7 +1880,7 @@ static int kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state) struct kgsl_pwrctrl *pwr = &device->pwrctrl; int status = 0; - if (kgsl_gmu_gpmu_isenabled(device)) + if (gmu_core_gpmu_isenabled(device)) return 0; /* * Disabling the regulator means also disabling dependent clocks. @@ -2579,7 +2579,7 @@ void kgsl_pre_hwaccess(struct kgsl_device *device) * A register access without device power will cause a fatal timeout. * This is not valid for targets with a GMU. */ - if (!kgsl_gmu_gpmu_isenabled(device)) + if (!gmu_core_gpmu_isenabled(device)) WARN_ON(!kgsl_pwrctrl_isenabled(device)); } EXPORT_SYMBOL(kgsl_pre_hwaccess); @@ -2600,8 +2600,8 @@ static int kgsl_pwrctrl_enable(struct kgsl_device *device) kgsl_pwrctrl_pwrlevel_change(device, level); - if (kgsl_gmu_gpmu_isenabled(device)) { - int ret = gmu_start(device); + if (gmu_core_gpmu_isenabled(device)) { + int ret = gmu_core_start(device); if (!ret) kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); @@ -2619,9 +2619,9 @@ static int kgsl_pwrctrl_enable(struct kgsl_device *device) static void kgsl_pwrctrl_disable(struct kgsl_device *device) { - if (kgsl_gmu_gpmu_isenabled(device)) { + if (gmu_core_gpmu_isenabled(device)) { kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); - return gmu_stop(device); + return gmu_core_stop(device); } /* Order pwrrail/clk sequence based upon platform */ @@ -2764,14 +2764,13 @@ static int _aware(struct kgsl_device *device) { int status = 0; - struct gmu_device *gmu = &device->gmu; unsigned int state = device->state; switch (device->state) { case KGSL_STATE_RESET: - if (!kgsl_gmu_gpmu_isenabled(device)) + if (!gmu_core_gpmu_isenabled(device)) break; - status = gmu_start(device); + status = gmu_core_start(device); break; case KGSL_STATE_INIT: status = kgsl_pwrctrl_enable(device); @@ -2786,8 +2785,8 @@ _aware(struct kgsl_device *device) break; case KGSL_STATE_SLUMBER: /* if GMU already in FAULT */ - if (kgsl_gmu_isenabled(device) && - test_bit(GMU_FAULT, &gmu->flags)) { + if (gmu_core_isenabled(device) && + gmu_core_testbit(device, GMU_FAULT)) { status = -EINVAL; break; } @@ -2799,10 +2798,10 @@ _aware(struct kgsl_device *device) } if (status) { - if (kgsl_gmu_isenabled(device)) { + if (gmu_core_isenabled(device)) { /* GMU hang recovery */ kgsl_pwrctrl_set_state(device, KGSL_STATE_RESET); - set_bit(GMU_FAULT, &gmu->flags); + gmu_core_setbit(device, GMU_FAULT); status = kgsl_pwrctrl_enable(device); if (status) { /* @@ -2820,7 +2819,7 @@ _aware(struct kgsl_device *device) KGSL_STATE_AWARE); } - clear_bit(GMU_FAULT, &gmu->flags); + gmu_core_clearbit(device, GMU_FAULT); return status; } diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 5b376a406f54a7dd35767c59fc2aa743789ae670..e207a073c78a5c5e6e9fad9d22e9a3274abfe4e6 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -288,8 +288,19 @@ void kgsl_pwrscale_enable(struct kgsl_device *device) if (WARN_ON(!mutex_is_locked(&device->mutex))) return; - device->pwrscale.enabled = false; - return; + if (device->pwrscale.devfreqptr) { + queue_work(device->pwrscale.devfreq_wq, + &device->pwrscale.devfreq_resume_ws); + device->pwrscale.enabled = true; + } else { + /* + * Don't enable it if devfreq is not set and let the device + * run at default level; + */ + kgsl_pwrctrl_pwrlevel_change(device, + device->pwrctrl.default_pwrlevel); + device->pwrscale.enabled = false; + } } EXPORT_SYMBOL(kgsl_pwrscale_enable); @@ -974,24 +985,12 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) data->disable_busy_time_burst = of_property_read_bool( device->pdev->dev.of_node, "qcom,disable-busy-time-burst"); - data->ctxt_aware_enable = - of_property_read_bool(device->pdev->dev.of_node, - "qcom,enable-ca-jump"); - - if (data->ctxt_aware_enable) { - if (of_property_read_u32(device->pdev->dev.of_node, - "qcom,ca-target-pwrlevel", - &data->bin.ctxt_aware_target_pwrlevel)) - data->bin.ctxt_aware_target_pwrlevel = 1; - - if ((data->bin.ctxt_aware_target_pwrlevel > - pwr->num_pwrlevels)) - data->bin.ctxt_aware_target_pwrlevel = 1; - - if (of_property_read_u32(device->pdev->dev.of_node, - "qcom,ca-busy-penalty", - &data->bin.ctxt_aware_busy_penalty)) - data->bin.ctxt_aware_busy_penalty = 12000; + if (pwrscale->ctxt_aware_enable) { + data->ctxt_aware_enable = pwrscale->ctxt_aware_enable; + data->bin.ctxt_aware_target_pwrlevel = + pwrscale->ctxt_aware_target_pwrlevel; + data->bin.ctxt_aware_busy_penalty = + pwrscale->ctxt_aware_busy_penalty; } if (of_property_read_bool(device->pdev->dev.of_node, diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h index 604a779dabb95c6ed545b9fbac9eb8112c61cf08..9e28b6594b8d38f30c56ce164f28adf111ca67a3 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.h +++ b/drivers/gpu/msm/kgsl_pwrscale.h @@ -91,6 +91,11 @@ struct kgsl_pwr_history { * @popp_level - Current level of POPP mitigation * @popp_state - Control state for POPP, on/off, recently pushed, etc * @cooling_dev - Thermal cooling device handle + * @ctxt_aware_enable - Whether or not ctxt aware DCVS feature is enabled + * @ctxt_aware_busy_penalty - The time in microseconds required to trigger + * ctxt aware power level jump + * @ctxt_aware_target_pwrlevel - pwrlevel to jump on in case of ctxt aware + * power level jump */ struct kgsl_pwrscale { struct devfreq *devfreqptr; @@ -113,6 +118,9 @@ struct kgsl_pwrscale { int popp_level; unsigned long popp_state; struct thermal_cooling_device *cooling_dev; + bool ctxt_aware_enable; + unsigned int ctxt_aware_target_pwrlevel; + unsigned int ctxt_aware_busy_penalty; }; int kgsl_pwrscale_init(struct device *dev, const char *governor); @@ -144,7 +152,7 @@ bool kgsl_popp_check(struct kgsl_device *device); #define KGSL_PWRSCALE_INIT(_priv_data) { \ - .enabled = false, \ + .enabled = true, \ .gpu_profile = { \ .private_data = _priv_data, \ .profile = { \ diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index b9c170f76f3aff2d8ea86d2ca34ecb188eb0f309..f2a41eb1c5e96d9c98e1f85a05423b035aae9ab7 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -341,7 +341,7 @@ int kgsl_allocate_user(struct kgsl_device *device, { int ret; - memdesc->flags = flags; + kgsl_memdesc_init(device, memdesc, flags); if (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE) ret = kgsl_sharedmem_alloc_contig(device, memdesc, size); @@ -407,6 +407,39 @@ static void kgsl_page_alloc_unmap_kernel(struct kgsl_memdesc *memdesc) mutex_unlock(&kernel_map_global_lock); } +static int kgsl_lock_sgt(struct sg_table *sgt) +{ + struct scatterlist *sg; + int dest_perms = PERM_READ | PERM_WRITE; + int source_vm = VMID_HLOS; + int dest_vm = VMID_CP_PIXEL; + int ret; + int i; + + ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vm, &dest_perms, 1); + if (!ret) { + /* Set private bit for each sg to indicate that its secured */ + for_each_sg(sgt->sgl, sg, sgt->nents, i) + SetPagePrivate(sg_page(sg)); + } + return ret; +} + +static int kgsl_unlock_sgt(struct sg_table *sgt) +{ + int dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC; + int source_vm = VMID_CP_PIXEL; + int dest_vm = VMID_HLOS; + int ret; + struct sg_page_iter sg_iter; + + ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vm, &dest_perms, 1); + + for_each_sg_page(sgt->sgl, &sg_iter, sgt->nents, 0) + ClearPagePrivate(sg_page_iter_page(&sg_iter)); + return ret; +} + static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc) { kgsl_page_alloc_unmap_kernel(memdesc); @@ -416,12 +449,8 @@ static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc) /* Secure buffers need to be unlocked before being freed */ if (memdesc->priv & KGSL_MEMDESC_TZ_LOCKED) { int ret; - int dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC; - int source_vm = VMID_CP_PIXEL; - int dest_vm = VMID_HLOS; - ret = hyp_assign_table(memdesc->sgt, &source_vm, 1, - &dest_vm, &dest_perms, 1); + ret = kgsl_unlock_sgt(memdesc->sgt); if (ret) { pr_err("Secure buf unlock failed: gpuaddr: %llx size: %llx ret: %d\n", memdesc->gpuaddr, memdesc->size, ret); @@ -432,15 +461,6 @@ static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc) atomic_long_sub(memdesc->size, &kgsl_driver.stats.page_alloc); } - if (memdesc->priv & KGSL_MEMDESC_TZ_LOCKED) { - struct sg_page_iter sg_iter; - - for_each_sg_page(memdesc->sgt->sgl, &sg_iter, - memdesc->sgt->nents, 0) - ClearPagePrivate(sg_page_iter_page(&sg_iter)); - - } - /* Free pages using the pages array for non secure paged memory */ if (memdesc->pages != NULL) kgsl_pool_free_pages(memdesc->pages, memdesc->page_count); @@ -696,6 +716,41 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, } EXPORT_SYMBOL(kgsl_cache_range_op); +void kgsl_memdesc_init(struct kgsl_device *device, + struct kgsl_memdesc *memdesc, uint64_t flags) +{ + struct kgsl_mmu *mmu = &device->mmu; + unsigned int align; + + memset(memdesc, 0, sizeof(*memdesc)); + /* Turn off SVM if the system doesn't support it */ + if (!kgsl_mmu_use_cpu_map(mmu)) + flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP); + + /* Secure memory disables advanced addressing modes */ + if (flags & KGSL_MEMFLAGS_SECURE) + flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP); + + /* Disable IO coherence if it is not supported on the chip */ + if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT)) + flags &= ~((uint64_t) KGSL_MEMFLAGS_IOCOHERENT); + + if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE)) + memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE; + + if (flags & KGSL_MEMFLAGS_SECURE) + memdesc->priv |= KGSL_MEMDESC_SECURE; + + memdesc->flags = flags; + memdesc->pad_to = mmu->va_padding; + memdesc->dev = device->dev->parent; + + align = max_t(unsigned int, + (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT, + ilog2(PAGE_SIZE)); + kgsl_memdesc_set_align(memdesc, align); +} + int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, uint64_t size) @@ -807,12 +862,6 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, /* Call to the hypervisor to lock any secure buffer allocations */ if (memdesc->flags & KGSL_MEMFLAGS_SECURE) { - unsigned int i; - struct scatterlist *sg; - int dest_perms = PERM_READ | PERM_WRITE; - int source_vm = VMID_HLOS; - int dest_vm = VMID_CP_PIXEL; - memdesc->sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL); if (memdesc->sgt == NULL) { ret = -ENOMEM; @@ -826,8 +875,7 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, goto done; } - ret = hyp_assign_table(memdesc->sgt, &source_vm, 1, - &dest_vm, &dest_perms, 1); + ret = kgsl_lock_sgt(memdesc->sgt); if (ret) { sg_free_table(memdesc->sgt); kfree(memdesc->sgt); @@ -835,10 +883,6 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, goto done; } - /* Set private bit for each sg to indicate that its secured */ - for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) - SetPagePrivate(sg_page(sg)); - memdesc->priv |= KGSL_MEMDESC_TZ_LOCKED; /* Record statistics */ @@ -896,11 +940,53 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) if (memdesc->pages) kgsl_free(memdesc->pages); - - memset(memdesc, 0, sizeof(*memdesc)); } EXPORT_SYMBOL(kgsl_sharedmem_free); +void kgsl_free_secure_page(struct page *page) +{ + struct sg_table sgt; + struct scatterlist sgl; + + if (!page) + return; + + sgt.sgl = &sgl; + sgt.nents = 1; + sgt.orig_nents = 1; + sg_init_table(&sgl, 1); + sg_set_page(&sgl, page, PAGE_SIZE, 0); + + kgsl_unlock_sgt(&sgt); + __free_page(page); +} + +struct page *kgsl_alloc_secure_page(void) +{ + struct page *page; + struct sg_table sgt; + struct scatterlist sgl; + int status; + + page = alloc_page(GFP_KERNEL | __GFP_ZERO | + __GFP_NORETRY | __GFP_HIGHMEM); + if (!page) + return NULL; + + sgt.sgl = &sgl; + sgt.nents = 1; + sgt.orig_nents = 1; + sg_init_table(&sgl, 1); + sg_set_page(&sgl, page, PAGE_SIZE, 0); + + status = kgsl_lock_sgt(&sgt); + if (status) { + __free_page(page); + return NULL; + } + return page; +} + int kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc, uint32_t *dst, diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 55bb34f748dbf5b95036e58f4863596f6e63414b..6811363da4db8d62ad7d28085d8bc343e4799c04 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2018, 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 @@ -57,6 +57,9 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size, unsigned int op); +void kgsl_memdesc_init(struct kgsl_device *device, + struct kgsl_memdesc *memdesc, uint64_t flags); + void kgsl_process_init_sysfs(struct kgsl_device *device, struct kgsl_process_private *private); void kgsl_process_uninit_sysfs(struct kgsl_process_private *private); @@ -73,6 +76,10 @@ void kgsl_get_memory_usage(char *str, size_t len, uint64_t memflags); int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, uint64_t size); +void kgsl_free_secure_page(struct page *page); + +struct page *kgsl_alloc_secure_page(void); + #define MEMFLAGS(_flags, _mask, _shift) \ ((unsigned int) (((_flags) & (_mask)) >> (_shift))) @@ -128,6 +135,7 @@ kgsl_memdesc_get_memtype(const struct kgsl_memdesc *memdesc) static inline int kgsl_memdesc_set_align(struct kgsl_memdesc *memdesc, unsigned int align) { + align = max_t(unsigned int, align, ilog2(memdesc->pad_to)); if (align > 32) align = 32; @@ -259,7 +267,8 @@ kgsl_memdesc_use_cpu_map(const struct kgsl_memdesc *memdesc) static inline uint64_t kgsl_memdesc_footprint(const struct kgsl_memdesc *memdesc) { - return memdesc->size + kgsl_memdesc_guard_page_size(memdesc); + return ALIGN(memdesc->size + kgsl_memdesc_guard_page_size(memdesc), + memdesc->pad_to); } /* @@ -282,8 +291,8 @@ static inline int kgsl_allocate_global(struct kgsl_device *device, { int ret; - memdesc->flags = flags; - memdesc->priv = priv; + kgsl_memdesc_init(device, memdesc, flags); + memdesc->priv |= priv; if (((memdesc->priv & KGSL_MEMDESC_CONTIG) != 0) || (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE)) diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h index 340a7db2f67acc236f03d06f475220e03d637656..c1ef28f7eddbe938f69856619bc371e864528672 100644 --- a/drivers/gpu/msm/kgsl_snapshot.h +++ b/drivers/gpu/msm/kgsl_snapshot.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -59,6 +59,7 @@ struct kgsl_snapshot_section_header { #define KGSL_SNAPSHOT_SECTION_MEMLIST_V2 0x0E02 #define KGSL_SNAPSHOT_SECTION_SHADER 0x1201 #define KGSL_SNAPSHOT_SECTION_MVC 0x1501 +#define KGSL_SNAPSHOT_SECTION_GMU 0x1601 #define KGSL_SNAPSHOT_SECTION_END 0xFFFF @@ -183,6 +184,18 @@ struct kgsl_snapshot_ib_v2 { __u64 size; /* Size of the IB */ } __packed; +#define SNAPSHOT_GMU_OTHER 0 +#define SNAPSHOT_GMU_HFIMEM 1 +#define SNAPSHOT_GMU_LOG 2 +#define SNAPSHOT_GMU_BWMEM 3 +#define SNAPSHOT_GMU_DUMPMEM 4 +#define SNAPSHOT_GMU_DCACHE 5 + +/* Indirect buffer sub-section header */ +struct kgsl_snapshot_gmu { + int type; /* Type of data to dump */ + int size; /* Size in bytes to dump */ +} __packed; /* Register sub-section header */ struct kgsl_snapshot_regs { diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ca2fbe56635a3db6655964a23158aa0af6f3eabe..672b0be41d44218040607f0086be2392a708d61b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1390,7 +1390,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) * of implement() working on 8 byte chunks */ - int len = hid_report_len(report) + 7; + u32 len = hid_report_len(report) + 7; return kmalloc(len, flags); } @@ -1455,7 +1455,7 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, { char *buf; int ret; - int len; + u32 len; buf = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) @@ -1481,14 +1481,14 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, } EXPORT_SYMBOL_GPL(__hid_request); -int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, +int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt) { struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; struct hid_driver *hdrv; unsigned int a; - int rsize, csize = size; + u32 rsize, csize = size; u8 *cdata = data; int ret = 0; @@ -1546,7 +1546,7 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); * * This is data entry for lower layers. */ -int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) +int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 199f6a01fc629ead34108da7ddc895269ed0820a..bb984cc9753be8d406d71bc9915d5d1244dfab5b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -387,7 +387,8 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - if (dev->battery_report_type == HID_FEATURE_REPORT) { + if (dev->battery_status != HID_BATTERY_REPORTED && + !dev->battery_avoid_query) { value = hidinput_query_battery_capacity(dev); if (value < 0) return value; @@ -403,17 +404,17 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_STATUS: - if (!dev->battery_reported && - dev->battery_report_type == HID_FEATURE_REPORT) { + if (dev->battery_status != HID_BATTERY_REPORTED && + !dev->battery_avoid_query) { value = hidinput_query_battery_capacity(dev); if (value < 0) return value; dev->battery_capacity = value; - dev->battery_reported = true; + dev->battery_status = HID_BATTERY_QUERIED; } - if (!dev->battery_reported) + if (dev->battery_status == HID_BATTERY_UNKNOWN) val->intval = POWER_SUPPLY_STATUS_UNKNOWN; else if (dev->battery_capacity == 100) val->intval = POWER_SUPPLY_STATUS_FULL; @@ -486,6 +487,14 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->battery_report_type = report_type; dev->battery_report_id = field->report->id; + /* + * Stylus is normally not connected to the device and thus we + * can't query the device and get meaningful battery strength. + * We have to wait for the device to report it on its own. + */ + dev->battery_avoid_query = report_type == HID_INPUT_REPORT && + field->physical == HID_DG_STYLUS; + dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); if (IS_ERR(dev->battery)) { error = PTR_ERR(dev->battery); @@ -530,9 +539,10 @@ static void hidinput_update_battery(struct hid_device *dev, int value) capacity = hidinput_scale_battery_capacity(dev, value); - if (!dev->battery_reported || capacity != dev->battery_capacity) { + if (dev->battery_status != HID_BATTERY_REPORTED || + capacity != dev->battery_capacity) { dev->battery_capacity = capacity; - dev->battery_reported = true; + dev->battery_status = HID_BATTERY_REPORTED; power_supply_changed(dev->battery); } } @@ -1359,7 +1369,8 @@ static void hidinput_led_worker(struct work_struct *work) led_work); struct hid_field *field; struct hid_report *report; - int len, ret; + int ret; + u32 len; __u8 *buf; field = hidinput_get_led_field(hid); diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 20b40ad2632503754685b84cc07d8787a4a44515..42ed887ba0be5d79d67738bcc1520eb8fb8a0190 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -34,7 +34,8 @@ module_param(emulate_scroll_wheel, bool, 0644); MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); static unsigned int scroll_speed = 32; -static int param_set_scroll_speed(const char *val, struct kernel_param *kp) { +static int param_set_scroll_speed(const char *val, + const struct kernel_param *kp) { unsigned long speed; if (!val || kstrtoul(val, 0, &speed) || speed > 63) return -EINVAL; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 6598501c1ad0804378bf8edfe1373dc4c16fef3b..c3b9bd5dba75cf5e607ea6ced9d95b0a7632d6b2 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -354,7 +354,8 @@ static const struct attribute_group mt_attribute_group = { static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) { struct mt_device *td = hid_get_drvdata(hdev); - int ret, size = hid_report_len(report); + int ret; + u32 size = hid_report_len(report); u8 *buf; /* @@ -1049,7 +1050,7 @@ static void mt_set_input_mode(struct hid_device *hdev) struct hid_report_enum *re; struct mt_class *cls = &td->mtclass; char *buf; - int report_len; + u32 report_len; if (td->inputmode < 0) return; diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index ef241d66562e00950e8979ef4fd0633fd4efdca3..cf5812188c3751e12ea66c4f1d3a6f1dea36febc 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -89,8 +89,8 @@ struct rmi_data { u8 *writeReport; u8 *readReport; - int input_report_size; - int output_report_size; + u32 input_report_size; + u32 output_report_size; unsigned long flags; diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 43617fb28b87f68acb331d97f2f83fe599906bbe..317c9c2c0a7ce9d019ece767a140a451ad249379 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -37,6 +37,8 @@ static uint kovaplus_convert_event_cpi(uint value) static void kovaplus_profile_activated(struct kovaplus_device *kovaplus, uint new_profile_index) { + if (new_profile_index >= ARRAY_SIZE(kovaplus->profile_settings)) + return; kovaplus->actual_profile = new_profile_index; kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level; kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 5fbe0f81ab2ebd2c972237fa1be2bc3b544afee8..01b5a9f01814f38786a93bc0e4e994876452d9da 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -192,6 +192,11 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t int ret = 0, len; unsigned char report_number; + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { + ret = -ENODEV; + goto out; + } + dev = hidraw_table[minor]->hid; if (!dev->ll_driver->raw_request) { diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 9145c2129a967464e1e7ed8978dbe53c6db9e86e..3535073a9a7d26c44d3ddc753f2758d88061378b 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -143,10 +143,10 @@ struct i2c_hid { * register of the HID * descriptor. */ unsigned int bufsize; /* i2c buffer size */ - char *inbuf; /* Input buffer */ - char *rawbuf; /* Raw Input buffer */ - char *cmdbuf; /* Command buffer */ - char *argsbuf; /* Command arguments buffer */ + u8 *inbuf; /* Input buffer */ + u8 *rawbuf; /* Raw Input buffer */ + u8 *cmdbuf; /* Command buffer */ + u8 *argsbuf; /* Command arguments buffer */ unsigned long flags; /* device flags */ unsigned long quirks; /* Various quirks */ @@ -450,7 +450,8 @@ static int i2c_hid_hwreset(struct i2c_client *client) static void i2c_hid_get_input(struct i2c_hid *ihid) { - int ret, ret_size; + int ret; + u32 ret_size; int size = le16_to_cpu(ihid->hdesc.wMaxInputLength); if (size > ihid->bufsize) @@ -475,7 +476,7 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) return; } - if (ret_size > size) { + if ((ret_size > size) || (ret_size <= 2)) { dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", __func__, size, ret_size); return; diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index a4e5ea19e99726602fe207accf55cbc71cd04958..d9ecdfefaaa833ee85e3b29e26580759a2d827a4 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -144,13 +144,11 @@ static int uhid_hid_open(struct hid_device *hid) struct uhid_device *uhid = hid->driver_data; int retval = 0; - mutex_lock(&hid->ll_open_lock); if (!hid->ll_open_count++) { retval = uhid_queue_event(uhid, UHID_OPEN); if (retval) hid->ll_open_count--; } - mutex_unlock(&hid->ll_open_lock); return retval; } @@ -158,10 +156,8 @@ static void uhid_hid_close(struct hid_device *hid) { struct uhid_device *uhid = hid->driver_data; - mutex_lock(&hid->ll_open_lock); if (!--hid->ll_open_count) uhid_queue_event(uhid, UHID_CLOSE); - mutex_unlock(&hid->ll_open_lock); } static int uhid_hid_parse(struct hid_device *hid) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 65f1cfbbe7fec594c781d8367580f525463f46f8..4c337585479eb403b94832eb57f1bf769ec0a4c4 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -115,7 +115,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); u8 *data; int ret; - int n; + u32 n; switch (equivalent_usage) { case HID_DG_CONTACTMAX: @@ -408,7 +408,7 @@ static int wacom_set_device_mode(struct hid_device *hdev, u8 *rep_data; struct hid_report *r; struct hid_report_enum *re; - int length; + u32 length; int error = -ENOMEM, limit = 0; if (wacom_wac->mode_report < 0) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 70cbe1e5a3d2064690ba8a87674f6c909508f049..c401b5b63f4c59c696acfcd21c6943b00b75ae54 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -689,6 +689,45 @@ static int wacom_intuos_get_tool_type(int tool_id) return tool_type; } +static void wacom_exit_report(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->pen_input; + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0; + + /* + * Reset all states otherwise we lose the initial states + * when in-prox next time + */ + input_report_abs(input, ABS_X, 0); + input_report_abs(input, ABS_Y, 0); + input_report_abs(input, ABS_DISTANCE, 0); + input_report_abs(input, ABS_TILT_X, 0); + input_report_abs(input, ABS_TILT_Y, 0); + if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { + input_report_key(input, BTN_LEFT, 0); + input_report_key(input, BTN_MIDDLE, 0); + input_report_key(input, BTN_RIGHT, 0); + input_report_key(input, BTN_SIDE, 0); + input_report_key(input, BTN_EXTRA, 0); + input_report_abs(input, ABS_THROTTLE, 0); + input_report_abs(input, ABS_RZ, 0); + } else { + input_report_abs(input, ABS_PRESSURE, 0); + input_report_key(input, BTN_STYLUS, 0); + input_report_key(input, BTN_STYLUS2, 0); + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_WHEEL, 0); + if (features->type >= INTUOS3S) + input_report_abs(input, ABS_Z, 0); + } + input_report_key(input, wacom->tool[idx], 0); + input_report_abs(input, ABS_MISC, 0); /* reset tool id */ + input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + wacom->id[idx] = 0; +} + static int wacom_intuos_inout(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; @@ -741,36 +780,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) if (!wacom->id[idx]) return 1; - /* - * Reset all states otherwise we lose the initial states - * when in-prox next time - */ - input_report_abs(input, ABS_X, 0); - input_report_abs(input, ABS_Y, 0); - input_report_abs(input, ABS_DISTANCE, 0); - input_report_abs(input, ABS_TILT_X, 0); - input_report_abs(input, ABS_TILT_Y, 0); - if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { - input_report_key(input, BTN_LEFT, 0); - input_report_key(input, BTN_MIDDLE, 0); - input_report_key(input, BTN_RIGHT, 0); - input_report_key(input, BTN_SIDE, 0); - input_report_key(input, BTN_EXTRA, 0); - input_report_abs(input, ABS_THROTTLE, 0); - input_report_abs(input, ABS_RZ, 0); - } else { - input_report_abs(input, ABS_PRESSURE, 0); - input_report_key(input, BTN_STYLUS, 0); - input_report_key(input, BTN_STYLUS2, 0); - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_WHEEL, 0); - if (features->type >= INTUOS3S) - input_report_abs(input, ABS_Z, 0); - } - input_report_key(input, wacom->tool[idx], 0); - input_report_abs(input, ABS_MISC, 0); /* reset tool id */ - input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); - wacom->id[idx] = 0; + wacom_exit_report(wacom); return 2; } @@ -1226,6 +1236,12 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) if (!valid) continue; + if (!prox) { + wacom->shared->stylus_in_proximity = false; + wacom_exit_report(wacom); + input_sync(pen_input); + return; + } if (range) { /* Fix rotation alignment: userspace expects zero at left */ int16_t rotation = (int16_t)get_unaligned_le16(&frame[9]); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 65c6d6bdce4cc239dec2d36f60b746eb64c86a9b..1939c0ca374152d50a68dd0c6122464bef9c2691 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -71,7 +71,7 @@ static const struct vmbus_device vmbus_devs[] = { /* PCIE */ { .dev_type = HV_PCIE, HV_PCIE_GUID, - .perf_device = true, + .perf_device = false, }, /* Synthetic Frame Buffer */ diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 62e38fa8cda23c790635664364ba90e6d8292414..e9e6aeabbf842ffd941e0476a185884adac85147 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -95,18 +95,20 @@ enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { u16 config_default; - int calibration_factor; + int calibration_value; int registers; int shunt_div; int bus_voltage_shift; int bus_voltage_lsb; /* uV */ - int power_lsb; /* uW */ + int power_lsb_factor; }; struct ina2xx_data { const struct ina2xx_config *config; long rshunt; + long current_lsb_uA; + long power_lsb_uW; struct mutex config_lock; struct regmap *regmap; @@ -116,21 +118,21 @@ struct ina2xx_data { static const struct ina2xx_config ina2xx_config[] = { [ina219] = { .config_default = INA219_CONFIG_DEFAULT, - .calibration_factor = 40960000, + .calibration_value = 4096, .registers = INA219_REGISTERS, .shunt_div = 100, .bus_voltage_shift = 3, .bus_voltage_lsb = 4000, - .power_lsb = 20000, + .power_lsb_factor = 20, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, - .calibration_factor = 5120000, + .calibration_value = 2048, .registers = INA226_REGISTERS, .shunt_div = 400, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, - .power_lsb = 25000, + .power_lsb_factor = 25, }, }; @@ -169,12 +171,16 @@ static u16 ina226_interval_to_reg(int interval) return INA226_SHIFT_AVG(avg_bits); } +/* + * Calibration register is set to the best value, which eliminates + * truncation errors on calculating current register in hardware. + * According to datasheet (eq. 3) the best values are 2048 for + * ina226 and 4096 for ina219. They are hardcoded as calibration_value. + */ static int ina2xx_calibrate(struct ina2xx_data *data) { - u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor, - data->rshunt); - - return regmap_write(data->regmap, INA2XX_CALIBRATION, val); + return regmap_write(data->regmap, INA2XX_CALIBRATION, + data->config->calibration_value); } /* @@ -187,10 +193,6 @@ static int ina2xx_init(struct ina2xx_data *data) if (ret < 0) return ret; - /* - * Set current LSB to 1mA, shunt is in uOhms - * (equation 13 in datasheet). - */ return ina2xx_calibrate(data); } @@ -268,15 +270,15 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, val = DIV_ROUND_CLOSEST(val, 1000); break; case INA2XX_POWER: - val = regval * data->config->power_lsb; + val = regval * data->power_lsb_uW; break; case INA2XX_CURRENT: - /* signed register, LSB=1mA (selected), in mA */ - val = (s16)regval; + /* signed register, result in mA */ + val = regval * data->current_lsb_uA; + val = DIV_ROUND_CLOSEST(val, 1000); break; case INA2XX_CALIBRATION: - val = DIV_ROUND_CLOSEST(data->config->calibration_factor, - regval); + val = regval; break; default: /* programmer goofed */ @@ -304,9 +306,32 @@ static ssize_t ina2xx_show_value(struct device *dev, ina2xx_get_value(data, attr->index, regval)); } -static ssize_t ina2xx_set_shunt(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) +/* + * In order to keep calibration register value fixed, the product + * of current_lsb and shunt_resistor should also be fixed and equal + * to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order + * to keep the scale. + */ +static int ina2xx_set_shunt(struct ina2xx_data *data, long val) +{ + unsigned int dividend = DIV_ROUND_CLOSEST(1000000000, + data->config->shunt_div); + if (val <= 0 || val > dividend) + return -EINVAL; + + mutex_lock(&data->config_lock); + data->rshunt = val; + data->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val); + data->power_lsb_uW = data->config->power_lsb_factor * + data->current_lsb_uA; + mutex_unlock(&data->config_lock); + + return 0; +} + +static ssize_t ina2xx_store_shunt(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) { unsigned long val; int status; @@ -316,18 +341,9 @@ static ssize_t ina2xx_set_shunt(struct device *dev, if (status < 0) return status; - if (val == 0 || - /* Values greater than the calibration factor make no sense. */ - val > data->config->calibration_factor) - return -EINVAL; - - mutex_lock(&data->config_lock); - data->rshunt = val; - status = ina2xx_calibrate(data); - mutex_unlock(&data->config_lock); + status = ina2xx_set_shunt(data, val); if (status < 0) return status; - return count; } @@ -387,7 +403,7 @@ static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL, /* shunt resistance */ static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR, - ina2xx_show_value, ina2xx_set_shunt, + ina2xx_show_value, ina2xx_store_shunt, INA2XX_CALIBRATION); /* update interval (ina226 only) */ @@ -438,6 +454,7 @@ static int ina2xx_probe(struct i2c_client *client, /* set the device type */ data->config = &ina2xx_config[chip]; + mutex_init(&data->config_lock); if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) { struct ina2xx_platform_data *pdata = dev_get_platdata(dev); @@ -448,10 +465,7 @@ static int ina2xx_probe(struct i2c_client *client, val = INA2XX_RSHUNT_DEFAULT; } - if (val <= 0 || val > data->config->calibration_factor) - return -ENODEV; - - data->rshunt = val; + ina2xx_set_shunt(data, val); ina2xx_regmap_config.max_register = data->config->registers; @@ -467,8 +481,6 @@ static int ina2xx_probe(struct i2c_client *client, return -ENODEV; } - mutex_init(&data->config_lock); - data->groups[group++] = &ina2xx_group; if (id->driver_data == ina226) data->groups[group++] = &ina226_group; diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 1a4b6ec08bcf331fcee11be6c0815b1206ac0f33..7cb22ed8c99ad6476e0e4364bdee46b6f3e46cd3 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -63,7 +63,6 @@ config CORESIGHT_SOURCE_ETM3X config CORESIGHT_SOURCE_ETM4X bool "CoreSight Embedded Trace Macrocell 4.x driver" - depends on ARM64 select CORESIGHT_LINKS_AND_SINKS help This driver provides support for the ETM4.x tracer module, tracing the diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c index d36d874435b983156369ec496c11fa6954237e0e..6fb00de354646de83b58fd82ef809d16e4c0b862 100644 --- a/drivers/hwtracing/coresight/coresight-byte-cntr.c +++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c @@ -24,17 +24,94 @@ static struct tmc_drvdata *tmcdrvdata; static void tmc_etr_read_bytes(struct byte_cntr *byte_cntr_data, loff_t *ppos, - size_t bytes, size_t *len) + size_t bytes, size_t *len, char **bufp) { - if (*len >= bytes) { - atomic_dec(&byte_cntr_data->irq_cnt); + + if (*bufp >= (char *)(tmcdrvdata->vaddr + tmcdrvdata->size)) + *bufp = tmcdrvdata->vaddr; + + if (*len >= bytes) *len = bytes; + else if (((uint32_t)*ppos % bytes) + *len > bytes) + *len = bytes - ((uint32_t)*ppos % bytes); + + if ((*bufp + *len) > (char *)(tmcdrvdata->vaddr + + tmcdrvdata->size)) + *len = (char *)(tmcdrvdata->vaddr + tmcdrvdata->size) - + *bufp; + if (*len == bytes || (*len + (uint32_t)*ppos) % bytes == 0) + atomic_dec(&byte_cntr_data->irq_cnt); +} + +static void tmc_etr_sg_read_pos(loff_t *ppos, + size_t bytes, bool noirq, size_t *len, + char **bufpp) +{ + uint32_t rwp, i = 0; + uint32_t blk_num, sg_tbl_num, blk_num_loc, read_off; + uint32_t *virt_pte, *virt_st_tbl; + void *virt_blk; + phys_addr_t phys_pte; + int total_ents = DIV_ROUND_UP(tmcdrvdata->size, PAGE_SIZE); + int ents_per_pg = PAGE_SIZE/sizeof(uint32_t); + + if (*len == 0) + return; + + blk_num = *ppos / PAGE_SIZE; + read_off = *ppos % PAGE_SIZE; + + virt_st_tbl = (uint32_t *)tmcdrvdata->vaddr; + + /* Compute table index and block entry index within that table */ + if (blk_num && (blk_num == (total_ents - 1)) && + !(blk_num % (ents_per_pg - 1))) { + sg_tbl_num = blk_num / ents_per_pg; + blk_num_loc = ents_per_pg - 1; + } else { + sg_tbl_num = blk_num / (ents_per_pg - 1); + blk_num_loc = blk_num % (ents_per_pg - 1); + } + + for (i = 0; i < sg_tbl_num; i++) { + virt_pte = virt_st_tbl + (ents_per_pg - 1); + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + virt_st_tbl = (uint32_t *)phys_to_virt(phys_pte); + } + + virt_pte = virt_st_tbl + blk_num_loc; + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + virt_blk = phys_to_virt(phys_pte); + + *bufpp = (char *)(virt_blk + read_off); + + if (noirq) { + rwp = readl_relaxed(tmcdrvdata->base + TMC_RWP); + tmc_etr_sg_rwp_pos(tmcdrvdata, rwp); + if (tmcdrvdata->sg_blk_num == blk_num && + rwp >= (phys_pte + read_off)) + *len = rwp - phys_pte - read_off; + else if (tmcdrvdata->sg_blk_num > blk_num) + *len = PAGE_SIZE - read_off; + else + *len = bytes; } else { - if (((uint32_t)*ppos % bytes) + *len > bytes) + + if (*len > (PAGE_SIZE - read_off)) + *len = PAGE_SIZE - read_off; + + if (*len >= (bytes - ((uint32_t)*ppos % bytes))) *len = bytes - ((uint32_t)*ppos % bytes); - if ((*len + (uint32_t)*ppos) % bytes == 0) - atomic_dec(&byte_cntr_data->irq_cnt); + + if (*len == bytes || (*len + (uint32_t)*ppos) % bytes == 0) + atomic_dec(&tmcdrvdata->byte_cntr->irq_cnt); } + + /* + * Invalidate cache range before reading. This will make sure that CPU + * reads latest contents from DDR + */ + dmac_inv_range((void *)(*bufpp), (void *)(*bufpp) + *len); } static irqreturn_t etr_handler(int irq, void *data) @@ -73,6 +150,8 @@ static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data, if (!byte_cntr_data->read_active) goto err0; + bufp = (char *)(tmcdrvdata->buf + *ppos); + if (byte_cntr_data->enable) { if (!atomic_read(&byte_cntr_data->irq_cnt)) { mutex_unlock(&byte_cntr_data->byte_cntr_lock); @@ -83,19 +162,38 @@ static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data, if (!byte_cntr_data->read_active) goto err0; } - bufp = (char *)(tmcdrvdata->vaddr + *ppos); - tmc_etr_read_bytes(byte_cntr_data, ppos, - byte_cntr_data->block_size, &len); + if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG) + tmc_etr_read_bytes(byte_cntr_data, ppos, + byte_cntr_data->block_size, &len, + &bufp); + else + tmc_etr_sg_read_pos(ppos, byte_cntr_data->block_size, 0, + &len, &bufp); + } else { if (!atomic_read(&byte_cntr_data->irq_cnt)) { - tmc_etr_flush_bytes(ppos, byte_cntr_data->block_size, - &len); + if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG) + tmc_etr_flush_bytes(ppos, + byte_cntr_data->block_size, + &len); + else + tmc_etr_sg_read_pos(ppos, + byte_cntr_data->block_size, + 1, + &len, &bufp); if (!len) goto err0; } else { - tmc_etr_read_bytes(byte_cntr_data, ppos, - byte_cntr_data->block_size, &len); + if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG) + tmc_etr_read_bytes(byte_cntr_data, ppos, + byte_cntr_data->block_size, + &len, &bufp); + else + tmc_etr_sg_read_pos(ppos, + byte_cntr_data->block_size, + 1, + &len, &bufp); } } diff --git a/drivers/hwtracing/coresight/coresight-event.c b/drivers/hwtracing/coresight/coresight-event.c index d5f304f670601da5854e36942e354e47bc756ac2..86e2b29fdd06ac7e563e82a9c2471eb77f65b183 100644 --- a/drivers/hwtracing/coresight/coresight-event.c +++ b/drivers/hwtracing/coresight/coresight-event.c @@ -20,12 +20,13 @@ #include static int event_abort_enable; -static int event_abort_set(const char *val, struct kernel_param *kp); +static int event_abort_set(const char *val, const struct kernel_param *kp); module_param_call(event_abort_enable, event_abort_set, param_get_int, &event_abort_enable, 0644); static int event_abort_early_panic = 1; -static int event_abort_on_panic_set(const char *val, struct kernel_param *kp); +static int event_abort_on_panic_set(const char *val, + const struct kernel_param *kp); module_param_call(event_abort_early_panic, event_abort_on_panic_set, param_get_int, &event_abort_early_panic, 0644); @@ -96,7 +97,7 @@ static void event_abort_unregister(void) unregister_trace_unhandled_abort(event_abort_unhandled_abort, NULL); } -static int event_abort_set(const char *val, struct kernel_param *kp) +static int event_abort_set(const char *val, const struct kernel_param *kp) { int ret; @@ -114,7 +115,8 @@ static int event_abort_set(const char *val, struct kernel_param *kp) return ret; } -static int event_abort_on_panic_set(const char *val, struct kernel_param *kp) +static int event_abort_on_panic_set(const char *val, + const struct kernel_param *kp) { int ret; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 7e6cd8954b8cc53fb6c8182cd2e779ca1fa0febb..8e925919e3009e744cd1d3695ded8d7fdd2ff06f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -21,12 +21,408 @@ #include "coresight-priv.h" #include "coresight-tmc.h" +static void tmc_etr_sg_tbl_free(uint32_t *vaddr, uint32_t size, uint32_t ents) +{ + uint32_t i = 0, pte_n = 0, last_pte; + uint32_t *virt_st_tbl, *virt_pte; + void *virt_blk; + phys_addr_t phys_pte; + int total_ents = DIV_ROUND_UP(size, PAGE_SIZE); + int ents_per_blk = PAGE_SIZE/sizeof(uint32_t); + + virt_st_tbl = vaddr; + + while (i < total_ents) { + last_pte = ((i + ents_per_blk) > total_ents) ? + total_ents : (i + ents_per_blk); + while (i < last_pte) { + virt_pte = virt_st_tbl + pte_n; + + /* Do not go beyond number of entries allocated */ + if (i == ents) { + free_page((unsigned long)virt_st_tbl); + return; + } + + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + virt_blk = phys_to_virt(phys_pte); + + if ((last_pte - i) > 1) { + free_page((unsigned long)virt_blk); + pte_n++; + } else if (last_pte == total_ents) { + free_page((unsigned long)virt_blk); + free_page((unsigned long)virt_st_tbl); + } else { + free_page((unsigned long)virt_st_tbl); + virt_st_tbl = (uint32_t *)virt_blk; + pte_n = 0; + break; + } + i++; + } + } +} + +static void tmc_etr_sg_tbl_flush(uint32_t *vaddr, uint32_t size) +{ + uint32_t i = 0, pte_n = 0, last_pte; + uint32_t *virt_st_tbl, *virt_pte; + void *virt_blk; + phys_addr_t phys_pte; + int total_ents = DIV_ROUND_UP(size, PAGE_SIZE); + int ents_per_blk = PAGE_SIZE/sizeof(uint32_t); + + virt_st_tbl = vaddr; + dmac_flush_range((void *)virt_st_tbl, (void *)virt_st_tbl + PAGE_SIZE); + + while (i < total_ents) { + last_pte = ((i + ents_per_blk) > total_ents) ? + total_ents : (i + ents_per_blk); + while (i < last_pte) { + virt_pte = virt_st_tbl + pte_n; + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + virt_blk = phys_to_virt(phys_pte); + + dmac_flush_range(virt_blk, virt_blk + PAGE_SIZE); + + if ((last_pte - i) > 1) { + pte_n++; + } else if (last_pte != total_ents) { + virt_st_tbl = (uint32_t *)virt_blk; + pte_n = 0; + break; + } + i++; + } + } +} + +/* + * Scatter gather table layout in memory: + * 1. Table contains 32-bit entries + * 2. Each entry in the table points to 4K block of memory + * 3. Last entry in the table points to next table + * 4. (*) Based on mem_size requested, if there is no need for next level of + * table, last entry in the table points directly to 4K block of memory. + * + * sg_tbl_num=0 + * |---------------|<-- drvdata->vaddr + * | blk_num=0 | + * |---------------| + * | blk_num=1 | + * |---------------| + * | blk_num=2 | + * |---------------| sg_tbl_num=1 + * |(*)Nxt Tbl Addr|------>|---------------| + * |---------------| | blk_num=3 | + * |---------------| + * | blk_num=4 | + * |---------------| + * | blk_num=5 | + * |---------------| sg_tbl_num=2 + * |(*)Nxt Tbl Addr|------>|---------------| + * |---------------| | blk_num=6 | + * |---------------| + * | blk_num=7 | + * |---------------| + * | blk_num=8 | + * |---------------| + * | |End of + * |---------------|----- + * Table + * For simplicity above diagram assumes following: + * a. mem_size = 36KB --> total_ents = 9 + * b. ents_per_blk = 4 + */ + +static int tmc_etr_sg_tbl_alloc(struct tmc_drvdata *drvdata) +{ + int ret; + uint32_t i = 0, last_pte; + uint32_t *virt_pgdir, *virt_st_tbl; + void *virt_pte; + int total_ents = DIV_ROUND_UP(drvdata->size, PAGE_SIZE); + int ents_per_blk = PAGE_SIZE/sizeof(uint32_t); + + virt_pgdir = (uint32_t *)get_zeroed_page(GFP_KERNEL); + if (!virt_pgdir) + return -ENOMEM; + + virt_st_tbl = virt_pgdir; + + while (i < total_ents) { + last_pte = ((i + ents_per_blk) > total_ents) ? + total_ents : (i + ents_per_blk); + while (i < last_pte) { + virt_pte = (void *)get_zeroed_page(GFP_KERNEL); + if (!virt_pte) { + ret = -ENOMEM; + goto err; + } + + if ((last_pte - i) > 1) { + *virt_st_tbl = + TMC_ETR_SG_ENT(virt_to_phys(virt_pte)); + virt_st_tbl++; + } else if (last_pte == total_ents) { + *virt_st_tbl = + TMC_ETR_SG_LST_ENT(virt_to_phys(virt_pte)); + } else { + *virt_st_tbl = + TMC_ETR_SG_NXT_TBL(virt_to_phys(virt_pte)); + virt_st_tbl = (uint32_t *)virt_pte; + break; + } + i++; + } + } + + drvdata->vaddr = virt_pgdir; + drvdata->paddr = virt_to_phys(virt_pgdir); + + /* Flush the dcache before proceeding */ + tmc_etr_sg_tbl_flush((uint32_t *)drvdata->vaddr, drvdata->size); + + dev_dbg(drvdata->dev, "%s: table starts at %#lx, total entries %d\n", + __func__, (unsigned long)drvdata->paddr, total_ents); + + return 0; +err: + tmc_etr_sg_tbl_free(virt_pgdir, drvdata->size, i); + return ret; +} + +/* + * TMC read logic when scatter gather feature is enabled: + * + * sg_tbl_num=0 + * |---------------|<-- drvdata->vaddr + * | blk_num=0 | + * | blk_num_rel=5 | + * |---------------| + * | blk_num=1 | + * | blk_num_rel=6 | + * |---------------| + * | blk_num=2 | + * | blk_num_rel=7 | + * |---------------| sg_tbl_num=1 + * | Next Table |------>|---------------| + * | Addr | | blk_num=3 | + * |---------------| | blk_num_rel=8 | + * |---------------| + * 4k Block Addr | blk_num=4 | + * |--------------| blk_num_rel=0 | + * | |---------------| + * | | blk_num=5 | + * | | blk_num_rel=1 | + * | |---------------| sg_tbl_num=2 + * |---------------| | Next Table |------>|---------------| + * | | | Addr | | blk_num=6 | + * | | |---------------| | blk_num_rel=2 | + * | read_off | |---------------| + * | | | blk_num=7 | + * | | ppos | blk_num_rel=3 | + * |---------------|----- |---------------| + * | | | blk_num=8 | + * | delta_up | | blk_num_rel=4 | + * | | RWP/drvdata->buf |---------------| + * |---------------|----------------- | | + * | | | | |End of + * | | | |---------------|----- + * | | drvdata->delta_bottom Table + * | | | + * |_______________| _|_ + * 4K Block + * + * For simplicity above diagram assumes following: + * a. mem_size = 36KB --> total_ents = 9 + * b. ents_per_blk = 4 + * c. RWP is on 5th block (blk_num = 5); so we have to start reading from RWP + * position + */ + +void tmc_etr_sg_compute_read(struct tmc_drvdata *drvdata, loff_t *ppos, + char **bufpp, size_t *len) +{ + uint32_t i = 0, blk_num_rel = 0, read_len = 0; + uint32_t blk_num, sg_tbl_num, blk_num_loc, read_off; + uint32_t *virt_pte, *virt_st_tbl; + void *virt_blk; + phys_addr_t phys_pte = 0; + int total_ents = DIV_ROUND_UP(drvdata->size, PAGE_SIZE); + int ents_per_blk = PAGE_SIZE/sizeof(uint32_t); + + /* + * Find relative block number from ppos and reading offset + * within block and find actual block number based on relative + * block number + */ + if (drvdata->buf == drvdata->vaddr) { + blk_num = *ppos / PAGE_SIZE; + read_off = *ppos % PAGE_SIZE; + } else { + if (*ppos < drvdata->delta_bottom) { + read_off = PAGE_SIZE - drvdata->delta_bottom; + } else { + blk_num_rel = (*ppos / PAGE_SIZE) + 1; + read_off = (*ppos - drvdata->delta_bottom) % PAGE_SIZE; + } + + blk_num = (drvdata->sg_blk_num + blk_num_rel) % total_ents; + } + + virt_st_tbl = (uint32_t *)drvdata->vaddr; + + /* Compute table index and block entry index within that table */ + if (blk_num && (blk_num == (total_ents - 1)) && + !(blk_num % (ents_per_blk - 1))) { + sg_tbl_num = blk_num / ents_per_blk; + blk_num_loc = ents_per_blk - 1; + } else { + sg_tbl_num = blk_num / (ents_per_blk - 1); + blk_num_loc = blk_num % (ents_per_blk - 1); + } + + for (i = 0; i < sg_tbl_num; i++) { + virt_pte = virt_st_tbl + (ents_per_blk - 1); + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + virt_st_tbl = (uint32_t *)phys_to_virt(phys_pte); + } + + virt_pte = virt_st_tbl + blk_num_loc; + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + virt_blk = phys_to_virt(phys_pte); + + *bufpp = virt_blk + read_off; + + if (*len > (PAGE_SIZE - read_off)) + *len = PAGE_SIZE - read_off; + + /* + * When buffer is wrapped around and trying to read last relative + * block (i.e. delta_up), compute len differently + */ + if (blk_num_rel && (blk_num == drvdata->sg_blk_num)) { + read_len = PAGE_SIZE - drvdata->delta_bottom - read_off; + if (*len > read_len) + *len = read_len; + } + + dev_dbg_ratelimited(drvdata->dev, + "%s: read at %p, phys %pa len %zu blk %d, rel blk %d RWP blk %d\n", + __func__, *bufpp, &phys_pte, *len, blk_num, blk_num_rel, + drvdata->sg_blk_num); +} + +static void tmc_etr_sg_mem_reset(uint32_t *vaddr, uint32_t size) +{ + uint32_t i = 0, pte_n = 0, last_pte; + uint32_t *virt_st_tbl, *virt_pte; + void *virt_blk; + phys_addr_t phys_pte; + int total_ents = DIV_ROUND_UP(size, PAGE_SIZE); + int ents_per_blk = PAGE_SIZE/sizeof(uint32_t); + + virt_st_tbl = vaddr; + + while (i < total_ents) { + last_pte = ((i + ents_per_blk) > total_ents) ? + total_ents : (i + ents_per_blk); + while (i < last_pte) { + virt_pte = virt_st_tbl + pte_n; + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + virt_blk = phys_to_virt(phys_pte); + + if ((last_pte - i) > 1) { + memset(virt_blk, 0, PAGE_SIZE); + pte_n++; + } else if (last_pte == total_ents) { + memset(virt_blk, 0, PAGE_SIZE); + } else { + virt_st_tbl = (uint32_t *)virt_blk; + pte_n = 0; + break; + } + i++; + } + } + + /* Flush the dcache before proceeding */ + tmc_etr_sg_tbl_flush(vaddr, size); +} + +void tmc_etr_sg_rwp_pos(struct tmc_drvdata *drvdata, uint32_t rwp) +{ + uint32_t i = 0, pte_n = 0, last_pte; + uint32_t *virt_st_tbl, *virt_pte; + void *virt_blk; + bool found = false; + phys_addr_t phys_pte; + int total_ents = DIV_ROUND_UP(drvdata->size, PAGE_SIZE); + int ents_per_blk = PAGE_SIZE/sizeof(uint32_t); + + virt_st_tbl = drvdata->vaddr; + + while (i < total_ents) { + last_pte = ((i + ents_per_blk) > total_ents) ? + total_ents : (i + ents_per_blk); + while (i < last_pte) { + virt_pte = virt_st_tbl + pte_n; + phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte); + + /* + * When the trace buffer is full; RWP could be on any + * 4K block from scatter gather table. Compute below - + * 1. Block number where RWP is currently residing + * 2. RWP position in that 4K block + * 3. Delta offset from current RWP position to end of + * block. + */ + if (phys_pte <= rwp && rwp < (phys_pte + PAGE_SIZE)) { + virt_blk = phys_to_virt(phys_pte); + drvdata->sg_blk_num = i; + drvdata->buf = virt_blk + rwp - phys_pte; + drvdata->delta_bottom = + phys_pte + PAGE_SIZE - rwp; + found = true; + break; + } + + if ((last_pte - i) > 1) { + pte_n++; + } else if (i < (total_ents - 1)) { + virt_blk = phys_to_virt(phys_pte); + virt_st_tbl = (uint32_t *)virt_blk; + pte_n = 0; + break; + } + + i++; + } + if (found) + break; + } +} +EXPORT_SYMBOL(tmc_etr_sg_rwp_pos); + +static void tmc_etr_mem_reset(struct tmc_drvdata *drvdata) +{ + if (drvdata->vaddr) { + if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) + memset(drvdata->vaddr, 0, drvdata->size); + else + tmc_etr_sg_mem_reset((uint32_t *)drvdata->vaddr, + drvdata->size); + } +} + void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) { u32 axictl, sts; /* Zero out the memory to help with debug */ - memset(drvdata->vaddr, 0, drvdata->size); + tmc_etr_mem_reset(drvdata); CS_UNLOCK(drvdata->base); @@ -38,6 +434,10 @@ void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) axictl = readl_relaxed(drvdata->base + TMC_AXICTL); axictl &= ~TMC_AXICTL_CLEAR_MASK; + if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) + axictl &= ~TMC_AXICTL_SCT_GAT_MODE; + else + axictl |= TMC_AXICTL_SCT_GAT_MODE; axictl |= (TMC_AXICTL_PROT_CTL_B1 | TMC_AXICTL_WR_BURST_16); axictl |= TMC_AXICTL_AXCACHE_OS; @@ -87,26 +487,41 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) rwp = tmc_read_rwp(drvdata); val = readl_relaxed(drvdata->base + TMC_STS); - /* - * Adjust the buffer to point to the beginning of the trace data - * and update the available trace data. - */ - if (val & TMC_STS_FULL) { - drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; - drvdata->len = drvdata->size; + if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) { + /* + * Adjust the buffer to point to the beginning of the trace data + * and update the available trace data. + */ + if (val & TMC_STS_FULL) { + drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; + drvdata->len = drvdata->size; - barrier = barrier_pkt; - temp = (u32 *)drvdata->buf; + barrier = barrier_pkt; + temp = (u32 *)drvdata->buf; - while (*barrier) { - *temp = *barrier; - temp++; - barrier++; - } + while (*barrier) { + *temp = *barrier; + temp++; + barrier++; + } + } else { + drvdata->buf = drvdata->vaddr; + drvdata->len = rwp - drvdata->paddr; + } } else { - drvdata->buf = drvdata->vaddr; - drvdata->len = rwp - drvdata->paddr; + /* + * Reset these variables before computing since we + * rely on their values during tmc read + */ + drvdata->sg_blk_num = 0; + drvdata->delta_bottom = 0; + drvdata->len = drvdata->size; + + if (val & TMC_STS_FULL) + tmc_etr_sg_rwp_pos(drvdata, rwp); + else + drvdata->buf = drvdata->vaddr; } } @@ -131,13 +546,19 @@ static int tmc_etr_alloc_mem(struct tmc_drvdata *drvdata) int ret; if (!drvdata->vaddr) { - drvdata->vaddr = dma_zalloc_coherent(drvdata->dev, - drvdata->size, - &drvdata->paddr, - GFP_KERNEL); - if (!drvdata->vaddr) { - ret = -ENOMEM; - goto err; + if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) { + drvdata->vaddr = dma_zalloc_coherent(drvdata->dev, + drvdata->size, + &drvdata->paddr, + GFP_KERNEL); + if (!drvdata->vaddr) { + ret = -ENOMEM; + goto err; + } + } else { + ret = tmc_etr_sg_tbl_alloc(drvdata); + if (ret) + goto err; } } /* @@ -154,8 +575,13 @@ static int tmc_etr_alloc_mem(struct tmc_drvdata *drvdata) static void tmc_etr_free_mem(struct tmc_drvdata *drvdata) { if (drvdata->vaddr) { - dma_free_coherent(drvdata->dev, drvdata->size, - drvdata->vaddr, drvdata->paddr); + if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) + dma_free_coherent(drvdata->dev, drvdata->size, + drvdata->vaddr, drvdata->paddr); + else + tmc_etr_sg_tbl_free((uint32_t *)drvdata->vaddr, + drvdata->size, + DIV_ROUND_UP(drvdata->size, PAGE_SIZE)); drvdata->vaddr = 0; drvdata->paddr = 0; } @@ -393,6 +819,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) if (drvdata->size != drvdata->mem_size) { tmc_etr_free_mem(drvdata); drvdata->size = drvdata->mem_size; + drvdata->memtype = drvdata->mem_type; } ret = tmc_etr_alloc_mem(drvdata); @@ -451,7 +878,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) /* Free memory outside the spinlock if need be */ if (!used && vaddr) - dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); + tmc_etr_free_mem(drvdata); if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) tmc_etr_byte_cntr_start(drvdata->byte_cntr); @@ -660,7 +1087,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) /* Free allocated memory out side of the spinlock */ if (vaddr) - dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); + tmc_etr_free_mem(drvdata); mutex_unlock(&drvdata->mem_lock); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 4027473dc9f590c00b9acd90db42e02ebd34bddd..fd7a7e1f11fa3b1b5ce3622b0ba53d38085fce6e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "coresight-priv.h" @@ -153,12 +154,19 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, len = drvdata->len - *ppos; if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - if (bufp == (char *)(drvdata->vaddr + drvdata->size)) - bufp = drvdata->vaddr; - else if (bufp > (char *)(drvdata->vaddr + drvdata->size)) - bufp -= drvdata->size; - if ((bufp + len) > (char *)(drvdata->vaddr + drvdata->size)) - len = (char *)(drvdata->vaddr + drvdata->size) - bufp; + if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) { + if (bufp == (char *)(drvdata->vaddr + drvdata->size)) + bufp = drvdata->vaddr; + else if (bufp > (char *)(drvdata->vaddr + + drvdata->size)) + bufp -= drvdata->size; + if ((bufp + len) > (char *)(drvdata->vaddr + + drvdata->size)) + len = (char *)(drvdata->vaddr + drvdata->size) - + bufp; + } else { + tmc_etr_sg_compute_read(drvdata, ppos, &bufp, &len); + } } if (copy_to_user(data, bufp, len)) { @@ -422,6 +430,43 @@ static ssize_t available_out_modes_show(struct device *dev, } static DEVICE_ATTR_RO(available_out_modes); +static ssize_t mem_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return scnprintf(buf, PAGE_SIZE, "%s\n", + str_tmc_etr_mem_type[drvdata->mem_type]); +} + +static ssize_t mem_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + char str[10] = ""; + + if (strlen(buf) >= 10) + return -EINVAL; + if (sscanf(buf, "%10s", str) != 1) + return -EINVAL; + + mutex_lock(&drvdata->mem_lock); + if (!strcmp(str, str_tmc_etr_mem_type[TMC_ETR_MEM_TYPE_CONTIG])) + drvdata->mem_type = TMC_ETR_MEM_TYPE_CONTIG; + else if (!strcmp(str, str_tmc_etr_mem_type[TMC_ETR_MEM_TYPE_SG])) + drvdata->mem_type = TMC_ETR_MEM_TYPE_SG; + else + size = -EINVAL; + + mutex_unlock(&drvdata->mem_lock); + + return size; +} +static DEVICE_ATTR_RW(mem_type); + static ssize_t block_size_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -517,6 +562,7 @@ static struct attribute *coresight_tmc_etf_attrs[] = { static struct attribute *coresight_tmc_etr_attrs[] = { &dev_attr_mem_size.attr, + &dev_attr_mem_type.attr, &dev_attr_trigger_cntr.attr, &dev_attr_out_mode.attr, &dev_attr_available_out_modes.attr, @@ -635,7 +681,12 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) if (ret) drvdata->size = SZ_1M; + if (of_property_read_bool(np, "arm,sg-enable")) + drvdata->memtype = TMC_ETR_MEM_TYPE_SG; + else + drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG; drvdata->mem_size = drvdata->size; + drvdata->mem_type = drvdata->memtype; drvdata->out_mode = TMC_ETR_OUT_MODE_MEM; } else { drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 3c4b3b78cb55b38125d2fae7837f3092fb284c3f..df02b7b46b68db453ee327817e31051ca3303d76 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -101,6 +101,11 @@ #define TMC_FFCR_TRIGON_TRIGIN BIT(8) #define TMC_FFCR_STOP_ON_FLUSH BIT(12) +#define TMC_ETR_SG_ENT_TO_BLK(phys_pte) (((phys_addr_t)phys_pte >> 4) \ + << PAGE_SHIFT) +#define TMC_ETR_SG_ENT(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x2) +#define TMC_ETR_SG_NXT_TBL(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x3) +#define TMC_ETR_SG_LST_ENT(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x1) #define TMC_DEVID_NOSCAT BIT(24) @@ -147,6 +152,15 @@ enum tmc_mem_intf_width { #define CORESIGHT_SOC_600_ETR_CAPS \ (TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE) +enum tmc_etr_mem_type { + TMC_ETR_MEM_TYPE_CONTIG, + TMC_ETR_MEM_TYPE_SG, +}; + +static const char * const str_tmc_etr_mem_type[] = { + [TMC_ETR_MEM_TYPE_CONTIG] = "contig", + [TMC_ETR_MEM_TYPE_SG] = "sg", +}; enum tmc_etr_out_mode { TMC_ETR_OUT_MODE_NONE, TMC_ETR_OUT_MODE_MEM, @@ -210,8 +224,11 @@ struct tmc_drvdata { struct mutex mem_lock; u32 mem_size; u32 trigger_cntr; + enum tmc_etr_mem_type mem_type; + enum tmc_etr_mem_type memtype; u32 etr_caps; u32 delta_bottom; + int sg_blk_num; enum tmc_etr_out_mode out_mode; struct usb_qdss_ch *usbch; struct tmc_etr_bam_data *bamdata; @@ -238,6 +255,8 @@ extern const struct coresight_ops tmc_etb_cs_ops; extern const struct coresight_ops tmc_etf_cs_ops; /* ETR functions */ +void tmc_etr_sg_compute_read(struct tmc_drvdata *drvdata, loff_t *ppos, + char **bufpp, size_t *len); int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); void __tmc_etr_disable_to_bam(struct tmc_drvdata *drvdata); @@ -251,7 +270,9 @@ int tmc_etr_bam_init(struct amba_device *adev, extern struct byte_cntr *byte_cntr_init(struct amba_device *adev, struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etr_cs_ops; +extern void tmc_etr_sg_rwp_pos(struct tmc_drvdata *drvdata, uint32_t rwp); +extern const struct coresight_ops tmc_etr_cs_ops; #define TMC_REG_PAIR(name, lo_off, hi_off) \ static inline u64 \ diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 8eac00efadc1ad8f8e477094e26790a1ec317117..ba8df2fde1b2769fe65bdf49f338625577b6ced9 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -965,8 +965,6 @@ static void i801_enable_host_notify(struct i2c_adapter *adapter) if (!(priv->features & FEATURE_HOST_NOTIFY)) return; - priv->original_slvcmd = inb_p(SMBSLVCMD(priv)); - if (!(SMBSLVCMD_HST_NTFY_INTREN & priv->original_slvcmd)) outb_p(SMBSLVCMD_HST_NTFY_INTREN | priv->original_slvcmd, SMBSLVCMD(priv)); @@ -1614,6 +1612,10 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) outb_p(inb_p(SMBAUXCTL(priv)) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + /* Remember original Host Notify setting */ + if (priv->features & FEATURE_HOST_NOTIFY) + priv->original_slvcmd = inb_p(SMBSLVCMD(priv)); + /* Default timeout in interrupt mode: 200 ms */ priv->adapter.timeout = HZ / 5; @@ -1698,6 +1700,15 @@ static void i801_remove(struct pci_dev *dev) */ } +static void i801_shutdown(struct pci_dev *dev) +{ + struct i801_priv *priv = pci_get_drvdata(dev); + + /* Restore config registers to avoid hard hang on some systems */ + i801_disable_host_notify(priv); + pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); +} + #ifdef CONFIG_PM static int i801_suspend(struct device *dev) { @@ -1727,6 +1738,7 @@ static struct pci_driver i801_driver = { .id_table = i801_ids, .probe = i801_probe, .remove = i801_remove, + .shutdown = i801_shutdown, .driver = { .pm = &i801_pm_ops, }, diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index d4a6e9c2e9aaeaa679bb159ade420bcdb37e6988..124f9b1cf1b034499c5108f1969cd5a31755f9bb 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -887,6 +887,11 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) } setup = of_device_get_match_data(&pdev->dev); + if (!setup) { + dev_err(&pdev->dev, "Can't get device data\n"); + ret = -ENODEV; + goto clk_free; + } i2c_dev->setup = *setup; ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-rising-time-ns", diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index d127ace6aa5757a2e91a67d74b7c18353e1dd689..6ee866fcc5dd035655cc8649101e8b3df1e578ea 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -244,7 +244,7 @@ struct chs_geom { static unsigned int ide_disks; static struct chs_geom ide_disks_chs[MAX_HWIFS * MAX_DRIVES]; -static int ide_set_disk_chs(const char *str, struct kernel_param *kp) +static int ide_set_disk_chs(const char *str, const struct kernel_param *kp) { unsigned int a, b, c = 0, h = 0, s = 0, i, j = 1; @@ -328,7 +328,7 @@ static void ide_dev_apply_params(ide_drive_t *drive, u8 unit) static unsigned int ide_ignore_cable; -static int ide_set_ignore_cable(const char *s, struct kernel_param *kp) +static int ide_set_ignore_cable(const char *s, const struct kernel_param *kp) { int i, j = 1; diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 309b4c8f5c035f0f149e43c4ec2421b526643d9e..fbf948c245f906c0989a8aabfdd11e23e0b3730e 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -529,11 +529,11 @@ static const struct adc_channels adc_chans_pmic5[ADC_MAX_CHANNEL] = { SCALE_HW_CALIB_DEFAULT) [ADC_XO_THERM_PU2] = ADC_CHAN_TEMP("xo_therm", 1, SCALE_HW_CALIB_XOTHERM) - [ADC_AMUX_THM1_PU2] = ADC_CHAN_TEMP("amux_thm1", 1, + [ADC_AMUX_THM1_PU2] = ADC_CHAN_TEMP("amux_thm1_pu2", 1, SCALE_HW_CALIB_THERM_100K_PULLUP) - [ADC_AMUX_THM2_PU2] = ADC_CHAN_TEMP("amux_thm2", 1, + [ADC_AMUX_THM2_PU2] = ADC_CHAN_TEMP("amux_thm2_pu2", 1, SCALE_HW_CALIB_THERM_100K_PULLUP) - [ADC_AMUX_THM3_PU2] = ADC_CHAN_TEMP("amux_thm3", 1, + [ADC_AMUX_THM3_PU2] = ADC_CHAN_TEMP("amux_thm3_pu2", 1, SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC_INT_EXT_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("int_ext_isense", 1, SCALE_HW_CALIB_CUR) @@ -541,6 +541,8 @@ static const struct adc_channels adc_chans_pmic5[ADC_MAX_CHANNEL] = { SCALE_HW_CALIB_CUR) [ADC_PARALLEL_ISENSE_VBAT_VDATA] = ADC_CHAN_POWER("parallel_isense", 1, SCALE_HW_CALIB_CUR) + [ADC_AMUX_THM2] = ADC_CHAN_TEMP("amux_thm2", 1, + SCALE_HW_CALIB_PM5_SMB_TEMP) }; static const struct adc_channels adc_chans_rev2[ADC_MAX_CHANNEL] = { diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index 6481a7a5a551b81171c8118826fd4a69937dac9f..7a370350cf0cbd35e6c0fffea889136c9c585d10 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -306,6 +306,35 @@ static int qcom_vadc_scale_hw_calib_die_temp( return 0; } +static int qcom_vadc_scale_hw_smb_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc_data *data, + u16 adc_code, int *result_mdec) +{ + s64 voltage = 0, adc_vdd_ref_mv = 1875; + u64 temp; + + if (adc_code > VADC5_MAX_CODE) + adc_code = 0; + + /* (ADC code * vref_vadc (1.875V)) / full_scale_code */ + voltage = (s64) adc_code * adc_vdd_ref_mv * 1000; + voltage = div64_s64(voltage, data->full_scale_code_volt); + if (voltage > 0) { + temp = voltage * prescale->den; + temp *= 100; + do_div(temp, prescale->num * PMIC5_SMB_TEMP_SCALE_FACTOR); + voltage = temp; + } else { + voltage = 0; + } + + voltage = PMIC5_SMB_TEMP_CONSTANT - voltage; + *result_mdec = voltage; + + return 0; +} + static int qcom_vadc_scale_hw_chg5_temp( const struct vadc_prescale_ratio *prescale, const struct adc_data *data, @@ -412,6 +441,9 @@ int qcom_vadc_hw_scale(enum vadc_scale_fn_type scaletype, case SCALE_HW_CALIB_PM5_CHG_TEMP: return qcom_vadc_scale_hw_chg5_temp(prescale, data, adc_code, result); + case SCALE_HW_CALIB_PM5_SMB_TEMP: + return qcom_vadc_scale_hw_smb_temp(prescale, data, + adc_code, result); default: return -EINVAL; } diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h index 1a636ab5b964cb08d1309a0a3176d95bfab24b85..7391bcddfaf5dc5ceafa842b4ab00c8619b5a1fb 100644 --- a/drivers/iio/adc/qcom-vadc-common.h +++ b/drivers/iio/adc/qcom-vadc-common.h @@ -40,6 +40,8 @@ #define KELVINMIL_CELSIUSMIL 273150 #define PMIC5_CHG_TEMP_SCALE_FACTOR 377500 +#define PMIC5_SMB_TEMP_CONSTANT 419400 +#define PMIC5_SMB_TEMP_SCALE_FACTOR 356 #define PMI_CHG_SCALE_1 -138890 #define PMI_CHG_SCALE_2 391750000000LL @@ -114,6 +116,8 @@ struct vadc_prescale_ratio { * SCALE_HW_CALIB_CUR: Returns result in uA for PMIC5. * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5 * charger temperature. + * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5 + * SMB1390 temperature. */ enum vadc_scale_fn_type { SCALE_DEFAULT = 0, @@ -127,6 +131,7 @@ enum vadc_scale_fn_type { SCALE_HW_CALIB_PMIC_THERM, SCALE_HW_CALIB_CUR, SCALE_HW_CALIB_PM5_CHG_TEMP, + SCALE_HW_CALIB_PM5_SMB_TEMP, }; struct adc_data { diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index d2f74721b3ba6563f14ed9ee6e1a83ffa33810c6..40475ebf3a61bc1f4acb51c7cd5bf6b6f1ca8a9e 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -207,6 +207,22 @@ int rdma_addr_size(struct sockaddr *addr) } EXPORT_SYMBOL(rdma_addr_size); +int rdma_addr_size_in6(struct sockaddr_in6 *addr) +{ + int ret = rdma_addr_size((struct sockaddr *) addr); + + return ret <= sizeof(*addr) ? ret : 0; +} +EXPORT_SYMBOL(rdma_addr_size_in6); + +int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr) +{ + int ret = rdma_addr_size((struct sockaddr *) addr); + + return ret <= sizeof(*addr) ? ret : 0; +} +EXPORT_SYMBOL(rdma_addr_size_kss); + static struct rdma_addr_client self; void rdma_addr_register_client(struct rdma_addr_client *client) @@ -597,6 +613,15 @@ static void process_one_req(struct work_struct *_work) list_del(&req->list); mutex_unlock(&lock); + /* + * Although the work will normally have been canceled by the + * workqueue, it can still be requeued as long as it is on the + * req_list, so it could have been requeued before we grabbed &lock. + * We need to cancel it after it is removed from req_list to really be + * sure it is safe to free. + */ + cancel_delayed_work(&req->work); + req->callback(req->status, (struct sockaddr *)&req->src_addr, req->addr, req->context); put_client(req->client); diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 6cae00ecc90586f3abf4c5d5ecab4aa725261e24..6c725c435f5dd745df6f1805e626c16469839640 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -624,11 +624,13 @@ static inline int cma_validate_port(struct ib_device *device, u8 port, if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port)) return ret; - if (dev_type == ARPHRD_ETHER && rdma_protocol_roce(device, port)) + if (dev_type == ARPHRD_ETHER && rdma_protocol_roce(device, port)) { ndev = dev_get_by_index(&init_net, bound_if_index); - else + if (!ndev) + return ret; + } else { gid_type = IB_GID_TYPE_IB; - + } ret = ib_find_cached_gid_by_port(device, gid, gid_type, port, ndev, NULL); @@ -4453,6 +4455,7 @@ static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb) id_stats->qp_type = id->qp_type; i_id++; + nlmsg_end(skb, nlh); } cb->args[1] = 0; diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c index f2ae75fa3128b9101985bb92e882b596404c7ebc..757d308bebe84c2a9b093c44dc46654f50304022 100644 --- a/drivers/infiniband/core/cq.c +++ b/drivers/infiniband/core/cq.c @@ -17,6 +17,7 @@ /* # of WCs to poll for with a single call to ib_poll_cq */ #define IB_POLL_BATCH 16 +#define IB_POLL_BATCH_DIRECT 8 /* # of WCs to iterate over before yielding */ #define IB_POLL_BUDGET_IRQ 256 @@ -25,7 +26,8 @@ #define IB_POLL_FLAGS \ (IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS) -static int __ib_process_cq(struct ib_cq *cq, int budget) +static int __ib_process_cq(struct ib_cq *cq, int budget, struct ib_wc *wcs, + int batch) { int i, n, completed = 0; @@ -34,10 +36,10 @@ static int __ib_process_cq(struct ib_cq *cq, int budget) * want to bound this call, thus we need unsigned * minimum here. */ - while ((n = ib_poll_cq(cq, min_t(u32, IB_POLL_BATCH, - budget - completed), cq->wc)) > 0) { + while ((n = ib_poll_cq(cq, min_t(u32, batch, + budget - completed), wcs)) > 0) { for (i = 0; i < n; i++) { - struct ib_wc *wc = &cq->wc[i]; + struct ib_wc *wc = &wcs[i]; if (wc->wr_cqe) wc->wr_cqe->done(cq, wc); @@ -47,8 +49,7 @@ static int __ib_process_cq(struct ib_cq *cq, int budget) completed += n; - if (n != IB_POLL_BATCH || - (budget != -1 && completed >= budget)) + if (n != batch || (budget != -1 && completed >= budget)) break; } @@ -60,18 +61,20 @@ static int __ib_process_cq(struct ib_cq *cq, int budget) * @cq: CQ to process * @budget: number of CQEs to poll for * - * This function is used to process all outstanding CQ entries on a - * %IB_POLL_DIRECT CQ. It does not offload CQ processing to a different - * context and does not ask for completion interrupts from the HCA. + * This function is used to process all outstanding CQ entries. + * It does not offload CQ processing to a different context and does + * not ask for completion interrupts from the HCA. + * Using direct processing on CQ with non IB_POLL_DIRECT type may trigger + * concurrent processing. * * Note: do not pass -1 as %budget unless it is guaranteed that the number * of completions that will be processed is small. */ int ib_process_cq_direct(struct ib_cq *cq, int budget) { - WARN_ON_ONCE(cq->poll_ctx != IB_POLL_DIRECT); + struct ib_wc wcs[IB_POLL_BATCH_DIRECT]; - return __ib_process_cq(cq, budget); + return __ib_process_cq(cq, budget, wcs, IB_POLL_BATCH_DIRECT); } EXPORT_SYMBOL(ib_process_cq_direct); @@ -85,7 +88,7 @@ static int ib_poll_handler(struct irq_poll *iop, int budget) struct ib_cq *cq = container_of(iop, struct ib_cq, iop); int completed; - completed = __ib_process_cq(cq, budget); + completed = __ib_process_cq(cq, budget, cq->wc, IB_POLL_BATCH); if (completed < budget) { irq_poll_complete(&cq->iop); if (ib_req_notify_cq(cq, IB_POLL_FLAGS) > 0) @@ -105,7 +108,8 @@ static void ib_cq_poll_work(struct work_struct *work) struct ib_cq *cq = container_of(work, struct ib_cq, work); int completed; - completed = __ib_process_cq(cq, IB_POLL_BUDGET_WORKQUEUE); + completed = __ib_process_cq(cq, IB_POLL_BUDGET_WORKQUEUE, cq->wc, + IB_POLL_BATCH); if (completed >= IB_POLL_BUDGET_WORKQUEUE || ib_req_notify_cq(cq, IB_POLL_FLAGS) > 0) queue_work(ib_comp_wq, &cq->work); diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 77ca9da570a25e2a37b6c03124977f8d9508765f..0698d92e265665b2643c24230470c61912a2b47d 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -132,7 +132,7 @@ static inline struct ucma_context *_ucma_find_context(int id, ctx = idr_find(&ctx_idr, id); if (!ctx) ctx = ERR_PTR(-ENOENT); - else if (ctx->file != file) + else if (ctx->file != file || !ctx->cm_id) ctx = ERR_PTR(-EINVAL); return ctx; } @@ -456,6 +456,7 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf, struct rdma_ucm_create_id cmd; struct rdma_ucm_create_id_resp resp; struct ucma_context *ctx; + struct rdma_cm_id *cm_id; enum ib_qp_type qp_type; int ret; @@ -476,10 +477,10 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf, return -ENOMEM; ctx->uid = cmd.uid; - ctx->cm_id = rdma_create_id(current->nsproxy->net_ns, - ucma_event_handler, ctx, cmd.ps, qp_type); - if (IS_ERR(ctx->cm_id)) { - ret = PTR_ERR(ctx->cm_id); + cm_id = rdma_create_id(current->nsproxy->net_ns, + ucma_event_handler, ctx, cmd.ps, qp_type); + if (IS_ERR(cm_id)) { + ret = PTR_ERR(cm_id); goto err1; } @@ -489,14 +490,19 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf, ret = -EFAULT; goto err2; } + + ctx->cm_id = cm_id; return 0; err2: - rdma_destroy_id(ctx->cm_id); + rdma_destroy_id(cm_id); err1: mutex_lock(&mut); idr_remove(&ctx_idr, ctx->id); mutex_unlock(&mut); + mutex_lock(&file->mut); + list_del(&ctx->list); + mutex_unlock(&file->mut); kfree(ctx); return ret; } @@ -626,6 +632,9 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf, if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT; + if (!rdma_addr_size_in6(&cmd.addr)) + return -EINVAL; + ctx = ucma_get_ctx(file, cmd.id); if (IS_ERR(ctx)) return PTR_ERR(ctx); @@ -639,22 +648,21 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf, int in_len, int out_len) { struct rdma_ucm_bind cmd; - struct sockaddr *addr; struct ucma_context *ctx; int ret; if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT; - addr = (struct sockaddr *) &cmd.addr; - if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr))) + if (cmd.reserved || !cmd.addr_size || + cmd.addr_size != rdma_addr_size_kss(&cmd.addr)) return -EINVAL; ctx = ucma_get_ctx(file, cmd.id); if (IS_ERR(ctx)) return PTR_ERR(ctx); - ret = rdma_bind_addr(ctx->cm_id, addr); + ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr); ucma_put_ctx(ctx); return ret; } @@ -670,13 +678,16 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file, if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT; + if (!rdma_addr_size_in6(&cmd.src_addr) || + !rdma_addr_size_in6(&cmd.dst_addr)) + return -EINVAL; + ctx = ucma_get_ctx(file, cmd.id); if (IS_ERR(ctx)) return PTR_ERR(ctx); ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, - (struct sockaddr *) &cmd.dst_addr, - cmd.timeout_ms); + (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms); ucma_put_ctx(ctx); return ret; } @@ -686,24 +697,23 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file, int in_len, int out_len) { struct rdma_ucm_resolve_addr cmd; - struct sockaddr *src, *dst; struct ucma_context *ctx; int ret; if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT; - src = (struct sockaddr *) &cmd.src_addr; - dst = (struct sockaddr *) &cmd.dst_addr; - if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) || - !cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst))) + if (cmd.reserved || + (cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) || + !cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr))) return -EINVAL; ctx = ucma_get_ctx(file, cmd.id); if (IS_ERR(ctx)) return PTR_ERR(ctx); - ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms); + ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, + (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms); ucma_put_ctx(ctx); return ret; } @@ -904,13 +914,14 @@ static ssize_t ucma_query_path(struct ucma_context *ctx, resp->path_data[i].flags = IB_PATH_GMP | IB_PATH_PRIMARY | IB_PATH_BIDIRECTIONAL; - if (rec->rec_type == SA_PATH_REC_TYPE_IB) { - ib_sa_pack_path(rec, &resp->path_data[i].path_rec); - } else { + if (rec->rec_type == SA_PATH_REC_TYPE_OPA) { struct sa_path_rec ib; sa_convert_path_opa_to_ib(&ib, rec); ib_sa_pack_path(&ib, &resp->path_data[i].path_rec); + + } else { + ib_sa_pack_path(rec, &resp->path_data[i].path_rec); } } @@ -1155,6 +1166,11 @@ static ssize_t ucma_init_qp_attr(struct ucma_file *file, if (IS_ERR(ctx)) return PTR_ERR(ctx); + if (!ctx->cm_id->device) { + ret = -EINVAL; + goto out; + } + resp.qp_attr_mask = 0; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = cmd.qp_state; @@ -1225,6 +1241,9 @@ static int ucma_set_ib_path(struct ucma_context *ctx, if (!optlen) return -EINVAL; + if (!ctx->cm_id->device) + return -EINVAL; + memset(&sa_path, 0, sizeof(sa_path)); sa_path.rec_type = SA_PATH_REC_TYPE_IB; @@ -1320,7 +1339,7 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, { struct rdma_ucm_notify cmd; struct ucma_context *ctx; - int ret; + int ret = -EINVAL; if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT; @@ -1329,7 +1348,9 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, if (IS_ERR(ctx)) return PTR_ERR(ctx); - ret = rdma_notify(ctx->cm_id, (enum ib_event_type) cmd.event); + if (ctx->cm_id->device) + ret = rdma_notify(ctx->cm_id, (enum ib_event_type)cmd.event); + ucma_put_ctx(ctx); return ret; } @@ -1415,7 +1436,7 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file, join_cmd.response = cmd.response; join_cmd.uid = cmd.uid; join_cmd.id = cmd.id; - join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr); + join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr); if (!join_cmd.addr_size) return -EINVAL; @@ -1434,7 +1455,7 @@ static ssize_t ucma_join_multicast(struct ucma_file *file, if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT; - if (!rdma_addr_size((struct sockaddr *)&cmd.addr)) + if (!rdma_addr_size_kss(&cmd.addr)) return -EINVAL; return ucma_process_join(file, &cmd, out_len); diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index 5286ad57d903c737cf44668b964d48f7b66ad636..8f2dc79ad4ecc09fc42e9e832a10f2de14d8ef1d 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -245,16 +245,13 @@ static long ib_uverbs_cmd_verbs(struct ib_device *ib_dev, uintptr_t data[UVERBS_OPTIMIZE_USING_STACK_SZ / sizeof(uintptr_t)]; #endif - if (hdr->reserved) - return -EINVAL; - object_spec = uverbs_get_object(ib_dev, hdr->object_id); if (!object_spec) - return -EOPNOTSUPP; + return -EPROTONOSUPPORT; method_spec = uverbs_get_method(object_spec, hdr->method_id); if (!method_spec) - return -EOPNOTSUPP; + return -EPROTONOSUPPORT; if ((method_spec->flags & UVERBS_ACTION_FLAG_CREATE_ROOT) ^ !file->ucontext) return -EINVAL; @@ -310,6 +307,16 @@ static long ib_uverbs_cmd_verbs(struct ib_device *ib_dev, err = uverbs_handle_method(buf, ctx->uattrs, hdr->num_attrs, ib_dev, file, method_spec, ctx->uverbs_attr_bundle); + + /* + * EPROTONOSUPPORT is ONLY to be returned if the ioctl framework can + * not invoke the method because the request is not supported. No + * other cases should return this code. + */ + if (unlikely(err == -EPROTONOSUPPORT)) { + WARN_ON_ONCE(err == -EPROTONOSUPPORT); + err = -EINVAL; + } out: #ifdef UVERBS_OPTIMIZE_USING_STACK_SZ if (ctx_size > UVERBS_OPTIMIZE_USING_STACK_SZ) @@ -348,7 +355,7 @@ long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } if (hdr.reserved) { - err = -EOPNOTSUPP; + err = -EPROTONOSUPPORT; goto out; } diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 0e17d03ef1cb286327c524a445afba206fc6f538..82114ba86041713de97d1611a8af09e9d143225b 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -8294,8 +8294,8 @@ static irqreturn_t sdma_interrupt(int irq, void *data) /* handle the interrupt(s) */ sdma_engine_interrupt(sde, status); } else { - dd_dev_err_ratelimited(dd, "SDMA engine %u interrupt, but no status bits set\n", - sde->this_idx); + dd_dev_info_ratelimited(dd, "SDMA engine %u interrupt, but no status bits set\n", + sde->this_idx); } return IRQ_HANDLED; } @@ -12967,7 +12967,14 @@ static void disable_intx(struct pci_dev *pdev) pci_intx(pdev, 0); } -static void clean_up_interrupts(struct hfi1_devdata *dd) +/** + * hfi1_clean_up_interrupts() - Free all IRQ resources + * @dd: valid device data data structure + * + * Free the MSI or INTx IRQs and assoicated PCI resources, + * if they have been allocated. + */ +void hfi1_clean_up_interrupts(struct hfi1_devdata *dd) { int i; @@ -13344,7 +13351,7 @@ static int set_up_interrupts(struct hfi1_devdata *dd) return 0; fail: - clean_up_interrupts(dd); + hfi1_clean_up_interrupts(dd); return ret; } @@ -14770,7 +14777,6 @@ void hfi1_start_cleanup(struct hfi1_devdata *dd) aspm_exit(dd); free_cntrs(dd); free_rcverr(dd); - clean_up_interrupts(dd); finish_chip_resources(dd); } @@ -15229,7 +15235,7 @@ struct hfi1_devdata *hfi1_init_dd(struct pci_dev *pdev, bail_free_cntrs: free_cntrs(dd); bail_clear_intr: - clean_up_interrupts(dd); + hfi1_clean_up_interrupts(dd); bail_cleanup: hfi1_pcie_ddcleanup(dd); bail_free: diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index fd28f09b44452aa2ca202dc6dd0e7fe65685b9af..ee2253d069844c437e58225888744d29914e3954 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -191,9 +191,6 @@ static int hfi1_file_open(struct inode *inode, struct file *fp) if (!atomic_inc_not_zero(&dd->user_refcount)) return -ENXIO; - /* Just take a ref now. Not all opens result in a context assign */ - kobject_get(&dd->kobj); - /* The real work is performed later in assign_ctxt() */ fd = kzalloc(sizeof(*fd), GFP_KERNEL); @@ -203,6 +200,7 @@ static int hfi1_file_open(struct inode *inode, struct file *fp) fd->mm = current->mm; mmgrab(fd->mm); fd->dd = dd; + kobject_get(&fd->dd->kobj); fp->private_data = fd; } else { fp->private_data = NULL; diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 3409eee1609258f066e29ac4d9c64168e407e7de..dc9c951ef946e0996056e43f670e7b767a8aa304 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -1954,6 +1954,7 @@ void hfi1_verbs_unregister_sysfs(struct hfi1_devdata *dd); int qsfp_dump(struct hfi1_pportdata *ppd, char *buf, int len); int hfi1_pcie_init(struct pci_dev *pdev, const struct pci_device_id *ent); +void hfi1_clean_up_interrupts(struct hfi1_devdata *dd); void hfi1_pcie_cleanup(struct pci_dev *pdev); int hfi1_pcie_ddinit(struct hfi1_devdata *dd, struct pci_dev *pdev); void hfi1_pcie_ddcleanup(struct hfi1_devdata *); diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index fba77001c3a7033791f04bfd923da94151645f72..d4fc8795cdf642f57bcf3fbe1bdf2d05f3d2d7f8 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1039,8 +1039,9 @@ static void shutdown_device(struct hfi1_devdata *dd) } dd->flags &= ~HFI1_INITTED; - /* mask interrupts, but not errors */ + /* mask and clean up interrupts, but not errors */ set_intr_state(dd, 0); + hfi1_clean_up_interrupts(dd); for (pidx = 0; pidx < dd->num_pports; ++pidx) { ppd = dd->pport + pidx; @@ -1696,6 +1697,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dd_dev_err(dd, "Failed to create /dev devices: %d\n", -j); if (initfail || ret) { + hfi1_clean_up_interrupts(dd); stop_timers(dd); flush_workqueue(ib_wq); for (pidx = 0; pidx < dd->num_pports; ++pidx) { diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.c b/drivers/infiniband/hw/i40iw/i40iw_cm.c index d6a1a308c6a096411fba8c9369e1551575c8b8a3..b7f1ce5333cb828d2da4ce4fa2732587d3133a8c 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_cm.c +++ b/drivers/infiniband/hw/i40iw/i40iw_cm.c @@ -125,7 +125,8 @@ static u8 i40iw_derive_hw_ird_setting(u16 cm_ird) * @conn_ird: connection IRD * @conn_ord: connection ORD */ -static void i40iw_record_ird_ord(struct i40iw_cm_node *cm_node, u16 conn_ird, u16 conn_ord) +static void i40iw_record_ird_ord(struct i40iw_cm_node *cm_node, u32 conn_ird, + u32 conn_ord) { if (conn_ird > I40IW_MAX_IRD_SIZE) conn_ird = I40IW_MAX_IRD_SIZE; @@ -3841,7 +3842,7 @@ int i40iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } cm_node->apbvt_set = true; - i40iw_record_ird_ord(cm_node, (u16)conn_param->ird, (u16)conn_param->ord); + i40iw_record_ird_ord(cm_node, conn_param->ird, conn_param->ord); if (cm_node->send_rdma0_op == SEND_RDMA_READ_ZERO && !cm_node->ord_size) cm_node->ord_size = 1; diff --git a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c index d86f3e6708040599fb61fc12190bb3d8c98c15d4..472ef4d6e858a7cd97bcaa5828f92ad0ff20bfcc 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c +++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c @@ -3875,8 +3875,10 @@ enum i40iw_status_code i40iw_config_fpm_values(struct i40iw_sc_dev *dev, u32 qp_ hmc_info->hmc_obj[I40IW_HMC_IW_APBVT_ENTRY].cnt = 1; hmc_info->hmc_obj[I40IW_HMC_IW_MR].cnt = mrwanted; - hmc_info->hmc_obj[I40IW_HMC_IW_XF].cnt = I40IW_MAX_WQ_ENTRIES * qpwanted; - hmc_info->hmc_obj[I40IW_HMC_IW_Q1].cnt = 4 * I40IW_MAX_IRD_SIZE * qpwanted; + hmc_info->hmc_obj[I40IW_HMC_IW_XF].cnt = + roundup_pow_of_two(I40IW_MAX_WQ_ENTRIES * qpwanted); + hmc_info->hmc_obj[I40IW_HMC_IW_Q1].cnt = + roundup_pow_of_two(2 * I40IW_MAX_IRD_SIZE * qpwanted); hmc_info->hmc_obj[I40IW_HMC_IW_XFFL].cnt = hmc_info->hmc_obj[I40IW_HMC_IW_XF].cnt / hmc_fpm_misc->xf_block_size; hmc_info->hmc_obj[I40IW_HMC_IW_Q1FL].cnt = diff --git a/drivers/infiniband/hw/i40iw/i40iw_d.h b/drivers/infiniband/hw/i40iw/i40iw_d.h index 24eabcad5e405c830cd6a1e34d7d830672f5126b..019ad3b939f97cda799a03023b12599757653127 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_d.h +++ b/drivers/infiniband/hw/i40iw/i40iw_d.h @@ -93,6 +93,7 @@ #define RDMA_OPCODE_MASK 0x0f #define RDMA_READ_REQ_OPCODE 1 #define Q2_BAD_FRAME_OFFSET 72 +#define Q2_FPSN_OFFSET 64 #define CQE_MAJOR_DRV 0x8000 #define I40IW_TERM_SENT 0x01 diff --git a/drivers/infiniband/hw/i40iw/i40iw_puda.c b/drivers/infiniband/hw/i40iw/i40iw_puda.c index 59f70676f0e0305ad03567192a340ecb0ef0c7a5..27a2d782f6d95f9899822c27b08a8eca268649f2 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_puda.c +++ b/drivers/infiniband/hw/i40iw/i40iw_puda.c @@ -48,7 +48,6 @@ static void i40iw_ieq_tx_compl(struct i40iw_sc_vsi *vsi, void *sqwrid); static void i40iw_ilq_putback_rcvbuf(struct i40iw_sc_qp *qp, u32 wqe_idx); static enum i40iw_status_code i40iw_puda_replenish_rq(struct i40iw_puda_rsrc *rsrc, bool initial); -static void i40iw_ieq_cleanup_qp(struct i40iw_puda_rsrc *ieq, struct i40iw_sc_qp *qp); /** * i40iw_puda_get_listbuf - get buffer from puda list * @list: list to use for buffers (ILQ or IEQ) @@ -1376,7 +1375,7 @@ static void i40iw_ieq_handle_exception(struct i40iw_puda_rsrc *ieq, u32 *hw_host_ctx = (u32 *)qp->hw_host_ctx; u32 rcv_wnd = hw_host_ctx[23]; /* first partial seq # in q2 */ - u32 fps = qp->q2_buf[16]; + u32 fps = *(u32 *)(qp->q2_buf + Q2_FPSN_OFFSET); struct list_head *rxlist = &pfpdu->rxlist; struct list_head *plist; @@ -1480,7 +1479,7 @@ static void i40iw_ieq_tx_compl(struct i40iw_sc_vsi *vsi, void *sqwrid) * @ieq: ieq resource * @qp: all pending fpdu buffers */ -static void i40iw_ieq_cleanup_qp(struct i40iw_puda_rsrc *ieq, struct i40iw_sc_qp *qp) +void i40iw_ieq_cleanup_qp(struct i40iw_puda_rsrc *ieq, struct i40iw_sc_qp *qp) { struct i40iw_puda_buf *buf; struct i40iw_pfpdu *pfpdu = &qp->pfpdu; diff --git a/drivers/infiniband/hw/i40iw/i40iw_puda.h b/drivers/infiniband/hw/i40iw/i40iw_puda.h index dba05ce7d3929db3f1ac55cdba359fec64f29dcd..ebe37f157d90f6fb55357c61bed0c187cb950c5f 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_puda.h +++ b/drivers/infiniband/hw/i40iw/i40iw_puda.h @@ -186,4 +186,5 @@ enum i40iw_status_code i40iw_cqp_qp_create_cmd(struct i40iw_sc_dev *dev, struct enum i40iw_status_code i40iw_cqp_cq_create_cmd(struct i40iw_sc_dev *dev, struct i40iw_sc_cq *cq); void i40iw_cqp_qp_destroy_cmd(struct i40iw_sc_dev *dev, struct i40iw_sc_qp *qp); void i40iw_cqp_cq_destroy_cmd(struct i40iw_sc_dev *dev, struct i40iw_sc_cq *cq); +void i40iw_ieq_cleanup_qp(struct i40iw_puda_rsrc *ieq, struct i40iw_sc_qp *qp); #endif diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 62be0a41ad0b2ffeccb0b6e73d9a29621e4944f1..b7961f21b555b5a348c520aa184edfe00dacd116 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -428,6 +428,7 @@ void i40iw_free_qp_resources(struct i40iw_device *iwdev, { struct i40iw_pbl *iwpbl = &iwqp->iwpbl; + i40iw_ieq_cleanup_qp(iwdev->vsi.ieq, &iwqp->sc_qp); i40iw_dealloc_push_page(iwdev, &iwqp->sc_qp); if (qp_num) i40iw_free_resource(iwdev, iwdev->allocated_qps, qp_num); @@ -1655,6 +1656,7 @@ static struct ib_mr *i40iw_alloc_mr(struct ib_pd *pd, err_code = -EOVERFLOW; goto err; } + stag &= ~I40IW_CQPSQ_STAG_KEY_MASK; iwmr->stag = stag; iwmr->ibmr.rkey = stag; iwmr->ibmr.lkey = stag; diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 6d48d8a93b62ca48178937698f848ab5b4e0a0c0..538f1784863a1b43ba08d010c0e536324e686238 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1220,6 +1220,8 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, return ERR_PTR(-EINVAL); mr = mlx5_ib_alloc_implicit_mr(to_mpd(pd), access_flags); + if (IS_ERR(mr)) + return ERR_CAST(mr); return &mr->ibmr; } #endif diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index c4d8cc1c2b1d4bac21c829a665165c5adf97718b..464c78f8cec9a459ae639b9251f2f0abda240571 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -2923,7 +2923,8 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, * If we moved a kernel QP to RESET, clean up all old CQ * entries and reinitialize the QP. */ - if (new_state == IB_QPS_RESET && !ibqp->uobject) { + if (new_state == IB_QPS_RESET && + !ibqp->uobject && ibqp->qp_type != IB_QPT_XRC_TGT) { mlx5_ib_cq_clean(recv_cq, base->mqp.qpn, ibqp->srq ? to_msrq(ibqp->srq) : NULL); if (send_cq != recv_cq) @@ -4636,13 +4637,10 @@ int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd) int err; err = mlx5_core_xrcd_dealloc(dev->mdev, xrcdn); - if (err) { + if (err) mlx5_ib_warn(dev, "failed to dealloc xrcdn 0x%x\n", xrcdn); - return err; - } kfree(xrcd); - return 0; } diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index 14cadf6d621463e07a98e9e598982648729afacc..a45e460989149db00406fb2573112fe24cb9c6fd 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -150,7 +150,7 @@ static struct kparam_string kp_txselect = { .string = txselect_list, .maxlen = MAX_ATTEN_LEN }; -static int setup_txselect(const char *, struct kernel_param *); +static int setup_txselect(const char *, const struct kernel_param *); module_param_call(txselect, setup_txselect, param_get_string, &kp_txselect, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(txselect, @@ -6169,7 +6169,7 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change) } /* handle the txselect parameter changing */ -static int setup_txselect(const char *str, struct kernel_param *kp) +static int setup_txselect(const char *str, const struct kernel_param *kp) { struct qib_devdata *dd; unsigned long val; diff --git a/drivers/infiniband/sw/rdmavt/cq.c b/drivers/infiniband/sw/rdmavt/cq.c index 97d71e49c092f4e820efc4ebe23ec2bbdc411cbd..88fa4d44ab5fbe8e504301b6990625bf4990edb7 100644 --- a/drivers/infiniband/sw/rdmavt/cq.c +++ b/drivers/infiniband/sw/rdmavt/cq.c @@ -198,7 +198,7 @@ struct ib_cq *rvt_create_cq(struct ib_device *ibdev, return ERR_PTR(-EINVAL); /* Allocate the completion queue structure. */ - cq = kzalloc(sizeof(*cq), GFP_KERNEL); + cq = kzalloc_node(sizeof(*cq), GFP_KERNEL, rdi->dparms.node); if (!cq) return ERR_PTR(-ENOMEM); @@ -214,7 +214,9 @@ struct ib_cq *rvt_create_cq(struct ib_device *ibdev, sz += sizeof(struct ib_uverbs_wc) * (entries + 1); else sz += sizeof(struct ib_wc) * (entries + 1); - wc = vmalloc_user(sz); + wc = udata ? + vmalloc_user(sz) : + vzalloc_node(sz, rdi->dparms.node); if (!wc) { ret = ERR_PTR(-ENOMEM); goto bail_cq; @@ -369,7 +371,9 @@ int rvt_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) sz += sizeof(struct ib_uverbs_wc) * (cqe + 1); else sz += sizeof(struct ib_wc) * (cqe + 1); - wc = vmalloc_user(sz); + wc = udata ? + vmalloc_user(sz) : + vzalloc_node(sz, rdi->dparms.node); if (!wc) return -ENOMEM; diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index afbf701dc9a7cea3764e555170c61463355ea7f8..906bacf365d4170b57a080901b590e4b2e168d29 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -712,9 +712,8 @@ static int init_send_wqe(struct rxe_qp *qp, struct ib_send_wr *ibwr, memcpy(wqe->dma.sge, ibwr->sg_list, num_sge * sizeof(struct ib_sge)); - wqe->iova = (mask & WR_ATOMIC_MASK) ? - atomic_wr(ibwr)->remote_addr : - rdma_wr(ibwr)->remote_addr; + wqe->iova = mask & WR_ATOMIC_MASK ? atomic_wr(ibwr)->remote_addr : + mask & WR_READ_OR_WRITE_MASK ? rdma_wr(ibwr)->remote_addr : 0; wqe->mask = mask; wqe->dma.length = length; wqe->dma.resid = length; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index a009e943362aa1cfcd71e330b188876b4b7c5270..6bc9a768f72193ef0a9b3ad00ed4e9f4da8f23d1 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -2273,6 +2273,9 @@ static struct net_device *ipoib_add_port(const char *format, priv->ca, ipoib_event); ib_register_event_handler(&priv->event_handler); + /* call event handler to ensure pkey in sync */ + queue_work(ipoib_workqueue, &priv->flush_heavy); + result = register_netdev(priv->dev); if (result) { printk(KERN_WARNING "%s: couldn't register ipoib port %d; error %d\n", diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 60d7b493ed2dc1d149e756338894cce34a806483..299a97b7e17fff2982e8762e4620afb904683870 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -2656,9 +2656,11 @@ static int srp_abort(struct scsi_cmnd *scmnd) ret = FAST_IO_FAIL; else ret = FAILED; - srp_free_req(ch, req, scmnd, 0); - scmnd->result = DID_ABORT << 16; - scmnd->scsi_done(scmnd); + if (ret == SUCCESS) { + srp_free_req(ch, req, scmnd, 0); + scmnd->result = DID_ABORT << 16; + scmnd->scsi_done(scmnd); + } return ret; } @@ -3428,12 +3430,10 @@ static ssize_t srp_create_target(struct device *dev, num_online_nodes()); const int ch_end = ((node_idx + 1) * target->ch_count / num_online_nodes()); - const int cv_start = (node_idx * ibdev->num_comp_vectors / - num_online_nodes() + target->comp_vector) - % ibdev->num_comp_vectors; - const int cv_end = ((node_idx + 1) * ibdev->num_comp_vectors / - num_online_nodes() + target->comp_vector) - % ibdev->num_comp_vectors; + const int cv_start = node_idx * ibdev->num_comp_vectors / + num_online_nodes(); + const int cv_end = (node_idx + 1) * ibdev->num_comp_vectors / + num_online_nodes(); int cpu_idx = 0; for_each_online_cpu(cpu) { diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index ee578fa713c28d9eb773211bfb53a7ce512327d4..7b1d7e58671ed5674ec49a13d2e050e3c3116b24 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -80,7 +80,7 @@ module_param(srpt_srq_size, int, 0444); MODULE_PARM_DESC(srpt_srq_size, "Shared receive queue (SRQ) size."); -static int srpt_get_u64_x(char *buffer, struct kernel_param *kp) +static int srpt_get_u64_x(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "0x%016llx", *(u64 *)kp->arg); } diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 3d9c294e84db65805f1937bcea085d951fe0b184..9a234da8cac23fa0c16f4efe4daf92be7e5863aa 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2544,13 +2544,31 @@ static int alps_update_btn_info_ss4_v2(unsigned char otp[][4], } static int alps_update_dual_info_ss4_v2(unsigned char otp[][4], - struct alps_data *priv) + struct alps_data *priv, + struct psmouse *psmouse) { bool is_dual = false; + int reg_val = 0; + struct ps2dev *ps2dev = &psmouse->ps2dev; - if (IS_SS4PLUS_DEV(priv->dev_id)) + if (IS_SS4PLUS_DEV(priv->dev_id)) { is_dual = (otp[0][0] >> 4) & 0x01; + if (!is_dual) { + /* For support TrackStick of Thinkpad L/E series */ + if (alps_exit_command_mode(psmouse) == 0 && + alps_enter_command_mode(psmouse) == 0) { + reg_val = alps_command_mode_read_reg(psmouse, + 0xD7); + } + alps_exit_command_mode(psmouse); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + + if (reg_val == 0x0C || reg_val == 0x1D) + is_dual = true; + } + } + if (is_dual) priv->flags |= ALPS_DUALPOINT | ALPS_DUALPOINT_WITH_PRESSURE; @@ -2573,7 +2591,7 @@ static int alps_set_defaults_ss4_v2(struct psmouse *psmouse, alps_update_btn_info_ss4_v2(otp, priv); - alps_update_dual_info_ss4_v2(otp, priv); + alps_update_dual_info_ss4_v2(otp, priv, psmouse); return 0; } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 6a5649e52eed932226b87dd76c86fbcfaaa8d33a..8ac9e03c05b457523b7480bfca1154144ac64da7 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -975,6 +975,21 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) psmouse->pt_deactivate = NULL; } +static bool psmouse_do_detect(int (*detect)(struct psmouse *, bool), + struct psmouse *psmouse, bool allow_passthrough, + bool set_properties) +{ + if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU && + !allow_passthrough) { + return false; + } + + if (set_properties) + psmouse_apply_defaults(psmouse); + + return detect(psmouse, set_properties) == 0; +} + static bool psmouse_try_protocol(struct psmouse *psmouse, enum psmouse_type type, unsigned int *max_proto, @@ -986,15 +1001,8 @@ static bool psmouse_try_protocol(struct psmouse *psmouse, if (!proto) return false; - if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU && - !proto->try_passthru) { - return false; - } - - if (set_properties) - psmouse_apply_defaults(psmouse); - - if (proto->detect(psmouse, set_properties) != 0) + if (!psmouse_do_detect(proto->detect, psmouse, proto->try_passthru, + set_properties)) return false; if (set_properties && proto->init && init_allowed) { @@ -1027,8 +1035,8 @@ static int psmouse_extensions(struct psmouse *psmouse, * Always check for focaltech, this is safe as it uses pnp-id * matching. */ - if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH, - &max_proto, set_properties, false)) { + if (psmouse_do_detect(focaltech_detect, + psmouse, false, set_properties)) { if (max_proto > PSMOUSE_IMEX && IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) && (!set_properties || focaltech_init(psmouse) == 0)) { @@ -1074,8 +1082,8 @@ static int psmouse_extensions(struct psmouse *psmouse, * probing for IntelliMouse. */ if (max_proto > PSMOUSE_PS2 && - psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto, - set_properties, false)) { + psmouse_do_detect(synaptics_detect, + psmouse, false, set_properties)) { synaptics_hardware = true; if (max_proto > PSMOUSE_IMEX) { diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index ee5466a374bf0f370000cf002d94bf2514d02318..a246fc686bb728dbe48b2fc84b90a1734af60c66 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1280,6 +1280,16 @@ static void set_input_params(struct psmouse *psmouse, INPUT_MT_POINTER | (cr48_profile_sensor ? INPUT_MT_TRACK : INPUT_MT_SEMI_MT)); + + /* + * For semi-mt devices we send ABS_X/Y ourselves instead of + * input_mt_report_pointer_emulation. But + * input_mt_init_slots() resets the fuzz to 0, leading to a + * filtered ABS_MT_POSITION_X but an unfiltered ABS_X + * position. Let's re-initialize ABS_X/Y here. + */ + if (!cr48_profile_sensor) + set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y); } if (SYN_CAP_PALMDETECT(info->capabilities)) diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 6cbbdc6e968756a495301f340a2085d3fdf74e65..b353d494ad404888bd2884527fe771937cb1416f 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -530,6 +530,20 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { } }; +static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = { + { + /* + * Sony Vaio VGN-CS series require MUX or the touch sensor + * buttons will disturb touchpad operation + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"), + }, + }, + { } +}; + /* * On some Asus laptops, just running self tests cause problems. */ @@ -620,6 +634,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "20046"), }, }, + { + /* Lenovo ThinkPad L460 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"), + }, + }, { /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ .matches = { @@ -1163,6 +1184,9 @@ static int __init i8042_platform_init(void) if (dmi_check_system(i8042_dmi_nomux_table)) i8042_nomux = true; + if (dmi_check_system(i8042_dmi_forcemux_table)) + i8042_nomux = false; + if (dmi_check_system(i8042_dmi_notimeout_table)) i8042_notimeout = true; diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index b3bbad7d228296118f35a2d4bff7c295b5e9839c..5dafafad6351a09362484ba011991dea4eb9b174 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -808,8 +808,10 @@ static int __maybe_unused goodix_suspend(struct device *dev) int error; /* We need gpio pins to suspend/resume */ - if (!ts->gpiod_int || !ts->gpiod_rst) + if (!ts->gpiod_int || !ts->gpiod_rst) { + disable_irq(client->irq); return 0; + } wait_for_completion(&ts->firmware_loading_complete); @@ -849,8 +851,10 @@ static int __maybe_unused goodix_resume(struct device *dev) struct goodix_ts_data *ts = i2c_get_clientdata(client); int error; - if (!ts->gpiod_int || !ts->gpiod_rst) + if (!ts->gpiod_int || !ts->gpiod_rst) { + enable_irq(client->irq); return 0; + } /* * Exit sleep mode by outputting HIGH level to INT pin diff --git a/drivers/input/touchscreen/st/Kconfig b/drivers/input/touchscreen/st/Kconfig index 817faea01742294b7683dbe37253e33feb2aac1d..10c908538fcfa29fd4fd13e9d528106a146fcdfa 100644 --- a/drivers/input/touchscreen/st/Kconfig +++ b/drivers/input/touchscreen/st/Kconfig @@ -2,8 +2,24 @@ # STMicroelectronics touchscreen driver configuration # +#config TOUCHSCREEN_ST +# bool "STMicroelectronics Touchscreen Driver" +# default n +# depends on I2C +# help +# Say Y here if you have a STMicroelectronics Touchscreen. +# If unsure, say N. +# + +#if TOUCHSCREEN_ST + config TOUCHSCREEN_ST_I2C - tristate "STMicroelectronics i2c touchscreen" + #tristate "STMicroelectronics i2c touchscreen" + string "STMicroelectronics ts directory name" + default "st" depends on TOUCHSCREEN_ST help - This enables support for ST touch panel over I2C based touchscreens. + This enables support for ST touch panel over I2C based touchscreens. + +#endif + diff --git a/drivers/input/touchscreen/st/Makefile b/drivers/input/touchscreen/st/Makefile index 0aa7b4a364dad7bd124042d0f9e27388081801ff..f67f9fe9df2f3ef0048771be99ec4d4e1061ce74 100644 --- a/drivers/input/touchscreen/st/Makefile +++ b/drivers/input/touchscreen/st/Makefile @@ -2,4 +2,4 @@ ## Makefile for the STMicroelectronics touchscreen driver. # -obj-$(CONFIG_TOUCHSCREEN_ST_I2C) += fts.o fts_gui.o fts_driver_test.o fts_lib/ +obj-$(CONFIG_TOUCHSCREEN_ST) += fts.o fts_gui.o fts_driver_test.o fts_lib/ diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 08bfb83a94478b4cb11ce24c6129abfc0437c979..99aa77338399ebc1fba77c0e7ef4dc09cdea56c3 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -3,7 +3,7 @@ * * FTS Capacitive touch screen controller (FingerTipS) * - * Copyright (C) 2016, STMicroelectronics Limited. + * Copyright (C) 2016-2018, STMicroelectronics Limited. * Authors: AMG(Analog Mems Group) * * marco.cali@st.com @@ -28,18 +28,25 @@ #include #include #include +/*#include */ +#include #include #include #include +#if defined(CONFIG_FB_MSM) #include #include +#else +#include +#endif #ifdef KERNEL_ABOVE_2_6_38 #include #endif + #include "fts.h" #include "fts_lib/ftsCompensation.h" #include "fts_lib/ftsIO.h" @@ -51,24 +58,26 @@ #include "fts_lib/ftsTime.h" #include "fts_lib/ftsTool.h" + + #define LINK_KOBJ_NAME "tp" /* * Uncomment to use polling mode instead of interrupt mode. * */ -/* #define FTS_USE_POLLING_MODE */ +// #define FTS_USE_POLLING_MODE + /* * Event installer helpers */ -#define event_id(_e) EVENTID_##_e +#define event_id(_e) EVENTID_##_e #define handler_name(_h) fts_##_h##_event_handler #define install_handler(_i, _evt, _hnd) \ -do { \ - _i->event_dispatch_table[event_id(_evt)] = handler_name(_hnd); \ -} while (0) + (_i->event_dispatch_table[event_id(_evt)] = handler_name(_hnd)) + /* * Asyncronouns command helper @@ -89,28 +98,45 @@ do { \ static struct class *fts_cmd_class; #endif -extern chipInfo ftsInfo; -unsigned char tune_version_same; +//struct chipInfo ftsInfo; + +/** + * #ifdef PHONE_GESTURE + * extern struct mutex gestureMask_mutex; + * #endif + */ -char tag[8] = "[ FTS ]\0"; +static char tag[8] = "[ FTS ]\0"; -static u32 *typeOfComand; +static char fts_ts_phys[64]; +static u32 typeOfComand[CMD_STR_LEN] = {0}; static int numberParameters; +#ifdef USE_ONE_FILE_NODE static int feature_feasibility = ERROR_OP_NOT_ALLOW; +#endif #ifdef PHONE_GESTURE -static u8 mask[GESTURE_MASK_SIZE+2]; +static u8 mask[GESTURE_MASK_SIZE + 2]; +//extern u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX]; +//extern u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX]; +//extern int gesture_coords_reported; +//extern struct mutex gestureMask_mutex; +#ifdef USE_CUSTOM_GESTURES +static int custom_gesture_res; +#endif +#endif +#ifdef USE_NOISE_PARAM +static u8 noise_params[NOISE_PARAMETERS_SIZE] = {0}; #endif static void fts_interrupt_enable(struct fts_ts_info *info); -static int fts_init_hw(struct fts_ts_info *info); +static int fts_init_afterProbe(struct fts_ts_info *info); static int fts_mode_handler(struct fts_ts_info *info, int force); static int fts_command(struct fts_ts_info *info, unsigned char cmd); -static int fts_chip_initialization(struct fts_ts_info *info); void touch_callback(unsigned int status) { - /* Empty */ + /* Empty */ } unsigned int le_to_uint(const unsigned char *ptr) @@ -123,168 +149,599 @@ unsigned int be_to_uint(const unsigned char *ptr) return (unsigned int) ptr[1] + (unsigned int) ptr[0] * 0x100; } -/* force update firmware*/ -static ssize_t fts_fw_control_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count){ +void release_all_touches(struct fts_ts_info *info) +{ + unsigned int type = MT_TOOL_FINGER; + int i; + + for (i = 0; i < TOUCH_ID_MAX; i++) { +#ifdef STYLUS_MODE + if (test_bit(i, &info->stylus_id)) + type = MT_TOOL_PEN; +#endif + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, type, 0); + input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); + } + input_sync(info->input_dev); + info->touch_id = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif +} + +/************************* FW UPGGRADE *********************************/ +/* update firmware*/ +/** + * echo 01/00 > fwupdate perform a fw update taking the FW to burn always + * from a bin file stored in /system/etc/firmware, 01= force the FW update + * whicheve fw_version and config_id; 00=perform a fw update only if the fw + * in the file has a greater fw_version or config_id + */ + +/** + * cat fwupdate to show the result of the burning procedure + * (example output in the terminal = "AA00000001BB" if the switch is enabled) + */ + +/** + * echo 01/00 > fwupdate; cat fwupdate to perform both operation stated before + * in just one call + */ +static ssize_t fts_fwupdate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ int ret, mode; + /*const struct firmware *fw = NULL;*/ + /*char *firmware_name = "st_fts.bin";*/ + struct Firmware fwD; struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); + int orig_size; + u8 *orig_data; /* reading out firmware upgrade mode */ - sscanf(buf, "%d", &mode); -#ifdef FTM3_CHIP - ret = flashProcedure(PATH_FILE_FW, mode, !mode); -#else - ret = flashProcedure(PATH_FILE_FW, mode, 1); -#endif + ret = kstrtoint(buf, 10, &mode); + if (ret != 0) { + pr_err("%s: ret = %d\n", __func__, ret); + return -EINVAL; + } + + fwD.data = NULL; + ret = getFWdata(PATH_FILE_FW, &orig_data, &orig_size, 0); + if (ret < OK) { + logError(1, "%s %s: impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto END; + } + + ret = parseBinFile(orig_data, orig_size, &fwD, !mode); + if (ret < OK) { + logError(1, "%s %s: impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto END; + } + + logError(0, "%s Starting flashing procedure...\n", tag); + ret = flash_burn(fwD, mode, !mode); + + if (ret < OK && ret != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) + logError(1, "%s flashProcedure: ERROR %02X\n", + tag, ERROR_FLASH_PROCEDURE); + logError(0, "%s flashing procedure Finished!\n", tag); + +END: + kfree(fwD.data); info->fwupdate_stat = ret; if (ret < OK) - logError(1, "%s %s :Unable to upgrade firmware\n", tag, __func__); + logError(1, "%s %s Unable to upgrade firmware! ERROR %08X\n", + tag, __func__, ret); + return count; } -static ssize_t fts_sysfs_config_id_show(struct device *dev, struct device_attribute *attr, - char *buf) { +static ssize_t fts_fwupdate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + //fwupdate_stat: ERROR code Returned by flashProcedure. + return snprintf(buf, PAGE_SIZE, "AA%08XBB\n", info->fwupdate_stat); +} + + +/****UTILITIES (current fw_ver/conf_id, active mode, file fw_ver/conf_id)****/ +/** + * cat appid show on the terminal fw_version.config_id of + * the FW running in the IC + */ +static ssize_t fts_sysfs_config_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ int error; - error = snprintf(buf, TSP_BUF_SIZE, "%x.%x\n", ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + error = snprintf(buf, PAGE_SIZE, "%x.%x\n", ftsInfo.u16_fwVer, + ftsInfo.u16_cfgId); return error; } -static ssize_t fts_sysfs_fwupdate_show(struct device *dev, struct device_attribute *attr, - char *buf) { +/** + * cat mode_active to show the bitmask of which indicate the modes/features + * which are running on the IC in a specific istant oftime (example output in + * the terminal = "AA10000000BB" only senseOn performed) + */ +static ssize_t fts_mode_active_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); - /* fwupdate_stat: ERROR code Returned by flashProcedure. */ - return snprintf(buf, TSP_BUF_SIZE, "%08X\n", info->fwupdate_stat); + logError(1, "%s Current mode active = %08X\n", tag, info->mode); + //return sprintf(buf, "AA%08XBB\n", info->mode); + return snprintf(buf, PAGE_SIZE, "AA%08XBB\n", info->mode); } -static ssize_t fts_fw_test_show(struct device *dev, struct device_attribute *attr, - char *buf) { - Firmware fw; +/** + * cat fw_file_test show on the terminal fw_version and config_id of the FW + * stored in the fw file/header file + */ +static ssize_t fts_fw_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Firmware fw; int ret; fw.data = NULL; ret = readFwFile(PATH_FILE_FW, &fw, 0); - if (ret < OK) { - logError(1, "%s: Error during reading FW file! ERROR %08X\n", tag, ret); - } else - logError(1, "%s: fw_version = %04X, config_version = %04X, size = %d bytes\n", - tag, fw.fw_ver, fw.config_id, fw.data_size); + if (ret < OK) + logError(1, "%s Error during reading FW file! ERROR %08X\n", + tag, ret); + else { + logError(1, "%s fw_version = %04X, config_version = %04X, ", + tag, fw.fw_ver, fw.config_id); + logError(1, "size = %dbytes\n", fw.data_size); + } kfree(fw.data); return 0; } -/* TODO: edit this function according to the features policy to allow during the screen on/off */ -int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature) + +/** + * cat lockdown_info to show the lockdown info on the terminal + * (example output in the terminal = "AA00000000X1X2..X10BB" ) + * where first 4 bytes correspond t + */ +static ssize_t fts_lockdown_info_show(struct device *dev, + struct device_attribute *attr, char *buf) { - int res = ERROR_OP_NOT_ALLOW; + u8 data[LOCKDOWN_CODE_SIZE] = {0}; + int ret, size = 100; + char buff[CMD_STR_LEN] = {0}; + char all_strbuff[100] = {0}; - if (info->resume_bit == 0) { - switch (feature) { -#ifdef PHONE_GESTURE - case FEAT_GESTURE: - res = OK; - break; -#endif - default: - logError(1, "%s %s: Feature not allowed in this operating mode! ERROR %08X\n", tag, __func__, res); - break; + ret = fts_disableInterrupt(); + if (ret < OK) + goto END; + + ret = lockDownInfo(data); + if (ret < OK) + goto END; + + +END: + ret |= fts_enableInterrupt(); + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%08X", ret); + strlcat(all_strbuff, buff, size); + if (ret >= OK) { + for (ret = 0; ret < LOCKDOWN_CODE_SIZE; ret++) { + snprintf(buff, sizeof(buff), "%02X", data[ret]); + strlcat(all_strbuff, buff, size); } - } else{ - switch (feature) { -#ifdef PHONE_GESTURE - case FEAT_GESTURE: + } else { + logError(1, "%s Error while reading lockdown info = %08X\n", + tag, ret); + } + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); +} + +/** + * cat strength_frame to obtain strength data + * the string returned in the shell is made up as follow: + * AA = start byte + * X1X2X3X4 = 4 bytes in HEX format which represent an + * error code (00000000 no error) + * + * if error code is all 0s + * FF = 1 byte in HEX format number of rows + * SS = 1 byte in HEX format number of columns + * N1, ... = the decimal value of each node separated by a coma + * + * BB = end byte + */ +static ssize_t fts_strength_frame_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + /*unsigned int temp;*/ + /*int res;*/ + /*struct i2c_client *client = to_i2c_client(dev); */ + /*struct fts_ts_info *info = i2c_get_clientdata(client); */ + + if (sscanf(p, "%d ", &typeOfComand[0]) != 1) + return -EINVAL; + + logError(1, "%s %s: Type of Strength Frame selected: %d\n", tag, + __func__, typeOfComand[0]); + return count; +} + + +static ssize_t fts_strength_frame_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct MutualSenseFrame frame; + int res = ERROR_OP_NOT_ALLOW, j, size = 6*2; + int count = 0; + u16 type = 0; + char *all_strbuff = NULL; + char buff[CMD_STR_LEN] = {0}; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + frame.node_data = NULL; + + res = fts_disableInterrupt(); + if (res < OK) + goto END; + + res = senseOn(); +#ifdef PHONE_KEY + res = keyOn(); #endif - case FEAT_GLOVE: - /* glove mode can only activate during sense on */ - res = OK; - break; + if (res < OK) { + logError(1, "%s %s: could not start scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + msleep(WAIT_FOR_FRESH_FRAMES); - default: - logError(1, "%s %s: Feature not allowed in this operating mode! ERROR %08X\n", tag, __func__, res); + res = senseOff(); +#ifdef PHONE_KEY + res = keyOff(); +#endif + if (res < OK) { + logError(1, "%s %s: could not finish scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + /* mdelay(WAIT_AFTER_SENSEOFF); */ + msleep(WAIT_AFTER_SENSEOFF); + flushFIFO(); + + switch (typeOfComand[0]) { + case 1: + type = ADDR_NORM_TOUCH; + break; +#ifdef PHONE_KEY + case 2: + type = ADDR_NORM_MS_KEY; break; +#endif + default: + logError(1, "%s %s: Strength type %d not valid! ERROR %08X\n", + tag, __func__, typeOfComand[0], ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + goto END; + } + + res = getMSFrame(type, &frame, 0); + if (res < OK) { + logError(1, "%s %s: could not get the frame! ERROR %08X\n", + tag, __func__, res); + goto END; + } else { + size += (res * 6); + logError(0, "%s The frame size is %d words\n", tag, res); + res = OK; + print_frame_short("MS Strength frame =", + array1dTo2d_short(frame.node_data, + frame.node_data_size, + frame.header.sense_node), + frame.header.force_node, + frame.header.sense_node); + } + +END: + flushFIFO(); + release_all_touches(info); + fts_mode_handler(info, 1); + + all_strbuff = kmalloc_array(size, sizeof(char), GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + snprintf(buff, sizeof(buff), "%02X", + (u8) frame.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frame.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frame.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%d,", + frame.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frame.node_data); } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s: Unable toallocate all_strbuff!ERROR %08X\n", + tag, ERROR_ALLOC); } - return res; + fts_enableInterrupt(); + return count; +} + +/********** FEATURES *********************/ + +/** + * TODO: edit this function according to the features policy to + * allow during the screen on/off, following is shown an example + * but check always with ST for more details + */ +int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature) +{ + int res = OK; + /** + * Example based on the status of the screen and + * on the feature that is trying to enable + */ + + /*Example based only on the feature that is going to be activated*/ + switch (feature) { + case FEAT_GESTURE: + if (info->cover_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + + logError(1, "%s %s:Feature not allowed when in Cover ", + tag, __func__); + logError(1, "mode %08X\n", res); + /** + * for example here can be place a code for + * disabling the cover mode when gesture is + * activated + */ + } + break; + + case FEAT_COVER: + if (info->gesture_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + /*logError(1,"Feature not allowed*/ + /*when Gestures enabled!");*/ + logError(1, "s %s: Feature not allowed when Gestures ", + tag, __func__); + logError(1, "enabled%08X\n", res); + /** + * for example here can be place a code for + * disabling the gesture mode when cover is + * activated (that means that cover mode has + * an higher priority on gesture mode) + */ + } + break; + + default: + logError(1, "%s %s: Feature Allowed!\n", tag, __func__); + } + return res; } -static ssize_t fts_feature_enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) { +#ifdef USE_ONE_FILE_NODE +/** + * echo XXXX 00/01 > feature_enable + * set the feature to disable/enable. + * XXXX = 4 bytes which identify the feature + * + * cat feature_enable + * set the enabled mode/features in the IC + * and return an error code + * + * echo XXXX 00/01 > feature_enable; + * cat feature_enable to perform both action stated + * before in just one call + */ +static ssize_t fts_feature_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); char *p = (char *)buf; unsigned int temp; int res = OK; - if ((count + 1) / 3 != 2) { - logError(1, "%s fts_feature_enable: Number of parameter wrong! %d > %d\n", - tag, (count + 1) / 3, 2); - } else{ - sscanf(p, "%02X ", &temp); - p += 3; - res = check_feature_feasibility(info, temp); - if (res >= OK) { - switch (temp) { + if ((count - 8 + 1) / 3 != 1) { + logError(1, "%s fts_feature_enable: ", tag); + logError(1, "Number of parameter wrong! %d > %d\n", + (count - 8 + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%08X ", &temp) != 1) + return -EINVAL; + p += 9; + res = check_feature_feasibility(info, temp); + if (res < OK) + return -EINVAL; + + switch (temp) { #ifdef PHONE_GESTURE - case FEAT_GESTURE: - sscanf(p, "%02X ", &info->gesture_enabled); - logError(1, "%s fts_feature_enable: Gesture Enabled = %d\n", tag, - info->gesture_enabled); - break; + case FEAT_GESTURE: + if (sscanf(p, "%02X ", &info->gesture_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Gesture Enabled = %d\n", + tag, info->gesture_enabled); + + break; #endif - case FEAT_GLOVE: - sscanf(p, "%02X ", &info->glove_enabled); - logError(1, "%s fts_feature_enable: Glove Enabled = %d\n", + +#ifdef GLOVE_MODE + case FEAT_GLOVE: + if (sscanf(p, "%02X ", &info->glove_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Glove Enabled = %d\n", tag, info->glove_enabled); - break; + break; +#endif - default: - logError(1, "%s fts_feature_enable: Feature %02X not valid! ERROR %08X\n", tag, temp, ERROR_OP_NOT_ALLOW); +#ifdef STYLUS_MODE + case FEAT_STYLUS: + if (sscanf(p, "%02X ", &info->stylus_enabled) != 1) + return -EINVAL; - } - feature_feasibility = res; - } + logError(1, "%s fts_feature_enable: Stylus Enabled = %d\n", + tag, info->stylus_enabled); + + break; +#endif + +#ifdef COVER_MODE + case FEAT_COVER: + if (sscanf(p, "%02X ", &info->cover_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Cover Enabled = %d\n", + tag, info->cover_enabled); + + break; +#endif + +#ifdef CHARGER_MODE + case FEAT_CHARGER: + if (sscanf(p, "%02X ", &info->charger_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Charger Enabled= %d\n", + tag, info->charger_enabled); + + break; +#endif + +#ifdef VR_MODE + case FEAT_VR: + if (sscanf(p, "%02X ", &info->vr_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: VR Enabled = %d\n", + tag, info->vr_enabled); + + break; +#endif + +#ifdef EDGE_REJ + case FEAT_EDGE_REJECTION: + if (sscanf(p, "%02X ", &info->edge_rej_enabled) != 1) + return -EINVAL; + logError(1, "%s %s: Edge Rejection Enabled= %d\n", + tag, __func__, info->edge_rej_enabled); + + break; +#endif + +#ifdef CORNER_REJ + case FEAT_CORNER_REJECTION: + if (sscanf(p, "%02X ", &info->corner_rej_enabled) != 1) + return -EINVAL; + + logError(1, "%s %s: Corner Rejection Enabled= %d\n", + tag, __func__, info->corner_rej_enabled); + + break; +#endif +#ifdef EDGE_PALM_REJ + case FEAT_EDGE_PALM_REJECTION: + if (sscanf(p, "%02X", &info->edge_palm_rej_enabled) != 1) + return -EINVAL; + + logError(1, "%s %s:Edge Palm RejectionEnabled= %d\n", + tag, __func__, info->edge_palm_rej_enabled); + + break; +#endif + default: + logError(1, "%s %s: Feature %08X not valid! ERROR %08X\n", + tag, __func__, temp, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + feature_feasibility = res; + + if (feature_feasibility >= OK) + feature_feasibility = fts_mode_handler(info, 1); + else { + logError(1, "%s %s: Call echo XXXX 00/01 > feature_enable ", + tag, __func__); + logError(1, "with a correct feature! ERROR %08X\n", res); } return count; } static ssize_t fts_feature_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { char buff[CMD_STR_LEN] = {0}; int size = 6 * 2; u8 *all_strbuff = NULL; - int count = 0, res; - struct i2c_client *client = to_i2c_client(dev); - struct fts_ts_info *info = i2c_get_clientdata(client); + int count = 0; - if (feature_feasibility >= OK) - res = fts_mode_handler(info, 1); - else{ - res = feature_feasibility; - logError(1, "%s %s: Call before echo xx xx > feature_enable with a correct feature! ERROR %08X\n", tag, __func__, res); + if (feature_feasibility < OK) { + logError(1, + "%s %s:Call before echo 00/01 > feature_enable %08X\n", + tag, __func__, feature_feasibility); } - all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + + all_strbuff = kmalloc(size, GFP_KERNEL); if (all_strbuff != NULL) { memset(all_strbuff, 0, size); snprintf(buff, sizeof(buff), "%02X", 0xAA); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof(buff), "%08X", res); + snprintf(buff, sizeof(buff), "%08X", feature_feasibility); strlcat(all_strbuff, buff, size); snprintf(buff, sizeof(buff), "%02X", 0xBB); @@ -292,121 +749,1406 @@ static ssize_t fts_feature_enable_show(struct device *dev, count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); kfree(all_strbuff); - } else{ - logError(1, "%s fts_feature_enable_show: Unable to allocate all_strbuff! ERROR %08X\n", tag, ERROR_ALLOC); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); } feature_feasibility = ERROR_OP_NOT_ALLOW; return count; } -#ifdef PHONE_GESTURE -static ssize_t fts_gesture_mask_show(struct device *dev, struct device_attribute *attr, char *buf) +#else + +#ifdef EDGE_REJ +/** + * echo 01/00 > edge_rej to enable/disable edge rejection + * cat edge_rej to show the status of the edge_rej_enabled + * switch (example output in the terminal = "AA00000001BB" + * if the switch is enabled) + * + * echo 01/00 > edge_rej; cat edge_rej to enable/disable + * edge rejection and see the switch status in just one call + */ +static ssize_t fts_edge_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) { char buff[CMD_STR_LEN] = {0}; - int size = 6 * 2; - u8 *all_strbuff = NULL; - int count = 0, res; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); - if (mask[0] == 0) { - res = ERROR_OP_NOT_ALLOW; - logError(1, "%s %s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n", tag, __func__, res); + logError(0, "%s %s: edge_rej_enabled = %d\n", + tag, __func__, info->edge_rej_enabled); - } else{ - res = fts_disableInterrupt(); - if (res >= OK) { - if (mask[1] == FEAT_ENABLE) - res = enableGesture(&mask[2], mask[0]); - else{ - if (mask[1] == FEAT_DISABLE) - res = disableGesture(&mask[2], mask[0]); - else - res = ERROR_OP_NOT_ALLOW; - } - if (res < OK) { - logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", tag, res); + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->edge_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + + return count; +} + + +static ssize_t fts_edge_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_EDGE_REJECTION); + if (res < OK && temp != FEAT_DISABLE) + return -EINVAL; + + info->edge_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s: Error during fts_mode_handler! ERROR %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef CORNER_REJ +/** + * echo 01/00 > corner_rej to enable/disable corner rejection + * cat corner_rej to show the status of the + * corner_rej_enabled switch (example output in the terminal + * = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > corner_rej; cat corner_rej to enable/disable + * corner rejection and see the switch status in just one call + */ +static ssize_t fts_corner_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: corner_rej_enabled = %d\n", + tag, __func__, info->corner_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->corner_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s%s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_corner_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature + * can be enabled + * + * second step: call fts_mode_handler to + * actually enable it + * + * NOTE: Disabling a feature is always + * allowed by default + */ + res = check_feature_feasibility(info, FEAT_CORNER_REJECTION); + if (res >= OK || temp == FEAT_DISABLE) { + info->corner_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s: During fts_mode_handler!ERROR %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef EDGE_PALM_REJ +/** + * echo 01/00 > edge_palm_rej + * to enable/disable edge palm rejection + * + * cat edge_palm_rej to show the status of the + * edge_palm_rej_enabled switch (example output + * in the terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > edge_palm_rej; cat edge_palm_rej + * to enable/disable edge palm rejection and see + * the switch status in just one call + */ +static ssize_t fts_edge_palm_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: edge_palm_rej_enabled = %d\n", + tag, __func__, info->edge_palm_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", + info->edge_palm_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", + all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s%s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_edge_palm_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s%s:Number bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_EDGE_PALM_REJECTION); + if (res >= OK || temp == FEAT_DISABLE) { + info->edge_palm_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s%s:Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef CHARGER_MODE +/** + * echo 01/00 > charger_mode to enable/disable charger mode + * + * cat charger_mode to show the status of + * the charger_enabled switch (example output in the terminal + * = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > charger_mode; cat charger_mode + * to enable/disable charger mode and see the + * switch status in just one call + */ +static ssize_t fts_charger_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s:charger_enabled = %d\n", + tag, __func__, info->charger_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->charger_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_charger_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature + * can be enabled + * second step: call fts_mode_handler to + * actually enable it + * + * NOTE: Disabling a feature is always + * allowed by default + */ + res = check_feature_feasibility(info, FEAT_CHARGER); + if (res >= OK || temp == FEAT_DISABLE) { + info->charger_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error during fts_mode_handler! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } + } + + return count; +} +#endif + +#ifdef GLOVE_MODE +/** + * echo 01/00 > glove_mode + * to enable/disable glove mode + * + * cat glove_mode to show the status of + * the glove_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > glove_mode; cat glove_mode + * to enable/disable glove mode and see the + * switch status in just one call + */ +static ssize_t fts_glove_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s:glove_enabled = %d\n", + tag, __func__, info->glove_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->glove_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_glove_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_GLOVE); + if (res >= OK || temp == FEAT_DISABLE) { + info->glove_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error during fts_mode_handler! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } + } + + return count; +} +#endif + +#ifdef VR_MODE +/** + * echo 01/00 > vr_mode to enable/disable vr mode + * + * cat vr_mode to show the status of + * the vr_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > vr_mode; cat vr_mode to enable/disable + * vr mode and see the switch status in just one call + */ +static ssize_t fts_vr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: vr_enabled = %d\n", + tag, __func__, info->vr_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->vr_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_vr_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_VR); + if (res >= OK || temp == FEAT_DISABLE) { + info->vr_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef COVER_MODE +/** + * echo 01/00 > cover_mode to enable/disable cover mode + * cat cover_mode to show the status of the + * cover_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > cover_mode; cat cover_mode to + * enable/disable cover mode and see the switch + * status in just one call + * + * NOTE: the cover can be handled also using a notifier, + * in this case the body of these functions + * should be copied in the notifier callback + */ +static ssize_t fts_cover_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: cover_enabled = %d\n", + tag, __func__, info->cover_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->cover_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_cover_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_COVER); + if (res >= OK || temp == FEAT_DISABLE) { + info->cover_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s%s:Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + + return count; +} +#endif + +#ifdef STYLUS_MODE +/** + * echo 01/00 > stylus_mode to enable/disable stylus mode + * cat stylus_mode to show the status of + * the stylus_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > stylus_mode; cat stylus_mode to + * enable/disable stylus mode and see the + * switch status in just one call + */ +static ssize_t fts_stylus_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: stylus_enabled = %d\n", + tag, __func__, info->stylus_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->stylus_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_stylus_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_STYLUS); + if (res >= OK || temp == FEAT_DISABLE) { + info->stylus_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s:Error during fts_mode_handler! %08X\n", + tag, __func__, res); + } + } + + + return count; +} +#endif + +#endif + +/************** GESTURES *************/ +#ifdef PHONE_GESTURE +#ifdef USE_GESTURE_MASK + +/** + * if this define is used, a gesture bit mask + * is used as method to select the gestures + * to enable/disable + */ + +/** + * echo EE X1 X2 ... X8 > gesture_mask set + * the gesture mask to disable/enable; + * EE = 00(disable) or 01(enable); + * X1 ... X8 = gesture mask (example 06 00 ... 00 + * this gesture mask represent the gestures with ID = 1 and 2) + * can be specified from 1 to 8 bytes, if less than 8 bytes + * are specified the remaining bytes are kept as previous settings + * + * cat gesture_mask enable/disable the given mask, + * if one or more gestures is enabled the driver will + * automatically enable the gesture mode. + * If all the gestures are disabled the driver + * automatically will disable the gesture mode. + * At the end an error code will be printed + * (example output in the terminal = "AA00000000BB" + * if there are no errors) + * + * echo EE X1 X2 ... X8 > gesture_mask; + * cat gesture_mask perform in one + * command both actions stated before + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0, res, temp; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s:Call before echo enable/disable xx xx >", + tag), __func__; + logError(1, "%s %s: gesture_mask with a correct number of ", + tag, __func__); + logError(1, "parameters! ERROR %08X\n", res); + return -EINVAL; + } + + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) { + logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", + tag, res); + } + + res |= check_feature_feasibility(info, FEAT_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + + logError(1, "%s fts_gesture_mask_store:Gesture Enabled = %d\n", + tag, info->gesture_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + mask[0] = 0; + return count; +} + + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + + if ((count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, "%s %s: Number of bytes of parameter wrong! ", tag, + __func__); + logError(1, "%d > (enable/disable + %d )\n", (count + 1) / 3, + GESTURE_MASK_SIZE); + mask[0] = 0; + return -EINVAL; + } + mask[0] = ((count + 1) / 3) - 1; + for (n = 1; n <= (count + 1) / 3; n++) { + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + mask[n] = (u8)temp; + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + } + + return count; +} + +#else + +/** + * if this define is not used, + * to select the gestures to enable/disable + * are used the IDs of the gestures + * + * echo EE X1 X2 ... > gesture_mask set + * the gesture to disable/enable; EE = 00(disable) + * or 01(enable); X1 ... = gesture IDs + * (example 01 02 05... represent the gestures with + * ID = 1, 2 and 5) there is no limit of the parameters + * that can be passed, but of course the gesture IDs + * should be valid (all the valid IDs are listed + * in ftsGesture.h) + * + * cat gesture_mask enable/disable the + * given gestures, if one or more gestures is enabled + * the driver will automatically enable the gesture mode. + * If all the gestures are disabled the driver automatically + * will disable the gesture mode. At the end an error code + * will be printed (example output in the terminal = + * "AA00000000BB" if there are no errors) + * + * echo EE X1 X2 ... > gesture_mask; cat gesture_mask + * perform in one command both actions stated before + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: gesture_enabled = %d\n", tag, __func__, + info->gesture_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->gesture_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if ((count + 1) / 3 < 2 || (count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, + "%s %s:Number bytes of parameter wrong! %d %d bytes)\n", + tag, __func__, (count + 1) / 3, GESTURE_MASK_SIZE); + mask[0] = 0; + return -EINVAL; + } + + memset(mask, 0, GESTURE_MASK_SIZE + 2); + mask[0] = ((count + 1) / 3) - 1; + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + mask[1] = (u8)temp; + for (n = 1; n < (count + 1) / 3; n++) { + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + gestureIDtoGestureMask((u8)temp, &mask[2]); + } + + for (n = 0; n < GESTURE_MASK_SIZE + 2; n++) + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s: Call before echo enable/disable xx xx ....", + tag, __func__); + logError(1, " > gesture_mask with parameters! ERROR %08X\n", + res); + + } else { + + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) + logError(1, "%s %s: ERROR %08X\n", tag, __func__, res); + + } + + res = check_feature_feasibility(info, FEAT_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + res = fts_mode_handler(info, 0); + + return count; +} +#endif + +#ifdef USE_CUSTOM_GESTURES +/** + * allow to use user defined gestures + * + * echo ID X1 Y1 X2 Y2 ... X30 Y30 > + * add_custom_gesture add a custom gesture; + * ID = 1 byte that represent the gesture ID of + * the custom gesture (can be chosen only between + * the custom IDs defined in ftsGesture.h); + * X1 Y1 ... = a series of 30 points (x,y) which + * represent the gesture template. + * The loaded gesture is enabled automatically + * + * cat add_custom_gesture/remove_custom_gesture + * Print the error code of the last operation + * performed with the custom gestures + * (example output in the terminal = "AA00000000BB" + * if there are no errors) + * + * echo ID X1 Y1 X2 Y2 ... X30 Y30 > + * add_custom_gesture; cat add_custom_gesture + * perform in one command both actions stated before + */ +static ssize_t fts_custom_gesture_result_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + + logError(0, "%s %s:Last Operation Result = %08X\n", + tag, __func__, custom_gesture_res); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", custom_gesture_res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + + +static ssize_t fts_add_custom_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + u8 gestureID; + u8 gestMask[GESTURE_MASK_SIZE] = {0}; + u8 template[GESTURE_CUSTOM_POINTS]; + int res; + /*struct i2c_client *client = to_i2c_client(dev);*/ + /*struct fts_ts_info *info = i2c_get_clientdata(client);*/ + + if ((count + 1) / 3 != GESTURE_CUSTOM_POINTS + 1) { + logError(1, + "%s %s: Number bytes of parameter wrong! %d != %d\n", + tag, __func__, (count + 1) / 3, + GESTURE_CUSTOM_POINTS + 1); + res = ERROR_OP_NOT_ALLOW; + return -EINVAL; + } + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + gestureID = (u8)temp; + + for (n = 1; n < (count + 1) / 3; n++) { + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + template[n-1] = (u8)temp; + logError(1, "%s template[%d] = %02X\n", + tag, n-1, template[n-1]); + } + + res = fts_disableInterrupt(); + if (res >= OK) { + logError(1, "%s %s: Adding custom gesture ID = %02X\n", + tag, __func__, gestureID); + res = addCustomGesture(template, + GESTURE_CUSTOM_POINTS, gestureID); + if (res < OK) { + logError(1, + "%s %s:error during add custom gesture ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } else { + logError(1, + "%s %s:Enabling in the gesture mask...\n", + tag, __func__); + gestureIDtoGestureMask(gestureID, gestMask); + res = enableGesture(gestMask, GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s %s:error during enable gesture", + tag, __func__); + logError(1, " mask: ERROR %08X\n", res); + } else { + /*if(check_feature_feasibility(info,*/ + /*FEAT_GESTURE)==OK)*/ + /*info->gesture_enabled =*/ + /*isAnyGestureActive();*/ + /*uncomment if you want to activate*/ + /* automatically*/ + /*the gesture mode when a custom gesture*/ + /*is loaded*/ + logError(1, "%s %s:Custom Gesture enabled!\n", + tag, __func__, res); + } + } + } + res |= fts_enableInterrupt(); + + custom_gesture_res = res; + + return count; +} + + +/** + * echo ID > remove_custom_gesture + * remove a custom gesture; + * ID = 1 byte that represent the gesture ID + * of the custom gesture (can be chosen only + * between the custom IDs defined in ftsGesture.h); + * the same gesture is disabled automatically + */ +static ssize_t fts_remove_custom_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + u8 gestureID; + u8 gestMask[GESTURE_MASK_SIZE] = {0}; + /*struct i2c_client *client = to_i2c_client(dev);*/ + /*struct fts_ts_info *info = i2c_get_clientdata(client);*/ + + if ((count + 1) / 3 < 1) { + logError(1, + "%s %s:Number bytes of parameter wrong! %d != %d\n", + tag, __func__, (count + 1) / 3, 1); + res = ERROR_OP_NOT_ALLOW; + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + gestureID = (u8)temp; + res = fts_disableInterrupt(); + if (res >= OK) { + logError(1, + "%s %s: Removing custom gesture ID = %02X\n", + tag, __func__, gestureID); + res = removeCustomGesture(gestureID); + if (res < OK) { + logError(1, + "%s %s:error in custom gesture:%08X\n", + tag, __func__, res); + } else { + logError(1, "%s %s: Enabling in the gesture mask...\n", + tag, __func__); + gestureIDtoGestureMask(gestureID, gestMask); + res = disableGesture(gestMask, GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, + "%s %s:error in enable gesture mask:%08X\n", + tag, __func__, res); + } else { + /*if(check_feature_feasibility*/ + /*(info,FEAT_GESTURE)==OK)*/ + /*info->gesture_enabled = */ + /*isAnyGestureActive();*/ + /** + * uncomment if you want to disable + * automatically + * the gesture mode when a custom gesture is + * removed and no other gestures were enabled + */ + logError(1, "%s %s: Custom Gesture disabled!\n", + tag, __func__, res); } + } - res |= fts_enableInterrupt(); } - all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + res |= fts_enableInterrupt(); + + custom_gesture_res = res; + return count; +} +#endif + + +/** + * cat gesture_coordinates to obtain the gesture coordinates + * the string returned in the shell follow this up as follow: + * AA = start byte + * X1X2X3X4 = 4 bytes in HEX format + * which represent an error code (00000000 no error) + */ + /**** if error code is all 0s ****/ +/** + * CC = 1 byte in HEX format number of coords + * (pair of x,y) returned + * + * X1X2 Y1Y2 ... = X1X2 2 bytes in HEX format for + * x[i] and Y1Y2 2 bytes in HEX format for y[i] (MSB first) + */ +/********************************/ +/* BB = end byte*/ +static ssize_t fts_gesture_coordinates_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + //u8 coords_num; + u8 *all_strbuff = NULL; + int count = 0, res, i = 0; + + logError(0, "%s %s: Getting gestures coordinates...\n", tag, __func__); + + if (gesture_coords_reported < OK) { + logError(1, "%s %s:invalid coordinates! ERROR %08X\n", + tag, __func__, gesture_coords_reported); + res = gesture_coords_reported; + } else { + /*coords are pairs of x,y (*2) where each coord*/ + /*is a short(2bytes=4char)(*4) + 1 byte(2char) num*/ + /*of coords (+2)*/ + size += gesture_coords_reported * 2 * 4 + 2; + /*coords_num = res;*/ + res = OK; + /*set error code to OK*/ + } + + all_strbuff = kmalloc(size, GFP_KERNEL); if (all_strbuff != NULL) { - memset(all_strbuff, 0, size); + memset(all_strbuff, 0, size); - snprintf(buff, sizeof(buff), "%02X", 0xAA); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof(buff), "%08X", res); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof(buff), "%02X", 0xBB); + if (res >= OK) { + snprintf(buff, sizeof(buff), "%02X", + gesture_coords_reported); strlcat(all_strbuff, buff, size); - count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); - kfree(all_strbuff); - } else{ - logError(1, "%s fts_gesture_mask_show: Unable to allocate all_strbuff! ERROR %08X\n", tag, ERROR_ALLOC); - } + for (i = 0; i < gesture_coords_reported; i++) { + snprintf(buff, sizeof(buff), "%04X", + gesture_coordinates_x[i]); + strlcat(all_strbuff, buff, size); - mask[0] = 0; - return count; -} + snprintf(buff, sizeof(buff), "%04X", + gesture_coordinates_y[i]); + strlcat(all_strbuff, buff, size); + } + } -static ssize_t fts_gesture_mask_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - char *p = (char *)buf; - int n; - unsigned int temp; + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); - if ((count + 1) / 3 > GESTURE_MASK_SIZE+1) { - logError(1, "%s fts_gesture_mask_store: Number of bytes of parameter wrong! %d > (enable/disable + %d )\n", tag, (count + 1) / 3, GESTURE_MASK_SIZE); - mask[0] = 0; + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); } else { - mask[0] = ((count + 1) / 3) - 1; - for (n = 1; n <= (count + 1) / 3; n++) { - sscanf(p, "%02X ", &temp); - p += 3; - mask[n] = (u8)temp; - logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); - - } - + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, ERROR_ALLOC); } return count; } #endif -/************************ PRODUCTION TEST **********************************/ -static ssize_t stm_fts_cmd_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) { + + +/***************** PRODUCTION TEST ****************/ +static ssize_t stm_fts_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ int n; char *p = (char *) buf; - typeOfComand = (u32 *) kmalloc(8 * sizeof (u32), GFP_KERNEL); - if (typeOfComand == NULL) { - logError(1, "%s impossible to allocate typeOfComand!\n", tag); - return count; - } - memset(typeOfComand, 0, 8 * sizeof (u32)); + memset(typeOfComand, 0, CMD_STR_LEN * sizeof(u32)); - logError(1, "%s\n", tag); + logError(1, "%s\n", tag); for (n = 0; n < (count + 1) / 3; n++) { - sscanf(p, "%02X ", &typeOfComand[n]); + + if (sscanf(p, "%02X ", &typeOfComand[n]) != 1) + return -EINVAL; p += 3; - logError(1, "%s typeOfComand[%d] = %02X\n", tag, n, typeOfComand[n]); + logError(1, "%s typeOfComand[%d] = %02X\n", + tag, n, typeOfComand[n]); } numberParameters = n; - logError(1, "%s Number of Parameters = %d\n", tag, numberParameters); + logError(1, "%s Number of Parameters = %d\n", tag, numberParameters); return count; } -static ssize_t stm_fts_cmd_show(struct device *dev, struct device_attribute *attr, - char *buf) { +static ssize_t stm_fts_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ char buff[CMD_STR_LEN] = {0}; int res, j, doClean = 0, count; @@ -415,15 +2157,16 @@ static ssize_t stm_fts_cmd_show(struct device *dev, struct device_attribute *att struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); - MutualSenseData compData; - SelfSenseData comData; - MutualSenseFrame frameMS; - SelfSenseFrame frameSS; + struct MutualSenseData compData; + struct SelfSenseData comData; + struct MutualSenseFrame frameMS; + struct SelfSenseFrame frameSS; - /* struct used for defining which test - *perform during the production test + /** + * struct used for defining which test + * perform during the production test */ - TestToDo todoDefault; + struct TestToDo todoDefault; todoDefault.MutualRaw = 1; todoDefault.MutualRawGap = 1; @@ -464,191 +2207,281 @@ static ssize_t stm_fts_cmd_show(struct device *dev, struct device_attribute *att todoDefault.SelfSenseCxTotal = 0; todoDefault.SelfSenseCxTotalAdj = 0; - if (numberParameters >= 1 && typeOfComand != NULL) { + + if (numberParameters >= 1) { res = fts_disableInterrupt(); if (res < 0) { - logError(0, "%s fts_disableInterrupt: ERROR %08X\n", tag, res); + logError(0, "%s fts_disableInterrupt: ERROR %08X\n", + tag, res); res = (res | ERROR_DISABLE_INTER); goto END; } +#if defined(CONFIG_FB_MSM) res = fb_unregister_client(&info->notifier); +#else + res = msm_drm_unregister_client(&info->notifier); +#endif if (res < 0) { - logError(1, "%s ERROR: unregister notifier failed!\n", tag); - goto END; + logError(1, "%s ERROR: unregister notifier failed!\n", + tag); + goto END; } switch (typeOfComand[0]) { - /*ITO TEST*/ + /*ITO TEST*/ case 0x01: res = production_test_ito(); break; - /*PRODUCTION TEST*/ + /*PRODUCTION TEST*/ case 0x00: if (ftsInfo.u32_mpPassFlag != INIT_MP) { - logError(0, "%s MP Flag not set!\n", tag, res); - res = production_test_main(LIMITS_FILE, 1, 1, &todoDefault, INIT_MP); - } else{ - logError(0, "%s MP Flag set!\n", tag, res); - res = production_test_main(LIMITS_FILE, 1, 0, &todoDefault, INIT_MP); - } - break; - /*read mutual raw*/ + logError(0, "%s MP Flag not set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 1, + &todoDefault, INIT_MP); + } else { + logError(0, "%s MP Flag set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 0, + &todoDefault, INIT_MP); + } + break; + /*read mutual raw*/ case 0x13: logError(0, "%s Get 1 MS Frame\n", tag); - /* res = getMSFrame(ADDR_RAW_TOUCH, &frame, 0); */ + //res = getMSFrame(ADDR_RAW_TOUCH, &frame, 0); res = getMSFrame2(MS_TOUCH_ACTIVE, &frameMS); if (res < 0) { - logError(0, "%s Error while taking the MS frame... ERROR %02X\n", tag, res); + logError(0, + "%s Error in taking MS frame.%02X\n", + tag, res); } else { - logError(0, "%s The frame size is %d words\n", tag, res); - size = (res * sizeof (short) + 8)*2; - /* set res to OK because if getMSFrame is - * successful res = number of words read - */ + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2; + /* set res to OK because if getMSFrame is*/ + /* successful res = number of words read*/ res = OK; + print_frame_short("MS frame =", + array1dTo2d_short(frameMS.node_data, + frameMS.node_data_size, + frameMS.header.sense_node), + frameMS.header.force_node, + frameMS.header.sense_node); } - break; - /*read self raw*/ + break; + /*read self raw*/ case 0x15: logError(0, "%s Get 1 SS Frame\n", tag); res = getSSFrame2(SS_TOUCH, &frameSS); if (res < OK) { - logError(0, "%s Error while taking the SS frame... ERROR %02X\n", tag, res); + logError(0, + "%s Error while taking the SS frame%02X\n", + tag, res); } else { - logError(0, "%s The frame size is %d words\n", tag, res); - size = (res * sizeof (short) + 8)*2+1; - /* set res to OK because if getMSFrame is + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2 + 1; + /** + * set res to OK because if getMSFrame is * successful res = number of words read */ res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short(frameSS.force_data, + frameSS.header.force_node, 1), + frameSS.header.force_node, 1); + print_frame_short("SS sense frame =", + array1dTo2d_short(frameSS.sense_data, + frameSS.header.sense_node, + frameSS.header.sense_node), + 1, + frameSS.header.sense_node); } - break; - case 0x14: /*read mutual comp data */ + /*read mutual comp data*/ + case 0x14: logError(0, "%s Get MS Compensation Data\n", tag); - res = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &compData); + res = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, + &compData); if (res < 0) { - logError(0, "%s Error reading MS compensation data ERROR %02X\n", tag, res); + logError(0, + "%s Error MS compensation data%02X\n", + tag, res); } else { - logError(0, "%s MS Compensation Data Reading Finished!\n", tag); - size = ((compData.node_data_size + 9) * sizeof (u8))*2; + logError(0, + "%s MS Data Reading Finished!\n", + tag); + size = ((compData.node_data_size + 9) * + sizeof(u8)) * 2; + print_frame_u8("MS Data (Cx2) =", + array1dTo2d_u8(compData.node_data, + compData.node_data_size, + compData.header.sense_node), + compData.header.force_node, + compData.header.sense_node); } break; - case 0x16: /* read self comp data */ + /*read self comp data*/ + case 0x16: logError(0, "%s Get SS Compensation Data...\n", tag); res = readSelfSenseCompensationData(SS_TOUCH, &comData); if (res < 0) { - logError(0, "%s Error reading SS compensation data ERROR %02X\n", tag, res); + logError(0, "%s Error reading SS data%02X\n", + tag, res); } else { - logError(0, "%s SS Compensation Data Reading Finished!\n", tag); - size = ((comData.header.force_node + comData.header.sense_node)*2 + 12) * sizeof (u8)*2; + logError(0, "%s SS Data Reading Finished!\n", + tag); + size = ((comData.header.force_node + + comData.header.sense_node) * 2 + 12); + size *= sizeof(u8) * 2; + print_frame_u8("SS Data Ix2_fm = ", + array1dTo2d_u8(comData.ix2_fm, + comData.header.force_node, 1), + comData.header.force_node, + 1); + print_frame_u8("SS Data Cx2_fm = ", + array1dTo2d_u8(comData.cx2_fm, + comData.header.force_node, 1), + comData.header.force_node, + 1); + print_frame_u8("SS Data Ix2_sn = ", + array1dTo2d_u8(comData.ix2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", + array1dTo2d_u8(comData.cx2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); } break; - case 0x03: /* MS Raw DATA TEST */ + /* MS Raw DATA TEST */ + case 0x03: res = fts_system_reset(); if (res >= OK) - res = production_test_ms_raw(LIMITS_FILE, 1, &todoDefault); + res = production_test_ms_raw(LIMITS_FILE, + 1, &todoDefault); break; - - case 0x04: /* MS CX DATA TEST */ + /* MS CX DATA TEST */ + case 0x04: res = fts_system_reset(); if (res >= OK) - res = production_test_ms_cx(LIMITS_FILE, 1, &todoDefault); + res = production_test_ms_cx(LIMITS_FILE, + 1, &todoDefault); break; - - case 0x05: /* SS RAW DATA TEST */ + /* SS RAW DATA TEST */ + case 0x05: res = fts_system_reset(); if (res >= OK) - res = production_test_ss_raw(LIMITS_FILE, 1, &todoDefault); + res = production_test_ss_raw(LIMITS_FILE, + 1, &todoDefault); break; - - case 0x06: /* SS IX CX DATA TEST */ + /* SS IX CX DATA TEST */ + case 0x06: res = fts_system_reset(); if (res >= OK) - res = production_test_ss_ix_cx(LIMITS_FILE, 1, &todoDefault); + res = production_test_ss_ix_cx(LIMITS_FILE, + 1, &todoDefault); break; case 0xF0: - case 0xF1: /* TOUCH ENABLE/DISABLE */ - doClean = (int) (typeOfComand[0]&0x01); + /* TOUCH ENABLE/DISABLE */ + case 0xF1: + doClean = (int) (typeOfComand[0] & 0x01); res = cleanUp(doClean); - break; default: - logError(1, "%s COMMAND NOT VALID!! Insert a proper value ...\n", tag); + logError(1, + "%s COMMAND NOT VALID!! Insert a proper value\n", + tag); res = ERROR_OP_NOT_ALLOW; break; } doClean = fts_enableInterrupt(); if (doClean < 0) { - logError(0, "%s fts_enableInterrupt: ERROR %08X\n", tag, (doClean|ERROR_ENABLE_INTER)); + logError(0, "%s fts_enableInterrupt: ERROR %08X\n", + tag, (doClean|ERROR_ENABLE_INTER)); } } else { - logError(1, "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", tag); + logError(1, "%s NO COMMAND SPECIFIED!!!\n", tag); res = ERROR_OP_NOT_ALLOW; - typeOfComand = NULL; - } - if (fb_register_client(&info->notifier) < 0) { - logError(1, "%s ERROR: register notifier failed!\n", tag); - } +#if defined(CONFIG_FB_MSM) + if (fb_register_client(&info->notifier) < 0) + logError(1, "%s ERROR: register notifier failed!\n", tag); +#else + if (msm_drm_register_client(&info->notifier) < 0) + logError(1, "%s ERROR: register notifier failed!\n", tag); +#endif -END: /* here start the reporting phase, assembling the data to send in the file node */ - all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); +END: + /*here start the reporting phase,*/ + /* assembling the data to send in the file node */ + all_strbuff = kmalloc(size, GFP_KERNEL); memset(all_strbuff, 0, size); snprintf(buff, sizeof(buff), "%02X", 0xAA); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 2); snprintf(buff, sizeof(buff), "%08X", res); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 8); if (res >= OK) { /*all the other cases are already fine printing only the res.*/ switch (typeOfComand[0]) { case 0x13: - snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.force_node); - strlcat(all_strbuff, buff, size); - - snprintf(buff, sizeof(buff), "%02X", (u8) frameMS.header.sense_node); - strlcat(all_strbuff, buff, size); - - for (j = 0; j < frameMS.node_data_size; j++) { - snprintf(buff, sizeof(buff), "%04X", frameMS.node_data[j]); - strlcat(all_strbuff, buff, size); - } + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.sense_node); + strlcat(all_strbuff, buff, 2); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameMS.node_data[j]); + strlcat(all_strbuff, buff, 4); + } - kfree(frameMS.node_data); - break; + kfree(frameMS.node_data); + break; case 0x15: - snprintf(buff, sizeof(buff), "%02X", (u8) frameSS.header.force_node); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, 2); - snprintf(buff, sizeof(buff), "%02X", (u8) frameSS.header.sense_node); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.sense_node); + strlcat(all_strbuff, buff, 2); /* Copying self raw data Force */ for (j = 0; j < frameSS.header.force_node; j++) { - snprintf(buff, sizeof(buff), "%04X", frameSS.force_data[j]); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%04X", + frameSS.force_data[j]); + strlcat(all_strbuff, buff, 4); } + /* Copying self raw data Sense */ for (j = 0; j < frameSS.header.sense_node; j++) { - snprintf(buff, sizeof(buff), "%04X", frameSS.sense_data[j]); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%04X", + frameSS.sense_data[j]); + strlcat(all_strbuff, buff, 4); } kfree(frameSS.force_data); @@ -656,66 +2489,75 @@ static ssize_t stm_fts_cmd_show(struct device *dev, struct device_attribute *att break; case 0x14: - snprintf(buff, sizeof(buff), "%02X", (u8) compData.header.force_node); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + (u8) compData.header.force_node); + strlcat(all_strbuff, buff, 2); - snprintf(buff, sizeof(buff), "%02X", (u8) compData.header.sense_node); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + (u8) compData.header.sense_node); + strlcat(all_strbuff, buff, 2); /* Cpying CX1 value */ snprintf(buff, sizeof(buff), "%02X", compData.cx1); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 2); /* Copying CX2 values */ for (j = 0; j < compData.node_data_size; j++) { - snprintf(buff, sizeof(buff), "%02X", *(compData.node_data + j)); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + *(compData.node_data + j)); + strlcat(all_strbuff, buff, 2); } kfree(compData.node_data); break; case 0x16: - snprintf(buff, sizeof(buff), "%02X", comData.header.force_node); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + comData.header.force_node); + strlcat(all_strbuff, buff, 2); - snprintf(buff, sizeof(buff), "%02X", comData.header.sense_node); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + comData.header.sense_node); + strlcat(all_strbuff, buff, 2); snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 2); snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 2); snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 2); snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 2); /* Copying IX2 Force */ for (j = 0; j < comData.header.force_node; j++) { - snprintf(buff, sizeof(buff), "%02X", comData.ix2_fm[j]); + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_fm[j]); strlcat(all_strbuff, buff, size); } /* Copying IX2 Sense */ for (j = 0; j < comData.header.sense_node; j++) { - snprintf(buff, sizeof(buff), "%02X", comData.ix2_sn[j]); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_sn[j]); + strlcat(all_strbuff, buff, 2); } /* Copying CX2 Force */ for (j = 0; j < comData.header.force_node; j++) { - snprintf(buff, sizeof(buff), "%02X", comData.cx2_fm[j]); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_fm[j]); + strlcat(all_strbuff, buff, 2); } - /* Copying CX2 Sense */ + /* Copying CX2 Sense */ for (j = 0; j < comData.header.sense_node; j++) { - snprintf(buff, sizeof(buff), "%02X", comData.cx2_sn[j]); - strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_sn[j]); + strlcat(all_strbuff, buff, 2); } kfree(comData.ix2_fm); @@ -726,45 +2568,145 @@ static ssize_t stm_fts_cmd_show(struct device *dev, struct device_attribute *att default: break; - } } snprintf(buff, sizeof(buff), "%02X", 0xBB); - strlcat(all_strbuff, buff, size); + strlcat(all_strbuff, buff, 2); count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); - numberParameters = 0; /* need to reset the number of parameters - * in order to wait the next command, comment - *if you want to repeat the last command sent - *just doing a cat - */ - /* logError(0,"%s numberParameters = %d\n", tag, numberParameters); */ + /** + * need to reset the number of parameters + * in order to wait the next command, + * comment if you want to repeat + * the last command sent just doing a cat + */ + numberParameters = 0; + /* logError(0,"%s numberParameters = %d\n",tag, numberParameters);*/ kfree(all_strbuff); - kfree(typeOfComand); return count; - } -static DEVICE_ATTR(fwupdate, (S_IRUGO | S_IWUSR | S_IWGRP), fts_sysfs_fwupdate_show, fts_fw_control_store); -static DEVICE_ATTR(appid, (S_IRUGO), fts_sysfs_config_id_show, NULL); -static DEVICE_ATTR(fw_file_test, (S_IRUGO), fts_fw_test_show, NULL); -static DEVICE_ATTR(stm_fts_cmd, (S_IRUGO | S_IWUSR | S_IWGRP), stm_fts_cmd_show, stm_fts_cmd_store); -static DEVICE_ATTR(feature_enable, (S_IRUGO | S_IWUSR | S_IWGRP), fts_feature_enable_show, fts_feature_enable_store); +static DEVICE_ATTR(fwupdate, 0664, + fts_fwupdate_show, fts_fwupdate_store); +static DEVICE_ATTR(appid, 0444, fts_sysfs_config_id_show, NULL); +static DEVICE_ATTR(mode_active, 0444, fts_mode_active_show, NULL); +static DEVICE_ATTR(lockdown_info, 0444, fts_lockdown_info_show, NULL); +static DEVICE_ATTR(strength_frame, 0664, + fts_strength_frame_show, fts_strength_frame_store); +static DEVICE_ATTR(fw_file_test, 0444, fts_fw_test_show, NULL); +static DEVICE_ATTR(stm_fts_cmd, 0664, + stm_fts_cmd_show, stm_fts_cmd_store); +#ifdef USE_ONE_FILE_NODE +static DEVICE_ATTR(feature_enable, 0664, + fts_feature_enable_show, fts_feature_enable_store); +#else + +#ifdef EDGE_REJ +static DEVICE_ATTR(edge_rej, 0664, + fts_edge_rej_show, fts_edge_rej_store); +#endif + +#ifdef CORNER_REJ +static DEVICE_ATTR(corner_rej, 0664, + fts_corner_rej_show, fts_corner_rej_store); +#endif + +#ifdef EDGE_PALM_REJ +static DEVICE_ATTR(edge_palm_rej, 0664, + fts_edge_palm_rej_show, fts_edge_palm_rej_store); +#endif + +#ifdef CHARGER_MODE +static DEVICE_ATTR(charger_mode, 0664, + fts_charger_mode_show, fts_charger_mode_store); +#endif + +#ifdef GLOVE_MODE +static DEVICE_ATTR(glove_mode, 0664, + fts_glove_mode_show, fts_glove_mode_store); +#endif + +#ifdef VR_MODE +static DEVICE_ATTR(vr_mode, 0664, + fts_vr_mode_show, fts_vr_mode_store); +#endif + +#ifdef COVER_MODE +static DEVICE_ATTR(cover_mode, 0664, + fts_cover_mode_show, fts_cover_mode_store); +#endif + +#ifdef STYLUS_MODE +static DEVICE_ATTR(stylus_mode, 0664, + fts_stylus_mode_show, fts_stylus_mode_store); +#endif + +#endif + #ifdef PHONE_GESTURE -static DEVICE_ATTR(gesture_mask, (S_IRUGO | S_IWUSR | S_IWGRP), fts_gesture_mask_show, fts_gesture_mask_store); +static DEVICE_ATTR(gesture_mask, 0664, + fts_gesture_mask_show, fts_gesture_mask_store); +static DEVICE_ATTR(gesture_coordinates, 0664, + fts_gesture_coordinates_show, NULL); +#ifdef USE_CUSTOM_GESTURES +static DEVICE_ATTR(add_custom_gesture, 0664, + fts_custom_gesture_result_show, fts_add_custom_gesture_store); +static DEVICE_ATTR(remove_custom_gesture, 0664, + fts_custom_gesture_result_show, + fts_remove_custom_gesture_store); +#endif #endif -/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ + +/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ static struct attribute *fts_attr_group[] = { &dev_attr_fwupdate.attr, &dev_attr_appid.attr, + &dev_attr_mode_active.attr, + &dev_attr_lockdown_info.attr, + &dev_attr_strength_frame.attr, &dev_attr_fw_file_test.attr, - /* &dev_attr_touch_debug.attr, */ &dev_attr_stm_fts_cmd.attr, +#ifdef USE_ONE_FILE_NODE &dev_attr_feature_enable.attr, +#else + +#ifdef EDGE_REJ + &dev_attr_edge_rej.attr, +#endif +#ifdef CORNER_REJ + &dev_attr_corner_rej.attr, +#endif +#ifdef EDGE_PALM_REJ + &dev_attr_edge_palm_rej.attr, +#endif +#ifdef CHARGER_MODE + &dev_attr_charger_mode.attr, +#endif +#ifdef GLOVE_MODE + &dev_attr_glove_mode.attr, +#endif +#ifdef VR_MODE + &dev_attr_vr_mode.attr, +#endif +#ifdef COVER_MODE + &dev_attr_cover_mode.attr, +#endif +#ifdef STYLUS_MODE + &dev_attr_stylus_mode.attr, +#endif + +#endif + #ifdef PHONE_GESTURE &dev_attr_gesture_mask.attr, + &dev_attr_gesture_coordinates.attr, +#ifdef USE_CUSTOM_GESTURES + &dev_attr_add_custom_gesture.attr, + &dev_attr_remove_custom_gesture.attr, +#endif + #endif NULL, }; @@ -776,13 +2718,14 @@ static int fts_command(struct fts_ts_info *info, unsigned char cmd) regAdd = cmd; - ret = fts_writeCmd(®Add, sizeof (regAdd)); /* 0 = ok */ + ret = fts_writeCmd(®Add, sizeof(regAdd)); /* 0 = ok */ logError(0, "%s Issued command 0x%02x, return value %08X\n", cmd, ret); return ret; } + void fts_input_report_key(struct fts_ts_info *info, int key_code) { mutex_lock(&info->input_report_mutex); @@ -793,10 +2736,10 @@ void fts_input_report_key(struct fts_ts_info *info, int key_code) mutex_unlock(&info->input_report_mutex); } + /* * New Interrupt handle implementation */ - static inline unsigned char *fts_next_event(unsigned char *evt) { /* Nothing to do with this event, moving to the next one */ @@ -807,25 +2750,38 @@ static inline unsigned char *fts_next_event(unsigned char *evt) } /* EventId : 0x00 */ -static unsigned char *fts_nop_event_handler(struct fts_ts_info *info, - unsigned char *event) { - /* logError(1, "%s %s Doing nothing for event = %02X %02X %02X %02X %02X %02X %02X %02X\n", - * tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); - */ - return fts_next_event(event); +static void fts_nop_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + /** + * logError(1, + * "%s %s Doing nothing for event = + * %02X %02X %02X %02X %02X %02X %02X %02X\n", + * tag, __func__, event[0], event[1], event[2], + * event[3], event[4], event[5], event[6], event[7]); + */ + /* return fts_next_event(event); */ } /* EventId : 0x03 */ -static unsigned char *fts_enter_pointer_event_handler(struct fts_ts_info *info, - unsigned char *event) { +static void fts_enter_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ unsigned char touchId, touchcount; int x, y, z; + int minor; + int major, distance; + u8 touchsize; + distance = 0; if (!info->resume_bit) goto no_report; touchId = event[1] & 0x0F; touchcount = (event[1] & 0xF0) >> 4; + touchsize = (event[5] & 0xC0) >> 6; + major = (event[5] & 0x1F); // bit0-bit4: major + minor = event[6]; // event6:minor __set_bit(touchId, &info->touch_id); @@ -840,46 +2796,92 @@ static unsigned char *fts_enter_pointer_event_handler(struct fts_ts_info *info, y--; input_mt_slot(info->input_dev, touchId); +/*#ifdef STYLUS_MODE*/ + /** + * TODO: check with ST how FW report a + * stylus touch in the touch event, + * this is an example code + */ + /*if (info->stylus_enabled == 1 && touchsize == STYLUS_SIZE) {*/ + /*__set_bit(touchId, &info->stylus_id);*/ + /*input_mt_report_slot_state(info->input_dev, MT_TOOL_PEN, 1);*/ + /*logError(0, "%s %s : It is a stylus!\n",tag,__func__); */ + /*} else*/ + /*input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1);*/ +/*#else*/ + /*input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1);*/ +/*#endif*/ input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1); - logError(0, "%s %s : TouchID = %d,Touchcount = %d\n", tag, __func__, touchId, touchcount); - if (touchcount == 1) { - input_report_key(info->input_dev, BTN_TOUCH, 1); - input_report_key(info->input_dev, BTN_TOOL_FINGER, 1); - } - /* input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, touchId); */ + /*logError(0,*/ + /*"%s %s:TouchID = %d, Touchcount = %d, minor:%d, major:%d\n",*/ + /*tag, __func__, touchId, touchcount, minor, major);*/ + /*logError(0,*/ + /*"%s %s : TouchID = %d,Touchcount = %d\n",*/ + /*tag, __func__, touchId,touchcount);*/ + /*if (touchcount == 1) {*/ + input_report_key(info->input_dev, BTN_TOUCH, 1); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 1); + /*}*/ + /* input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, touchId);*/ input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); - input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, z); - input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, z); - input_report_abs(info->input_dev, ABS_MT_PRESSURE, z); - logError(0, "%s %s : Event 0x%02x - ID[%d], (x, y, z) = (%3d, %3d, %3d)\n", tag, __func__, *event, touchId, x, y, z); + /*input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, z);*/ + /*input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, z);*/ + /*input_report_abs(info->input_dev, ABS_MT_PRESSURE, z);*/ + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, major); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, minor); + input_report_abs(info->input_dev, ABS_MT_DISTANCE, distance); + /*logError(0,*/ + /*"%s%s:Event 0x%02x - ID[%d], (x, y, z) = (%3d, %3d, %3d)\n",*/ + /*tag, __func__, *event, touchId, x, y, z);*/ no_report: - return fts_next_event(event); + return; + /* return fts_next_event(event); */ } /* EventId : 0x04 */ -static unsigned char *fts_leave_pointer_event_handler(struct fts_ts_info *info, - unsigned char *event) { +static void fts_leave_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ unsigned char touchId, touchcount; + u8 touchsize; touchId = event[1] & 0x0F; touchcount = (event[1] & 0xF0) >> 4; + touchsize = (event[5] & 0xC0) >> 6; __clear_bit(touchId, &info->touch_id); input_mt_slot(info->input_dev, touchId); +/*#ifdef STYLUS_MODE*/ + /** + * TODO: check with ST how FW report a stylus touch + * in the touch event, this is an example code + */ + /*if (info->stylus_enabled == 1 && touchsize == STYLUS_SIZE) {*/ + /* __clear_bit(touchId, &info->stylus_id);*/ + /* input_mt_report_slot_state(info->input_dev, MT_TOOL_PEN, 0);*/ + /*logError(0, "%s %s : It is a stylus!\n",tag,__func__);*/ + /*} else*/ + /*input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0);*/ +/*#else*/ + /*input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0);*/ +/*#endif*/ input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0); - logError(0, "%s %s : TouchID = %d, Touchcount = %d\n", tag, __func__, touchId, touchcount); - if (touchcount == 0) { - input_report_key(info->input_dev, BTN_TOUCH, 0); - input_report_key(info->input_dev, BTN_TOOL_FINGER, 0); - } + /* input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0);*/ + /*logError(0, "%s %s : TouchID = %d, Touchcount = %d\n",*/ + /*tag,__func__,touchId,touchcount);*/ + /*if (touchcount == 0) {*/ + input_report_key(info->input_dev, BTN_TOUCH, 0); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 0); + /*}*/ input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); - logError(0, "%s %s : Event 0x%02x - release ID[%d]\n", tag, __func__, event[0], touchId); + /*logError(0, "%s %s : Event 0x%02x - release ID[%d]\n",*/ + /*tag, __func__, event[0], touchId);*/ - return fts_next_event(event); + /*return fts_next_event(event);*/ } /* EventId : 0x05 */ @@ -887,16 +2889,26 @@ static unsigned char *fts_leave_pointer_event_handler(struct fts_ts_info *info, #ifdef PHONE_KEY /* EventId : 0x0E */ -static unsigned char *fts_key_status_event_handler(struct fts_ts_info *info, unsigned char *event) +static void fts_key_status_event_handler(struct fts_ts_info *info, + unsigned char *event) { int value; - logError(0, "%s %s Received event %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); - /* TODO: the customer should handle the events coming from the keys according his needs (this is an example that report only the single pressure of one key at time) */ - if (event[2] != 0) { /* event[2] contain the bitmask of the keys that are actually pressed */ + + logError(0, + "%s %sReceived event %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + /** + * TODO: the customer should handle the events coming + * from the keys according his needs (this is an example + * that report only the single pressure of one key at time) + */ + /* event[2] contain the bitmask of the keys that are actually pressed */ + if (event[2] != 0) { switch (event[2]) { case KEY1: value = KEY_HOMEPAGE; - logError(0, "%s %s: Button HOME !\n", tag, __func__); + logError(0, "%s %s: Button HOME!\n", tag, __func__); break; case KEY2: @@ -910,88 +2922,93 @@ static unsigned char *fts_key_status_event_handler(struct fts_ts_info *info, uns break; default: - logError(0, "%s %s: No valid Button ID or more than one key pressed!\n", tag, __func__); - goto done; + logError(0, + "%s %s:No valid Button ID or more than one key pressed!\n", + tag, __func__); + //goto done; + return; } - fts_input_report_key(info, value); - } else{ + fts_input_report_key(info, value); + } else { logError(0, "%s %s: All buttons released!\n", tag, __func__); } -done: - return fts_next_event(event); +//done: + /* return fts_next_event(event); */ + //return; } #endif /* EventId : 0x0F */ -static unsigned char *fts_error_event_handler(struct fts_ts_info *info, - unsigned char *event) { - int error = 0, i = 0; - logError(0, "%s %s Received event 0x%02x 0x%02x\n", tag, __func__, event[0], event[1]); +static void fts_error_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int error = 0; + + logError(0, + "%s %sReceived event:%02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); switch (event[1]) { - case EVENT_TYPE_ESD_ERROR: - { - for (i = 0; i < TOUCH_ID_MAX; i++) { - input_mt_slot(info->input_dev, i); - input_mt_report_slot_state(info->input_dev, (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); - } - input_sync(info->input_dev); + case EVENT_TYPE_ESD_ERROR: /* esd */ + /* before reset clear all slot */ + release_all_touches(info); fts_chip_powercycle(info); - error = fts_system_reset(); - error |= fts_mode_handler(info, 0); - error |= fts_enableInterrupt(); + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); if (error < OK) { - logError(1, "%s %s Cannot restore the device ERROR %08X\n", tag, __func__, error); + logError(1, + "%s %s Cannot restore the device ERROR %08X\n", + tag, __func__, error); } - } - break; - case EVENT_TYPE_WATCHDOG_ERROR: /* watch dog timer */ - { - if (event[2] == 0) { - /* before reset clear all slot */ - for (i = 0; i < TOUCH_ID_MAX; i++) { - input_mt_slot(info->input_dev, i); - input_mt_report_slot_state(info->input_dev, - (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); - } - input_sync(info->input_dev); - error = fts_system_reset(); - error |= fts_mode_handler(info, 0); - error |= fts_enableInterrupt(); - if (error < OK) { - logError(1, "%s %s Cannot reset the device ERROR %08X\n", tag, __func__, error); - } + break; + case EVENT_TYPE_WATCHDOG_ERROR: /*watch dog timer*/ + /*if (event[2] == 0) { */ + dumpErrorInfo(); + /*before reset clear all slot */ + release_all_touches(info); + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, + "%s %s Cannot reset the device ERROR %08X\n", + tag, __func__, error); } - } - break; - - } - return fts_next_event(event); + /* } */ + break; +} + /* return fts_next_event(event); */ } /* EventId : 0x10 */ -static unsigned char *fts_controller_ready_event_handler( - struct fts_ts_info *info, unsigned char *event) { +static void fts_controller_ready_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ int error; + logError(0, "%s %s Received event 0x%02x\n", tag, __func__, event[0]); - info->touch_id = 0; - input_sync(info->input_dev); + release_all_touches(info); setSystemResettedUp(1); setSystemResettedDown(1); error = fts_mode_handler(info, 0); if (error < OK) { - logError(1, "%s %s Cannot restore the device status ERROR %08X\n", tag, __func__, error); + logError(1, + "%s %s Cannot restore the device status ERROR %08X\n", + tag, __func__, error); } - return fts_next_event(event); + /* return fts_next_event(event); */ } /* EventId : 0x16 */ -static unsigned char *fts_status_event_handler( - struct fts_ts_info *info, unsigned char *event) { - /* logError(1, "%s Received event 0x%02x\n", tag, event[0]); */ +static void fts_status_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + /*logError(1, "%s Received event 0x%02x\n", tag, event[0]);*/ switch (event[1]) { case EVENT_TYPE_MS_TUNING_CMPL: @@ -1003,61 +3020,77 @@ static unsigned char *fts_status_event_handler( case FTS_WATER_MODE_ON: case FTS_WATER_MODE_OFF: default: - logError(0, - "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n", - tag, __func__, event[0], event[1], event[2], - event[3], event[4], event[5], event[6], event[7]); - break; + logError(1, "%s %s Received unhandled status event = ", + tag, __func__); + logError(1, "%02X %02X %02X %02X %02X %02X %02X %02X\n", + event[0], event[1], event[2], event[3], event[4], + event[5], event[6], event[7]); + break; } - return fts_next_event(event); + /* return fts_next_event(event); */ } #ifdef PHONE_GESTURE -static unsigned char *fts_gesture_event_handler(struct fts_ts_info *info, unsigned char *event) +/** + * TODO: Customer should implement their own action + * in respons of a gesture event. + * This is an example that simply print the gesture received + */ +static void fts_gesture_event_handler(struct fts_ts_info *info, + unsigned char *event) { unsigned char touchId; int value; + int needCoords = 0; - logError(0, "%s gesture event data: %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + logError(0, + "%s gesture event: %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); if (event[1] == 0x03) { - - logError(1, "%s %s: Gesture ID %02X enable_status = %02X\n", tag, __func__, event[2], event[3]); - + logError(1, "%s %s: Gesture ID %02X enable_status = %02X\n", + tag, __func__, event[2], event[3]); } + if (event[1] == EVENT_TYPE_ENB && event[2] == 0x00) { switch (event[3]) { case GESTURE_ENABLE: - logError(1, "%s %s: Gesture Enabled! res = %02X\n", tag, __func__, event[4]); + logError(1, "%s %s: Gesture Enabled! res = %02X\n", + tag, __func__, event[4]); break; case GESTURE_DISABLE: - logError(1, "%s %s: Gesture Disabled! res = %02X\n", tag, __func__, event[4]); + logError(1, "%s %s: Gesture Disabled! res = %02X\n", + tag, __func__, event[4]); break; default: - logError(1, "%s %s: Event not Valid!\n", tag, __func__); - + logError(1, "%s %s: Event not Valid!\n", tag, __func__); } - } - /* always use touchId zero */ - touchId = 0; - __set_bit(touchId, &info->touch_id); + if (event[0] == EVENTID_GESTURE && (event[1] == EVENT_TYPE_GESTURE_DTC1 + || event[1] == EVENT_TYPE_GESTURE_DTC2)) { + /* always use touchId zero */ + touchId = 0; + __set_bit(touchId, &info->touch_id); - if (event[0] == EVENTID_GESTURE && (event[1] == EVENT_TYPE_GESTURE_DTC1 || event[1] == EVENT_TYPE_GESTURE_DTC2)) { + /* by default read the coordinates*/ + /* for all gestures excluding double tap */ + needCoords = 1; switch (event[2]) { case GES_ID_DBLTAP: value = KEY_WAKEUP; - logError(0, "%s %s: double tap !\n", tag, __func__); + logError(0, "%s %s: double tap!\n", tag, __func__); + needCoords = 0; break; case GES_ID_AT: value = KEY_WWW; - logError(0, "%s %s: @ !\n", tag, __func__); + logError(0, "%s %s: @!\n", tag, __func__); break; case GES_ID_C: @@ -1068,7 +3101,7 @@ static unsigned char *fts_gesture_event_handler(struct fts_ts_info *info, unsign case GES_ID_E: value = KEY_E; logError(0, "%s %s: e !\n", tag, __func__); - break; + break; case GES_ID_F: value = KEY_F; @@ -1165,206 +3198,84 @@ static unsigned char *fts_gesture_event_handler(struct fts_ts_info *info, unsign logError(0, "%s %s: > !\n", tag, __func__); break; default: - logError(0, "%s %s: No valid GestureID!\n", tag, __func__); + logError(0, "%s %s: No valid GestureID!\n", + tag, __func__); goto gesture_done; - - } - - fts_input_report_key(info, value); - - gesture_done: - /* Done with gesture event, clear bit. */ - __clear_bit(touchId, &info->touch_id); - } - - return fts_next_event(event); -} -#endif - -/* EventId : 0x05 */ -#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler - -/* - * This handler is called each time there is at least - * one new event in the FIFO - */ -static void fts_event_handler(struct work_struct *work) -{ - struct fts_ts_info *info; - int error, error1; - int left_events; - unsigned char regAdd; - unsigned char data[FIFO_EVENT_SIZE * (FIFO_DEPTH)] = {0}; - unsigned char *event = NULL; - unsigned char eventId; - event_dispatch_handler_t event_handler; - - info = container_of(work, struct fts_ts_info, work); - /* - * to avoid reading all FIFO, we read the first event and - * then check how many events left in the FIFO - */ - - - regAdd = FIFO_CMD_READONE; - error = fts_readCmd(®Add, - sizeof (regAdd), data, FIFO_EVENT_SIZE); - - if (!error) { - - left_events = data[7] & 0x1F; - if ((left_events > 0) && (left_events < FIFO_DEPTH)) { - /* - * Read remaining events. - */ - regAdd = FIFO_CMD_READALL; - - error1 = fts_readCmd(®Add, sizeof (regAdd), - &data[FIFO_EVENT_SIZE], - left_events * FIFO_EVENT_SIZE); - /* - * Got an error reading remaining events, - * process at least * the first one that was - * reading fine. - */ - if (error1) - data[7] &= 0xE0; - } - - /* At least one event is available */ - event = data; - do { - eventId = *event; - event_handler = info->event_dispatch_table[eventId]; - - if (eventId < EVENTID_LAST) { - event = event_handler(info, (event)); - } else { - event = fts_next_event(event); - } - input_sync(info->input_dev); - } while (event); - } - - /* - * re-enable interrupts - */ - fts_interrupt_enable(info); -} - -static int cx_crc_check(void) -{ - unsigned char regAdd1[3] = {FTS_CMD_HW_REG_R, ADDR_CRC_BYTE0, ADDR_CRC_BYTE1}; - unsigned char val = 0; - unsigned char crc_status; - unsigned int error; - - error = fts_readCmd(regAdd1, sizeof (regAdd1), &val, 1); - if (error < OK) { - logError(1, "%s %s Cannot read crc status ERROR %08X\n", tag, __func__, error); - return error; - } - - crc_status = val & CRC_MASK; - if (crc_status != OK) { /* CRC error if crc_status!= 0 */ - logError(1, "%s %s CRC ERROR = %X\n", tag, __func__, crc_status); - } - - return crc_status; /* return OK if no CRC error, or a number >OK if crc error */ -} - -static void fts_fw_update_auto(struct work_struct *work) -{ - int retval = 0; - int retval1 = 0; - int ret; - struct fts_ts_info *info; - struct delayed_work *fwu_work = container_of(work, struct delayed_work, work); - int crc_status = 0; - int error = 0; - info = container_of(fwu_work, struct fts_ts_info, fwu_work); - - logError(1, "%s Fw Auto Update is starting...\n", tag); - - /* check CRC status */ - ret = cx_crc_check(); - if (ret > OK && ftsInfo.u16_fwVer == 0x0000) { - logError(1, "%s %s: CRC Error or NO FW!\n", tag, __func__); - crc_status = 1; - } else { - crc_status = 0; - logError(1, "%s %s: NO CRC Error or Impossible to read CRC register!\n", tag, __func__); - } -#ifdef FTM3_CHIP - retval = flashProcedure(PATH_FILE_FW, crc_status, !crc_status); -#else - retval = flashProcedure(PATH_FILE_FW, crc_status, 1); -#endif - if ((retval & 0xFF000000) == ERROR_FLASH_PROCEDURE) { - logError(1, "%s %s: firmware update failed and retry! ERROR %08X\n", tag, __func__, retval); - fts_chip_powercycle(info); /* power reset */ -#ifdef FTM3_CHIP - retval1 = flashProcedure(PATH_FILE_FW, crc_status, !crc_status); -#else - retval1 = flashProcedure(PATH_FILE_FW, crc_status, 1); -#endif - if ((retval1 & 0xFF000000) == ERROR_FLASH_PROCEDURE) { - logError(1, "%s %s: firmware update failed again! ERROR %08X\n", tag, __func__, retval1); - logError(1, "%s Fw Auto Update Failed!\n", tag); - /* return; */ } - } - if ((ftsInfo.u32_mpPassFlag != INIT_MP) && (ftsInfo.u32_mpPassFlag != INIT_FIELD)) - ret = ERROR_GET_INIT_STATUS; - else - ret = OK; + /*no coordinates for gestures reported by FW */ + if (event[1] == EVENT_TYPE_GESTURE_DTC1) + needCoords = 0; - if (ret == ERROR_GET_INIT_STATUS) { /* initialization status not correct or after FW complete update, do initialization. */ - error = fts_chip_initialization(info); - if (error < OK) { - logError(1, "%s %s Cannot initialize the chip ERROR %08X\n", tag, __func__, error); - } - } - error = fts_init_hw(info); - if (error < OK) { - logError(1, "%s Cannot initialize the hardware device ERROR %08X\n", tag, error); - } + if (needCoords == 1) + readGestureCoords(event); + + fts_input_report_key(info, value); - logError(1, "%s Fw Auto Update Finished!\n", tag); +gesture_done: + /* Done with gesture event, clear bit. */ + __clear_bit(touchId, &info->touch_id); + } + /* return fts_next_event(event); */ } +#endif + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler -static int fts_chip_initialization(struct fts_ts_info *info) +/* + * This handler is called each time there is at least + * one new event in the FIFO + */ +static void fts_event_handler(struct work_struct *work) { - int ret2 = 0; - int retry; - int initretrycnt = 0; + struct fts_ts_info *info; + int error = 0, count = 0; + unsigned char regAdd; + unsigned char data[FIFO_EVENT_SIZE] = {0}; + unsigned char eventId; + + event_dispatch_handler_t event_handler; + + info = container_of(work, struct fts_ts_info, work); + /* + * read all the FIFO and parsing events + */ + + /*wake_lock_timeout(&info->wakelock, HZ); */ + __pm_wakeup_event(&info->wakeup_source, HZ); + /*logError(1, "%s %s: begin\n", tag, __func__);*/ + regAdd = FIFO_CMD_READONE; - /* initialization error, retry initialization */ - for (retry = 0; retry <= INIT_FLAG_CNT; retry++) { - ret2 = production_test_initialization(); - if (ret2 == OK) { - ret2 = save_mp_flag(INIT_FIELD); - if (ret2 == OK) + for (count = 0; count < FIFO_DEPTH; count++) { + error = fts_readCmd(®Add, sizeof(regAdd), data, + FIFO_EVENT_SIZE); + if (error == OK && data[0] != EVENTID_NO_EVENT) + eventId = data[0]; + else break; + /* if(data[7]&0x20) */ + /* logError(1, "%s %s overflow ID = %02X Last = %02X\n",*/ + /* tag, __func__, data[0], data[7]);*/ + + if (eventId < EVENTID_LAST) { + event_handler = info->event_dispatch_table[eventId]; + event_handler(info, (data)); + } } - initretrycnt++; - logError(1, "%s initialization cycle count = %04d - ERROR %08X\n", tag, initretrycnt, ret2); - fts_chip_powercycle(info); - } - if (ret2 < OK) { /* initialization error */ - logError(1, "%s fts initialization failed 3 times\n", tag); - } + input_sync(info->input_dev); - return ret2; + /*re-enable interrupts */ + fts_interrupt_enable(info); } + #ifdef FTS_USE_POLLING_MODE static enum hrtimer_restart fts_timer_func(struct hrtimer *timer) { struct fts_ts_info *info = - container_of(timer, struct fts_ts_info, timer); + container_of(timer, struct fts_ts_info, timer); queue_work(info->event_wq, &info->work); return HRTIMER_NORESTART; @@ -1374,8 +3285,11 @@ static enum hrtimer_restart fts_timer_func(struct hrtimer *timer) static irqreturn_t fts_interrupt_handler(int irq, void *handle) { struct fts_ts_info *info = handle; + disable_irq_nosync(info->client->irq); + queue_work(info->event_wq, &info->work); + return IRQ_HANDLED; } #endif @@ -1384,17 +3298,16 @@ static int fts_interrupt_install(struct fts_ts_info *info) { int i, error = 0; - info->event_dispatch_table = kzalloc( - sizeof (event_dispatch_handler_t) * EVENTID_LAST, GFP_KERNEL); + info->event_dispatch_table = kzalloc(sizeof(event_dispatch_handler_t) + * EVENTID_LAST, GFP_KERNEL); if (!info->event_dispatch_table) { - logError(1, "%s OOM allocating event dispatch table\n", tag); + logError(1, "%s OOM allocating event dispatch table\n", tag); return -ENOMEM; } for (i = 0; i < EVENTID_LAST; i++) info->event_dispatch_table[i] = fts_nop_event_handler; - install_handler(info, ENTER_POINTER, enter_pointer); install_handler(info, LEAVE_POINTER, leave_pointer); install_handler(info, MOTION_POINTER, motion_pointer); @@ -1407,28 +3320,25 @@ static int fts_interrupt_install(struct fts_ts_info *info) #ifdef PHONE_KEY install_handler(info, KEY_STATUS, key_status); #endif - /* disable interrupts in any case */ error = fts_disableInterrupt(); #ifdef FTS_USE_POLLING_MODE - logError(1, "%s Polling Mode\n"); + logError(1, "%s Polling Mode\n"); hrtimer_init(&info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); info->timer.function = fts_timer_func; hrtimer_start(&info->timer, ktime_set(1, 0), HRTIMER_MODE_REL); #else - logError(1, "%s Interrupt Mode\n", tag); + logError(1, "%s Interrupt Mode\n", tag); if (request_irq(info->client->irq, fts_interrupt_handler, - IRQF_TRIGGER_LOW, info->client->name, - info)) { - logError(1, "%s Request irq failed\n", tag); + IRQF_TRIGGER_LOW, info->client->name, info)) { + logError(1, "%s Request irq failed\n", tag); kfree(info->event_dispatch_table); error = -EBUSY; - } /*else { - error = fts_enableInterrupt(); - }*/ + } /*else {*/ + /*error = fts_enableInterrupt();*/ + /*}*/ #endif - return error; } @@ -1448,104 +3358,108 @@ static void fts_interrupt_uninstall(struct fts_ts_info *info) static void fts_interrupt_enable(struct fts_ts_info *info) { #ifdef FTS_USE_POLLING_MODE - hrtimer_start(&info->timer, - ktime_set(0, 10000000), HRTIMER_MODE_REL); + hrtimer_start(&info->timer, ktime_set(0, 10000000), HRTIMER_MODE_REL); #else enable_irq(info->client->irq); #endif } -/* -static void fts_interrupt_disable(struct fts_ts_info *info) -{ -#ifdef FTS_USE_POLLING_MODE - hrtimer_cancel(&info->timer); -#else - disable_irq(info->client->irq); -#endif -} -*/ - static int fts_init(struct fts_ts_info *info) { int error; error = fts_system_reset(); if (error < OK && error != (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { - logError(1, "%s Cannot reset the device! ERROR %08X\n", tag, error); + logError(1, "%s Cannot reset the device! ERROR %08X\n", + tag, error); return error; } if (error == (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { - logError(1, "%s Setting default Chip INFO!\n", tag); + logError(1, "%s Setting default Chip INFO!\n", tag); defaultChipInfo(0); } else { - error = readChipInfo(0); /* system reset OK */ + error = readChipInfo(0);/*system reset OK*/ if (error < OK) { - logError(1, "%s Cannot read Chip Info! ERROR %08X\n", tag, error); - } + logError(1, "%s Cannot read Chip Info!ERROR:%08X\n", + tag, error); + } } error = fts_interrupt_install(info); if (error != OK) - logError(1, "%s Init (1) error (ERROR = %08X)\n", error); + logError(1, "%s Init (1) error (ERROR = %08X)\n", tag, error); return error; } int fts_chip_powercycle(struct fts_ts_info *info) { - int error, i; + int error = 0; + + logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); + + /** + * if IRQ pin is short with DVDD a call to + * the ISR will triggered when the regulator is turned off + */ - logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); + logError(1, "%s %s: Disabling IRQ...\n", tag, __func__); + disable_irq_nosync(info->client->irq); if (info->pwr_reg) { error = regulator_disable(info->pwr_reg); if (error < 0) { - logError(1, "%s %s: Failed to disable DVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to disable DVDD regulator\n", + tag, __func__); } } if (info->bus_reg) { error = regulator_disable(info->bus_reg); if (error < 0) { - logError(1, "%s %s: Failed to disable AVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to disable AVDD regulator\n", + tag, __func__); } } if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) gpio_set_value(info->bdata->reset_gpio, 0); + else + msleep(300); - msleep(300); if (info->pwr_reg) { error = regulator_enable(info->bus_reg); if (error < 0) { - logError(1, "%s %s: Failed to enable AVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to enable AVDD regulator\n", + tag, __func__); } } if (info->bus_reg) { error = regulator_enable(info->pwr_reg); if (error < 0) { - logError(1, "%s %s: Failed to enable DVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to enable DVDD regulator\n", + tag, __func__); } } - msleep(300); /* time needed by the regulators for reaching the regime values */ + /*time needed by the regulators for reaching the regime values*/ + msleep(20); + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { - msleep(10); /* time to wait before bring up the reset gpio after the power up of the regulators */ + /* time to wait before bring up the reset*/ + /* gpio after the power up of the regulators */ + msleep(20); gpio_set_value(info->bdata->reset_gpio, 1); - /* msleep(300); */ + /* mdelay(300); */ } - /* before reset clear all slot */ - for (i = 0; i < TOUCH_ID_MAX; i++) { - input_mt_slot(info->input_dev, i); - input_mt_report_slot_state(info->input_dev, - (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); - } - input_sync(info->input_dev); + release_all_touches(info); - logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", tag, __func__, error); + logError(1, "%s %s: Enabling IRQ...\n", tag, __func__); + enable_irq(info->client->irq); + logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", + tag, __func__, error); setSystemResettedUp(1); setSystemResettedDown(1); return error; @@ -1553,160 +3467,430 @@ int fts_chip_powercycle(struct fts_ts_info *info) int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep) { - int error, i; + int error = 0; + + logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); - logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); if (info->pwr_reg) { error = regulator_disable(info->pwr_reg); if (error < 0) { - logError(1, "%s %s: Failed to disable DVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to disable DVDD regulator\n", + tag, __func__); } } if (info->bus_reg) { error = regulator_disable(info->bus_reg); if (error < 0) { - logError(1, "%s %s: Failed to disable AVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to disable AVDD regulator\n", + tag, __func__); } } if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) gpio_set_value(info->bdata->reset_gpio, 0); + /*mdelay(sleep);*/ msleep(sleep); if (info->pwr_reg) { error = regulator_enable(info->bus_reg); if (error < 0) { - logError(1, "%s %s: Failed to enable AVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to enable AVDD regulator\n", + tag, __func__); } } if (info->bus_reg) { error = regulator_enable(info->pwr_reg); if (error < 0) { - logError(1, "%s %s: Failed to enable DVDD regulator\n", tag, __func__); + logError(1, "%s %s: Failed to enable DVDD regulator\n", + tag, __func__); } } - msleep(500); /*time needed by the regulators for reaching the regime values */ + /*time needed by the regulators for reaching the regime values*/ + msleep(500); + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { - msleep(10); /*time to wait before bring up the reset gpio after the power up of the regulators */ + /** + * time to wait before bring up the reset + * gpio after the power up of the regulators + */ + msleep(20); gpio_set_value(info->bdata->reset_gpio, 1); - /* msleep(300); */ + /*msleep(300);*/ } - /* before reset clear all slot */ - for (i = 0; i < TOUCH_ID_MAX; i++) { - input_mt_slot(info->input_dev, i); - input_mt_report_slot_state(info->input_dev, - (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); - } - input_sync(info->input_dev); + /*before reset clear all slot */ + release_all_touches(info); - logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", tag, __func__, error); + logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", + tag, __func__, error); setSystemResettedUp(1); setSystemResettedDown(1); return error; } -static int fts_init_hw(struct fts_ts_info *info) +static int fts_init_afterProbe(struct fts_ts_info *info) { int error = 0; - error = cleanUp(1); - if (error < OK) - logError(1, "%s Init (2) error (ERROR = %08X)\n", tag, error); + /* system reset */ + error = cleanUp(0); + + /* enable the features and the sensing */ + error |= fts_mode_handler(info, 0); + + /* enable the interrupt */ + error |= fts_enableInterrupt(); + +#if defined(CONFIG_FB_MSM) + error |= fb_register_client(&info->notifier); +#else + error |= msm_drm_register_client(&info->notifier); +#endif - info->mode = MODE_NORMAL; + if (error < OK) + logError(1, "%s %s Init after Probe error (ERROR = %08X)\n", + tag, __func__, error); return error; } - /* - * TODO: change this function according with the needs of - *customer in temrs of feature to enable/disable - */ +/** + * TODO: change this function according with the needs + * of customer in terms of feature to enable/disable + */ static int fts_mode_handler(struct fts_ts_info *info, int force) { int res = OK; int ret = OK; + /* initialize the mode to Nothing in order*/ + /*to be updated depending on the features enabled */ + info->mode = MODE_NOTHING; + logError(0, "%s %s: Mode Handler starting...\n", tag, __func__); switch (info->resume_bit) { - case 0:/* screen down */ - logError(0, "%s %s: Screen OFF...\n", tag, __func__); + case 0: + /* screen down */ + logError(0, "%s %s: Screen OFF...\n", tag, __func__); + /** + * do sense off in order to avoid the flooding + * of the fifo with touch events if someone is + * touching the panel during suspend + */ + logError(0, "%s %s: Sense OFF!\n", tag, __func__); + /*we need to use fts_command for speed reason*/ + /*(no need to check echo in this case and interrupt*/ + /* can be enabled)*/ + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_OFF); +#ifdef PHONE_KEY + logError(0, "%s %s: Key OFF!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_OFF); +#endif + #ifdef PHONE_GESTURE - if (info->gesture_enabled == 1) { - logError(0, "%s %s: enter in gesture mode !\n", tag, __func__); - res = enterGestureMode(isSystemResettedDown()); - if (res >= OK) { + if (info->gesture_enabled == 1) { + logError(0, "%s %s: enter in gesture mode!\n", + tag, __func__); + ret = enterGestureMode(isSystemResettedDown()); + if (ret >= OK) { + info->mode |= FEAT_GESTURE; + } else { + logError(1, + "%s %s:enterGestureMode failed!%08X recovery in senseOff\n", + tag, __func__, ret); + } + res |= ret; + } +#endif + if (info->mode != (FEAT_GESTURE|MODE_NOTHING) + || info->gesture_enabled == 0) + info->mode |= MODE_SENSEOFF; + setSystemResettedDown(0); + break; - info->mode = MODE_GESTURE; - /* return OK; */ - } else { - logError(1, "%s %s: enterGestureMode failed! ERROR %08X recovery in senseOff...\n", tag, __func__, res); + case 1: + /* screen up */ + logError(0, "%s %s: Screen ON...\n", tag, __func__); + +#ifdef FEAT_GLOVE + if ((info->glove_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Glove Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->glove_enabled, + FEAT_GLOVE); + if (ret < OK) { + logError(1, + "%s %s:error in setting GLOVE_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->glove_enabled == FEAT_ENABLE) { + info->mode |= FEAT_GLOVE; + logError(1, "%s %s: GLOVE_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: GLOVE_MODE Disabled!\n", + tag, __func__); + } } - } #endif - if (info->mode != MODE_GESTURE || info->gesture_enabled == 0) { - logError(0, "%s %s: Sense OFF!\n", tag, __func__); - res |= fts_command(info, FTS_CMD_MS_MT_SENSE_OFF); /* we need to use fts_command for speed reason (no need to check echo in this case and interrupt can be enabled) */ -#ifdef PHONE_KEY - logError(0, "%s %s: Key OFF!\n", tag, __func__); - res |= fts_command(info, FTS_CMD_MS_KEY_OFF); +#ifdef FEAT_STYLUS + if ((info->stylus_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Stylus Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->stylus_enabled, + FEAT_STYLUS); + if (ret < OK) { + logError(1, + "%s %s:error in set STYLUS_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->stylus_enabled == FEAT_ENABLE) { + info->mode |= FEAT_STYLUS; + logError(1, "%s %s: STYLUS_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: STYLUS_MODE Disabled!\n", + tag, __func__); + } + } #endif +#ifdef FEAT_COVER + if ((info->cover_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Cover Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->cover_enabled, + FEAT_COVER); + if (ret < OK) { + logError(1, + "%s %s:error setting COVER_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; - info->mode = MODE_SENSEOFF; + if (ret >= OK && info->cover_enabled == FEAT_ENABLE) { + info->mode |= FEAT_COVER; + logError(1, "%s %s: COVER_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: COVER_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_CHARGER + if ((info->charger_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Charger Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->charger_enabled, + FEAT_CHARGER); + if (ret < OK) { + logError(1, + "%s %s:error set CHARGER_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; - } - setSystemResettedDown(0); - break; + if (ret >= OK && info->charger_enabled == FEAT_ENABLE) { + info->mode |= FEAT_CHARGER; + logError(1, "%s %s: CHARGER_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: CHARGER_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_VR + if ((info->vr_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Vr Mode setting\n", tag, __func__); + ret = featureEnableDisable(info->vr_enabled, FEAT_VR); + if (ret < OK) { + logError(1, + "%s %s:error setting VR_MODE!:%08X\n", + tag, __func__, ret); + } + res |= ret; - case 1: /* screen up */ - logError(0, "%s %s: Screen ON...\n", tag, __func__); + if (ret >= OK && info->vr_enabled == FEAT_ENABLE) { + info->mode |= FEAT_VR; + logError(1, "%s %s: VR_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: VR_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_EDGE_REJECTION + if ((info->edge_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Edge Rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->edge_rej_enabled, + FEAT_EDGE_REJECTION); + if (ret < OK) { + logError(1, + "%s %s:err set EDGE_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->edge_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_EDGE_REJECTION; + logError(1, + "%s %s:EDGE_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s %s:EDGE_REJECTION_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_CORNER_REJECTION + if ((info->corner_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Corner rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->corner_rej_enabled, + FEAT_CORNER_REJECTION); + if (ret < OK) { + logError(1, + "%s%s:err CORNER_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->corner_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_CORNER_REJECTION; + logError(1, + "%s%s:CORNER_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s%s:CORNER_REJECTION_MODE Disabled\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_EDGE_PALM_REJECTION + if ((info->edge_palm_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s:Edge Palm rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->edge_palm_rej_enabled, + FEAT_EDGE_PALM_REJECTION); + if (ret < OK) { + logError(1, + "%s %s:err EDGE_PALM_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->edge_palm_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_EDGE_PALM_REJECTION; + logError(1, + "%s %s:EDGE_PALM_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s %s:EDGE_PALM_REJECTION_MODE Disabled!\n", + tag, __func__); + } + } +#endif logError(0, "%s %s: Sense ON!\n", tag, __func__); res |= fts_command(info, FTS_CMD_MS_MT_SENSE_ON); + info->mode |= MODE_SENSEON; #ifdef PHONE_KEY logError(0, "%s %s: Key ON!\n", tag, __func__); res |= fts_command(info, FTS_CMD_MS_KEY_ON); #endif - info->mode = MODE_NORMAL; - - if (info->glove_enabled == FEAT_ENABLE || force == 1) { - if (isSystemResettedUp() || force == 1) { - logError(0, "%s %s: Glove Mode setting...\n", tag, __func__); - ret = featureEnableDisable(info->glove_enabled, FEAT_GLOVE); - if (ret < OK) { - logError(1, "%s %s: error during setting GLOVE_MODE! ERROR %08X\n", tag, __func__, ret); - } - res |= ret; - } - if (ret >= OK && info->glove_enabled == FEAT_ENABLE) { - info->mode = MODE_GLOVE; - logError(1, "%s %s: GLOVE_MODE Enabled!\n", tag, __func__); - } else{ - logError(1, "%s %s: GLOVE_MODE Disabled!\n", tag, __func__); - } - } - - setSystemResettedUp(0); - break; + setSystemResettedUp(0); + break; default: - logError(1, "%s %s: invalid resume_bit value = %d! ERROR %08X\n", tag, __func__, info->resume_bit, ERROR_OP_NOT_ALLOW); - res = ERROR_OP_NOT_ALLOW; + logError(1, + "%s %s: invalid resume_bit value = %d! ERROR %08X\n", + tag, __func__, info->resume_bit, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; } - - logError(0, "%s %s: Mode Handler finished! res = %08X\n", tag, __func__, res); + logError(0, "%s %s: Mode Handler finished! res = %08X\n", tag, __func__, + res); return res; +} + + +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_info *info; + + info = container_of(work, struct fts_ts_info, resume_work); + + __pm_wakeup_event(&info->wakeup_source, HZ); + + info->resume_bit = 1; +#ifdef USE_NOISE_PARAM + readNoiseParameters(noise_params); +#endif + fts_system_reset(); + +#ifdef USE_NOISE_PARAM + writeNoiseParameters(noise_params); +#endif + + release_all_touches(info); + + fts_mode_handler(info, 0); + + info->sensor_sleep = false; + + fts_enableInterrupt(); +} + + +static void fts_suspend_work(struct work_struct *work) +{ + struct fts_ts_info *info; + + info = container_of(work, struct fts_ts_info, suspend_work); + + __pm_wakeup_event(&info->wakeup_source, HZ); + info->resume_bit = 0; + + fts_mode_handler(info, 0); + + release_all_touches(info); + info->sensor_sleep = true; + + fts_enableInterrupt(); } -static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long val, void *data) + +#if defined(CONFIG_FB_MSM) +static int fts_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) { - struct fts_ts_info *info = container_of(nb, struct fts_ts_info, notifier); + + struct fts_ts_info *info = container_of(nb, + struct fts_ts_info, notifier); struct fb_event *evdata = data; - int i; unsigned int blank; if (val != FB_EVENT_BLANK) @@ -1723,23 +3907,10 @@ static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long va if (info->sensor_sleep) break; - logError(0, "%s %s: FB_BLANK_POWERDOWN\n", tag, __func__); - - /* Release all slots */ - for (i = 0; i < TOUCH_ID_MAX; i++) { - input_mt_slot(info->input_dev, i); - input_mt_report_slot_state(info->input_dev, - (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); - } - input_sync(info->input_dev); - - info->resume_bit = 0; - - fts_mode_handler(info, 0); + logError(0, "%s %s: FB_BLANK_POWERDOWN\n", + tag, __func__); - info->sensor_sleep = true; - - fts_disableInterrupt(); + queue_work(info->event_wq, &info->suspend_work); break; @@ -1747,41 +3918,66 @@ static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long va if (!info->sensor_sleep) break; - logError(0, "%s %s: FB_BLANK_UNBLANK\n", tag, __func__); + logError(0, "%s %s: FB_BLANK_UNBLANK\n", + tag, __func__); - for (i = 0; i < TOUCH_ID_MAX; i++) { - input_mt_slot(info->input_dev, i); - input_mt_report_slot_state(info->input_dev, - (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); - } - input_sync(info->input_dev); + queue_work(info->event_wq, &info->resume_work); + break; + default: + break; + } + } + return NOTIFY_OK; +} + +#else +static int fts_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct fts_ts_info *info = container_of(nb, struct fts_ts_info, + notifier); + struct msm_drm_notifier *evdata = data; + unsigned int blank; - info->resume_bit = 1; + if (val != MSM_DRM_EVENT_BLANK) + return 0; + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); - fts_mode_handler(info, 0); + if (evdata && evdata->data && val == MSM_DRM_EVENT_BLANK && info) { + blank = *(int *) (evdata->data); - info->sensor_sleep = false; + switch (blank) { + case MSM_DRM_BLANK_POWERDOWN: + if (info->sensor_sleep) + break; + logError(0, "%s %s: MSM_DRM_BLANK_UNBLANK\n", + tag, __func__); + queue_work(info->event_wq, &info->suspend_work); + break; - fts_enableInterrupt(); + case MSM_DRM_BLANK_UNBLANK: + if (!info->sensor_sleep) + break; + logError(0, "%s %s: MSM_DRM_BLANK_UNBLANK\n", + tag, __func__); + queue_work(info->event_wq, &info->resume_work); break; default: break; - } } return NOTIFY_OK; - } +#endif static struct notifier_block fts_noti_block = { .notifier_call = fts_fb_state_chg_callback, }; -static int fts_get_reg(struct fts_ts_info *rmi4_data, - bool get) { +static int fts_get_reg(struct fts_ts_info *info, bool get) +{ int retval; - const struct fts_i2c_platform_data *bdata = - rmi4_data->bdata; + const struct fts_i2c_platform_data *bdata = info->bdata; if (!get) { retval = 0; @@ -1789,23 +3985,24 @@ static int fts_get_reg(struct fts_ts_info *rmi4_data, } if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) { - rmi4_data->pwr_reg = regulator_get(rmi4_data->dev, - bdata->pwr_reg_name); - if (IS_ERR(rmi4_data->pwr_reg)) { - logError(1, "%s %s: Failed to get power regulator\n", tag, - __func__); - retval = PTR_ERR(rmi4_data->pwr_reg); + info->pwr_reg = regulator_get(info->dev, + bdata->pwr_reg_name); + if (IS_ERR(info->pwr_reg)) { + logError(1, "%s %s: Failed to get power regulator\n", + tag, __func__); + retval = PTR_ERR(info->pwr_reg); goto regulator_put; } } if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) { - rmi4_data->bus_reg = regulator_get(rmi4_data->dev, - bdata->bus_reg_name); - if (IS_ERR(rmi4_data->bus_reg)) { - logError(1, "%s %s: Failed to get bus pullup regulator\n", tag, - __func__); - retval = PTR_ERR(rmi4_data->bus_reg); + info->bus_reg = regulator_get(info->dev, + bdata->bus_reg_name); + if (IS_ERR(info->bus_reg)) { + logError(1, + "%s %s:Failed to get bus pullup regulator\n", + tag, __func__); + retval = PTR_ERR(info->bus_reg); goto regulator_put; } } @@ -1813,21 +4010,22 @@ static int fts_get_reg(struct fts_ts_info *rmi4_data, return 0; regulator_put: - if (rmi4_data->pwr_reg) { - regulator_put(rmi4_data->pwr_reg); - rmi4_data->pwr_reg = NULL; + if (info->pwr_reg) { + regulator_put(info->pwr_reg); + info->pwr_reg = NULL; } - if (rmi4_data->bus_reg) { - regulator_put(rmi4_data->bus_reg); - rmi4_data->bus_reg = NULL; + if (info->bus_reg) { + regulator_put(info->bus_reg); + info->bus_reg = NULL; } return retval; } -static int fts_enable_reg(struct fts_ts_info *rmi4_data, - bool enable) { +static int fts_enable_reg(struct fts_ts_info *info, + bool enable) +{ int retval; if (!enable) { @@ -1835,20 +4033,20 @@ static int fts_enable_reg(struct fts_ts_info *rmi4_data, goto disable_pwr_reg; } - if (rmi4_data->bus_reg) { - retval = regulator_enable(rmi4_data->bus_reg); + if (info->bus_reg) { + retval = regulator_enable(info->bus_reg); if (retval < 0) { - logError(1, "%s %s: Failed to enable bus regulator\n", tag, - __func__); + logError(1, "%s %s: Failed to enable bus regulator\n", + tag, __func__); goto exit; } } - if (rmi4_data->pwr_reg) { - retval = regulator_enable(rmi4_data->pwr_reg); + if (info->pwr_reg) { + retval = regulator_enable(info->pwr_reg); if (retval < 0) { - logError(1, "%s %s: Failed to enable power regulator\n", tag, - __func__); + logError(1, "%s %s: Failed to enable power regulator\n", + tag, __func__); goto disable_bus_reg; } } @@ -1856,12 +4054,12 @@ static int fts_enable_reg(struct fts_ts_info *rmi4_data, return OK; disable_pwr_reg: - if (rmi4_data->pwr_reg) - regulator_disable(rmi4_data->pwr_reg); + if (info->pwr_reg) + regulator_disable(info->pwr_reg); disable_bus_reg: - if (rmi4_data->bus_reg) - regulator_disable(rmi4_data->bus_reg); + if (info->bus_reg) + regulator_disable(info->bus_reg); exit: return retval; @@ -1877,8 +4075,8 @@ static int fts_gpio_setup(int gpio, bool config, int dir, int state) retval = gpio_request(gpio, buf); if (retval) { - logError(1, "%s %s: Failed to get gpio %d (code: %d)", tag, - __func__, gpio, retval); + logError(1, "%s %s: Failed to get gpio %d (code: %d)", + tag, __func__, gpio, retval); return retval; } @@ -1887,8 +4085,8 @@ static int fts_gpio_setup(int gpio, bool config, int dir, int state) else retval = gpio_direction_output(gpio, state); if (retval) { - logError(1, "%s %s: Failed to set gpio %d direction", tag, - __func__, gpio); + logError(1, "%s %s: Failed to set gpio %d direction", + tag, __func__, gpio); return retval; } } else { @@ -1898,28 +4096,30 @@ static int fts_gpio_setup(int gpio, bool config, int dir, int state) return retval; } -static int fts_set_gpio(struct fts_ts_info *rmi4_data) +static int fts_set_gpio(struct fts_ts_info *info) { int retval; const struct fts_i2c_platform_data *bdata = - rmi4_data->bdata; + info->bdata; retval = fts_gpio_setup(bdata->irq_gpio, true, 0, 0); if (retval < 0) { - logError(1, "%s %s: Failed to configure irq GPIO\n", tag, __func__); + logError(1, "%s %s: Failed to configure irq GPIO\n", + tag, __func__); goto err_gpio_irq; } if (bdata->reset_gpio >= 0) { retval = fts_gpio_setup(bdata->reset_gpio, true, 1, 0); if (retval < 0) { - logError(1, "%s %s: Failed to configure reset GPIO\n", tag, __func__); + logError(1, "%s %s: Failed to configure reset GPIO\n", + tag, __func__); goto err_gpio_reset; } } if (bdata->reset_gpio >= 0) { gpio_set_value(bdata->reset_gpio, 0); - msleep(10); + msleep(20); gpio_set_value(bdata->reset_gpio, 1); } @@ -1933,14 +4133,15 @@ static int fts_set_gpio(struct fts_ts_info *rmi4_data) return retval; } -static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata) +static int parse_dt(struct device *dev, + struct fts_i2c_platform_data *bdata) { int retval; const char *name; struct device_node *np = dev->of_node; bdata->irq_gpio = of_get_named_gpio_flags(np, - "st,irq-gpio", 0, NULL); + "st,irq-gpio", 0, NULL); logError(0, "%s irq_gpio = %d\n", tag, bdata->irq_gpio); @@ -1949,6 +4150,7 @@ static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata) bdata->pwr_reg_name = NULL; else if (retval < 0) return retval; + bdata->pwr_reg_name = name; logError(0, "%s pwr_reg_name = %s\n", tag, name); @@ -1957,6 +4159,7 @@ static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata) bdata->bus_reg_name = NULL; else if (retval < 0) return retval; + bdata->bus_reg_name = name; logError(0, "%s bus_reg_name = %s\n", tag, name); @@ -1972,27 +4175,33 @@ static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata) } static int fts_probe(struct i2c_client *client, - const struct i2c_device_id *idp) { + const struct i2c_device_id *idp) +{ struct fts_ts_info *info = NULL; - char fts_ts_phys[64]; int error = 0; struct device_node *dp = client->dev.of_node; int retval; + int skip_5_1 = 0; - logError(1, "%s %s: driver probe begin!\n", tag, __func__); + logError(1, "%s %s: driver probe begin!\n", tag, __func__); - logError(1, "%s SET I2C Functionality and Dev INFO:\n", tag); + logError(1, "%s SET I2C Functionality and Dev INFO:\n", tag); openChannel(client); + /* logError(1, "%s driver ver. %s (built on %s, %s)\n", tag,*/ + /* FTS_TS_DRV_VERSION, __DATE__, __TIME__);*/ + logError(1, "%s driver ver. %s (built on)\n", tag, FTS_TS_DRV_VERSION); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - logError(1, "%s Unsupported I2C functionality\n", tag); + logError(1, "%s Unsupported I2C functionality\n", tag); error = -EIO; goto ProbeErrorExit_0; } - info = kzalloc(sizeof (struct fts_ts_info), GFP_KERNEL); + info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL); if (!info) { - logError(1, "%s Out of memory... Impossible to allocate struct info!\n", tag); + logError(1, + "%s Out of memory, can't to allocate struct info!\n", + tag); error = -ENOMEM; goto ProbeErrorExit_0; } @@ -2000,68 +4209,92 @@ static int fts_probe(struct i2c_client *client, info->client = client; i2c_set_clientdata(client, info); - logError(1, "%s i2c address: %x\n", tag, client->addr); + logError(1, "%s i2c address: %x\n", tag, client->addr); info->dev = &info->client->dev; if (dp) { - info->bdata = devm_kzalloc(&client->dev, sizeof (struct fts_i2c_platform_data), GFP_KERNEL); + info->bdata = devm_kzalloc(&client->dev, + sizeof(struct fts_i2c_platform_data), + GFP_KERNEL); if (!info->bdata) { - logError(1, "%s ERROR:info.bdata kzalloc failed\n", tag); + logError(1, "%s ERROR:info.bdata kzalloc failed\n", + tag); goto ProbeErrorExit_1; } parse_dt(&client->dev, info->bdata); } - logError(1, "%s SET Regulators:\n", tag); + logError(1, "%s SET Regulators:\n", tag); retval = fts_get_reg(info, true); if (retval < 0) { - logError(1, "%s ERROR: %s: Failed to get regulators\n", tag, __func__); + logError(1, "%s ERROR: %s: Failed to get regulators\n", + tag, __func__); goto ProbeErrorExit_1; } retval = fts_enable_reg(info, true); if (retval < 0) { - logError(1, "%s %s: ERROR Failed to enable regulators\n", tag, __func__); + logError(1, "%s %s: ERROR Failed to enable regulators\n", + tag, __func__); goto ProbeErrorExit_2; } - logError(1, "%s SET GPIOS:\n", tag); + logError(1, "%s SET GPIOS:\n", tag); retval = fts_set_gpio(info); if (retval < 0) { - logError(1, "%s %s: ERROR Failed to set up GPIO's\n", tag, __func__); + logError(1, "%s %s: ERROR Failed to set up GPIO's\n", + tag, __func__); goto ProbeErrorExit_2; } info->client->irq = gpio_to_irq(info->bdata->irq_gpio); - logError(1, "%s SET Auto Fw Update:\n", tag); - info->fwu_workqueue = create_singlethread_workqueue("fts-fwu-queue"); + logError(0, "%s SET Auto Fw Update:\n", tag); + /*info->fwu_workqueue =*/ + /*create_singlethread_workqueue("fts-fwu-queue");*/ + info->fwu_workqueue = alloc_workqueue("fts-fwu-queue", + WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); if (!info->fwu_workqueue) { - logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); + logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); + goto ProbeErrorExit_3; + } + + error = fts_init_afterProbe(info); + if (error < OK) { + logError(1, + "%s Cannot initialize the hardware device ERROR %08X\n", + tag, error); goto ProbeErrorExit_3; } - INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); - logError(1, "%s SET Event Handler:\n", tag); - info->event_wq = create_singlethread_workqueue("fts-event-queue"); + logError(1, "%s SET Event Handler:\n", tag); + /*wake_lock_init(&info->wakelock, WAKE_LOCK_SUSPEND, "fts_tp");*/ + wakeup_source_init(&info->wakeup_source, "fts_tp"); + /*info->event_wq = create_singlethread_workqueue("fts-event-queue");*/ + info->event_wq = alloc_workqueue("fts-event-queue", + WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); if (!info->event_wq) { - logError(1, "%s ERROR: Cannot create work thread\n", tag); + logError(1, "%s ERROR: Cannot create work thread\n", tag); error = -ENOMEM; goto ProbeErrorExit_4; } INIT_WORK(&info->work, fts_event_handler); - logError(1, "%s SET Input Device Property:\n", tag); - info->dev = &info->client->dev; + INIT_WORK(&info->resume_work, fts_resume_work); + INIT_WORK(&info->suspend_work, fts_suspend_work); + + logError(1, "%s SET Input Device Property:\n", tag); + /*info->dev = &info->client->dev;*/ info->input_dev = input_allocate_device(); if (!info->input_dev) { - logError(1, "%s ERROR: No such input device defined!\n", tag); + logError(1, "%s ERROR: No such input device defined!\n", + tag); error = -ENODEV; goto ProbeErrorExit_5; } info->input_dev->dev.parent = &client->dev; info->input_dev->name = FTS_TS_DRV_NAME; - snprintf(fts_ts_phys, sizeof (fts_ts_phys), "%s/input0", - info->input_dev->name); + snprintf(fts_ts_phys, sizeof(fts_ts_phys), "%s/input0", + info->input_dev->name); info->input_dev->phys = fts_ts_phys; info->input_dev->id.bustype = BUS_I2C; info->input_dev->id.vendor = 0x0001; @@ -2076,9 +4309,10 @@ static int fts_probe(struct i2c_client *client, input_mt_init_slots(info->input_dev, TOUCH_ID_MAX, INPUT_MT_DIRECT); - /* input_mt_init_slots(info->input_dev, TOUCH_ID_MAX); */ + /*input_mt_init_slots(info->input_dev, TOUCH_ID_MAX);*/ - /* input_set_abs_params(info->input_dev, ABS_MT_TRACKING_ID, 0, FINGER_MAX, 0, 0); */ + /*input_set_abs_params(info->input_dev,*/ + /*ABS_MT_TRACKING_ID, 0, FINGER_MAX, 0, 0);*/ input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0); input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, @@ -2087,8 +4321,8 @@ static int fts_probe(struct i2c_client *client, AREA_MIN, AREA_MAX, 0, 0); input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, AREA_MIN, AREA_MAX, 0, 0); - input_set_abs_params(info->input_dev, ABS_MT_PRESSURE, - PRESSURE_MIN, PRESSURE_MAX, 0, 0); + /*input_set_abs_params(info->input_dev, ABS_MT_PRESSURE,*/ + /*PRESSURE_MIN, PRESSURE_MAX, 0, 0);*/ #ifdef PHONE_GESTURE input_set_capability(info->input_dev, EV_KEY, KEY_WAKEUP); @@ -2121,7 +4355,7 @@ static int fts_probe(struct i2c_client *client, #endif #ifdef PHONE_KEY - /* KEY associated to the touch screen buttons */ + /*KEY associated to the touch screen buttons*/ input_set_capability(info->input_dev, EV_KEY, KEY_HOMEPAGE); input_set_capability(info->input_dev, EV_KEY, KEY_BACK); input_set_capability(info->input_dev, EV_KEY, KEY_MENU); @@ -2129,42 +4363,65 @@ static int fts_probe(struct i2c_client *client, mutex_init(&(info->input_report_mutex)); + +#ifdef PHONE_GESTURE + mutex_init(&gestureMask_mutex); +#endif + /* register the multi-touch input device */ error = input_register_device(info->input_dev); if (error) { - logError(1, "%s ERROR: No such input device\n", tag); + logError(1, "%s ERROR: No such input device\n", tag); error = -ENODEV; goto ProbeErrorExit_5_1; } + skip_5_1 = 1; /* track slots */ info->touch_id = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif /* init hardware device */ - logError(1, "%s Device Initialization:\n", tag); + logError(1, "%s Device Initialization:\n", tag); error = fts_init(info); if (error < OK) { - logError(1, "%s Cannot initialize the device ERROR %08X\n", tag, error); + logError(1, "%s Cannot initialize the device ERROR %08X\n", + tag, error); error = -ENODEV; goto ProbeErrorExit_6; } + /** + * init feature switches (by default all the features + * are disable, if one feature want to be enabled from + * the start, set the corresponding value to 1) + */ info->gesture_enabled = 0; info->glove_enabled = 0; + info->charger_enabled = 0; + info->stylus_enabled = 0; + info->vr_enabled = 0; + info->cover_enabled = 0; + info->edge_rej_enabled = 0; + info->corner_rej_enabled = 0; + info->edge_palm_rej_enabled = 0; + info->resume_bit = 1; info->notifier = fts_noti_block; - error = fb_register_client(&info->notifier); - if (error) { - logError(1, "%s ERROR: register notifier failed!\n", tag); - goto ProbeErrorExit_6; - } + /*error = fb_register_client(&info->notifier);*/ + /*if (error) {*/ + /*logError(1, "%s ERROR: register notifier failed!\n", tag);*/ + /*goto ProbeErrorExit_6;*/ + /*}*/ - logError(1, "%s SET Device File Nodes:\n", tag); + logError(1, "%s SET Device File Nodes:\n", tag); /* sysfs stuff */ info->attrs.attrs = fts_attr_group; error = sysfs_create_group(&client->dev.kobj, &info->attrs); if (error) { - logError(1, "%s ERROR: Cannot create sysfs structure!\n", tag); + logError(1, "%s ERROR: Cannot create sysfs structure!\n", tag); error = -ENODEV; goto ProbeErrorExit_7; } @@ -2176,7 +4433,9 @@ static int fts_probe(struct i2c_client *client, info->i2c_cmd_dev = device_create(fts_cmd_class, NULL, DCHIP_ID_0, info, "fts_i2c"); if (IS_ERR(info->i2c_cmd_dev)) { - logError(1, "%s ERROR: Failed to create device for the sysfs!\n", tag); + logError(1, + "%s ERROR: Failed to create device for the sysfs!\n", + tag); goto ProbeErrorExit_8; } @@ -2185,19 +4444,21 @@ static int fts_probe(struct i2c_client *client, error = sysfs_create_group(&info->i2c_cmd_dev->kobj, &i2c_cmd_attr_group); if (error) { - logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); goto ProbeErrorExit_9; } - #endif + #ifdef DRIVER_TEST if (fts_cmd_class == NULL) fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); info->test_cmd_dev = device_create(fts_cmd_class, NULL, DCHIP_ID_0, info, "fts_driver_test"); if (IS_ERR(info->test_cmd_dev)) { - logError(1, "%s ERROR: Failed to create device for the sysfs!\n", tag); + logError(1, + "%s ERROR: Failed to create device for the sysfs!\n", + tag); goto ProbeErrorExit_10; } @@ -2206,19 +4467,12 @@ static int fts_probe(struct i2c_client *client, error = sysfs_create_group(&info->test_cmd_dev->kobj, &test_cmd_attr_group); if (error) { - logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); goto ProbeErrorExit_11; } - #endif - /*if wanna auto-update FW when probe, - * please don't comment the following code - */ - /* queue_delayed_work(info->fwu_workqueue, &info->fwu_work, - * msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); - */ - logError(1, "%s Probe Finished!\n", tag); + logError(1, "%s Probe Finished!\n", tag); return OK; /* error exit path */ @@ -2231,7 +4485,7 @@ static int fts_probe(struct i2c_client *client, ProbeErrorExit_10: #ifndef SCRIPTLESS sysfs_remove_group(&client->dev.kobj, &info->attrs); -#endif +#endif /*if fail before creating the*/ #endif #ifdef SCRIPTLESS @@ -2243,19 +4497,22 @@ static int fts_probe(struct i2c_client *client, #endif ProbeErrorExit_7: - fb_unregister_client(&info->notifier); + /*fb_unregister_client(&info->notifier);*/ ProbeErrorExit_6: input_unregister_device(info->input_dev); ProbeErrorExit_5_1: - /* intput_free_device(info->input_dev ); */ + if (skip_5_1 != 1) + input_free_device(info->input_dev); - ProbeErrorExit_5: - destroy_workqueue(info->event_wq); +ProbeErrorExit_5: + destroy_workqueue(info->event_wq); ProbeErrorExit_4: destroy_workqueue(info->fwu_workqueue); + /* wake_lock_destroy(&info->wakelock); */ + wakeup_source_trash(&info->wakeup_source); ProbeErrorExit_3: fts_enable_reg(info, false); @@ -2267,7 +4524,7 @@ static int fts_probe(struct i2c_client *client, kfree(info); ProbeErrorExit_0: - logError(1, "%s Probe Failed!\n", tag); + logError(1, "%s Probe Failed!\n", tag); return error; } @@ -2283,9 +4540,7 @@ static int fts_remove(struct i2c_client *client) #ifdef SCRIPTLESS /*I2C cmd*/ - sysfs_remove_group(&info->i2c_cmd_dev->kobj, - &i2c_cmd_attr_group); - + sysfs_remove_group(&info->i2c_cmd_dev->kobj, &i2c_cmd_attr_group); #endif #if defined(SCRIPTLESS) || defined(DRIVER_TEST) @@ -2298,12 +4553,16 @@ static int fts_remove(struct i2c_client *client) /* remove interrupt and event handlers */ fts_interrupt_uninstall(info); +#if defined(CONFIG_FB_MSM) fb_unregister_client(&info->notifier); +#else + msm_drm_unregister_client(&info->notifier); +#endif /* unregister the device */ input_unregister_device(info->input_dev); - /* intput_free_device(info->input_dev ); */ + /* input_free_device(info->input_dev ); */ /* Empty the FIFO buffer */ fts_command(info, FIFO_CMD_FLUSH); @@ -2311,6 +4570,8 @@ static int fts_remove(struct i2c_client *client) /* Remove the work thread */ destroy_workqueue(info->event_wq); + /* wake_lock_destroy(&info->wakelock); */ + wakeup_source_trash(&info->wakeup_source); destroy_workqueue(info->fwu_workqueue); fts_enable_reg(info, false); @@ -2322,7 +4583,7 @@ static int fts_remove(struct i2c_client *client) return OK; } -static struct of_device_id fts_of_match_table[] = { +static const struct of_device_id fts_of_match_table[] = { { .compatible = "st,fts", }, @@ -2353,9 +4614,9 @@ static void __exit fts_driver_exit(void) i2c_del_driver(&fts_i2c_driver); } -MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); -MODULE_AUTHOR("STMicroelectronics, Inc."); -MODULE_LICENSE("GPL"); late_initcall(fts_driver_init); module_exit(fts_driver_exit); + +MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h index 7d72d226349f2a94c96384b041de7fe9748dac25..cf2a2196d024719612f6499c0a03e55732b15019 100644 --- a/drivers/input/touchscreen/st/fts.h +++ b/drivers/input/touchscreen/st/fts.h @@ -1,117 +1,186 @@ /* - * fts.c - * * FTS Capacitive touch screen controller (FingerTipS) * - * Copyright (C) 2016, STMicroelectronics Limited. - * Authors: AMG(Analog Mems Group) + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * * - * marco.cali@st.com + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ #ifndef _LINUX_FTS_I2C_H_ #define _LINUX_FTS_I2C_H_ +/*#include */ +#include + #include "fts_lib/ftsSoftware.h" #include "fts_lib/ftsHardware.h" +#include "fts_lib/ftsGesture.h" -#define FTS_POWER_ON 1 -#define FTS_POWER_OFF 0 +#define FTS_POWER_ON 1 +#define FTS_POWER_OFF 0 /****************** CONFIGURATION SECTION ******************/ -/* #define PHONE_KEY */ -/* #define PHONE_GESTURE */ +/**** CODE CONFIGURATION ****/ -#define SCRIPTLESS +#define FTS_TS_DRV_NAME "fts" +#define FTS_TS_DRV_VERSION "4.2.14" /* version */ + +#define SCRIPTLESS /*allow to work in scriptless mode with the GUI*/ #ifdef SCRIPTLESS #define SCRIPTLESS_DEBUG -/* uncomment this macro definition to print debug -* message for script less support -*/ +/** + * uncomment this macro definition to print debug + * message for script less support + */ #endif #define DRIVER_TEST -#define FW_H_FILE +/* #define FW_H_FILE */ /*include the FW as header file*/ #ifdef FW_H_FILE -#define FW_SIZE_NAME myArray_size -#define FW_ARRAY_NAME myArray + #define FW_SIZE_NAME myArray_size + #define FW_ARRAY_NAME myArray #endif -#define LIMITS_H_FILE +/*#define LIMITS_H_FILE*/ /*include the limits file as header file*/ #ifdef LIMITS_H_FILE -#define LIMITS_SIZE_NAME myArray2_size -#define LIMITS_ARRAY_NAME myArray2 + #define LIMITS_SIZE_NAME myArray2_size + #define LIMITS_ARRAY_NAME myArray2 #endif -#define FTS_TS_DRV_NAME "fts" -#define FTS_TS_DRV_VERSION "4.1.0" +/**** END ****/ + + +/**** FEATURES USED IN THE IC ***/ +#define PHONE_KEY /*enable the keys*/ + +#define PHONE_GESTURE /*allow to use the gestures*/ +#ifdef PHONE_GESTURE + #define USE_GESTURE_MASK + #define USE_CUSTOM_GESTURES +#endif + +#define USE_ONE_FILE_NODE +/*allow to enable/disable all the features just using one file node*/ + +#define EDGE_REJ +/*allow edge rej feature (comment to disable)*/ -#define X_AXIS_MAX 1440 -#define X_AXIS_MIN 0 -#define Y_AXIS_MAX 2560 -#define Y_AXIS_MIN 0 +#define CORNER_REJ +/*allow corn rej feature (comment to disable)*/ -#define PRESSURE_MIN 0 -#define PRESSURE_MAX 127 +#define EDGE_PALM_REJ +/*allow edge palm rej feature (comment to disable)*/ -#define FINGER_MAX 10 -#define STYLUS_MAX 1 -#define TOUCH_ID_MAX (FINGER_MAX + STYLUS_MAX) +#define CHARGER_MODE +/*allow charger mode feature (comment to disable)*/ -#define AREA_MIN PRESSURE_MIN -#define AREA_MAX PRESSURE_MAX +#define GLOVE_MODE +/*allow glove mode feature (comment to disable)*/ + +#define VR_MODE +/*allow vr mode feature (comment to disable)*/ + +#define COVER_MODE +/*allow cover mode feature (comment to disable)*/ + +#define STYLUS_MODE +/*allow stylus mode feature (comment to disable)*/ + +#define USE_NOISE_PARAM +/*set noise params during resume (comment to disable)*/ + +/**** END ****/ + + +/**** PANEL SPECIFICATION ****/ +#define X_AXIS_MAX 1440 +#define X_AXIS_MIN 0 +#define Y_AXIS_MAX 2880 +#define Y_AXIS_MIN 0 + +#define PRESSURE_MIN 0 +#define PRESSURE_MAX 127 + +#define TOUCH_ID_MAX 10 + +#define AREA_MIN PRESSURE_MIN +#define AREA_MAX PRESSURE_MAX +/**** END ****/ /*********************************************************/ /* Flash programming */ -#define INIT_FLAG_CNT 3 +#define INIT_FLAG_CNT 3 /* KEYS */ -#define KEY1 0x02 -#define KEY2 0x01 -#define KEY3 0x04 +#define KEY1 0x02 +#define KEY2 0x01 +#define KEY3 0x04 /* * Configuration mode */ -#define MODE_NORMAL 0 -#define MODE_GESTURE 1 -#define MODE_GLOVE 2 -#define MODE_SENSEOFF 3 +/** + * bitmask which can assume the value defined as + * features in ftsSoftware.h or the following values + */ + +#define MODE_NOTHING 0x00000000 +#define MODE_SENSEON 0x10000000 +#define MODE_SENSEOFF 0x20000000 +#define FEAT_GESTURE 0x40000000 + /* * Status Event Field: - * id of command that triggered the event + * id of command that triggered the event */ -#define FTS_FLASH_WRITE_CONFIG 0x03 -#define FTS_FLASH_WRITE_COMP_MEMORY 0x04 -#define FTS_FORCE_CAL_SELF_MUTUAL 0x05 -#define FTS_FORCE_CAL_SELF 0x06 -#define FTS_WATER_MODE_ON 0x07 -#define FTS_WATER_MODE_OFF 0x08 +#define FTS_FLASH_WRITE_CONFIG 0x03 +#define FTS_FLASH_WRITE_COMP_MEMORY 0x04 +#define FTS_FORCE_CAL_SELF_MUTUAL 0x05 +#define FTS_FORCE_CAL_SELF 0x06 +#define FTS_WATER_MODE_ON 0x07 +#define FTS_WATER_MODE_OFF 0x08 + -#define EXP_FN_WORK_DELAY_MS 1000 +#define EXP_FN_WORK_DELAY_MS 1000 + +#define CMD_STR_LEN 32 -#define CMD_STR_LEN 32 #ifdef SCRIPTLESS /* * I2C Command Read/Write Function */ -#define CMD_RESULT_STR_LEN 2048 +#define CMD_RESULT_STR_LEN 2048 #endif -#define TSP_BUF_SIZE 4096 +#define TSP_BUF_SIZE 4096 + +/*add by guchong*/ +#ifdef PHONE_GESTURE +extern u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX]; +extern u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX]; +extern int gesture_coords_reported; +extern struct mutex gestureMask_mutex; +#endif struct fts_i2c_platform_data { int (*power)(bool on); @@ -119,21 +188,18 @@ struct fts_i2c_platform_data { int reset_gpio; const char *pwr_reg_name; const char *bus_reg_name; - }; /* * Forward declaration */ struct fts_ts_info; -extern char tag[8]; /* * Dispatch event handler */ -typedef unsigned char * (*event_dispatch_handler_t) -(struct fts_ts_info *info, unsigned char *data); - +typedef void (*event_dispatch_handler_t) + (struct fts_ts_info *info, unsigned char *data); /* * struct fts_ts_info - FTS capacitive touch screen device information * @dev: Pointer to the structure device @@ -141,109 +207,113 @@ typedef unsigned char * (*event_dispatch_handler_t) * @input_dev Input device structure * @work Work thread * @event_wq Event queue for work thread - * @cmd_done Asyncronous command notification * @event_dispatch_table Event dispatch table handlers - * @fw_version Firmware version * @attrs SysFS attributes - * @mode Device operating mode + * @mode Device operating mode (bitmask) * @touch_id Bitmask for touch id (mapped to input slots) - * @buttons Bitmask for buttons status + * @stylus_id Bitmask for tracking the stylus touches + * (mapped using the touchId) * @timer Timer when operating in polling mode - * @early_suspend Structure for early suspend functions * @power Power on/off routine + * @bdata HW info retrived from device tree + * @pwr_reg DVDD power regulator + * @bus_reg AVDD power regulator + * @resume_bit Indicate if screen off/on + * @fwupdate_stat Store the result of a fw update triggered by the host + * @notifier Used for be notified from a suspend/resume event + * @sensor_sleep true susped was called, false resume was called + * @wakelock Wake Lock struct + * @input_report_mutex mutex for handling the pressure of keys + * @series of switches to store the enabling status of a particular + * feature from the host */ - struct fts_ts_info { - struct device *dev; - struct i2c_client *client; - struct input_dev *input_dev; + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; - struct work_struct work; - struct workqueue_struct *event_wq; + struct work_struct work; + struct work_struct suspend_work; + struct work_struct resume_work; + struct workqueue_struct *event_wq; - struct delayed_work fwu_work; - struct workqueue_struct *fwu_workqueue; - struct completion cmd_done; + struct delayed_work fwu_work; + struct workqueue_struct *fwu_workqueue; + struct completion cmd_done; - event_dispatch_handler_t *event_dispatch_table; + event_dispatch_handler_t *event_dispatch_table; - unsigned int fw_version; - unsigned int config_id; + struct attribute_group attrs; - struct attribute_group attrs; + unsigned int mode; + unsigned long touch_id; +#ifdef STYLUS_MODE + unsigned long stylus_id; +#endif - unsigned int mode; - unsigned long touch_id; - unsigned int buttons; #ifdef FTS_USE_POLLING_MODE - struct hrtimer timer; + struct hrtimer timer; #endif + #ifdef SCRIPTLESS - /*I2C cmd*/ - struct device *i2c_cmd_dev; - char cmd_read_result[CMD_RESULT_STR_LEN]; - char cmd_wr_result[CMD_RESULT_STR_LEN]; - char cmd_write_result[20]; + /*I2C cmd*/ + struct device *i2c_cmd_dev; + char cmd_read_result[CMD_RESULT_STR_LEN]; + char cmd_wr_result[CMD_RESULT_STR_LEN]; + char cmd_write_result[20]; #endif #ifdef DRIVER_TEST - struct device *test_cmd_dev; + struct device *test_cmd_dev; #endif + int (*power)(bool on); - int (*power)(bool on); - - struct fts_i2c_platform_data *bdata; - struct regulator *pwr_reg; - struct regulator *bus_reg; - - bool fw_force; - int debug_enable; - - int resume_bit; - int fwupdate_stat; - int touch_debug; - - struct notifier_block notifier; - bool sensor_sleep; - bool stay_awake; - - /* input lock */ - struct mutex input_report_mutex; - - /* switches */ - int gesture_enabled; - int glove_enabled; - + struct fts_i2c_platform_data *bdata; + struct regulator *pwr_reg; + struct regulator *bus_reg; + + int resume_bit; + int fwupdate_stat; + + struct notifier_block notifier; + bool sensor_sleep; + struct wakeup_source wakeup_source; + + /* input lock */ + struct mutex input_report_mutex; + + /*switches for features*/ + int gesture_enabled; + int glove_enabled; + int charger_enabled; + int stylus_enabled; + int vr_enabled; + int cover_enabled; + int edge_rej_enabled; + int corner_rej_enabled; + int edge_palm_rej_enabled; }; -typedef enum { - ERR_ITO_NO_ERR, /* < 0 No ITO Error */ - ERR_ITO_PANEL_OPEN_FORCE, /* < 1 Panel Open Force */ - ERR_ITO_PANEL_OPEN_SENSE, /* < 2 Panel Open Sense */ - ERR_ITO_F2G, /* < 3 Force short to ground */ - ERR_ITO_S2G, /* < 4 Sense short to ground */ - ERR_ITO_F2VDD, /* < 5 Force short to VDD */ - ERR_ITO_S2VDD, /* < 6 Sense short to VDD */ - ERR_ITO_P2P_FORCE, /* < 7 Pin to Pin short (Force) */ - ERR_ITO_P2P_SENSE, /* < 8 Pin to Pin short (Sense) */ -} errItoSubTypes_t; +extern struct chipInfo ftsInfo; int fts_chip_powercycle(struct fts_ts_info *info); int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep); -int fts_get_fw_version(struct fts_ts_info *info); -extern unsigned int le_to_uint(const unsigned char *ptr); -extern unsigned int be_to_uint(const unsigned char *ptr); +/*int fts_get_fw_version(struct fts_ts_info *info);*/ +/*extern unsigned int le_to_uint(const unsigned char *ptr);*/ +/*extern unsigned int be_to_uint(const unsigned char *ptr);*/ extern int input_register_notifier_client(struct notifier_block *nb); extern int input_unregister_notifier_client(struct notifier_block *nb); #ifdef SCRIPTLESS -extern struct attribute_group i2c_cmd_attr_group; +extern struct attribute_group i2c_cmd_attr_group; #endif #ifdef DRIVER_TEST -extern struct attribute_group test_cmd_attr_group; +extern struct attribute_group test_cmd_attr_group; #endif + #endif + diff --git a/drivers/input/touchscreen/st/fts_driver_test.c b/drivers/input/touchscreen/st/fts_driver_test.c index 5c55d8e4ac8860d173e6c33234283f6032dc9516..e8fe81de29116bb4fc4b9dd69ad28fc2a4233d70 100644 --- a/drivers/input/touchscreen/st/fts_driver_test.c +++ b/drivers/input/touchscreen/st/fts_driver_test.c @@ -1,17 +1,22 @@ /* - -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* API used by Driver Test Apk * -* * -************************************************************************** -************************************************************************** - -*/ + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ #include #include @@ -27,6 +32,8 @@ #include #include #include +/*#include */ +#include #include #include @@ -43,64 +50,64 @@ #ifdef DRIVER_TEST -#define MAX_PARAMS 10 - -/* DEFINE COMMANDS TO TEST */ -#define CMD_READ 0x00 -#define CMD_WRITE 0x01 -#define CMD_READU16 0x02 -#define CMD_READB2 0x03 -#define CMD_READB2U16 0x04 -#define CMD_POLLFOREVENT 0x05 -#define CMD_SYSTEMRESET 0x06 -#define CMD_CLEANUP 0x07 -#define CMD_GETFORCELEN 0x08 -#define CMD_GETSENSELEN 0x09 -#define CMD_GETMSFRAME 0x0A -/* #define CMD_GETMSKEYFRAME 0x0B */ -#define CMD_GETSSFRAME 0x0C -#define CMD_REQCOMPDATA 0x0D -#define CMD_READCOMPDATAHEAD 0x0E -#define CMD_READMSCOMPDATA 0x0F -#define CMD_READSSCOMPDATA 0x10 -#define CMD_READGNCOMPDATA 0x11 -#define CMD_GETFWVER 0x12 -#define CMD_FLASHSTATUS 0x13 -#define CMD_FLASHUNLOCK 0x14 -#define CMD_READFWFILE 0x15 -#define CMD_FLASHPROCEDURE 0x16 -#define CMD_ITOTEST 0x17 -#define CMD_INITTEST 0x18 -#define CMD_MSRAWTEST 0x19 -#define CMD_MSINITDATATEST 0x1A -#define CMD_SSRAWTEST 0x1B -#define CMD_SSINITDATATEST 0x1C -#define CMD_MAINTEST 0x1D -#define CMD_POWERCYCLE 0x1E -#define CMD_FWWRITE 0x1F -#define CMD_READCHIPINFO 0x20 -#define CMD_REQFRAME 0x21 - -u32 *functionToTest; -int numberParam; +#define MAX_PARAMS 50 + +/*DEFINE COMMANDS TO TEST*/ +#define CMD_READ 0x00 +#define CMD_WRITE 0x01 +#define CMD_READU16 0x02 +#define CMD_READB2 0x03 +#define CMD_READB2U16 0x04 +#define CMD_POLLFOREVENT 0x05 +#define CMD_SYSTEMRESET 0x06 +#define CMD_CLEANUP 0x07 +#define CMD_GETFORCELEN 0x08 +#define CMD_GETSENSELEN 0x09 +#define CMD_GETMSFRAME 0x0A +/*#define CMD_GETMSKEYFRAME 0x0B*/ +#define CMD_GETSSFRAME 0x0C +#define CMD_REQCOMPDATA 0x0D +#define CMD_READCOMPDATAHEAD 0x0E +#define CMD_READMSCOMPDATA 0x0F +#define CMD_READSSCOMPDATA 0x10 +#define CMD_READGNCOMPDATA 0x11 +#define CMD_GETFWVER 0x12 +#define CMD_FLASHSTATUS 0x13 +#define CMD_FLASHUNLOCK 0x14 +#define CMD_READFWFILE 0x15 +#define CMD_FLASHPROCEDURE 0x16 +#define CMD_ITOTEST 0x17 +#define CMD_INITTEST 0x18 +#define CMD_MSRAWTEST 0x19 +#define CMD_MSINITDATATEST 0x1A +#define CMD_SSRAWTEST 0x1B +#define CMD_SSINITDATATEST 0x1C +#define CMD_MAINTEST 0x1D +#define CMD_POWERCYCLE 0x1E +#define CMD_FWWRITE 0x1F +#define CMD_READCHIPINFO 0x20 +#define CMD_REQFRAME 0x21 + +static char tag[8] = "[ FTS ]\0"; +static u32 functionToTest[MAX_PARAMS]; +static int numberParam; static ssize_t stm_driver_test_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) { + struct device_attribute *attr, const char *buf, size_t count) +{ int n; - char *p = (char *) buf; + char *p = (char *)buf; + int ret; - functionToTest = (u32 *) kmalloc(MAX_PARAMS * sizeof (u32), GFP_KERNEL); - if (functionToTest == NULL) { - logError(1, "%s impossible to allocate functionToTest!\n", tag); - return count; - } - memset(functionToTest, 0, MAX_PARAMS * sizeof (u32)); + memset(functionToTest, 0, MAX_PARAMS * sizeof(u32)); for (n = 0; n < (count + 1) / 3 && n < MAX_PARAMS; n++) { - sscanf(p, "%02X ", &functionToTest[n]); + ret = sscanf(p, "%02X ", &functionToTest[n]); + if (ret != 1) + return -EINVAL; p += 3; - logError(1, "%s functionToTest[%d] = %02X\n", tag, n, functionToTest[n]); - + logError(1, "%s functionToTest[%d] = %02X\n", tag, n, + functionToTest[n]); } numberParam = n; @@ -108,34 +115,37 @@ static ssize_t stm_driver_test_store(struct device *dev, return count; } -static ssize_t stm_driver_test_show(struct device *dev, struct device_attribute *attr, - char *buf) { +static ssize_t stm_driver_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ char buff[CMD_STR_LEN] = {0}; int res = -1, j, count; - /* int res2; */ int size = 6 * 2; - int temp, i, byteToRead; + int temp = 0; + int i; + int byteToRead = 0; u8 *readData = NULL; u8 *all_strbuff = NULL; - u8 *cmd; + u8 *cmd = NULL; - MutualSenseFrame frameMS; - SelfSenseFrame frameSS; + struct MutualSenseFrame frameMS; + struct SelfSenseFrame frameSS; - DataHeader dataHead; - MutualSenseData compData; - SelfSenseData comData; - GeneralData gnData; + struct DataHeader dataHead; + struct MutualSenseData compData; + struct SelfSenseData comData; + struct GeneralData gnData; u16 address; u16 fw_version; u16 config_id; - Firmware fw; + struct Firmware fw; - /* struct used for defining which test perform during the MP test */ + /*struct used for defining which test*/ + /*perform during the MP test*/ - TestToDo todoDefault; + struct TestToDo todoDefault; struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); @@ -181,495 +191,661 @@ static ssize_t stm_driver_test_show(struct device *dev, struct device_attribute todoDefault.SelfSenseCxTotal = 0; todoDefault.SelfSenseCxTotalAdj = 0; - if (numberParam >= 1 && functionToTest != NULL) { - res = fts_disableInterrupt(); - if (res < 0) { - logError(0, "%s stm_driver_test_show: ERROR %08X\n", tag, res); - res = (res | ERROR_DISABLE_INTER); - goto END; - } - switch (functionToTest[0]) { - case CMD_READ: - if (numberParam >= 4) { - temp = (int) functionToTest[1]; - if (numberParam == 4 + (temp - 1) && temp != 0) { - cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); - for (i = 0; i < temp; i++) { - cmd[i] = functionToTest[i + 2]; - } - byteToRead = functionToTest[i + 2]; - readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); - res = fts_readCmd(cmd, temp, readData, byteToRead); - size += (byteToRead * sizeof (u8))*2; - } else { - logError(1, "%s Wrong parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - break; - - case CMD_WRITE: - if (numberParam >= 3) { /* need to pass: cmdLength cmd[0] cmd[1] … cmd[cmdLength-1] */ - temp = (int) functionToTest[1]; - if (numberParam == 3 + (temp - 1) && temp != 0) { - cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); - for (i = 0; i < temp; i++) { - cmd[i] = functionToTest[i + 2]; - } - res = fts_writeCmd(cmd, temp); - } else { - logError(1, "%s Wrong parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + if (numberParam < 1) { + logError(1, "%s NO COMMAND SPECIFIED!!! ", tag); + logError(1, "do: 'echo [cmd_code] [args] > stm_fts_cmd' "); + logError(1, "before looking for result!\n"); + res = ERROR_OP_NOT_ALLOW; + goto END; + } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s %s: ERROR %08X\n", tag, __func__, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + switch (functionToTest[0]) { + case CMD_READ: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + /** + * need to pass:cmdLength + * cmd[0]cmd[1]…cmd[cmdLength-1] + * byteToRead + */ + temp = (int)functionToTest[1]; + if (numberParam == 4 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + byteToRead = functionToTest[i + 2]; + readData = (u8 *)kmalloc_array(byteToRead, sizeof(u8), + GFP_KERNEL); + res = fts_readCmd(cmd, temp, readData, byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; - case CMD_FWWRITE: - if (numberParam >= 3) { /* need to pass: cmdLength cmd[0] cmd[1] … cmd[cmdLength-1] */ - temp = (int) functionToTest[1]; - if (numberParam == 3 + (temp - 1) && temp != 0) { - cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); - for (i = 0; i < temp; i++) { - cmd[i] = functionToTest[i + 2]; - } - res = fts_writeFwCmd(cmd, temp); - } else { - logError(1, "%s Wrong parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + case CMD_WRITE: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + /** + * need to pass:cmdLength + * cmd[0] cmd[1]…cmd[cmdLength-1] + */ + temp = (int)functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + res = fts_writeCmd(cmd, temp); + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; - case CMD_READU16: - if (numberParam == 6) { /* need to pass: cmd addr[0] addr[1] byteToRead hasDummyByte */ - byteToRead = functionToTest[4]; - readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); - res = readCmdU16((u8) functionToTest[1], (u16) ((((u8) functionToTest[2] & 0x00FF) << 8) + ((u8) functionToTest[3] & 0x00FF)), readData, byteToRead, functionToTest[5]); - size += (byteToRead * sizeof (u8))*2; - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + case CMD_FWWRITE: + if (numberParam != 3) { + logError(1, "%s Wrong number parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + /** + * need to pass:cmdLength + * cmd[0] cmd[1]…cmd[cmdLength-1] + */ + temp = (int)functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + res = fts_writeFwCmd(cmd, temp); + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; - case CMD_READB2: - if (numberParam == 4) { /* need to pass: addr[0] addr[1] byteToRead */ - byteToRead = functionToTest[3]; - readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); - res = readB2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), readData, byteToRead); - size += (byteToRead * sizeof (u8))*2; - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + case CMD_READU16: + if (numberParam != 6) { + logError(1, "%s Wrong number parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_READB2U16: - if (numberParam == 4) { /* need to pass: addr[0] addr[1] byteToRead */ - byteToRead = functionToTest[3]; - readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); - res = readB2U16((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), readData, byteToRead); - size += (byteToRead * sizeof (u8))*2; - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + } + /** + * need to pass: cmd addr[0] addr[1] + * byteToRead hasDummyByte + */ + byteToRead = functionToTest[4]; + readData = kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + res = readCmdU16((u8)functionToTest[1], + (u16)((((u8) functionToTest[2] + & 0x00FF) << 8) + ((u8) functionToTest[3] + & 0x00FF)), + readData, + byteToRead, + functionToTest[5]); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_READB2: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_POLLFOREVENT: - if (numberParam >= 5) { /* need to pass: eventLength event[0] event[1] event[eventLength-1] timeTowait */ - temp = (int) functionToTest[1]; - if (numberParam == 5 + (temp - 1) && temp != 0) { - readData = (u8 *) kmalloc(FIFO_EVENT_SIZE * sizeof (u8), GFP_KERNEL); - res = pollForEvent((int *) &functionToTest[2], temp, readData, ((functionToTest[temp + 2] & 0x00FF) << 8)+(functionToTest[temp + 3] & 0x00FF)); - if (res >= OK) - res = OK; /* pollForEvent return the number of error found */ - size += (FIFO_EVENT_SIZE * sizeof (u8))*2; - byteToRead = FIFO_EVENT_SIZE; - } else { - logError(1, "%s Wrong parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + } + /*need to pass: addr[0] addr[1] byteToRead*/ + byteToRead = functionToTest[3]; + readData = kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + res = readB2((u16)( + (((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8) functionToTest[2] & 0x00FF)), + readData, + byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_READB2U16: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_SYSTEMRESET: - res = fts_system_reset(); - + } + /*need to pass: addr[0] addr[1] byteToRead*/ + byteToRead = functionToTest[3]; + readData = (u8 *)kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + res = readB2U16((u16)((((u8)functionToTest[1] + & 0x00FF) << 8) + ((u8)functionToTest[2] + & 0x00FF)), readData, byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_POLLFOREVENT: + if (numberParam < 5) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } - case CMD_READCHIPINFO: - if (numberParam == 2) { /* need to pass: doRequest */ - res = readChipInfo(functionToTest[1]); - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - - break; + /** + * need to pass: eventLength event[0] event[1] + * … event[eventLength-1] timeTowait + */ + temp = (int)functionToTest[1]; + if (numberParam == 5 + (temp - 1) && temp != 0) { + readData = (u8 *)kmalloc_array(FIFO_EVENT_SIZE, + sizeof(u8), GFP_KERNEL); + res = pollForEvent((int *)&functionToTest[2], + temp, + readData, + ((functionToTest[temp + 2] & 0x00FF) << 8) + + (functionToTest[temp + 3] & 0x00FF)); + //pollForEvent return the number of error found + if (res >= OK) + res = OK; + size += (FIFO_EVENT_SIZE * sizeof(u8)) * 2; + byteToRead = FIFO_EVENT_SIZE; + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; - case CMD_CLEANUP: /* TOUCH ENABLE/DISABLE */ - if (numberParam == 2) { /* need to pass: enableTouch */ - res = cleanUp(functionToTest[1]); - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + case CMD_SYSTEMRESET: + res = fts_system_reset(); + break; + case CMD_READCHIPINFO: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_GETFORCELEN: /* read number Tx channels */ - temp = getForceLen(); - if (temp < OK) - res = temp; - else { - size += (1 * sizeof (u8))*2; - res = OK; - } + } + /*need to pass: doRequest */ + res = readChipInfo(functionToTest[1]); + break; + + /* TOUCH ENABLE/DISABLE */ + case CMD_CLEANUP: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + /* need to pass: enableTouch*/ + res = cleanUp(functionToTest[1]); + break; + + case CMD_GETFORCELEN: + /*read number Tx channels */ + temp = getForceLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)) * 2; + res = OK; + } + break; + + case CMD_GETSENSELEN: + /* read number Rx channels */ + temp = getSenseLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)) * 2; + res = OK; + } + break; - case CMD_GETSENSELEN: /* read number Rx channels */ - temp = getSenseLen(); - if (temp < OK) - res = temp; - else { - size += (1 * sizeof (u8))*2; - res = OK; - } + case CMD_REQFRAME: + /* request a frame */ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + logError(0, "%s Requesting Frame\n", tag); + res = requestFrame((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, "%s Err requesting frame ERROR:%02X\n", + tag, res); + } else { + logError(0, "%s Requesting Frame Finished!\n", tag); + } + break; - case CMD_REQFRAME: /* request a frame */ - if (numberParam == 3) { - logError(0, "%s Requesting Frame\n", tag); - res = requestFrame((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); - - if (res < OK) { - logError(0, "%s Error requesting frame ERROR %02X\n", tag, res); - } else { - logError(0, "%s Requesting Frame Finished!\n", tag); - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - break; - - case CMD_GETMSFRAME: - if (numberParam == 3) { - logError(0, "%s Get 1 MS Frame\n", tag); - flushFIFO(); /* delete the events related to some touch (allow to call this function while touching the sreen without having a flooding of the FIFO) */ - res = getMSFrame2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &frameMS); - if (res < 0) { - logError(0, "%s Error while taking the MS frame... ERROR %02X\n", tag, res); - - } else { - logError(0, "%s The frame size is %d words\n", tag, res); - size = (res * sizeof (short) + 8)*2; - /* set res to OK because if getMSFrame is - successful res = number of words read - */ - res = OK; - print_frame_short("MS frame =", array1dTo2d_short(frameMS.node_data, frameMS.node_data_size, frameMS.header.sense_node), frameMS.header.force_node, frameMS.header.sense_node); - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + case CMD_GETMSFRAME: + if (numberParam != 3) { + logError(1, "%s Wrong number of param!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + logError(0, "%s Get 1 MS Frame\n", tag); + flushFIFO(); + /** + * delete the events related to some + * touch (allow to call this function + * while touching the sreen without + * having a flooding of the FIFO) + */ + res = getMSFrame2((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), &frameMS); + if (res < 0) { + logError(0, "%s Err while taking MS frame:%02X\n", + tag, res); + } else { + logError(0, "%s:frame size is %d words\n", tag, res); + size = (res * sizeof(short) + 8) * 2; + /*set res to OK because if getMSFrame is*/ + /*successful res = number of words read*/ + res = OK; + print_frame_short("MS frame =", + array1dTo2d_short(frameMS.node_data, + frameMS.node_data_size, + frameMS.header.sense_node), + frameMS.header.force_node, + frameMS.header.sense_node); + } + break; - /*read self raw*/ - case CMD_GETSSFRAME: - if (numberParam == 3) { - logError(0, "%s Get 1 SS Frame\n", tag); - flushFIFO(); /* delete the events related to some touch (allow to call this function while touching the sreen without having a flooding of the FIFO) */ - res = getSSFrame2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &frameSS); - - if (res < OK) { - logError(0, "%s Error while taking the SS frame... ERROR %02X\n", tag, res); - - } else { - logError(0, "%s The frame size is %d words\n", tag, res); - size = (res * sizeof (short) + 8)*2+1; - /* set res to OK because if getMSFrame is - successful res = number of words read - */ - res = OK; - print_frame_short("SS force frame =", array1dTo2d_short(frameSS.force_data, frameSS.header.force_node, 1), frameSS.header.force_node, 1); - print_frame_short("SS sense frame =", array1dTo2d_short(frameSS.sense_data, frameSS.header.sense_node, frameSS.header.sense_node), 1, frameSS.header.sense_node); - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + /*read self raw*/ + case CMD_GETSSFRAME: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } - case CMD_REQCOMPDATA: /* request comp data */ - if (numberParam == 3) { - logError(0, "%s Requesting Compensation Data\n", tag); - res = requestCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); - - if (res < OK) { - logError(0, "%s Error requesting compensation data ERROR %02X\n", tag, res); - } else { - logError(0, "%s Requesting Compensation Data Finished!\n", tag); - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - break; + logError(0, "%s Get 1 SS Frame\n", tag); + flushFIFO(); + /** + * delete the events related to some + * touch (allow to call this function + * while touching the sreen without + * having a flooding of the FIFO) + */ + res = getSSFrame2((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), &frameSS); + + if (res < OK) { + logError(0, + "%s Error while taking the SS frame... ERROR %02X\n", + tag, res); + } else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2 + 1; + + /*set res to OK because if getMSFrame is*/ + /*successful res = number of words read*/ + res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short(frameSS.force_data, + frameSS.header.force_node, 1), + frameSS.header.force_node, + 1); + print_frame_short("SS sense frame =", + array1dTo2d_short(frameSS.sense_data, + frameSS.header.sense_node, + frameSS.header.sense_node), + 1, + frameSS.header.sense_node); + } + break; - case CMD_READCOMPDATAHEAD: /* read comp data header */ - if (numberParam == 3) { - logError(0, "%s Requesting Compensation Data\n", tag); - res = requestCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); - if (res < OK) { - logError(0, "%s Error requesting compensation data ERROR %02X\n", tag, res); - } else { - logError(0, "%s Requesting Compensation Data Finished!\n", tag); - res = readCompensationDataHeader((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &dataHead, &address); - if (res < OK) { - logError(0, "%s Read Compensation Data Header ERROR %02X\n", tag, res); - } else { - logError(0, "%s Read Compensation Data Header OK!\n", tag); - size += (2 * sizeof (u8))*2; - } - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + case CMD_REQCOMPDATA: + /*request comp data*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } - case CMD_READMSCOMPDATA: /* read mutual comp data */ - if (numberParam == 3) { - logError(0, "%s Get MS Compensation Data\n", tag); - res = readMutualSenseCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &compData); + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData((u16) + ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, + "%s Error requesting compensation data ERROR %02X\n", + tag, res); + } else { + logError(0, + "%s Requesting Compensation Data Finished!\n", + tag); + } + break; - if (res < OK) { - logError(0, "%s Error reading MS compensation data ERROR %02X\n", tag, res); - } else { - logError(0, "%s MS Compensation Data Reading Finished!\n", tag); - size = ((compData.node_data_size + 9) * sizeof (u8))*2; - print_frame_u8("MS Data (Cx2) =", array1dTo2d_u8(compData.node_data, compData.node_data_size, compData.header.sense_node), compData.header.force_node, compData.header.sense_node); - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + case CMD_READCOMPDATAHEAD: + /*read comp data header*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } - case CMD_READSSCOMPDATA: - if (numberParam == 3) { /* read self comp data */ - logError(0, "%s Get SS Compensation Data...\n", tag); - res = readSelfSenseCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &comData); - if (res < OK) { - logError(0, "%s Error reading SS compensation data ERROR %02X\n", tag, res); - } else { - logError(0, "%s SS Compensation Data Reading Finished!\n", tag); - size = ((comData.header.force_node + comData.header.sense_node)*2 + 12) * sizeof (u8)*2; - print_frame_u8("SS Data Ix2_fm = ", array1dTo2d_u8(comData.ix2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); - print_frame_u8("SS Data Cx2_fm = ", array1dTo2d_u8(comData.cx2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); - print_frame_u8("SS Data Ix2_sn = ", array1dTo2d_u8(comData.ix2_sn, comData.header.sense_node, comData.header.sense_node), 1, comData.header.sense_node); - print_frame_u8("SS Data Cx2_sn = ", array1dTo2d_u8(comData.cx2_sn, comData.header.sense_node, comData.header.sense_node), 1, comData.header.sense_node); - } + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData( + (u16) ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + if (res < OK) { + logError(0, "%s Error requesting:%02X\n", tag, res); + } else { + logError(0, + "%s Requesting Compensation Data Finished!\n", tag); + res = readCompensationDataHeader( + (u16)((((u8)functionToTest[1] & 0x00FF) << 8) + +((u8)functionToTest[2] & 0x00FF)), + &dataHead, + &address); + if (res < OK) { + logError(0, "%s Read Header ERROR:%02X\n", + tag, res); } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; + logError(0, "%s Read Header OK!\n", tag); + size += (2 * sizeof(u8)) * 2; } + } + break; + case CMD_READMSCOMPDATA: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_READGNCOMPDATA: - if (numberParam == 3) { /* read self comp data */ - logError(0, "%s Get General Compensation Data...\n", tag); - res = readGeneralCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &gnData); - if (res < OK) { - logError(0, "%s Error reading General compensation data ERROR %02X\n", tag, res); - } else { - logError(0, "%s General Compensation Data Reading Finished!\n", tag); - size = (14) * sizeof (u8)*2; - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + } + /*read mutual comp data */ + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData( + (u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), + &compData); + + if (res < OK) { + logError(0, "%s Error reading MS compe data:%02X\n", + tag, res); + } else { + logError(0, "%s MS Compensa Reading Finished!\n", + tag); + + size = ((compData.node_data_size + 9) * sizeof(u8)) * 2; + print_frame_u8("MS Data (Cx2) = ", + array1dTo2d_u8(compData.node_data, + compData.node_data_size, + compData.header.sense_node), + compData.header.force_node, + compData.header.sense_node); + } + break; + case CMD_READSSCOMPDATA: + if (numberParam != 3) { + logError(1, "%sWrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } - case CMD_GETFWVER: - res = getFirmwareVersion(&fw_version, &config_id); - if (res < OK) { - logError(1, "%s Error reading firmware version and config id ERROR %02X\n", tag, res); - } else { - logError(0, "%s getFirmwareVersion Finished!\n", tag); - size += (4) * sizeof (u8)*2; - } + /*read self comp data*/ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData((u16) + ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), + &comData); + if (res < OK) { + logError(0, "%s Error reading SS Compensa data %02X\n", + tag, res); + } else { + logError(0, "%s SS Compensa Reading Finished!\n", tag); + size = comData.header.force_node + + comData.header.sense_node; + size = (size * 2 + 12) * sizeof(u8) * 2; + print_frame_u8("SS Data Ix2_fm = ", + array1dTo2d_u8(comData.ix2_fm, + comData.header.force_node, + comData.header.force_node), + 1, + comData.header.force_node); + print_frame_u8("SS Data Cx2_fm = ", + array1dTo2d_u8(comData.cx2_fm, + comData.header.force_node, + comData.header.force_node), + 1, + comData.header.force_node); + print_frame_u8("SS Data Ix2_sn = ", + array1dTo2d_u8(comData.ix2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", + array1dTo2d_u8(comData.cx2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + } + break; + + case CMD_READGNCOMPDATA: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + /*read self comp data */ + logError(0, "%s Get General Compensation Data...\n", tag); + res = readGeneralCompensationData((u16) + ((((u8)functionToTest[1] + & 0x00FF) << 8) + ((u8)functionToTest[2] + & 0x00FF)), &gnData); + if (res < OK) { + logError(0, + "%s Reading General compensa data ERROR %02X\n", + tag, res); + } else { + logError(0, "%s:General compensa Reading Finished!\n", + tag); + size = (14) * sizeof(u8) * 2; + } + break; + case CMD_GETFWVER: + res = getFirmwareVersion(&fw_version, &config_id); + if (res < OK) { + logError(1, "%s Reading firmware version ERROR %02X\n", + tag, res); + } else { + logError(0, "%s getFirmware Version Finished!\n", tag); + size += (4) * sizeof(u8) * 2; + } + break; #ifdef FTM3_CHIP - case CMD_FLASHSTATUS: - res = flash_status(); /* return 0 = flash ready, 1 = flash busy, <0 error */ - if (res < OK) { - logError(1, "%s Error reading flash status ERROR %02X\n", tag, res); - } else { - logError(0, "%s Flash Status: %d\n", tag, res); - size += (1 * sizeof (u8))*2; - temp = res; /* need to store the value for further display */ - res = OK; /* set res =ok for returning code */ - } - break; + case CMD_FLASHSTATUS: + res = flash_status(); + /*return 0 = flash ready, 1 = flash busy, <0 error*/ + if (res < OK) { + logError(1, "%s Reading flash status ERROR %02X\n", + tag, res); + } else { + logError(0, "%s Flash Status: %d\n", tag, res); + size += (1 * sizeof(u8)) * 2; + /*need to store the value for further display */ + temp = res; + + /*set res =ok for returning code*/ + res = OK; + } + break; #endif - - case CMD_FLASHUNLOCK: - res = flash_unlock(); - if (res < OK) { - logError(1, "%s Impossible Unlock Flash ERROR %02X\n", tag, res); - } else { - logError(0, "%s Flash Unlock OK!\n", tag); - } + case CMD_FLASHUNLOCK: + res = flash_unlock(); + if (res < OK) { + logError(1, "%s:Impossible Unlock Flash ERROR %02X\n", + tag, res); + } else { + logError(0, "%s Flash Unlock OK!\n", tag); + } + break; + case CMD_READFWFILE: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_READFWFILE: - if (numberParam == 2) { /* read fw file */ - logError(0, "%s Reading FW File...\n", tag); - res = readFwFile(PATH_FILE_FW, &fw, functionToTest[1]); - if (res < OK) { - logError(0, "%s Error reading FW File ERROR %02X\n", tag, res); - } else { - logError(0, "%s Read FW File Finished!\n", tag); - } + } + /*read fw file */ + logError(0, "%s Reading FW File...\n", tag); + res = readFwFile(PATH_FILE_FW, &fw, functionToTest[1]); + if (res < OK) { + logError(0, "%s Error reading FW File:%02X\n", + tag, res); + } else { + logError(0, "%s Read FW File Finished!\n", tag); + } kfree(fw.data); - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - break; - - case CMD_FLASHPROCEDURE: - if (numberParam == 3) { /* flashing procedure */ - logError(0, "%s Starting Flashing Procedure...\n", tag); - res = flashProcedure(PATH_FILE_FW, functionToTest[1], functionToTest[2]); - if (res < OK) { - logError(0, "%s Error during flash procedure ERROR %02X\n", tag, res); - } else { - logError(0, "%s Flash Procedure Finished!\n", tag); - } - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - break; - - /*ITO TEST*/ - case CMD_ITOTEST: - res = production_test_ito(); - break; - - /*Initialization*/ - case CMD_INITTEST: - if (numberParam == 2) { /* need to specify if if save value on Flash */ - if (functionToTest[1] == 0x01) - res = production_test_initialization(); - else - res = production_test_splited_initialization(false); - } else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + break; + case CMD_FLASHPROCEDURE: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; + } + /*flashing procedure*/ + logError(0, "%s Starting Flashing Procedure\n", tag); + res = flashProcedure(PATH_FILE_FW, + functionToTest[1], functionToTest[2]); + if (res < OK) { + logError(0, "%s During flash procedure ERROR %02X", + tag, res); + } else { + logError(0, "%s Flash Procedure Finished!\n", tag); + } + break; - case CMD_MSRAWTEST: /* MS Raw DATA TEST */ - if (numberParam == 2) /* need to specify if stopOnFail */ - res = production_test_ms_raw(LIMITS_FILE, functionToTest[1], &todoDefault); - else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } - break; + /*ITO TEST*/ + case CMD_ITOTEST: + res = production_test_ito(); + break; - case CMD_MSINITDATATEST: /* MS CX DATA TEST */ - if (numberParam == 2) /* need to specify if stopOnFail */ - res = production_test_ms_cx(LIMITS_FILE, functionToTest[1], &todoDefault); - else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + /*Initialization*/ + case CMD_INITTEST: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_SSRAWTEST: /* SS RAW DATA TEST */ - if (numberParam == 2) /* need to specify if stopOnFail */ - res = production_test_ss_raw(LIMITS_FILE, functionToTest[1], &todoDefault); - else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + } + /*need to specify if if save value on Flash*/ + if (functionToTest[1] == 0x01) + res = production_test_initialization(); + else + res = production_test_split_initialization(false); + break; + + case CMD_MSRAWTEST: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_SSINITDATATEST: /* SS IX CX DATA TEST */ - if (numberParam == 2) /* need to specify if stopOnFail */ - res = production_test_ss_ix_cx(LIMITS_FILE, functionToTest[1], &todoDefault); - else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + } + /* MS Raw DATA TEST*/ + /* need to specify if stopOnFail */ + res = production_test_ms_raw(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_MSINITDATATEST: + /*MS CX DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - /*PRODUCTION TEST*/ - case CMD_MAINTEST: - if (numberParam == 3) /* need to specify if stopOnFail and saveInit */ - res = production_test_main(LIMITS_FILE, functionToTest[1], functionToTest[2], &todoDefault, INIT_FIELD); - else { - logError(1, "%s Wrong number of parameters!\n", tag); - res = ERROR_OP_NOT_ALLOW; - } + } + /*need to specify if stopOnFail*/ + res = production_test_ms_cx(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_SSRAWTEST: + /*SS RAW DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - case CMD_POWERCYCLE: - res = fts_chip_powercycle(info); + } + /*need to specify if stopOnFail*/ + res = production_test_ss_raw(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_SSINITDATATEST: + /*SS IX CX DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; break; - - default: - logError(1, "%s COMMAND ID NOT VALID!! Inset a value between 00 and 1E..\n", tag); + } + /*need to specify if stopOnFail*/ + res = production_test_ss_ix_cx(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_MAINTEST: + /*PRODUCTION TEST*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); res = ERROR_OP_NOT_ALLOW; break; } - - /*res2 = fts_enableInterrupt(); enabling the interrupt was disabled on purpose in this node because it can be used for testing procedure and between one step and another the interrupt wan to be kept disabled - if (res2 < 0) { - logError(0, "%s stm_driver_test_show: ERROR %08X\n", tag, (res2 | ERROR_ENABLE_INTER)); - }*/ - } else { - logError(1, "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", tag); + /*need to specify if stopOnFail and saveInit*/ + res = production_test_main(LIMITS_FILE, functionToTest[1], + functionToTest[2], &todoDefault, INIT_FIELD); + break; + + case CMD_POWERCYCLE: + res = fts_chip_powercycle(info); + break; + + default: + logError(1, "%s COMMAND ID NOT VALID!!\n", tag); + logError(1, "%s Inset a value between 00 and 1E.\n", tag); res = ERROR_OP_NOT_ALLOW; - functionToTest = NULL; + break; } -END: /* here start the reporting phase, assembling the data to send in the file node */ - all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); +END: + /** + * here start the reporting phase, + * assembling the data to send in the file node + */ + all_strbuff = kmalloc(size, GFP_KERNEL); memset(all_strbuff, 0, size); - snprintf(buff, sizeof (buff), "%02X", 0xAA); + snprintf(buff, sizeof(buff), "%02X", 0xAA); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%08X", res); + snprintf(buff, sizeof(buff), "%08X", res); strlcat(all_strbuff, buff, size); if (res >= OK) { - /*all the other cases are already fine printing only the res.*/ + /*all the other cases are already*/ + /*fine printing only the res.*/ switch (functionToTest[0]) { case CMD_READ: case CMD_READU16: @@ -677,7 +853,8 @@ static ssize_t stm_driver_test_show(struct device *dev, struct device_attribute case CMD_READB2U16: case CMD_POLLFOREVENT: for (j = 0; j < byteToRead; j++) { - snprintf(buff, sizeof (buff), "%02X", readData[j]); + snprintf(buff, sizeof(buff), "%02X", + readData[j]); strlcat(all_strbuff, buff, size); } break; @@ -685,108 +862,121 @@ static ssize_t stm_driver_test_show(struct device *dev, struct device_attribute case CMD_GETFORCELEN: case CMD_GETSENSELEN: case CMD_FLASHSTATUS: - snprintf(buff, sizeof (buff), "%02X", (u8) temp); + snprintf(buff, sizeof(buff), "%02X", (u8)temp); strlcat(all_strbuff, buff, size); break; case CMD_GETMSFRAME: - snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.force_node); + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.force_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.sense_node); + snprintf(buff, sizeof(buff), "%02X", + (u8)frameMS.header.sense_node); strlcat(all_strbuff, buff, size); for (j = 0; j < frameMS.node_data_size; j++) { - snprintf(buff, sizeof (buff), "%04X", frameMS.node_data[j]); + snprintf(buff, sizeof(buff), "%04X", + frameMS.node_data[j]); strlcat(all_strbuff, buff, size); } - kfree(frameMS.node_data); break; case CMD_GETSSFRAME: - snprintf(buff, sizeof (buff), "%02X", (u8) frameSS.header.force_node); + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.force_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", (u8) frameSS.header.sense_node); + snprintf(buff, sizeof(buff), "%02X", + (u8)frameSS.header.sense_node); strlcat(all_strbuff, buff, size); /* Copying self raw data Force */ for (j = 0; j < frameSS.header.force_node; j++) { - snprintf(buff, sizeof (buff), "%04X", frameSS.force_data[j]); + snprintf(buff, sizeof(buff), "%04X", + frameSS.force_data[j]); strlcat(all_strbuff, buff, size); } + /* Copying self raw data Sense */ for (j = 0; j < frameSS.header.sense_node; j++) { - snprintf(buff, sizeof (buff), "%04X", frameSS.sense_data[j]); + snprintf(buff, sizeof(buff), "%04X", + frameSS.sense_data[j]); strlcat(all_strbuff, buff, size); } - kfree(frameSS.force_data); kfree(frameSS.sense_data); break; case CMD_READMSCOMPDATA: - snprintf(buff, sizeof (buff), "%02X", (u8) compData.header.force_node); + snprintf(buff, sizeof(buff), "%02X", + (u8)compData.header.force_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", (u8) compData.header.sense_node); + snprintf(buff, sizeof(buff), "%02X", + (u8)compData.header.sense_node); strlcat(all_strbuff, buff, size); /* Cpying CX1 value */ - snprintf(buff, sizeof (buff), "%02X", compData.cx1); + snprintf(buff, sizeof(buff), "%02X", + compData.cx1); strlcat(all_strbuff, buff, size); /* Copying CX2 values */ for (j = 0; j < compData.node_data_size; j++) { - snprintf(buff, sizeof (buff), "%02X", *(compData.node_data + j)); + snprintf(buff, sizeof(buff), "%02X", + *(compData.node_data + j)); strlcat(all_strbuff, buff, size); } - kfree(compData.node_data); break; case CMD_READSSCOMPDATA: - snprintf(buff, sizeof (buff), "%02X", comData.header.force_node); + snprintf(buff, sizeof(buff), "%02X", + comData.header.force_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", comData.header.sense_node); + snprintf(buff, sizeof(buff), "%02X", + comData.header.sense_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", comData.f_ix1); + snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", comData.s_ix1); + snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", comData.f_cx1); + snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", comData.s_cx1); + snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); strlcat(all_strbuff, buff, size); /* Copying IX2 Force */ for (j = 0; j < comData.header.force_node; j++) { - snprintf(buff, sizeof (buff), "%02X", comData.ix2_fm[j]); + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_fm[j]); strlcat(all_strbuff, buff, size); } - - /* Copying IX2 Sense */ + /* Copying IX2 Sense*/ for (j = 0; j < comData.header.sense_node; j++) { - snprintf(buff, sizeof (buff), "%02X", comData.ix2_sn[j]); + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_sn[j]); strlcat(all_strbuff, buff, size); } - /* Copying CX2 Force */ for (j = 0; j < comData.header.force_node; j++) { - snprintf(buff, sizeof (buff), "%02X", comData.cx2_fm[j]); + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_fm[j]); strlcat(all_strbuff, buff, size); } /* Copying CX2 Sense */ for (j = 0; j < comData.header.sense_node; j++) { - snprintf(buff, sizeof (buff), "%02X", comData.cx2_sn[j]); + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_sn[j]); strlcat(all_strbuff, buff, size); } @@ -797,67 +987,83 @@ static ssize_t stm_driver_test_show(struct device *dev, struct device_attribute break; case CMD_READGNCOMPDATA: - snprintf(buff, sizeof (buff), "%02X", gnData.header.force_node); + snprintf(buff, sizeof(buff), "%02X", + gnData.header.force_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", gnData.header.sense_node); + snprintf(buff, sizeof(buff), "%02X", + gnData.header.sense_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal0); + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal0); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal1); + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal1); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal2); + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal2); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal3); + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal3); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", gnData.ftsa_lp_timer_cal0); + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsa_lp_timer_cal0); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", gnData.ftsa_lp_timer_cal1); + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsa_lp_timer_cal1); strlcat(all_strbuff, buff, size); break; case CMD_GETFWVER: - snprintf(buff, sizeof (buff), "%04X", fw_version); + snprintf(buff, sizeof(buff), "%04X", fw_version); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%04X", config_id); + snprintf(buff, sizeof(buff), "%04X", config_id); strlcat(all_strbuff, buff, size); break; case CMD_READCOMPDATAHEAD: - snprintf(buff, sizeof (buff), "%02X", dataHead.force_node); + snprintf(buff, sizeof(buff), "%02X", + dataHead.force_node); strlcat(all_strbuff, buff, size); - snprintf(buff, sizeof (buff), "%02X", dataHead.sense_node); + snprintf(buff, sizeof(buff), "%02X", + dataHead.sense_node); strlcat(all_strbuff, buff, size); break; default: break; - } } - snprintf(buff, sizeof (buff), "%02X", 0xBB); + snprintf(buff, sizeof(buff), "%02X", 0xBB); strlcat(all_strbuff, buff, size); count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); - numberParam = 0; /* need to reset the number of parameters in order to wait the next comand, comment if you want to repeat the last comand sent just doing a cat */ - /* logError(0,"%s numberParameters = %d\n",tag, numberParam); */ + numberParam = 0; + /** + * need to reset the number of parameters + * in order to wait the next command, + * comment if you want to repeat + * the last command sent just doing a cat + */ + + kfree(readData); kfree(all_strbuff); - kfree(functionToTest); return count; - } -/*static DEVICE_ATTR(stm_driver_test, (S_IRWXU|S_IRWXG), stm_driver_test_show, stm_driver_test_store);*/ -static DEVICE_ATTR(stm_driver_test, (S_IRUGO | S_IWUSR | S_IWGRP), stm_driver_test_show, stm_driver_test_store); + +static DEVICE_ATTR(stm_driver_test, 0664, stm_driver_test_show, + stm_driver_test_store); + static struct attribute *test_cmd_attributes[] = { &dev_attr_stm_driver_test.attr, @@ -867,5 +1073,4 @@ static struct attribute *test_cmd_attributes[] = { struct attribute_group test_cmd_attr_group = { .attrs = test_cmd_attributes, }; - #endif diff --git a/drivers/input/touchscreen/st/fts_fw.h b/drivers/input/touchscreen/st/fts_fw.h index d928a06951ea7a639a913908b3c6df6c34803e90..51e65f0fe978b4a17681becc7570f376ab676618 100644 --- a/drivers/input/touchscreen/st/fts_fw.h +++ b/drivers/input/touchscreen/st/fts_fw.h @@ -1,10 +1,6532 @@ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * fts firmware * + * * + * * + ************************************************************************** + ************************************************************************** + */ + #ifndef FTS_FW_H #define FTS_FW_H -/* This is an auto generated header file -* --->Remember to change the name of the two variables!<--- */ -const uint32_t myArray_size; +//This is an auto generated header file +//--->Remember to change the name of the two variables!<--- +const uint32_t myArray_size = 77860; //size const uint8_t myArray[] = { + 0x55, 0xAA, 0x55, 0xAA, 0x01, 0x00, 0x00, 0x00, 0x36, 0x70, 0x00, 0x00, + 0x00, 0x39, 0x01, 0x30, 0x49, 0xB5, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x09, 0x20, 0x15, 0x01, 0x00, 0x00, 0xE0, 0x27, 0x01, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9C, 0x79, 0x24, 0xAC, 0x0B, 0xBF, 0x2B, 0xDA, 0xF7, 0x49, 0x00, 0x00, + 0xF4, 0x40, 0xD4, 0x25, 0x08, 0xB6, 0xFF, 0xFF, 0x00, 0x22, 0x10, 0x00, + 0xE1, 0x00, 0x00, 0x00, 0xD9, 0xE0, 0x00, 0x00, 0xD9, 0x01, 0x00, 0x00, + 0x25, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0xBD, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xDD, 0xE0, 0x00, 0x00, 0xE5, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0xED, 0xE0, 0x00, 0x00, + 0xF5, 0xE0, 0x00, 0x00, 0x03, 0xE1, 0x00, 0x00, 0x11, 0xE1, 0x00, 0x00, + 0x1F, 0xE1, 0x00, 0x00, 0x2D, 0xE1, 0x00, 0x00, 0x3B, 0xE1, 0x00, 0x00, + 0x43, 0xE1, 0x00, 0x00, 0x51, 0xE1, 0x00, 0x00, 0xA5, 0xE1, 0x00, 0x00, + 0x17, 0x03, 0x00, 0x00, 0x5F, 0xE1, 0x00, 0x00, 0x67, 0xE1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0xE1, 0x00, 0x00, 0xA7, 0xE1, 0x00, 0x00, + 0xA9, 0xE1, 0x00, 0x00, 0xB7, 0xE1, 0x00, 0x00, 0xB9, 0xE1, 0x00, 0x00, + 0xBB, 0xE1, 0x00, 0x00, 0x95, 0xE1, 0x00, 0x00, 0x9D, 0xE1, 0x00, 0x00, + 0x85, 0xE1, 0x00, 0x00, 0x8D, 0xE1, 0x00, 0x00, 0xBD, 0xE1, 0x00, 0x00, + 0xBF, 0xE1, 0x00, 0x00, 0xCD, 0xE1, 0x00, 0x00, 0x6F, 0xE1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB5, 0x00, 0x00, + 0x00, 0x39, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x02, 0xF8, 0x00, 0xF0, 0x68, 0xF8, + 0x0A, 0xA0, 0x90, 0xE8, 0x00, 0x0C, 0x82, 0x44, 0x83, 0x44, 0xAA, 0xF1, + 0x01, 0x07, 0xDA, 0x45, 0x01, 0xD1, 0x00, 0xF0, 0x5D, 0xF8, 0xAF, 0xF2, + 0x09, 0x0E, 0xBA, 0xE8, 0x0F, 0x00, 0x13, 0xF0, 0x01, 0x0F, 0x18, 0xBF, + 0xFB, 0x1A, 0x43, 0xF0, 0x01, 0x03, 0x18, 0x47, 0x8C, 0x25, 0x01, 0x00, + 0xBC, 0x25, 0x01, 0x00, 0x0A, 0x44, 0x4F, 0xF0, 0x00, 0x0C, 0x10, 0xF8, + 0x01, 0x3B, 0x13, 0xF0, 0x07, 0x04, 0x08, 0xBF, 0x10, 0xF8, 0x01, 0x4B, + 0x1D, 0x11, 0x08, 0xBF, 0x10, 0xF8, 0x01, 0x5B, 0x64, 0x1E, 0x05, 0xD0, + 0x10, 0xF8, 0x01, 0x6B, 0x64, 0x1E, 0x01, 0xF8, 0x01, 0x6B, 0xF9, 0xD1, + 0x13, 0xF0, 0x08, 0x0F, 0x1E, 0xBF, 0x10, 0xF8, 0x01, 0x4B, 0xAD, 0x1C, + 0x0C, 0x1B, 0x09, 0xD1, 0x6D, 0x1E, 0x58, 0xBF, 0x01, 0xF8, 0x01, 0xCB, + 0xFA, 0xD5, 0x05, 0xE0, 0x14, 0xF8, 0x01, 0x6B, 0x01, 0xF8, 0x01, 0x6B, + 0x6D, 0x1E, 0xF9, 0xD5, 0x91, 0x42, 0xD6, 0xD3, 0x70, 0x47, 0x00, 0x00, + 0x10, 0x3A, 0x24, 0xBF, 0x78, 0xC8, 0x78, 0xC1, 0xFA, 0xD8, 0x52, 0x07, + 0x24, 0xBF, 0x30, 0xC8, 0x30, 0xC1, 0x44, 0xBF, 0x04, 0x68, 0x0C, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, + 0x10, 0x3A, 0x28, 0xBF, 0x78, 0xC1, 0xFB, 0xD8, 0x52, 0x07, 0x28, 0xBF, + 0x30, 0xC1, 0x48, 0xBF, 0x0B, 0x60, 0x70, 0x47, 0x1F, 0xB5, 0x1F, 0xBD, + 0x10, 0xB5, 0x10, 0xBD, 0xDF, 0xF8, 0x0C, 0xD0, 0xFF, 0xF7, 0xF8, 0xFF, + 0x0E, 0xF0, 0xE8, 0xF8, 0x0F, 0xF0, 0x51, 0xFD, 0x00, 0x22, 0x10, 0x00, + 0x03, 0xB4, 0xFF, 0xF7, 0xF1, 0xFF, 0x03, 0xBC, 0x0F, 0xF0, 0x50, 0xFD, + 0x11, 0x48, 0x4F, 0xF0, 0x0F, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, + 0x01, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, 0x02, 0x01, 0x00, 0xF8, + 0x01, 0x1B, 0x4F, 0xF0, 0x00, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, + 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, + 0x01, 0x1B, 0x1E, 0xF0, 0x04, 0x0F, 0x0C, 0xBF, 0xEF, 0xF3, 0x08, 0x80, + 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, 0x02, 0x01, 0x0B, 0xF0, 0x5A, 0xBD, + 0x00, 0x02, 0x00, 0x20, 0x11, 0x48, 0x4F, 0xF0, 0x0F, 0x01, 0x00, 0xF8, + 0x01, 0x1B, 0x4F, 0xF0, 0x01, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, + 0x03, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, 0x00, 0x01, 0x00, 0xF8, + 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, + 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x1E, 0xF0, 0x04, 0x0F, 0x0C, 0xBF, + 0xEF, 0xF3, 0x08, 0x80, 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, 0x03, 0x01, + 0x0B, 0xF0, 0x34, 0xBD, 0x00, 0x02, 0x00, 0x20, 0x11, 0x48, 0x4F, 0xF0, + 0x0F, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, 0x01, 0x01, 0x00, 0xF8, + 0x01, 0x1B, 0x4F, 0xF0, 0x04, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, + 0x00, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, + 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x1E, 0xF0, + 0x04, 0x0F, 0x0C, 0xBF, 0xEF, 0xF3, 0x08, 0x80, 0xEF, 0xF3, 0x09, 0x80, + 0x4F, 0xF0, 0x04, 0x01, 0x0B, 0xF0, 0x0E, 0xBD, 0x00, 0x02, 0x00, 0x20, + 0x11, 0x48, 0x4F, 0xF0, 0x0F, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, + 0x01, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x4F, 0xF0, 0x05, 0x01, 0x00, 0xF8, + 0x01, 0x1B, 0x4F, 0xF0, 0x00, 0x01, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, + 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, 0x01, 0x1B, 0x00, 0xF8, + 0x01, 0x1B, 0x1E, 0xF0, 0x04, 0x0F, 0x0C, 0xBF, 0xEF, 0xF3, 0x08, 0x80, + 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, 0x05, 0x01, 0x0B, 0xF0, 0xE8, 0xBC, + 0x00, 0x02, 0x00, 0x20, 0x00, 0xB5, 0x0D, 0xF0, 0xD7, 0xFE, 0x5D, 0xF8, + 0x04, 0xEB, 0x00, 0xF0, 0x2F, 0xB8, 0x1E, 0xF0, 0x04, 0x0F, 0x0C, 0xBF, + 0xEF, 0xF3, 0x08, 0x80, 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, 0x00, 0x01, + 0x0B, 0xF0, 0xD4, 0xBC, 0x0A, 0x48, 0x4F, 0xF0, 0xFF, 0x01, 0x01, 0x70, + 0x4F, 0xF0, 0x00, 0x00, 0x80, 0xF3, 0x11, 0x88, 0x4F, 0xF0, 0x00, 0x00, + 0x80, 0xF3, 0x09, 0x88, 0x05, 0x48, 0x4F, 0xF0, 0x02, 0x01, 0x01, 0x70, + 0x62, 0xB6, 0x04, 0x48, 0x4F, 0xF0, 0x80, 0x51, 0x01, 0x60, 0x70, 0x47, + 0x22, 0xED, 0x00, 0xE0, 0x44, 0x25, 0x10, 0x00, 0x04, 0xED, 0x00, 0xE0, + 0x02, 0x48, 0x4F, 0xF0, 0x80, 0x51, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x04, 0xED, 0x00, 0xE0, 0x4F, 0xF0, 0x20, 0x00, 0x80, 0xF3, 0x11, 0x88, + 0xEF, 0xF3, 0x09, 0x80, 0x98, 0xB1, 0x12, 0x48, 0x00, 0x68, 0x12, 0x49, + 0x09, 0x68, 0x88, 0x42, 0x17, 0xD0, 0xEF, 0xF3, 0x09, 0x80, 0x0E, 0x49, + 0x09, 0x68, 0x20, 0xE9, 0xF0, 0x0F, 0x88, 0x60, 0x00, 0xB5, 0x0B, 0x48, + 0x00, 0x68, 0x0E, 0xF0, 0xE2, 0xF8, 0x5D, 0xF8, 0x04, 0xEB, 0x08, 0x48, + 0x08, 0x49, 0x0A, 0x68, 0x02, 0x60, 0x00, 0x68, 0x81, 0x68, 0xB1, 0xE8, + 0xF0, 0x0F, 0x81, 0xF3, 0x09, 0x88, 0x4E, 0xF0, 0x04, 0x0E, 0x4F, 0xF0, + 0x00, 0x00, 0x80, 0xF3, 0x11, 0x88, 0x70, 0x47, 0x48, 0x25, 0x10, 0x00, + 0x4C, 0x25, 0x10, 0x00, 0xEF, 0xF3, 0x11, 0x80, 0x4F, 0xF0, 0x20, 0x01, + 0x81, 0xF3, 0x11, 0x88, 0x70, 0x47, 0x00, 0x00, 0x4F, 0xF0, 0x00, 0x00, + 0x80, 0xF3, 0x11, 0x88, 0x70, 0x47, 0x00, 0x00, 0x62, 0xB6, 0x70, 0x47, + 0x72, 0xB6, 0x70, 0x47, 0x40, 0xBA, 0x70, 0x47, 0xC0, 0xBA, 0x70, 0x47, + 0x72, 0xB6, 0x78, 0x46, 0x00, 0xF1, 0x0A, 0x00, 0x4D, 0xF8, 0x08, 0x0C, + 0x62, 0xB6, 0x30, 0xBF, 0x00, 0xBF, 0x70, 0x47, 0x10, 0xB5, 0x01, 0x46, + 0x44, 0x22, 0xA5, 0x48, 0x0F, 0xF0, 0x2B, 0xFB, 0x01, 0x21, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x20, 0x0E, 0xF0, 0xD0, 0xB8, 0xF8, 0xB5, 0x06, 0x46, + 0xA0, 0x48, 0x9F, 0x4D, 0x03, 0x24, 0x30, 0x60, 0x30, 0x46, 0x0D, 0xF0, + 0xC9, 0xF8, 0x00, 0x22, 0x6B, 0x46, 0x01, 0x21, 0x10, 0x46, 0x0E, 0xF0, + 0x11, 0xF9, 0x01, 0x21, 0x00, 0x20, 0x0E, 0xF0, 0xEC, 0xF8, 0x95, 0xF8, + 0x42, 0x00, 0x00, 0x28, 0x04, 0xD0, 0x0B, 0xF0, 0x6A, 0xFC, 0x64, 0x1E, + 0x00, 0x2C, 0xE9, 0xDC, 0xF8, 0xBD, 0x2D, 0xE9, 0xF0, 0x47, 0xDF, 0xF8, + 0x4C, 0x92, 0x80, 0x46, 0x01, 0x27, 0xD9, 0xF8, 0x00, 0x00, 0x91, 0x4C, + 0x45, 0x7D, 0x01, 0x7D, 0x90, 0xF8, 0x41, 0x00, 0x4D, 0x43, 0xC0, 0xF3, + 0x81, 0x00, 0x87, 0x40, 0xA9, 0x00, 0x20, 0x46, 0x0F, 0xF0, 0x4F, 0xFB, + 0x00, 0x26, 0xDF, 0xF8, 0x1C, 0xA2, 0x12, 0xE0, 0x85, 0x48, 0x44, 0x30, + 0xFF, 0xF7, 0xC6, 0xFF, 0x00, 0x20, 0xDA, 0xF8, 0x00, 0x10, 0x07, 0xE0, + 0x54, 0xF8, 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, 0x1A, 0x44, 0x44, 0xF8, + 0x20, 0x20, 0x40, 0x1C, 0xA8, 0x42, 0xF5, 0xDB, 0x76, 0x1C, 0xBE, 0x42, + 0xEA, 0xDB, 0x00, 0x20, 0x4B, 0x46, 0x0A, 0xE0, 0x1A, 0x68, 0x54, 0xF8, + 0x20, 0x10, 0x92, 0xF8, 0x41, 0x20, 0xC2, 0xF3, 0x81, 0x02, 0x11, 0x41, + 0x28, 0xF8, 0x10, 0x10, 0x40, 0x1C, 0xA8, 0x42, 0xF2, 0xDB, 0xD9, 0xF8, + 0x00, 0x00, 0x90, 0xF8, 0x30, 0x11, 0x89, 0x07, 0x07, 0xD5, 0x02, 0x7D, + 0x41, 0x7D, 0x40, 0x46, 0xBD, 0xE8, 0xF0, 0x47, 0x71, 0x4B, 0x0C, 0xF0, + 0xB5, 0xBA, 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xF0, 0x43, 0x6C, 0x4F, + 0x6C, 0x4C, 0x01, 0x26, 0x38, 0x68, 0x9F, 0xB0, 0x90, 0xF8, 0x41, 0x10, + 0xC1, 0xF3, 0x01, 0x12, 0x41, 0x7D, 0x00, 0x7D, 0x06, 0xFA, 0x02, 0xF9, + 0x04, 0xEB, 0x81, 0x05, 0x08, 0x44, 0x81, 0x00, 0x20, 0x46, 0x0F, 0xF0, + 0x00, 0xFB, 0x60, 0x48, 0x44, 0x30, 0x0D, 0xF0, 0x16, 0xF8, 0x5E, 0x48, + 0x44, 0x30, 0x06, 0x72, 0x00, 0x26, 0x34, 0xE0, 0x5B, 0x48, 0x44, 0x30, + 0xFF, 0xF7, 0x72, 0xFF, 0x38, 0x68, 0xDF, 0xF8, 0x64, 0x81, 0x40, 0x7D, + 0xD8, 0xF8, 0x18, 0x10, 0x42, 0x00, 0x10, 0xA8, 0x0F, 0xF0, 0x48, 0xFA, + 0x38, 0x68, 0xD8, 0xF8, 0x1C, 0x10, 0x00, 0x7D, 0x42, 0x00, 0x68, 0x46, + 0x0F, 0xF0, 0x40, 0xFA, 0x00, 0x20, 0x10, 0xA9, 0x07, 0xE0, 0x54, 0xF8, + 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, 0x1A, 0x44, 0x44, 0xF8, 0x20, 0x20, + 0x40, 0x1C, 0x3A, 0x68, 0x52, 0x7D, 0x82, 0x42, 0xF3, 0xDC, 0x00, 0x20, + 0x69, 0x46, 0x07, 0xE0, 0x55, 0xF8, 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, + 0x1A, 0x44, 0x45, 0xF8, 0x20, 0x20, 0x40, 0x1C, 0x3A, 0x68, 0x12, 0x7D, + 0x82, 0x42, 0xF3, 0xDC, 0x76, 0x1C, 0x4E, 0x45, 0xC8, 0xDB, 0x00, 0x20, + 0x44, 0x4B, 0x39, 0x68, 0x09, 0xE0, 0x91, 0xF8, 0x41, 0x60, 0x54, 0xF8, + 0x20, 0x20, 0xC6, 0xF3, 0x01, 0x16, 0x32, 0x41, 0x23, 0xF8, 0x10, 0x20, + 0x40, 0x1C, 0x4A, 0x7D, 0x82, 0x42, 0xF2, 0xDC, 0x00, 0x20, 0x3D, 0x4B, + 0x09, 0xE0, 0x91, 0xF8, 0x41, 0x40, 0x55, 0xF8, 0x20, 0x20, 0xC4, 0xF3, + 0x01, 0x14, 0x22, 0x41, 0x23, 0xF8, 0x10, 0x20, 0x40, 0x1C, 0x0A, 0x7D, + 0x82, 0x42, 0xF2, 0xDC, 0x1F, 0xB0, 0xBD, 0xE8, 0xF0, 0x83, 0x2D, 0xE9, + 0xF0, 0x41, 0x04, 0x46, 0x00, 0x20, 0x0E, 0xF0, 0x96, 0xF8, 0x02, 0x20, + 0x0E, 0xF0, 0x93, 0xF8, 0x29, 0x48, 0x44, 0x30, 0x0C, 0xF0, 0xA9, 0xFF, + 0x2E, 0x49, 0x08, 0x78, 0x02, 0x28, 0x01, 0xD0, 0x03, 0x28, 0x01, 0xD1, + 0x04, 0x20, 0x08, 0x70, 0x25, 0x4D, 0x28, 0x68, 0x90, 0xF8, 0x40, 0x00, + 0x80, 0x07, 0x01, 0xD4, 0x24, 0xF0, 0x01, 0x04, 0x1F, 0x4F, 0xE0, 0x07, + 0x4F, 0xF0, 0x01, 0x08, 0x07, 0xF1, 0x44, 0x07, 0x16, 0xD0, 0x38, 0x46, + 0x0C, 0xF0, 0x8F, 0xFF, 0xA0, 0x06, 0x15, 0xD5, 0x87, 0xF8, 0x08, 0x80, + 0x28, 0x68, 0x00, 0x25, 0x90, 0xF8, 0x41, 0x00, 0x00, 0xF0, 0x03, 0x00, + 0x08, 0xFA, 0x00, 0xF6, 0x04, 0xE0, 0x14, 0x48, 0x44, 0x30, 0xFF, 0xF7, + 0xE3, 0xFE, 0x6D, 0x1C, 0xB5, 0x42, 0xF8, 0xDB, 0xA0, 0x06, 0x01, 0xD5, + 0xFF, 0xF7, 0x47, 0xFF, 0xA0, 0x07, 0x08, 0xD5, 0x0D, 0x48, 0x44, 0x30, + 0x0C, 0xF0, 0x71, 0xFF, 0x87, 0xF8, 0x04, 0x80, 0x12, 0x48, 0xFF, 0xF7, + 0xEE, 0xFE, 0x60, 0x07, 0x08, 0xD5, 0x08, 0x48, 0x44, 0x30, 0x0C, 0xF0, + 0x66, 0xFF, 0x03, 0x20, 0x38, 0x71, 0x0D, 0x48, 0xFF, 0xF7, 0xE3, 0xFE, + 0x00, 0x20, 0x0E, 0xF0, 0x5F, 0xF8, 0xBD, 0xE8, 0xF0, 0x41, 0x02, 0x20, + 0x0E, 0xF0, 0x5A, 0xB8, 0xDC, 0x25, 0x10, 0x00, 0x15, 0x04, 0x00, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x60, 0x54, 0x10, 0x00, 0x48, 0x53, 0x10, 0x00, + 0xFC, 0x22, 0x01, 0x20, 0x38, 0x23, 0x01, 0x20, 0x60, 0x24, 0x10, 0x00, + 0x7C, 0x1B, 0x01, 0x20, 0x10, 0xB5, 0x16, 0x4C, 0x40, 0xF2, 0xFF, 0x11, + 0x20, 0x68, 0x11, 0xF0, 0xFD, 0xFA, 0x21, 0x68, 0xC1, 0xF8, 0xFC, 0x07, + 0x00, 0x21, 0x3F, 0x20, 0x0F, 0xF1, 0x88, 0xFA, 0x21, 0x68, 0xBD, 0xE8, + 0x10, 0x40, 0x3F, 0x23, 0x4F, 0xF4, 0x00, 0x72, 0x0D, 0x48, 0x10, 0xF0, + 0x1D, 0xBE, 0x10, 0xB5, 0x40, 0xF2, 0xFF, 0x31, 0x0B, 0x48, 0x11, 0xF0, + 0xE7, 0xFA, 0x0A, 0x49, 0xC1, 0xF8, 0xFC, 0x0F, 0x01, 0x21, 0x00, 0x20, + 0x0F, 0xF1, 0x72, 0xFA, 0x00, 0x23, 0x4F, 0xF4, 0x80, 0x62, 0xBD, 0xE8, + 0x10, 0x40, 0x04, 0x49, 0x10, 0x02, 0x10, 0xF0, 0x07, 0xBE, 0x00, 0x00, + 0x50, 0x24, 0x10, 0x00, 0xF0, 0xEF, 0x03, 0x00, 0x00, 0x10, 0x10, 0x00, + 0xFE, 0x49, 0x08, 0x70, 0x4F, 0xF4, 0x00, 0x51, 0x00, 0x20, 0x0D, 0xF0, + 0x41, 0xBF, 0x0D, 0xF0, 0x95, 0xB8, 0x30, 0xB5, 0xFA, 0x49, 0x0A, 0x68, + 0x92, 0xF8, 0xB2, 0x34, 0x02, 0x78, 0x63, 0xF3, 0x01, 0x02, 0x02, 0x70, + 0x0B, 0x68, 0x93, 0xF8, 0xB2, 0x34, 0x9B, 0x08, 0x63, 0xF3, 0x82, 0x02, + 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xB2, 0x34, 0xDB, 0x08, 0x63, 0xF3, + 0xC3, 0x02, 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xB2, 0x34, 0x1B, 0x09, + 0x63, 0xF3, 0x04, 0x12, 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xB2, 0x34, + 0x5B, 0x09, 0x63, 0xF3, 0x45, 0x12, 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, + 0xB2, 0x34, 0x9B, 0x09, 0x63, 0xF3, 0x86, 0x12, 0x02, 0x70, 0x0B, 0x68, + 0x93, 0xF8, 0xB2, 0x34, 0xDB, 0x09, 0x63, 0xF3, 0xC7, 0x12, 0x02, 0x70, + 0x0A, 0x68, 0x92, 0xF8, 0xAC, 0x34, 0x42, 0x78, 0x63, 0xF3, 0x01, 0x02, + 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xAC, 0x34, 0x9B, 0x08, 0x63, 0xF3, + 0x82, 0x02, 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xAC, 0x34, 0xDB, 0x08, + 0x63, 0xF3, 0xC3, 0x02, 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xAC, 0x34, + 0x1B, 0x09, 0x63, 0xF3, 0x04, 0x12, 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, + 0xAC, 0x34, 0x5B, 0x09, 0x63, 0xF3, 0x45, 0x12, 0x42, 0x70, 0x0B, 0x68, + 0x93, 0xF8, 0xAC, 0x34, 0x9B, 0x09, 0x63, 0xF3, 0x86, 0x12, 0x42, 0x70, + 0x0B, 0x68, 0x93, 0xF8, 0xAC, 0x34, 0xDB, 0x09, 0x63, 0xF3, 0xC7, 0x12, + 0x42, 0x70, 0x0A, 0x68, 0x92, 0xF8, 0xC0, 0x24, 0x93, 0x08, 0x82, 0x78, + 0x63, 0xF3, 0x04, 0x12, 0x82, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xC0, 0x34, + 0xDB, 0x08, 0x63, 0xF3, 0x45, 0x12, 0x82, 0x70, 0x0B, 0x68, 0x93, 0xF8, + 0xC2, 0x34, 0x1B, 0x09, 0x63, 0xF3, 0x87, 0x12, 0x82, 0x70, 0x0B, 0x68, + 0x93, 0xF8, 0xC3, 0x34, 0x1C, 0x09, 0xC3, 0x78, 0x64, 0xF3, 0x01, 0x03, + 0xC3, 0x70, 0x0C, 0x68, 0x94, 0xF8, 0xC1, 0x54, 0xC5, 0x71, 0x94, 0xF8, + 0xC0, 0x44, 0x64, 0x08, 0x64, 0xF3, 0xC3, 0x02, 0x82, 0x70, 0x0A, 0x68, + 0x92, 0xF8, 0xC2, 0x44, 0x04, 0xF0, 0x0F, 0x04, 0x04, 0x71, 0x92, 0xF8, + 0xC3, 0x24, 0x62, 0xF3, 0x84, 0x03, 0xC3, 0x70, 0x42, 0x79, 0x22, 0xF0, + 0x7F, 0x02, 0x42, 0x71, 0x4F, 0xF6, 0x05, 0x22, 0x82, 0x81, 0x09, 0x68, + 0xB1, 0xF8, 0xC8, 0x24, 0xC2, 0x81, 0xB1, 0xF8, 0xCA, 0x24, 0x02, 0x82, + 0xB1, 0xF8, 0xCC, 0x24, 0x42, 0x82, 0xB1, 0xF8, 0xCE, 0x24, 0x82, 0x82, + 0xB1, 0xF8, 0xD0, 0x24, 0xC2, 0x82, 0xB1, 0xF8, 0xD2, 0x24, 0x02, 0x83, + 0xB1, 0xF8, 0xD4, 0x24, 0x42, 0x83, 0xB1, 0xF8, 0xD6, 0x24, 0x82, 0x83, + 0xB1, 0xF8, 0xC4, 0x24, 0x02, 0x81, 0xB1, 0xF8, 0xC6, 0x14, 0x41, 0x81, + 0x30, 0xBD, 0x70, 0xB5, 0x8E, 0xB0, 0x01, 0xA8, 0xFF, 0xF7, 0x3D, 0xFF, + 0x01, 0xA8, 0x10, 0xF0, 0x49, 0xF8, 0x97, 0x4C, 0x00, 0x25, 0x00, 0x21, + 0x25, 0x70, 0x25, 0x63, 0x65, 0x63, 0xA5, 0x63, 0xE5, 0x63, 0x25, 0x64, + 0x65, 0x64, 0xA5, 0x64, 0xC4, 0xE9, 0x14, 0x55, 0xC4, 0xE9, 0x16, 0x55, + 0xC4, 0xE9, 0x18, 0x55, 0xC4, 0xE9, 0x1A, 0x51, 0xC4, 0xE9, 0x1C, 0x11, + 0xC4, 0xE9, 0x1E, 0x11, 0x04, 0xF1, 0x80, 0x00, 0xC0, 0xE9, 0x00, 0x11, + 0x65, 0x60, 0xE5, 0x61, 0x25, 0x62, 0x65, 0x62, 0xA5, 0x62, 0xE5, 0x62, + 0x0C, 0xF0, 0xC3, 0xFF, 0x09, 0x90, 0xFF, 0xF7, 0x12, 0xFF, 0xCD, 0xE9, + 0x0A, 0x01, 0x04, 0x21, 0x09, 0xA8, 0x11, 0xF0, 0xB7, 0xF9, 0x06, 0x46, + 0x08, 0x21, 0x0A, 0xA8, 0x11, 0xF0, 0xB2, 0xF9, 0x00, 0xEB, 0x46, 0x01, + 0xC4, 0xE9, 0x03, 0x16, 0xC4, 0xE9, 0x05, 0x60, 0x4F, 0xF4, 0x00, 0x61, + 0x7C, 0x48, 0x0F, 0xF0, 0xEE, 0xF8, 0x4F, 0xF4, 0xF0, 0x61, 0x7B, 0x48, + 0x0F, 0xF0, 0xC7, 0xF8, 0x00, 0xF0, 0x02, 0xFD, 0xB8, 0xB9, 0x00, 0x22, + 0x4F, 0xF4, 0x00, 0x56, 0x0C, 0xAB, 0x31, 0x46, 0x10, 0x46, 0x0D, 0xF0, + 0x7D, 0xFE, 0x31, 0x46, 0x00, 0x20, 0x0D, 0xF0, 0x58, 0xFE, 0x20, 0x78, + 0x48, 0xB1, 0x0B, 0xF0, 0xD8, 0xF9, 0x00, 0x22, 0x0F, 0x21, 0x05, 0x20, + 0x0B, 0xF0, 0xE8, 0xFA, 0x00, 0x20, 0x0E, 0xB0, 0x70, 0xBD, 0x10, 0xF0, + 0x87, 0xF8, 0x09, 0xAC, 0x00, 0x95, 0x94, 0xE8, 0x0E, 0x00, 0x6A, 0x48, + 0x0F, 0xF0, 0x40, 0xFA, 0x69, 0x48, 0x0F, 0xF0, 0x57, 0xFA, 0x68, 0x48, + 0x3A, 0x30, 0x0F, 0xF0, 0x93, 0xFA, 0x66, 0x48, 0x20, 0x38, 0x0F, 0xF0, + 0x53, 0xFA, 0x64, 0x48, 0x18, 0x30, 0x0F, 0xF0, 0x8F, 0xFA, 0x01, 0x20, + 0xE3, 0xE7, 0x2D, 0xE9, 0xF0, 0x41, 0x0D, 0x46, 0x06, 0x46, 0x4F, 0xF0, + 0xFF, 0x34, 0x0C, 0xF0, 0x68, 0xFF, 0x03, 0x46, 0x00, 0x20, 0xDF, 0xF8, + 0x5C, 0xC1, 0x02, 0x46, 0x01, 0x27, 0x07, 0xFA, 0x02, 0xF1, 0x19, 0x42, + 0x00, 0xD0, 0x64, 0x1C, 0xB4, 0x42, 0x06, 0xD1, 0x01, 0x2D, 0x09, 0xD0, + 0x23, 0xEA, 0x01, 0x00, 0xCC, 0xF8, 0x08, 0x20, 0x1E, 0x22, 0x52, 0x1C, + 0x1E, 0x2A, 0xEE, 0xD9, 0xBD, 0xE8, 0xF0, 0x81, 0x08, 0x46, 0xF5, 0xE7, + 0x2D, 0xE9, 0xF0, 0x5F, 0x8A, 0x46, 0x83, 0x46, 0x4F, 0xF0, 0xFF, 0x35, + 0xFF, 0xF7, 0x97, 0xFE, 0x00, 0x26, 0x81, 0x46, 0x88, 0x46, 0x37, 0x46, + 0x34, 0x46, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x0E, 0xF0, 0xA7, 0xFF, + 0x02, 0x46, 0x0B, 0x46, 0x00, 0xEA, 0x09, 0x00, 0x01, 0xEA, 0x08, 0x01, + 0x08, 0x43, 0x00, 0xD0, 0x6D, 0x1C, 0x5D, 0x45, 0x09, 0xD1, 0xBA, 0xF1, + 0x01, 0x0F, 0x0D, 0xD0, 0x29, 0xEA, 0x02, 0x06, 0x28, 0xEA, 0x03, 0x07, + 0x38, 0x48, 0x84, 0x60, 0x20, 0x24, 0x64, 0x1C, 0x20, 0x2C, 0xE2, 0xD3, + 0x30, 0x46, 0x39, 0x46, 0xBD, 0xE8, 0xF0, 0x9F, 0x16, 0x46, 0x1F, 0x46, + 0xF2, 0xE7, 0x2D, 0xE9, 0xF0, 0x41, 0x17, 0x46, 0x04, 0x46, 0x0D, 0x46, + 0x00, 0x26, 0x0A, 0xE0, 0xE0, 0x07, 0x04, 0xD0, 0x32, 0x46, 0x39, 0x46, + 0x05, 0x20, 0x0B, 0xF0, 0x69, 0xFA, 0x76, 0x1C, 0x6D, 0x08, 0x4F, 0xEA, + 0x34, 0x04, 0x54, 0xEA, 0x05, 0x00, 0xF1, 0xD1, 0xB4, 0xE7, 0x2D, 0xE9, + 0xF0, 0x5F, 0x26, 0x4C, 0x60, 0x68, 0x00, 0x28, 0x7E, 0xD0, 0xD4, 0xE9, + 0x0D, 0x12, 0x8A, 0x43, 0xA2, 0x63, 0x20, 0x6B, 0x23, 0x6C, 0x88, 0x43, + 0xD4, 0xE9, 0x15, 0x87, 0x98, 0x43, 0xD4, 0xE9, 0x1C, 0x63, 0x25, 0x6D, + 0xD4, 0xF8, 0x5C, 0xC0, 0xBD, 0x43, 0x28, 0xEA, 0x0C, 0x08, 0x90, 0x43, + 0xB5, 0x43, 0x28, 0xEA, 0x03, 0x08, 0x20, 0x63, 0xD4, 0xE9, 0x18, 0x36, + 0x9D, 0x43, 0x28, 0xEA, 0x06, 0x08, 0xC4, 0xE9, 0x14, 0x58, 0xD4, 0xF8, + 0x48, 0xA0, 0x04, 0xF1, 0x80, 0x05, 0x2A, 0xEA, 0x01, 0x0A, 0xD5, 0xE9, + 0x00, 0x89, 0x2A, 0xEA, 0x02, 0x0A, 0xE2, 0x6B, 0x28, 0xEA, 0x07, 0x08, + 0x2A, 0xEA, 0x02, 0x0A, 0x29, 0xEA, 0x0C, 0x09, 0x28, 0xEA, 0x03, 0x08, + 0x29, 0xEA, 0x06, 0x09, 0xC4, 0xF8, 0x48, 0xA0, 0xD4, 0xE9, 0x1A, 0x63, + 0x28, 0xEA, 0x06, 0x08, 0x29, 0xEA, 0x03, 0x09, 0x91, 0x43, 0xC5, 0xE9, + 0x00, 0x89, 0x61, 0x63, 0x01, 0x22, 0x00, 0x21, 0xFF, 0xF7, 0xA1, 0xFF, + 0xD4, 0xE9, 0x14, 0x01, 0x02, 0x22, 0xFF, 0xF7, 0x9C, 0xFF, 0x0B, 0xE0, + 0x08, 0x22, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, + 0x38, 0xA9, 0x01, 0x20, 0x80, 0x13, 0x10, 0x00, 0x54, 0x17, 0x10, 0x00, + 0x03, 0x22, 0x00, 0x21, 0x60, 0x6B, 0xFF, 0xF7, 0x8A, 0xFF, 0xD4, 0xE9, + 0x16, 0x01, 0x04, 0x22, 0xFF, 0xF7, 0x85, 0xFF, 0x05, 0x22, 0x00, 0x21, + 0x20, 0x6C, 0xFF, 0xF7, 0x80, 0xFF, 0xD4, 0xE9, 0x1C, 0x01, 0x06, 0x22, + 0xFF, 0xF7, 0x7B, 0xFF, 0x07, 0x22, 0x00, 0x21, 0xE0, 0x6B, 0xFF, 0xF7, + 0x76, 0xFF, 0xD4, 0xE9, 0x1A, 0x01, 0x08, 0x22, 0xFF, 0xF7, 0x71, 0xFF, + 0x09, 0x22, 0x00, 0x21, 0x60, 0x6C, 0xFF, 0xF7, 0x6C, 0xFF, 0xD4, 0xE9, + 0x1E, 0x01, 0x0A, 0x22, 0xFF, 0xF7, 0x67, 0xFF, 0x0B, 0x22, 0x00, 0xE0, + 0x09, 0xE0, 0x00, 0x21, 0xA0, 0x6C, 0xFF, 0xF7, 0x60, 0xFF, 0xD5, 0xE9, + 0x00, 0x01, 0xBD, 0xE8, 0xF0, 0x5F, 0x0C, 0x22, 0x59, 0xE7, 0xBD, 0xE8, + 0xF0, 0x5F, 0x00, 0x22, 0x11, 0x46, 0x05, 0x20, 0x0B, 0xF0, 0xCA, 0xB9, + 0x2D, 0xE9, 0xF0, 0x41, 0x16, 0x46, 0x1F, 0x46, 0x80, 0x46, 0x4F, 0xF0, + 0xFF, 0x35, 0x00, 0x24, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x0E, 0xF0, + 0xCE, 0xFE, 0x30, 0x40, 0x39, 0x40, 0x08, 0x43, 0x04, 0xD0, 0x6D, 0x1C, + 0x45, 0x45, 0x01, 0xD1, 0x20, 0x46, 0x07, 0xE7, 0x64, 0x1C, 0x40, 0x2C, + 0xEE, 0xD3, 0x00, 0x20, 0x02, 0xE7, 0x70, 0xB5, 0xFE, 0x4E, 0x01, 0x25, + 0xA6, 0xF1, 0x80, 0x04, 0x0D, 0x28, 0x11, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, + 0xA2, 0x76, 0x80, 0x07, 0x11, 0x23, 0x2D, 0x3F, 0x49, 0x5A, 0x64, 0x91, + 0x96, 0x00, 0x00, 0x23, 0x08, 0x46, 0xE2, 0x6C, 0xFF, 0xF7, 0xD0, 0xFF, + 0x85, 0x40, 0x60, 0x6B, 0x05, 0x43, 0x65, 0x63, 0x90, 0xE0, 0xD6, 0xE9, + 0x02, 0x23, 0x08, 0x46, 0xFF, 0xF7, 0xC6, 0xFF, 0x01, 0x23, 0x02, 0x46, + 0x00, 0x21, 0x18, 0x46, 0x0E, 0xF0, 0x9B, 0xFE, 0xD4, 0xE9, 0x16, 0x23, + 0x10, 0x43, 0x19, 0x43, 0xC4, 0xE9, 0x16, 0x01, 0x7E, 0xE0, 0x00, 0x23, + 0x08, 0x46, 0xE2, 0x6C, 0xFF, 0xF7, 0xB4, 0xFF, 0x85, 0x40, 0x20, 0x6C, + 0x05, 0x43, 0x25, 0x64, 0x74, 0xE0, 0xD6, 0xE9, 0x02, 0x23, 0x08, 0x46, + 0xFF, 0xF7, 0xAA, 0xFF, 0x01, 0x23, 0x02, 0x46, 0x00, 0x21, 0x18, 0x46, + 0x0E, 0xF0, 0x7F, 0xFE, 0xD4, 0xE9, 0x1C, 0x23, 0x10, 0x43, 0x19, 0x43, + 0xC4, 0xE9, 0x1C, 0x01, 0x62, 0xE0, 0x00, 0x23, 0x08, 0x46, 0xE2, 0x6C, + 0xFF, 0xF7, 0x98, 0xFF, 0x85, 0x40, 0xE0, 0x6B, 0x05, 0x43, 0xE5, 0x63, + 0x58, 0xE0, 0xD6, 0xE9, 0x02, 0x23, 0x08, 0x46, 0xFF, 0xF7, 0x8E, 0xFF, + 0x02, 0x46, 0x01, 0x20, 0x00, 0x21, 0x0E, 0xF0, 0x64, 0xFE, 0xD4, 0xE9, + 0x1A, 0x23, 0x10, 0x43, 0x19, 0x43, 0xC4, 0xE9, 0x1A, 0x01, 0x47, 0xE0, + 0x00, 0x23, 0x08, 0x46, 0xE2, 0x6C, 0xFF, 0xF7, 0x7D, 0xFF, 0x85, 0x40, + 0x60, 0x6C, 0x05, 0x43, 0x65, 0x64, 0x3D, 0xE0, 0xD6, 0xE9, 0x02, 0x23, + 0x08, 0x46, 0xFF, 0xF7, 0x73, 0xFF, 0x01, 0x23, 0x02, 0x46, 0x00, 0x21, + 0x18, 0x46, 0x0E, 0xF0, 0x48, 0xFE, 0xD4, 0xE9, 0x1E, 0x23, 0x10, 0x43, + 0x19, 0x43, 0xC4, 0xE9, 0x1E, 0x01, 0x2B, 0xE0, 0x00, 0x23, 0x08, 0x46, + 0xE2, 0x6C, 0xFF, 0xF7, 0x61, 0xFF, 0x85, 0x40, 0x20, 0x6B, 0x05, 0x43, + 0x25, 0x63, 0x21, 0xE0, 0xD6, 0xE9, 0x02, 0x23, 0x08, 0x46, 0xFF, 0xF7, + 0x57, 0xFF, 0x02, 0x46, 0x01, 0x20, 0x00, 0x21, 0x0E, 0xF0, 0x2D, 0xFE, + 0xD4, 0xE9, 0x14, 0x23, 0x10, 0x43, 0x19, 0x43, 0xC4, 0xE9, 0x14, 0x01, + 0x10, 0xE0, 0xA0, 0x6C, 0x8D, 0x40, 0x05, 0x43, 0xA5, 0x64, 0x0B, 0xE0, + 0x00, 0x23, 0x0A, 0x46, 0x01, 0x20, 0x19, 0x46, 0x0E, 0xF0, 0x1B, 0xFE, + 0xD6, 0xE9, 0x00, 0x23, 0x10, 0x43, 0x19, 0x43, 0xC6, 0xE9, 0x00, 0x01, + 0x60, 0x68, 0x40, 0x1C, 0x60, 0x60, 0x70, 0xBD, 0x2D, 0xE9, 0xF8, 0x4F, + 0x82, 0x46, 0x00, 0x20, 0x99, 0x46, 0x93, 0x46, 0x0F, 0x46, 0x06, 0x46, + 0x00, 0x90, 0x52, 0xE0, 0xA2, 0x48, 0x00, 0x24, 0x4F, 0xF0, 0xFF, 0x38, + 0xD0, 0xE9, 0x02, 0x23, 0x30, 0x46, 0xFF, 0xF7, 0x23, 0xFF, 0x02, 0x46, + 0x01, 0x20, 0x00, 0x21, 0x0E, 0xF0, 0xF9, 0xFD, 0x9B, 0x4A, 0x80, 0x3A, + 0xD2, 0xE9, 0x1A, 0x32, 0x18, 0x40, 0x11, 0x40, 0x08, 0x43, 0x3B, 0xD1, + 0x00, 0x25, 0x2A, 0xE0, 0x05, 0xFB, 0x07, 0x60, 0x3B, 0xF9, 0x10, 0x20, + 0xB2, 0xEB, 0x09, 0x1F, 0x22, 0xDA, 0x64, 0x1C, 0xB8, 0xF1, 0xFF, 0x3F, + 0x00, 0xD1, 0xA8, 0x46, 0xAA, 0xF1, 0x01, 0x01, 0x8D, 0x42, 0x19, 0xD1, + 0x01, 0x2C, 0x17, 0xD1, 0x79, 0x1E, 0x8E, 0x42, 0x04, 0xD1, 0x29, 0x46, + 0x0A, 0x98, 0xFF, 0xF7, 0x16, 0xFF, 0x0E, 0xE0, 0x0B, 0xEB, 0x40, 0x00, + 0xB0, 0xF9, 0x02, 0x20, 0xB2, 0xEB, 0x09, 0x1F, 0x05, 0xDB, 0x36, 0xB1, + 0x30, 0xF9, 0x02, 0x1C, 0xB1, 0xEB, 0x09, 0x1F, 0x01, 0xDA, 0x00, 0x24, + 0x00, 0xE0, 0x02, 0x24, 0x6D, 0x1C, 0x55, 0x45, 0xD2, 0xDB, 0xAA, 0xEB, + 0x08, 0x00, 0xA0, 0x42, 0x08, 0xD8, 0x31, 0x46, 0x0B, 0x98, 0xFF, 0xF7, + 0xFA, 0xFE, 0x01, 0x20, 0x00, 0x99, 0xB0, 0x40, 0x08, 0x43, 0x00, 0x90, + 0x76, 0x1C, 0xBE, 0x42, 0xAA, 0xDB, 0x00, 0x25, 0x45, 0xE0, 0x00, 0x26, + 0x34, 0x46, 0x3A, 0xE0, 0xDF, 0xF8, 0xD4, 0x81, 0xA8, 0xF1, 0x80, 0x08, + 0xD8, 0xE9, 0x1A, 0x21, 0xD8, 0xE9, 0x16, 0x30, 0x1A, 0x43, 0x01, 0x43, + 0x0A, 0x43, 0x1F, 0xD0, 0x6F, 0x48, 0xD0, 0xE9, 0x02, 0x23, 0x20, 0x46, + 0xFF, 0xF7, 0xC0, 0xFE, 0x01, 0x23, 0x02, 0x46, 0x00, 0x21, 0x18, 0x46, + 0x0E, 0xF0, 0x95, 0xFD, 0xC6, 0x46, 0xD8, 0xF8, 0x68, 0x80, 0xDE, 0xF8, + 0x6C, 0x20, 0x03, 0x46, 0x8C, 0x46, 0x00, 0xEA, 0x08, 0x00, 0x11, 0x40, + 0x08, 0x43, 0x15, 0xD1, 0xDE, 0xE9, 0x16, 0x10, 0x0B, 0x40, 0x0C, 0xEA, + 0x00, 0x0C, 0x53, 0xEA, 0x0C, 0x03, 0x0D, 0xD1, 0x05, 0xFB, 0x07, 0x41, + 0x3B, 0xF9, 0x11, 0x10, 0xB1, 0xEB, 0x09, 0x1F, 0x06, 0xDA, 0x01, 0x20, + 0x00, 0x9A, 0xA0, 0x40, 0x10, 0x40, 0x00, 0x00, 0x00, 0xD1, 0x76, 0x1C, + 0x64, 0x1C, 0xBC, 0x42, 0xC2, 0xDB, 0x1E, 0xB1, 0x29, 0x46, 0x0A, 0x98, + 0xFF, 0xF7, 0xAB, 0xFE, 0x6D, 0x1C, 0x55, 0x45, 0xB7, 0xDB, 0xBD, 0xE8, + 0xF8, 0x8F, 0x2D, 0xE9, 0xFC, 0x41, 0x0F, 0x46, 0x80, 0x46, 0x00, 0x25, + 0x00, 0xF0, 0x52, 0xFA, 0x4E, 0x4E, 0x80, 0x3E, 0x90, 0xB9, 0x00, 0x22, + 0x4F, 0xF4, 0x00, 0x54, 0x6B, 0x46, 0x21, 0x46, 0x10, 0x46, 0x0D, 0xF0, + 0xCB, 0xFB, 0x21, 0x46, 0x00, 0x20, 0x0D, 0xF0, 0xA6, 0xFB, 0x30, 0x78, + 0x20, 0xB1, 0x0A, 0xF0, 0x26, 0xFF, 0x00, 0x20, 0xBD, 0xE8, 0xFC, 0x81, + 0x03, 0x24, 0x39, 0x46, 0x40, 0x46, 0x0F, 0xF0, 0x45, 0xFB, 0x00, 0x22, + 0x01, 0xAB, 0x01, 0x21, 0x10, 0x46, 0x0D, 0xF0, 0xB5, 0xFB, 0x01, 0x21, + 0x00, 0x20, 0x0D, 0xF0, 0x90, 0xFB, 0x30, 0x78, 0x30, 0xB1, 0x0A, 0xF0, + 0x10, 0xFF, 0x64, 0x1E, 0x00, 0x2C, 0xEA, 0xDC, 0x28, 0x46, 0xE5, 0xE7, + 0x7D, 0x68, 0xFB, 0xE7, 0x36, 0x49, 0x80, 0x39, 0x08, 0x70, 0x01, 0x21, + 0x00, 0x20, 0x0D, 0xF0, 0x4F, 0xBB, 0x70, 0xB5, 0x0D, 0x46, 0x04, 0x46, + 0x50, 0x21, 0x0E, 0xF0, 0xFA, 0xFD, 0x31, 0x48, 0xE4, 0xE8, 0x05, 0x05, + 0x0C, 0xF0, 0xB1, 0xFC, 0x44, 0xF8, 0x0C, 0x0C, 0xFF, 0xF7, 0xFF, 0xFB, + 0x44, 0xE9, 0x01, 0x01, 0x2C, 0x48, 0x00, 0x21, 0x21, 0x71, 0x00, 0x68, + 0xB0, 0xF8, 0xAA, 0x24, 0xE2, 0x80, 0xB0, 0xF8, 0xA6, 0x24, 0x22, 0x81, + 0xB0, 0xF8, 0xA8, 0x24, 0x62, 0x81, 0x90, 0xF8, 0xA4, 0x24, 0x22, 0x73, + 0xA1, 0x73, 0x90, 0xF8, 0xA5, 0x24, 0x02, 0xF0, 0x3F, 0x02, 0xE2, 0x73, + 0x90, 0xF8, 0xA2, 0x24, 0xC2, 0xF3, 0x01, 0x12, 0x22, 0x74, 0x90, 0xF8, + 0xA2, 0x24, 0x92, 0x09, 0x62, 0x74, 0x90, 0xF8, 0xA2, 0x24, 0x02, 0xF0, + 0x03, 0x02, 0xA2, 0x74, 0x90, 0xF8, 0xA2, 0x24, 0xC2, 0xF3, 0x81, 0x02, + 0xE2, 0x74, 0x90, 0xF8, 0xA5, 0x24, 0x02, 0xF0, 0x3F, 0x02, 0x22, 0x75, + 0x90, 0xF8, 0xA3, 0x24, 0xC2, 0xF3, 0x01, 0x12, 0x62, 0x75, 0x90, 0xF8, + 0xA3, 0x24, 0x92, 0x09, 0xA2, 0x75, 0x90, 0xF8, 0xA3, 0x24, 0x02, 0xF0, + 0x03, 0x02, 0xE2, 0x75, 0x90, 0xF8, 0xA3, 0x24, 0xC2, 0xF3, 0x81, 0x02, + 0x22, 0x76, 0x61, 0x76, 0xA1, 0x76, 0xE1, 0x76, 0x21, 0x77, 0x61, 0x77, + 0xA1, 0x77, 0x90, 0xF8, 0xAD, 0x14, 0xE1, 0x77, 0x90, 0xF8, 0xAE, 0x14, + 0x84, 0xF8, 0x20, 0x10, 0xB0, 0xF8, 0xB0, 0x14, 0xE1, 0x85, 0x90, 0xF8, + 0xAF, 0x14, 0xCA, 0x09, 0x21, 0x6B, 0x05, 0xE0, 0x88, 0x22, 0x10, 0x00, + 0x21, 0x0F, 0x00, 0x00, 0x50, 0x24, 0x10, 0x00, 0x62, 0xF3, 0x10, 0x41, + 0x21, 0x63, 0x90, 0xF8, 0xAF, 0x24, 0x92, 0x09, 0x62, 0xF3, 0x51, 0x41, + 0x21, 0x63, 0x90, 0xF8, 0xAF, 0x04, 0x60, 0xF3, 0x92, 0x41, 0x21, 0x63, + 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x4F, 0x0D, 0x46, 0xFF, 0x4E, 0xDF, 0xF8, + 0x00, 0xA4, 0xDF, 0xF8, 0x00, 0x84, 0xA5, 0xB0, 0x4F, 0xF0, 0x00, 0x0B, + 0x06, 0xF1, 0x80, 0x07, 0x08, 0xEB, 0x45, 0x09, 0x0A, 0xEB, 0x85, 0x04, + 0x07, 0x28, 0x3E, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x8C, 0x04, 0x11, 0x2E, + 0x3E, 0x50, 0x6E, 0x00, 0x14, 0xA8, 0x00, 0xF0, 0xC1, 0xF9, 0x16, 0x98, + 0xF0, 0x64, 0xDD, 0xE9, 0x18, 0x01, 0xC7, 0xE9, 0x02, 0x01, 0x14, 0xA8, + 0x00, 0xF0, 0x85, 0xF9, 0x2A, 0xE0, 0xEF, 0x49, 0x68, 0x46, 0xFF, 0xF7, + 0x5A, 0xFF, 0x02, 0x98, 0xF0, 0x64, 0xDD, 0xE9, 0x04, 0x01, 0xC7, 0xE9, + 0x02, 0x01, 0x69, 0x46, 0x00, 0x20, 0xFF, 0xF7, 0x12, 0xFF, 0x83, 0x46, + 0x70, 0x69, 0xE7, 0x49, 0x42, 0x00, 0xE7, 0x48, 0x0E, 0xF0, 0xA8, 0xFC, + 0x70, 0x69, 0xB3, 0x69, 0x42, 0x00, 0x0A, 0xEB, 0x40, 0x01, 0x08, 0xEB, + 0x43, 0x00, 0x3C, 0xE0, 0xE1, 0x49, 0x68, 0x46, 0xFF, 0xF7, 0x3D, 0xFF, + 0x02, 0x98, 0xF0, 0x64, 0xDD, 0xE9, 0x04, 0x01, 0xC7, 0xE9, 0x02, 0x01, + 0x69, 0x46, 0x01, 0x20, 0xFF, 0xF7, 0xF5, 0xFE, 0x83, 0x46, 0x4D, 0xE0, + 0x21, 0x46, 0x68, 0x46, 0xFF, 0xF7, 0x2D, 0xFF, 0x02, 0x98, 0xF0, 0x64, + 0x01, 0x21, 0x28, 0x46, 0xFF, 0xF7, 0x73, 0xFC, 0x02, 0x90, 0xDD, 0xE9, + 0x04, 0x01, 0xC7, 0xE9, 0x02, 0x01, 0x69, 0x46, 0x00, 0x20, 0x2F, 0xE0, + 0xCF, 0x49, 0x68, 0x46, 0xFF, 0xF7, 0x1B, 0xFF, 0x02, 0x98, 0xF0, 0x64, + 0x00, 0x21, 0x28, 0x46, 0xFF, 0xF7, 0x61, 0xFC, 0x02, 0x90, 0xDD, 0xE9, + 0x04, 0x01, 0xC7, 0xE9, 0x02, 0x01, 0x69, 0x46, 0x01, 0x20, 0xFF, 0xF7, + 0xCE, 0xFE, 0x83, 0x46, 0xB0, 0x69, 0x42, 0x00, 0x0A, 0xEB, 0x40, 0x01, + 0x45, 0x43, 0x08, 0xEB, 0x45, 0x00, 0x0E, 0xF0, 0x61, 0xFC, 0x1D, 0xE0, + 0x21, 0x46, 0x68, 0x46, 0xFF, 0xF7, 0xFD, 0xFE, 0x02, 0x98, 0xF0, 0x64, + 0xDD, 0xE9, 0x04, 0x01, 0xC7, 0xE9, 0x02, 0x01, 0x01, 0x21, 0x28, 0x46, + 0xFF, 0xF7, 0x62, 0xFC, 0xCD, 0xE9, 0x04, 0x01, 0x69, 0x46, 0x01, 0x20, + 0xFF, 0xF7, 0xAF, 0xFE, 0x22, 0x88, 0xA9, 0xF8, 0x00, 0x20, 0xB1, 0x69, + 0x83, 0x46, 0x29, 0x44, 0x08, 0xEB, 0x41, 0x00, 0x61, 0x88, 0x01, 0x80, + 0x25, 0xB0, 0x58, 0x46, 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xF0, 0x5F, + 0x00, 0x27, 0x0C, 0xF0, 0x96, 0xFB, 0x4F, 0xEA, 0x40, 0x08, 0xFF, 0xF7, + 0xE4, 0xFA, 0x0E, 0x46, 0xA9, 0x49, 0x05, 0x46, 0x4F, 0xEA, 0x58, 0x00, + 0xC8, 0x64, 0x80, 0x31, 0xC1, 0xE9, 0x02, 0x56, 0x0F, 0xF0, 0xBD, 0xFB, + 0x81, 0x46, 0x0F, 0xF0, 0xE5, 0xFB, 0x82, 0x46, 0x0E, 0xF0, 0x8A, 0xFF, + 0x00, 0x24, 0x4F, 0xF0, 0x01, 0x0B, 0x1B, 0xE0, 0x0B, 0xFA, 0x04, 0xF0, + 0x10, 0xEA, 0x08, 0x0F, 0x15, 0xD0, 0xE0, 0xB2, 0x0E, 0xF0, 0xD4, 0xFE, + 0x01, 0x28, 0x04, 0xD0, 0x02, 0x28, 0x05, 0xD0, 0x03, 0x28, 0x06, 0xD0, + 0x09, 0xE0, 0x39, 0x46, 0x05, 0x20, 0x04, 0xE0, 0x39, 0x46, 0x03, 0x20, + 0x01, 0xE0, 0x39, 0x46, 0x07, 0x20, 0xFF, 0xF7, 0x10, 0xFD, 0x7F, 0x1C, + 0xFF, 0xB2, 0x64, 0x1C, 0x4C, 0x45, 0xE1, 0xDB, 0x0E, 0xF0, 0x66, 0xFF, + 0x00, 0x27, 0x3C, 0x46, 0x1F, 0xE0, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x0E, 0xF0, 0xC1, 0xFB, 0x28, 0x40, 0x31, 0x40, 0x08, 0x43, 0x15, 0xD0, + 0xE0, 0xB2, 0x0E, 0xF0, 0xFF, 0xFE, 0x01, 0x28, 0x04, 0xD0, 0x02, 0x28, + 0x05, 0xD0, 0x03, 0x28, 0x06, 0xD0, 0x09, 0xE0, 0x39, 0x46, 0x06, 0x20, + 0x04, 0xE0, 0x39, 0x46, 0x04, 0x20, 0x01, 0xE0, 0x39, 0x46, 0x08, 0x20, + 0xFF, 0xF7, 0xE9, 0xFC, 0x7F, 0x1C, 0xFF, 0xB2, 0x64, 0x1C, 0x54, 0x45, + 0xDD, 0xDD, 0xBD, 0xE8, 0xF0, 0x5F, 0x0E, 0xF0, 0x3D, 0xBF, 0xF0, 0xB5, + 0x85, 0xB0, 0x00, 0x20, 0x0D, 0xF0, 0x73, 0xFA, 0xFF, 0xF7, 0x3F, 0xFB, + 0x01, 0x28, 0x79, 0xD1, 0x76, 0x4D, 0x79, 0x4C, 0x68, 0x88, 0x20, 0xB9, + 0x21, 0x68, 0x91, 0xF8, 0xA0, 0x14, 0xC9, 0x07, 0x01, 0xD1, 0xC0, 0x07, + 0x01, 0xD0, 0xFF, 0xF7, 0x83, 0xFF, 0x68, 0x88, 0x20, 0xB9, 0x21, 0x68, + 0x91, 0xF8, 0xA0, 0x14, 0x49, 0x07, 0x01, 0xD4, 0x40, 0x07, 0x03, 0xD5, + 0x00, 0xF0, 0xDC, 0xF9, 0x01, 0x28, 0x69, 0xD1, 0x68, 0x88, 0x20, 0xB9, + 0x21, 0x68, 0x91, 0xF8, 0xA0, 0x14, 0x09, 0x07, 0x01, 0xD4, 0x00, 0x07, + 0x03, 0xD5, 0x00, 0xF0, 0x97, 0xF9, 0x01, 0x28, 0x5C, 0xD1, 0x68, 0x88, + 0x20, 0xB9, 0x21, 0x68, 0x91, 0xF8, 0xA0, 0x14, 0xC9, 0x06, 0x01, 0xD4, + 0xC0, 0x06, 0x03, 0xD5, 0x00, 0xF0, 0x5D, 0xF9, 0x01, 0x28, 0x4F, 0xD1, + 0x68, 0x88, 0x20, 0xB9, 0x21, 0x68, 0x91, 0xF8, 0xA0, 0x14, 0x89, 0x06, + 0x01, 0xD4, 0x80, 0x06, 0x03, 0xD5, 0x00, 0xF0, 0x03, 0xF9, 0x01, 0x28, + 0x42, 0xD1, 0x68, 0x88, 0x20, 0xB9, 0x21, 0x68, 0x91, 0xF8, 0xA0, 0x14, + 0x49, 0x06, 0x01, 0xD4, 0x40, 0x06, 0x03, 0xD5, 0x00, 0xF0, 0xC8, 0xF8, + 0x01, 0x28, 0x35, 0xD1, 0x68, 0x88, 0x20, 0xB9, 0x21, 0x68, 0x91, 0xF8, + 0xA0, 0x14, 0x89, 0x07, 0x01, 0xD4, 0x80, 0x07, 0x2A, 0xD5, 0x00, 0x21, + 0x01, 0x20, 0xFF, 0xF7, 0x90, 0xFE, 0x05, 0x00, 0x1D, 0xD0, 0x0C, 0xF0, + 0xCC, 0xFA, 0x04, 0x90, 0x0C, 0xF0, 0xB2, 0xFA, 0xCD, 0xE9, 0x02, 0x01, + 0x04, 0x21, 0x04, 0xA8, 0x10, 0xF0, 0xC0, 0xFC, 0x07, 0x46, 0x08, 0x21, + 0x02, 0xA8, 0x10, 0xF0, 0xBB, 0xFC, 0x01, 0x46, 0x02, 0x20, 0x01, 0x26, + 0xCD, 0xE9, 0x00, 0x60, 0x20, 0x68, 0x2A, 0x46, 0x90, 0xF8, 0xB3, 0x34, + 0x38, 0x46, 0xFF, 0xF7, 0x0D, 0xFD, 0x06, 0xE0, 0x08, 0xE0, 0x01, 0x22, + 0x0F, 0x21, 0x05, 0x20, 0x0A, 0xF0, 0x06, 0xFE, 0x00, 0x26, 0x0E, 0xB1, + 0xFF, 0xF7, 0xA1, 0xFB, 0x05, 0xB0, 0x00, 0x20, 0xBD, 0xE8, 0xF0, 0x40, + 0x0D, 0xF0, 0x00, 0xBA, 0x10, 0xB5, 0x0F, 0xF0, 0x7F, 0xF8, 0x08, 0xB1, + 0x01, 0x20, 0x10, 0xBD, 0x32, 0x48, 0x0F, 0xF0, 0x99, 0xF8, 0x00, 0x20, + 0x10, 0xBD, 0x2D, 0xE9, 0xFC, 0x41, 0x07, 0x46, 0x00, 0x25, 0xFF, 0xF7, + 0xEF, 0xFF, 0x29, 0x4E, 0x88, 0xB9, 0x00, 0x22, 0x4F, 0xF4, 0x00, 0x54, + 0x6B, 0x46, 0x21, 0x46, 0x10, 0x46, 0x0D, 0xF0, 0x69, 0xF9, 0x21, 0x46, + 0x00, 0x20, 0x0D, 0xF0, 0x44, 0xF9, 0x30, 0x78, 0x18, 0xB1, 0x0A, 0xF0, + 0xC4, 0xFC, 0x00, 0x20, 0x9C, 0xE5, 0x03, 0x24, 0x38, 0x46, 0x0E, 0xF0, + 0x2D, 0xFF, 0x00, 0x22, 0x01, 0xAB, 0x01, 0x21, 0x10, 0x46, 0x0D, 0xF0, + 0x55, 0xF9, 0x01, 0x21, 0x00, 0x20, 0x0D, 0xF0, 0x30, 0xF9, 0x30, 0x78, + 0x30, 0xB1, 0x0A, 0xF0, 0xB0, 0xFC, 0x64, 0x1E, 0x00, 0x2C, 0xEB, 0xDC, + 0x28, 0x46, 0x85, 0xE5, 0x7D, 0x68, 0xFB, 0xE7, 0x1F, 0xB5, 0x04, 0x46, + 0x40, 0x21, 0x0E, 0xF0, 0xA2, 0xFB, 0x68, 0x46, 0x0E, 0xF0, 0x88, 0xFE, + 0x13, 0x48, 0x20, 0x60, 0x0F, 0x48, 0x60, 0x60, 0x0C, 0xF0, 0x55, 0xFA, + 0x44, 0xF8, 0x08, 0x0F, 0x0C, 0xF0, 0x3A, 0xFA, 0xC4, 0xE9, 0x02, 0x01, + 0x00, 0x20, 0x20, 0x74, 0x9D, 0xF8, 0x00, 0x00, 0xA0, 0x74, 0x09, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0xB2, 0x04, 0x60, 0x74, 0x9D, 0xF8, 0x01, 0x00, + 0xE0, 0x74, 0x9D, 0xF8, 0x02, 0x00, 0x20, 0x75, 0x0C, 0xE0, 0x00, 0x00, + 0x08, 0x22, 0x10, 0x00, 0xF0, 0xC7, 0x01, 0x20, 0x38, 0xA9, 0x01, 0x20, + 0x50, 0x24, 0x10, 0x00, 0x3D, 0x07, 0x00, 0x00, 0x21, 0x0F, 0x00, 0x00, + 0x9D, 0xF8, 0x03, 0x00, 0x60, 0x75, 0x9D, 0xF8, 0x07, 0x00, 0x60, 0x76, + 0x9D, 0xF8, 0x08, 0x00, 0xA0, 0x76, 0x9D, 0xF8, 0x09, 0x00, 0xE0, 0x76, + 0x9D, 0xF8, 0x0A, 0x00, 0x20, 0x77, 0x9D, 0xF8, 0x0B, 0x00, 0x60, 0x77, + 0x9D, 0xF8, 0x0D, 0x00, 0xE0, 0x77, 0x9D, 0xF8, 0x0E, 0x00, 0x0E, 0xF0, + 0x5A, 0xFE, 0x1F, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x8A, 0x4E, 0x00, 0x20, + 0x05, 0x46, 0x8A, 0x4F, 0xF0, 0x61, 0x18, 0xE0, 0x29, 0x46, 0x06, 0x20, + 0xFF, 0xF7, 0xCB, 0xFD, 0x04, 0x00, 0x18, 0xD0, 0x39, 0x68, 0xB4, 0xF9, + 0x02, 0x00, 0x91, 0xF8, 0xB8, 0x14, 0xB0, 0xEB, 0x01, 0x1F, 0x03, 0xD9, + 0x29, 0x46, 0x08, 0x20, 0xFF, 0xF7, 0xA9, 0xFB, 0xB4, 0xF9, 0x02, 0x00, + 0xF1, 0x69, 0x88, 0x42, 0x00, 0xDD, 0xF0, 0x61, 0x6D, 0x1C, 0xB0, 0x69, + 0x85, 0x42, 0xE3, 0xD3, 0x01, 0x20, 0xBD, 0xE8, 0xF0, 0x81, 0x06, 0x22, + 0x0F, 0x21, 0x05, 0x20, 0x0A, 0xF0, 0x46, 0xFD, 0x00, 0x20, 0xF6, 0xE7, + 0x2D, 0xE9, 0xF0, 0x41, 0x73, 0x4F, 0x00, 0x20, 0x06, 0x46, 0x78, 0x62, + 0x39, 0xE0, 0x31, 0x46, 0x05, 0x20, 0xFF, 0xF7, 0x9E, 0xFD, 0x05, 0x00, + 0x38, 0xD0, 0x00, 0x24, 0x2D, 0xE0, 0xFF, 0xF7, 0x2A, 0xF9, 0x02, 0x46, + 0x0B, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0x64, 0xFB, 0x02, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x0E, 0xF0, 0x3A, 0xFA, 0xD7, 0xE9, 0x16, 0x23, 0x10, 0x40, + 0x19, 0x40, 0x08, 0x43, 0x1A, 0xD1, 0x65, 0x49, 0xB8, 0x69, 0x09, 0x68, + 0x20, 0x44, 0x35, 0xF9, 0x10, 0x00, 0x91, 0xF8, 0xB7, 0x14, 0xB0, 0xEB, + 0x01, 0x1F, 0x07, 0xD9, 0x31, 0x46, 0x07, 0x20, 0xFF, 0xF7, 0x65, 0xFB, + 0x21, 0x46, 0x08, 0x20, 0xFF, 0xF7, 0x61, 0xFB, 0xB8, 0x69, 0x79, 0x6A, + 0x20, 0x44, 0x35, 0xF9, 0x10, 0x00, 0x88, 0x42, 0x00, 0xDD, 0x78, 0x62, + 0x64, 0x1C, 0xB8, 0x69, 0x84, 0x42, 0xCE, 0xD3, 0x76, 0x1C, 0x78, 0x69, + 0x86, 0x42, 0xC2, 0xD3, 0x01, 0x20, 0xB0, 0xE7, 0x05, 0x22, 0x0F, 0x21, + 0x10, 0x46, 0x0A, 0xF0, 0xF9, 0xFC, 0x00, 0x20, 0xA9, 0xE7, 0x2D, 0xE9, + 0xF0, 0x41, 0x4D, 0x4E, 0x00, 0x20, 0x05, 0x46, 0x4C, 0x4F, 0x30, 0x62, + 0x18, 0xE0, 0x29, 0x46, 0x04, 0x20, 0xFF, 0xF7, 0x50, 0xFD, 0x04, 0x00, + 0x17, 0xD0, 0x39, 0x68, 0xB4, 0xF9, 0x02, 0x00, 0x91, 0xF8, 0xB6, 0x14, + 0xB0, 0xEB, 0x01, 0x1F, 0x03, 0xD9, 0x29, 0x46, 0x07, 0x20, 0xFF, 0xF7, + 0x2E, 0xFB, 0xB4, 0xF9, 0x02, 0x00, 0x31, 0x6A, 0x88, 0x42, 0x00, 0xDD, + 0x30, 0x62, 0x6D, 0x1C, 0x70, 0x69, 0x85, 0x42, 0xE3, 0xD3, 0x01, 0x20, + 0x83, 0xE7, 0x04, 0x22, 0x0F, 0x21, 0x05, 0x20, 0x0A, 0xF0, 0xCC, 0xFC, + 0x00, 0x20, 0x7C, 0xE7, 0x2D, 0xE9, 0xF0, 0x47, 0x00, 0x21, 0x03, 0x20, + 0xFF, 0xF7, 0x29, 0xFD, 0x05, 0x00, 0x28, 0xD0, 0x33, 0x48, 0x08, 0x21, + 0x88, 0x30, 0x10, 0xF0, 0x5F, 0xFB, 0x06, 0x46, 0x00, 0x24, 0xDF, 0xF8, + 0xC0, 0x80, 0xDF, 0xF8, 0xC0, 0x90, 0x17, 0xE0, 0xD9, 0xF8, 0x00, 0x00, + 0x37, 0x19, 0x35, 0xF9, 0x17, 0x10, 0x90, 0xF8, 0xB5, 0x04, 0xB1, 0xEB, + 0x00, 0x1F, 0x03, 0xD9, 0x21, 0x46, 0x04, 0x20, 0xFF, 0xF7, 0xF9, 0xFA, + 0x35, 0xF9, 0x17, 0x00, 0xD8, 0xF8, 0x2C, 0x20, 0x90, 0x42, 0x01, 0xDD, + 0xC8, 0xF8, 0x2C, 0x00, 0x64, 0x1C, 0x64, 0xB2, 0xB4, 0x42, 0xE5, 0xDB, + 0x01, 0x20, 0xBD, 0xE8, 0xF0, 0x87, 0x03, 0x22, 0x0F, 0x21, 0x05, 0x20, + 0x0A, 0xF0, 0x94, 0xFC, 0x00, 0x20, 0xF6, 0xE7, 0x2D, 0xE9, 0xF0, 0x47, + 0x00, 0x21, 0x02, 0x20, 0xFF, 0xF7, 0xF1, 0xFC, 0x05, 0x00, 0x27, 0xD0, + 0x17, 0x48, 0x04, 0x21, 0x4C, 0x30, 0x10, 0xF0, 0x27, 0xFB, 0x06, 0x46, + 0x00, 0x24, 0xDF, 0xF8, 0x50, 0x80, 0xDF, 0xF8, 0x50, 0x90, 0x17, 0xE0, + 0xD9, 0xF8, 0x00, 0x00, 0x37, 0x19, 0x35, 0xF9, 0x17, 0x10, 0x90, 0xF8, + 0xB4, 0x04, 0xB1, 0xEB, 0x00, 0x1F, 0x03, 0xD9, 0x21, 0x46, 0x03, 0x20, + 0xFF, 0xF7, 0xC1, 0xFA, 0x35, 0xF9, 0x17, 0x00, 0xD8, 0xF8, 0x28, 0x20, + 0x90, 0x42, 0x01, 0xDD, 0xC8, 0xF8, 0x28, 0x00, 0x64, 0x1C, 0x64, 0xB2, + 0xB4, 0x42, 0xE5, 0xDB, 0x01, 0x20, 0xC6, 0xE7, 0x02, 0x22, 0x0F, 0x21, + 0x05, 0x20, 0x0A, 0xF0, 0x5D, 0xFC, 0x00, 0x20, 0xBF, 0xE7, 0x00, 0x00, + 0x08, 0x22, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x10, 0xB5, 0x04, 0x46, + 0x02, 0x21, 0x00, 0x20, 0x0C, 0xF0, 0x88, 0xFF, 0x0F, 0x48, 0x04, 0x60, + 0x10, 0xBD, 0x08, 0xB5, 0x0E, 0x48, 0x10, 0xF0, 0x0F, 0xFA, 0x00, 0x22, + 0x6B, 0x46, 0x02, 0x21, 0x10, 0x46, 0x0C, 0xF0, 0xCB, 0xFF, 0x02, 0x21, + 0x00, 0x20, 0x0C, 0xF0, 0xA6, 0xFF, 0x09, 0x48, 0x55, 0x22, 0x01, 0x68, + 0x05, 0x48, 0x01, 0xF8, 0x20, 0x2F, 0x00, 0x68, 0x48, 0x70, 0x02, 0x0A, + 0x8A, 0x70, 0x02, 0x0C, 0xCA, 0x70, 0x10, 0xF0, 0xBB, 0xFA, 0x08, 0xBD, + 0x98, 0x22, 0x10, 0x00, 0xB1, 0x16, 0x00, 0x00, 0x54, 0x24, 0x10, 0x00, + 0x10, 0xB5, 0x01, 0x46, 0x44, 0x22, 0x36, 0x48, 0x0E, 0xF0, 0xB1, 0xF9, + 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x20, 0x0C, 0xF0, 0x56, 0xBF, + 0x2D, 0xE9, 0xF0, 0x4F, 0x85, 0xB0, 0x24, 0x21, 0x30, 0x48, 0x0E, 0xF0, + 0xDE, 0xF9, 0x0C, 0x25, 0xDF, 0xF8, 0xBC, 0xB0, 0xDF, 0xF8, 0xB0, 0x90, + 0xDF, 0xF8, 0xB8, 0x80, 0x00, 0x26, 0x4F, 0xF0, 0x07, 0x0A, 0xEC, 0xB2, + 0x68, 0x46, 0x0B, 0xF0, 0x0C, 0xFF, 0xAD, 0xF8, 0x0C, 0x40, 0x8D, 0xF8, + 0x04, 0xA0, 0x03, 0x24, 0x4F, 0x46, 0xCD, 0xF8, 0x00, 0xB0, 0x68, 0x46, + 0x0B, 0xF0, 0x38, 0xFF, 0x00, 0x22, 0x04, 0xAB, 0x01, 0x21, 0x10, 0x46, + 0x0C, 0xF0, 0x80, 0xFF, 0x01, 0x21, 0x00, 0x20, 0x0C, 0xF0, 0x5B, 0xFF, + 0x97, 0xF8, 0x42, 0x00, 0x20, 0xB1, 0x0A, 0xF0, 0xDA, 0xFA, 0x64, 0x1E, + 0x00, 0x2C, 0xEA, 0xDC, 0xD8, 0xF8, 0x00, 0x00, 0xB9, 0x46, 0x3F, 0x68, + 0x90, 0xF8, 0x10, 0x01, 0x80, 0x06, 0x13, 0xD5, 0x01, 0x24, 0xD8, 0xF8, + 0x00, 0x00, 0x01, 0x7D, 0xB0, 0xF9, 0x24, 0x21, 0xCD, 0xE9, 0x00, 0x21, + 0xB0, 0xF9, 0x22, 0x31, 0x61, 0x43, 0x07, 0xEB, 0x41, 0x00, 0x3A, 0x46, + 0x01, 0x46, 0x0B, 0xF0, 0xB1, 0xF9, 0x64, 0x1C, 0x10, 0x2C, 0xEC, 0xDB, + 0xD8, 0xF8, 0x00, 0x00, 0x10, 0x21, 0x02, 0x7D, 0xD9, 0xF8, 0x00, 0x00, + 0x0B, 0xF0, 0x60, 0xFA, 0x07, 0x49, 0x21, 0xF8, 0x16, 0x50, 0x01, 0xEB, + 0x46, 0x01, 0xAD, 0x1C, 0xAD, 0xB2, 0x76, 0x1C, 0x48, 0x82, 0x1C, 0x2D, + 0xAF, 0xD9, 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x30, 0x26, 0x10, 0x00, + 0xAC, 0x00, 0x01, 0x20, 0x09, 0x17, 0x00, 0x00, 0x50, 0x24, 0x10, 0x00, + 0x10, 0xB5, 0x01, 0x46, 0x44, 0x22, 0xFE, 0x48, 0x0E, 0xF0, 0x39, 0xF9, + 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x20, 0x0C, 0xF0, 0xDE, 0xBE, + 0x30, 0xB5, 0x85, 0xB0, 0x04, 0x46, 0x68, 0x46, 0x0B, 0xF0, 0xA3, 0xFE, + 0xF7, 0x48, 0x00, 0x90, 0x24, 0xB3, 0x01, 0x2C, 0x24, 0xD0, 0x02, 0x2C, + 0x24, 0xD0, 0x03, 0x2C, 0x26, 0xD0, 0x04, 0x2C, 0x1A, 0xD1, 0x03, 0x20, + 0x8D, 0xF8, 0x05, 0x00, 0xEF, 0x4D, 0x03, 0x24, 0x68, 0x46, 0x0B, 0xF0, + 0xC7, 0xFE, 0x00, 0x22, 0x04, 0xAB, 0x01, 0x21, 0x10, 0x46, 0x0C, 0xF0, + 0x0F, 0xFF, 0x01, 0x21, 0x00, 0x20, 0x0C, 0xF0, 0xEA, 0xFE, 0x95, 0xF8, + 0x42, 0x00, 0x00, 0x28, 0x04, 0xD0, 0x0A, 0xF0, 0x68, 0xFA, 0x64, 0x1E, + 0x00, 0x2C, 0xE9, 0xDC, 0x05, 0xB0, 0x30, 0xBD, 0x04, 0x20, 0x02, 0xE0, + 0x05, 0x20, 0x00, 0xE0, 0x06, 0x20, 0x8D, 0xF8, 0x04, 0x00, 0xDD, 0xE7, + 0x02, 0x20, 0xD9, 0xE7, 0x2D, 0xE9, 0xF0, 0x5F, 0x91, 0x46, 0xDC, 0x4A, + 0x82, 0x46, 0x01, 0x27, 0x0E, 0x46, 0xDF, 0xF8, 0x70, 0x83, 0x3B, 0x02, + 0x10, 0x68, 0x51, 0x46, 0xBA, 0xF1, 0x06, 0x0F, 0x0C, 0xD2, 0xDF, 0xE8, + 0x0A, 0xF0, 0x06, 0x06, 0x03, 0x09, 0x09, 0x03, 0x04, 0x46, 0x1D, 0x46, + 0x04, 0xE0, 0x04, 0x46, 0x01, 0x25, 0x01, 0xE0, 0x94, 0x68, 0x02, 0x25, + 0x96, 0xF8, 0x00, 0xB0, 0x00, 0x20, 0x30, 0x70, 0x08, 0x46, 0xFF, 0xF7, + 0xA5, 0xFF, 0x4F, 0xEA, 0x49, 0x02, 0x21, 0x46, 0x40, 0x46, 0x0E, 0xF0, + 0x8D, 0xF8, 0x0F, 0x20, 0x30, 0x70, 0x50, 0x46, 0xFF, 0xF7, 0x9A, 0xFF, + 0xC9, 0x49, 0x00, 0x20, 0x0A, 0x68, 0x0D, 0xE0, 0x34, 0xF9, 0x10, 0x30, + 0x38, 0xF9, 0x10, 0x10, 0x59, 0x1A, 0x00, 0xD5, 0x49, 0x42, 0xB2, 0xF9, + 0x26, 0x37, 0x99, 0x42, 0x01, 0xDA, 0x00, 0x27, 0x02, 0xE0, 0x40, 0x1C, + 0x48, 0x45, 0xEF, 0xD3, 0x86, 0xF8, 0x00, 0xB0, 0x1F, 0xB9, 0x29, 0x46, + 0x30, 0x20, 0x0A, 0xF0, 0x2A, 0xFA, 0x38, 0x46, 0xBD, 0xE8, 0xF0, 0x9F, + 0x2D, 0xE9, 0xFF, 0x5F, 0x93, 0x46, 0xB6, 0x4A, 0x9A, 0x46, 0x01, 0x46, + 0x4F, 0xF0, 0x00, 0x09, 0x01, 0x26, 0x33, 0x02, 0x10, 0x68, 0xC8, 0x46, + 0x4F, 0x46, 0x06, 0x29, 0x0C, 0xD2, 0xDF, 0xE8, 0x01, 0xF0, 0x06, 0x06, + 0x03, 0x09, 0x09, 0x03, 0x04, 0x46, 0x1D, 0x46, 0x04, 0xE0, 0x04, 0x46, + 0x01, 0x25, 0x01, 0xE0, 0x94, 0x68, 0x02, 0x25, 0x08, 0x46, 0xFF, 0xF7, + 0x5D, 0xFF, 0x00, 0x20, 0x40, 0xF6, 0xFF, 0x72, 0x11, 0xE0, 0x34, 0xF9, + 0x10, 0x10, 0x21, 0xB1, 0x91, 0x42, 0x04, 0xD1, 0x4F, 0xF0, 0x01, 0x08, + 0x08, 0xE0, 0x01, 0x27, 0x06, 0xE0, 0x59, 0x45, 0x02, 0xDC, 0x01, 0x9B, + 0x99, 0x42, 0x01, 0xDA, 0x4F, 0xF0, 0x01, 0x09, 0x40, 0x1C, 0x50, 0x45, + 0xEB, 0xD3, 0x27, 0xB1, 0x29, 0x46, 0x21, 0x20, 0x0A, 0xF0, 0xEB, 0xF9, + 0x00, 0x26, 0xB8, 0xF1, 0x00, 0x0F, 0x04, 0xD0, 0x29, 0x46, 0x22, 0x20, + 0x0A, 0xF0, 0xE3, 0xF9, 0x00, 0x26, 0xB9, 0xF1, 0x00, 0x0F, 0x04, 0xD0, + 0x29, 0x46, 0x20, 0x20, 0x0A, 0xF0, 0xDB, 0xF9, 0x00, 0x26, 0x30, 0x46, + 0x04, 0xB0, 0xAD, 0xE7, 0x31, 0xB5, 0x0E, 0xF0, 0xAA, 0xFF, 0x04, 0x00, + 0x07, 0xD4, 0x00, 0x98, 0x01, 0x22, 0x02, 0xFA, 0x04, 0xF1, 0x01, 0x42, + 0x01, 0xD1, 0x64, 0x1E, 0xF9, 0xD5, 0x04, 0x21, 0x68, 0x46, 0x10, 0xF0, + 0x63, 0xF9, 0xC0, 0x07, 0x06, 0xD0, 0x07, 0x20, 0x64, 0x1C, 0x00, 0x99, + 0xA0, 0x40, 0x08, 0x43, 0x00, 0x90, 0x38, 0xBD, 0x03, 0x20, 0xF7, 0xE7, + 0x2D, 0xE9, 0xF1, 0x4F, 0x8C, 0xB0, 0x0B, 0xF0, 0x56, 0xFF, 0x04, 0x90, + 0x0B, 0xF0, 0x3C, 0xFF, 0xCD, 0xE9, 0x02, 0x01, 0x7F, 0x4C, 0x0C, 0x98, + 0x40, 0xB1, 0x01, 0x28, 0x06, 0xD0, 0x20, 0x68, 0xB0, 0xF8, 0x34, 0x97, + 0x7C, 0x48, 0x01, 0x90, 0x7C, 0x48, 0x1C, 0xE0, 0x20, 0x68, 0x90, 0xF8, + 0x7E, 0x07, 0x40, 0x07, 0x03, 0xD5, 0x04, 0x98, 0xFF, 0xF7, 0xC6, 0xFF, + 0x04, 0x90, 0x77, 0x48, 0x08, 0x38, 0x00, 0x78, 0x10, 0xB1, 0x01, 0x28, + 0x04, 0xD0, 0x08, 0xE0, 0x20, 0x68, 0xB0, 0xF8, 0x2C, 0x07, 0x03, 0xE0, + 0x20, 0x68, 0x90, 0xF8, 0x23, 0x07, 0x00, 0x01, 0x81, 0x46, 0x70, 0x48, + 0x01, 0x90, 0x6E, 0x48, 0x00, 0x1F, 0x05, 0x90, 0x20, 0x20, 0x0A, 0x90, + 0x40, 0x20, 0x09, 0x90, 0x04, 0x21, 0x04, 0xA8, 0x10, 0xF0, 0x1C, 0xF9, + 0x04, 0x46, 0x08, 0x21, 0x02, 0xA8, 0x10, 0xF0, 0x17, 0xF9, 0x44, 0x43, + 0x62, 0x48, 0x61, 0x00, 0x80, 0x46, 0x0E, 0xF0, 0x36, 0xF8, 0x4F, 0xF4, + 0x80, 0x71, 0x64, 0x48, 0x0E, 0xF0, 0x53, 0xF8, 0x05, 0x98, 0x00, 0x21, + 0x0D, 0x46, 0x01, 0x80, 0x0C, 0x98, 0xFF, 0xF7, 0xBD, 0xFE, 0x58, 0x48, + 0x00, 0x68, 0x08, 0x90, 0x00, 0x20, 0x04, 0x46, 0x00, 0x90, 0x01, 0x20, + 0x00, 0x26, 0x06, 0x90, 0x79, 0xE0, 0x01, 0x20, 0x04, 0x99, 0xB0, 0x40, + 0x08, 0x42, 0x73, 0xD0, 0x00, 0x27, 0x5E, 0xE0, 0x3A, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x0D, 0xF0, 0x5E, 0xFF, 0xDD, 0xE9, 0x02, 0x23, 0x82, 0x46, + 0x8B, 0x46, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x50, 0xD0, 0x50, 0x48, + 0x00, 0xEB, 0xC6, 0x00, 0x07, 0x90, 0xD0, 0xE9, 0x00, 0x10, 0x01, 0xEA, + 0x0A, 0x01, 0x00, 0xEA, 0x0B, 0x00, 0x01, 0x43, 0x43, 0xD1, 0x00, 0x2D, + 0x0E, 0xDD, 0x08, 0x98, 0x38, 0xF9, 0x14, 0x10, 0x30, 0xF9, 0x14, 0x00, + 0x08, 0x1A, 0x00, 0xD5, 0x40, 0x42, 0x05, 0x99, 0x80, 0xB2, 0x09, 0x88, + 0x81, 0x42, 0x01, 0xD2, 0x05, 0x99, 0x08, 0x80, 0x08, 0x98, 0x30, 0xF9, + 0x14, 0x20, 0x4A, 0x45, 0x22, 0xDC, 0x00, 0x2D, 0x20, 0xDD, 0x38, 0xF9, + 0x14, 0x10, 0xB1, 0xEB, 0x09, 0x00, 0xA2, 0xEB, 0x09, 0x01, 0x00, 0xD5, + 0x40, 0x42, 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0x88, 0x42, 0x07, 0xDC, + 0x68, 0x1E, 0xC3, 0xB2, 0xDD, 0xE9, 0x00, 0x10, 0x3A, 0x46, 0x0E, 0xF0, + 0x08, 0xFA, 0x01, 0xE0, 0x28, 0xF8, 0x14, 0x20, 0x07, 0x98, 0xD0, 0xE9, + 0x00, 0x21, 0x42, 0xEA, 0x0A, 0x02, 0x41, 0xEA, 0x0B, 0x01, 0xC0, 0xE9, + 0x00, 0x21, 0x0A, 0xE0, 0x3F, 0x2D, 0x08, 0xDA, 0x28, 0xF8, 0x14, 0x20, + 0x68, 0x1C, 0xC3, 0xB2, 0xDD, 0xE9, 0x00, 0x10, 0x3A, 0x46, 0x0E, 0xF0, + 0xF0, 0xF9, 0x64, 0x1C, 0x7F, 0x1C, 0x09, 0x98, 0x87, 0x42, 0x9D, 0xDB, + 0x25, 0x48, 0x00, 0xEB, 0xC6, 0x00, 0xD0, 0xE9, 0x00, 0x10, 0xDD, 0xE9, + 0x02, 0x23, 0x51, 0x40, 0x58, 0x40, 0x01, 0x43, 0x01, 0xD0, 0x00, 0x20, + 0x06, 0x90, 0x00, 0x98, 0x40, 0x1C, 0x00, 0x90, 0x76, 0x1C, 0x0A, 0x98, + 0x86, 0x42, 0x82, 0xDB, 0x06, 0x98, 0x01, 0x28, 0x03, 0xD0, 0x6D, 0x1C, + 0x3F, 0x2D, 0x7F, 0xF7, 0x6F, 0xAF, 0x0D, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x2D, 0xE9, 0xFF, 0x5F, 0x82, 0x46, 0x0B, 0xF0, 0x72, 0xFE, 0x00, 0x90, + 0x0B, 0xF0, 0x58, 0xFE, 0x13, 0x4D, 0x0E, 0x4C, 0xBA, 0xF1, 0x00, 0x0F, + 0xCD, 0xE9, 0x02, 0x01, 0x38, 0xD0, 0xBA, 0xF1, 0x01, 0x0F, 0x35, 0xD0, + 0x20, 0x68, 0x0B, 0x4F, 0xB0, 0xF8, 0x32, 0x97, 0x28, 0x68, 0xBF, 0x1E, + 0x00, 0xF1, 0x03, 0x08, 0x04, 0x21, 0x68, 0x46, 0x10, 0xF0, 0x56, 0xF8, + 0x06, 0x46, 0x11, 0xE0, 0x78, 0x27, 0x10, 0x00, 0xF9, 0x17, 0x00, 0x00, + 0x60, 0x54, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x80, 0x13, 0x10, 0x00, + 0xA4, 0x22, 0x10, 0x00, 0x40, 0x10, 0x10, 0x00, 0x78, 0x26, 0x10, 0x00, + 0x54, 0x24, 0x10, 0x00, 0x08, 0x21, 0x02, 0xA8, 0x10, 0xF0, 0x3E, 0xF8, + 0x46, 0x43, 0x00, 0x20, 0x47, 0xF6, 0xFF, 0x75, 0x38, 0x80, 0x04, 0x46, + 0xAB, 0x46, 0x88, 0xF8, 0x00, 0x40, 0x50, 0x46, 0xFF, 0xF7, 0xEA, 0xFD, + 0x84, 0x48, 0xDC, 0x46, 0x59, 0x46, 0x03, 0x68, 0x00, 0x20, 0x16, 0xE0, + 0x20, 0x68, 0x90, 0xF8, 0x7E, 0x07, 0x40, 0x07, 0x03, 0xD5, 0x00, 0x98, + 0xFF, 0xF7, 0xB0, 0xFE, 0x00, 0x90, 0x20, 0x68, 0x7D, 0x4F, 0xB0, 0xF8, + 0x2A, 0x97, 0x28, 0x68, 0x00, 0xF1, 0x01, 0x08, 0xC0, 0xE7, 0x33, 0xF9, + 0x10, 0x20, 0x8A, 0x42, 0x00, 0xDA, 0x11, 0x46, 0x40, 0x1C, 0xB0, 0x42, + 0xF7, 0xDB, 0x65, 0x45, 0x07, 0xD0, 0x68, 0x1A, 0x00, 0xD5, 0x40, 0x42, + 0x3A, 0x88, 0x80, 0xB2, 0x82, 0x42, 0x00, 0xD2, 0x38, 0x80, 0x0D, 0x46, + 0x00, 0x20, 0x0A, 0xE0, 0x33, 0xF9, 0x10, 0x20, 0x4A, 0x45, 0x05, 0xDA, + 0x14, 0xB1, 0x64, 0x1E, 0x88, 0xF8, 0x00, 0x40, 0x01, 0x20, 0x85, 0xE6, + 0x40, 0x1C, 0xB0, 0x42, 0xF2, 0xDB, 0x64, 0x1C, 0xE4, 0xB2, 0x0F, 0x2C, + 0xBD, 0xD9, 0x00, 0x20, 0x7C, 0xE6, 0x2D, 0xE9, 0xFE, 0x43, 0x65, 0x4D, + 0x01, 0x24, 0xAD, 0x1E, 0x28, 0x78, 0x30, 0xB1, 0x01, 0x28, 0x04, 0xD0, + 0x01, 0x21, 0x40, 0x20, 0x0A, 0xF0, 0x49, 0xF8, 0xB9, 0xE0, 0x00, 0x20, + 0x0C, 0xF0, 0x29, 0xFD, 0x28, 0x78, 0x5E, 0x4F, 0x10, 0xB1, 0x01, 0x28, + 0x06, 0xD0, 0x0C, 0xE0, 0x38, 0x68, 0x4F, 0xF0, 0x00, 0x08, 0xB0, 0xF8, + 0x2C, 0x07, 0x05, 0xE0, 0x38, 0x68, 0x4F, 0xF0, 0x01, 0x08, 0x90, 0xF8, + 0x23, 0x07, 0x00, 0x01, 0x68, 0x81, 0x0B, 0xF0, 0xD0, 0xFD, 0x02, 0x90, + 0x0B, 0xF0, 0xB6, 0xFD, 0xCD, 0xE9, 0x00, 0x01, 0x38, 0x68, 0x90, 0xF8, + 0x7E, 0x07, 0x40, 0x07, 0x03, 0xD5, 0x02, 0x98, 0xFF, 0xF7, 0x4C, 0xFE, + 0x02, 0x90, 0x38, 0x68, 0x4D, 0x4E, 0x90, 0xF8, 0x28, 0x17, 0xC9, 0x07, + 0x01, 0xD0, 0x00, 0x20, 0x01, 0xE0, 0x90, 0xF8, 0x2E, 0x07, 0x31, 0x68, + 0x48, 0x70, 0x38, 0x68, 0x90, 0xF8, 0x28, 0x07, 0x80, 0x07, 0x05, 0xD5, + 0xDD, 0xE9, 0x01, 0x31, 0x45, 0x48, 0x00, 0x9A, 0x0D, 0xF0, 0x9A, 0xFF, + 0x38, 0x68, 0x90, 0xF8, 0x28, 0x17, 0xC9, 0x07, 0x0A, 0xD0, 0xB0, 0xF8, + 0x2A, 0x07, 0x68, 0x81, 0x40, 0x46, 0xFF, 0xF7, 0x29, 0xFF, 0x70, 0xB1, + 0x69, 0x88, 0x01, 0x20, 0x0A, 0xF0, 0x1B, 0xFC, 0x38, 0x68, 0xB9, 0x46, + 0x90, 0xF8, 0x28, 0x17, 0x89, 0x07, 0x19, 0xD5, 0x29, 0x78, 0x41, 0xB1, + 0x01, 0x29, 0x09, 0xD0, 0x0C, 0xE0, 0x01, 0x21, 0x08, 0x46, 0x09, 0xF0, + 0xEC, 0xFF, 0x00, 0x24, 0xEE, 0xE7, 0xB0, 0xF8, 0x2C, 0x07, 0x02, 0xE0, + 0x90, 0xF8, 0x23, 0x07, 0x00, 0x01, 0x68, 0x81, 0x40, 0x46, 0xFF, 0xF7, + 0x25, 0xFE, 0x20, 0xB3, 0xA9, 0x88, 0x02, 0x20, 0x0A, 0xF0, 0xFB, 0xFB, + 0x04, 0x21, 0x02, 0xA8, 0x0F, 0xF0, 0x74, 0xFF, 0x07, 0x46, 0x08, 0x21, + 0x68, 0x46, 0x0F, 0xF0, 0x6F, 0xFF, 0x07, 0xFB, 0x00, 0xF3, 0xD9, 0xF8, + 0x00, 0x00, 0x69, 0x89, 0x4F, 0x46, 0x90, 0xF8, 0x2F, 0x07, 0x00, 0xEB, + 0x80, 0x02, 0x40, 0x42, 0x01, 0xEB, 0x42, 0x02, 0x00, 0xEB, 0x80, 0x00, + 0x01, 0xEB, 0x40, 0x00, 0x81, 0xB2, 0x92, 0xB2, 0x40, 0x46, 0xFF, 0xF7, + 0x95, 0xFD, 0x30, 0xB1, 0x06, 0xE0, 0x01, 0x21, 0x02, 0x20, 0x09, 0xF0, + 0xB6, 0xFF, 0x00, 0x24, 0xD8, 0xE7, 0x00, 0x24, 0x04, 0x21, 0x02, 0xA8, + 0x0F, 0xF0, 0x4C, 0xFF, 0x05, 0x46, 0x08, 0x21, 0x68, 0x46, 0x0F, 0xF0, + 0x47, 0xFF, 0x31, 0x68, 0x05, 0xFB, 0x00, 0xF2, 0x40, 0x46, 0x49, 0x1C, + 0xFF, 0xF7, 0x30, 0xFD, 0x00, 0xB9, 0x00, 0x24, 0x40, 0x46, 0xFF, 0xF7, + 0xF3, 0xFC, 0x38, 0x68, 0x31, 0x68, 0x90, 0xF8, 0x29, 0x07, 0x08, 0x70, + 0x38, 0x68, 0x90, 0xF8, 0x30, 0x01, 0x80, 0x07, 0x02, 0xD5, 0x09, 0x48, + 0x0A, 0xF0, 0xC8, 0xFC, 0x00, 0x20, 0x0C, 0xF0, 0x8D, 0xFC, 0x20, 0x46, + 0xBD, 0xE8, 0xFE, 0x83, 0x78, 0x27, 0x10, 0x00, 0x9E, 0x22, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x54, 0x24, 0x10, 0x00, 0x40, 0x10, 0x10, 0x00, + 0x70, 0x53, 0x10, 0x00, 0x10, 0xB5, 0x00, 0x23, 0x01, 0x22, 0x19, 0x46, + 0x10, 0x20, 0x0A, 0xF0, 0x7A, 0xFB, 0xFF, 0xF7, 0x24, 0xFC, 0x00, 0x23, + 0x01, 0x22, 0x19, 0x46, 0x20, 0x20, 0x0A, 0xF0, 0x72, 0xFB, 0x00, 0x23, + 0x1A, 0x46, 0x01, 0x21, 0x10, 0x20, 0x0A, 0xF0, 0x6C, 0xFB, 0x25, 0x4C, + 0x20, 0x68, 0x10, 0xF8, 0x3A, 0x1F, 0x21, 0xF0, 0x01, 0x01, 0x01, 0x70, + 0x22, 0x49, 0x00, 0x20, 0x08, 0x70, 0xFF, 0xF7, 0x08, 0xFF, 0x21, 0x68, + 0x01, 0x28, 0x11, 0xF8, 0x3B, 0x2F, 0x60, 0xF3, 0x00, 0x02, 0x0A, 0x70, + 0x1B, 0xD1, 0x00, 0x23, 0x1A, 0x46, 0x01, 0x21, 0x20, 0x20, 0x0A, 0xF0, + 0x52, 0xFB, 0x00, 0x23, 0x10, 0x21, 0x1A, 0x46, 0x08, 0x46, 0x0A, 0xF0, + 0x4C, 0xFB, 0x20, 0x68, 0x10, 0xF8, 0x3A, 0x1F, 0x21, 0xF0, 0x08, 0x01, + 0x01, 0x70, 0x01, 0xF0, 0x12, 0xF8, 0x21, 0x68, 0x01, 0x28, 0x11, 0xF8, + 0x3B, 0x2F, 0x60, 0xF3, 0xC3, 0x02, 0x0A, 0x70, 0x07, 0xD0, 0x00, 0x23, + 0x1A, 0x46, 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x07, 0x20, 0x0A, 0xF0, + 0x34, 0xBB, 0x00, 0x23, 0x1A, 0x46, 0x10, 0x21, 0x20, 0x20, 0x0A, 0xF0, + 0x2E, 0xFB, 0xFE, 0xF7, 0xF8, 0xFB, 0x00, 0x23, 0x1A, 0x46, 0x19, 0x46, + 0x04, 0x20, 0x0A, 0xF0, 0x26, 0xFB, 0x00, 0x23, 0x1A, 0x46, 0x19, 0x46, + 0xE8, 0xE7, 0x00, 0x00, 0x54, 0x24, 0x10, 0x00, 0x9C, 0x22, 0x10, 0x00, + 0x10, 0xB5, 0x01, 0x46, 0x44, 0x22, 0xF0, 0x48, 0x0D, 0xF0, 0x9D, 0xFD, + 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x20, 0x0C, 0xF0, 0x42, 0xBB, + 0xF8, 0xB5, 0x07, 0x46, 0xEB, 0x48, 0x00, 0x25, 0xE9, 0x4E, 0x03, 0x24, + 0x38, 0x60, 0x64, 0x1E, 0x64, 0xB2, 0x38, 0x46, 0x0B, 0xF0, 0x38, 0xFB, + 0x00, 0x22, 0x6B, 0x46, 0x01, 0x21, 0x10, 0x46, 0x0C, 0xF0, 0x80, 0xFB, + 0x01, 0x21, 0x00, 0x20, 0x0C, 0xF0, 0x5B, 0xFB, 0x96, 0xF8, 0x42, 0x00, + 0x28, 0xB1, 0x09, 0xF0, 0xDA, 0xFE, 0x00, 0x2C, 0xE9, 0xDC, 0x28, 0x46, + 0xF8, 0xBD, 0x01, 0x25, 0xFB, 0xE7, 0x2D, 0xE9, 0xF8, 0x43, 0x80, 0x46, + 0xDB, 0x48, 0xDA, 0x49, 0xD8, 0x4F, 0x00, 0x68, 0x90, 0xF8, 0x24, 0x00, + 0xC8, 0xF8, 0x00, 0x10, 0x05, 0x09, 0x03, 0x24, 0x1A, 0xE0, 0x00, 0x26, + 0x13, 0xE0, 0x64, 0x1E, 0xE4, 0xB2, 0x40, 0x46, 0x0B, 0xF0, 0x0E, 0xFB, + 0x00, 0x22, 0x6B, 0x46, 0x01, 0x21, 0x10, 0x46, 0x0C, 0xF0, 0x56, 0xFB, + 0x01, 0x21, 0x00, 0x20, 0x0C, 0xF0, 0x31, 0xFB, 0x97, 0xF8, 0x42, 0x00, + 0x58, 0xB1, 0x09, 0xF0, 0xB0, 0xFE, 0x00, 0x2C, 0xE9, 0xD1, 0x1E, 0xB1, + 0x6D, 0x1E, 0xED, 0xB2, 0x00, 0x2D, 0xE2, 0xD1, 0x30, 0x46, 0xBD, 0xE8, + 0xF8, 0x83, 0x01, 0x26, 0xF6, 0xE7, 0x2D, 0xE9, 0xF0, 0x47, 0xC2, 0x48, + 0x44, 0x30, 0x0B, 0xF0, 0xB6, 0xFA, 0xC0, 0x49, 0xC1, 0x4F, 0x44, 0x31, + 0x01, 0x20, 0x08, 0x71, 0x38, 0x68, 0x46, 0x7D, 0x00, 0x7D, 0x46, 0x43, + 0x08, 0x46, 0xFF, 0xF7, 0xBE, 0xFF, 0xBD, 0x4C, 0xB1, 0x00, 0x20, 0x46, + 0x0D, 0xF0, 0x8B, 0xFD, 0x38, 0x68, 0x01, 0x21, 0xDF, 0xF8, 0xE8, 0x82, + 0x90, 0xF8, 0x24, 0x00, 0xDF, 0xF8, 0xD0, 0x92, 0x00, 0xF0, 0x0F, 0x05, + 0x6D, 0x1C, 0x17, 0xE0, 0xB1, 0x48, 0x44, 0x30, 0xFF, 0xF7, 0x88, 0xFF, + 0x01, 0x46, 0x01, 0x28, 0x14, 0xD1, 0x00, 0x20, 0xD9, 0xF8, 0x00, 0x20, + 0x08, 0xE0, 0x54, 0xF8, 0x20, 0x30, 0x32, 0xF9, 0x10, 0xC0, 0x63, 0x44, + 0x44, 0xF8, 0x20, 0x30, 0x40, 0x1C, 0x80, 0xB2, 0xB0, 0x42, 0xF4, 0xDB, + 0x6D, 0x1E, 0xED, 0xB2, 0x00, 0x2D, 0xE5, 0xD1, 0x01, 0x29, 0x04, 0xD0, + 0x00, 0x23, 0x1A, 0x46, 0x98, 0xF8, 0x00, 0x10, 0x28, 0xE0, 0x4F, 0xF4, + 0xF1, 0x61, 0xA5, 0x48, 0x0D, 0xF0, 0x37, 0xFD, 0x38, 0x68, 0xA3, 0x4A, + 0x90, 0xF8, 0x24, 0x00, 0x08, 0x32, 0x00, 0xF0, 0x0F, 0x03, 0x00, 0x20, + 0x5B, 0x1C, 0x07, 0xE0, 0x54, 0xF8, 0x20, 0x10, 0x91, 0xFB, 0xF3, 0xF1, + 0x22, 0xF8, 0x10, 0x10, 0x40, 0x1C, 0x80, 0xB2, 0xB0, 0x42, 0xF5, 0xDB, + 0x99, 0x4A, 0x02, 0x20, 0xA2, 0xF8, 0x01, 0x00, 0xB5, 0x20, 0x10, 0x70, + 0x98, 0xF8, 0x00, 0x10, 0xD1, 0x70, 0x38, 0x68, 0x43, 0x7D, 0x13, 0x71, + 0x03, 0x7D, 0x53, 0x71, 0x03, 0x7D, 0x42, 0x7D, 0xBD, 0xE8, 0xF0, 0x47, + 0x02, 0x20, 0x0A, 0xF0, 0x83, 0xB8, 0x2D, 0xE9, 0xF0, 0x47, 0x89, 0x48, + 0x44, 0x30, 0x0B, 0xF0, 0x44, 0xFA, 0x87, 0x49, 0x88, 0x4F, 0x44, 0x31, + 0x03, 0x20, 0x08, 0x71, 0x38, 0x68, 0x46, 0x7D, 0x00, 0x7D, 0x46, 0x43, + 0x08, 0x46, 0xFF, 0xF7, 0x4C, 0xFF, 0x84, 0x4C, 0xB1, 0x00, 0x20, 0x46, + 0x0D, 0xF0, 0x19, 0xFD, 0x38, 0x68, 0x01, 0x21, 0xDF, 0xF8, 0x04, 0x82, + 0x90, 0xF8, 0x24, 0x00, 0xDF, 0xF8, 0xEC, 0x91, 0x00, 0xF0, 0x0F, 0x05, + 0x6D, 0x1C, 0x17, 0xE0, 0x78, 0x48, 0x44, 0x30, 0xFF, 0xF7, 0x16, 0xFF, + 0x01, 0x46, 0x01, 0x28, 0x14, 0xD1, 0x00, 0x20, 0xD9, 0xF8, 0x00, 0x20, + 0x08, 0xE0, 0x54, 0xF8, 0x20, 0x30, 0x32, 0xF9, 0x10, 0xC0, 0x63, 0x44, + 0x44, 0xF8, 0x20, 0x30, 0x40, 0x1C, 0x80, 0xB2, 0xB0, 0x42, 0xF4, 0xDB, + 0x6D, 0x1E, 0xED, 0xB2, 0x00, 0x2D, 0xE5, 0xD1, 0x01, 0x29, 0x04, 0xD0, + 0x00, 0x23, 0x1A, 0x46, 0x98, 0xF8, 0x00, 0x10, 0x28, 0xE0, 0x4F, 0xF4, + 0xF1, 0x61, 0x6C, 0x48, 0x0D, 0xF0, 0xC5, 0xFC, 0x38, 0x68, 0x6A, 0x4A, + 0x90, 0xF8, 0x24, 0x00, 0x08, 0x32, 0x00, 0xF0, 0x0F, 0x03, 0x00, 0x20, + 0x5B, 0x1C, 0x07, 0xE0, 0x54, 0xF8, 0x20, 0x10, 0x91, 0xFB, 0xF3, 0xF1, + 0x22, 0xF8, 0x10, 0x10, 0x40, 0x1C, 0x80, 0xB2, 0xB0, 0x42, 0xF5, 0xDB, + 0x60, 0x4A, 0x04, 0x20, 0xA2, 0xF8, 0x01, 0x00, 0xB5, 0x20, 0x10, 0x70, + 0x98, 0xF8, 0x00, 0x10, 0xD1, 0x70, 0x38, 0x68, 0x43, 0x7D, 0x13, 0x71, + 0x03, 0x7D, 0x53, 0x71, 0x03, 0x7D, 0x42, 0x7D, 0xBD, 0xE8, 0xF0, 0x47, + 0x04, 0x20, 0x0A, 0xF0, 0x11, 0xB8, 0x2D, 0xE9, 0xF0, 0x5F, 0x50, 0x48, + 0x44, 0x30, 0x0B, 0xF0, 0xD2, 0xF9, 0x4E, 0x49, 0xDF, 0xF8, 0x3C, 0xA1, + 0x44, 0x31, 0x01, 0x20, 0x08, 0x72, 0xDA, 0xF8, 0x00, 0x00, 0x56, 0x46, + 0x45, 0x7D, 0x07, 0x7D, 0x08, 0x46, 0xFF, 0xF7, 0xD8, 0xFE, 0x4A, 0x4C, + 0x05, 0xEB, 0x07, 0x09, 0x4F, 0xEA, 0x89, 0x01, 0x20, 0x46, 0x0D, 0xF0, + 0xA2, 0xFC, 0x30, 0x68, 0x01, 0x22, 0xDF, 0xF8, 0x18, 0xB1, 0x90, 0xF8, + 0x24, 0x00, 0x00, 0xF0, 0x0F, 0x06, 0x76, 0x1C, 0x29, 0xE0, 0x3E, 0x48, + 0x44, 0x30, 0xFF, 0xF7, 0xA1, 0xFE, 0x02, 0x46, 0x01, 0x28, 0x26, 0xD1, + 0xDF, 0xF8, 0xE8, 0xC0, 0x00, 0x20, 0xDC, 0xF8, 0x18, 0x10, 0x08, 0xE0, + 0x54, 0xF8, 0x20, 0x30, 0x31, 0xF9, 0x10, 0x80, 0x43, 0x44, 0x44, 0xF8, + 0x20, 0x30, 0x40, 0x1C, 0x80, 0xB2, 0xA8, 0x42, 0xF4, 0xD3, 0x00, 0x20, + 0xDC, 0xF8, 0x1C, 0x30, 0x09, 0xE0, 0x29, 0x18, 0x33, 0xF9, 0x10, 0x80, + 0x54, 0xF8, 0x21, 0xC0, 0x40, 0x1C, 0xC4, 0x44, 0x44, 0xF8, 0x21, 0xC0, + 0x80, 0xB2, 0xB8, 0x42, 0xF3, 0xD3, 0x76, 0x1E, 0xF6, 0xB2, 0x00, 0x2E, + 0xD3, 0xD1, 0x01, 0x2A, 0x04, 0xD0, 0x00, 0x23, 0x1A, 0x46, 0x9B, 0xF8, + 0x00, 0x10, 0x26, 0xE0, 0x4F, 0xF4, 0xF1, 0x61, 0x28, 0x48, 0x0D, 0xF0, + 0x3E, 0xFC, 0xDA, 0xF8, 0x00, 0x60, 0x26, 0x4A, 0x96, 0xF8, 0x24, 0x00, + 0x00, 0xF0, 0x0F, 0x03, 0x00, 0x20, 0x5B, 0x1C, 0x09, 0xE0, 0x54, 0xF8, + 0x20, 0x10, 0x02, 0xEB, 0x40, 0x0C, 0x91, 0xFB, 0xF3, 0xF1, 0x40, 0x1C, + 0xAC, 0xF8, 0x08, 0x10, 0x80, 0xB2, 0x81, 0x45, 0xF3, 0xD8, 0x20, 0x20, + 0xA2, 0xF8, 0x01, 0x00, 0xB5, 0x20, 0x10, 0x70, 0x9B, 0xF8, 0x00, 0x10, + 0xD1, 0x70, 0x15, 0x71, 0x57, 0x71, 0x33, 0x7D, 0x72, 0x7D, 0xBD, 0xE8, + 0xF0, 0x5F, 0x20, 0x20, 0x09, 0xF0, 0x8C, 0xBF, 0x10, 0xB5, 0x04, 0x46, + 0x00, 0x20, 0x0C, 0xF0, 0x34, 0xFA, 0x10, 0x48, 0x01, 0x78, 0x49, 0x1C, + 0x01, 0x70, 0x02, 0x2C, 0x04, 0xD0, 0x04, 0x2C, 0x05, 0xD0, 0x20, 0x2C, + 0x06, 0xD0, 0x07, 0xE0, 0xFF, 0xF7, 0x85, 0xFE, 0x04, 0xE0, 0xFF, 0xF7, + 0xF4, 0xFE, 0x01, 0xE0, 0xFF, 0xF7, 0x63, 0xFF, 0xBD, 0xE8, 0x10, 0x40, + 0x00, 0x20, 0x0C, 0xF0, 0x37, 0xBA, 0x00, 0x00, 0xBC, 0x27, 0x10, 0x00, + 0x31, 0x1F, 0x00, 0x00, 0x50, 0x24, 0x10, 0x00, 0x60, 0x54, 0x10, 0x00, + 0xA8, 0x22, 0x10, 0x00, 0x00, 0x80, 0x01, 0x20, 0x70, 0xB5, 0x04, 0x46, + 0x0D, 0x46, 0xC0, 0x07, 0x02, 0xD0, 0x10, 0x20, 0x09, 0xF0, 0x23, 0xFD, + 0xA0, 0x07, 0x03, 0xD5, 0x29, 0x46, 0x11, 0x20, 0x09, 0xF0, 0x1D, 0xFD, + 0x60, 0x07, 0x03, 0xD5, 0x29, 0x46, 0x12, 0x20, 0x09, 0xF0, 0x17, 0xFD, + 0x20, 0x07, 0x03, 0xD5, 0x29, 0x46, 0x13, 0x20, 0x09, 0xF0, 0x11, 0xFD, + 0xE0, 0x06, 0x03, 0xD5, 0x29, 0x46, 0x16, 0x20, 0x09, 0xF0, 0x0B, 0xFD, + 0xA0, 0x06, 0x03, 0xD5, 0x29, 0x46, 0x17, 0x20, 0x09, 0xF0, 0x05, 0xFD, + 0x60, 0x06, 0x03, 0xD5, 0x29, 0x46, 0x18, 0x20, 0x09, 0xF0, 0xFF, 0xFC, + 0x20, 0x06, 0x03, 0xD5, 0x29, 0x46, 0x19, 0x20, 0x09, 0xF0, 0xF9, 0xFC, + 0xE0, 0x05, 0x03, 0xD5, 0x29, 0x46, 0x14, 0x20, 0x09, 0xF0, 0xF3, 0xFC, + 0xA0, 0x05, 0x03, 0xD5, 0x29, 0x46, 0x15, 0x20, 0x09, 0xF0, 0xED, 0xFC, + 0x60, 0x05, 0x03, 0xD5, 0x29, 0x46, 0x20, 0x20, 0x09, 0xF0, 0xE7, 0xFC, + 0x20, 0x05, 0x03, 0xD5, 0x29, 0x46, 0x21, 0x20, 0x09, 0xF0, 0xE1, 0xFC, + 0xE0, 0x04, 0x03, 0xD5, 0x29, 0x46, 0x22, 0x20, 0x09, 0xF0, 0xDB, 0xFC, + 0xA0, 0x04, 0x05, 0xD5, 0x29, 0x46, 0xBD, 0xE8, 0x70, 0x40, 0x30, 0x20, + 0x09, 0xF0, 0xD3, 0xBC, 0x70, 0xBD, 0x10, 0xB5, 0x01, 0x46, 0x44, 0x22, + 0xF8, 0x48, 0x0D, 0xF0, 0x54, 0xFB, 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, + 0x00, 0x20, 0x0C, 0xF0, 0xF9, 0xB8, 0x30, 0xB5, 0x85, 0xB0, 0x68, 0x46, + 0x0B, 0xF0, 0xBF, 0xF8, 0xF2, 0x48, 0x02, 0x21, 0x00, 0x78, 0x30, 0xB1, + 0x01, 0x28, 0x07, 0xD0, 0x02, 0x28, 0x09, 0xD0, 0x03, 0x28, 0x0A, 0xD0, + 0x0B, 0xE0, 0x8D, 0xF8, 0x07, 0x10, 0x08, 0xE0, 0x05, 0x20, 0x8D, 0xF8, + 0x08, 0x00, 0x04, 0xE0, 0x8D, 0xF8, 0x09, 0x10, 0x01, 0xE0, 0x8D, 0xF8, + 0x0A, 0x10, 0xE8, 0x48, 0xE5, 0x4D, 0x03, 0x24, 0x00, 0x90, 0x68, 0x46, + 0x0B, 0xF0, 0xD8, 0xF8, 0x00, 0x22, 0x04, 0xAB, 0x01, 0x21, 0x10, 0x46, + 0x0C, 0xF0, 0x20, 0xF9, 0x01, 0x21, 0x00, 0x20, 0x0C, 0xF0, 0xFB, 0xF8, + 0x95, 0xF8, 0x42, 0x00, 0x00, 0x28, 0x04, 0xD0, 0x09, 0xF0, 0x79, 0xFC, + 0x64, 0x1E, 0x00, 0x2C, 0xE9, 0xDC, 0x05, 0xB0, 0x30, 0xBD, 0x2D, 0xE9, + 0xF0, 0x47, 0x00, 0x26, 0x0F, 0x46, 0x81, 0x46, 0x35, 0x46, 0x34, 0x46, + 0xFF, 0xF7, 0xBF, 0xFF, 0xD4, 0x49, 0x40, 0xF6, 0xFF, 0x73, 0x8A, 0x46, + 0xC8, 0x6A, 0x0A, 0x6A, 0xD0, 0xF8, 0x00, 0xC0, 0x00, 0x20, 0x0E, 0xE0, + 0x3C, 0xF9, 0x10, 0x10, 0x19, 0xB1, 0x99, 0x42, 0x03, 0xD1, 0x01, 0x25, + 0x06, 0xE0, 0x01, 0x24, 0x04, 0xE0, 0xB9, 0x42, 0x01, 0xDC, 0x49, 0x45, + 0x00, 0xDA, 0x01, 0x26, 0x40, 0x1C, 0x90, 0x42, 0xEE, 0xDB, 0xDA, 0xF8, + 0x30, 0x00, 0xD4, 0x46, 0x9A, 0x46, 0x02, 0x68, 0x00, 0x20, 0xDC, 0xF8, + 0x24, 0x30, 0x0E, 0xE0, 0x32, 0xF9, 0x10, 0x10, 0x19, 0xB1, 0x51, 0x45, + 0x03, 0xD1, 0x01, 0x25, 0x06, 0xE0, 0x01, 0x24, 0x04, 0xE0, 0xB9, 0x42, + 0x01, 0xDC, 0x49, 0x45, 0x00, 0xDA, 0x01, 0x26, 0x40, 0x1C, 0x98, 0x42, + 0xEE, 0xDB, 0xDC, 0xF8, 0x28, 0x00, 0x20, 0xF4, 0xE0, 0x50, 0xCC, 0xF8, + 0x28, 0x00, 0x1C, 0xB1, 0x40, 0xF4, 0x00, 0x60, 0xCC, 0xF8, 0x28, 0x00, + 0x1D, 0xB1, 0x40, 0xF4, 0x80, 0x50, 0xCC, 0xF8, 0x28, 0x00, 0x00, 0x2E, + 0x03, 0xD0, 0x40, 0xF4, 0x80, 0x60, 0xCC, 0xF8, 0x28, 0x00, 0xBD, 0xE8, + 0xF0, 0x87, 0x2D, 0xE9, 0xF0, 0x47, 0xAD, 0x4B, 0x00, 0x26, 0x35, 0x46, + 0xDC, 0x69, 0x30, 0x46, 0x01, 0x27, 0x07, 0xFA, 0x00, 0xF2, 0x22, 0x42, + 0x04, 0xD0, 0xD9, 0x6B, 0x09, 0x5C, 0xB1, 0x42, 0x00, 0xD9, 0x0E, 0x46, + 0x40, 0x1C, 0x20, 0x28, 0xF3, 0xD3, 0xD3, 0xE9, 0x16, 0x78, 0x00, 0x24, + 0x99, 0x46, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x0D, 0xF0, 0x27, 0xFA, + 0x38, 0x40, 0x01, 0xEA, 0x08, 0x01, 0x4A, 0x46, 0x08, 0x43, 0x05, 0xD0, + 0xD9, 0xF8, 0x40, 0x00, 0x00, 0x5D, 0xA8, 0x42, 0x00, 0xD9, 0x05, 0x46, + 0x64, 0x1C, 0x40, 0x2C, 0xEB, 0xD3, 0x11, 0x6D, 0x17, 0x46, 0x4F, 0xF0, + 0xFF, 0x0C, 0x0E, 0x70, 0x50, 0x6D, 0x05, 0x70, 0x52, 0x6B, 0xBC, 0x6A, + 0x12, 0x78, 0x24, 0xF4, 0x00, 0x74, 0x93, 0x19, 0xBA, 0x6B, 0x12, 0x78, + 0xBC, 0x62, 0x2A, 0x44, 0x93, 0x42, 0x11, 0xD2, 0xD7, 0xF8, 0x20, 0x80, + 0xB8, 0xF1, 0x00, 0x0F, 0x0C, 0xDD, 0xD2, 0x1A, 0x32, 0x44, 0xFF, 0x2A, + 0x01, 0xD8, 0x0A, 0x70, 0x04, 0xE0, 0x81, 0xF8, 0x00, 0xC0, 0x44, 0xF4, + 0x00, 0x71, 0xB9, 0x62, 0x05, 0x70, 0xAC, 0xE7, 0x9A, 0x42, 0x11, 0xD2, + 0xD7, 0xF8, 0x24, 0x80, 0xB8, 0xF1, 0x00, 0x0F, 0x0C, 0xDD, 0x9A, 0x1A, + 0x2A, 0x44, 0xFF, 0x2A, 0x01, 0xD8, 0x02, 0x70, 0x04, 0xE0, 0x80, 0xF8, + 0x00, 0xC0, 0x44, 0xF4, 0x00, 0x70, 0xB8, 0x62, 0x0E, 0x70, 0x98, 0xE7, + 0x0E, 0x70, 0xE7, 0xE7, 0x2D, 0xE9, 0xF0, 0x5F, 0x79, 0x4F, 0x00, 0x26, + 0x35, 0x46, 0xB8, 0x6B, 0x79, 0x6B, 0x79, 0x4C, 0x90, 0xF8, 0x00, 0xA0, + 0x91, 0xF8, 0x00, 0x90, 0x0E, 0x70, 0x06, 0x70, 0xFF, 0xF7, 0x89, 0xFF, + 0xFF, 0xF7, 0xF9, 0xFE, 0xF8, 0x6A, 0x01, 0x68, 0x38, 0x6A, 0x42, 0x00, + 0x20, 0x46, 0x0D, 0xF0, 0xFB, 0xF9, 0x38, 0x6B, 0x01, 0x68, 0x78, 0x6A, + 0x42, 0x00, 0x38, 0x6A, 0x04, 0xEB, 0x40, 0x00, 0x0D, 0xF0, 0xF2, 0xF9, + 0x79, 0x6B, 0xFF, 0x20, 0x08, 0x70, 0xB9, 0x6B, 0x08, 0x70, 0xFF, 0xF7, + 0x70, 0xFF, 0xFF, 0xF7, 0xE0, 0xFE, 0xF8, 0x6A, 0x67, 0x49, 0x3B, 0x6A, + 0xD0, 0xF8, 0x00, 0xC0, 0x00, 0x20, 0x0A, 0x68, 0x0E, 0xE0, 0x3C, 0xF9, + 0x10, 0x10, 0x34, 0xF9, 0x10, 0x80, 0xB1, 0xEB, 0x08, 0x01, 0x00, 0xD5, + 0x49, 0x42, 0xB2, 0xF9, 0x4A, 0x87, 0x41, 0x45, 0x01, 0xDA, 0x01, 0x25, + 0x02, 0xE0, 0x40, 0x1C, 0x98, 0x42, 0xEE, 0xDB, 0x38, 0x6B, 0xBB, 0x46, + 0x7F, 0x6A, 0xD0, 0xF8, 0x00, 0xC0, 0x00, 0x20, 0x10, 0xE0, 0x03, 0xEB, + 0x00, 0x08, 0x3C, 0xF9, 0x10, 0x10, 0x34, 0xF9, 0x18, 0x80, 0xB1, 0xEB, + 0x08, 0x01, 0x00, 0xD5, 0x49, 0x42, 0xB2, 0xF9, 0x4A, 0x87, 0x41, 0x45, + 0x01, 0xDA, 0x01, 0x26, 0x02, 0xE0, 0x40, 0x1C, 0xB8, 0x42, 0xEC, 0xDB, + 0xDB, 0xF8, 0x34, 0x10, 0x5C, 0x46, 0x81, 0xF8, 0x00, 0x90, 0xDB, 0xF8, + 0x38, 0x10, 0x81, 0xF8, 0x00, 0xA0, 0xFF, 0xF7, 0x30, 0xFF, 0xA0, 0x6A, + 0x35, 0x43, 0x20, 0xF4, 0x00, 0x50, 0xA0, 0x62, 0x02, 0xD0, 0x40, 0xF4, + 0x00, 0x50, 0xA0, 0x62, 0xBD, 0xE8, 0xF0, 0x9F, 0x2D, 0xE9, 0xFF, 0x4F, + 0x81, 0xB0, 0x00, 0x24, 0x06, 0x46, 0xDD, 0xE9, 0x0F, 0x79, 0x01, 0x20, + 0x25, 0x46, 0xDD, 0xF8, 0x44, 0x80, 0xDF, 0xF8, 0xEC, 0xA0, 0x83, 0x46, + 0x00, 0x90, 0x5C, 0xE0, 0x04, 0x98, 0x05, 0xF0, 0x07, 0x01, 0x00, 0xEB, + 0xE5, 0x00, 0x0B, 0xFA, 0x01, 0xF2, 0x00, 0x78, 0x02, 0x42, 0x51, 0xD0, + 0x19, 0xF8, 0x05, 0x00, 0x48, 0xBB, 0x37, 0xF9, 0x14, 0x00, 0x00, 0x28, + 0x0B, 0xDD, 0x36, 0xF9, 0x14, 0x10, 0x40, 0x1A, 0x00, 0xD5, 0x40, 0x42, + 0xB8, 0xF8, 0x00, 0x10, 0x80, 0xB2, 0x81, 0x42, 0x01, 0xD2, 0xA8, 0xF8, + 0x00, 0x00, 0x36, 0xF9, 0x14, 0x20, 0xBA, 0xF8, 0x08, 0x10, 0x8A, 0x42, + 0x1B, 0xDD, 0x29, 0x46, 0x0E, 0x9A, 0x02, 0x98, 0x0D, 0xF0, 0x52, 0xFB, + 0x00, 0x28, 0x0D, 0xDD, 0x00, 0x21, 0x40, 0x1E, 0xC2, 0xB2, 0x00, 0x91, + 0x29, 0x46, 0x0E, 0x9B, 0x02, 0x98, 0x0D, 0xF0, 0x12, 0xFC, 0x36, 0xF8, + 0x14, 0x00, 0x27, 0xF8, 0x14, 0x00, 0x22, 0xE0, 0xDA, 0xF8, 0x28, 0x10, + 0x41, 0xF0, 0x40, 0x01, 0xCA, 0xF8, 0x28, 0x10, 0x1B, 0xE0, 0x37, 0xF9, + 0x14, 0x00, 0x00, 0x28, 0x15, 0xDD, 0x40, 0x1A, 0xA2, 0xEB, 0x01, 0x01, + 0x00, 0xD5, 0x40, 0x42, 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0x88, 0x42, + 0x0B, 0xDC, 0x29, 0x46, 0x0E, 0x9A, 0x02, 0x98, 0x0D, 0xF0, 0x28, 0xFB, + 0x40, 0x1C, 0xC2, 0xB2, 0x29, 0x46, 0x0E, 0x9B, 0x02, 0x98, 0x0D, 0xF0, + 0xEC, 0xFB, 0x09, 0xF8, 0x05, 0xB0, 0x64, 0x1C, 0x6D, 0x1C, 0x03, 0x98, + 0x84, 0x42, 0x9F, 0xDB, 0x00, 0x98, 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x2D, 0xE9, 0xFF, 0x5F, 0x00, 0x25, 0x98, 0x46, 0x91, 0x46, 0x82, 0x46, + 0x2C, 0x46, 0x01, 0x27, 0x02, 0x4E, 0xDD, 0xF8, 0x38, 0xB0, 0x30, 0xE0, + 0x10, 0x28, 0x10, 0x00, 0xB0, 0x22, 0x10, 0x00, 0xC3, 0x23, 0x00, 0x00, + 0x60, 0x54, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x08, 0xEB, 0xE4, 0x00, + 0x01, 0x22, 0x01, 0x78, 0x04, 0xF0, 0x07, 0x00, 0x82, 0x40, 0x0A, 0x42, + 0x1C, 0xD0, 0x3A, 0xF9, 0x15, 0x00, 0x31, 0x89, 0x88, 0x42, 0x16, 0xDA, + 0x5A, 0x46, 0x21, 0x46, 0x01, 0x98, 0x0D, 0xF0, 0xED, 0xFA, 0x3F, 0x28, + 0x0B, 0xDA, 0x00, 0x27, 0x00, 0x1D, 0x3F, 0x28, 0x00, 0xDD, 0x3F, 0x20, + 0xC2, 0xB2, 0x5B, 0x46, 0x21, 0x46, 0x01, 0x98, 0x0D, 0xF0, 0xAB, 0xFB, + 0x03, 0xE0, 0xB0, 0x6A, 0x40, 0xF0, 0x80, 0x00, 0xB0, 0x62, 0x6D, 0x1C, + 0x64, 0x1C, 0x4D, 0x45, 0xD6, 0xDB, 0x38, 0x46, 0x04, 0xB0, 0x47, 0xE7, + 0x2D, 0xE9, 0xF0, 0x4F, 0x95, 0xB0, 0x8A, 0x46, 0x83, 0x46, 0x20, 0x21, + 0x04, 0xA8, 0x0D, 0xF0, 0x82, 0xF9, 0x20, 0x21, 0x0C, 0xA8, 0x0D, 0xF0, + 0x7E, 0xF9, 0x00, 0x24, 0x25, 0x46, 0x27, 0x46, 0xA0, 0x46, 0xF6, 0x4E, + 0xFF, 0xF7, 0xCD, 0xFD, 0xB0, 0x6A, 0x20, 0xF0, 0x80, 0x00, 0xB0, 0x62, + 0x54, 0xB9, 0xCD, 0xF8, 0x00, 0x80, 0xF0, 0x6A, 0xF0, 0x4B, 0x32, 0x6A, + 0x00, 0x68, 0x1C, 0x33, 0xB1, 0x6C, 0xFF, 0xF7, 0x9B, 0xFF, 0x04, 0x46, + 0x55, 0xB9, 0x01, 0x20, 0x00, 0x90, 0x30, 0x6B, 0xEA, 0x4B, 0x72, 0x6A, + 0x00, 0x68, 0x58, 0x33, 0xF1, 0x6C, 0xFF, 0xF7, 0x8F, 0xFF, 0x05, 0x46, + 0x04, 0xB1, 0x55, 0xB9, 0xFF, 0xF7, 0x3B, 0xFE, 0xB0, 0x6C, 0x0D, 0xF0, + 0xE9, 0xFA, 0xF0, 0x6C, 0x0D, 0xF0, 0x26, 0xFB, 0x7F, 0x1C, 0x18, 0x2F, + 0xD4, 0xDB, 0x00, 0x24, 0xDF, 0xF8, 0x80, 0x93, 0xAB, 0xF8, 0x00, 0x40, + 0xAA, 0xF8, 0x00, 0x40, 0x25, 0x46, 0x09, 0xF5, 0x08, 0x68, 0x48, 0x46, + 0x31, 0x6A, 0x0D, 0xF0, 0x1E, 0xF9, 0x40, 0x46, 0x71, 0x6A, 0x0D, 0xF0, + 0x1A, 0xF9, 0x00, 0x27, 0xFF, 0xF7, 0x8F, 0xFD, 0xB0, 0x6A, 0x20, 0xF0, + 0x40, 0x00, 0xB0, 0x62, 0x74, 0xB9, 0x04, 0xAB, 0xCD, 0xE9, 0x02, 0x3B, + 0x00, 0x22, 0xCD, 0xE9, 0x00, 0x29, 0xF0, 0x6A, 0xCF, 0x4B, 0x32, 0x6A, + 0x00, 0x68, 0x1C, 0x33, 0xB1, 0x6C, 0xFF, 0xF7, 0xE5, 0xFE, 0x04, 0x46, + 0x75, 0xB9, 0x0C, 0xAB, 0xCD, 0xE9, 0x02, 0x3A, 0x01, 0x22, 0xCD, 0xE9, + 0x00, 0x28, 0x30, 0x6B, 0xC7, 0x4B, 0x72, 0x6A, 0x00, 0x68, 0x58, 0x33, + 0xF1, 0x6C, 0xFF, 0xF7, 0xD5, 0xFE, 0x05, 0x46, 0x24, 0xB1, 0x1D, 0xB1, + 0xFF, 0xF7, 0xF5, 0xFD, 0x15, 0xB0, 0x3F, 0xE7, 0xFF, 0xF7, 0xF1, 0xFD, + 0xB0, 0x6C, 0x0D, 0xF0, 0x9F, 0xFA, 0xF0, 0x6C, 0x0D, 0xF0, 0xDC, 0xFA, + 0x7F, 0x1C, 0x18, 0x2F, 0xC8, 0xDB, 0xF1, 0xE7, 0x2D, 0xE9, 0xF0, 0x5F, + 0x00, 0x25, 0x82, 0x46, 0x89, 0x46, 0xAA, 0xF8, 0x00, 0x50, 0x4F, 0xF0, + 0x01, 0x0B, 0x2E, 0x46, 0x0D, 0x80, 0x2F, 0x46, 0xFF, 0xF7, 0x4B, 0xFD, + 0xB3, 0x4A, 0xD0, 0x6A, 0xD2, 0xF8, 0x20, 0xC0, 0x04, 0x68, 0xBC, 0xF1, + 0x00, 0x0F, 0x02, 0xDD, 0xB4, 0xF9, 0x00, 0x00, 0x01, 0xE0, 0x4F, 0xF6, + 0xFF, 0x70, 0x00, 0xB2, 0x01, 0x21, 0x05, 0xE0, 0x34, 0xF9, 0x11, 0x30, + 0x83, 0x42, 0x00, 0xDD, 0x18, 0x46, 0x49, 0x1C, 0x61, 0x45, 0xF7, 0xDB, + 0x11, 0x6B, 0x53, 0x6A, 0xD1, 0xF8, 0x00, 0x80, 0x00, 0x2B, 0x02, 0xDD, + 0xB8, 0xF9, 0x00, 0x10, 0x01, 0xE0, 0x4F, 0xF6, 0xFF, 0x71, 0x0C, 0xB2, + 0x01, 0x21, 0x96, 0x46, 0x05, 0xE0, 0x38, 0xF9, 0x11, 0x20, 0xA2, 0x42, + 0x00, 0xDD, 0x14, 0x46, 0x49, 0x1C, 0x99, 0x42, 0xF7, 0xDB, 0xA0, 0x42, + 0x01, 0xDD, 0x02, 0x46, 0x00, 0xE0, 0x22, 0x46, 0xDE, 0xF8, 0x28, 0x80, + 0xBB, 0xF1, 0x00, 0x0F, 0x28, 0xF0, 0x30, 0x08, 0xCE, 0xF8, 0x28, 0x80, + 0x0E, 0xD0, 0x05, 0x46, 0xBE, 0xF8, 0x06, 0x00, 0x26, 0x46, 0x82, 0x42, + 0x05, 0xDB, 0x92, 0x48, 0x81, 0x6A, 0x41, 0xF0, 0x10, 0x01, 0x81, 0x62, + 0x68, 0xE6, 0x4F, 0xF0, 0x00, 0x0B, 0x1A, 0xE0, 0xBC, 0xF1, 0x00, 0x0F, + 0x0A, 0xDD, 0x41, 0x1B, 0x00, 0xD5, 0x49, 0x42, 0xBA, 0xF8, 0x00, 0x50, + 0x89, 0xB2, 0x8D, 0x42, 0x01, 0xD2, 0xAA, 0xF8, 0x00, 0x10, 0x05, 0x46, + 0x00, 0x2B, 0x0A, 0xDD, 0xA0, 0x1B, 0x00, 0xD5, 0x40, 0x42, 0xB9, 0xF8, + 0x00, 0x10, 0x80, 0xB2, 0x81, 0x42, 0x01, 0xD2, 0xA9, 0xF8, 0x00, 0x00, + 0x26, 0x46, 0xBE, 0xF8, 0x06, 0x00, 0x73, 0x46, 0x82, 0x42, 0x07, 0xDD, + 0x7D, 0x48, 0x41, 0x6C, 0x08, 0x78, 0x00, 0x28, 0xD6, 0xD0, 0x40, 0x1E, + 0x08, 0x70, 0x3D, 0xE6, 0x59, 0x6C, 0x08, 0x78, 0x0F, 0x28, 0x02, 0xD2, + 0x40, 0x1C, 0x08, 0x70, 0x03, 0xE0, 0x48, 0xF0, 0x20, 0x00, 0xCE, 0xF8, + 0x28, 0x00, 0x7F, 0x1C, 0x18, 0x2F, 0xFF, 0xF6, 0x7B, 0xAF, 0x2D, 0xE6, + 0xF0, 0xB5, 0x00, 0x25, 0x2C, 0x46, 0x01, 0x27, 0x12, 0xE0, 0x02, 0xEB, + 0xE4, 0x06, 0x04, 0xF0, 0x07, 0x0C, 0x36, 0x78, 0x07, 0xFA, 0x0C, 0xFE, + 0x1E, 0xEA, 0x06, 0x0F, 0x07, 0xD0, 0x06, 0x5D, 0x9E, 0x42, 0x01, 0xD9, + 0xF6, 0x1A, 0x00, 0xE0, 0x00, 0x26, 0x06, 0x55, 0x6D, 0x1C, 0x64, 0x1C, + 0x8D, 0x42, 0xEA, 0xDB, 0xF0, 0xBD, 0x2D, 0xE9, 0xFF, 0x4F, 0x81, 0xB0, + 0x00, 0x24, 0x06, 0x46, 0xDD, 0xE9, 0x0F, 0x79, 0x01, 0x20, 0x25, 0x46, + 0xDD, 0xF8, 0x44, 0x80, 0xDF, 0xF8, 0x78, 0xA1, 0x83, 0x46, 0x00, 0x90, + 0x5C, 0xE0, 0x04, 0x98, 0x05, 0xF0, 0x07, 0x01, 0x00, 0xEB, 0xE5, 0x00, + 0x0B, 0xFA, 0x01, 0xF2, 0x00, 0x78, 0x02, 0x42, 0x51, 0xD0, 0x19, 0xF8, + 0x05, 0x00, 0x48, 0xBB, 0x37, 0xF9, 0x14, 0x00, 0x00, 0x28, 0x0B, 0xDD, + 0x36, 0xF9, 0x14, 0x10, 0x08, 0x1A, 0x00, 0xD5, 0x40, 0x42, 0xB8, 0xF8, + 0x00, 0x10, 0x80, 0xB2, 0x81, 0x42, 0x01, 0xD2, 0xA8, 0xF8, 0x00, 0x00, + 0x36, 0xF9, 0x14, 0x20, 0xBA, 0xF8, 0x04, 0x10, 0x8A, 0x42, 0x1B, 0xDA, + 0x29, 0x46, 0x0E, 0x9A, 0x02, 0x98, 0x0D, 0xF0, 0x6B, 0xF9, 0xFF, 0x28, + 0x0D, 0xDA, 0x00, 0x21, 0x40, 0x1C, 0xC2, 0xB2, 0x00, 0x91, 0x29, 0x46, + 0x0E, 0x9B, 0x02, 0x98, 0x0D, 0xF0, 0x2C, 0xFA, 0x36, 0xF8, 0x14, 0x00, + 0x27, 0xF8, 0x14, 0x00, 0x22, 0xE0, 0xDA, 0xF8, 0x28, 0x10, 0x41, 0xF0, + 0x08, 0x01, 0xCA, 0xF8, 0x28, 0x10, 0x1B, 0xE0, 0x37, 0xF9, 0x14, 0x00, + 0x00, 0x28, 0x15, 0xDD, 0x40, 0x1A, 0xA2, 0xEB, 0x01, 0x01, 0x00, 0xD5, + 0x40, 0x42, 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0x88, 0x42, 0x0B, 0xDC, + 0x29, 0x46, 0x0E, 0x9A, 0x02, 0x98, 0x0D, 0xF0, 0x41, 0xF9, 0x40, 0x1E, + 0xC2, 0xB2, 0x29, 0x46, 0x0E, 0x9B, 0x02, 0x98, 0x0D, 0xF0, 0x06, 0xFA, + 0x09, 0xF8, 0x05, 0xB0, 0x64, 0x1C, 0x6D, 0x1C, 0x03, 0x98, 0x84, 0x42, + 0x9F, 0xDB, 0x00, 0x98, 0x13, 0xE6, 0x2D, 0xE9, 0xFF, 0x5F, 0x00, 0x25, + 0x98, 0x46, 0x91, 0x46, 0x82, 0x46, 0x2C, 0x46, 0x01, 0x27, 0x27, 0x4E, + 0xDD, 0xF8, 0x38, 0xB0, 0x27, 0xE0, 0x08, 0xEB, 0xE4, 0x00, 0x01, 0x22, + 0x01, 0x78, 0x04, 0xF0, 0x07, 0x00, 0x82, 0x40, 0x0A, 0x42, 0x1D, 0xD0, + 0x3A, 0xF9, 0x15, 0x00, 0xB1, 0x88, 0x88, 0x42, 0x17, 0xDD, 0x5A, 0x46, + 0x21, 0x46, 0x01, 0x98, 0x0D, 0xF0, 0x12, 0xF9, 0x00, 0x28, 0x0C, 0xDD, + 0x00, 0x27, 0x04, 0x28, 0x01, 0xDB, 0x00, 0x1F, 0x00, 0xE0, 0x00, 0x20, + 0xC2, 0xB2, 0x5B, 0x46, 0x21, 0x46, 0x01, 0x98, 0x0D, 0xF0, 0xD0, 0xF9, + 0x03, 0xE0, 0xB0, 0x6A, 0x40, 0xF0, 0x04, 0x00, 0xB0, 0x62, 0x6D, 0x1C, + 0x64, 0x1C, 0x4D, 0x45, 0xD5, 0xDB, 0x38, 0x46, 0x1E, 0xE6, 0xF0, 0xB5, + 0x00, 0x24, 0x07, 0x46, 0x01, 0x20, 0x21, 0x46, 0x84, 0x46, 0xDF, 0xF8, + 0x30, 0xE0, 0x12, 0xE0, 0x03, 0xEB, 0xE1, 0x05, 0x2E, 0x78, 0x01, 0xF0, + 0x07, 0x05, 0x0C, 0xFA, 0x05, 0xF5, 0x35, 0x42, 0x08, 0xD0, 0x37, 0xF9, + 0x14, 0x50, 0xBE, 0xF8, 0x04, 0x60, 0xB5, 0x42, 0x01, 0xDC, 0x00, 0x20, + 0xF0, 0xBD, 0x64, 0x1C, 0x49, 0x1C, 0x94, 0x42, 0xEA, 0xDB, 0xF0, 0xBD, + 0xB0, 0x22, 0x10, 0x00, 0x60, 0x54, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x4F, + 0x95, 0xB0, 0x8A, 0x46, 0x83, 0x46, 0x20, 0x21, 0x04, 0xA8, 0x0C, 0xF0, + 0x80, 0xFF, 0x20, 0x21, 0x0C, 0xA8, 0x0C, 0xF0, 0x7C, 0xFF, 0x00, 0x24, + 0x25, 0x46, 0xFF, 0x23, 0xFE, 0x4A, 0xA2, 0xF1, 0x1C, 0x06, 0x31, 0x6A, + 0xF0, 0x6B, 0x00, 0xF0, 0xB0, 0xFA, 0xFF, 0x23, 0x06, 0xF1, 0x58, 0x02, + 0x71, 0x6A, 0x30, 0x6C, 0x00, 0xF0, 0xA9, 0xFA, 0xFF, 0xF7, 0x4D, 0xFC, + 0xFF, 0xF7, 0xBD, 0xFB, 0xF0, 0x6A, 0xF5, 0x4B, 0x32, 0x6A, 0x00, 0x68, + 0xF1, 0x6B, 0xFF, 0xF7, 0xB4, 0xFF, 0x00, 0xF0, 0x01, 0x07, 0x30, 0x6B, + 0x06, 0xF1, 0x58, 0x03, 0x72, 0x6A, 0x00, 0x68, 0x31, 0x6C, 0xFF, 0xF7, + 0xAA, 0xFF, 0x38, 0x42, 0xB0, 0x6A, 0x20, 0xF0, 0x08, 0x00, 0xB0, 0x62, + 0x03, 0xD1, 0x40, 0xF0, 0x08, 0x00, 0xB0, 0x62, 0x3A, 0xE6, 0x00, 0x27, + 0xB8, 0x46, 0xFF, 0xF7, 0x9E, 0xFB, 0xB0, 0x6A, 0x20, 0xF0, 0x04, 0x00, + 0xB0, 0x62, 0x4C, 0xB9, 0xCD, 0xF8, 0x00, 0x80, 0xF0, 0x6A, 0xE2, 0x4B, + 0x32, 0x6A, 0x00, 0x68, 0xF1, 0x6B, 0xFF, 0xF7, 0x56, 0xFF, 0x04, 0x46, + 0x55, 0xB9, 0x01, 0x20, 0x00, 0x90, 0x30, 0x6B, 0xDC, 0x4B, 0x72, 0x6A, + 0x00, 0x68, 0x3C, 0x33, 0x31, 0x6C, 0xFF, 0xF7, 0x4A, 0xFF, 0x05, 0x46, + 0x04, 0xB1, 0x25, 0xB9, 0xFF, 0xF7, 0x0D, 0xFC, 0x7F, 0x1C, 0x60, 0x2F, + 0xDB, 0xDB, 0x00, 0x24, 0xDF, 0xF8, 0x54, 0x93, 0xAB, 0xF8, 0x00, 0x40, + 0xAA, 0xF8, 0x00, 0x40, 0x25, 0x46, 0x09, 0xF5, 0x08, 0x68, 0x48, 0x46, + 0x31, 0x6A, 0x0C, 0xF0, 0xF6, 0xFE, 0x40, 0x46, 0x71, 0x6A, 0x0C, 0xF0, + 0xF2, 0xFE, 0x00, 0x27, 0xFF, 0xF7, 0x67, 0xFB, 0xB0, 0x6A, 0x20, 0xF0, + 0x08, 0x00, 0xB0, 0x62, 0x6C, 0xB9, 0x04, 0xAB, 0x00, 0x22, 0xCD, 0xE9, + 0x02, 0x3B, 0xCD, 0xE9, 0x00, 0x29, 0xF0, 0x6A, 0xC4, 0x4B, 0x32, 0x6A, + 0x00, 0x68, 0xF1, 0x6B, 0xFF, 0xF7, 0xA9, 0xFE, 0x04, 0x46, 0x75, 0xB9, + 0x0C, 0xAB, 0xCD, 0xE9, 0x02, 0x3A, 0x01, 0x22, 0xCD, 0xE9, 0x00, 0x28, + 0x30, 0x6B, 0xBD, 0x4B, 0x72, 0x6A, 0x00, 0x68, 0x3C, 0x33, 0x31, 0x6C, + 0xFF, 0xF7, 0x99, 0xFE, 0x05, 0x46, 0x1C, 0xB1, 0x15, 0xB1, 0xFF, 0xF7, + 0xCE, 0xFB, 0xD7, 0xE5, 0xFF, 0xF7, 0xCB, 0xFB, 0x7F, 0x1C, 0x60, 0x2F, + 0xD0, 0xDB, 0xD1, 0xE5, 0xF0, 0xB5, 0x00, 0x2A, 0x05, 0x9F, 0x2A, 0xDD, + 0xB0, 0xF9, 0x00, 0x50, 0x01, 0x24, 0x05, 0xE0, 0x30, 0xF9, 0x14, 0x60, + 0xAE, 0x42, 0x00, 0xDD, 0x35, 0x46, 0x64, 0x1C, 0x94, 0x42, 0xF7, 0xDB, + 0xB7, 0xF9, 0x00, 0x00, 0x38, 0xB1, 0x28, 0x1A, 0x00, 0xD5, 0x40, 0x42, + 0x1A, 0x88, 0x80, 0xB2, 0x82, 0x42, 0x00, 0xD2, 0x18, 0x80, 0xA6, 0x4A, + 0x3D, 0x80, 0x1C, 0x3A, 0x50, 0x88, 0x85, 0x42, 0x08, 0x78, 0x0A, 0xDA, + 0xFF, 0x28, 0x03, 0xD2, 0x40, 0x1C, 0x08, 0x70, 0x00, 0x20, 0xF0, 0xBD, + 0x90, 0x6A, 0x40, 0xF0, 0x02, 0x00, 0x90, 0x62, 0x01, 0xE0, 0x40, 0x1E, + 0x08, 0x70, 0x01, 0x20, 0xF0, 0xBD, 0x30, 0xB5, 0xB0, 0xF9, 0x00, 0x40, + 0x01, 0x23, 0x05, 0xE0, 0x30, 0xF9, 0x13, 0x50, 0xA5, 0x42, 0x00, 0xDD, + 0x2C, 0x46, 0x5B, 0x1C, 0x93, 0x42, 0xF7, 0xDB, 0x00, 0x2A, 0x12, 0xDD, + 0x93, 0x4A, 0x1C, 0x3A, 0x50, 0x88, 0x84, 0x42, 0x0D, 0xDD, 0x08, 0x78, + 0x38, 0xB1, 0x10, 0x28, 0x01, 0xD3, 0x10, 0x38, 0x00, 0xE0, 0x00, 0x20, + 0x08, 0x70, 0x00, 0x20, 0x30, 0xBD, 0x90, 0x6A, 0x40, 0xF0, 0x01, 0x00, + 0x90, 0x62, 0x01, 0x20, 0x30, 0xBD, 0x2D, 0xE9, 0xFE, 0x43, 0x88, 0x4F, + 0x88, 0x46, 0x1C, 0x3F, 0x81, 0x46, 0x79, 0x6B, 0xFF, 0x20, 0x00, 0x25, + 0x08, 0x70, 0xB9, 0x6B, 0x2E, 0x46, 0x08, 0x70, 0xFF, 0xF7, 0x63, 0xFB, + 0xFF, 0xF7, 0xD3, 0xFA, 0xF8, 0x6A, 0x3A, 0x6A, 0x79, 0x6B, 0x00, 0x68, + 0x00, 0xF0, 0xCB, 0xF9, 0x00, 0xF0, 0x01, 0x04, 0x38, 0x6B, 0x7A, 0x6A, + 0xB9, 0x6B, 0x00, 0x68, 0x00, 0xF0, 0xC3, 0xF9, 0x20, 0x42, 0xB8, 0x6A, + 0x20, 0xF0, 0x02, 0x00, 0xB8, 0x62, 0x04, 0xD1, 0x40, 0xF0, 0x02, 0x00, + 0xB8, 0x62, 0xBD, 0xE8, 0xFE, 0x83, 0x00, 0x24, 0xFF, 0xF7, 0xB7, 0xFA, + 0xB8, 0x6A, 0x20, 0xF0, 0x01, 0x00, 0xB8, 0x62, 0x35, 0xB9, 0xF8, 0x6A, + 0x3A, 0x6A, 0x79, 0x6B, 0x00, 0x68, 0xFF, 0xF7, 0xA4, 0xFF, 0x05, 0x46, + 0x36, 0xB9, 0x38, 0x6B, 0x7A, 0x6A, 0xB9, 0x6B, 0x00, 0x68, 0xFF, 0xF7, + 0x9C, 0xFF, 0x06, 0x46, 0x05, 0xB1, 0x26, 0xB9, 0xFF, 0xF7, 0x2D, 0xFB, + 0x64, 0x1C, 0x18, 0x2C, 0xE2, 0xDB, 0x00, 0x24, 0xA9, 0xF8, 0x00, 0x40, + 0xA8, 0xF8, 0x00, 0x40, 0x25, 0x46, 0xAD, 0xF8, 0x04, 0x40, 0xAD, 0xF8, + 0x08, 0x40, 0x26, 0x46, 0xFF, 0xF7, 0x8F, 0xFA, 0xB8, 0x6A, 0x20, 0xF0, + 0x02, 0x00, 0xB8, 0x62, 0x4C, 0xB9, 0x01, 0xA8, 0x00, 0x90, 0xF8, 0x6A, + 0x4B, 0x46, 0x3A, 0x6A, 0x00, 0x68, 0x79, 0x6B, 0xFF, 0xF7, 0x48, 0xFF, + 0x04, 0x46, 0x4D, 0xB9, 0x02, 0xA8, 0x00, 0x90, 0x38, 0x6B, 0x43, 0x46, + 0x7A, 0x6A, 0x00, 0x68, 0xB9, 0x6B, 0xFF, 0xF7, 0x3D, 0xFF, 0x05, 0x46, + 0x1C, 0xB1, 0x15, 0xB1, 0xFF, 0xF7, 0xFF, 0xFA, 0xB3, 0xE7, 0xFF, 0xF7, + 0xFC, 0xFA, 0x76, 0x1C, 0x18, 0x2E, 0xD9, 0xDB, 0xAD, 0xE7, 0x2D, 0xE9, + 0xF0, 0x41, 0x4B, 0x4C, 0x00, 0x25, 0x1C, 0x3C, 0x01, 0x26, 0x28, 0x46, + 0xA5, 0x62, 0x0B, 0xF0, 0x0A, 0xFC, 0x0A, 0xF0, 0xD2, 0xFE, 0xE0, 0x61, + 0x0A, 0xF0, 0xB8, 0xFE, 0xC4, 0xE9, 0x16, 0x01, 0x04, 0x21, 0x43, 0x48, + 0x0E, 0xF0, 0xB8, 0xFE, 0x20, 0x62, 0x08, 0x21, 0x04, 0xF1, 0x58, 0x00, + 0x0E, 0xF0, 0xB2, 0xFE, 0xDF, 0xF8, 0x00, 0x81, 0x60, 0x62, 0x40, 0x48, + 0xD8, 0xF8, 0x00, 0x10, 0x00, 0xF1, 0x38, 0x07, 0x01, 0xF1, 0x12, 0x03, + 0x5A, 0x1C, 0xA2, 0x63, 0xC4, 0xE9, 0x0F, 0x07, 0x97, 0x1E, 0x67, 0x64, + 0x00, 0xF1, 0x20, 0x07, 0xA7, 0x64, 0x3A, 0x37, 0xE7, 0x64, 0x57, 0x1C, + 0x15, 0x31, 0xC4, 0xE9, 0x14, 0x71, 0x36, 0x49, 0xE1, 0x62, 0x09, 0x1D, + 0xC4, 0xE9, 0x0C, 0x13, 0x34, 0x4F, 0x01, 0x21, 0x21, 0x70, 0x39, 0x68, + 0x91, 0xF8, 0x4C, 0xC7, 0x5F, 0xEA, 0xCC, 0x7C, 0x02, 0xD0, 0x1D, 0x70, + 0x15, 0x70, 0x06, 0xE0, 0x91, 0xF8, 0x56, 0x17, 0x19, 0x70, 0x39, 0x68, + 0x91, 0xF8, 0x57, 0x17, 0x11, 0x70, 0x39, 0x68, 0x91, 0xF8, 0x4C, 0x17, + 0x89, 0x07, 0x0B, 0xD5, 0x00, 0x23, 0x23, 0x4A, 0x21, 0x6A, 0x00, 0xF0, + 0xFC, 0xF8, 0x21, 0x4A, 0x00, 0x23, 0x3C, 0x32, 0x61, 0x6A, 0x20, 0x6C, + 0x00, 0xF0, 0xF5, 0xF8, 0x38, 0x68, 0x90, 0xF8, 0x4C, 0x17, 0x49, 0x07, + 0x02, 0xD5, 0x60, 0x6C, 0x05, 0x70, 0x03, 0xE0, 0x61, 0x6C, 0x90, 0xF8, + 0x58, 0x07, 0x08, 0x70, 0x38, 0x68, 0x90, 0xF8, 0x4C, 0x07, 0x00, 0x07, + 0x0C, 0xD5, 0x00, 0x23, 0x14, 0x49, 0x22, 0x6A, 0xA0, 0x6C, 0x0C, 0xF0, + 0x93, 0xFE, 0x12, 0x49, 0x01, 0x23, 0x3C, 0x31, 0x62, 0x6A, 0xE0, 0x6C, + 0x0C, 0xF0, 0x8C, 0xFE, 0xFF, 0xF7, 0x7B, 0xFA, 0x38, 0x68, 0x90, 0xF8, + 0x4C, 0x17, 0xC9, 0x07, 0x0F, 0xD0, 0x0B, 0x49, 0xB0, 0xF8, 0x4E, 0x07, + 0x60, 0x80, 0x10, 0x39, 0x88, 0x1E, 0xFF, 0xF7, 0xFC, 0xFE, 0x61, 0x89, + 0x05, 0x20, 0x09, 0xF0, 0xC2, 0xFA, 0xA1, 0x89, 0x06, 0x20, 0x09, 0xF0, + 0xBE, 0xFA, 0x38, 0x68, 0x90, 0xF8, 0x4C, 0x17, 0x89, 0x07, 0x1C, 0xD5, + 0x0C, 0xE0, 0x00, 0x00, 0xCC, 0x22, 0x10, 0x00, 0x60, 0x54, 0x10, 0x00, + 0x54, 0x24, 0x10, 0x00, 0x34, 0x17, 0x10, 0x00, 0x28, 0x28, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0xB0, 0xF8, 0x50, 0x07, 0x6C, 0x49, 0xA0, 0x80, + 0x88, 0x1E, 0xFF, 0xF7, 0xDF, 0xFD, 0xE1, 0x89, 0x07, 0x20, 0x09, 0xF0, + 0xA0, 0xFA, 0x21, 0x8A, 0x08, 0x20, 0x09, 0xF0, 0x9C, 0xFA, 0x3A, 0x68, + 0x92, 0xF8, 0x4C, 0x07, 0xC1, 0x07, 0x18, 0xD0, 0x40, 0x07, 0x16, 0xD5, + 0x60, 0x6B, 0x92, 0xF8, 0x59, 0x27, 0x01, 0x78, 0x91, 0x42, 0x02, 0xD9, + 0x89, 0x1A, 0x01, 0x70, 0x00, 0xE0, 0x05, 0x70, 0xA0, 0x6B, 0x3A, 0x68, + 0x01, 0x78, 0x92, 0xF8, 0x59, 0x27, 0x91, 0x42, 0x02, 0xD9, 0x89, 0x1A, + 0x01, 0x70, 0x00, 0xE0, 0x05, 0x70, 0xFF, 0xF7, 0x26, 0xFA, 0x38, 0x68, + 0x90, 0xF8, 0x4C, 0x17, 0x8A, 0x07, 0x14, 0xD5, 0x09, 0x07, 0x12, 0xD5, + 0x53, 0x4A, 0x90, 0xF8, 0x5A, 0x37, 0x0C, 0x32, 0x21, 0x6A, 0xE0, 0x6B, + 0xFF, 0xF7, 0xC2, 0xFC, 0x38, 0x68, 0x4F, 0x4A, 0x61, 0x6A, 0x90, 0xF8, + 0x5A, 0x37, 0x48, 0x32, 0x20, 0x6C, 0xFF, 0xF7, 0xB9, 0xFC, 0xFF, 0xF7, + 0x0C, 0xFA, 0x38, 0x68, 0x90, 0xF8, 0x4C, 0x17, 0x49, 0x07, 0x0F, 0xD5, + 0x47, 0x49, 0xB0, 0xF8, 0x52, 0x07, 0xE0, 0x80, 0x09, 0x1D, 0x88, 0x1E, + 0xFF, 0xF7, 0x18, 0xFC, 0x61, 0x8A, 0x09, 0x20, 0x09, 0xF0, 0x53, 0xFA, + 0xA1, 0x8A, 0x0A, 0x20, 0x09, 0xF0, 0x4F, 0xFA, 0x38, 0x68, 0x90, 0xF8, + 0x4C, 0x17, 0x09, 0x07, 0x0F, 0xD5, 0x3D, 0x49, 0xB0, 0xF8, 0x54, 0x07, + 0x20, 0x81, 0x08, 0x31, 0x88, 0x1E, 0xFF, 0xF7, 0x7B, 0xFB, 0xE1, 0x8A, + 0x0B, 0x20, 0x09, 0xF0, 0x3E, 0xFA, 0x21, 0x8B, 0x0C, 0x20, 0x09, 0xF0, + 0x3A, 0xFA, 0x38, 0x68, 0xD8, 0xF8, 0x00, 0x10, 0x90, 0xF8, 0x4D, 0x27, + 0x0A, 0x74, 0x90, 0xF8, 0x4C, 0x17, 0x0A, 0x07, 0x02, 0xD5, 0xB0, 0xF8, + 0x54, 0x07, 0x0D, 0xE0, 0x4A, 0x07, 0x02, 0xD5, 0xB0, 0xF8, 0x52, 0x07, + 0x08, 0xE0, 0x8A, 0x07, 0x02, 0xD5, 0xB0, 0xF8, 0x50, 0x07, 0x03, 0xE0, + 0xC9, 0x07, 0xF0, 0xD0, 0xB0, 0xF8, 0x4E, 0x07, 0x60, 0x83, 0xFF, 0xF7, + 0x2B, 0xFA, 0x38, 0x68, 0x62, 0x8B, 0x90, 0xF8, 0x5B, 0x07, 0x00, 0xEB, + 0x80, 0x01, 0x40, 0x42, 0x00, 0xEB, 0x80, 0x00, 0x02, 0xEB, 0x41, 0x01, + 0x02, 0xEB, 0x40, 0x00, 0x89, 0xB2, 0x80, 0xB2, 0xFF, 0xF7, 0x5D, 0xF9, + 0x10, 0x21, 0xA0, 0x6A, 0xFF, 0xF7, 0xBC, 0xF8, 0x00, 0x20, 0x0B, 0xF0, + 0xE3, 0xFA, 0xA0, 0x6A, 0x00, 0xB1, 0x00, 0x26, 0x30, 0x46, 0xBD, 0xE8, + 0xF0, 0x81, 0xF0, 0xB5, 0x00, 0x25, 0x2C, 0x46, 0x4F, 0xF0, 0x01, 0x0C, + 0x0C, 0xE0, 0x02, 0xEB, 0xE4, 0x06, 0x04, 0xF0, 0x07, 0x07, 0x36, 0x78, + 0x0C, 0xFA, 0x07, 0xFE, 0x1E, 0xEA, 0x06, 0x0F, 0x01, 0xD0, 0x03, 0x55, + 0x6D, 0x1C, 0x64, 0x1C, 0x8D, 0x42, 0xF0, 0xDB, 0xF0, 0xBD, 0x10, 0xB5, + 0xB0, 0xF9, 0x00, 0x30, 0x01, 0x21, 0x05, 0xE0, 0x30, 0xF9, 0x11, 0x40, + 0x9C, 0x42, 0x00, 0xDD, 0x23, 0x46, 0x49, 0x1C, 0x91, 0x42, 0xF7, 0xDB, + 0x00, 0x2A, 0x06, 0xDD, 0x04, 0x48, 0x10, 0x38, 0x40, 0x88, 0x83, 0x42, + 0x01, 0xDC, 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0x00, 0x00, + 0xC0, 0x22, 0x10, 0x00, 0x6C, 0x48, 0x08, 0xB5, 0x00, 0x26, 0x06, 0x80, + 0x6B, 0x48, 0x6C, 0x4F, 0x6C, 0x4D, 0x06, 0x70, 0x00, 0x22, 0x6B, 0x46, + 0x39, 0x46, 0x10, 0x46, 0x0B, 0xF0, 0x22, 0xFA, 0x00, 0x98, 0x40, 0x07, + 0x17, 0xD5, 0x28, 0x68, 0x10, 0xF8, 0x3A, 0x1F, 0x21, 0xF0, 0x01, 0x01, + 0x01, 0x70, 0xFE, 0xF7, 0x3E, 0xFD, 0x29, 0x68, 0x00, 0x23, 0x11, 0xF8, + 0x3B, 0x2F, 0x60, 0xF3, 0x00, 0x02, 0x0A, 0x70, 0x1A, 0x46, 0x01, 0x21, + 0x20, 0x20, 0x09, 0xF0, 0x8A, 0xF9, 0x04, 0x21, 0x00, 0x20, 0x0B, 0xF0, + 0xE6, 0xF9, 0x00, 0x98, 0xC0, 0x06, 0x17, 0xD5, 0x28, 0x68, 0x10, 0xF8, + 0x3A, 0x1F, 0x21, 0xF0, 0x08, 0x01, 0x01, 0x70, 0xFF, 0xF7, 0x49, 0xFE, + 0x29, 0x68, 0x00, 0x23, 0x11, 0xF8, 0x3B, 0x2F, 0x60, 0xF3, 0xC3, 0x02, + 0x0A, 0x70, 0x1A, 0x46, 0x10, 0x21, 0x20, 0x20, 0x09, 0xF0, 0x6F, 0xF9, + 0x10, 0x21, 0x00, 0x20, 0x0B, 0xF0, 0xCB, 0xF9, 0x00, 0x98, 0x80, 0x06, + 0x03, 0xD5, 0x20, 0x21, 0x00, 0x20, 0x0B, 0xF0, 0xC4, 0xF9, 0x00, 0x98, + 0x40, 0x06, 0x03, 0xD5, 0x40, 0x21, 0x00, 0x20, 0x0B, 0xF0, 0xBD, 0xF9, + 0x00, 0x98, 0x00, 0x04, 0x04, 0xD5, 0x4F, 0xF4, 0x00, 0x41, 0x00, 0x20, + 0x0B, 0xF0, 0xB5, 0xF9, 0x00, 0x98, 0x00, 0x07, 0x03, 0xD5, 0x08, 0x21, + 0x00, 0x20, 0x0B, 0xF0, 0xAE, 0xF9, 0x00, 0x98, 0x00, 0x06, 0x05, 0xD5, + 0xFE, 0xF7, 0x24, 0xFA, 0x80, 0x21, 0x00, 0x20, 0x0B, 0xF0, 0xA5, 0xF9, + 0x00, 0x98, 0xC0, 0x05, 0x06, 0xD5, 0x4F, 0xF4, 0x80, 0x71, 0x00, 0x20, + 0x0B, 0xF0, 0x9D, 0xF9, 0xFD, 0xF7, 0xA5, 0xFF, 0x00, 0x98, 0x80, 0x05, + 0x18, 0xD5, 0x35, 0x48, 0x04, 0x68, 0x06, 0x60, 0x20, 0x46, 0xFD, 0xF7, + 0x78, 0xF9, 0x4F, 0xF4, 0x00, 0x71, 0x00, 0x20, 0x0B, 0xF0, 0x8D, 0xF9, + 0xE1, 0xB2, 0x00, 0x23, 0xC4, 0xF3, 0x07, 0x22, 0x06, 0x20, 0x09, 0xF0, + 0x26, 0xF9, 0x2D, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xA8, 0x13, 0x07, 0x20, + 0x08, 0xF0, 0x93, 0xF8, 0x00, 0x98, 0x40, 0x05, 0x0C, 0xD5, 0xFE, 0xF7, + 0xC6, 0xF9, 0x00, 0x23, 0x01, 0x22, 0x19, 0x46, 0x20, 0x20, 0x09, 0xF0, + 0x14, 0xF9, 0x4F, 0xF4, 0x80, 0x61, 0x00, 0x20, 0x0B, 0xF0, 0x6F, 0xF9, + 0x00, 0x98, 0x00, 0x05, 0x06, 0xD5, 0xFD, 0xF7, 0xBF, 0xF9, 0x4F, 0xF4, + 0x00, 0x61, 0x00, 0x20, 0x0B, 0xF0, 0x65, 0xF9, 0x00, 0x98, 0xC0, 0x04, + 0x06, 0xD5, 0xFD, 0xF7, 0xCC, 0xF9, 0x4F, 0xF4, 0x80, 0x51, 0x00, 0x20, + 0x0B, 0xF0, 0x5B, 0xF9, 0x00, 0x98, 0x40, 0x04, 0x09, 0xD5, 0x08, 0xF0, + 0x99, 0xFD, 0x08, 0xB9, 0x08, 0xF0, 0x61, 0xFD, 0x4F, 0xF4, 0x80, 0x41, + 0x00, 0x20, 0x0B, 0xF0, 0x4E, 0xF9, 0x00, 0x98, 0x80, 0x03, 0x06, 0xD5, + 0xFE, 0xF7, 0x68, 0xFD, 0x4F, 0xF4, 0x00, 0x31, 0x00, 0x20, 0x0B, 0xF0, + 0x44, 0xF9, 0x00, 0x98, 0x00, 0x03, 0x7F, 0xF5, 0x39, 0xAF, 0x0B, 0x48, + 0x00, 0x68, 0xFE, 0xF7, 0x85, 0xFF, 0x4F, 0xF4, 0x00, 0x21, 0x00, 0x20, + 0x0B, 0xF0, 0x37, 0xF9, 0x2E, 0xE7, 0x00, 0x00, 0x0A, 0x22, 0x10, 0x00, + 0x9C, 0x22, 0x10, 0x00, 0xFF, 0xDF, 0x0E, 0x00, 0x54, 0x24, 0x10, 0x00, + 0x64, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x68, 0x24, 0x10, 0x00, + 0x08, 0xB5, 0x6B, 0x46, 0x00, 0x22, 0x01, 0x21, 0x03, 0x20, 0x0B, 0xF0, + 0x41, 0xF9, 0x00, 0x98, 0xC0, 0x07, 0xF6, 0xD0, 0x01, 0x21, 0x03, 0x20, + 0x0B, 0xF0, 0x19, 0xF9, 0xF1, 0xE7, 0x00, 0x00, 0x0E, 0xF0, 0xC2, 0xB9, + 0x10, 0xB5, 0xD1, 0x48, 0xD1, 0x4C, 0x00, 0x68, 0x10, 0xB1, 0x04, 0x20, + 0x20, 0x70, 0x10, 0xBD, 0x0E, 0xF0, 0xB8, 0xF9, 0x01, 0x21, 0x08, 0x46, + 0x0B, 0xF0, 0x07, 0xF9, 0x00, 0xF0, 0xD0, 0xFB, 0x01, 0x20, 0xF3, 0xE7, + 0x10, 0xB5, 0x0E, 0xF0, 0xAD, 0xF9, 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, + 0x08, 0x46, 0x0B, 0xF0, 0xCB, 0xB8, 0x08, 0xB5, 0x00, 0x20, 0x0B, 0xF0, + 0x78, 0xF9, 0x00, 0xF0, 0x7B, 0xFC, 0x00, 0xF0, 0x1E, 0xF9, 0xC2, 0x49, + 0x0E, 0xF0, 0x84, 0xF9, 0x00, 0xF0, 0x26, 0xFD, 0x01, 0x28, 0x0E, 0xD1, + 0x6B, 0x46, 0x00, 0x22, 0x08, 0x21, 0x0B, 0xF0, 0x07, 0xF9, 0x08, 0x21, + 0x01, 0x20, 0x0B, 0xF0, 0xE2, 0xF8, 0xBB, 0x48, 0x90, 0xF8, 0x42, 0x00, + 0x28, 0xB1, 0x08, 0xF0, 0x60, 0xFC, 0x00, 0x20, 0x0B, 0xF0, 0x76, 0xF9, + 0x08, 0xBD, 0xB7, 0x48, 0x01, 0x68, 0xB7, 0x48, 0x00, 0x68, 0x00, 0xF0, + 0x47, 0xFB, 0xF4, 0xE7, 0xF8, 0xB5, 0xB5, 0x48, 0x00, 0x25, 0x04, 0x68, + 0xAD, 0x4E, 0x30, 0x68, 0x90, 0xEA, 0x04, 0x0F, 0x66, 0xD0, 0x00, 0xB9, + 0x01, 0x25, 0x20, 0x46, 0x00, 0xF0, 0x20, 0xF9, 0x34, 0x60, 0xFF, 0xF7, + 0xAB, 0xFF, 0x00, 0x2D, 0x5B, 0xD0, 0xFF, 0xF7, 0xC2, 0xFF, 0xAC, 0x4F, + 0x3C, 0x68, 0x94, 0xF8, 0xE8, 0x07, 0x40, 0x06, 0x04, 0xD5, 0x4F, 0xF4, + 0xF0, 0x61, 0xA9, 0x48, 0x0C, 0xF0, 0x11, 0xFB, 0x60, 0x7D, 0x21, 0x7D, + 0xA7, 0x4D, 0x48, 0x43, 0x64, 0x26, 0x01, 0xE0, 0x25, 0xF8, 0x10, 0x60, + 0x40, 0x1E, 0xFB, 0xD2, 0x94, 0xF8, 0x30, 0x01, 0x80, 0x06, 0x08, 0xD5, + 0x20, 0x7D, 0x00, 0x90, 0xA0, 0x49, 0x63, 0x7D, 0x94, 0xF8, 0x4A, 0x21, + 0x08, 0x46, 0x02, 0xF0, 0xF7, 0xFC, 0x38, 0x68, 0x90, 0xF8, 0x30, 0x11, + 0x49, 0x06, 0x08, 0xD5, 0x01, 0x7D, 0x00, 0x91, 0x99, 0x49, 0x43, 0x7D, + 0x90, 0xF8, 0x4B, 0x21, 0x08, 0x46, 0x02, 0xF0, 0x1A, 0xFD, 0x3A, 0x68, + 0x92, 0xF8, 0x2F, 0x01, 0xC0, 0x07, 0x08, 0xD0, 0x10, 0x7D, 0x00, 0x90, + 0x92, 0x49, 0x53, 0x7D, 0x08, 0x46, 0x02, 0xF2, 0xC9, 0x32, 0x02, 0xF0, + 0x39, 0xFD, 0x3A, 0x68, 0x92, 0xF8, 0x2F, 0x01, 0x80, 0x07, 0x08, 0xD5, + 0x10, 0x7D, 0x00, 0x90, 0x8B, 0x49, 0x53, 0x7D, 0x08, 0x46, 0x02, 0xF2, + 0xAB, 0x32, 0x02, 0xF0, 0x65, 0xFD, 0x39, 0x68, 0x48, 0x7D, 0x09, 0x7D, + 0x48, 0x43, 0x06, 0xE0, 0x35, 0xF9, 0x10, 0x10, 0xC9, 0x01, 0xB1, 0xFB, + 0xF6, 0xF1, 0x25, 0xF8, 0x10, 0x10, 0x40, 0x1E, 0xF6, 0xD2, 0xF8, 0xBD, + 0x00, 0x2C, 0xFC, 0xD1, 0x77, 0x48, 0x00, 0x78, 0x07, 0x28, 0x03, 0xD0, + 0x05, 0x28, 0x01, 0xD0, 0x06, 0x28, 0xF4, 0xD1, 0x34, 0x60, 0xBD, 0xE8, + 0xF8, 0x40, 0x3D, 0xE7, 0x70, 0xB5, 0x7A, 0x48, 0x05, 0x68, 0x7A, 0x4E, + 0x31, 0x68, 0x91, 0xEA, 0x05, 0x04, 0x49, 0xD0, 0x73, 0x48, 0xA2, 0x07, + 0x00, 0x68, 0x08, 0xD5, 0xAA, 0x07, 0x16, 0xD5, 0x90, 0xF8, 0xD0, 0x21, + 0xD2, 0x07, 0x02, 0xD0, 0x41, 0xF0, 0x02, 0x01, 0x31, 0x60, 0xE1, 0x07, + 0x14, 0xD0, 0xE9, 0x07, 0x0E, 0xD0, 0x90, 0xF8, 0xEA, 0x11, 0xC9, 0x07, + 0x0E, 0xD0, 0x31, 0x68, 0x62, 0x4A, 0x41, 0xF0, 0x01, 0x01, 0x31, 0x60, + 0x04, 0x21, 0x11, 0x70, 0x06, 0xE0, 0x21, 0xF0, 0x02, 0x01, 0xEB, 0xE7, + 0x31, 0x68, 0x21, 0xF0, 0x01, 0x01, 0x31, 0x60, 0x21, 0x07, 0x0F, 0xD5, + 0x29, 0x07, 0x15, 0xD5, 0x90, 0xF8, 0xAF, 0x02, 0xC0, 0x07, 0x09, 0xD0, + 0x30, 0x68, 0x00, 0x23, 0x40, 0xF0, 0x08, 0x00, 0x30, 0x60, 0x1A, 0x46, + 0x19, 0x46, 0xCC, 0x20, 0x08, 0xF0, 0xB5, 0xFF, 0xE0, 0x03, 0x11, 0xD5, + 0xE8, 0x03, 0x30, 0x68, 0x0B, 0xD5, 0x40, 0xF4, 0x80, 0x30, 0x0A, 0xE0, + 0x30, 0x68, 0x00, 0x23, 0x20, 0xF0, 0x08, 0x00, 0x30, 0x60, 0x1A, 0x46, + 0x19, 0x46, 0xCD, 0x20, 0xEC, 0xE7, 0x20, 0xF4, 0x80, 0x30, 0x30, 0x60, + 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0xDF, 0xF8, 0x48, 0x81, 0x46, 0x4F, + 0x44, 0x48, 0x98, 0xF8, 0x00, 0x50, 0x3C, 0x78, 0x00, 0x26, 0x06, 0x60, + 0xFF, 0xF7, 0xDE, 0xFE, 0x05, 0x20, 0x38, 0x70, 0x4C, 0x48, 0x05, 0x2C, + 0x06, 0x70, 0x13, 0xD0, 0x07, 0x2C, 0x11, 0xD0, 0x06, 0x2C, 0x0F, 0xD0, + 0x49, 0x49, 0x08, 0x68, 0x20, 0xF0, 0x02, 0x00, 0x40, 0xF0, 0x04, 0x00, + 0x08, 0x60, 0x01, 0x21, 0x08, 0xF0, 0x98, 0xFF, 0x4F, 0xF4, 0x00, 0x71, + 0x00, 0x20, 0x0A, 0xF0, 0xAB, 0xFF, 0x01, 0xE0, 0x88, 0xF8, 0x00, 0x50, + 0xBD, 0xE8, 0xF0, 0x41, 0xD9, 0xE6, 0x38, 0xB5, 0x31, 0x48, 0x01, 0x78, + 0x36, 0x48, 0x89, 0x1E, 0x06, 0x29, 0x00, 0x68, 0x13, 0xD2, 0xDF, 0xE8, + 0x01, 0xF0, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x0C, 0x90, 0xF8, 0x28, 0x40, + 0x0C, 0xE0, 0x90, 0xF8, 0x27, 0x40, 0x09, 0xE0, 0x90, 0xF8, 0x26, 0x40, + 0x06, 0xE0, 0x90, 0xF8, 0x29, 0x40, 0x03, 0xE0, 0x90, 0xF8, 0x2A, 0x40, + 0x00, 0xE0, 0x01, 0x24, 0x00, 0x25, 0x0D, 0xF0, 0x83, 0xFF, 0x00, 0xB1, + 0x01, 0x25, 0x69, 0x46, 0x04, 0x20, 0x0A, 0xF0, 0xBE, 0xFF, 0x00, 0x98, + 0x00, 0xB9, 0x1D, 0xB1, 0x14, 0x2C, 0x01, 0xD2, 0x14, 0x24, 0x01, 0xE0, + 0x04, 0xB9, 0x01, 0x24, 0x20, 0x46, 0x38, 0xBD, 0x70, 0xB5, 0x18, 0x4D, + 0x04, 0x46, 0x28, 0x68, 0x50, 0xB9, 0x4C, 0xB1, 0x0E, 0xF0, 0x48, 0xF8, + 0x01, 0x21, 0x08, 0x46, 0x0A, 0xF0, 0x97, 0xFF, 0x00, 0xF0, 0xBE, 0xFA, + 0x00, 0xF0, 0x42, 0xF8, 0x28, 0x78, 0x84, 0x43, 0xE0, 0x07, 0x1C, 0xD0, + 0x1D, 0x49, 0x01, 0x20, 0x08, 0x70, 0x14, 0x48, 0x03, 0x21, 0x00, 0x68, + 0x90, 0xF8, 0xB0, 0x20, 0xD2, 0x07, 0x03, 0xD1, 0x90, 0xF8, 0xD4, 0x00, + 0xC0, 0x07, 0x00, 0xD0, 0x23, 0x21, 0x15, 0x4A, 0x10, 0x68, 0x08, 0x43, + 0x01, 0x21, 0x10, 0x60, 0x08, 0xF0, 0x32, 0xFF, 0xBD, 0xE8, 0x70, 0x40, + 0x4F, 0xF4, 0x00, 0x71, 0x00, 0x20, 0x0A, 0xF0, 0x43, 0xBF, 0x70, 0xBD, + 0x24, 0x23, 0x10, 0x00, 0x60, 0x24, 0x10, 0x00, 0x25, 0x34, 0x00, 0x00, + 0x90, 0x46, 0x10, 0x00, 0x2C, 0x23, 0x10, 0x00, 0x28, 0x23, 0x10, 0x00, + 0x58, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x38, 0x9C, 0x01, 0x20, + 0x3C, 0x88, 0x01, 0x20, 0x5C, 0x24, 0x10, 0x00, 0x30, 0x23, 0x10, 0x00, + 0x34, 0x23, 0x10, 0x00, 0x10, 0x23, 0x10, 0x00, 0x64, 0x24, 0x10, 0x00, + 0x61, 0x24, 0x10, 0x00, 0xFD, 0x49, 0x01, 0x20, 0x08, 0x70, 0xFD, 0x49, + 0x00, 0x20, 0x08, 0x70, 0xFC, 0x49, 0x08, 0x70, 0x48, 0x70, 0x70, 0x47, + 0xFB, 0x48, 0x10, 0xB5, 0x00, 0x68, 0x90, 0xF8, 0xAF, 0x02, 0xC1, 0x07, + 0x15, 0xD0, 0x81, 0x07, 0x13, 0xD5, 0x00, 0x24, 0x40, 0x09, 0x0D, 0xF0, + 0xA7, 0xFE, 0x00, 0xB1, 0x01, 0x24, 0xF3, 0x48, 0x01, 0x78, 0x8C, 0x42, + 0x09, 0xD0, 0x04, 0x70, 0x0C, 0xB1, 0x02, 0x21, 0x00, 0xE0, 0x04, 0x21, + 0xBD, 0xE8, 0x10, 0x40, 0x04, 0x20, 0x0A, 0xF0, 0xFB, 0xBE, 0x10, 0xBD, + 0x70, 0xB5, 0xEE, 0x4C, 0xEC, 0x48, 0xEF, 0x4D, 0x20, 0x61, 0xED, 0x48, + 0xA0, 0x60, 0x3C, 0x30, 0xE0, 0x60, 0xE8, 0x48, 0x00, 0x68, 0x41, 0x7D, + 0x84, 0xF8, 0x2F, 0x10, 0x01, 0x7D, 0x84, 0xF8, 0x30, 0x10, 0x40, 0x7D, + 0x29, 0x6A, 0x42, 0x00, 0xE3, 0x48, 0x0C, 0xF0, 0xF3, 0xF8, 0x94, 0xF8, + 0x2F, 0x00, 0xA9, 0x69, 0x42, 0x00, 0xE2, 0x48, 0x0C, 0xF0, 0xEC, 0xF8, + 0x94, 0xF8, 0x30, 0x00, 0xE9, 0x69, 0x42, 0x00, 0xBD, 0xE8, 0x70, 0x40, + 0xDD, 0x48, 0x3C, 0x30, 0x0C, 0xF0, 0xE2, 0xB8, 0x2D, 0xE9, 0xF0, 0x41, + 0x0F, 0x46, 0x04, 0x46, 0x40, 0xEA, 0x07, 0x05, 0x00, 0x26, 0x03, 0x20, + 0x0A, 0xF0, 0x79, 0xFF, 0x34, 0x21, 0xD5, 0x48, 0x0C, 0xF0, 0x75, 0xF9, + 0xE8, 0x07, 0x06, 0xD0, 0x28, 0x46, 0x00, 0xF0, 0xDE, 0xF9, 0xE8, 0x06, + 0x03, 0xD4, 0x01, 0x26, 0x04, 0xE0, 0xE8, 0x06, 0x02, 0xD5, 0xFF, 0xF7, + 0xBD, 0xFF, 0xF8, 0xE7, 0xCF, 0x48, 0xCC, 0x4D, 0xCD, 0x49, 0x00, 0x68, + 0xE8, 0x61, 0xCE, 0x48, 0xDF, 0xF8, 0x38, 0x83, 0x00, 0x78, 0x85, 0xF8, + 0x33, 0x00, 0xC5, 0xE9, 0x08, 0x47, 0xC8, 0x6B, 0xA8, 0x62, 0x98, 0xF8, + 0x00, 0x00, 0x85, 0xF8, 0x2C, 0x00, 0x91, 0xF8, 0x41, 0x10, 0xC1, 0xF3, + 0x00, 0x11, 0x85, 0xF8, 0x2D, 0x10, 0xE1, 0x07, 0x01, 0xD0, 0xE1, 0x06, + 0x05, 0xD4, 0x02, 0x28, 0x13, 0xD1, 0xF8, 0x07, 0x11, 0xD0, 0xE0, 0x06, + 0x0F, 0xD5, 0x00, 0xF0, 0x8D, 0xFA, 0x01, 0x28, 0x03, 0xD1, 0xE8, 0x69, + 0x40, 0xF0, 0x10, 0x00, 0xE8, 0x61, 0x00, 0xF0, 0x93, 0xFA, 0x01, 0x28, + 0x03, 0xD1, 0xE8, 0x69, 0x40, 0xF0, 0x20, 0x00, 0xE8, 0x61, 0x03, 0x20, + 0x0A, 0xF0, 0x4C, 0xFF, 0x00, 0xF0, 0x4A, 0xFB, 0x00, 0x2E, 0x0F, 0xD0, + 0x98, 0xF8, 0x00, 0x00, 0x05, 0x28, 0x09, 0xD0, 0x06, 0x28, 0x07, 0xD0, + 0x07, 0x28, 0x05, 0xD0, 0x01, 0x21, 0xBD, 0xE8, 0xF0, 0x41, 0x02, 0x20, + 0x0A, 0xF0, 0x6E, 0xBE, 0x02, 0x21, 0xF8, 0xE7, 0xBD, 0xE8, 0xF0, 0x81, + 0x70, 0xB5, 0xA4, 0x4B, 0x00, 0x22, 0xC1, 0xB2, 0x1B, 0x68, 0xAA, 0x4C, + 0x5D, 0x7D, 0x28, 0x1A, 0x0D, 0xE0, 0x34, 0xF9, 0x11, 0x50, 0xDE, 0x8E, + 0xB5, 0x42, 0x06, 0xDD, 0x93, 0xF8, 0x35, 0x50, 0x52, 0x1C, 0x95, 0x42, + 0x01, 0xDC, 0x01, 0x20, 0x70, 0xBD, 0x49, 0x1C, 0xC9, 0xB2, 0x88, 0x42, + 0xEF, 0xDC, 0x00, 0x20, 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x96, 0x4C, + 0x0E, 0x46, 0x13, 0x46, 0x21, 0x68, 0x91, 0xF8, 0x3C, 0x20, 0x19, 0x46, + 0x09, 0xF0, 0xA4, 0xF8, 0x00, 0x21, 0x90, 0x4D, 0x01, 0x28, 0x04, 0xD0, + 0x69, 0x70, 0x98, 0x49, 0x68, 0x78, 0x88, 0x71, 0xCE, 0xE7, 0x68, 0x78, + 0x40, 0x1C, 0xC0, 0xB2, 0x68, 0x70, 0x22, 0x68, 0x92, 0xF8, 0x3D, 0x20, + 0x82, 0x42, 0xF2, 0xD2, 0x8F, 0x48, 0x69, 0x70, 0x91, 0x4F, 0x04, 0x78, + 0x02, 0x2C, 0x1F, 0xD0, 0x03, 0x2C, 0x1D, 0xD0, 0x38, 0x68, 0x40, 0xF0, + 0x04, 0x00, 0x38, 0x60, 0x8A, 0x48, 0x3C, 0x22, 0x31, 0x46, 0x78, 0x38, + 0x0C, 0xF0, 0x30, 0xF8, 0x87, 0x48, 0x3C, 0x22, 0x31, 0x46, 0x3C, 0x38, + 0x0C, 0xF0, 0x2A, 0xF8, 0x02, 0x2C, 0x0F, 0xD0, 0x03, 0x2C, 0x0D, 0xD0, + 0x22, 0x21, 0x38, 0x68, 0x08, 0xF0, 0xFA, 0xFD, 0x4F, 0xF4, 0x00, 0x71, + 0x00, 0x20, 0x0A, 0xF0, 0x0D, 0xFE, 0xCC, 0xE7, 0x38, 0x68, 0x40, 0xF0, + 0x22, 0x00, 0xE0, 0xE7, 0x23, 0x21, 0xF0, 0xE7, 0x7C, 0xB5, 0x06, 0x46, + 0x6F, 0x48, 0x71, 0x4C, 0x0D, 0x46, 0x00, 0x78, 0x50, 0xB9, 0x80, 0x20, + 0xCD, 0xE9, 0x00, 0x05, 0x20, 0x68, 0x7F, 0x23, 0x31, 0x46, 0x42, 0x7D, + 0x73, 0x48, 0x78, 0x38, 0x09, 0xF0, 0xF2, 0xF8, 0x21, 0x68, 0x91, 0xF8, + 0x34, 0x00, 0xC0, 0x07, 0x00, 0xD0, 0x6D, 0xB1, 0x6E, 0x48, 0x3C, 0x22, + 0x31, 0x46, 0x3C, 0x38, 0x0B, 0xF0, 0xF8, 0xFF, 0x6B, 0x4D, 0x00, 0x20, + 0x78, 0x3D, 0x05, 0xF1, 0x3C, 0x03, 0x69, 0x49, 0x24, 0x68, 0x16, 0xE0, + 0x4A, 0x7D, 0xB1, 0xF9, 0x3A, 0x00, 0xCD, 0xE9, 0x00, 0x02, 0x65, 0x4A, + 0xB1, 0xF9, 0x38, 0x30, 0x78, 0x3A, 0x31, 0x46, 0x02, 0xF1, 0x3C, 0x00, + 0x09, 0xF0, 0x78, 0xF8, 0xE8, 0xE7, 0x35, 0xF8, 0x10, 0x20, 0x33, 0xF8, + 0x10, 0x60, 0x92, 0x1B, 0x21, 0xF8, 0x10, 0x20, 0x40, 0x1C, 0x62, 0x7D, + 0x82, 0x42, 0xF4, 0xDC, 0x7C, 0xBD, 0x2D, 0xE9, 0xF0, 0x47, 0x55, 0x48, + 0x57, 0x4F, 0xDF, 0xF8, 0x34, 0x81, 0xD0, 0xF8, 0x18, 0x90, 0x38, 0x78, + 0x00, 0x21, 0x98, 0xF8, 0x00, 0x20, 0x4C, 0x4D, 0x49, 0x4E, 0x02, 0x28, + 0x0C, 0xD0, 0x03, 0x28, 0x0A, 0xD0, 0x05, 0x28, 0x23, 0xD0, 0x06, 0x28, + 0x21, 0xD0, 0x00, 0x24, 0x34, 0x70, 0x39, 0x78, 0x88, 0xF8, 0x00, 0x10, + 0xBD, 0xE8, 0xF0, 0x87, 0x82, 0x42, 0x04, 0xD0, 0x04, 0x2A, 0x01, 0xD0, + 0x01, 0x2A, 0x00, 0xD1, 0x01, 0x21, 0x48, 0x46, 0xFF, 0xF7, 0x98, 0xFF, + 0x28, 0x68, 0x90, 0xF8, 0x34, 0x10, 0x49, 0x07, 0x04, 0xD5, 0x42, 0x7D, + 0x49, 0x46, 0x43, 0x48, 0xFF, 0xF7, 0x45, 0xFF, 0x00, 0x20, 0xFF, 0xF7, + 0x27, 0xFF, 0x04, 0x46, 0x01, 0x28, 0x2A, 0xD0, 0xDE, 0xE7, 0x06, 0x2A, + 0x02, 0xD0, 0x05, 0x2A, 0x00, 0xD0, 0x01, 0x21, 0x05, 0x28, 0x03, 0xD0, + 0x06, 0x28, 0xD5, 0xD1, 0x01, 0x24, 0xD3, 0xE7, 0x48, 0x46, 0xFF, 0xF7, + 0x79, 0xFF, 0x28, 0x68, 0x90, 0xF8, 0x34, 0x10, 0x89, 0x07, 0x04, 0xD5, + 0x42, 0x7D, 0x49, 0x46, 0x33, 0x48, 0xFF, 0xF7, 0x26, 0xFF, 0x28, 0x68, + 0x90, 0xF9, 0x93, 0x02, 0xFF, 0xF7, 0x06, 0xFF, 0x30, 0x4B, 0x04, 0x46, + 0x31, 0x78, 0x5A, 0x79, 0x01, 0x28, 0x64, 0xF3, 0x00, 0x02, 0x61, 0xF3, + 0x41, 0x02, 0x5A, 0x71, 0xB6, 0xD1, 0x00, 0x29, 0xB4, 0xD1, 0xFF, 0xF7, + 0x79, 0xFC, 0x01, 0x21, 0x08, 0x46, 0x0A, 0xF0, 0x5D, 0xFD, 0xAD, 0xE7, + 0x70, 0xB5, 0x26, 0x4C, 0x0D, 0x46, 0x06, 0x46, 0x61, 0x88, 0x00, 0x20, + 0x20, 0x60, 0x60, 0x60, 0x20, 0x81, 0x61, 0x80, 0x29, 0x46, 0x30, 0x46, + 0xFF, 0xF7, 0x89, 0xFF, 0x29, 0x46, 0x30, 0x46, 0xFF, 0xF7, 0x78, 0xFE, + 0x04, 0x20, 0x0A, 0xF0, 0xF8, 0xFD, 0x2C, 0x21, 0x1D, 0x48, 0x0B, 0xF0, + 0xF4, 0xFF, 0x04, 0x20, 0x0A, 0xF0, 0x0C, 0xFE, 0xFF, 0xF7, 0x26, 0xFE, + 0x06, 0x20, 0x20, 0x80, 0x60, 0x88, 0x40, 0x1C, 0x60, 0x80, 0x13, 0x48, + 0x00, 0x78, 0x20, 0x71, 0x0A, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xE9, 0x07, + 0x06, 0x28, 0x07, 0xD1, 0x13, 0x48, 0x21, 0x68, 0x00, 0x68, 0x01, 0x60, + 0x61, 0x68, 0x41, 0x60, 0x21, 0x89, 0x01, 0x81, 0x70, 0xBD, 0x00, 0x00, + 0x35, 0x23, 0x10, 0x00, 0x34, 0x23, 0x10, 0x00, 0x11, 0x23, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x54, 0x46, 0x10, 0x00, 0xE4, 0x52, 0x10, 0x00, + 0xB8, 0xA3, 0x01, 0x20, 0x90, 0x46, 0x10, 0x00, 0x30, 0x23, 0x10, 0x00, + 0x14, 0x23, 0x10, 0x00, 0x60, 0x24, 0x10, 0x00, 0x00, 0x88, 0x01, 0x20, + 0xD0, 0x25, 0x10, 0x00, 0x64, 0x24, 0x10, 0x00, 0x18, 0x53, 0x10, 0x00, + 0x74, 0x24, 0x10, 0x00, 0x10, 0xB5, 0xFF, 0xF7, 0xE1, 0xFD, 0x03, 0x20, + 0x0A, 0xF0, 0xB1, 0xFD, 0x34, 0x21, 0x25, 0x48, 0x0B, 0xF0, 0xAD, 0xFF, + 0x23, 0x49, 0x01, 0x20, 0x81, 0xF8, 0x2C, 0x00, 0x03, 0x20, 0x0A, 0xF0, + 0xC1, 0xFD, 0x01, 0x21, 0x02, 0x20, 0x0A, 0xF0, 0xF1, 0xFC, 0x04, 0x20, + 0x0A, 0xF0, 0x9F, 0xFD, 0x2C, 0x21, 0x1D, 0x48, 0x0B, 0xF0, 0x9B, 0xFF, + 0x04, 0x20, 0x0A, 0xF0, 0xB3, 0xFD, 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, + 0x03, 0x20, 0x0A, 0xF0, 0xE1, 0xBC, 0x70, 0xB5, 0x17, 0x49, 0x15, 0x4C, + 0x17, 0x4D, 0x09, 0x68, 0x40, 0x07, 0x4A, 0x7D, 0x84, 0xF8, 0x2F, 0x20, + 0x0B, 0x7D, 0x84, 0xF8, 0x30, 0x30, 0x95, 0xF8, 0x40, 0x20, 0x84, 0xF8, + 0x2E, 0x20, 0x12, 0x4A, 0x62, 0x60, 0x09, 0xD5, 0x91, 0xF8, 0x11, 0x01, + 0x69, 0x68, 0x80, 0x07, 0x42, 0x0F, 0x92, 0x1C, 0x5A, 0x43, 0x0D, 0x48, + 0x0B, 0xF0, 0xD4, 0xFE, 0x0C, 0x48, 0x44, 0xF8, 0x11, 0x0B, 0x29, 0x68, + 0xE2, 0x7F, 0x60, 0x7F, 0x42, 0x43, 0xA0, 0x7F, 0xBD, 0xE8, 0x70, 0x40, + 0x40, 0x00, 0x42, 0x43, 0x06, 0x48, 0x0B, 0xF0, 0xC5, 0xBE, 0x00, 0x00, + 0xE4, 0x52, 0x10, 0x00, 0x18, 0x53, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, + 0x90, 0x46, 0x10, 0x00, 0xD0, 0x00, 0x01, 0x20, 0x54, 0x28, 0x10, 0x00, + 0xFE, 0x48, 0x00, 0x21, 0x01, 0x22, 0x81, 0x60, 0xC1, 0x60, 0x41, 0x70, + 0x82, 0x70, 0xFC, 0x4A, 0x12, 0x68, 0x92, 0xF8, 0xA2, 0x23, 0xC2, 0x70, + 0x01, 0x71, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0xF8, 0x48, 0x00, 0x78, + 0x18, 0xB9, 0x07, 0xF0, 0xB4, 0xFB, 0x00, 0xB1, 0x01, 0x20, 0x00, 0x25, + 0xF5, 0x4E, 0xF3, 0x4A, 0xF1, 0x4C, 0x01, 0x28, 0x03, 0xD0, 0xF4, 0x48, + 0x01, 0x68, 0xC8, 0x03, 0x0A, 0xD5, 0x30, 0x78, 0x04, 0x28, 0x05, 0xD0, + 0x10, 0x68, 0x90, 0xF8, 0xA2, 0x13, 0x01, 0x20, 0x00, 0xF0, 0x6B, 0xF9, + 0x04, 0x20, 0x14, 0xE0, 0x30, 0x78, 0x02, 0x23, 0x04, 0x28, 0x02, 0xD0, + 0x03, 0x28, 0x27, 0xD0, 0x23, 0xE0, 0x10, 0x68, 0x90, 0xF8, 0x30, 0x20, + 0x97, 0x07, 0x0A, 0xD5, 0xA1, 0x68, 0x49, 0x1C, 0xA1, 0x60, 0x90, 0xF8, + 0x31, 0x00, 0x0A, 0x30, 0x81, 0x42, 0x17, 0xDD, 0x03, 0x20, 0x30, 0x70, + 0x13, 0xE0, 0xD2, 0x07, 0x12, 0xD0, 0x90, 0xF8, 0xEA, 0x21, 0xC2, 0xF3, + 0x00, 0x12, 0x0A, 0x42, 0x0C, 0xD1, 0xA1, 0x68, 0x49, 0x1C, 0xA1, 0x60, + 0x90, 0xF8, 0x32, 0x00, 0x40, 0x1C, 0x00, 0xEB, 0x80, 0x00, 0xB1, 0xEB, + 0x40, 0x0F, 0x01, 0xDD, 0x33, 0x70, 0xA5, 0x60, 0xBD, 0xE8, 0xF0, 0x81, + 0x10, 0x68, 0x90, 0xF8, 0x30, 0x20, 0xE4, 0xE7, 0xD2, 0x49, 0x08, 0x78, + 0x04, 0x28, 0x0C, 0xD0, 0x03, 0x28, 0x0A, 0xD0, 0x02, 0x28, 0x08, 0xD0, + 0x05, 0x28, 0x0A, 0xD1, 0xCC, 0x48, 0xCA, 0x4A, 0x00, 0x78, 0x28, 0xB1, + 0x53, 0x78, 0x0B, 0xB1, 0x02, 0xE0, 0x9D, 0xE7, 0x06, 0x23, 0x0B, 0x70, + 0x50, 0x70, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0xC4, 0x4E, 0x01, 0x24, + 0x30, 0x68, 0x90, 0xF8, 0x9F, 0x03, 0xC0, 0x07, 0x24, 0xD0, 0xC3, 0x48, + 0x00, 0x78, 0x04, 0x28, 0x20, 0xD1, 0xC3, 0x4D, 0x00, 0x24, 0x28, 0x78, + 0x38, 0xB1, 0x29, 0x78, 0xC1, 0x4F, 0x38, 0x78, 0x00, 0xF0, 0x0B, 0xF9, + 0x00, 0x20, 0x28, 0x70, 0x38, 0x70, 0xB8, 0x49, 0xC8, 0x78, 0x08, 0xB1, + 0x01, 0x24, 0x0F, 0xE0, 0x30, 0x68, 0x90, 0xF8, 0x9F, 0x23, 0x92, 0x07, + 0x0A, 0xD5, 0x90, 0xF8, 0xA0, 0x23, 0x09, 0x79, 0x8A, 0x42, 0x05, 0xD2, + 0x90, 0xF8, 0xA1, 0x13, 0x08, 0x20, 0x00, 0xF0, 0xF4, 0xF8, 0xED, 0xE7, + 0x20, 0x46, 0xB3, 0xE7, 0xAD, 0x48, 0x10, 0xB5, 0x00, 0x68, 0x90, 0xF8, + 0xB0, 0x00, 0xC0, 0x07, 0x04, 0xD0, 0xFF, 0xF7, 0xC7, 0xFF, 0x08, 0xB1, + 0x01, 0x20, 0x10, 0xBD, 0x00, 0x20, 0x10, 0xBD, 0xA6, 0x48, 0x10, 0xB5, + 0x00, 0x68, 0x90, 0xF8, 0xD4, 0x00, 0xC0, 0x07, 0x04, 0xD0, 0xFF, 0xF7, + 0xB9, 0xFF, 0x08, 0xB1, 0x01, 0x20, 0x10, 0xBD, 0x00, 0x20, 0x10, 0xBD, + 0x2D, 0xE9, 0xF0, 0x41, 0x9E, 0x4E, 0x00, 0x24, 0x30, 0x68, 0x90, 0xF8, + 0x40, 0x00, 0xC0, 0x07, 0x38, 0xD0, 0x9D, 0x4F, 0x99, 0x4D, 0xA0, 0x46, + 0x38, 0x78, 0x05, 0x28, 0x0B, 0xD0, 0x07, 0x28, 0x09, 0xD0, 0x9D, 0x48, + 0x00, 0x78, 0xC0, 0x07, 0x20, 0xD0, 0x01, 0x24, 0xFF, 0xF7, 0xCC, 0xFF, + 0x01, 0x28, 0x02, 0xD0, 0x02, 0xE0, 0x01, 0x24, 0x07, 0xE0, 0x11, 0x24, + 0xFF, 0xF7, 0xD2, 0xFF, 0x01, 0x28, 0x01, 0xD1, 0x44, 0xF0, 0x10, 0x04, + 0x84, 0xB1, 0x38, 0x78, 0x02, 0x28, 0x14, 0xD0, 0x04, 0x28, 0x03, 0xD0, + 0x05, 0x28, 0x0C, 0xD0, 0x06, 0x28, 0x0A, 0xD0, 0x30, 0x68, 0x90, 0xF8, + 0x43, 0x00, 0xE9, 0x68, 0x49, 0x1C, 0xE9, 0x60, 0x81, 0x42, 0x0A, 0xDD, + 0xC5, 0xF8, 0x0C, 0x80, 0x08, 0xE0, 0x30, 0x68, 0x90, 0xF8, 0x45, 0x00, + 0xF3, 0xE7, 0x30, 0x68, 0x90, 0xF8, 0x44, 0x00, 0xEF, 0xE7, 0x00, 0x24, + 0x20, 0x46, 0x53, 0xE7, 0x10, 0xB5, 0x01, 0x46, 0x44, 0x22, 0x83, 0x48, + 0x0B, 0xF0, 0xF3, 0xFD, 0x08, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x01, 0x20, + 0x0A, 0xF0, 0x98, 0xBB, 0x2D, 0xE9, 0xFF, 0x41, 0x78, 0x48, 0x03, 0x24, + 0x7D, 0x4D, 0x00, 0x78, 0xDF, 0xF8, 0xF4, 0x81, 0x01, 0x26, 0x02, 0x28, + 0x19, 0xD0, 0x03, 0x28, 0x13, 0xD0, 0x05, 0x28, 0x31, 0xD0, 0x06, 0x28, + 0x68, 0x46, 0x45, 0xD0, 0x00, 0xF0, 0x78, 0xF8, 0x77, 0x48, 0x00, 0x90, + 0x68, 0x46, 0x09, 0xF0, 0x81, 0xFB, 0x76, 0x49, 0x69, 0x4A, 0x89, 0x78, + 0x11, 0x70, 0x88, 0xB3, 0x00, 0x20, 0x04, 0xB0, 0x26, 0xE7, 0x68, 0x46, + 0x00, 0xF0, 0xA0, 0xF8, 0xEE, 0xE7, 0x68, 0x46, 0x00, 0x27, 0x09, 0xF0, + 0x3A, 0xFB, 0xFF, 0xF7, 0x85, 0xFF, 0x68, 0x49, 0x09, 0x78, 0xC9, 0x07, + 0x00, 0xD0, 0x10, 0x27, 0xC1, 0x07, 0x01, 0xD0, 0x8D, 0xF8, 0x04, 0x60, + 0xC1, 0x06, 0x02, 0xD5, 0x8D, 0xF8, 0x08, 0x60, 0x03, 0xE0, 0xF9, 0x06, + 0x01, 0xD5, 0x8D, 0xF8, 0x08, 0x40, 0x28, 0x60, 0xC8, 0xF8, 0x00, 0x70, + 0xD2, 0xE7, 0x68, 0x46, 0x09, 0xF0, 0x1F, 0xFB, 0xFF, 0xF7, 0x6A, 0xFF, + 0x10, 0x21, 0xC2, 0x07, 0x01, 0xD0, 0x8D, 0xF8, 0x04, 0x40, 0xC2, 0x06, + 0x03, 0xD5, 0x8D, 0xF8, 0x08, 0x60, 0x03, 0xE0, 0x10, 0xE0, 0x04, 0x22, + 0x8D, 0xF8, 0x08, 0x20, 0x28, 0x60, 0xC8, 0xF8, 0x00, 0x10, 0xBB, 0xE7, + 0x00, 0x26, 0x09, 0xF0, 0x08, 0xFB, 0x8D, 0xF8, 0x04, 0x40, 0x01, 0x20, + 0x2E, 0x60, 0xC8, 0xF8, 0x00, 0x00, 0xB1, 0xE7, 0x01, 0x20, 0xBA, 0xE7, + 0x44, 0x49, 0x51, 0x4A, 0x8B, 0x78, 0x10, 0x7A, 0x63, 0xF3, 0x03, 0x00, + 0x10, 0x72, 0xC8, 0x78, 0x50, 0x72, 0x00, 0x22, 0x28, 0xB1, 0x40, 0x1E, + 0x10, 0xF0, 0xFF, 0x00, 0xC8, 0x70, 0x00, 0xD1, 0x8A, 0x70, 0x3D, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x9F, 0x33, 0x9B, 0x07, 0x08, 0xD5, 0x90, 0xF8, + 0xA0, 0x03, 0x0B, 0x79, 0x98, 0x42, 0x00, 0xD2, 0x0A, 0x71, 0x08, 0x79, + 0x40, 0x1C, 0x08, 0x71, 0x70, 0x47, 0x34, 0x4A, 0xD3, 0x78, 0x99, 0x42, + 0x01, 0xD9, 0xD1, 0x70, 0x90, 0x70, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, + 0x00, 0x26, 0x05, 0x46, 0x34, 0x46, 0x09, 0xF0, 0xD0, 0xFA, 0x34, 0x48, + 0x2D, 0x4F, 0x00, 0x78, 0xC0, 0x07, 0x12, 0xD0, 0x01, 0x24, 0xFF, 0xF7, + 0xF9, 0xFE, 0x01, 0x28, 0x00, 0xD1, 0x11, 0x24, 0xFF, 0xF7, 0x02, 0xFF, + 0x01, 0x28, 0x01, 0xD1, 0x44, 0xF0, 0x10, 0x04, 0x38, 0x68, 0x90, 0xF8, + 0x10, 0x01, 0xC0, 0x07, 0x01, 0xD0, 0x44, 0xF0, 0x04, 0x04, 0xE1, 0x07, + 0x4F, 0xF0, 0x01, 0x00, 0x09, 0xD0, 0x28, 0x71, 0x39, 0x68, 0x91, 0xF8, + 0x25, 0x10, 0xC9, 0x07, 0x03, 0xD0, 0x2A, 0x49, 0x09, 0x78, 0x01, 0xB1, + 0xA8, 0x73, 0x61, 0x07, 0x00, 0xD5, 0xA8, 0x71, 0xE1, 0x06, 0x00, 0xD5, + 0x28, 0x72, 0x20, 0x48, 0x06, 0x60, 0x20, 0x48, 0x04, 0x60, 0x83, 0xE6, + 0x70, 0xB5, 0x06, 0x46, 0x00, 0x24, 0x09, 0xF0, 0x9A, 0xFA, 0xFF, 0xF7, + 0xE5, 0xFE, 0x05, 0x46, 0x17, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x13, 0xD0, + 0x01, 0x24, 0xFF, 0xF7, 0xC1, 0xFE, 0x01, 0x28, 0x00, 0xD1, 0x11, 0x24, + 0xFF, 0xF7, 0xCA, 0xFE, 0x01, 0x28, 0x01, 0xD1, 0x44, 0xF0, 0x10, 0x04, + 0x09, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x10, 0x01, 0xC0, 0x07, 0x01, 0xD0, + 0x44, 0xF0, 0x04, 0x04, 0x45, 0xEA, 0x04, 0x00, 0xC1, 0x07, 0x4F, 0xF0, + 0x02, 0x00, 0x00, 0xD0, 0x30, 0x71, 0x62, 0x07, 0x1E, 0xE0, 0x00, 0x00, + 0x14, 0x23, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x34, 0x23, 0x10, 0x00, + 0x60, 0x24, 0x10, 0x00, 0x30, 0x23, 0x10, 0x00, 0x0A, 0x24, 0x10, 0x00, + 0x0B, 0x24, 0x10, 0x00, 0x24, 0x23, 0x10, 0x00, 0x90, 0x46, 0x10, 0x00, + 0x2C, 0x23, 0x10, 0x00, 0x28, 0x23, 0x10, 0x00, 0x85, 0x3E, 0x00, 0x00, + 0x6C, 0x24, 0x10, 0x00, 0xD0, 0x25, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0x4F, 0xF0, 0x01, 0x01, 0x00, 0xD5, 0xB1, 0x71, 0xEA, 0x06, 0x01, 0xD5, + 0x31, 0x72, 0x02, 0xE0, 0xE1, 0x06, 0x00, 0xD5, 0x30, 0x72, 0x02, 0x48, + 0x05, 0x60, 0x02, 0x48, 0x04, 0x60, 0x70, 0xBD, 0x2C, 0x23, 0x10, 0x00, + 0x28, 0x23, 0x10, 0x00, 0x08, 0xB5, 0x17, 0x49, 0x17, 0x4B, 0x00, 0x20, + 0x01, 0x22, 0x08, 0x60, 0x1A, 0x70, 0x48, 0x60, 0x88, 0x60, 0x41, 0x1E, + 0x11, 0xE0, 0x6B, 0x46, 0x00, 0x22, 0x17, 0x21, 0x01, 0x20, 0x0A, 0xF0, + 0xB7, 0xFA, 0x00, 0x98, 0x81, 0x07, 0x03, 0xD5, 0xFF, 0xF7, 0xC0, 0xF9, + 0x02, 0x21, 0x04, 0xE0, 0x41, 0x07, 0x06, 0xD5, 0xFF, 0xF7, 0x38, 0xFA, + 0x04, 0x21, 0x01, 0x20, 0x0A, 0xF0, 0x87, 0xFA, 0xE9, 0xE7, 0xC1, 0x06, + 0x03, 0xD5, 0xFF, 0xF7, 0x82, 0xFA, 0x10, 0x21, 0xF5, 0xE7, 0xC0, 0x07, + 0xE1, 0xD0, 0x01, 0x21, 0x08, 0x46, 0x0A, 0xF0, 0x7A, 0xFA, 0xFF, 0xF7, + 0x7E, 0xF9, 0xDA, 0xE7, 0x24, 0x23, 0x10, 0x00, 0x60, 0x24, 0x10, 0x00, + 0x10, 0xB5, 0x01, 0xF0, 0x00, 0xF9, 0x45, 0x48, 0x00, 0x21, 0x01, 0x70, + 0x41, 0x60, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, 0xB0, 0xBB, 0x2D, 0xE9, + 0xF1, 0x4F, 0x41, 0x48, 0x4F, 0xF0, 0x00, 0x0A, 0x84, 0xB0, 0x00, 0x68, + 0xD1, 0x46, 0x54, 0x46, 0x90, 0xF8, 0xD4, 0x10, 0x89, 0x07, 0x01, 0xD5, + 0x4F, 0xF0, 0x01, 0x09, 0x3B, 0x4D, 0xDF, 0xF8, 0xF0, 0x80, 0xDF, 0xF8, + 0xF0, 0xB0, 0x2A, 0x7E, 0x98, 0xF8, 0x18, 0x10, 0xCA, 0x42, 0x25, 0xD1, + 0x39, 0x4F, 0xB0, 0xF8, 0xDA, 0x60, 0x3B, 0x46, 0x38, 0x89, 0x30, 0x44, + 0x02, 0xB2, 0x37, 0x48, 0x01, 0x78, 0x37, 0x48, 0x01, 0xF0, 0x01, 0xF8, + 0x02, 0x90, 0x78, 0x89, 0x3B, 0x1D, 0x30, 0x44, 0x02, 0xB2, 0x34, 0x48, + 0x01, 0x78, 0x34, 0x48, 0x00, 0xF0, 0xF7, 0xFF, 0x02, 0x99, 0x01, 0x29, + 0x07, 0xD1, 0x01, 0x28, 0x05, 0xD1, 0x4B, 0x46, 0x3A, 0x46, 0x2F, 0x49, + 0x2C, 0x48, 0x00, 0xF0, 0xB2, 0xFB, 0x29, 0x7E, 0x98, 0xF8, 0x18, 0x00, + 0xC1, 0x42, 0x3C, 0xD0, 0x00, 0x20, 0x00, 0x90, 0x01, 0x90, 0x01, 0xAA, + 0x69, 0x46, 0x04, 0x98, 0x00, 0xF0, 0x7A, 0xFB, 0x00, 0x9E, 0x96, 0xB3, + 0x01, 0x9A, 0x82, 0xB3, 0x7F, 0x24, 0x01, 0x20, 0x03, 0x46, 0x2F, 0x7E, + 0x09, 0xE0, 0x03, 0xFA, 0x00, 0xF1, 0x31, 0x42, 0x04, 0xD0, 0x29, 0x18, + 0x09, 0x7B, 0xA1, 0x42, 0x00, 0xD2, 0x0C, 0x46, 0x40, 0x1C, 0x87, 0x42, + 0xF3, 0xD2, 0x01, 0x20, 0x1F, 0x46, 0x45, 0x46, 0x98, 0xF8, 0x18, 0x60, + 0x09, 0xE0, 0x07, 0xFA, 0x00, 0xF3, 0x13, 0x42, 0x04, 0xD0, 0x29, 0x18, + 0x09, 0x7B, 0xA1, 0x42, 0x00, 0xD2, 0x0C, 0x46, 0x40, 0x1C, 0x86, 0x42, + 0xF3, 0xD2, 0x0B, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xDC, 0x00, 0xA0, 0x42, + 0x07, 0xD2, 0x9B, 0xF8, 0x0A, 0x10, 0x4F, 0xF0, 0x01, 0x0A, 0x41, 0xF0, + 0x10, 0x01, 0x8B, 0xF8, 0x0A, 0x10, 0x8B, 0xF8, 0x1B, 0x40, 0x05, 0xB0, + 0x50, 0x46, 0xBD, 0xE8, 0xF0, 0x8F, 0x00, 0x00, 0x38, 0x23, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0xDD, 0x46, 0x10, 0x00, 0xF6, 0x46, 0x10, 0x00, + 0x8A, 0x25, 0x10, 0x00, 0xA4, 0x48, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, + 0x80, 0x0A, 0x01, 0x20, 0x01, 0x24, 0x10, 0x00, 0xBC, 0x0A, 0x01, 0x20, + 0x2D, 0xE9, 0xF0, 0x5F, 0x9B, 0x46, 0x75, 0x4B, 0x8A, 0x46, 0xDD, 0xE9, + 0x0A, 0x91, 0x1B, 0x68, 0x01, 0x26, 0xB3, 0xF8, 0xAA, 0x30, 0x19, 0x44, + 0x30, 0xF9, 0x12, 0x30, 0x0F, 0xFA, 0x81, 0xF8, 0x03, 0xFB, 0x02, 0xF4, + 0x31, 0x46, 0x2B, 0xE0, 0x55, 0x1A, 0x6D, 0xB2, 0x00, 0x2D, 0x10, 0xDB, + 0x01, 0x2E, 0x0E, 0xD1, 0x30, 0xF9, 0x15, 0x70, 0x47, 0x45, 0x09, 0xDD, + 0x00, 0xEB, 0x45, 0x0C, 0xBC, 0xF9, 0x02, 0xC0, 0x67, 0x45, 0x03, 0xDC, + 0x07, 0xFB, 0x05, 0x44, 0x3B, 0x44, 0x00, 0xE0, 0x00, 0x26, 0x55, 0x18, + 0x6D, 0xB2, 0x55, 0x45, 0x10, 0xDA, 0x01, 0x2E, 0x0E, 0xD1, 0x30, 0xF9, + 0x15, 0x70, 0x47, 0x45, 0x09, 0xDD, 0x00, 0xEB, 0x45, 0x0C, 0x3C, 0xF9, + 0x02, 0xCC, 0x67, 0x45, 0x03, 0xDC, 0x07, 0xFB, 0x05, 0x44, 0x3B, 0x44, + 0x00, 0xE0, 0x00, 0x26, 0x49, 0x1C, 0xC9, 0xB2, 0x59, 0x45, 0xD1, 0xD9, + 0xAA, 0xF1, 0x01, 0x00, 0x83, 0xFB, 0x00, 0x23, 0x84, 0xFB, 0x09, 0x01, + 0x0B, 0xF0, 0x44, 0xFB, 0x00, 0x28, 0x00, 0xDA, 0x00, 0x20, 0xA9, 0xF1, + 0x01, 0x01, 0x88, 0x42, 0x00, 0xDD, 0x08, 0x46, 0xBD, 0xE8, 0xF0, 0x9F, + 0x2D, 0xE9, 0xF0, 0x41, 0x92, 0xB0, 0x00, 0x24, 0xDF, 0xF8, 0x2C, 0x81, + 0x3B, 0xE0, 0x22, 0x46, 0x49, 0x49, 0x02, 0xA8, 0x00, 0xF0, 0x60, 0xF8, + 0x46, 0x4F, 0x48, 0x4E, 0x25, 0x44, 0x38, 0x68, 0xB6, 0xF9, 0x08, 0x20, + 0xB0, 0xF9, 0x18, 0x10, 0xCD, 0xE9, 0x00, 0x12, 0x90, 0xF8, 0xAC, 0x30, + 0x43, 0x48, 0x95, 0xF9, 0x01, 0x20, 0x01, 0x78, 0x42, 0x48, 0xFF, 0xF7, + 0x8D, 0xFF, 0x0F, 0x90, 0x38, 0x68, 0xB6, 0xF9, 0x0A, 0x20, 0xB0, 0xF9, + 0x16, 0x10, 0xCD, 0xE9, 0x00, 0x12, 0x90, 0xF8, 0xAC, 0x30, 0x3D, 0x48, + 0x95, 0xF9, 0x03, 0x20, 0x01, 0x78, 0x3C, 0x48, 0xFF, 0xF7, 0x7C, 0xFF, + 0x10, 0x90, 0x0F, 0xA8, 0x04, 0xF0, 0xA4, 0xF8, 0x10, 0x98, 0xAD, 0xF8, + 0x0A, 0x00, 0x0F, 0x99, 0xAD, 0xF8, 0x0E, 0x00, 0xAD, 0xF8, 0x08, 0x10, + 0xAD, 0xF8, 0x0C, 0x10, 0x02, 0xA8, 0x03, 0xF0, 0x5D, 0xFA, 0x64, 0x1C, + 0xE4, 0xB2, 0x98, 0xF8, 0x00, 0x00, 0x45, 0x46, 0xA0, 0x42, 0xBE, 0xD8, + 0x12, 0xB0, 0xBD, 0xE8, 0xF0, 0x81, 0x2E, 0x49, 0x01, 0x20, 0x08, 0x70, + 0x26, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, 0x01, 0x72, 0x70, 0x47, + 0x29, 0x48, 0x10, 0xB5, 0x00, 0x78, 0x00, 0x24, 0x01, 0x28, 0x0D, 0xD0, + 0x20, 0x48, 0x00, 0x78, 0x10, 0xB1, 0x01, 0x24, 0xFF, 0xF7, 0xA0, 0xFF, + 0x24, 0x49, 0x00, 0x20, 0x08, 0x70, 0x24, 0x49, 0x0C, 0x70, 0x24, 0x49, + 0x08, 0x70, 0x10, 0xBD, 0x00, 0xF0, 0xCC, 0xFB, 0x04, 0x46, 0xF3, 0xE7, + 0x10, 0xB5, 0x00, 0x23, 0x0A, 0x44, 0x03, 0x60, 0x51, 0x79, 0xD3, 0x79, + 0x59, 0x43, 0x02, 0x23, 0x83, 0x73, 0x1D, 0x4B, 0x1B, 0x88, 0x5B, 0x1C, + 0x03, 0x85, 0x43, 0x8B, 0x43, 0xF0, 0x08, 0x03, 0x43, 0x83, 0x81, 0x83, + 0x01, 0x84, 0x49, 0x1C, 0xC1, 0x83, 0x41, 0xF2, 0x88, 0x31, 0x01, 0x63, + 0xC1, 0x62, 0x51, 0x79, 0xD3, 0x79, 0x84, 0x8B, 0xCA, 0x18, 0x4C, 0x43, + 0x61, 0x00, 0xB1, 0xFB, 0xF2, 0xF1, 0x84, 0x8B, 0x5C, 0x43, 0x63, 0x00, + 0xB3, 0xFB, 0xF2, 0xF2, 0x91, 0x42, 0x02, 0xD9, 0x81, 0x84, 0xC2, 0x84, + 0x10, 0xBD, 0x82, 0x84, 0xC1, 0x84, 0x10, 0xBD, 0x50, 0x24, 0x10, 0x00, + 0xD4, 0x46, 0x10, 0x00, 0xA4, 0x48, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, + 0x80, 0x0A, 0x01, 0x20, 0x01, 0x24, 0x10, 0x00, 0xBC, 0x0A, 0x01, 0x20, + 0x40, 0x23, 0x10, 0x00, 0x05, 0x24, 0x10, 0x00, 0x06, 0x24, 0x10, 0x00, + 0x07, 0x24, 0x10, 0x00, 0xC6, 0x23, 0x10, 0x00, 0xF0, 0xB5, 0x05, 0x9E, + 0x34, 0x46, 0x0F, 0xE0, 0x50, 0xF8, 0x24, 0x50, 0x95, 0x42, 0x0B, 0xDA, + 0x67, 0x1C, 0xB7, 0x42, 0x05, 0xD2, 0x00, 0xEB, 0x84, 0x07, 0x7D, 0x60, + 0x0F, 0x19, 0x0D, 0x5D, 0x7D, 0x70, 0x40, 0xF8, 0x24, 0x20, 0x0B, 0x55, + 0x64, 0x1E, 0xED, 0xD2, 0xF0, 0xBD, 0x2D, 0xE9, 0xFF, 0x4F, 0x91, 0xB0, + 0x00, 0x20, 0x0E, 0x46, 0x0F, 0x90, 0x9B, 0x46, 0x14, 0x21, 0x06, 0xA8, + 0x0B, 0xF0, 0x2B, 0xFB, 0x00, 0x20, 0x0D, 0x90, 0x82, 0x46, 0x01, 0x22, + 0x0E, 0x90, 0x11, 0x46, 0x1E, 0x98, 0x00, 0xF0, 0xF6, 0xFB, 0x01, 0x20, + 0x62, 0xE0, 0x13, 0x99, 0x01, 0x25, 0x01, 0xEB, 0x49, 0x08, 0x57, 0xE0, + 0x0B, 0xEB, 0x45, 0x07, 0x14, 0x21, 0x01, 0xA8, 0x0B, 0xF0, 0x15, 0xFB, + 0x00, 0x20, 0x0B, 0x90, 0x01, 0x24, 0x0C, 0x90, 0x23, 0xE0, 0x06, 0xEB, + 0x84, 0x02, 0x98, 0xF8, 0x00, 0x00, 0x91, 0x78, 0x81, 0x42, 0x1B, 0xD3, + 0xD1, 0x78, 0x98, 0xF8, 0x01, 0x00, 0x81, 0x42, 0x16, 0xD8, 0x16, 0xF8, + 0x24, 0x00, 0x39, 0x78, 0x88, 0x42, 0x11, 0xD3, 0x50, 0x78, 0x79, 0x78, + 0x88, 0x42, 0x0D, 0xD8, 0xE1, 0xB2, 0x1E, 0x9B, 0x11, 0x98, 0x04, 0xF0, + 0x19, 0xFB, 0x05, 0x20, 0x00, 0x90, 0x1E, 0x98, 0xE3, 0xB2, 0x0B, 0xA9, + 0xC2, 0x6A, 0x01, 0xA8, 0xFF, 0xF7, 0xA0, 0xFF, 0x64, 0x1C, 0x96, 0xF8, + 0x0E, 0x01, 0xA0, 0x42, 0xD7, 0xD2, 0x01, 0x98, 0x00, 0x28, 0x20, 0xDD, + 0x01, 0x46, 0xFE, 0x48, 0x02, 0x68, 0x92, 0xF8, 0xC6, 0x20, 0x51, 0x43, + 0x64, 0x22, 0x91, 0xFB, 0xF2, 0xF7, 0x00, 0x24, 0x0B, 0xA8, 0x03, 0x5D, + 0x9B, 0xB1, 0x01, 0xA8, 0x50, 0xF8, 0x24, 0x20, 0xBA, 0x42, 0x0E, 0xDB, + 0x05, 0x20, 0x00, 0x90, 0x0D, 0xA9, 0x06, 0xA8, 0xFF, 0xF7, 0x80, 0xFF, + 0xF2, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xB0, 0x00, 0xC0, 0x06, 0x02, 0xD4, + 0x64, 0x1C, 0x05, 0x2C, 0xE8, 0xD3, 0x6D, 0x1C, 0x9B, 0xF8, 0x18, 0x00, + 0xA8, 0x42, 0xA3, 0xD2, 0x09, 0xF1, 0x01, 0x00, 0x81, 0x46, 0x13, 0x98, + 0x01, 0x7E, 0x49, 0x45, 0x97, 0xD2, 0xE8, 0x49, 0x05, 0x20, 0x0D, 0xAC, + 0x01, 0x25, 0x0B, 0x68, 0x11, 0xE0, 0x21, 0x5C, 0x79, 0xB1, 0x05, 0xFA, + 0x01, 0xF2, 0x0F, 0x99, 0x0A, 0x43, 0x0A, 0xF1, 0x01, 0x01, 0x01, 0xF0, + 0xFF, 0x0A, 0x0F, 0x92, 0x93, 0xF8, 0xB0, 0x10, 0x89, 0x06, 0x02, 0xD5, + 0xBA, 0xF1, 0x02, 0x0F, 0x01, 0xD2, 0x40, 0x1E, 0xEB, 0xD2, 0x0F, 0x98, + 0x15, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xF0, 0x5F, 0x83, 0x46, + 0x46, 0x1C, 0xD8, 0x48, 0x0C, 0x46, 0x90, 0x46, 0x01, 0x78, 0xD5, 0x48, + 0x01, 0x29, 0x00, 0x68, 0x67, 0xD0, 0x90, 0xF8, 0xEA, 0x90, 0xB0, 0xF8, + 0xE4, 0x00, 0x18, 0x44, 0x07, 0xB2, 0x00, 0x20, 0x82, 0x46, 0x11, 0x46, + 0x20, 0x46, 0x00, 0xF0, 0xB3, 0xFE, 0x05, 0x46, 0x41, 0x46, 0x20, 0x46, + 0x00, 0xF0, 0x9F, 0xFE, 0xBD, 0x42, 0x00, 0xDA, 0x3D, 0x46, 0xB8, 0x42, + 0x00, 0xDA, 0x38, 0x46, 0x28, 0x1A, 0x00, 0xFB, 0x09, 0xF2, 0x64, 0x21, + 0x92, 0xFB, 0xF1, 0xF2, 0x3A, 0x44, 0x17, 0xB2, 0xC4, 0x4A, 0x12, 0x78, + 0x01, 0x2A, 0x4C, 0xD0, 0xC3, 0x4A, 0x12, 0x78, 0x00, 0x2A, 0xC0, 0x4A, + 0x12, 0x68, 0x4B, 0xD0, 0x92, 0xF8, 0xE7, 0x20, 0x4F, 0xF0, 0x01, 0x09, + 0x50, 0x43, 0x90, 0xFB, 0xF1, 0xF0, 0x03, 0xB2, 0xB4, 0xF9, 0x00, 0x00, + 0xB8, 0x42, 0x01, 0xDD, 0x86, 0xF8, 0x00, 0x90, 0x04, 0xEB, 0x48, 0x00, + 0x0B, 0xF1, 0x01, 0x01, 0x30, 0xF9, 0x02, 0x2C, 0x41, 0x44, 0xBA, 0x42, + 0x01, 0xDD, 0x01, 0xF8, 0x01, 0x9C, 0xB4, 0xF9, 0x02, 0xC0, 0x0C, 0xEB, + 0x03, 0x02, 0x12, 0xB2, 0xBC, 0x45, 0x09, 0xDD, 0xB4, 0xF9, 0x00, 0xC0, + 0x94, 0x45, 0x03, 0xDD, 0xB4, 0xF9, 0x04, 0xC0, 0x94, 0x45, 0x01, 0xDC, + 0x86, 0xF8, 0x01, 0x90, 0x30, 0xF9, 0x04, 0xCC, 0x0C, 0xEB, 0x03, 0x02, + 0x12, 0xB2, 0xBC, 0x45, 0x09, 0xDD, 0x30, 0xF9, 0x06, 0xCC, 0x94, 0x45, + 0x03, 0xDD, 0x30, 0xF9, 0x02, 0x0C, 0x90, 0x42, 0x01, 0xDC, 0x01, 0xF8, + 0x02, 0x9C, 0x02, 0x22, 0xA8, 0xF1, 0x02, 0x0E, 0x42, 0xE0, 0x90, 0xF8, + 0xBC, 0x90, 0xB0, 0xF8, 0xBD, 0x00, 0x18, 0x44, 0x07, 0xB2, 0x01, 0x20, + 0x96, 0xE7, 0x9B, 0x4A, 0x12, 0x68, 0x92, 0xF8, 0xC0, 0x20, 0xB5, 0xE7, + 0x92, 0xF8, 0xE6, 0x20, 0xB2, 0xE7, 0x34, 0xF9, 0x12, 0xC0, 0xBC, 0x45, + 0x2D, 0xDD, 0x04, 0xEB, 0x42, 0x01, 0x0C, 0xEB, 0x03, 0x00, 0x31, 0xF9, + 0x02, 0xBC, 0x00, 0xB2, 0x83, 0x45, 0x03, 0xDD, 0xB1, 0xF9, 0x02, 0x80, + 0x80, 0x45, 0x0D, 0xDC, 0x31, 0xF9, 0x04, 0x8C, 0x80, 0x45, 0x03, 0xDD, + 0xB1, 0xF9, 0x02, 0x80, 0x80, 0x45, 0x05, 0xDC, 0x83, 0x45, 0x14, 0xDD, + 0xB1, 0xF9, 0x04, 0x10, 0x81, 0x42, 0x10, 0xDD, 0xBA, 0xF1, 0x00, 0x0F, + 0x0F, 0xD0, 0x00, 0x2D, 0x0D, 0xDD, 0x85, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0xBF, 0x00, 0x40, 0xB1, 0x64, 0x21, 0x0C, 0xFB, 0x01, 0xFC, 0x9C, 0xFB, + 0xF5, 0xF1, 0x81, 0x42, 0x01, 0xDD, 0x06, 0xF8, 0x02, 0x90, 0x52, 0x1C, + 0x72, 0x45, 0xCA, 0xD3, 0xBD, 0xE8, 0xF0, 0x9F, 0x30, 0xB5, 0x01, 0x25, + 0x40, 0x1C, 0x05, 0xE0, 0x14, 0x68, 0x05, 0xFA, 0x01, 0xF3, 0x1C, 0x42, + 0x00, 0xD0, 0x45, 0x54, 0x49, 0x1E, 0xF7, 0xD2, 0x30, 0xBD, 0x2D, 0xE9, + 0xF8, 0x4F, 0x1C, 0x46, 0x89, 0x46, 0xDD, 0xE9, 0x0A, 0x63, 0x06, 0x21, + 0x00, 0x25, 0x92, 0x46, 0xCF, 0x1F, 0xDF, 0xF8, 0xCC, 0xC1, 0x08, 0xE0, + 0x62, 0x18, 0x15, 0x73, 0x97, 0x74, 0x04, 0xEB, 0x41, 0x02, 0xBC, 0xF8, + 0x00, 0x80, 0xA2, 0xF8, 0x00, 0x80, 0x49, 0x1E, 0xF4, 0xD2, 0x6A, 0x49, + 0x25, 0x76, 0x0A, 0x78, 0x67, 0x49, 0x01, 0x2A, 0x09, 0x68, 0x08, 0xD0, + 0xB1, 0xF8, 0xE8, 0x10, 0x01, 0x22, 0x19, 0x44, 0x0F, 0xB2, 0x00, 0x21, + 0x93, 0x46, 0x00, 0x95, 0x15, 0xE0, 0xB1, 0xF8, 0xC1, 0x10, 0xF5, 0xE7, + 0x43, 0x18, 0x93, 0xF8, 0x01, 0xC0, 0xBC, 0xF1, 0x00, 0x0F, 0x15, 0xD0, + 0x5A, 0x70, 0x39, 0xF9, 0x11, 0x30, 0xBB, 0x42, 0x06, 0xDD, 0xDD, 0xF8, + 0x00, 0xC0, 0x0B, 0xFA, 0x02, 0xF3, 0x43, 0xEA, 0x0C, 0x03, 0x00, 0x93, + 0x49, 0x1C, 0x51, 0x45, 0xEA, 0xD3, 0x01, 0x23, 0x1A, 0x46, 0x58, 0x4F, + 0xAE, 0x46, 0x00, 0x21, 0x35, 0x60, 0x47, 0xE0, 0x43, 0x5C, 0x00, 0x2B, + 0xF2, 0xD0, 0x52, 0x1C, 0xD2, 0xB2, 0xEF, 0xE7, 0x45, 0x18, 0x95, 0xF8, + 0x01, 0xC0, 0xBC, 0xF1, 0x00, 0x0F, 0x24, 0xD0, 0xDD, 0xF8, 0x00, 0x80, + 0x0B, 0xFA, 0x03, 0xFC, 0x1C, 0xEA, 0x08, 0x0F, 0x31, 0xD0, 0x6A, 0x70, + 0xD6, 0xF8, 0x00, 0xC0, 0x0B, 0xFA, 0x01, 0xF5, 0x4C, 0xEA, 0x05, 0x0C, + 0xA5, 0x18, 0xC6, 0xF8, 0x00, 0xC0, 0x95, 0xF8, 0x0C, 0xC0, 0x0C, 0xF1, + 0x01, 0x0C, 0x85, 0xF8, 0x0C, 0xC0, 0x39, 0xF9, 0x11, 0xC0, 0xBC, 0x45, + 0x01, 0xDD, 0x67, 0x46, 0xA9, 0x74, 0x04, 0xEB, 0x42, 0x05, 0x95, 0xF8, + 0x00, 0xC0, 0xBC, 0xF1, 0xFF, 0x0F, 0x11, 0xD0, 0x11, 0xE0, 0x3D, 0x4F, + 0x45, 0x5C, 0x95, 0xB1, 0xDD, 0xF8, 0x00, 0xC0, 0x0B, 0xFA, 0x03, 0xF5, + 0x15, 0xEA, 0x0C, 0x0F, 0x03, 0xD0, 0x05, 0x2A, 0x01, 0xD2, 0x52, 0x1C, + 0xD2, 0xB2, 0x5B, 0x1C, 0xDB, 0xB2, 0x04, 0xE0, 0x29, 0x70, 0x69, 0x70, + 0x01, 0xE0, 0x85, 0xF8, 0x01, 0xE0, 0x49, 0x1C, 0x51, 0x45, 0xBB, 0xD3, + 0x04, 0x21, 0x68, 0x46, 0x0D, 0xF0, 0xF6, 0xF9, 0x20, 0x76, 0xBD, 0xE8, + 0xF8, 0x8F, 0x2F, 0x48, 0x10, 0xB5, 0x2D, 0x49, 0x01, 0x60, 0x2E, 0x49, + 0x41, 0x60, 0x19, 0x21, 0x2D, 0x48, 0x0B, 0xF0, 0x0E, 0xF9, 0x2C, 0x48, + 0x19, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x19, 0x30, 0x0B, 0xF0, 0x07, 0xB9, + 0xF0, 0xB5, 0x4F, 0xF0, 0x00, 0x0C, 0x25, 0x4E, 0xC1, 0xF8, 0x00, 0xC0, + 0x83, 0x78, 0x01, 0x25, 0x37, 0x68, 0x0B, 0xE0, 0xFC, 0x18, 0x64, 0x78, + 0x3C, 0xB1, 0xD1, 0xF8, 0x00, 0xE0, 0x05, 0xFA, 0x04, 0xF4, 0x4E, 0xEA, + 0x04, 0x0E, 0xC1, 0xF8, 0x00, 0xE0, 0x5B, 0x1C, 0xC4, 0x78, 0x9C, 0x42, + 0xF0, 0xD2, 0xC2, 0xF8, 0x00, 0xC0, 0x01, 0x78, 0x09, 0xE0, 0x73, 0x68, + 0x0B, 0x44, 0x5B, 0x78, 0x23, 0xB1, 0x14, 0x68, 0x05, 0xFA, 0x03, 0xF3, + 0x1C, 0x43, 0x14, 0x60, 0x49, 0x1C, 0x43, 0x78, 0x8B, 0x42, 0xF2, 0xD2, + 0xF0, 0xBD, 0x2D, 0xE9, 0xFC, 0x47, 0x10, 0x4D, 0x8A, 0x46, 0x81, 0x46, + 0x98, 0x46, 0x14, 0x46, 0x20, 0x21, 0x28, 0x68, 0x0B, 0xF0, 0xD1, 0xF8, + 0x22, 0x21, 0x68, 0x68, 0x0B, 0xF0, 0xCD, 0xF8, 0x0C, 0x4E, 0x0D, 0x4F, + 0xB8, 0xF1, 0x01, 0x0F, 0x38, 0xD0, 0x22, 0x46, 0x31, 0x78, 0x15, 0xE0, + 0x50, 0x24, 0x10, 0x00, 0x55, 0x23, 0x10, 0x00, 0x4C, 0x23, 0x10, 0x00, + 0x14, 0x1D, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x1E, 0x48, 0x10, 0x00, + 0x44, 0x23, 0x10, 0x00, 0x5E, 0x48, 0x10, 0x00, 0xDD, 0x46, 0x10, 0x00, + 0x00, 0x24, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, 0x28, 0x68, 0xFF, 0xF7, + 0xEF, 0xFE, 0x22, 0x1D, 0x39, 0x78, 0x68, 0x68, 0xFF, 0xF7, 0xEA, 0xFE, + 0xB4, 0xF9, 0x08, 0x00, 0xD5, 0x4B, 0xCD, 0xE9, 0x00, 0x40, 0x32, 0x78, + 0x49, 0x46, 0x28, 0x68, 0xFF, 0xF7, 0xED, 0xFE, 0xB4, 0xF9, 0x0A, 0x00, + 0x24, 0x1D, 0xCD, 0xE9, 0x00, 0x40, 0xCF, 0x4B, 0x3A, 0x78, 0x19, 0x33, + 0x51, 0x46, 0x68, 0x68, 0xFF, 0xF7, 0xE1, 0xFE, 0xBD, 0xE8, 0xFC, 0x87, + 0xB4, 0xF9, 0x08, 0x30, 0x32, 0x78, 0x49, 0x46, 0x28, 0x68, 0xFF, 0xF7, + 0x10, 0xFE, 0xB4, 0xF9, 0x0A, 0x30, 0x3A, 0x78, 0x51, 0x46, 0x68, 0x68, + 0xFF, 0xF7, 0x09, 0xFE, 0xD8, 0xE7, 0x70, 0xB5, 0x04, 0x46, 0xC3, 0x48, + 0x00, 0x78, 0x00, 0x07, 0x09, 0xD5, 0xC2, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0xB4, 0x02, 0x60, 0x43, 0x64, 0x21, 0x90, 0xFB, 0xF1, 0xF0, 0x20, 0x44, + 0x04, 0xB2, 0xBE, 0x4D, 0x2B, 0x46, 0x28, 0x89, 0x20, 0x44, 0x02, 0xB2, + 0xBC, 0x48, 0x01, 0x78, 0xBC, 0x48, 0x00, 0xF0, 0xC4, 0xFB, 0x68, 0x89, + 0x2B, 0x1D, 0x20, 0x44, 0x02, 0xB2, 0xBA, 0x48, 0x01, 0x78, 0xBA, 0x48, + 0x00, 0xF0, 0xBB, 0xFB, 0x28, 0x46, 0x00, 0xF0, 0xBB, 0xFC, 0x28, 0x1D, + 0xBD, 0xE8, 0x70, 0x40, 0x00, 0xF0, 0xB6, 0xBC, 0x2D, 0xE9, 0xFF, 0x5F, + 0xB4, 0x48, 0xD3, 0xE9, 0x00, 0x87, 0x00, 0x78, 0x8B, 0x46, 0x01, 0x28, + 0x05, 0xD1, 0xB2, 0x48, 0x00, 0x78, 0x10, 0xB9, 0x4F, 0xF0, 0xFF, 0x37, + 0xB8, 0x46, 0xB0, 0x48, 0x4F, 0xF4, 0x88, 0x61, 0x00, 0x68, 0x0B, 0xF0, + 0x40, 0xF8, 0xA7, 0x48, 0x00, 0x26, 0x35, 0x46, 0x04, 0x78, 0x4F, 0xF0, + 0x01, 0x09, 0x23, 0xE0, 0x09, 0xFA, 0x04, 0xF0, 0x10, 0xEA, 0x08, 0x0F, + 0x1E, 0xD0, 0xA7, 0x48, 0xE1, 0xB2, 0x00, 0x68, 0x0C, 0xF0, 0xAA, 0xFE, + 0x82, 0x46, 0xE1, 0xB2, 0x00, 0x98, 0x0C, 0xF0, 0xAF, 0xFE, 0x01, 0x46, + 0x9D, 0x48, 0x4B, 0x46, 0x00, 0x78, 0x0D, 0xE0, 0x03, 0xFA, 0x00, 0xF2, + 0x3A, 0x42, 0x09, 0xD0, 0x31, 0xF9, 0x10, 0xC0, 0xDC, 0x45, 0x05, 0xDD, + 0x0A, 0xF8, 0x00, 0x30, 0x31, 0xF9, 0x10, 0x20, 0x76, 0x1C, 0x15, 0x44, + 0x40, 0x1E, 0xEF, 0xD2, 0x64, 0x1E, 0xD9, 0xD2, 0x02, 0x98, 0x05, 0x60, + 0x30, 0x46, 0x04, 0xB0, 0x4E, 0xE6, 0x2D, 0xE9, 0xFF, 0x4F, 0x89, 0x4F, + 0x93, 0x48, 0x32, 0x37, 0x83, 0xB0, 0x00, 0x68, 0x4F, 0xF0, 0x00, 0x0A, + 0x38, 0x60, 0x99, 0x46, 0x8B, 0x46, 0x55, 0x46, 0x01, 0x20, 0x80, 0x46, + 0x03, 0x98, 0x01, 0x7E, 0x41, 0x45, 0x50, 0xD3, 0x03, 0x98, 0x01, 0x26, + 0x10, 0xF8, 0x18, 0x00, 0xB8, 0x70, 0x03, 0x98, 0x00, 0xEB, 0x48, 0x00, + 0x40, 0x78, 0xF8, 0x70, 0x3E, 0xE0, 0x1B, 0xF8, 0x16, 0x00, 0x38, 0x70, + 0x0B, 0xEB, 0x46, 0x00, 0x84, 0x49, 0x40, 0x78, 0x78, 0x70, 0x01, 0x20, + 0xCD, 0xE9, 0x00, 0x01, 0x03, 0x46, 0x7F, 0x48, 0x74, 0x49, 0x00, 0x22, + 0x32, 0x31, 0x00, 0x68, 0x04, 0xF0, 0x94, 0xFC, 0x00, 0x21, 0x29, 0xF8, + 0x15, 0x10, 0x01, 0x24, 0x1D, 0xE0, 0x00, 0xEB, 0x44, 0x00, 0x00, 0x90, + 0xB0, 0xF8, 0xB4, 0x10, 0x05, 0x98, 0x81, 0x42, 0x04, 0xD9, 0x0A, 0xF1, + 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x0A, 0x07, 0xE0, 0x72, 0x48, 0x68, 0x4A, + 0xE1, 0xB2, 0x73, 0x4B, 0x32, 0x32, 0x00, 0x68, 0x04, 0xF0, 0x5B, 0xFD, + 0x00, 0x98, 0x39, 0xF8, 0x15, 0x10, 0x64, 0x1C, 0xB0, 0xF8, 0xB4, 0x00, + 0x08, 0x44, 0x29, 0xF8, 0x15, 0x00, 0x60, 0x48, 0x32, 0x30, 0x90, 0xF8, + 0x0E, 0x11, 0xA1, 0x42, 0xDB, 0xD2, 0x6D, 0x1C, 0xED, 0xB2, 0x76, 0x1C, + 0x9B, 0xF8, 0x18, 0x00, 0xB0, 0x42, 0xBC, 0xD2, 0x08, 0xF1, 0x01, 0x00, + 0xA9, 0xE7, 0x50, 0x46, 0x07, 0xB0, 0x2C, 0xE5, 0x2D, 0xE9, 0xF0, 0x4F, + 0x97, 0xB0, 0x61, 0x49, 0x00, 0x20, 0xCD, 0xE9, 0x00, 0x01, 0x5D, 0x4E, + 0x52, 0x49, 0x02, 0x46, 0x01, 0x23, 0x32, 0x31, 0x30, 0x68, 0x04, 0xF0, + 0x4F, 0xFC, 0x5C, 0x48, 0x4E, 0x4F, 0x4F, 0xF0, 0xFF, 0x35, 0x00, 0x78, + 0x32, 0x37, 0x0D, 0xF1, 0x0C, 0x09, 0x01, 0x28, 0x0E, 0xD1, 0xCD, 0xF8, + 0x00, 0x90, 0xA7, 0xF1, 0x19, 0x03, 0x48, 0x4A, 0x39, 0x46, 0x30, 0x68, + 0xFF, 0xF7, 0x71, 0xFC, 0x53, 0x49, 0x05, 0x46, 0x97, 0xF8, 0x0E, 0x01, + 0x08, 0x76, 0x4D, 0x76, 0x48, 0x48, 0x4F, 0xF0, 0x00, 0x08, 0xBB, 0x46, + 0x00, 0x78, 0x8D, 0xF8, 0x52, 0x00, 0x0D, 0xF1, 0x2E, 0x00, 0x10, 0x90, + 0x0C, 0xA8, 0x11, 0x90, 0x80, 0x1C, 0x12, 0x90, 0x0D, 0x38, 0x01, 0x24, + 0x13, 0x90, 0x3B, 0xE0, 0x01, 0x21, 0xA1, 0x40, 0x29, 0x42, 0x36, 0xD0, + 0x00, 0xEB, 0x84, 0x0A, 0x0B, 0xEB, 0x44, 0x00, 0xE1, 0xB2, 0xB0, 0xF8, + 0xB4, 0x20, 0x03, 0xA8, 0x00, 0xF0, 0x55, 0xF8, 0xE1, 0xB2, 0x03, 0xAB, + 0x52, 0x46, 0x30, 0x68, 0x03, 0xF0, 0x9E, 0xFF, 0x3E, 0x4F, 0x15, 0xAA, + 0x53, 0x46, 0xB7, 0xF9, 0x00, 0x10, 0xCD, 0xF8, 0x08, 0x90, 0xCD, 0xE9, + 0x00, 0x21, 0x31, 0x68, 0xE2, 0xB2, 0x08, 0x46, 0x03, 0xF0, 0x13, 0xFE, + 0xBD, 0xF8, 0x28, 0x00, 0xA8, 0xB1, 0xBD, 0xF9, 0x34, 0x10, 0xB7, 0xF9, + 0x00, 0x20, 0x91, 0x42, 0x0F, 0xDD, 0xAD, 0xF8, 0x50, 0x00, 0x4F, 0xF0, + 0x01, 0x08, 0xE1, 0xB2, 0x10, 0xAB, 0x52, 0x46, 0x30, 0x68, 0x09, 0xF0, + 0x8A, 0xFE, 0xE1, 0xB2, 0x03, 0xAB, 0x52, 0x46, 0x30, 0x68, 0x03, 0xF0, + 0x39, 0xFD, 0x64, 0x1C, 0x9B, 0xF8, 0x0E, 0x11, 0x58, 0x46, 0xA1, 0x42, + 0xBE, 0xD2, 0x40, 0x46, 0x17, 0xB0, 0xAE, 0xE4, 0x70, 0xB5, 0x01, 0x21, + 0x1C, 0x4C, 0x19, 0x4B, 0x0A, 0x46, 0x4E, 0x1E, 0x1D, 0x68, 0x02, 0xFA, + 0x06, 0xF6, 0x35, 0x42, 0x03, 0xD0, 0x25, 0x78, 0xAD, 0x1C, 0x4D, 0x43, + 0x42, 0x55, 0x49, 0x1C, 0x20, 0x29, 0xF2, 0xD3, 0x01, 0x21, 0x16, 0x46, + 0x4D, 0x1E, 0x5C, 0x68, 0x06, 0xFA, 0x05, 0xF2, 0x14, 0x42, 0x00, 0xD0, + 0x46, 0x54, 0x49, 0x1C, 0x22, 0x29, 0xF5, 0xD3, 0x70, 0xBD, 0x70, 0xB5, + 0x0E, 0x46, 0x15, 0x46, 0x04, 0x46, 0x34, 0x21, 0x0A, 0xF0, 0x25, 0xFF, + 0x02, 0x20, 0xA0, 0x73, 0x13, 0x48, 0x00, 0x68, 0x20, 0x61, 0x60, 0x61, + 0x26, 0x76, 0x25, 0x84, 0xA5, 0x83, 0x70, 0xBD, 0xDD, 0x46, 0x10, 0x00, + 0x1C, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0xA4, 0x48, 0x10, 0x00, + 0x00, 0x24, 0x10, 0x00, 0x80, 0x0A, 0x01, 0x20, 0x01, 0x24, 0x10, 0x00, + 0xBC, 0x0A, 0x01, 0x20, 0x71, 0x23, 0x10, 0x00, 0x6D, 0x23, 0x10, 0x00, + 0xA0, 0x23, 0x10, 0x00, 0x60, 0x1D, 0x01, 0x00, 0xAC, 0x23, 0x10, 0x00, + 0x55, 0x23, 0x10, 0x00, 0x8A, 0x25, 0x10, 0x00, 0xCC, 0x23, 0x10, 0x00, + 0x5C, 0x23, 0x10, 0x00, 0x64, 0x4A, 0x30, 0xB4, 0x12, 0x68, 0x00, 0x21, + 0xB2, 0xF8, 0x7C, 0x30, 0x83, 0x42, 0x05, 0xDA, 0x92, 0xF8, 0x78, 0x30, + 0x5B, 0x07, 0x01, 0xD5, 0x02, 0x21, 0x08, 0xE0, 0xB2, 0xF8, 0x7A, 0x30, + 0x83, 0x42, 0x04, 0xDA, 0x92, 0xF8, 0x78, 0x00, 0x80, 0x07, 0x00, 0xD5, + 0x01, 0x21, 0x5A, 0x4B, 0x58, 0x78, 0x81, 0x42, 0x14, 0xD1, 0x1C, 0x78, + 0x84, 0x42, 0x1D, 0xD0, 0x57, 0x4D, 0x2D, 0x78, 0x28, 0x43, 0x57, 0x4D, + 0x2D, 0x78, 0x28, 0x43, 0x07, 0xD0, 0x58, 0x68, 0x40, 0x1E, 0x58, 0x60, + 0x00, 0x28, 0x11, 0xDC, 0x58, 0x78, 0x18, 0x70, 0x0E, 0xE0, 0x5C, 0x70, + 0x58, 0x68, 0xF7, 0xE7, 0x99, 0xB1, 0x02, 0x29, 0x01, 0xD1, 0x01, 0x28, + 0x0F, 0xD0, 0x92, 0xF8, 0x1A, 0x01, 0x58, 0x60, 0x59, 0x70, 0x00, 0x28, + 0x00, 0xDC, 0x19, 0x70, 0x18, 0x78, 0x01, 0x43, 0x08, 0xD0, 0x92, 0xF8, + 0xA7, 0x13, 0x30, 0xBC, 0x06, 0x20, 0x06, 0xF0, 0x46, 0xBB, 0x92, 0xF8, + 0x1B, 0x01, 0xEE, 0xE7, 0x30, 0xBC, 0x70, 0x47, 0x40, 0x48, 0x00, 0x21, + 0x01, 0x70, 0x41, 0x70, 0x41, 0x60, 0x70, 0x47, 0x2D, 0xE9, 0xFC, 0x41, + 0x3F, 0x4E, 0x07, 0x46, 0x0D, 0x46, 0x30, 0x78, 0x3E, 0x49, 0x42, 0x00, + 0x00, 0x24, 0x01, 0xF1, 0x3C, 0x00, 0x0A, 0xF0, 0xFB, 0xFD, 0x30, 0x78, + 0x39, 0x46, 0x42, 0x00, 0x39, 0x48, 0x0A, 0xF0, 0xF5, 0xFD, 0x28, 0x46, + 0x37, 0x4D, 0x78, 0x35, 0x01, 0x28, 0x0F, 0xD0, 0x31, 0x78, 0x00, 0x20, + 0xCD, 0xE9, 0x00, 0x01, 0x03, 0x46, 0xA5, 0xF1, 0x3C, 0x02, 0x39, 0x46, + 0x32, 0x48, 0x07, 0xF0, 0x7B, 0xFE, 0x00, 0x20, 0xDF, 0xF8, 0xA8, 0xC0, + 0x37, 0x78, 0x35, 0xE0, 0x30, 0x78, 0x39, 0x46, 0x42, 0x00, 0x2C, 0x48, + 0x3C, 0x30, 0x0A, 0xF0, 0xD9, 0xFD, 0x30, 0x78, 0x41, 0x00, 0x29, 0x48, + 0x78, 0x30, 0x0A, 0xF0, 0x52, 0xFE, 0x29, 0xE0, 0x26, 0x49, 0x27, 0x4A, + 0x3C, 0x31, 0x31, 0xF9, 0x10, 0x10, 0x32, 0xF9, 0x10, 0x20, 0x89, 0x1A, + 0x00, 0xD5, 0x49, 0x42, 0x35, 0xF9, 0x10, 0x30, 0x09, 0xB2, 0xDC, 0xF8, + 0x00, 0x20, 0x8B, 0x42, 0x02, 0xDA, 0x92, 0xF8, 0x7E, 0x20, 0x01, 0xE0, + 0x92, 0xF8, 0x7F, 0x20, 0x53, 0x43, 0xC2, 0xF5, 0x80, 0x72, 0x01, 0xFB, + 0x02, 0x31, 0xCA, 0x17, 0x01, 0xEB, 0x12, 0x61, 0x09, 0x12, 0x25, 0xF8, + 0x10, 0x10, 0x35, 0xF9, 0x10, 0x10, 0xA1, 0x42, 0x00, 0xDD, 0x0C, 0x46, + 0x40, 0x1C, 0x00, 0xB2, 0xB8, 0x42, 0xD5, 0xDB, 0x20, 0x46, 0xFF, 0xF7, + 0x4D, 0xFF, 0x0C, 0x4F, 0x11, 0x49, 0x12, 0x4B, 0x38, 0x78, 0x4A, 0x7C, + 0x60, 0xF3, 0x03, 0x02, 0x4A, 0x74, 0xDA, 0x7C, 0x60, 0xF3, 0x07, 0x12, + 0xDA, 0x74, 0x3A, 0x79, 0xCA, 0x74, 0xCC, 0x82, 0x31, 0x78, 0x25, 0xF8, + 0x11, 0x00, 0x05, 0xEB, 0x41, 0x00, 0x44, 0x80, 0xBD, 0xE8, 0xFC, 0x81, + 0x50, 0x24, 0x10, 0x00, 0x4C, 0x23, 0x10, 0x00, 0x29, 0x24, 0x10, 0x00, + 0x57, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0xD0, 0x01, 0x01, 0x20, + 0x1E, 0x48, 0x10, 0x00, 0x6C, 0x25, 0x10, 0x00, 0x58, 0x25, 0x10, 0x00, + 0x2D, 0xE9, 0xF0, 0x4F, 0x47, 0xF6, 0xFF, 0x76, 0xDD, 0xE9, 0x09, 0xB5, + 0xF4, 0x43, 0x9A, 0x46, 0x01, 0x27, 0x81, 0x46, 0xBC, 0x46, 0x2B, 0x46, + 0x14, 0xE0, 0x32, 0xF8, 0x13, 0x00, 0x31, 0xF8, 0x13, 0x80, 0xA0, 0xEB, + 0x08, 0x00, 0x00, 0xB2, 0xB0, 0x42, 0x00, 0xDA, 0x06, 0x46, 0xA0, 0x42, + 0x00, 0xDD, 0x04, 0x46, 0x00, 0x28, 0x02, 0xDD, 0x4F, 0xF0, 0x00, 0x0C, + 0x02, 0xE0, 0x01, 0xDA, 0x4F, 0xF0, 0x00, 0x07, 0x5B, 0x1E, 0xE8, 0xD2, + 0x27, 0xB1, 0xBC, 0xF1, 0x00, 0x0F, 0x01, 0xD0, 0x00, 0x20, 0x09, 0xE0, + 0x57, 0xEA, 0x0C, 0x00, 0x06, 0xD0, 0x27, 0xB9, 0xBC, 0xF1, 0x00, 0x0F, + 0x01, 0xD0, 0x20, 0x46, 0x00, 0xE0, 0x30, 0x46, 0x00, 0x23, 0x1E, 0x46, + 0x2C, 0x46, 0xCB, 0xF1, 0x00, 0x0C, 0x0D, 0xE0, 0x32, 0xF8, 0x14, 0x70, + 0x31, 0xF8, 0x14, 0x80, 0xA7, 0xEB, 0x08, 0x07, 0x3F, 0x1A, 0x3F, 0xB2, + 0x57, 0x45, 0x03, 0xDA, 0x67, 0x45, 0x01, 0xDD, 0x3B, 0x44, 0x76, 0x1C, + 0x64, 0x1E, 0xEF, 0xD2, 0x06, 0xB9, 0x01, 0x26, 0x93, 0xFB, 0xF6, 0xF2, + 0x13, 0x18, 0x04, 0xE0, 0x31, 0xF8, 0x15, 0x00, 0x18, 0x44, 0x29, 0xF8, + 0x15, 0x00, 0x6D, 0x1E, 0xF8, 0xD2, 0xBD, 0xE8, 0xF0, 0x8F, 0xF0, 0xB5, + 0x00, 0x25, 0x2E, 0x46, 0x0D, 0xE0, 0x30, 0xF9, 0x11, 0x40, 0x00, 0x2C, + 0x01, 0xDB, 0x27, 0x46, 0x00, 0xE0, 0x67, 0x42, 0x97, 0x42, 0x04, 0xDD, + 0x00, 0x2C, 0x01, 0xDD, 0x26, 0x44, 0x00, 0xE0, 0x25, 0x44, 0x49, 0x1E, + 0xEF, 0xD2, 0x00, 0x2D, 0x00, 0xDA, 0x6D, 0x42, 0xB5, 0x42, 0x01, 0xDD, + 0x01, 0x20, 0x18, 0x70, 0xF0, 0xBD, 0x2D, 0xE9, 0xFC, 0x41, 0x00, 0x25, + 0x0F, 0x46, 0x8D, 0xF8, 0x00, 0x50, 0x8D, 0xF8, 0x04, 0x50, 0x2C, 0x46, + 0xFE, 0x4E, 0x6B, 0x46, 0x31, 0x68, 0xB1, 0xF9, 0x84, 0x20, 0xFD, 0x49, + 0x09, 0x78, 0xFF, 0xF7, 0xD2, 0xFF, 0x30, 0x68, 0x01, 0xAB, 0xB0, 0xF9, + 0x84, 0x20, 0xFA, 0x48, 0x01, 0x78, 0x38, 0x46, 0xFF, 0xF7, 0xC9, 0xFF, + 0xF8, 0x4A, 0xD0, 0x79, 0x68, 0xB1, 0x50, 0x69, 0x40, 0x1C, 0x50, 0x61, + 0x31, 0x68, 0x91, 0xF8, 0x87, 0x10, 0x81, 0x42, 0x02, 0xDA, 0x55, 0x61, + 0xD5, 0x71, 0x01, 0x24, 0x20, 0x46, 0xBD, 0xE8, 0xFC, 0x81, 0x9D, 0xF8, + 0x00, 0x00, 0x01, 0x28, 0x03, 0xD1, 0x9D, 0xF8, 0x04, 0x00, 0x01, 0x28, + 0x01, 0xD0, 0x55, 0x61, 0xF2, 0xE7, 0x51, 0x69, 0x49, 0x1C, 0x51, 0x61, + 0x30, 0x68, 0x90, 0xF8, 0x86, 0x30, 0x8B, 0x42, 0x07, 0xDA, 0x01, 0x21, + 0x55, 0x61, 0xD1, 0x71, 0x90, 0xF8, 0x87, 0x10, 0x09, 0xB9, 0xD5, 0x71, + 0x01, 0x24, 0x90, 0xF8, 0xA8, 0x13, 0x07, 0x20, 0x06, 0xF0, 0xEF, 0xF9, + 0xDC, 0xE7, 0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x52, 0x42, 0x05, 0xE0, + 0x30, 0xF9, 0x11, 0x60, 0x96, 0x42, 0x01, 0xDC, 0x64, 0x1C, 0xE4, 0xB2, + 0x49, 0x1E, 0xF7, 0xD2, 0x9C, 0x42, 0x00, 0xD3, 0x01, 0x25, 0x28, 0x46, + 0x70, 0xBD, 0x10, 0xB5, 0x01, 0x24, 0x05, 0xE0, 0x30, 0xF9, 0x11, 0x30, + 0x93, 0x42, 0x01, 0xDA, 0x00, 0x24, 0x01, 0xE0, 0x49, 0x1E, 0xF7, 0xD2, + 0x20, 0x46, 0x10, 0xBD, 0x2D, 0xE9, 0xFC, 0x47, 0x10, 0x26, 0xCD, 0xE9, + 0x00, 0x62, 0xCF, 0x4F, 0xCB, 0x4D, 0x89, 0x46, 0x14, 0x46, 0x01, 0x46, + 0x4F, 0xF0, 0x00, 0x08, 0x2A, 0x78, 0xCC, 0x48, 0x3B, 0x68, 0x07, 0xF0, + 0x79, 0xFD, 0xCD, 0xE9, 0x00, 0x64, 0xC6, 0x4E, 0xC8, 0x48, 0x49, 0x46, + 0x32, 0x78, 0x3C, 0x30, 0x3B, 0x68, 0x07, 0xF0, 0x6F, 0xFD, 0xC0, 0x4C, + 0x20, 0x68, 0x90, 0xF8, 0x82, 0x10, 0x89, 0x07, 0x19, 0xD5, 0x2A, 0x78, + 0xB0, 0xF9, 0x8A, 0x10, 0xCD, 0xE9, 0x00, 0x12, 0xBF, 0x49, 0xB0, 0xF9, + 0x88, 0x30, 0xBF, 0x4A, 0x08, 0x46, 0xFF, 0xF7, 0xF3, 0xFE, 0x20, 0x68, + 0x32, 0x78, 0xB0, 0xF9, 0x8E, 0x10, 0xCD, 0xE9, 0x00, 0x12, 0xB9, 0x49, + 0xB0, 0xF9, 0x8C, 0x30, 0x3C, 0x31, 0xB9, 0x4A, 0x08, 0x46, 0xFF, 0xF7, + 0xE5, 0xFE, 0xB5, 0x49, 0x2B, 0x78, 0xB5, 0x4A, 0x01, 0xF1, 0x7C, 0x00, + 0x07, 0xF0, 0x33, 0xFB, 0xB1, 0x49, 0x33, 0x78, 0x3C, 0x31, 0xB2, 0x4A, + 0x01, 0xF1, 0x7C, 0x00, 0x07, 0xF0, 0x2B, 0xFB, 0x20, 0x68, 0x90, 0xF8, + 0x82, 0x10, 0x09, 0x07, 0x06, 0xD4, 0xAE, 0x49, 0x09, 0x78, 0xA1, 0xB1, + 0x90, 0xF8, 0xB0, 0x10, 0x49, 0x06, 0x10, 0xD5, 0xA7, 0x49, 0x90, 0xF8, + 0xAD, 0x30, 0x7C, 0x31, 0x2A, 0x78, 0x08, 0x46, 0x07, 0xF0, 0x47, 0xFD, + 0x20, 0x68, 0xA3, 0x49, 0x32, 0x78, 0x90, 0xF8, 0xAD, 0x30, 0xB8, 0x31, + 0x08, 0x46, 0x07, 0xF0, 0x3E, 0xFD, 0x20, 0x68, 0x90, 0xF8, 0x82, 0x00, + 0xC0, 0x07, 0x09, 0xD0, 0x9C, 0x49, 0xB8, 0x31, 0xA1, 0xF1, 0x3C, 0x00, + 0xFF, 0xF7, 0x23, 0xFF, 0x97, 0x49, 0x80, 0x46, 0x20, 0x20, 0x48, 0x71, + 0x40, 0x46, 0xBD, 0xE8, 0xFC, 0x87, 0xF0, 0xB5, 0x06, 0x46, 0x00, 0x20, + 0x01, 0x24, 0x18, 0x60, 0x09, 0xE0, 0x36, 0xF9, 0x11, 0x50, 0x95, 0x42, + 0x05, 0xDD, 0x1D, 0x68, 0x01, 0x20, 0x04, 0xFA, 0x01, 0xF7, 0x3D, 0x43, + 0x1D, 0x60, 0x49, 0x1E, 0xF3, 0xD2, 0xF0, 0xBD, 0x2D, 0xE9, 0xFF, 0x5F, + 0x87, 0x49, 0x91, 0x46, 0xDF, 0xF8, 0x38, 0xB2, 0x0A, 0x78, 0xDF, 0xF8, + 0x24, 0x82, 0x00, 0x24, 0x94, 0x46, 0x4F, 0xF0, 0x01, 0x0A, 0x5D, 0x46, + 0x08, 0xF1, 0xF8, 0x08, 0x92, 0x1E, 0x1A, 0xD0, 0x80, 0x49, 0xAC, 0xF1, + 0x02, 0x0C, 0x0E, 0x78, 0x02, 0xFB, 0x06, 0xF1, 0x00, 0xEB, 0x41, 0x07, + 0x31, 0x46, 0x0C, 0xE0, 0x37, 0xF9, 0x11, 0x30, 0xAB, 0x42, 0x00, 0xDD, + 0x1D, 0x46, 0x01, 0x2A, 0x01, 0xD0, 0x62, 0x45, 0x03, 0xD1, 0x28, 0xF8, + 0x14, 0x30, 0x64, 0x1C, 0xE4, 0xB2, 0x49, 0x1E, 0xF0, 0xD2, 0x52, 0x1E, + 0xE8, 0xD1, 0x76, 0x48, 0x21, 0x46, 0xF8, 0x30, 0x07, 0xF0, 0x72, 0xFA, + 0x08, 0xEB, 0x44, 0x01, 0x64, 0x1C, 0x0B, 0x88, 0xE2, 0xB2, 0x5B, 0x1C, + 0x0B, 0x80, 0x28, 0xF8, 0x12, 0x00, 0x6B, 0x49, 0x28, 0x1A, 0x05, 0xB2, + 0x09, 0x78, 0x01, 0x20, 0x5C, 0x46, 0x4B, 0x1E, 0x05, 0xE0, 0x39, 0xF9, + 0x10, 0x20, 0xA2, 0x42, 0x00, 0xDD, 0x14, 0x46, 0x40, 0x1C, 0x98, 0x42, + 0xF7, 0xD3, 0x48, 0x46, 0x49, 0x1E, 0x07, 0xF0, 0x55, 0xFA, 0x6A, 0x49, + 0x20, 0x1A, 0x00, 0xB2, 0xCD, 0x80, 0x08, 0x81, 0x01, 0x99, 0x8D, 0x42, + 0x04, 0xDD, 0x03, 0x99, 0x88, 0x42, 0x01, 0xDA, 0x4F, 0xF0, 0x00, 0x0A, + 0x04, 0xB0, 0x50, 0x46, 0xBD, 0xE8, 0xF0, 0x9F, 0x10, 0xB5, 0x00, 0x24, + 0x05, 0xE0, 0x30, 0xF9, 0x11, 0x30, 0x93, 0x42, 0x01, 0xDD, 0x01, 0x24, + 0x01, 0xE0, 0x49, 0x1E, 0xF7, 0xD2, 0x20, 0x46, 0x10, 0xBD, 0x2D, 0xE9, + 0xF0, 0x47, 0x58, 0x48, 0x00, 0x24, 0x52, 0x4E, 0x00, 0x78, 0xDF, 0xF8, + 0x38, 0x81, 0x9C, 0x46, 0x92, 0x46, 0x27, 0x46, 0x01, 0x28, 0x1B, 0xD0, + 0xD8, 0xF8, 0x00, 0x00, 0xB0, 0xF8, 0xF8, 0x50, 0xBC, 0xF8, 0x08, 0x00, + 0x28, 0x44, 0x02, 0xB2, 0x47, 0x48, 0x01, 0x78, 0x4A, 0x48, 0x7C, 0x30, + 0xFF, 0xF7, 0xD8, 0xFF, 0x81, 0x46, 0xBC, 0xF8, 0x0A, 0x00, 0x28, 0x44, + 0x02, 0xB2, 0x43, 0x48, 0x01, 0x78, 0x45, 0x48, 0xB8, 0x30, 0xFF, 0xF7, + 0xCD, 0xFF, 0x01, 0x28, 0x02, 0xD0, 0x05, 0xE0, 0xF4, 0x78, 0x03, 0xE0, + 0xB9, 0xF1, 0x01, 0x0F, 0x00, 0xD1, 0x01, 0x24, 0xD8, 0xF8, 0x00, 0x00, + 0x45, 0x46, 0xB0, 0xF9, 0xF2, 0x10, 0x42, 0x48, 0xB0, 0xF9, 0x00, 0x20, + 0x50, 0x46, 0x01, 0xF0, 0xC8, 0xF9, 0x00, 0x22, 0x0C, 0xB9, 0x01, 0x28, + 0x03, 0xD0, 0xF2, 0x60, 0x38, 0x46, 0xBD, 0xE8, 0xF0, 0x87, 0xF0, 0x68, + 0x40, 0x1C, 0xF0, 0x60, 0x29, 0x68, 0x91, 0xF8, 0xF4, 0x30, 0x83, 0x42, + 0x01, 0xDA, 0x01, 0x27, 0xF2, 0x60, 0x91, 0xF8, 0xA8, 0x13, 0x07, 0x20, + 0x06, 0xF0, 0x85, 0xF8, 0xEC, 0xE7, 0x30, 0xB5, 0x2A, 0x48, 0x01, 0x21, + 0x32, 0x4A, 0x81, 0x70, 0x00, 0x21, 0xC1, 0x70, 0x01, 0x71, 0x41, 0x70, + 0x82, 0x60, 0x31, 0x4B, 0x2F, 0x4A, 0x38, 0xCB, 0x82, 0xE8, 0x38, 0x00, + 0x41, 0x71, 0x01, 0x61, 0x41, 0x61, 0xC1, 0x71, 0x30, 0xBD, 0x03, 0x46, + 0x47, 0xF6, 0xFF, 0x70, 0x04, 0xE0, 0x33, 0xF9, 0x11, 0x20, 0x82, 0x42, + 0x00, 0xDA, 0x10, 0x46, 0x49, 0x1E, 0xF8, 0xD2, 0x00, 0x28, 0x00, 0xDA, + 0x00, 0x20, 0x70, 0x47, 0x03, 0x46, 0x1F, 0x48, 0x04, 0xE0, 0x33, 0xF9, + 0x11, 0x20, 0x82, 0x42, 0x00, 0xDD, 0x10, 0x46, 0x49, 0x1E, 0xF8, 0xD2, + 0x00, 0x28, 0x00, 0xDA, 0x00, 0x20, 0x70, 0x47, 0x01, 0x68, 0x41, 0xEA, + 0x51, 0x01, 0x41, 0xEA, 0x41, 0x01, 0x01, 0x60, 0x70, 0x47, 0x2D, 0xE9, + 0xFF, 0x4F, 0x83, 0xB0, 0x0D, 0x4C, 0x10, 0x9E, 0x4F, 0xF0, 0x00, 0x08, + 0x82, 0x46, 0x86, 0xF8, 0x00, 0x80, 0xA5, 0x78, 0x11, 0x48, 0x84, 0xF8, + 0x02, 0x80, 0x84, 0xF8, 0x05, 0x80, 0xB0, 0xF9, 0x00, 0x00, 0x0F, 0x46, + 0x42, 0x00, 0x4F, 0xF0, 0x01, 0x09, 0x19, 0x46, 0xA0, 0x68, 0x1D, 0xE0, + 0x50, 0x24, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, + 0x54, 0x23, 0x10, 0x00, 0xBC, 0x23, 0x10, 0x00, 0x04, 0x0A, 0x01, 0x20, + 0xFC, 0x22, 0x01, 0x20, 0x38, 0x23, 0x01, 0x20, 0x6D, 0x23, 0x10, 0x00, + 0x00, 0x80, 0xFF, 0xFF, 0x8A, 0x25, 0x10, 0x00, 0x0C, 0x24, 0x10, 0x00, + 0x84, 0x02, 0x01, 0x20, 0xA4, 0x48, 0x10, 0x00, 0x18, 0x1D, 0x01, 0x00, + 0x0A, 0xF0, 0xE0, 0xFA, 0x2A, 0x46, 0x39, 0x46, 0x50, 0x46, 0xFF, 0xF7, + 0x3F, 0xFE, 0x01, 0x28, 0x47, 0xD0, 0xA4, 0x49, 0xA2, 0x4A, 0xA1, 0xF1, + 0x3C, 0x00, 0x00, 0xF0, 0x71, 0xF8, 0xE0, 0x70, 0xA0, 0x48, 0xA1, 0x4D, + 0x3C, 0x38, 0x02, 0x90, 0x29, 0x68, 0xDF, 0xF8, 0x78, 0xB2, 0x9C, 0x4F, + 0x91, 0xF8, 0xF0, 0x10, 0x00, 0x20, 0xD4, 0xF8, 0x08, 0xA0, 0xC9, 0x07, + 0x0B, 0xD0, 0x3B, 0x46, 0x52, 0x46, 0x59, 0x46, 0x02, 0x98, 0xFF, 0xF7, + 0x1A, 0xFF, 0x01, 0x28, 0x01, 0xD0, 0x10, 0xB1, 0x10, 0xE0, 0x28, 0x21, + 0x0D, 0xE0, 0x29, 0x68, 0x91, 0xF8, 0xF0, 0x10, 0x89, 0x07, 0x09, 0xD5, + 0x3B, 0x46, 0x52, 0x46, 0x59, 0x46, 0x02, 0x98, 0x00, 0xF0, 0xDD, 0xF8, + 0x01, 0x28, 0x01, 0xD1, 0x29, 0x21, 0x61, 0x71, 0x07, 0x46, 0x01, 0x28, + 0x10, 0xD0, 0x28, 0x68, 0x8B, 0x4A, 0x8C, 0x4E, 0xB0, 0xF9, 0x96, 0x10, + 0x12, 0x78, 0xB0, 0xF9, 0xAE, 0x30, 0x01, 0x2A, 0x0B, 0xD0, 0xA2, 0x79, + 0x62, 0xB1, 0x32, 0x78, 0x72, 0xB1, 0xB0, 0xF9, 0x98, 0x10, 0x0B, 0xE0, + 0x01, 0x20, 0x30, 0x70, 0x00, 0x20, 0x07, 0xB0, 0x69, 0xE5, 0xB0, 0xF9, + 0x94, 0x10, 0x03, 0xE0, 0x31, 0x78, 0x21, 0xB3, 0xB0, 0xF9, 0x9C, 0x10, + 0x7F, 0x48, 0x00, 0x78, 0x02, 0x28, 0x04, 0xD9, 0x7E, 0x4A, 0x05, 0x98, + 0xFF, 0xF7, 0x70, 0xFE, 0x81, 0x46, 0x69, 0x46, 0xA0, 0x68, 0x00, 0xF0, + 0x4A, 0xF8, 0xE2, 0x78, 0x49, 0x46, 0x68, 0x46, 0x00, 0xF0, 0x72, 0xF8, + 0x78, 0x49, 0x60, 0x70, 0x81, 0xF8, 0x18, 0x80, 0x77, 0x49, 0x81, 0xF8, + 0x18, 0x80, 0x30, 0x78, 0x28, 0xB1, 0x28, 0x68, 0x90, 0xF8, 0xA6, 0x13, + 0x04, 0x20, 0x05, 0xF0, 0x9A, 0xFF, 0x38, 0x46, 0xD3, 0xE7, 0xB0, 0xF9, + 0x9A, 0x10, 0xD9, 0xE7, 0xF0, 0xB5, 0x84, 0x46, 0x00, 0x26, 0x6F, 0x48, + 0x16, 0x81, 0x15, 0x46, 0x56, 0x81, 0x02, 0x78, 0x65, 0x48, 0x0F, 0x46, + 0x31, 0x46, 0x00, 0x68, 0xDA, 0xB1, 0x6B, 0x4A, 0x52, 0x78, 0x01, 0x2A, + 0x17, 0xD0, 0xB0, 0xF9, 0x92, 0x40, 0x2B, 0x46, 0x08, 0x19, 0x02, 0xB2, + 0x61, 0x48, 0x01, 0x78, 0x60, 0x46, 0xFF, 0xF7, 0x24, 0xFE, 0x84, 0x46, + 0x68, 0x89, 0x2B, 0x1D, 0x20, 0x44, 0x02, 0xB2, 0x62, 0x48, 0x01, 0x78, + 0x38, 0x46, 0xFF, 0xF7, 0x1A, 0xFE, 0xBC, 0xF1, 0x01, 0x0F, 0x03, 0xD0, + 0x05, 0xE0, 0xB0, 0xF9, 0x90, 0x40, 0xE6, 0xE7, 0x01, 0x28, 0x00, 0xD1, + 0x01, 0x26, 0x30, 0x46, 0xF0, 0xBD, 0x30, 0xB5, 0x50, 0x4A, 0x12, 0x68, + 0xB2, 0xF9, 0xB7, 0x30, 0x00, 0x22, 0xCA, 0x80, 0x8A, 0x80, 0x0A, 0x60, + 0x56, 0x4A, 0x5C, 0x42, 0xB2, 0xF9, 0x00, 0x20, 0x12, 0xE0, 0x30, 0xF9, + 0x12, 0x30, 0x00, 0x2B, 0x0E, 0xDA, 0x8B, 0x88, 0x5B, 0x1C, 0x8B, 0x80, + 0x30, 0xF9, 0x12, 0x50, 0x0B, 0x68, 0x2B, 0x44, 0x0B, 0x60, 0x30, 0xF9, + 0x12, 0x30, 0xA3, 0x42, 0x02, 0xDA, 0xCB, 0x88, 0x5B, 0x1C, 0xCB, 0x80, + 0x52, 0x1E, 0xEA, 0xD2, 0x08, 0x68, 0x00, 0x28, 0x00, 0xDA, 0x40, 0x42, + 0x48, 0x4A, 0x08, 0x60, 0x90, 0x81, 0x88, 0x79, 0xD0, 0x72, 0x30, 0xBD, + 0x70, 0xB5, 0x45, 0x48, 0x00, 0x24, 0x82, 0x7A, 0x61, 0xF3, 0x82, 0x02, + 0x82, 0x72, 0x43, 0x48, 0x00, 0x78, 0xC0, 0x06, 0x24, 0xD5, 0x37, 0x4D, + 0x49, 0xB1, 0x00, 0xF0, 0x1F, 0xF9, 0x2C, 0x78, 0x3A, 0x4E, 0x28, 0x78, + 0x31, 0x78, 0x88, 0x42, 0x18, 0xD0, 0x21, 0xB1, 0x0B, 0xE0, 0x00, 0xF0, + 0xF6, 0xF8, 0x01, 0x24, 0xF4, 0xE7, 0x01, 0x28, 0x10, 0xD1, 0x00, 0x23, + 0x1A, 0x46, 0x19, 0x46, 0xE1, 0x20, 0x06, 0xF0, 0xA0, 0xFF, 0x30, 0x78, + 0x01, 0x28, 0x07, 0xD1, 0x28, 0x78, 0x28, 0xB9, 0x00, 0x23, 0x1A, 0x46, + 0x19, 0x46, 0xE2, 0x20, 0x06, 0xF0, 0x95, 0xFF, 0x28, 0x78, 0x30, 0x70, + 0x00, 0x20, 0x01, 0x2C, 0x00, 0xD1, 0x01, 0x20, 0x70, 0xBD, 0x2D, 0xE9, + 0xF0, 0x41, 0x1F, 0x4E, 0x90, 0x46, 0x00, 0x24, 0x30, 0x68, 0x25, 0x46, + 0x90, 0xF8, 0xF6, 0xC0, 0x1E, 0x48, 0x62, 0x46, 0x01, 0x78, 0x19, 0x48, + 0x3C, 0x38, 0xFF, 0xF7, 0x0F, 0xFE, 0x07, 0x46, 0x20, 0x48, 0x62, 0x46, + 0x01, 0x78, 0x15, 0x48, 0xFF, 0xF7, 0x08, 0xFE, 0x01, 0x28, 0x01, 0xD0, + 0x01, 0x2F, 0x00, 0xD1, 0x01, 0x24, 0x30, 0x68, 0x90, 0xF8, 0xF7, 0x10, + 0x1A, 0x48, 0xB0, 0xF9, 0x00, 0x20, 0x40, 0x46, 0x01, 0xF0, 0x09, 0xF8, + 0x15, 0x4B, 0x00, 0x27, 0x04, 0xB1, 0x18, 0xB1, 0x1F, 0x61, 0x28, 0x46, + 0xBD, 0xE8, 0xF0, 0x81, 0x19, 0x69, 0x49, 0x1C, 0x19, 0x61, 0x32, 0x68, + 0x92, 0xF8, 0xF5, 0x00, 0x88, 0x42, 0x01, 0xDA, 0x01, 0x25, 0x1F, 0x61, + 0x92, 0xF8, 0xA8, 0x13, 0x07, 0x20, 0x05, 0xF0, 0xC6, 0xFE, 0xEC, 0xE7, + 0xA4, 0x48, 0x10, 0x00, 0xBC, 0x0A, 0x01, 0x20, 0x50, 0x24, 0x10, 0x00, + 0x71, 0x23, 0x10, 0x00, 0x6D, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, + 0xFC, 0x22, 0x01, 0x20, 0xDD, 0x46, 0x10, 0x00, 0xF6, 0x46, 0x10, 0x00, + 0xB8, 0x23, 0x10, 0x00, 0x54, 0x23, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, + 0x0C, 0x24, 0x10, 0x00, 0x8A, 0x25, 0x10, 0x00, 0x1C, 0x24, 0x10, 0x00, + 0xF0, 0xB5, 0x00, 0x24, 0x1C, 0x70, 0x04, 0x7E, 0x0D, 0x7E, 0x4F, 0xF0, + 0xFF, 0x36, 0x6C, 0x43, 0xE7, 0xB2, 0x4F, 0xF0, 0x00, 0x0E, 0x35, 0x46, + 0x74, 0x46, 0x07, 0xE0, 0x32, 0xF8, 0x14, 0xC0, 0xF4, 0x45, 0x01, 0xD9, + 0xE6, 0x46, 0x65, 0xB2, 0x64, 0x1C, 0xE4, 0xB2, 0xBC, 0x42, 0xF5, 0xD3, + 0x00, 0x2D, 0x1B, 0xDB, 0x01, 0x24, 0x1C, 0x70, 0x0C, 0x7E, 0x95, 0xFB, + 0xF4, 0xF4, 0x64, 0x1C, 0xE4, 0xB2, 0x04, 0x44, 0x94, 0xF8, 0x12, 0xC0, + 0x83, 0xF8, 0x01, 0xC0, 0x24, 0x7B, 0x5C, 0x71, 0x0C, 0x7E, 0x95, 0xFB, + 0xF4, 0xFC, 0x04, 0xFB, 0x1C, 0x54, 0x64, 0x1C, 0xE4, 0xB2, 0x0C, 0x44, + 0x94, 0xF8, 0x12, 0xC0, 0x83, 0xF8, 0x03, 0xC0, 0x24, 0x7B, 0xDC, 0x71, + 0x01, 0x2F, 0x2F, 0xD9, 0xFE, 0x4C, 0x24, 0x68, 0x94, 0xF8, 0xB0, 0x40, + 0xA4, 0x07, 0x29, 0xD5, 0x4F, 0xF0, 0x00, 0x0E, 0x74, 0x46, 0x09, 0xE0, + 0x32, 0xF8, 0x14, 0xC0, 0xF4, 0x45, 0x03, 0xD9, 0xA5, 0x42, 0x01, 0xD0, + 0xE6, 0x46, 0x66, 0xB2, 0x64, 0x1C, 0xE4, 0xB2, 0xBC, 0x42, 0xF3, 0xD3, + 0x00, 0x2E, 0x17, 0xDB, 0x02, 0x22, 0x1A, 0x70, 0x0A, 0x7E, 0x96, 0xFB, + 0xF2, 0xF2, 0x52, 0x1C, 0xD2, 0xB2, 0x10, 0x44, 0x82, 0x7C, 0x9A, 0x70, + 0x00, 0x7B, 0x98, 0x71, 0x08, 0x7E, 0x96, 0xFB, 0xF0, 0xF2, 0x00, 0xFB, + 0x12, 0x60, 0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x44, 0x81, 0x7C, 0x19, 0x71, + 0x00, 0x7B, 0x18, 0x72, 0xF0, 0xBD, 0x70, 0xB5, 0xFF, 0xF7, 0xB7, 0xFD, + 0xE5, 0x4C, 0x00, 0x25, 0x78, 0x21, 0x25, 0x70, 0xA5, 0x70, 0xE5, 0x70, + 0x65, 0x70, 0x25, 0x71, 0xE2, 0x48, 0x0A, 0xF0, 0x9E, 0xF9, 0x65, 0x71, + 0xA5, 0x71, 0xFF, 0xF7, 0x60, 0xF8, 0xBD, 0xE8, 0x70, 0x40, 0xFE, 0xF7, + 0xE2, 0xBD, 0x70, 0xB5, 0xDB, 0x4C, 0xDA, 0x4E, 0x00, 0x25, 0xE5, 0x70, + 0x30, 0x68, 0x90, 0xF8, 0xA6, 0x13, 0x04, 0x20, 0x05, 0xF0, 0x1D, 0xFE, + 0x20, 0x78, 0x68, 0xB9, 0xA0, 0x78, 0x40, 0x1C, 0xC0, 0xB2, 0xA0, 0x70, + 0x31, 0x68, 0x91, 0xF8, 0xC8, 0x10, 0x01, 0xF0, 0x0F, 0x01, 0x81, 0x42, + 0x04, 0xD2, 0x01, 0x20, 0x20, 0x70, 0xA5, 0x70, 0x01, 0x20, 0x70, 0xBD, + 0x00, 0x20, 0x70, 0xBD, 0xCC, 0x49, 0x00, 0x22, 0x8A, 0x70, 0x08, 0x78, + 0x01, 0x28, 0x0C, 0xD1, 0xC8, 0x78, 0xC8, 0x4B, 0x40, 0x1C, 0xC0, 0xB2, + 0xC8, 0x70, 0x1B, 0x68, 0x93, 0xF8, 0xC8, 0x30, 0xB0, 0xEB, 0x13, 0x1F, + 0x03, 0xD9, 0x0A, 0x70, 0xCA, 0x70, 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, + 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x4F, 0x0F, 0x46, 0xA3, 0xB0, 0x00, 0x21, + 0xBD, 0x4A, 0x88, 0x46, 0x21, 0x91, 0x11, 0x68, 0x4F, 0xF0, 0x01, 0x09, + 0x91, 0xF8, 0xB0, 0x30, 0x5B, 0x07, 0x55, 0xD5, 0xBB, 0x4B, 0x1B, 0x78, + 0xA3, 0xBB, 0xB1, 0xF8, 0xB5, 0x20, 0xFB, 0x6A, 0x93, 0x42, 0x4D, 0xDA, + 0xD7, 0xF8, 0x0A, 0x10, 0x20, 0x91, 0x01, 0x46, 0x80, 0x22, 0x68, 0x46, + 0x0A, 0xF0, 0xE5, 0xF8, 0x3A, 0x46, 0x20, 0xA9, 0x68, 0x46, 0x02, 0xF0, + 0xBE, 0xFE, 0x00, 0x26, 0x35, 0x46, 0x9D, 0xF8, 0x82, 0x40, 0x23, 0xE0, + 0xE1, 0xB2, 0x38, 0x69, 0x0B, 0xF0, 0x96, 0xFF, 0x9D, 0xF8, 0x80, 0x10, + 0xEA, 0x46, 0xDF, 0xF8, 0xA8, 0xB2, 0x9D, 0xF8, 0x81, 0xC0, 0x14, 0xE0, + 0x5A, 0xF8, 0x24, 0x20, 0x09, 0xFA, 0x01, 0xF3, 0x1A, 0x42, 0x0D, 0xD0, + 0x30, 0xF9, 0x11, 0x20, 0x00, 0x2A, 0x08, 0xDB, 0x16, 0x44, 0x5B, 0xF8, + 0x24, 0x20, 0x1A, 0x42, 0x04, 0xD0, 0x4F, 0xF0, 0x01, 0x08, 0x01, 0xE0, + 0x1C, 0xE0, 0x15, 0x44, 0x49, 0x1C, 0x8C, 0x45, 0xE8, 0xDA, 0x64, 0x1C, + 0x9D, 0xF8, 0x83, 0x00, 0xA0, 0x42, 0xD7, 0xDA, 0x0D, 0xB9, 0x4F, 0xF0, + 0xFF, 0x35, 0x64, 0x20, 0x46, 0x43, 0x95, 0x49, 0x68, 0x42, 0x96, 0xFB, + 0xF0, 0xF0, 0x09, 0x68, 0xB1, 0xF8, 0xB3, 0x10, 0x81, 0x42, 0x11, 0xDD, + 0x91, 0x49, 0x01, 0x20, 0x21, 0x90, 0x81, 0xF8, 0x05, 0x80, 0x0B, 0xE0, + 0xB1, 0xF8, 0xB5, 0x10, 0xF8, 0x6A, 0x88, 0x42, 0x03, 0xDB, 0x8C, 0x48, + 0x00, 0x21, 0x41, 0x71, 0x02, 0xE0, 0x8A, 0x49, 0x81, 0xF8, 0x05, 0x90, + 0x21, 0x98, 0x23, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xF0, 0x43, + 0x4F, 0xF0, 0x00, 0x0C, 0x8A, 0x78, 0x87, 0x4E, 0x4F, 0xF0, 0x01, 0x09, + 0x1E, 0xE0, 0x0B, 0x78, 0x09, 0xFA, 0x02, 0xF7, 0x16, 0xE0, 0x50, 0xF8, + 0x22, 0x40, 0x09, 0xFA, 0x03, 0xF5, 0x2C, 0x42, 0x0F, 0xD0, 0xD6, 0xF8, + 0x00, 0x80, 0x18, 0xEA, 0x07, 0x0F, 0x07, 0xD0, 0xD6, 0xF8, 0x04, 0x80, + 0x18, 0xEA, 0x05, 0x0F, 0x02, 0xD0, 0x4F, 0xF0, 0x01, 0x0C, 0x02, 0xE0, + 0xAC, 0x43, 0x40, 0xF8, 0x22, 0x40, 0x5B, 0x1C, 0x4C, 0x78, 0x9C, 0x42, + 0xE5, 0xDA, 0x52, 0x1C, 0xCB, 0x78, 0x93, 0x42, 0xDD, 0xDA, 0x60, 0x46, + 0xBD, 0xE8, 0xF0, 0x83, 0xF0, 0xB5, 0x82, 0x78, 0x01, 0x25, 0x6F, 0x4F, + 0xDF, 0xF8, 0xB4, 0xC1, 0x90, 0xF8, 0x03, 0xE0, 0x0E, 0xE0, 0x01, 0x78, + 0x46, 0x78, 0x08, 0xE0, 0x57, 0xF8, 0x22, 0x40, 0x05, 0xFA, 0x01, 0xF3, + 0x1C, 0x42, 0x01, 0xD0, 0x8C, 0xF8, 0x05, 0x50, 0x49, 0x1C, 0x8E, 0x42, + 0xF4, 0xDA, 0x52, 0x1C, 0x96, 0x45, 0xEE, 0xDA, 0xF0, 0xBD, 0x2D, 0xE9, + 0xFC, 0x47, 0x82, 0x46, 0x10, 0x68, 0x00, 0x90, 0x50, 0x68, 0x01, 0x90, + 0x89, 0x46, 0x68, 0x46, 0xFF, 0xF7, 0xD6, 0xFC, 0x68, 0x46, 0xFF, 0xF7, + 0xD3, 0xFC, 0x01, 0xA8, 0xFF, 0xF7, 0xD0, 0xFC, 0x01, 0xA8, 0xFF, 0xF7, + 0xCD, 0xFC, 0x00, 0x24, 0x01, 0x26, 0xDF, 0xF8, 0x6C, 0x81, 0x21, 0xE0, + 0xE1, 0xB2, 0x50, 0x46, 0x0B, 0xF0, 0xE8, 0xFE, 0x05, 0x46, 0xE1, 0xB2, + 0x48, 0x46, 0x0B, 0xF0, 0xE3, 0xFE, 0x03, 0x46, 0x00, 0x21, 0x06, 0xFA, + 0x04, 0xF0, 0x54, 0x4F, 0x0E, 0xE0, 0x00, 0x9A, 0x10, 0x42, 0x06, 0xD0, + 0xDD, 0xF8, 0x04, 0xC0, 0x06, 0xFA, 0x01, 0xF2, 0x12, 0xEA, 0x0C, 0x0F, + 0x03, 0xD1, 0x35, 0xF8, 0x11, 0x20, 0x23, 0xF8, 0x11, 0x20, 0x49, 0x1C, + 0x3A, 0x78, 0x91, 0x42, 0xED, 0xDB, 0x64, 0x1C, 0x98, 0xF8, 0x00, 0x00, + 0x84, 0x42, 0xD9, 0xDB, 0xBD, 0xE8, 0xFC, 0x87, 0x70, 0xB5, 0x47, 0x49, + 0x00, 0x20, 0x45, 0x4A, 0x09, 0x68, 0x46, 0x4C, 0x42, 0x4D, 0x07, 0xE0, + 0x13, 0x78, 0x34, 0xF8, 0x10, 0x60, 0x43, 0x43, 0x40, 0x1C, 0x21, 0xF8, + 0x13, 0x60, 0xC0, 0xB2, 0x2B, 0x78, 0x98, 0x42, 0xF4, 0xD3, 0x00, 0x20, + 0x3F, 0x4B, 0x05, 0xE0, 0x33, 0xF8, 0x10, 0x40, 0x21, 0xF8, 0x10, 0x40, + 0x40, 0x1C, 0xC0, 0xB2, 0x14, 0x78, 0xA0, 0x42, 0xF6, 0xD3, 0x70, 0xBD, + 0x3A, 0x48, 0x03, 0x21, 0x01, 0x80, 0x41, 0x88, 0x49, 0x1C, 0x41, 0x80, + 0x38, 0x49, 0x0A, 0x78, 0x01, 0x79, 0x62, 0xF3, 0x03, 0x01, 0x2C, 0x4A, + 0x13, 0x78, 0x63, 0xF3, 0x45, 0x11, 0x13, 0x79, 0x82, 0x7A, 0x63, 0xF3, + 0x00, 0x02, 0x82, 0x72, 0x32, 0x4A, 0x12, 0x78, 0x62, 0xF3, 0x04, 0x11, + 0x01, 0x71, 0x31, 0x49, 0x09, 0x7E, 0x81, 0x73, 0x30, 0x49, 0x09, 0x7E, + 0xC1, 0x73, 0x30, 0x49, 0x0A, 0x78, 0x82, 0x74, 0x4A, 0x78, 0x02, 0x75, + 0x8A, 0x78, 0x42, 0x75, 0xCA, 0x78, 0x82, 0x75, 0x09, 0x79, 0xC1, 0x75, + 0x2B, 0x49, 0x09, 0x78, 0x41, 0x71, 0x70, 0x47, 0x2D, 0xE9, 0xF1, 0x4F, + 0x18, 0x48, 0x00, 0x27, 0x01, 0x26, 0x00, 0x68, 0x8E, 0xB0, 0x3D, 0x46, + 0x30, 0xF9, 0xC3, 0xAF, 0x3B, 0x46, 0x90, 0xF8, 0x02, 0xB0, 0x10, 0xF8, + 0x13, 0x0C, 0x80, 0x07, 0x00, 0xD5, 0x01, 0x23, 0x1D, 0x4C, 0xDF, 0xF8, + 0x78, 0x80, 0x21, 0x7E, 0x98, 0xF8, 0x18, 0x00, 0xC1, 0x42, 0x04, 0xD1, + 0x10, 0x4A, 0x15, 0x49, 0x13, 0x48, 0xFE, 0xF7, 0xF6, 0xFE, 0x20, 0x7E, + 0x98, 0xF8, 0x18, 0x10, 0x00, 0xFB, 0x01, 0xF2, 0xD4, 0xB2, 0x06, 0xF0, + 0x3B, 0xFE, 0x00, 0xF0, 0xFF, 0x08, 0x61, 0x00, 0x68, 0x46, 0x09, 0xF0, + 0xE6, 0xFF, 0x12, 0x49, 0x00, 0x20, 0x4F, 0xF0, 0x01, 0x09, 0x08, 0x70, + 0xDC, 0xB3, 0x21, 0xE0, 0x50, 0x24, 0x10, 0x00, 0x6D, 0x23, 0x10, 0x00, + 0xB0, 0x48, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, 0xA4, 0x48, 0x10, 0x00, + 0x00, 0x24, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, 0x10, 0x24, 0x10, 0x00, + 0x80, 0x0A, 0x01, 0x20, 0xBC, 0x0A, 0x01, 0x20, 0x8A, 0x25, 0x10, 0x00, + 0x55, 0x23, 0x10, 0x00, 0x57, 0x23, 0x10, 0x00, 0xDD, 0x46, 0x10, 0x00, + 0xF6, 0x46, 0x10, 0x00, 0xD4, 0x46, 0x10, 0x00, 0x08, 0x24, 0x10, 0x00, + 0x19, 0x2C, 0x20, 0xD2, 0x1B, 0x48, 0x19, 0x4B, 0x19, 0x4A, 0x51, 0x46, + 0x00, 0x68, 0xFE, 0xF7, 0x3D, 0xFF, 0x19, 0x49, 0x6B, 0x46, 0x5A, 0x46, + 0x08, 0x60, 0x18, 0x49, 0x18, 0x48, 0xFE, 0xF7, 0x7C, 0xFF, 0x05, 0x46, + 0xA8, 0x45, 0x05, 0xD8, 0xA5, 0x42, 0x03, 0xD8, 0x01, 0x2C, 0x01, 0xD9, + 0x02, 0x27, 0x0F, 0xE0, 0x00, 0x26, 0x02, 0x27, 0x12, 0x4B, 0x6A, 0x46, + 0x0F, 0x49, 0x10, 0x48, 0xFF, 0xF7, 0x86, 0xFD, 0x06, 0xE0, 0x0E, 0x99, + 0x2A, 0x20, 0x00, 0x26, 0x81, 0xF8, 0x00, 0x90, 0x0D, 0x49, 0x08, 0x70, + 0x0D, 0x48, 0x05, 0x74, 0x41, 0x7C, 0x66, 0xF3, 0x00, 0x01, 0x41, 0x74, + 0x0B, 0x49, 0x38, 0x46, 0x81, 0xF8, 0x00, 0x90, 0x0F, 0xB0, 0x99, 0xE6, + 0xA4, 0x48, 0x10, 0x00, 0xD8, 0x23, 0x10, 0x00, 0x5C, 0x23, 0x10, 0x00, + 0xC0, 0x23, 0x10, 0x00, 0xF6, 0x46, 0x10, 0x00, 0xDD, 0x46, 0x10, 0x00, + 0xD4, 0x46, 0x10, 0x00, 0x59, 0x23, 0x10, 0x00, 0x8A, 0x25, 0x10, 0x00, + 0x40, 0x23, 0x10, 0x00, 0x2D, 0xE9, 0xFF, 0x5F, 0x1C, 0x46, 0xDD, 0xE9, + 0x0E, 0x36, 0xCD, 0xE9, 0x02, 0x03, 0x0D, 0x46, 0x91, 0x46, 0xB2, 0xE0, + 0x47, 0xF6, 0xFF, 0x72, 0xD3, 0x43, 0x00, 0x27, 0x31, 0x46, 0x0B, 0xE0, + 0x39, 0xF9, 0x11, 0x00, 0x35, 0xF9, 0x11, 0xC0, 0xA0, 0xEB, 0x0C, 0x00, + 0x90, 0x42, 0x00, 0xDA, 0x02, 0x46, 0x98, 0x42, 0x00, 0xDD, 0x03, 0x46, + 0x49, 0x1E, 0xF1, 0xD2, 0x00, 0x2B, 0x01, 0xDA, 0x1F, 0x46, 0x02, 0xE0, + 0x00, 0x2A, 0x00, 0xDD, 0x17, 0x46, 0x4F, 0xF0, 0x00, 0x08, 0x30, 0x46, + 0x0E, 0xE0, 0x39, 0xF9, 0x10, 0x10, 0x35, 0xF9, 0x10, 0x20, 0x89, 0x1A, + 0xC9, 0x1B, 0xA1, 0x42, 0x06, 0xDA, 0x21, 0x44, 0x00, 0x29, 0x03, 0xDD, + 0x01, 0x21, 0x81, 0x40, 0x41, 0xEA, 0x08, 0x08, 0x40, 0x1E, 0xEE, 0xD2, + 0xDF, 0xF8, 0xB8, 0xA7, 0xDA, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x31, 0x11, + 0x09, 0x06, 0x0F, 0xD5, 0x00, 0x96, 0xB0, 0xF8, 0x61, 0x21, 0x49, 0x46, + 0x13, 0x46, 0x28, 0x46, 0x07, 0xF0, 0x1F, 0xF8, 0xDA, 0xF8, 0x00, 0x10, + 0x91, 0xF8, 0x63, 0x11, 0x21, 0xB1, 0x07, 0xF0, 0x41, 0xF8, 0x01, 0xE0, + 0x4F, 0xF0, 0xFF, 0x30, 0x08, 0xEA, 0x00, 0x01, 0x01, 0x91, 0x04, 0x21, + 0x01, 0xA8, 0x0B, 0xF0, 0xD1, 0xFF, 0xDA, 0xF8, 0x00, 0x10, 0x91, 0xF8, + 0x7C, 0x11, 0x88, 0x42, 0x01, 0xDA, 0xCD, 0xF8, 0x04, 0x80, 0x00, 0x21, + 0x0A, 0x46, 0x0B, 0x46, 0x8B, 0x46, 0x8A, 0x46, 0x30, 0x46, 0xDD, 0xF8, + 0x04, 0xE0, 0x16, 0xE0, 0x4F, 0xF0, 0x01, 0x0C, 0x0C, 0xFA, 0x00, 0xFC, + 0x1C, 0xEA, 0x0E, 0x0F, 0x0F, 0xD0, 0x39, 0xF9, 0x10, 0xC0, 0x35, 0xF9, + 0x10, 0x80, 0x00, 0xFB, 0x00, 0x22, 0xAC, 0xEB, 0x08, 0x0C, 0xAC, 0xEB, + 0x07, 0x0C, 0x00, 0xFB, 0x0C, 0xBB, 0x01, 0x44, 0x63, 0x44, 0x0A, 0xF1, + 0x01, 0x0A, 0x40, 0x1E, 0xE6, 0xD2, 0x02, 0xFB, 0x0A, 0xF0, 0x01, 0xFB, + 0x11, 0x0C, 0x0B, 0xFB, 0x0A, 0xF0, 0x01, 0xFB, 0x13, 0x08, 0x5A, 0x43, + 0x0B, 0xFB, 0x11, 0x20, 0xBC, 0xF1, 0x00, 0x0F, 0x05, 0xD0, 0x90, 0xFB, + 0xFC, 0xF2, 0x31, 0x46, 0xC4, 0xF1, 0x00, 0x0A, 0x19, 0xE0, 0x00, 0x22, + 0xF9, 0xE7, 0xBC, 0xF1, 0x00, 0x0F, 0x08, 0xD0, 0x08, 0xFB, 0x01, 0xF0, + 0x90, 0xFB, 0xFC, 0xF0, 0x10, 0x44, 0xA0, 0x42, 0x03, 0xDD, 0x20, 0x46, + 0x04, 0xE0, 0x00, 0x20, 0xF9, 0xE7, 0xE0, 0x42, 0x00, 0xD5, 0x50, 0x46, + 0x35, 0xF8, 0x11, 0x30, 0x38, 0x44, 0x18, 0x44, 0x02, 0x9B, 0x23, 0xF8, + 0x11, 0x00, 0x49, 0x1E, 0xE5, 0xD2, 0x02, 0x98, 0x05, 0xEB, 0x46, 0x05, + 0x00, 0xEB, 0x46, 0x00, 0x09, 0xEB, 0x46, 0x09, 0x02, 0x90, 0x03, 0x98, + 0x40, 0x1E, 0x03, 0x90, 0x40, 0x1C, 0x7F, 0xF4, 0x47, 0xAF, 0xBD, 0xE8, + 0xFF, 0x9F, 0x2D, 0xE9, 0xFF, 0x4F, 0x9A, 0x46, 0xDD, 0xE9, 0x0D, 0x97, + 0x86, 0x46, 0x58, 0xE0, 0x01, 0x99, 0x09, 0xFB, 0x07, 0xF0, 0x01, 0xEB, + 0x40, 0x06, 0x02, 0x99, 0x47, 0xF6, 0xFF, 0x75, 0x01, 0xEB, 0x40, 0x03, + 0x01, 0x24, 0x0E, 0xEB, 0x40, 0x0B, 0x6F, 0xEA, 0x05, 0x0C, 0x22, 0x46, + 0x39, 0x46, 0x14, 0xE0, 0x33, 0xF8, 0x11, 0x00, 0x36, 0xF8, 0x11, 0x80, + 0xA0, 0xEB, 0x08, 0x00, 0x00, 0xB2, 0xA8, 0x42, 0x00, 0xDA, 0x05, 0x46, + 0x60, 0x45, 0x00, 0xDD, 0x84, 0x46, 0x00, 0x28, 0x02, 0xDD, 0x4F, 0xF0, + 0x00, 0x02, 0x02, 0xE0, 0x01, 0xDA, 0x4F, 0xF0, 0x00, 0x04, 0x49, 0x1E, + 0xE8, 0xD2, 0x14, 0xB1, 0x0A, 0xB1, 0x00, 0x20, 0x07, 0xE0, 0x54, 0xEA, + 0x02, 0x00, 0x04, 0xD0, 0x14, 0xB9, 0x0A, 0xB1, 0x60, 0x46, 0x00, 0xE0, + 0x28, 0x46, 0x00, 0x21, 0x0C, 0x46, 0x3A, 0x46, 0xCA, 0xF1, 0x00, 0x08, + 0x0D, 0xE0, 0x33, 0xF8, 0x12, 0x50, 0x36, 0xF8, 0x12, 0xC0, 0xA5, 0xEB, + 0x0C, 0x05, 0x2D, 0x1A, 0x2D, 0xB2, 0x55, 0x45, 0x03, 0xDA, 0x45, 0x45, + 0x01, 0xDD, 0x29, 0x44, 0x64, 0x1C, 0x52, 0x1E, 0xEF, 0xD2, 0x04, 0xB9, + 0x01, 0x24, 0x91, 0xFB, 0xF4, 0xF1, 0x0A, 0x18, 0x3B, 0x46, 0x04, 0xE0, + 0x36, 0xF8, 0x13, 0x00, 0x10, 0x44, 0x2B, 0xF8, 0x13, 0x00, 0x5B, 0x1E, + 0xF8, 0xD2, 0xB9, 0xF1, 0x01, 0x09, 0xA3, 0xD2, 0xBD, 0xE8, 0xFF, 0x8F, + 0xF0, 0xB5, 0x1E, 0x46, 0x1D, 0xE0, 0x00, 0x24, 0x94, 0x46, 0x09, 0xE0, + 0x0C, 0xFB, 0x03, 0x65, 0x31, 0xF9, 0x15, 0x50, 0x00, 0x2D, 0x14, 0xDD, + 0x0C, 0xB1, 0xA5, 0x42, 0x00, 0xDA, 0x2C, 0x46, 0xBC, 0xF1, 0x01, 0x0C, + 0xF2, 0xD2, 0x00, 0x2C, 0x0B, 0xDD, 0x17, 0x46, 0x07, 0xE0, 0x07, 0xFB, + 0x03, 0x65, 0x31, 0xF8, 0x15, 0xC0, 0xAC, 0xEB, 0x04, 0x0C, 0x20, 0xF8, + 0x15, 0xC0, 0x7F, 0x1E, 0xF5, 0xD2, 0x76, 0x1E, 0xDF, 0xD2, 0xF0, 0xBD, + 0x2D, 0xE9, 0xF0, 0x47, 0x8A, 0x46, 0x81, 0x46, 0x1C, 0x46, 0xDD, 0xF8, + 0x20, 0xC0, 0x24, 0xE0, 0x64, 0x2A, 0x05, 0xDD, 0xC2, 0xF1, 0x64, 0x00, + 0x60, 0x43, 0x90, 0xFB, 0xF3, 0xF1, 0x06, 0xE0, 0xA2, 0xF1, 0x64, 0x00, + 0x00, 0xFB, 0x04, 0xF1, 0x91, 0xFB, 0xF3, 0xF1, 0x41, 0x1A, 0x04, 0xFB, + 0x0C, 0xF0, 0x0A, 0xEB, 0x40, 0x07, 0x09, 0xEB, 0x40, 0x05, 0x64, 0x31, + 0x60, 0x46, 0x4F, 0xF0, 0x64, 0x08, 0x08, 0xE0, 0x37, 0xF9, 0x10, 0x60, + 0x00, 0x2E, 0x04, 0xDD, 0x4E, 0x43, 0x96, 0xFB, 0xF8, 0xF6, 0x25, 0xF8, + 0x10, 0x60, 0x40, 0x1E, 0xF4, 0xD2, 0x64, 0x1E, 0xD8, 0xD2, 0xBD, 0xE8, + 0xF0, 0x87, 0x2D, 0xE9, 0xF0, 0x43, 0x99, 0x46, 0x07, 0x9E, 0x33, 0x46, + 0x22, 0xE0, 0x64, 0x2A, 0x05, 0xDD, 0xC2, 0xF1, 0x64, 0x04, 0x5C, 0x43, + 0x94, 0xFB, 0xF6, 0xF7, 0x06, 0xE0, 0xA2, 0xF1, 0x64, 0x04, 0x04, 0xFB, + 0x03, 0xF5, 0x95, 0xFB, 0xF6, 0xF5, 0x67, 0x1B, 0x64, 0x37, 0x4C, 0x46, + 0x4F, 0xF0, 0x64, 0x08, 0x0C, 0xE0, 0x04, 0xFB, 0x06, 0x35, 0x31, 0xF9, + 0x15, 0xC0, 0xBC, 0xF1, 0x00, 0x0F, 0x05, 0xDD, 0x0C, 0xFB, 0x07, 0xFC, + 0x9C, 0xFB, 0xF8, 0xFC, 0x20, 0xF8, 0x15, 0xC0, 0x64, 0x1E, 0xF0, 0xD2, + 0x5B, 0x1E, 0xDA, 0xD2, 0xBD, 0xE8, 0xF0, 0x83, 0x2D, 0xE9, 0xF0, 0x47, + 0xA3, 0xF1, 0x01, 0x08, 0x08, 0x9D, 0x8A, 0x46, 0x81, 0x46, 0x56, 0x19, + 0x08, 0xFB, 0x05, 0xF8, 0x2F, 0x46, 0x29, 0xE0, 0x08, 0xEB, 0x07, 0x00, + 0x0A, 0xEB, 0x40, 0x01, 0x09, 0xEB, 0x40, 0x00, 0x1C, 0x46, 0x1F, 0xE0, + 0x32, 0x78, 0x64, 0x2A, 0x05, 0xD9, 0xC2, 0xF1, 0x64, 0x02, 0x62, 0x43, + 0x92, 0xFB, 0xF3, 0xF2, 0x06, 0xE0, 0x64, 0x3A, 0x02, 0xFB, 0x04, 0xFC, + 0x9C, 0xFB, 0xF3, 0xFC, 0xA2, 0xEB, 0x0C, 0x02, 0xB1, 0xF9, 0x00, 0xC0, + 0x64, 0x32, 0xBC, 0xF1, 0x00, 0x0F, 0x05, 0xDD, 0x0C, 0xFB, 0x02, 0xFC, + 0x64, 0x22, 0x9C, 0xFB, 0xF2, 0xF2, 0x02, 0x80, 0xA1, 0xEB, 0x45, 0x01, + 0xA0, 0xEB, 0x45, 0x00, 0x64, 0x1E, 0xDD, 0xD2, 0x76, 0x1E, 0x7F, 0x1E, + 0xD2, 0xD2, 0x96, 0xE7, 0xF0, 0xB5, 0x5C, 0x1E, 0x05, 0x9D, 0x1A, 0x44, + 0x6C, 0x43, 0x01, 0xEB, 0x44, 0x01, 0x00, 0xEB, 0x44, 0x06, 0x52, 0x1E, + 0x5B, 0x1E, 0x22, 0xD3, 0x2C, 0x46, 0x19, 0xE0, 0x10, 0x78, 0x64, 0x28, + 0x05, 0xD9, 0xC0, 0xF1, 0x64, 0x00, 0x60, 0x43, 0x90, 0xFB, 0xF5, 0xF0, + 0x05, 0xE0, 0x64, 0x38, 0x00, 0xFB, 0x04, 0xF7, 0x97, 0xFB, 0xF5, 0xF7, + 0xC0, 0x1B, 0x31, 0xF9, 0x14, 0x70, 0x64, 0x30, 0x00, 0x2F, 0x05, 0xDD, + 0x47, 0x43, 0x64, 0x20, 0x97, 0xFB, 0xF0, 0xF0, 0x26, 0xF8, 0x14, 0x00, + 0x64, 0x1E, 0xE3, 0xD2, 0xA1, 0xEB, 0x45, 0x01, 0xA6, 0xEB, 0x45, 0x06, + 0xD9, 0xE7, 0xF0, 0xBD, 0x2D, 0xE9, 0xFC, 0x47, 0xFF, 0x4F, 0x91, 0x46, + 0x00, 0x26, 0x39, 0x68, 0xB1, 0xF8, 0x3E, 0xA1, 0xFD, 0x4D, 0x4F, 0xF0, + 0x01, 0x08, 0x2C, 0x46, 0x22, 0x46, 0xFC, 0x49, 0xCD, 0xE9, 0x00, 0x28, + 0x0A, 0x78, 0xFB, 0x49, 0x2B, 0x46, 0x09, 0x78, 0x06, 0xF0, 0x1D, 0xFC, + 0x20, 0x69, 0x29, 0x68, 0x6A, 0x68, 0x40, 0x1A, 0x61, 0x69, 0x89, 0x1A, + 0x08, 0x44, 0xA1, 0x69, 0xAA, 0x68, 0x89, 0x1A, 0x08, 0x44, 0xE1, 0x69, + 0xEA, 0x68, 0x89, 0x1A, 0x08, 0x44, 0x50, 0x45, 0x00, 0xDD, 0x01, 0x26, + 0xF0, 0x4A, 0x00, 0x23, 0x10, 0x78, 0x70, 0xB1, 0x90, 0x68, 0x40, 0x1C, + 0x90, 0x60, 0x39, 0x68, 0x91, 0xF8, 0x44, 0x11, 0x81, 0x42, 0x03, 0xDA, + 0x93, 0x60, 0x13, 0x70, 0x89, 0xF8, 0x00, 0x80, 0x30, 0x46, 0xBD, 0xE8, + 0xFC, 0x87, 0xC6, 0xB1, 0x90, 0x68, 0x40, 0x1C, 0x90, 0x60, 0x39, 0x68, + 0x91, 0xF8, 0x43, 0x41, 0x84, 0x42, 0x09, 0xDA, 0x93, 0x60, 0x82, 0xF8, + 0x00, 0x80, 0x91, 0xF8, 0x44, 0x11, 0x40, 0x46, 0x11, 0xB9, 0x13, 0x70, + 0x89, 0xF8, 0x00, 0x00, 0x38, 0x68, 0x90, 0xF8, 0xA8, 0x13, 0x07, 0x20, + 0x05, 0xF0, 0x9B, 0xF9, 0xE2, 0xE7, 0x93, 0x60, 0xE0, 0xE7, 0x2D, 0xE9, + 0xF0, 0x43, 0x00, 0x25, 0xDD, 0xE9, 0x07, 0xC9, 0x2C, 0x46, 0x0D, 0xE0, + 0x31, 0xF9, 0x12, 0x70, 0x30, 0xF9, 0x12, 0x60, 0x07, 0xEB, 0x03, 0x08, + 0x46, 0x45, 0x01, 0xDD, 0x6D, 0x1C, 0x03, 0xE0, 0xFF, 0x1A, 0xBE, 0x42, + 0x00, 0xDA, 0x64, 0x1C, 0x52, 0x1E, 0xEF, 0xD2, 0x65, 0x45, 0x01, 0xD8, + 0x64, 0x45, 0x02, 0xD9, 0x01, 0x21, 0x89, 0xF8, 0x00, 0x10, 0x1D, 0xE7, + 0xFE, 0xB5, 0x00, 0x25, 0x8D, 0xF8, 0x08, 0x50, 0xDF, 0xF8, 0x20, 0xC3, + 0xDF, 0xF8, 0x20, 0xE3, 0xC1, 0x4E, 0x9C, 0xF8, 0x00, 0xC0, 0x9E, 0xF8, + 0x00, 0xE0, 0x34, 0x68, 0x4C, 0xEA, 0x0E, 0x0C, 0xDF, 0xF8, 0x10, 0xE3, + 0x17, 0x46, 0x94, 0xF8, 0x45, 0x21, 0xB4, 0xF9, 0x46, 0x31, 0x9E, 0xF8, + 0x00, 0xE0, 0x52, 0x00, 0x94, 0xF8, 0x48, 0x41, 0x5C, 0xEA, 0x0E, 0x0C, + 0x08, 0xD1, 0x0D, 0xF1, 0x08, 0x0C, 0xCD, 0xE9, 0x00, 0x2C, 0xBC, 0x4A, + 0xB2, 0xF9, 0x00, 0x20, 0xFF, 0xF7, 0xB9, 0xFF, 0xB5, 0x49, 0x01, 0x22, + 0x48, 0x78, 0x68, 0xB1, 0xC8, 0x68, 0x40, 0x1C, 0xC8, 0x60, 0x33, 0x68, + 0x93, 0xF8, 0x49, 0x31, 0x83, 0x42, 0x02, 0xDA, 0xCD, 0x60, 0x4D, 0x70, + 0x3A, 0x70, 0x9D, 0xF8, 0x08, 0x00, 0xFE, 0xBD, 0x9D, 0xF8, 0x08, 0x00, + 0x01, 0x28, 0x01, 0xD0, 0xCD, 0x60, 0xF6, 0xE7, 0xC8, 0x68, 0x40, 0x1C, + 0xC8, 0x60, 0xA0, 0x42, 0x07, 0xDD, 0xCD, 0x60, 0x4A, 0x70, 0x30, 0x68, + 0x90, 0xF8, 0x49, 0x01, 0x08, 0xB9, 0x4D, 0x70, 0x3A, 0x70, 0x30, 0x68, + 0x90, 0xF8, 0xA8, 0x13, 0x07, 0x20, 0x05, 0xF0, 0x26, 0xF9, 0xE2, 0xE7, + 0x2D, 0xE9, 0xF0, 0x5F, 0x9A, 0x48, 0xA3, 0x4B, 0x9F, 0x4C, 0x01, 0x68, + 0x1B, 0x68, 0x25, 0x78, 0xB1, 0xF9, 0x52, 0x21, 0xB1, 0xF9, 0xD2, 0xC1, + 0xDE, 0x07, 0x02, 0xD0, 0x0D, 0xB9, 0xB1, 0xF9, 0x0A, 0x22, 0x9D, 0x4C, + 0x24, 0x78, 0x01, 0x2C, 0x01, 0xD1, 0xB1, 0xF9, 0x08, 0x22, 0x82, 0x46, + 0x9A, 0x48, 0xB1, 0xF9, 0xF6, 0x61, 0x04, 0x78, 0x01, 0x2C, 0x4A, 0xD0, + 0x02, 0x2C, 0x04, 0xD3, 0xB1, 0xF8, 0x56, 0x01, 0x08, 0xB1, 0xB1, 0xF9, + 0x56, 0x21, 0x18, 0x07, 0x14, 0xD5, 0x91, 0xF8, 0xB5, 0x02, 0x64, 0x27, + 0x00, 0xFB, 0x02, 0xF8, 0x98, 0xFB, 0xF7, 0xF8, 0x42, 0x44, 0x00, 0xFB, + 0x0C, 0xF8, 0x70, 0x43, 0x98, 0xFB, 0xF7, 0xF8, 0x90, 0xFB, 0xF7, 0xF0, + 0xC4, 0x44, 0x30, 0x44, 0x12, 0xB2, 0x0F, 0xFA, 0x8C, 0xFC, 0x06, 0xB2, + 0x89, 0x48, 0x8A, 0x4F, 0xDF, 0xF8, 0x28, 0x92, 0x02, 0x80, 0xA7, 0xF8, + 0x00, 0xC0, 0xA9, 0xF8, 0x00, 0x60, 0xD8, 0x07, 0x00, 0xD0, 0x75, 0xB3, + 0x91, 0xF8, 0x54, 0x01, 0x91, 0xF8, 0xD4, 0x81, 0xD3, 0x46, 0xAC, 0xEB, + 0x08, 0x0A, 0x91, 0xF8, 0xF8, 0xC1, 0x10, 0x1A, 0xA6, 0xEB, 0x0C, 0x08, + 0x80, 0x4E, 0x00, 0xB2, 0xDF, 0xF8, 0x04, 0xC2, 0x30, 0x80, 0x7F, 0x4E, + 0xA6, 0xF8, 0x00, 0xA0, 0xAC, 0xF8, 0x00, 0x80, 0x5F, 0xEA, 0x83, 0x78, + 0x01, 0xD5, 0x02, 0x2C, 0x01, 0xD3, 0x3A, 0x80, 0x30, 0x80, 0x13, 0xF0, + 0x05, 0x0F, 0x12, 0xD0, 0x14, 0xE0, 0x91, 0xF8, 0x55, 0x01, 0x10, 0x44, + 0x02, 0xB2, 0x91, 0xF8, 0xD5, 0x01, 0x60, 0x44, 0x0F, 0xFA, 0x80, 0xFC, + 0x91, 0xF8, 0xF9, 0x01, 0x30, 0x44, 0x06, 0xB2, 0xAD, 0xE7, 0xFF, 0xE7, + 0x91, 0xF8, 0x0C, 0x02, 0xCE, 0xE7, 0x5F, 0xEA, 0xC3, 0x58, 0x01, 0xD5, + 0x02, 0x2C, 0x03, 0xD3, 0xA9, 0xF8, 0x00, 0x20, 0xAC, 0xF8, 0x00, 0x00, + 0xDF, 0xF8, 0xAC, 0xA1, 0x6B, 0x4C, 0x5F, 0xEA, 0x83, 0x68, 0x01, 0xD5, + 0x1B, 0x07, 0x03, 0xD5, 0xAA, 0xF8, 0x00, 0x20, 0x20, 0x80, 0x06, 0xE0, + 0x31, 0xF8, 0xD6, 0x3F, 0xAA, 0xF8, 0x00, 0x30, 0x89, 0x78, 0x59, 0x1A, + 0x21, 0x80, 0x01, 0x2D, 0x03, 0xD0, 0x63, 0x49, 0x09, 0x78, 0x01, 0x29, + 0x05, 0xD1, 0x3A, 0x80, 0x30, 0x80, 0xA9, 0xF8, 0x00, 0x20, 0xAC, 0xF8, + 0x00, 0x00, 0xBC, 0xF9, 0x00, 0x20, 0xB6, 0xF9, 0x00, 0x10, 0x06, 0xF0, + 0x2E, 0xFA, 0x5C, 0x4D, 0x00, 0xB2, 0x28, 0x80, 0xDB, 0xF8, 0x00, 0x10, + 0x91, 0xF8, 0xD4, 0x20, 0x52, 0x07, 0x05, 0xD5, 0xB1, 0xF8, 0xEC, 0x10, + 0x11, 0xB1, 0x06, 0xF0, 0x27, 0xFA, 0x28, 0x80, 0xB4, 0xF9, 0x00, 0x10, + 0xB5, 0xF9, 0x00, 0x00, 0x06, 0xF0, 0x20, 0xFA, 0x28, 0x80, 0xBD, 0xE8, + 0xF0, 0x9F, 0x2D, 0xE9, 0xF0, 0x4F, 0xA5, 0xB0, 0x0E, 0x46, 0x07, 0x46, + 0x9A, 0x46, 0x15, 0x46, 0x00, 0x24, 0x20, 0x21, 0x1D, 0xA8, 0x09, 0xF0, + 0xC6, 0xFB, 0x24, 0x21, 0x14, 0xA8, 0x09, 0xF0, 0xC2, 0xFB, 0x28, 0x21, + 0x68, 0x46, 0x09, 0xF0, 0xBE, 0xFB, 0x28, 0x21, 0x0A, 0xA8, 0x09, 0xF0, + 0xBA, 0xFB, 0x32, 0x4A, 0x01, 0x21, 0x00, 0x20, 0x0D, 0xF1, 0x74, 0x08, + 0x13, 0x78, 0x06, 0xE0, 0x37, 0xF9, 0x10, 0x20, 0xAA, 0x42, 0x01, 0xDD, + 0x08, 0xF8, 0x00, 0x10, 0x40, 0x1C, 0x98, 0x42, 0xF6, 0xDB, 0x29, 0x4A, + 0x00, 0x20, 0x0D, 0xF1, 0x50, 0x0E, 0x12, 0x78, 0x06, 0xE0, 0x36, 0xF9, + 0x10, 0xC0, 0xAC, 0x45, 0x01, 0xDD, 0x0E, 0xF8, 0x00, 0x10, 0x40, 0x1C, + 0x90, 0x42, 0xF6, 0xDB, 0x01, 0x21, 0x00, 0x20, 0xA3, 0xF1, 0x01, 0x09, + 0xEB, 0x46, 0x14, 0xE0, 0x18, 0xF8, 0x00, 0x30, 0x7B, 0xB1, 0x08, 0xF8, + 0x00, 0x10, 0x0B, 0xEB, 0x81, 0x03, 0x37, 0xF9, 0x10, 0xC0, 0x53, 0xF8, + 0x04, 0x5D, 0x65, 0x44, 0x1D, 0x60, 0x08, 0xEB, 0x00, 0x03, 0x5B, 0x78, + 0x0B, 0xB9, 0x49, 0x1C, 0xC9, 0xB2, 0x40, 0x1C, 0xC0, 0xB2, 0x48, 0x45, + 0xE8, 0xDB, 0x01, 0x21, 0x00, 0x20, 0x0D, 0xF1, 0x28, 0x0C, 0x77, 0x46, + 0x52, 0x1E, 0x11, 0xE0, 0x3B, 0x5C, 0x6B, 0xB1, 0x39, 0x54, 0x0C, 0xEB, + 0x81, 0x03, 0x36, 0xF9, 0x10, 0x80, 0x53, 0xF8, 0x04, 0x5D, 0x45, 0x44, + 0x1D, 0x60, 0x3B, 0x18, 0x5B, 0x78, 0x0B, 0xB9, 0x49, 0x1C, 0xC9, 0xB2, + 0x40, 0x1C, 0xC0, 0xB2, 0x90, 0x42, 0xEB, 0xDB, 0x00, 0x21, 0x5D, 0x46, + 0x00, 0x20, 0x55, 0xF8, 0x21, 0x20, 0x5C, 0xF8, 0x20, 0x30, 0x2B, 0xE0, + 0x50, 0x24, 0x10, 0x00, 0x48, 0x53, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, + 0x00, 0x24, 0x10, 0x00, 0x74, 0x23, 0x10, 0x00, 0x08, 0x24, 0x10, 0x00, + 0x6D, 0x23, 0x10, 0x00, 0x06, 0x24, 0x10, 0x00, 0x0C, 0x24, 0x10, 0x00, + 0x1C, 0x24, 0x10, 0x00, 0x07, 0x24, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0xC6, 0x23, 0x10, 0x00, 0xC8, 0x23, 0x10, 0x00, 0xCA, 0x23, 0x10, 0x00, + 0xCC, 0x23, 0x10, 0x00, 0xCE, 0x23, 0x10, 0x00, 0xD0, 0x23, 0x10, 0x00, + 0xD4, 0x23, 0x10, 0x00, 0xD6, 0x23, 0x10, 0x00, 0x6E, 0x23, 0x10, 0x00, + 0xD2, 0x23, 0x10, 0x00, 0xD6, 0x18, 0x56, 0x45, 0x00, 0xDD, 0x01, 0x24, + 0x40, 0x1C, 0xC0, 0xB2, 0x0A, 0x28, 0xC8, 0xD3, 0x49, 0x1C, 0xC9, 0xB2, + 0x0A, 0x29, 0xC1, 0xD3, 0x25, 0xB0, 0x20, 0x46, 0xBD, 0xE8, 0xF0, 0x8F, + 0x70, 0xB5, 0xFE, 0x4C, 0xFE, 0x4D, 0xE2, 0x78, 0x2B, 0x68, 0x01, 0x2A, + 0x15, 0xD0, 0x33, 0xF9, 0xFA, 0x2F, 0xB3, 0xF9, 0x04, 0x30, 0xFF, 0xF7, + 0x40, 0xFF, 0x00, 0x21, 0x01, 0x28, 0x0F, 0xD0, 0xE0, 0x78, 0x01, 0x28, + 0x18, 0xD0, 0xA1, 0x80, 0xE1, 0x80, 0xF6, 0x49, 0xE2, 0x78, 0xC8, 0x7B, + 0x62, 0xF3, 0x04, 0x10, 0xC8, 0x73, 0x00, 0x20, 0x70, 0xBD, 0x33, 0xF9, + 0xFC, 0x2F, 0xE8, 0xE7, 0xA0, 0x88, 0x40, 0x1C, 0x80, 0xB2, 0xA0, 0x80, + 0x2A, 0x68, 0x92, 0xF8, 0x02, 0x21, 0x82, 0x42, 0xEB, 0xD2, 0x01, 0x20, + 0xE0, 0x70, 0xE7, 0xE7, 0xE0, 0x88, 0x40, 0x1C, 0x80, 0xB2, 0xE0, 0x80, + 0x2A, 0x68, 0x92, 0xF8, 0x03, 0x21, 0x82, 0x42, 0xDF, 0xD2, 0xE1, 0x70, + 0xA1, 0x80, 0xE1, 0x80, 0x01, 0x20, 0x70, 0xBD, 0xF0, 0xB5, 0xE2, 0x4E, + 0x00, 0x24, 0x27, 0x46, 0x33, 0x68, 0xB3, 0xF9, 0x4C, 0x51, 0xE1, 0x4B, + 0xB3, 0xF9, 0x00, 0x30, 0x0B, 0xE0, 0x31, 0xF8, 0x13, 0xC0, 0x20, 0xF8, + 0x13, 0xC0, 0x31, 0xF9, 0x13, 0xC0, 0xAC, 0x45, 0x02, 0xDA, 0x20, 0xF8, + 0x13, 0x70, 0x00, 0xE0, 0x64, 0x1C, 0x5B, 0x1E, 0xF1, 0xD2, 0x30, 0x68, + 0x90, 0xF8, 0x31, 0x11, 0xC9, 0x07, 0x05, 0xD0, 0xB0, 0xF8, 0x4E, 0x01, + 0xA0, 0x42, 0x01, 0xDA, 0x01, 0x20, 0x10, 0x70, 0xF0, 0xBD, 0xD3, 0x4A, + 0x10, 0xB5, 0x10, 0x68, 0xCE, 0x49, 0x40, 0xF0, 0x02, 0x00, 0x10, 0x60, + 0x09, 0x68, 0x91, 0xF8, 0xB0, 0x30, 0xDB, 0x07, 0x03, 0xD1, 0x91, 0xF8, + 0xD4, 0x10, 0xC9, 0x07, 0x02, 0xD0, 0x40, 0xF0, 0x20, 0x00, 0x10, 0x60, + 0xC5, 0x48, 0x81, 0x78, 0x10, 0x68, 0x05, 0xF0, 0xDF, 0xFF, 0xBD, 0xE8, + 0x10, 0x40, 0x4F, 0xF4, 0x00, 0x71, 0x00, 0x20, 0x07, 0xF0, 0xF0, 0xBF, + 0x2D, 0xE9, 0xF0, 0x5F, 0xDF, 0xF8, 0x0C, 0x93, 0x88, 0x46, 0x83, 0x46, + 0x92, 0x46, 0x4F, 0xF4, 0x88, 0x61, 0xD9, 0xF8, 0x00, 0x00, 0x09, 0xF0, + 0x72, 0xFA, 0xBF, 0x48, 0x00, 0x26, 0x35, 0x46, 0x04, 0x78, 0x19, 0xE0, + 0xE1, 0xB2, 0xD9, 0xF8, 0x00, 0x00, 0x0B, 0xF0, 0xE3, 0xF8, 0x07, 0x46, + 0xE1, 0xB2, 0x58, 0x46, 0x0B, 0xF0, 0xE8, 0xF8, 0x01, 0x46, 0xB8, 0x48, + 0x01, 0x22, 0x00, 0x78, 0x08, 0xE0, 0x31, 0xF9, 0x10, 0xC0, 0xC4, 0x45, + 0x04, 0xDD, 0x3A, 0x54, 0x31, 0xF9, 0x10, 0x30, 0x76, 0x1C, 0x1D, 0x44, + 0x40, 0x1E, 0xF4, 0xD2, 0x64, 0x1E, 0xE3, 0xD2, 0x30, 0x46, 0xCA, 0xF8, + 0x00, 0x50, 0x9A, 0xE6, 0x70, 0xB5, 0x0D, 0x46, 0xAB, 0x49, 0x14, 0x46, + 0x09, 0x78, 0x4A, 0x00, 0x01, 0x46, 0xAB, 0x48, 0x09, 0xF0, 0xC4, 0xF9, + 0xA8, 0x48, 0x29, 0x46, 0x00, 0x78, 0x42, 0x00, 0xA8, 0x48, 0x09, 0xF0, + 0xBD, 0xF9, 0xA1, 0x48, 0x21, 0x46, 0xB0, 0xF9, 0x00, 0x00, 0x42, 0x00, + 0xA5, 0x48, 0x00, 0x68, 0x09, 0xF0, 0xB4, 0xF9, 0xA4, 0x49, 0x01, 0x20, + 0x08, 0x70, 0xA4, 0x49, 0x08, 0x70, 0x70, 0xBD, 0x10, 0xB5, 0x00, 0x24, + 0x05, 0xE0, 0x30, 0xF9, 0x12, 0x30, 0x8B, 0x42, 0x01, 0xDD, 0x01, 0x24, + 0x01, 0xE0, 0x52, 0x1E, 0xF7, 0xD2, 0x20, 0x46, 0x10, 0xBD, 0x30, 0xB5, + 0x04, 0x46, 0x00, 0x20, 0x4D, 0x42, 0x07, 0xE0, 0x34, 0xF9, 0x12, 0x30, + 0x8B, 0x42, 0x01, 0xDC, 0xAB, 0x42, 0x01, 0xDA, 0x01, 0x20, 0x30, 0xBD, + 0x52, 0x1E, 0xF5, 0xD2, 0x30, 0xBD, 0x88, 0x49, 0x94, 0x48, 0x95, 0x4A, + 0x08, 0x61, 0x00, 0x20, 0x10, 0x70, 0x94, 0x4A, 0x10, 0x70, 0xC8, 0x70, + 0x88, 0x80, 0xC8, 0x80, 0x88, 0x60, 0x08, 0x70, 0xC8, 0x60, 0x48, 0x70, + 0x00, 0xF0, 0x2F, 0xBA, 0x2D, 0xE9, 0xF7, 0x4F, 0x8C, 0xB0, 0x8E, 0x49, + 0x4F, 0xF0, 0x00, 0x09, 0x8D, 0xF8, 0x14, 0x90, 0x91, 0xF8, 0x00, 0x80, + 0x7A, 0x4C, 0x85, 0x49, 0x7A, 0x48, 0x84, 0xF8, 0x02, 0x90, 0x0A, 0x78, + 0x02, 0x92, 0x81, 0xF8, 0x00, 0x90, 0x00, 0x68, 0x86, 0x4D, 0xDF, 0xF8, + 0xF0, 0xB1, 0x90, 0xF8, 0x31, 0x01, 0xDF, 0xF8, 0xF4, 0xA1, 0x4F, 0x46, + 0x4E, 0x46, 0x40, 0x06, 0x1C, 0xD5, 0x82, 0x48, 0xB0, 0xF9, 0x00, 0x20, + 0x81, 0x48, 0xB0, 0xF9, 0x00, 0x10, 0x81, 0x48, 0xB0, 0xF9, 0x00, 0x00, + 0x06, 0xF0, 0x3B, 0xF8, 0x03, 0xB2, 0x70, 0x48, 0x9B, 0xF8, 0x00, 0x10, + 0x00, 0x78, 0xCD, 0xE9, 0x00, 0x01, 0xDA, 0xF8, 0x00, 0x20, 0x0C, 0x99, + 0x20, 0x69, 0xFF, 0xF7, 0x77, 0xFA, 0x79, 0x48, 0x02, 0x9B, 0x21, 0x69, + 0x02, 0x68, 0x03, 0xE0, 0x76, 0x48, 0x13, 0x46, 0x0C, 0x99, 0x02, 0x68, + 0x28, 0x68, 0x00, 0xF0, 0xE1, 0xF9, 0x02, 0x20, 0x07, 0xF0, 0xDB, 0xFF, + 0x5D, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x30, 0x01, 0x80, 0x07, 0x08, 0xD5, + 0xB8, 0xF1, 0x00, 0x0F, 0x05, 0xD1, 0x05, 0xAA, 0x02, 0x99, 0x28, 0x68, + 0xFF, 0xF7, 0x70, 0xFC, 0x07, 0x46, 0x56, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0x30, 0x01, 0x40, 0x07, 0x09, 0xD5, 0xB8, 0xF1, 0x00, 0x0F, 0x06, 0xD1, + 0x05, 0xAA, 0xDA, 0xF8, 0x00, 0x10, 0x28, 0x68, 0xFF, 0xF7, 0xD6, 0xFC, + 0x06, 0x46, 0x01, 0x2F, 0x46, 0xD0, 0x01, 0x2E, 0x44, 0xD0, 0xFF, 0xF7, + 0x23, 0xFD, 0x4B, 0x4F, 0x38, 0x68, 0x90, 0xF8, 0x30, 0x01, 0x00, 0x07, + 0x17, 0xD5, 0x59, 0x48, 0xB0, 0xF9, 0x00, 0x20, 0x58, 0x48, 0xB0, 0xF9, + 0x00, 0x10, 0x58, 0x48, 0xB0, 0xF9, 0x00, 0x00, 0x05, 0xF0, 0xE9, 0xFF, + 0x03, 0xB2, 0x47, 0x48, 0x9B, 0xF8, 0x00, 0x10, 0x00, 0x78, 0xCD, 0xE9, + 0x00, 0x01, 0x29, 0x68, 0xDA, 0xF8, 0x00, 0x20, 0x08, 0x46, 0xFF, 0xF7, + 0xEA, 0xFA, 0x3E, 0x4E, 0xDA, 0xF8, 0x00, 0x20, 0x29, 0x68, 0xB6, 0xF9, + 0x00, 0x30, 0x20, 0x69, 0x05, 0xF0, 0xC7, 0xFF, 0x02, 0x20, 0x07, 0xF0, + 0xA7, 0xFF, 0x38, 0x68, 0x90, 0xF8, 0x30, 0x01, 0xC0, 0x06, 0x07, 0xD5, + 0x38, 0x48, 0x21, 0x69, 0x9B, 0xF8, 0x00, 0x30, 0x02, 0x78, 0x08, 0x46, + 0xFF, 0xF7, 0x36, 0xFB, 0x38, 0x68, 0x90, 0xF8, 0x30, 0x11, 0x8A, 0x06, + 0x1A, 0xD4, 0x49, 0x06, 0x18, 0xD4, 0x90, 0xF8, 0x2F, 0x01, 0xC1, 0x07, + 0x14, 0xD1, 0x11, 0xE0, 0x02, 0x20, 0x07, 0xF0, 0x8B, 0xFF, 0x9D, 0xF8, + 0x14, 0x00, 0x01, 0x28, 0x09, 0xD1, 0x01, 0x2F, 0x01, 0xD1, 0x12, 0x20, + 0xA0, 0x70, 0x01, 0x2E, 0x01, 0xD1, 0x11, 0x20, 0x4B, 0xE1, 0xFF, 0xF7, + 0xA2, 0xFE, 0xA7, 0xE0, 0x80, 0x07, 0x06, 0xD5, 0x21, 0x69, 0xB6, 0xF9, + 0x00, 0x30, 0x33, 0x4A, 0x08, 0x46, 0x00, 0xF0, 0x4E, 0xF9, 0xDF, 0xF8, + 0xC8, 0xA0, 0x32, 0x4D, 0x9A, 0xF8, 0x00, 0x00, 0x10, 0xF0, 0x30, 0x0F, + 0x29, 0xD0, 0x02, 0x98, 0x8D, 0xF8, 0x04, 0x90, 0x01, 0x28, 0x01, 0xD1, + 0x20, 0x49, 0x08, 0x70, 0x02, 0x20, 0x07, 0xF0, 0x46, 0xFF, 0x01, 0xA8, + 0x00, 0x90, 0x1B, 0x48, 0x23, 0x69, 0x02, 0x68, 0xDD, 0xE9, 0x0D, 0x01, + 0xFE, 0xF7, 0x83, 0xFD, 0x8D, 0xF8, 0x14, 0x00, 0x02, 0x20, 0x07, 0xF0, + 0x53, 0xFF, 0x9D, 0xF8, 0x14, 0x00, 0xDF, 0xF8, 0x8C, 0x80, 0x01, 0x28, + 0x65, 0xD0, 0x9D, 0xF8, 0x04, 0x00, 0x01, 0x28, 0x64, 0xD0, 0x9A, 0xF8, + 0x00, 0x00, 0xC0, 0x06, 0x03, 0xD5, 0x1E, 0x48, 0x00, 0x78, 0x01, 0x28, + 0x70, 0xD0, 0x38, 0x68, 0x90, 0xF8, 0xF0, 0x00, 0x40, 0x07, 0x35, 0xE0, + 0x74, 0x23, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x58, 0x25, 0x10, 0x00, + 0x0C, 0x24, 0x10, 0x00, 0x64, 0x24, 0x10, 0x00, 0xA0, 0x23, 0x10, 0x00, + 0x00, 0x24, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, 0xFC, 0x22, 0x01, 0x20, + 0x38, 0x23, 0x01, 0x20, 0x18, 0x24, 0x10, 0x00, 0x61, 0x24, 0x10, 0x00, + 0x56, 0x23, 0x10, 0x00, 0x60, 0x54, 0x10, 0x00, 0x29, 0x24, 0x10, 0x00, + 0x28, 0x24, 0x10, 0x00, 0x6D, 0x23, 0x10, 0x00, 0x14, 0x24, 0x10, 0x00, + 0xCA, 0x23, 0x10, 0x00, 0xC8, 0x23, 0x10, 0x00, 0xC6, 0x23, 0x10, 0x00, + 0xBC, 0x23, 0x10, 0x00, 0x3C, 0x88, 0x01, 0x20, 0x1C, 0x24, 0x10, 0x00, + 0x10, 0x24, 0x10, 0x00, 0x59, 0x23, 0x10, 0x00, 0x55, 0x23, 0x10, 0x00, + 0x0B, 0xD5, 0x9A, 0xF8, 0x00, 0x00, 0x80, 0x06, 0x07, 0xD5, 0x86, 0x49, + 0x86, 0x48, 0xFF, 0xF7, 0xC3, 0xFD, 0x8D, 0xF8, 0x14, 0x00, 0x01, 0x28, + 0x54, 0xD0, 0x84, 0x48, 0x00, 0x78, 0x02, 0x28, 0x38, 0x68, 0x7C, 0xD3, + 0x90, 0xF8, 0x30, 0x01, 0x00, 0x06, 0x04, 0xD5, 0x21, 0x69, 0x05, 0xAA, + 0x08, 0x46, 0xFF, 0xF7, 0xEB, 0xFD, 0x9D, 0xF8, 0x14, 0x00, 0xE8, 0xB3, + 0x92, 0xE0, 0x98, 0xF8, 0x00, 0x00, 0xB0, 0xE0, 0x02, 0x20, 0x07, 0xF0, + 0xC6, 0xFE, 0x98, 0xF8, 0x00, 0x10, 0x22, 0x20, 0x05, 0xF0, 0xF8, 0xFD, + 0xDD, 0xE9, 0x0D, 0x01, 0x0C, 0x9A, 0xFF, 0xF7, 0x4D, 0xFE, 0x02, 0x20, + 0x07, 0xF0, 0xD4, 0xFE, 0x00, 0x20, 0x0F, 0xB0, 0x94, 0xE5, 0xFF, 0xE7, + 0x01, 0xA8, 0xFF, 0xF7, 0x9F, 0xF8, 0x6F, 0x49, 0x82, 0x46, 0x81, 0xF8, + 0x00, 0x90, 0xB6, 0xF9, 0x00, 0x00, 0x21, 0x69, 0x42, 0x00, 0x28, 0x68, + 0x09, 0xF0, 0x06, 0xF8, 0xFF, 0xF7, 0x44, 0xF8, 0x9D, 0xF8, 0x04, 0x00, + 0x01, 0x28, 0xD7, 0xD0, 0x38, 0x68, 0x90, 0xF8, 0xF1, 0x00, 0xC0, 0x07, + 0x10, 0xD0, 0x65, 0x48, 0x00, 0x78, 0x68, 0xB1, 0x02, 0x20, 0x07, 0xF0, + 0x96, 0xFE, 0x64, 0x48, 0x62, 0x4A, 0x01, 0x68, 0x0C, 0x98, 0xFE, 0xF7, + 0xEE, 0xFF, 0x00, 0xE0, 0x06, 0xE0, 0x02, 0x20, 0x07, 0xF0, 0xA6, 0xFE, + 0x50, 0x46, 0xD0, 0xE7, 0x24, 0x20, 0x6E, 0xE0, 0x3F, 0x68, 0x97, 0xF8, + 0x31, 0x01, 0x80, 0x07, 0x48, 0xD5, 0x14, 0x22, 0x5A, 0x49, 0x06, 0xA8, + 0x09, 0xF0, 0x21, 0xF8, 0x2A, 0x68, 0xB7, 0xF8, 0x50, 0x71, 0x23, 0x69, + 0x8D, 0xF8, 0x2C, 0x90, 0x8D, 0xF8, 0x2E, 0x90, 0x9B, 0xF8, 0x00, 0x00, + 0x0D, 0xF1, 0x18, 0x0C, 0x41, 0x1E, 0x8D, 0xF8, 0x2D, 0x10, 0x52, 0x49, + 0x09, 0x78, 0xA1, 0xF1, 0x01, 0x08, 0x8D, 0xF8, 0x2F, 0x80, 0x8D, 0xE8, + 0x0C, 0x10, 0xAD, 0xF8, 0x10, 0x70, 0x8D, 0xF8, 0x13, 0x10, 0x0B, 0xAA, + 0x8D, 0xF8, 0x12, 0x00, 0x03, 0x92, 0x68, 0x46, 0x00, 0xE0, 0x02, 0xE0, + 0x08, 0xF0, 0xA2, 0xF8, 0x25, 0xE0, 0x90, 0xF8, 0xE8, 0x17, 0x49, 0x06, + 0x10, 0xD5, 0xB6, 0xF9, 0x00, 0x00, 0x2A, 0x68, 0x08, 0xE0, 0x21, 0x69, + 0x32, 0xF9, 0x10, 0x30, 0x31, 0xF9, 0x10, 0x10, 0x99, 0x42, 0x01, 0xDD, + 0x22, 0xF8, 0x10, 0x10, 0x40, 0x1E, 0x00, 0xB2, 0xF3, 0xD2, 0x10, 0xE0, + 0x90, 0xF8, 0x30, 0x01, 0x00, 0x06, 0x05, 0xD5, 0x05, 0xAA, 0x21, 0x69, + 0x28, 0x68, 0xFF, 0xF7, 0x55, 0xFD, 0x06, 0xE0, 0xB6, 0xF9, 0x00, 0x00, + 0x21, 0x69, 0x42, 0x00, 0x28, 0x68, 0x08, 0xF0, 0x91, 0xFF, 0x9D, 0xF8, + 0x14, 0x00, 0x01, 0x28, 0x14, 0xD0, 0xB6, 0xF9, 0x00, 0x20, 0x32, 0x4E, + 0x28, 0x68, 0xB6, 0xF9, 0x00, 0x10, 0xFF, 0xF7, 0xD7, 0xFD, 0x01, 0x28, + 0x0D, 0xD0, 0x28, 0x49, 0x00, 0x24, 0x81, 0xF8, 0x00, 0x90, 0x2D, 0x49, + 0x08, 0x78, 0x08, 0xB1, 0x40, 0x1E, 0x08, 0x70, 0x20, 0x46, 0x60, 0xE7, + 0x13, 0x20, 0xA0, 0x70, 0xB1, 0xE6, 0x21, 0x49, 0x01, 0x24, 0x28, 0x4A, + 0x0C, 0x70, 0xB6, 0xF9, 0x00, 0x10, 0x28, 0x68, 0xFF, 0xF7, 0x6C, 0xFD, + 0x25, 0x49, 0x08, 0x60, 0xEE, 0xE7, 0x30, 0xB5, 0x07, 0xE0, 0x31, 0xF9, + 0x13, 0x40, 0x32, 0xF9, 0x13, 0x50, 0x6C, 0x43, 0xE4, 0x11, 0x20, 0xF8, + 0x13, 0x40, 0x5B, 0x1E, 0xF5, 0xD2, 0x30, 0xBD, 0x1C, 0xB5, 0x10, 0x24, + 0xCD, 0xE9, 0x00, 0x43, 0x13, 0x46, 0x1C, 0x4A, 0xB2, 0xF9, 0x00, 0x20, + 0x06, 0xF0, 0x40, 0xF8, 0x1C, 0xBD, 0x1A, 0x48, 0x1A, 0x4A, 0x1B, 0x4B, + 0x01, 0x68, 0xB1, 0xF8, 0x52, 0x01, 0x10, 0x80, 0xB1, 0xF8, 0xD2, 0x21, + 0x1A, 0x80, 0x18, 0x4B, 0xB1, 0xF8, 0xF6, 0x11, 0x19, 0x80, 0x17, 0x4B, + 0x18, 0x80, 0x17, 0x4B, 0x1A, 0x80, 0x17, 0x4A, 0x11, 0x80, 0x17, 0x49, + 0x08, 0x80, 0x17, 0x49, 0x08, 0x80, 0x70, 0x47, 0xBC, 0x0A, 0x01, 0x20, + 0x80, 0x0A, 0x01, 0x20, 0xB8, 0x23, 0x10, 0x00, 0x29, 0x24, 0x10, 0x00, + 0x08, 0x24, 0x10, 0x00, 0xA4, 0x48, 0x10, 0x00, 0x18, 0x24, 0x10, 0x00, + 0x24, 0x1D, 0x01, 0x00, 0x00, 0x24, 0x10, 0x00, 0xD2, 0x23, 0x10, 0x00, + 0xE0, 0x23, 0x10, 0x00, 0xD8, 0x23, 0x10, 0x00, 0xC0, 0x23, 0x10, 0x00, + 0x0C, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0xC6, 0x23, 0x10, 0x00, + 0xC8, 0x23, 0x10, 0x00, 0xCA, 0x23, 0x10, 0x00, 0xCC, 0x23, 0x10, 0x00, + 0xCE, 0x23, 0x10, 0x00, 0xD0, 0x23, 0x10, 0x00, 0xD4, 0x23, 0x10, 0x00, + 0xD6, 0x23, 0x10, 0x00, 0x70, 0xB5, 0x04, 0x46, 0xDE, 0x4B, 0x00, 0x20, + 0x84, 0xF8, 0x29, 0x00, 0x93, 0xF9, 0x00, 0x00, 0x00, 0x28, 0x01, 0xDD, + 0x40, 0x1E, 0x18, 0x70, 0xDA, 0x48, 0xDB, 0x4D, 0x94, 0xF8, 0x25, 0x60, + 0x02, 0x78, 0x28, 0x68, 0x04, 0x2E, 0x06, 0xD0, 0x93, 0xF9, 0x00, 0x30, + 0x00, 0x2B, 0x21, 0xDD, 0x90, 0xF8, 0x8B, 0x01, 0x15, 0xE0, 0xD5, 0x4B, + 0x1B, 0x78, 0x43, 0xB1, 0x00, 0x2A, 0x94, 0xF9, 0x26, 0x20, 0x07, 0xD0, + 0x90, 0xF8, 0x01, 0x02, 0x8A, 0x42, 0x07, 0xD0, 0x09, 0xE0, 0x90, 0xF8, + 0xFF, 0x01, 0x03, 0xE0, 0x90, 0xF8, 0x00, 0x02, 0x8A, 0x42, 0x02, 0xD1, + 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xE0, 0x00, 0x09, 0x84, 0xF8, 0x29, 0x00, + 0x20, 0x46, 0x00, 0xF0, 0x34, 0xF9, 0x01, 0x28, 0x1B, 0xD0, 0x24, 0xE0, + 0x00, 0x2A, 0x94, 0xF9, 0x26, 0x20, 0x04, 0xD0, 0x8A, 0x42, 0x11, 0xD1, + 0x90, 0xF8, 0x89, 0x01, 0xED, 0xE7, 0x8A, 0x42, 0x02, 0xD1, 0x90, 0xF8, + 0x89, 0x01, 0xE5, 0xE7, 0x90, 0xF8, 0x8A, 0x11, 0x01, 0xF0, 0x0F, 0x01, + 0x84, 0xF8, 0x29, 0x10, 0xBC, 0x49, 0x09, 0x78, 0x01, 0x29, 0xE1, 0xD1, + 0x90, 0xF8, 0x8A, 0x01, 0xDB, 0xE7, 0x29, 0x68, 0x94, 0xF8, 0x29, 0x00, + 0x91, 0xF8, 0x8B, 0x11, 0x01, 0xF0, 0x0F, 0x01, 0x08, 0x44, 0x84, 0xF8, + 0x29, 0x00, 0xB5, 0x48, 0x00, 0x78, 0x01, 0x28, 0x08, 0xD1, 0x29, 0x68, + 0x14, 0xF8, 0x29, 0x0F, 0x91, 0xF8, 0xCB, 0x10, 0x01, 0xF0, 0x0F, 0x01, + 0x08, 0x44, 0x20, 0x70, 0x70, 0xBD, 0xAC, 0x49, 0x00, 0x20, 0x08, 0x70, + 0xAD, 0x49, 0x05, 0x20, 0x08, 0x70, 0x70, 0x47, 0xAC, 0x49, 0x08, 0x78, + 0x20, 0xB1, 0x40, 0x1E, 0x10, 0xF0, 0xFF, 0x00, 0x08, 0x70, 0x04, 0xD1, + 0xA9, 0x49, 0x00, 0x20, 0x08, 0x70, 0xA9, 0x49, 0x08, 0x60, 0x70, 0x47, + 0xA1, 0x4A, 0x10, 0xB5, 0x14, 0x78, 0x9F, 0x4A, 0x90, 0xF8, 0x29, 0x10, + 0x12, 0x68, 0x92, 0xF8, 0x89, 0x31, 0x03, 0xF0, 0x0F, 0x03, 0x5C, 0xB1, + 0x92, 0xF8, 0x00, 0x22, 0x02, 0xF0, 0x0F, 0x02, 0xD2, 0x1A, 0x89, 0x1A, + 0x00, 0x29, 0x00, 0xDC, 0x00, 0x21, 0x80, 0xF8, 0x29, 0x10, 0x10, 0xBD, + 0x92, 0xF8, 0xFF, 0x21, 0xF2, 0xE7, 0x98, 0x49, 0x00, 0x20, 0x08, 0x70, + 0x92, 0x49, 0x08, 0x70, 0x70, 0x47, 0x2D, 0xE9, 0xF8, 0x4F, 0x06, 0x46, + 0x00, 0x88, 0xDF, 0xF8, 0x4C, 0xB2, 0x00, 0x04, 0x00, 0x0C, 0x13, 0xD0, + 0x00, 0x24, 0x0A, 0x22, 0x35, 0x68, 0x20, 0x46, 0x01, 0x27, 0x07, 0xFA, + 0x00, 0xF1, 0x0D, 0x42, 0x19, 0xD0, 0x65, 0x21, 0x01, 0xFB, 0x00, 0x61, + 0x91, 0xF8, 0x2D, 0x30, 0x09, 0x31, 0x01, 0x2B, 0x07, 0xD0, 0x0A, 0x22, + 0x10, 0x46, 0x0E, 0xE0, 0x00, 0x20, 0x8B, 0xF8, 0x00, 0x00, 0xBD, 0xE8, + 0xF8, 0x8F, 0x0B, 0x6D, 0xB1, 0xF8, 0x31, 0x10, 0x93, 0xFB, 0xF1, 0xFC, + 0xA4, 0x45, 0x02, 0xDD, 0x93, 0xFB, 0xF1, 0xF4, 0x42, 0xB2, 0x40, 0x1C, + 0x0A, 0x28, 0xDE, 0xDB, 0x0A, 0x25, 0x4F, 0xF0, 0x02, 0x09, 0xDF, 0xF8, + 0xDC, 0x81, 0x4F, 0xF0, 0x08, 0x0A, 0x00, 0x92, 0x8B, 0xE0, 0x30, 0x68, + 0x07, 0xFA, 0x05, 0xF1, 0x08, 0x42, 0x6C, 0xD0, 0x65, 0x20, 0x05, 0xFB, + 0x00, 0x64, 0x94, 0xF8, 0x2D, 0x00, 0x09, 0x34, 0x01, 0x28, 0x08, 0xD0, + 0x05, 0x28, 0x15, 0xD0, 0x02, 0x28, 0x0A, 0xD0, 0x06, 0x28, 0x17, 0xD0, + 0x07, 0x28, 0x5D, 0xD0, 0x60, 0xE0, 0x20, 0x46, 0x00, 0x99, 0xFF, 0xF7, + 0x0B, 0xFF, 0x84, 0xF8, 0x24, 0x90, 0x94, 0xF8, 0x2F, 0x00, 0xC0, 0x07, + 0x1B, 0xD0, 0x20, 0x46, 0x00, 0xF0, 0x6D, 0xF8, 0x50, 0xB1, 0x16, 0xE0, + 0x20, 0x46, 0x00, 0xF0, 0x84, 0xF8, 0x06, 0x20, 0x84, 0xF8, 0x24, 0x00, + 0x94, 0xF8, 0x2A, 0x00, 0xC0, 0xB3, 0x58, 0xE0, 0xD8, 0xF8, 0x00, 0x10, + 0x94, 0xF8, 0x29, 0x00, 0x91, 0xF8, 0x8B, 0x11, 0x01, 0xF0, 0x0F, 0x01, + 0x88, 0x42, 0x06, 0xD9, 0x40, 0x1A, 0x84, 0xF8, 0x29, 0x00, 0x94, 0xF8, + 0x29, 0x00, 0x18, 0xB1, 0x10, 0xE0, 0x00, 0x20, 0x84, 0xF8, 0x29, 0x00, + 0x03, 0x20, 0x84, 0xF8, 0x24, 0x00, 0x9B, 0xF8, 0x00, 0x10, 0x49, 0x1C, + 0x8B, 0xF8, 0x00, 0x10, 0x94, 0xF8, 0x25, 0x00, 0x04, 0x28, 0x18, 0xD0, + 0xFF, 0xF7, 0x41, 0xFF, 0xD8, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x9F, 0x03, + 0xC2, 0x07, 0x1F, 0xD0, 0x40, 0x07, 0x1D, 0xD5, 0x94, 0xF8, 0x2F, 0x00, + 0x00, 0x06, 0x19, 0xD4, 0x84, 0xF8, 0x24, 0x90, 0xD8, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0xA3, 0x13, 0x02, 0x20, 0x04, 0xF0, 0xFC, 0xFA, 0x0F, 0xE0, + 0x02, 0xE0, 0x41, 0x48, 0x07, 0x70, 0xE5, 0xE7, 0x94, 0xF8, 0x2F, 0x00, + 0x80, 0x07, 0x03, 0xD5, 0x07, 0x20, 0x84, 0xF8, 0x24, 0x00, 0x18, 0xE0, + 0x84, 0xF8, 0x24, 0xA0, 0xFF, 0xF7, 0x22, 0xFF, 0x94, 0xF8, 0x24, 0x00, + 0x02, 0x28, 0x02, 0xD0, 0x06, 0x28, 0x0E, 0xD1, 0x06, 0xE0, 0x94, 0xF8, + 0x29, 0x00, 0x50, 0xB1, 0x40, 0x1E, 0x84, 0xF8, 0x29, 0x00, 0x06, 0xE0, + 0x94, 0xF8, 0x2A, 0x00, 0x18, 0xB1, 0xC0, 0xB2, 0x40, 0x1E, 0x84, 0xF8, + 0x2A, 0x00, 0x6D, 0x1E, 0xBF, 0xF4, 0x71, 0xAF, 0x57, 0xE7, 0x90, 0xF8, + 0x21, 0x10, 0x90, 0xF8, 0x20, 0x20, 0x8A, 0x1A, 0xB0, 0xF8, 0x2F, 0x10, + 0x52, 0x1C, 0x02, 0x2A, 0x0B, 0xD9, 0x90, 0xF8, 0x23, 0x20, 0x90, 0xF8, + 0x22, 0x30, 0x9A, 0x42, 0x05, 0xD1, 0x41, 0xF0, 0x01, 0x01, 0xA0, 0xF8, + 0x2F, 0x10, 0x01, 0x20, 0x70, 0x47, 0x21, 0xF0, 0x01, 0x01, 0xA0, 0xF8, + 0x2F, 0x10, 0x00, 0x20, 0x70, 0x47, 0x10, 0xB5, 0x00, 0x21, 0x80, 0xF8, + 0x2A, 0x10, 0x1B, 0x49, 0x0A, 0x78, 0x1B, 0x49, 0x00, 0x2A, 0x09, 0x68, + 0x91, 0xF8, 0x8C, 0x21, 0x07, 0xD0, 0x12, 0x09, 0x80, 0xF8, 0x2A, 0x20, + 0x90, 0xF8, 0x25, 0x20, 0x04, 0x2A, 0x03, 0xD0, 0x0A, 0xE0, 0x02, 0xF0, + 0x0F, 0x02, 0xF5, 0xE7, 0x90, 0xF8, 0x2A, 0x20, 0x91, 0xF8, 0xFF, 0x31, + 0x02, 0xEB, 0x13, 0x12, 0x80, 0xF8, 0x2A, 0x20, 0x91, 0xF8, 0x8D, 0x21, + 0x83, 0x6D, 0x02, 0xF0, 0x0F, 0x04, 0xA3, 0x42, 0x05, 0xDD, 0x90, 0xF8, + 0x2A, 0x30, 0x03, 0xEB, 0x12, 0x12, 0x80, 0xF8, 0x2A, 0x20, 0x0B, 0x4A, + 0x12, 0x78, 0x01, 0x2A, 0x06, 0xD1, 0x10, 0xF8, 0x2A, 0x2F, 0x91, 0xF8, + 0xCB, 0x10, 0x02, 0xEB, 0x11, 0x11, 0x01, 0x70, 0x10, 0xBD, 0x00, 0x00, + 0x88, 0x23, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, + 0x2A, 0x24, 0x10, 0x00, 0x6E, 0x23, 0x10, 0x00, 0x6D, 0x23, 0x10, 0x00, + 0xE0, 0x23, 0x10, 0x00, 0x08, 0x24, 0x10, 0x00, 0x38, 0x23, 0x10, 0x00, + 0x3C, 0x23, 0x10, 0x00, 0xF6, 0x4A, 0x10, 0xB5, 0x13, 0x78, 0xF6, 0x4A, + 0x12, 0x68, 0x53, 0xB1, 0x92, 0xF8, 0x9B, 0x01, 0xB2, 0xF8, 0x9C, 0x31, + 0x00, 0xF0, 0x0F, 0x04, 0x00, 0x09, 0x5B, 0x43, 0xB2, 0xF8, 0x9E, 0x21, + 0x13, 0xE0, 0x90, 0xF8, 0x25, 0x00, 0x01, 0x28, 0x16, 0xD0, 0x04, 0x28, + 0x17, 0xD0, 0xED, 0x48, 0x00, 0x78, 0x01, 0x28, 0x1E, 0xD0, 0x92, 0xF8, + 0x9A, 0x01, 0xB2, 0xF8, 0xA4, 0x31, 0x00, 0xF0, 0x0F, 0x04, 0x00, 0x09, + 0xB2, 0xF8, 0xA6, 0x21, 0x5B, 0x43, 0x52, 0x43, 0x99, 0x42, 0x02, 0xDD, + 0x91, 0x42, 0x12, 0xDB, 0x20, 0x46, 0x10, 0xBD, 0x92, 0xF8, 0xE6, 0x01, + 0xED, 0xE7, 0x92, 0xF8, 0x06, 0x02, 0xB2, 0xF8, 0x02, 0x32, 0x00, 0xF0, + 0x0F, 0x04, 0x00, 0x09, 0x5B, 0x43, 0xB2, 0xF8, 0x04, 0x22, 0xEA, 0xE7, + 0x92, 0xF8, 0xCC, 0x00, 0xDF, 0xE7, 0x9A, 0x42, 0x00, 0xD1, 0x52, 0x1C, + 0xC9, 0x1A, 0x04, 0x1B, 0x61, 0x43, 0xD2, 0x1A, 0x91, 0xFB, 0xF2, 0xF1, + 0x40, 0x1A, 0x10, 0xBD, 0xD2, 0x49, 0xD3, 0x4A, 0x0B, 0x78, 0xC1, 0x78, + 0x00, 0x2B, 0x02, 0xD0, 0x41, 0xF0, 0x04, 0x01, 0x01, 0xE0, 0x21, 0xF0, + 0x04, 0x01, 0xC1, 0x70, 0x16, 0xD0, 0x41, 0xF0, 0x04, 0x01, 0xC1, 0x70, + 0x13, 0x68, 0x93, 0xF8, 0xB0, 0x31, 0x4F, 0xEA, 0x93, 0x03, 0x63, 0xF3, + 0x01, 0x01, 0xC1, 0x70, 0x11, 0x68, 0x91, 0xF8, 0xB2, 0x11, 0x41, 0x70, + 0x11, 0x68, 0x91, 0xF8, 0xB1, 0x11, 0x01, 0x70, 0x11, 0x68, 0x91, 0xF8, + 0xB0, 0x11, 0x15, 0xE0, 0x21, 0xF0, 0x04, 0x01, 0xC1, 0x70, 0x13, 0x68, + 0x93, 0xF8, 0xAD, 0x31, 0x4F, 0xEA, 0x93, 0x03, 0x63, 0xF3, 0x01, 0x01, + 0xC1, 0x70, 0x11, 0x68, 0x91, 0xF8, 0xAF, 0x11, 0x41, 0x70, 0x11, 0x68, + 0x91, 0xF8, 0xAE, 0x11, 0x01, 0x70, 0x11, 0x68, 0x91, 0xF8, 0xAD, 0x11, + 0x4F, 0xEA, 0x11, 0x11, 0x81, 0x70, 0x11, 0x68, 0x91, 0xF8, 0xB3, 0x21, + 0x42, 0x71, 0x91, 0xF8, 0xB4, 0x11, 0x01, 0x71, 0x70, 0x47, 0x2D, 0xE9, + 0xF0, 0x41, 0xB0, 0x4F, 0x05, 0x46, 0xDF, 0xF8, 0xC4, 0x82, 0x38, 0x68, + 0x0C, 0x46, 0x16, 0x46, 0x90, 0xF8, 0x81, 0x01, 0x80, 0x06, 0x26, 0xD5, + 0x95, 0xF8, 0x24, 0x00, 0x29, 0x46, 0x03, 0x28, 0x0A, 0xD0, 0x02, 0x28, + 0x08, 0xD0, 0x04, 0x28, 0x0D, 0xD0, 0x06, 0x28, 0x13, 0xD0, 0x07, 0x28, + 0x11, 0xD0, 0x08, 0x28, 0x0F, 0xD0, 0x13, 0xE0, 0xA4, 0x4A, 0x91, 0xF9, + 0x26, 0x00, 0x0C, 0x32, 0x07, 0xF0, 0x82, 0xFE, 0x0C, 0xE0, 0xA1, 0x4A, + 0x95, 0xF9, 0x26, 0x00, 0x0C, 0x32, 0x21, 0x46, 0x07, 0xF0, 0xB5, 0xFE, + 0x04, 0xE0, 0x95, 0xF9, 0x26, 0x00, 0x21, 0x46, 0x07, 0xF0, 0xD5, 0xFF, + 0x21, 0x68, 0xC8, 0xF8, 0x04, 0x10, 0x38, 0x68, 0x90, 0xF8, 0x81, 0x01, + 0x40, 0x06, 0x2A, 0xD5, 0xEA, 0x6D, 0x21, 0x46, 0x52, 0x1C, 0xEA, 0x65, + 0x95, 0xF8, 0x24, 0x00, 0x03, 0x28, 0x08, 0xD0, 0x05, 0x2A, 0x16, 0xDD, + 0x04, 0x28, 0x0C, 0xD0, 0x06, 0x28, 0x0A, 0xD0, 0x07, 0x28, 0x08, 0xD0, + 0x16, 0xE0, 0x95, 0xF9, 0x26, 0x00, 0x8E, 0x4A, 0x29, 0x46, 0x08, 0xF0, + 0x3C, 0xF8, 0x00, 0x21, 0x05, 0xE0, 0x95, 0xF9, 0x26, 0x00, 0x8A, 0x4A, + 0x08, 0xF0, 0xE8, 0xFA, 0x06, 0x21, 0xE9, 0x65, 0x06, 0xE0, 0x04, 0x28, + 0x04, 0xD1, 0x95, 0xF9, 0x26, 0x00, 0x85, 0x4A, 0x08, 0xF0, 0x7F, 0xF8, + 0x21, 0x68, 0xC8, 0xF8, 0x08, 0x10, 0x38, 0x68, 0x90, 0xF8, 0x81, 0x01, + 0xC0, 0x07, 0x2A, 0xD0, 0x95, 0xF8, 0x24, 0x00, 0x03, 0x28, 0x06, 0xD0, + 0x04, 0x28, 0x0A, 0xD0, 0x07, 0x28, 0x17, 0xD0, 0x08, 0x28, 0x1B, 0xD0, + 0x1F, 0xE0, 0x95, 0xF9, 0x26, 0x00, 0x21, 0x46, 0x07, 0xF0, 0xC1, 0xFF, + 0x19, 0xE0, 0x29, 0x46, 0x28, 0x1D, 0x00, 0xF0, 0xAF, 0xFC, 0x01, 0x46, + 0x28, 0x46, 0xFF, 0xF7, 0xF1, 0xFE, 0x02, 0x46, 0x95, 0xF9, 0x26, 0x00, + 0x21, 0x46, 0x07, 0xF0, 0xB7, 0xFF, 0x0A, 0xE0, 0x95, 0xF9, 0x26, 0x00, + 0x21, 0x46, 0x07, 0xF0, 0xCC, 0xFF, 0x04, 0xE0, 0x95, 0xF9, 0x26, 0x00, + 0x21, 0x46, 0x07, 0xF0, 0xCB, 0xFF, 0x38, 0x68, 0xB0, 0xF9, 0x16, 0x10, + 0xB4, 0xF9, 0x02, 0x00, 0x05, 0xF0, 0x3F, 0xFD, 0x60, 0x80, 0x38, 0x68, + 0xB0, 0xF9, 0x18, 0x10, 0xB4, 0xF9, 0x00, 0x00, 0x05, 0xF0, 0x37, 0xFD, + 0x20, 0x80, 0x95, 0xF8, 0x24, 0x00, 0x03, 0x28, 0x0A, 0xD0, 0x04, 0x28, + 0x0C, 0xD0, 0x07, 0x28, 0x01, 0xD0, 0x08, 0x28, 0x02, 0xD1, 0xB5, 0xF8, + 0x3F, 0x00, 0x30, 0x80, 0xBD, 0xE8, 0xF0, 0x81, 0x30, 0x88, 0xA5, 0xF8, + 0x3F, 0x00, 0xF9, 0xE7, 0x38, 0x68, 0x35, 0xF8, 0x3F, 0x2F, 0x90, 0xF8, + 0x8E, 0x01, 0x00, 0xF0, 0x0F, 0x00, 0xC0, 0xF1, 0x10, 0x01, 0x42, 0x43, + 0x30, 0x88, 0x00, 0xFB, 0x01, 0x20, 0x00, 0x09, 0x28, 0x80, 0xE8, 0xE7, + 0x70, 0xB5, 0x04, 0x46, 0x90, 0xF8, 0x47, 0x00, 0x0D, 0x46, 0x7F, 0x28, + 0x0F, 0xD1, 0x21, 0x46, 0x20, 0x1D, 0x00, 0xF0, 0x5B, 0xFC, 0x01, 0x46, + 0x20, 0x46, 0x00, 0xF0, 0x46, 0xF9, 0x40, 0xB2, 0x84, 0xF8, 0x47, 0x00, + 0x0F, 0x28, 0x02, 0xDD, 0x0F, 0x20, 0x84, 0xF8, 0x47, 0x00, 0x94, 0xF9, + 0x47, 0x00, 0xB5, 0xF9, 0x02, 0x20, 0xC0, 0xF1, 0x10, 0x01, 0x51, 0x43, + 0xB4, 0xF9, 0x12, 0x20, 0x00, 0xFB, 0x02, 0x10, 0x00, 0x11, 0x68, 0x80, + 0x94, 0xF9, 0x47, 0x00, 0xB5, 0xF9, 0x00, 0x20, 0xC0, 0xF1, 0x10, 0x01, + 0x51, 0x43, 0xB4, 0xF9, 0x10, 0x20, 0x00, 0xFB, 0x02, 0x10, 0x36, 0x49, + 0x00, 0x11, 0x28, 0x80, 0x09, 0x68, 0x94, 0xF8, 0x47, 0x00, 0x91, 0xF8, + 0x8F, 0x11, 0x01, 0xF0, 0x0F, 0x01, 0x40, 0x1A, 0x40, 0xB2, 0x84, 0xF8, + 0x47, 0x00, 0x00, 0x28, 0x02, 0xDA, 0x00, 0x20, 0x84, 0xF8, 0x47, 0x00, + 0x28, 0x68, 0x20, 0x61, 0x70, 0xBD, 0xF8, 0xB5, 0x04, 0x46, 0x29, 0x48, + 0x29, 0x4F, 0x0D, 0x46, 0x01, 0x78, 0x38, 0x68, 0x01, 0x26, 0xE1, 0xB1, + 0x90, 0xF8, 0xA2, 0x11, 0xAD, 0xF8, 0x02, 0x10, 0x90, 0xF8, 0xA3, 0x01, + 0xAD, 0xF8, 0x00, 0x00, 0x6A, 0x46, 0x29, 0x46, 0x04, 0xF1, 0x10, 0x00, + 0x00, 0xF0, 0xE9, 0xF8, 0xA0, 0xB1, 0x39, 0x68, 0xB4, 0xF8, 0x41, 0x00, + 0x91, 0xF8, 0xC4, 0x11, 0x01, 0xF0, 0x0F, 0x02, 0x90, 0x42, 0x1B, 0xD2, + 0x00, 0xF1, 0x01, 0x00, 0xA4, 0xF8, 0x41, 0x00, 0x2C, 0xE0, 0x90, 0xF8, + 0xA0, 0x11, 0xAD, 0xF8, 0x02, 0x10, 0x90, 0xF8, 0xA1, 0x01, 0xE1, 0xE7, + 0xB4, 0xF8, 0x43, 0x00, 0x38, 0xB1, 0xA0, 0xF1, 0x01, 0x00, 0xA4, 0xF8, + 0x43, 0x00, 0x20, 0x69, 0x28, 0x60, 0x38, 0x68, 0x15, 0xE0, 0x28, 0x68, + 0x20, 0x61, 0x4F, 0xF0, 0x00, 0x00, 0xE5, 0xE7, 0x07, 0xD1, 0x09, 0x07, + 0x05, 0xD0, 0x40, 0x1C, 0xA4, 0xF8, 0x41, 0x00, 0x28, 0x68, 0x20, 0x61, + 0x0C, 0xE0, 0x20, 0x69, 0x28, 0x60, 0x38, 0x68, 0x90, 0xF8, 0xC4, 0x11, + 0x09, 0x09, 0xA4, 0xF8, 0x43, 0x10, 0x90, 0xF8, 0x81, 0x01, 0x40, 0x07, + 0x00, 0xD4, 0x00, 0x26, 0x30, 0x46, 0xF8, 0xBD, 0xB8, 0x23, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x6D, 0x23, 0x10, 0x00, 0x89, 0x23, 0x10, 0x00, + 0x28, 0x49, 0x10, 0x00, 0x70, 0xB5, 0x04, 0x46, 0xFD, 0x4E, 0x0D, 0x46, + 0x01, 0x20, 0x94, 0xF8, 0x24, 0x10, 0x32, 0x68, 0x03, 0x46, 0x03, 0x29, + 0x08, 0xD0, 0x04, 0x29, 0x28, 0xD0, 0x07, 0x29, 0x26, 0xD0, 0x08, 0x29, + 0x01, 0xD1, 0xE1, 0x68, 0x29, 0x60, 0x70, 0xBD, 0x29, 0x68, 0x21, 0x61, + 0x92, 0xF8, 0x80, 0x11, 0x8D, 0x06, 0x4F, 0xF0, 0x00, 0x01, 0x06, 0xD5, + 0x92, 0xF8, 0xC4, 0x51, 0x2D, 0x09, 0xA4, 0xF8, 0x43, 0x50, 0xA4, 0xF8, + 0x41, 0x10, 0x92, 0xF8, 0x81, 0x51, 0x2D, 0x07, 0x06, 0xD5, 0x84, 0xF8, + 0x45, 0x10, 0x84, 0xF8, 0x46, 0x30, 0x7F, 0x23, 0x84, 0xF8, 0x47, 0x30, + 0x92, 0xF8, 0x80, 0x21, 0xD2, 0x06, 0xE0, 0xD5, 0x24, 0xF8, 0x48, 0x1F, + 0x61, 0x80, 0x70, 0xBD, 0x92, 0xF8, 0x81, 0x11, 0x09, 0x07, 0x08, 0xD5, + 0x29, 0x46, 0x20, 0x46, 0x00, 0xF0, 0xA0, 0xF8, 0x94, 0xF8, 0x45, 0x10, + 0x01, 0x29, 0x02, 0xD0, 0x70, 0xBD, 0x84, 0xF8, 0x45, 0x30, 0x31, 0x68, + 0x91, 0xF8, 0x80, 0x11, 0x8A, 0x06, 0x04, 0xD5, 0x29, 0x46, 0x20, 0x46, + 0xBD, 0xE8, 0x70, 0x40, 0x51, 0xE7, 0xC9, 0x06, 0xF0, 0xD5, 0x29, 0x46, + 0x20, 0x46, 0xBD, 0xE8, 0x70, 0x40, 0x00, 0xF0, 0xC8, 0xB8, 0x10, 0xB5, + 0xD3, 0x49, 0xD2, 0x4C, 0x00, 0x20, 0x08, 0x60, 0x20, 0x68, 0x90, 0xF8, + 0x81, 0x01, 0x80, 0x06, 0x05, 0xD5, 0xD0, 0x48, 0xFF, 0xF7, 0xEC, 0xFD, + 0xCE, 0x48, 0x07, 0xF0, 0xCD, 0xFC, 0x20, 0x68, 0x90, 0xF8, 0x81, 0x01, + 0x40, 0x06, 0x05, 0xD5, 0xCB, 0x48, 0x00, 0xF0, 0x25, 0xF8, 0xCA, 0x48, + 0x07, 0xF0, 0x9E, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0x07, 0xF0, 0x55, 0xBE, + 0x38, 0xB5, 0x05, 0x46, 0xC2, 0x4C, 0x00, 0x20, 0x00, 0x90, 0x21, 0x68, + 0x91, 0xF8, 0x81, 0x01, 0x80, 0x06, 0x02, 0xD5, 0xC0, 0x48, 0xFF, 0xF7, + 0xCD, 0xFD, 0x20, 0x68, 0x90, 0xF8, 0x81, 0x01, 0x40, 0x06, 0x02, 0xD5, + 0xBD, 0x48, 0x00, 0xF0, 0x09, 0xF8, 0x69, 0x46, 0x28, 0x46, 0x00, 0xF0, + 0xE7, 0xF8, 0x69, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x4A, 0xF9, 0x38, 0xBD, + 0xB4, 0x49, 0x0A, 0x68, 0xB2, 0xF8, 0xB6, 0x21, 0x02, 0x80, 0x09, 0x68, + 0xD1, 0xF8, 0xB8, 0x21, 0x42, 0x60, 0x91, 0xF8, 0xB5, 0x11, 0x01, 0x72, + 0x70, 0x47, 0x10, 0xB5, 0xB2, 0xF9, 0x02, 0x30, 0xB2, 0xF9, 0x00, 0x20, + 0x5B, 0x43, 0x02, 0xFB, 0x02, 0x34, 0x00, 0xF0, 0x17, 0xFB, 0xA0, 0x42, + 0x01, 0xDD, 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0xAA, 0x48, + 0x10, 0xB5, 0x03, 0x78, 0xA4, 0x48, 0x02, 0x68, 0x83, 0xB1, 0x92, 0xF8, + 0x99, 0x01, 0x92, 0xF8, 0x96, 0x31, 0x00, 0xF0, 0x0F, 0x04, 0x00, 0x09, + 0x92, 0xF8, 0x97, 0x21, 0x5B, 0x43, 0x52, 0x43, 0x99, 0x42, 0x02, 0xDD, + 0x91, 0x42, 0x0C, 0xDB, 0x20, 0x46, 0x10, 0xBD, 0x92, 0xF8, 0x98, 0x01, + 0x92, 0xF8, 0x94, 0x31, 0x00, 0xF0, 0x0F, 0x04, 0x00, 0x09, 0x5B, 0x43, + 0x92, 0xF8, 0x95, 0x21, 0xED, 0xE7, 0x9A, 0x42, 0x00, 0xD1, 0x52, 0x1C, + 0xC9, 0x1A, 0x04, 0x1B, 0x61, 0x43, 0xD2, 0x1A, 0x91, 0xFB, 0xF2, 0xF1, + 0x40, 0x1A, 0x10, 0xBD, 0xF8, 0xB5, 0x04, 0x46, 0x90, 0xF9, 0x47, 0x00, + 0x0F, 0x46, 0x01, 0x25, 0x20, 0xB1, 0x94, 0xF8, 0x46, 0x10, 0x01, 0x29, + 0x04, 0xD0, 0x2C, 0xE0, 0x01, 0x20, 0x84, 0xF8, 0x45, 0x00, 0xF8, 0xBD, + 0x8B, 0x48, 0x87, 0x4E, 0x01, 0x78, 0x30, 0x68, 0xB9, 0xB1, 0x90, 0xF8, + 0x92, 0x11, 0xAD, 0xF8, 0x02, 0x10, 0x90, 0xF8, 0x93, 0x01, 0xAD, 0xF8, + 0x00, 0x00, 0x6A, 0x46, 0x39, 0x46, 0x04, 0xF1, 0x10, 0x00, 0xFF, 0xF7, + 0xA0, 0xFF, 0x78, 0xB1, 0x20, 0x69, 0x38, 0x60, 0x30, 0x68, 0x90, 0xF8, + 0x81, 0x01, 0x40, 0x07, 0x13, 0xD4, 0x00, 0x25, 0x11, 0xE0, 0x90, 0xF8, + 0x90, 0x11, 0xAD, 0xF8, 0x02, 0x10, 0x90, 0xF8, 0x91, 0x01, 0xE6, 0xE7, + 0x00, 0x20, 0x84, 0xF8, 0x46, 0x00, 0x94, 0xF9, 0x47, 0x00, 0x00, 0x28, + 0x03, 0xDD, 0x39, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0x3F, 0xFE, 0x28, 0x46, + 0xF8, 0xBD, 0xF0, 0xB5, 0x72, 0x4A, 0x6E, 0x4E, 0x01, 0x25, 0x13, 0x78, + 0x32, 0x68, 0xA3, 0xB1, 0x92, 0xF8, 0xC2, 0x31, 0x92, 0xF8, 0xC3, 0x41, + 0xB1, 0xF9, 0x02, 0x20, 0xB0, 0xF9, 0x12, 0x70, 0xD2, 0x1B, 0x00, 0xD5, + 0x52, 0x42, 0x12, 0xB2, 0x00, 0x27, 0x01, 0x2A, 0x0A, 0xDB, 0xB0, 0xF8, + 0x48, 0xC0, 0x62, 0x44, 0xA0, 0xF8, 0x48, 0x20, 0x06, 0xE0, 0x92, 0xF8, + 0xC0, 0x31, 0x92, 0xF8, 0xC1, 0x41, 0xE9, 0xE7, 0xA0, 0xF8, 0x48, 0x70, + 0xB1, 0xF9, 0x00, 0x20, 0xB0, 0xF9, 0x10, 0xC0, 0xB2, 0xEB, 0x0C, 0x02, + 0x00, 0xD5, 0x52, 0x42, 0x12, 0xB2, 0x01, 0x2A, 0x05, 0xDB, 0xB0, 0xF8, + 0x4A, 0xC0, 0x62, 0x44, 0xA0, 0xF8, 0x4A, 0x20, 0x01, 0xE0, 0xA0, 0xF8, + 0x4A, 0x70, 0xB0, 0xF8, 0x48, 0x20, 0x9A, 0x42, 0x03, 0xD8, 0xB0, 0xF8, + 0x4A, 0xC0, 0xA4, 0x45, 0x0E, 0xD9, 0x9A, 0x42, 0x03, 0xD9, 0x4A, 0x88, + 0x42, 0x82, 0xA0, 0xF8, 0x48, 0x70, 0xB0, 0xF8, 0x4A, 0x20, 0xA2, 0x42, + 0x0C, 0xD9, 0x09, 0x88, 0x01, 0x82, 0xA0, 0xF8, 0x4A, 0x70, 0x07, 0xE0, + 0x00, 0x69, 0x08, 0x60, 0x30, 0x68, 0x90, 0xF8, 0x81, 0x01, 0x40, 0x07, + 0x00, 0xD4, 0x00, 0x25, 0x28, 0x46, 0xF0, 0xBD, 0x2D, 0xE9, 0xFC, 0x47, + 0x0E, 0x46, 0xDF, 0xF8, 0x0C, 0xA1, 0x00, 0x21, 0x80, 0x46, 0x0A, 0x25, + 0x4F, 0xF0, 0x01, 0x09, 0xCA, 0xF8, 0x00, 0x10, 0x55, 0xE0, 0xD8, 0xF8, + 0x00, 0x10, 0x09, 0xFA, 0x05, 0xF0, 0x01, 0x42, 0x4F, 0xD0, 0x65, 0x20, + 0x05, 0xFB, 0x00, 0x84, 0x94, 0xF8, 0x2D, 0x00, 0x09, 0x34, 0x03, 0x28, + 0x05, 0xD0, 0x04, 0x28, 0x03, 0xD0, 0x07, 0x28, 0x01, 0xD0, 0x08, 0x28, + 0x41, 0xD1, 0x20, 0x68, 0x00, 0x90, 0x34, 0x48, 0x21, 0x68, 0x0C, 0x38, + 0x01, 0x27, 0x01, 0x60, 0xB4, 0xF8, 0x3D, 0x00, 0xAD, 0xF8, 0x04, 0x00, + 0x21, 0x46, 0x20, 0x1D, 0x00, 0xF0, 0x1C, 0xFA, 0xDA, 0xF8, 0x00, 0x20, + 0x90, 0x42, 0x01, 0xDD, 0xCA, 0xF8, 0x00, 0x00, 0x01, 0xAA, 0x69, 0x46, + 0x20, 0x46, 0xFF, 0xF7, 0xE4, 0xFC, 0x26, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0x81, 0x01, 0x80, 0x07, 0x04, 0xD5, 0x69, 0x46, 0x20, 0x46, 0xFF, 0xF7, + 0x45, 0xFE, 0x07, 0x46, 0x94, 0xF8, 0x25, 0x00, 0x04, 0x28, 0x07, 0xD1, + 0x23, 0x48, 0x00, 0x78, 0x20, 0xB9, 0x94, 0xF8, 0x24, 0x00, 0x08, 0x28, + 0x00, 0xD0, 0x00, 0x27, 0x00, 0x98, 0x60, 0x61, 0xBD, 0xF8, 0x04, 0x00, + 0xA4, 0xF8, 0x3F, 0x00, 0x01, 0x2F, 0x08, 0xD1, 0x00, 0x98, 0xE0, 0x60, + 0x94, 0xF8, 0x26, 0x20, 0x31, 0x68, 0x09, 0xFA, 0x02, 0xF0, 0x01, 0x43, + 0x31, 0x60, 0x6D, 0x1E, 0xA7, 0xD2, 0xBD, 0xE8, 0xFC, 0x87, 0x2D, 0xE9, + 0xF0, 0x47, 0x89, 0x46, 0x07, 0x46, 0x04, 0x21, 0x00, 0x1D, 0x0A, 0xF0, + 0xB3, 0xFA, 0xC6, 0xB2, 0x00, 0x24, 0x4F, 0xF0, 0x01, 0x08, 0x39, 0x68, + 0x08, 0xFA, 0x04, 0xF0, 0x01, 0x42, 0x49, 0xD0, 0x65, 0x20, 0x04, 0xFB, + 0x00, 0x75, 0x95, 0xF8, 0x2D, 0x00, 0x09, 0x35, 0x03, 0x28, 0x13, 0xD0, + 0x04, 0x28, 0x17, 0xD0, 0x07, 0x28, 0x15, 0xD0, 0x08, 0x28, 0x13, 0xD0, + 0x3A, 0xE0, 0x00, 0x00, 0x50, 0x24, 0x10, 0x00, 0xDC, 0x23, 0x10, 0x00, + 0x95, 0x23, 0x10, 0x00, 0x28, 0x49, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0x2A, 0x24, 0x10, 0x00, 0x29, 0x46, 0x38, 0x46, 0x00, 0xF0, 0xA8, 0xF8, + 0x76, 0x1C, 0xF6, 0xB2, 0x95, 0xF8, 0x26, 0x20, 0xD9, 0xF8, 0x00, 0x10, + 0x08, 0xFA, 0x02, 0xF0, 0x01, 0x42, 0x13, 0xD0, 0x95, 0xF8, 0x24, 0x00, + 0x08, 0x28, 0x01, 0xD1, 0x76, 0x1E, 0xF6, 0xB2, 0x31, 0x46, 0x28, 0x46, + 0x04, 0xF0, 0x43, 0xFD, 0x0D, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xE8, 0x07, + 0xC0, 0x06, 0x03, 0xD5, 0x31, 0x46, 0x28, 0x46, 0x04, 0xF0, 0xC4, 0xFD, + 0x95, 0xF8, 0x24, 0x00, 0x08, 0x28, 0x07, 0xD1, 0x29, 0x46, 0x38, 0x46, + 0x00, 0xF0, 0x97, 0xF8, 0x29, 0x46, 0x38, 0x46, 0x00, 0xF0, 0x72, 0xF8, + 0x64, 0x1C, 0x0A, 0x2C, 0xAD, 0xDB, 0xBD, 0xE8, 0xF0, 0x87, 0x00, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x10, 0xB5, 0x4F, 0xF4, 0x7F, 0x71, 0x49, 0x48, + 0x08, 0xF0, 0x99, 0xF9, 0x4F, 0xF4, 0x44, 0x71, 0x47, 0x48, 0x08, 0xF0, + 0x94, 0xF9, 0x00, 0xF0, 0xEE, 0xFA, 0x00, 0xF0, 0x8D, 0xF8, 0xFF, 0xF7, + 0x80, 0xFA, 0xBD, 0xE8, 0x10, 0x40, 0xFF, 0xF7, 0x02, 0xBE, 0x10, 0xB5, + 0x41, 0x48, 0x00, 0x24, 0x44, 0x72, 0x3E, 0x48, 0x00, 0xF0, 0xE3, 0xFA, + 0x3C, 0x48, 0xFF, 0xF7, 0x78, 0xFA, 0x3B, 0x48, 0xFF, 0xF7, 0x14, 0xFE, + 0x3A, 0x48, 0x04, 0x70, 0x10, 0xBD, 0x10, 0xB5, 0x38, 0x4C, 0x20, 0x78, + 0x88, 0xB1, 0x38, 0x49, 0x48, 0x72, 0x21, 0x46, 0x34, 0x48, 0x00, 0xF0, + 0x30, 0xFB, 0x33, 0x48, 0x00, 0xF0, 0x6B, 0xF8, 0x31, 0x48, 0xFF, 0xF7, + 0x62, 0xFA, 0x30, 0x48, 0xFF, 0xF7, 0xFE, 0xFD, 0x00, 0x20, 0x20, 0x70, + 0x10, 0xBD, 0xBD, 0xE8, 0x10, 0x40, 0xD6, 0xE7, 0x10, 0xB5, 0x2C, 0x4C, + 0x01, 0x46, 0x20, 0x78, 0x0F, 0x28, 0x0C, 0xD2, 0x00, 0xEB, 0x80, 0x02, + 0x02, 0xEB, 0xC0, 0x00, 0x04, 0xEB, 0x80, 0x00, 0x34, 0x22, 0x00, 0x1D, + 0x08, 0xF0, 0xF5, 0xF8, 0x20, 0x78, 0x40, 0x1C, 0x20, 0x70, 0x10, 0xBD, + 0x22, 0x48, 0x00, 0x78, 0x70, 0x47, 0x21, 0x48, 0x70, 0x47, 0x10, 0xB5, + 0x00, 0x21, 0x02, 0x68, 0x01, 0x24, 0x04, 0xFA, 0x01, 0xF3, 0x1A, 0x42, + 0x04, 0xD1, 0x8C, 0x40, 0x22, 0x43, 0x02, 0x60, 0x48, 0xB2, 0x10, 0xBD, + 0x49, 0x1C, 0x0A, 0x29, 0xF3, 0xDB, 0x4F, 0xF0, 0xFF, 0x30, 0x10, 0xBD, + 0x10, 0xB5, 0x91, 0xF8, 0x26, 0x40, 0x01, 0x23, 0x02, 0x68, 0xA3, 0x40, + 0x9A, 0x43, 0x02, 0x60, 0x00, 0x20, 0x81, 0xF8, 0x24, 0x00, 0x10, 0xBD, + 0x30, 0xB5, 0x00, 0x22, 0x43, 0x68, 0x01, 0x25, 0x05, 0xFA, 0x02, 0xF4, + 0x23, 0x42, 0x05, 0xD1, 0x95, 0x40, 0x2B, 0x43, 0x43, 0x60, 0x81, 0xF8, + 0x27, 0x20, 0x30, 0xBD, 0x52, 0x1C, 0x0A, 0x2A, 0xF2, 0xDB, 0xFF, 0x20, + 0x81, 0xF8, 0x27, 0x00, 0x30, 0xBD, 0x10, 0xB5, 0x11, 0xF8, 0x27, 0x4F, + 0x42, 0x68, 0x01, 0x23, 0xA3, 0x40, 0x9A, 0x43, 0x42, 0x60, 0xFF, 0x20, + 0x08, 0x70, 0x10, 0xBD, 0x34, 0x49, 0x10, 0x00, 0x30, 0x4D, 0x10, 0x00, + 0x58, 0x25, 0x10, 0x00, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0x0A, 0x25, + 0x06, 0x46, 0x01, 0x27, 0xDF, 0xF8, 0xA0, 0x80, 0x49, 0xE0, 0x30, 0x68, + 0x07, 0xFA, 0x05, 0xF1, 0x08, 0x42, 0x44, 0xD0, 0x65, 0x20, 0x05, 0xFB, + 0x00, 0x64, 0x94, 0xF8, 0x2D, 0x00, 0x09, 0x34, 0x01, 0x28, 0x01, 0xD0, + 0x02, 0x28, 0x3A, 0xD1, 0x94, 0xF8, 0x25, 0x00, 0x02, 0x28, 0x06, 0xD0, + 0x03, 0x28, 0x04, 0xD0, 0x01, 0x28, 0x1E, 0xD0, 0x04, 0x28, 0x30, 0xD1, + 0x25, 0xE0, 0x1B, 0x48, 0x94, 0xF8, 0x2D, 0x10, 0xB0, 0xF9, 0x00, 0x00, + 0xC9, 0x06, 0x02, 0xD5, 0x18, 0x48, 0xB0, 0xF9, 0x00, 0x00, 0xB4, 0xF9, + 0x4C, 0x10, 0x81, 0x42, 0x21, 0xDA, 0xB8, 0xF9, 0x00, 0x00, 0x81, 0x42, + 0x19, 0xDB, 0x00, 0xF0, 0x67, 0xFF, 0x01, 0x28, 0x15, 0xD1, 0x04, 0x20, + 0x84, 0xF8, 0x25, 0x00, 0x10, 0x48, 0x07, 0x70, 0x13, 0xE0, 0xB4, 0xF8, + 0x2D, 0x10, 0xB4, 0xF9, 0x4E, 0x00, 0x00, 0x22, 0x01, 0xF0, 0xA6, 0xFE, + 0xB4, 0xF9, 0x4C, 0x10, 0x03, 0xE0, 0xB4, 0xF9, 0x4C, 0x10, 0xB8, 0xF9, + 0x00, 0x00, 0x81, 0x42, 0x03, 0xDA, 0x21, 0x46, 0x30, 0x46, 0xFF, 0xF7, + 0x7B, 0xFF, 0x6D, 0x1E, 0xB3, 0xD2, 0xBD, 0xE8, 0xF0, 0x81, 0x00, 0x00, + 0xCA, 0x23, 0x10, 0x00, 0xC6, 0x23, 0x10, 0x00, 0xD4, 0x23, 0x10, 0x00, + 0x07, 0x24, 0x10, 0x00, 0x70, 0xB5, 0x16, 0x46, 0x0D, 0x46, 0x04, 0x46, + 0x01, 0x2B, 0x05, 0xD0, 0x00, 0xF0, 0xCA, 0xFA, 0xA8, 0x7B, 0x01, 0x28, + 0x28, 0xD0, 0x29, 0xE0, 0x01, 0x20, 0x84, 0xF8, 0x24, 0x00, 0xA8, 0x7B, + 0x84, 0xF8, 0x25, 0x00, 0x00, 0x20, 0xA4, 0xF8, 0x2F, 0x00, 0xA0, 0x65, + 0x84, 0xF8, 0x60, 0x00, 0xA4, 0xF8, 0x61, 0x00, 0xA4, 0xF8, 0x63, 0x00, + 0x84, 0xF8, 0x2C, 0x00, 0xA8, 0x7B, 0x01, 0x28, 0x01, 0xD1, 0x68, 0x68, + 0x28, 0x60, 0x21, 0x46, 0x03, 0x20, 0x03, 0xE0, 0x01, 0xEB, 0x80, 0x02, + 0x2B, 0x68, 0x13, 0x60, 0x40, 0x1E, 0xF9, 0xD2, 0xF7, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0xA3, 0x13, 0x02, 0x20, 0x03, 0xF0, 0x02, 0xFD, 0x0E, 0xE0, + 0x68, 0x68, 0x28, 0x60, 0xA0, 0x6D, 0x40, 0x1C, 0xA0, 0x65, 0x02, 0x20, + 0x04, 0xEB, 0x80, 0x01, 0x40, 0x1E, 0x51, 0xF8, 0x04, 0x2C, 0x0A, 0x60, + 0xF8, 0xD1, 0x29, 0x68, 0x21, 0x60, 0x84, 0xF8, 0x26, 0x60, 0x68, 0x7E, + 0x84, 0xF8, 0x28, 0x00, 0x28, 0x7E, 0x84, 0xF8, 0x2B, 0x00, 0x68, 0x8C, + 0xA4, 0xF8, 0x37, 0x00, 0xA8, 0x8B, 0xA4, 0xF8, 0x31, 0x00, 0x28, 0x8C, + 0xA4, 0xF8, 0x35, 0x00, 0xE8, 0x8B, 0xA4, 0xF8, 0x33, 0x00, 0xA8, 0x8C, + 0xA4, 0xF8, 0x39, 0x00, 0xE8, 0x8C, 0xA4, 0xF8, 0x3B, 0x00, 0xA8, 0x8D, + 0xA4, 0xF8, 0x3D, 0x00, 0x28, 0x8D, 0xA4, 0xF8, 0x4C, 0x00, 0xE8, 0x6A, + 0x20, 0x65, 0x28, 0x6B, 0x60, 0x65, 0x68, 0x8D, 0xA4, 0xF8, 0x4E, 0x00, + 0x68, 0x8B, 0xA4, 0xF8, 0x2D, 0x00, 0xD8, 0x48, 0x00, 0x78, 0x10, 0xF0, + 0x30, 0x0F, 0x05, 0xD0, 0xB4, 0xF8, 0x2F, 0x00, 0x40, 0xF0, 0x80, 0x00, + 0xA4, 0xF8, 0x2F, 0x00, 0xD5, 0xF8, 0x0A, 0x00, 0x20, 0x62, 0x70, 0xBD, + 0x10, 0xB5, 0x53, 0x88, 0x4C, 0x88, 0x1B, 0x1B, 0x43, 0x80, 0x12, 0x88, + 0x09, 0x88, 0x51, 0x1A, 0x01, 0x80, 0x10, 0xBD, 0x08, 0xB5, 0x0A, 0x46, + 0x01, 0x46, 0x68, 0x46, 0xFF, 0xF7, 0xF0, 0xFF, 0xBD, 0xF9, 0x00, 0x10, + 0xBD, 0xF9, 0x02, 0x00, 0x49, 0x43, 0x00, 0xFB, 0x00, 0x10, 0x08, 0xBD, + 0x2D, 0xE9, 0xFF, 0x4F, 0xC2, 0x48, 0x88, 0x46, 0xAD, 0xF5, 0x69, 0x7D, + 0x00, 0x68, 0x4F, 0xF0, 0x00, 0x0A, 0x9B, 0x46, 0xC1, 0x8B, 0xCD, 0xF8, + 0x00, 0xA0, 0x01, 0xEB, 0x81, 0x01, 0xC1, 0xF3, 0x97, 0x11, 0x01, 0xFB, + 0x01, 0xF9, 0xBC, 0x49, 0x6F, 0x46, 0x09, 0x78, 0x31, 0xB1, 0xB0, 0xF8, + 0x84, 0x61, 0xBA, 0x4A, 0x11, 0x78, 0x01, 0x29, 0x03, 0xD0, 0x0A, 0xE0, + 0xB0, 0xF8, 0x82, 0x61, 0xF7, 0xE7, 0xB0, 0xF8, 0x86, 0x11, 0x21, 0xB1, + 0x0E, 0x46, 0x00, 0x21, 0x11, 0x70, 0x4F, 0xF0, 0x01, 0x0A, 0xB3, 0x49, + 0x09, 0x78, 0x01, 0x29, 0x01, 0xD1, 0xB0, 0xF8, 0xC9, 0x60, 0x90, 0xF8, + 0x88, 0x01, 0x76, 0x43, 0x00, 0x25, 0xE5, 0x90, 0xE9, 0x98, 0x01, 0x68, + 0x01, 0x20, 0xA8, 0x40, 0x01, 0x42, 0x6C, 0xD0, 0x65, 0x20, 0xE9, 0x99, + 0x68, 0x43, 0x00, 0xEB, 0x01, 0x0C, 0x9C, 0xF8, 0x2D, 0x00, 0x0C, 0xF1, + 0x09, 0x0C, 0x07, 0x28, 0xF3, 0xD0, 0x61, 0x46, 0x08, 0x1D, 0xFF, 0xF7, + 0xA9, 0xFF, 0xE5, 0x99, 0x48, 0x43, 0x00, 0xFB, 0x01, 0xF4, 0xB4, 0x42, + 0x00, 0xDA, 0x34, 0x46, 0x62, 0x46, 0x11, 0x1D, 0xE3, 0xA8, 0xFF, 0xF7, + 0x93, 0xFF, 0x0C, 0xF1, 0x04, 0x02, 0x11, 0x1D, 0xE6, 0xA8, 0xFF, 0xF7, + 0x8D, 0xFF, 0x00, 0x20, 0xAD, 0xF8, 0x8A, 0x03, 0xAD, 0xF8, 0x88, 0x03, + 0x9C, 0xF8, 0x24, 0x00, 0x06, 0x28, 0x07, 0xD1, 0x03, 0x20, 0x8C, 0xF8, + 0x60, 0x00, 0x00, 0x20, 0xAC, 0xF8, 0x61, 0x00, 0xAC, 0xF8, 0x63, 0x00, + 0xDC, 0xF8, 0x58, 0x10, 0x01, 0x29, 0x3D, 0xDD, 0x9C, 0xF8, 0x60, 0x00, + 0x90, 0xBB, 0xE3, 0xAA, 0xE6, 0xA9, 0xE2, 0xA8, 0xFF, 0xF7, 0x70, 0xFF, + 0x00, 0x20, 0xAD, 0xF8, 0x92, 0x03, 0xAD, 0xF8, 0x90, 0x03, 0xBC, 0xF9, + 0x61, 0x00, 0xBD, 0xF9, 0x8A, 0x13, 0x08, 0x44, 0x40, 0x10, 0xAC, 0xF8, + 0x61, 0x00, 0xBC, 0xF9, 0x63, 0x00, 0xBD, 0xF9, 0x88, 0x13, 0x08, 0x44, + 0x40, 0x10, 0xAC, 0xF8, 0x63, 0x00, 0xE3, 0xA9, 0xE4, 0xA8, 0xFF, 0xF7, + 0x61, 0xFF, 0x48, 0x45, 0x05, 0xD8, 0xE2, 0xA9, 0xE4, 0xA8, 0xFF, 0xF7, + 0x5B, 0xFF, 0x48, 0x45, 0x0E, 0xD9, 0xBD, 0xF8, 0x8E, 0x03, 0xBC, 0xF8, + 0x61, 0x10, 0x08, 0x44, 0xAD, 0xF8, 0x8A, 0x03, 0xBD, 0xF8, 0x8C, 0x03, + 0xBC, 0xF8, 0x63, 0x10, 0x08, 0x44, 0x04, 0xE0, 0x07, 0xE0, 0x75, 0xE0, + 0x00, 0x20, 0xAD, 0xF8, 0x8A, 0x03, 0xAD, 0xF8, 0x88, 0x03, 0x3A, 0xE0, + 0x09, 0xD0, 0x9C, 0xF8, 0x60, 0x00, 0x03, 0x28, 0x05, 0xD0, 0x01, 0x28, + 0x03, 0xD0, 0x49, 0xB3, 0x02, 0x28, 0x27, 0xD0, 0x2F, 0xE0, 0x00, 0x20, + 0xAD, 0xF8, 0x92, 0x03, 0xAD, 0xF8, 0x90, 0x03, 0xE3, 0xA9, 0xE4, 0xA8, + 0xFF, 0xF7, 0x30, 0xFF, 0x48, 0x45, 0x24, 0xD9, 0xBD, 0xF8, 0x8E, 0x03, + 0xAD, 0xF8, 0x8A, 0x03, 0xBD, 0xF8, 0x8C, 0x03, 0xAD, 0xF8, 0x88, 0x03, + 0xBA, 0xF1, 0x00, 0x0F, 0x19, 0xD1, 0x9C, 0xF8, 0x60, 0x00, 0x28, 0xB1, + 0xC6, 0xEB, 0xC6, 0x00, 0xA0, 0x42, 0x15, 0xDD, 0x04, 0x46, 0x13, 0xE0, + 0x06, 0xEB, 0xC6, 0x00, 0x00, 0xEB, 0x06, 0x10, 0xA0, 0x42, 0x12, 0xDD, + 0x04, 0x46, 0x10, 0xE0, 0xBA, 0xF1, 0x00, 0x0F, 0x05, 0xD1, 0x02, 0x28, + 0x16, 0xD0, 0x06, 0xEB, 0x06, 0x10, 0x00, 0xEB, 0x46, 0x14, 0x9C, 0xF8, + 0x60, 0x00, 0x20, 0xB1, 0x9C, 0xF8, 0x60, 0x00, 0x40, 0x1E, 0x8C, 0xF8, + 0x60, 0x00, 0xBD, 0xF9, 0x88, 0x33, 0xBD, 0xF9, 0x8A, 0x23, 0x61, 0x46, + 0xE7, 0xA8, 0x00, 0xF0, 0xA5, 0xF9, 0x4F, 0xF0, 0x00, 0x0C, 0x1D, 0xE0, + 0xC6, 0xEB, 0xC6, 0x04, 0xEC, 0xE7, 0x0C, 0xEB, 0x8C, 0x00, 0x00, 0xEB, + 0xCC, 0x01, 0x08, 0xEB, 0x81, 0x01, 0xE7, 0xA8, 0x09, 0x1D, 0xFF, 0xF7, + 0xE9, 0xFE, 0x01, 0x46, 0xA0, 0x42, 0x0B, 0xDA, 0x38, 0x68, 0x00, 0xEB, + 0x40, 0x00, 0x07, 0xEB, 0x40, 0x00, 0x41, 0x60, 0x80, 0xF8, 0x09, 0xC0, + 0x05, 0x72, 0x38, 0x68, 0x40, 0x1C, 0x38, 0x60, 0x0C, 0xF1, 0x01, 0x0C, + 0x98, 0xF8, 0x00, 0x00, 0x60, 0x45, 0xE0, 0xD8, 0x6D, 0x1C, 0x0A, 0x2D, + 0xFF, 0xF4, 0x12, 0xAF, 0x38, 0x68, 0x00, 0x28, 0x41, 0xD0, 0x38, 0x46, + 0x00, 0xF0, 0x52, 0xF9, 0x00, 0x24, 0x39, 0xE0, 0x04, 0xEB, 0x44, 0x00, + 0x07, 0xEB, 0x40, 0x00, 0xEB, 0x9A, 0x90, 0xF9, 0x08, 0x10, 0x01, 0x23, + 0x15, 0x68, 0x90, 0xF9, 0x09, 0x00, 0x03, 0xFA, 0x01, 0xF2, 0x15, 0x42, + 0x29, 0xD1, 0xDB, 0xF8, 0x00, 0x60, 0x83, 0x40, 0x1E, 0x42, 0x24, 0xD1, + 0x15, 0x43, 0xEB, 0x9A, 0x15, 0x60, 0xDB, 0xF8, 0x00, 0x20, 0x1A, 0x43, + 0xCB, 0xF8, 0x00, 0x20, 0x65, 0x22, 0x51, 0x43, 0xE9, 0x9A, 0x00, 0x23, + 0x8D, 0x18, 0x00, 0xEB, 0x80, 0x01, 0x01, 0xEB, 0xC0, 0x01, 0x95, 0xF9, + 0x2F, 0x20, 0x08, 0xEB, 0x81, 0x01, 0x09, 0x35, 0x28, 0x46, 0x09, 0x1D, + 0xFF, 0xF7, 0x14, 0xFE, 0x95, 0xF8, 0x24, 0x00, 0x03, 0x28, 0x03, 0xD0, + 0x06, 0x28, 0x01, 0xD0, 0x04, 0x28, 0x02, 0xD1, 0x04, 0x20, 0x85, 0xF8, + 0x24, 0x00, 0x64, 0x1C, 0x38, 0x68, 0xA0, 0x42, 0xC2, 0xDC, 0x0D, 0xF5, + 0x6D, 0x7D, 0xBD, 0xE8, 0xF0, 0x8F, 0x11, 0x49, 0x00, 0x20, 0x08, 0x70, + 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0x0B, 0x4F, 0x06, 0x46, 0x00, 0x25, + 0x4F, 0xF0, 0x01, 0x08, 0x31, 0x68, 0x08, 0xFA, 0x05, 0xF0, 0x01, 0x42, + 0x4E, 0xD0, 0x65, 0x20, 0x05, 0xFB, 0x00, 0x64, 0x94, 0xF8, 0x2D, 0x00, + 0x09, 0x34, 0x03, 0x28, 0x0E, 0xD0, 0x04, 0x28, 0x0C, 0xD0, 0x02, 0x28, + 0x38, 0xD0, 0x41, 0xE0, 0x50, 0x24, 0x10, 0x00, 0x1C, 0x24, 0x10, 0x00, + 0xB8, 0x23, 0x10, 0x00, 0xC4, 0x23, 0x10, 0x00, 0x6D, 0x23, 0x10, 0x00, + 0x05, 0x20, 0x84, 0xF8, 0x24, 0x00, 0x38, 0x68, 0x90, 0xF8, 0xA4, 0x13, + 0x03, 0x20, 0x03, 0xF0, 0x06, 0xFB, 0x38, 0x68, 0x90, 0xF8, 0x81, 0x11, + 0xC9, 0x06, 0x07, 0xD4, 0x90, 0xF8, 0x80, 0x01, 0xC0, 0x07, 0x25, 0xD0, + 0x9F, 0x48, 0x00, 0x78, 0x01, 0x28, 0x21, 0xD1, 0x21, 0x46, 0x20, 0x1D, + 0xFF, 0xF7, 0x46, 0xFE, 0x01, 0x46, 0x9C, 0x48, 0x00, 0x78, 0x00, 0x28, + 0x38, 0x68, 0x0A, 0xD0, 0xB0, 0xF8, 0xAA, 0x01, 0x40, 0x43, 0x81, 0x42, + 0x12, 0xDD, 0x34, 0xF8, 0x2F, 0x0F, 0x40, 0xF0, 0x02, 0x00, 0x20, 0x80, + 0x0C, 0xE0, 0xB0, 0xF8, 0xA8, 0x01, 0xF3, 0xE7, 0x38, 0x68, 0x90, 0xF8, + 0xA4, 0x13, 0x03, 0x20, 0x03, 0xF0, 0xDB, 0xFA, 0x21, 0x46, 0x30, 0x46, + 0xFF, 0xF7, 0x10, 0xFD, 0x6D, 0x1C, 0x0A, 0x2D, 0xA8, 0xD3, 0xBD, 0xE8, + 0xF0, 0x81, 0x2D, 0xE9, 0xFC, 0x47, 0x05, 0x46, 0x00, 0x68, 0x0E, 0x46, + 0x10, 0xB3, 0x00, 0x20, 0x00, 0x90, 0x01, 0x90, 0x6B, 0x46, 0x01, 0xAA, + 0x28, 0x46, 0xFF, 0xF7, 0x23, 0xFE, 0x00, 0x24, 0xDF, 0xF8, 0x10, 0x92, + 0x4F, 0xF0, 0x05, 0x08, 0x01, 0x27, 0x29, 0x68, 0x07, 0xFA, 0x04, 0xF0, + 0x01, 0x42, 0x35, 0xD0, 0x01, 0x99, 0x08, 0x42, 0x32, 0xD1, 0x65, 0x20, + 0x04, 0xFB, 0x00, 0x51, 0x91, 0xF8, 0x2D, 0x00, 0x09, 0x31, 0x03, 0x28, + 0x1D, 0xD0, 0x04, 0x28, 0x1B, 0xD0, 0x02, 0x28, 0x23, 0xD0, 0x25, 0xE0, + 0x34, 0x78, 0x12, 0xE0, 0x28, 0x46, 0xFF, 0xF7, 0xCA, 0xFC, 0x02, 0x00, + 0x0D, 0xD4, 0x65, 0x20, 0x04, 0xEB, 0x84, 0x01, 0x00, 0xFB, 0x02, 0x50, + 0x01, 0xEB, 0xC4, 0x01, 0x06, 0xEB, 0x81, 0x01, 0x01, 0x23, 0x09, 0x30, + 0x09, 0x1D, 0xFF, 0xF7, 0x5F, 0xFD, 0x64, 0x1E, 0xEA, 0xD2, 0xBD, 0xE8, + 0xFC, 0x87, 0x81, 0xF8, 0x24, 0x80, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0xA4, 0x13, 0x03, 0x20, 0x03, 0xF0, 0x87, 0xFA, 0x02, 0xE0, 0x28, 0x46, + 0xFF, 0xF7, 0xBC, 0xFC, 0x64, 0x1C, 0x0A, 0x2C, 0xC1, 0xD3, 0x34, 0x78, + 0x17, 0xE0, 0x00, 0x99, 0x07, 0xFA, 0x04, 0xF0, 0x08, 0x42, 0x12, 0xD1, + 0x28, 0x46, 0xFF, 0xF7, 0x9C, 0xFC, 0x02, 0x00, 0x0D, 0xD4, 0x65, 0x20, + 0x04, 0xEB, 0x84, 0x01, 0x00, 0xFB, 0x02, 0x50, 0x01, 0xEB, 0xC4, 0x01, + 0x06, 0xEB, 0x81, 0x01, 0x01, 0x23, 0x09, 0x30, 0x09, 0x1D, 0xFF, 0xF7, + 0x31, 0xFD, 0x64, 0x1E, 0xE5, 0xD2, 0xD0, 0xE7, 0x70, 0xB5, 0x04, 0x46, + 0x90, 0xF8, 0x25, 0x00, 0x0D, 0x46, 0x04, 0x28, 0x33, 0xD1, 0xA8, 0x7B, + 0x00, 0x26, 0x04, 0x28, 0x2D, 0xD0, 0x94, 0xF8, 0x24, 0x00, 0x02, 0x28, + 0x14, 0xD0, 0x03, 0x28, 0x01, 0xD0, 0x04, 0x28, 0x27, 0xD1, 0x4B, 0x48, + 0x94, 0xF8, 0x2C, 0x10, 0x00, 0x68, 0x90, 0xF8, 0xFF, 0x01, 0x00, 0xF0, + 0x0F, 0x02, 0x48, 0x48, 0x91, 0x42, 0x00, 0x78, 0x0B, 0xD9, 0x84, 0xF8, + 0x2C, 0x60, 0x01, 0x28, 0x04, 0xD0, 0x11, 0xE0, 0x20, 0x46, 0xFE, 0xF7, + 0x81, 0xFE, 0x09, 0xE0, 0xFE, 0xF7, 0x69, 0xFE, 0x06, 0xE0, 0x01, 0x28, + 0x0D, 0xD1, 0x49, 0x1C, 0x84, 0xF8, 0x2C, 0x10, 0x04, 0x20, 0xA8, 0x73, + 0x94, 0xF8, 0x25, 0x00, 0x04, 0x28, 0x04, 0xD1, 0xA8, 0x7B, 0x04, 0x28, + 0x01, 0xD1, 0x84, 0xF8, 0x2C, 0x60, 0x70, 0xBD, 0x7C, 0xB5, 0x00, 0x22, + 0x1C, 0xE0, 0x02, 0xEB, 0x42, 0x03, 0x51, 0x1C, 0x00, 0xEB, 0x43, 0x03, + 0x12, 0xE0, 0x01, 0xEB, 0x41, 0x04, 0x00, 0xEB, 0x44, 0x04, 0x5E, 0x68, + 0x65, 0x68, 0xAE, 0x42, 0x09, 0xDD, 0x00, 0x96, 0x1E, 0x89, 0xAD, 0xF8, + 0x04, 0x60, 0x5D, 0x60, 0x25, 0x89, 0x1D, 0x81, 0x00, 0x9D, 0x65, 0x60, + 0x26, 0x81, 0x49, 0x1C, 0x04, 0x68, 0x8C, 0x42, 0xE9, 0xDC, 0x52, 0x1C, + 0x01, 0x68, 0x91, 0x42, 0xDF, 0xDC, 0x7C, 0xBD, 0xF0, 0xB5, 0x24, 0x4C, + 0xB1, 0xF9, 0x02, 0x60, 0x24, 0x68, 0x16, 0xEB, 0x02, 0x0C, 0xB4, 0xF9, + 0x16, 0x50, 0xB4, 0xF9, 0x18, 0x70, 0x4F, 0xF4, 0x80, 0x74, 0x01, 0xD5, + 0x35, 0x02, 0x03, 0xE0, 0xAC, 0x45, 0x07, 0xDD, 0xAD, 0x1B, 0x2D, 0x02, + 0x95, 0xFB, 0xF2, 0xF5, 0x00, 0x2D, 0x02, 0xDA, 0x6D, 0x42, 0x00, 0xE0, + 0x25, 0x46, 0xB1, 0xF9, 0x00, 0xC0, 0x1C, 0xEB, 0x03, 0x0E, 0x02, 0xD5, + 0x4F, 0xEA, 0x0C, 0x24, 0x04, 0xE0, 0xBE, 0x45, 0x07, 0xDD, 0xA7, 0xEB, + 0x0C, 0x04, 0x24, 0x02, 0x94, 0xFB, 0xF3, 0xF4, 0x00, 0x2C, 0x00, 0xDA, + 0x64, 0x42, 0xA5, 0x42, 0x06, 0xDA, 0x02, 0xFB, 0x05, 0xF2, 0x4F, 0xEA, + 0x22, 0x22, 0x03, 0xFB, 0x05, 0xF3, 0x06, 0xE0, 0x07, 0xDD, 0x02, 0xFB, + 0x04, 0xF2, 0x4F, 0xEA, 0x22, 0x22, 0x03, 0xFB, 0x04, 0xF3, 0x4F, 0xEA, + 0x23, 0x23, 0x32, 0x44, 0x42, 0x80, 0x09, 0x88, 0x19, 0x44, 0x01, 0x80, + 0xF0, 0xBD, 0x00, 0x00, 0x55, 0x23, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x2A, 0x24, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x47, + 0x99, 0x46, 0x15, 0x46, 0x0F, 0x46, 0x82, 0x46, 0x00, 0x26, 0x94, 0x78, + 0x4F, 0xF0, 0x01, 0x08, 0x16, 0xE0, 0xE1, 0xB2, 0x50, 0x46, 0x09, 0xF0, + 0x6D, 0xFB, 0x03, 0x46, 0x00, 0x21, 0x28, 0x78, 0x95, 0xF8, 0x01, 0xC0, + 0x07, 0xE0, 0x1A, 0x5C, 0xBA, 0x42, 0x03, 0xD1, 0x08, 0xFA, 0x00, 0xF2, + 0x11, 0x43, 0x01, 0x26, 0x40, 0x1C, 0x84, 0x45, 0xF5, 0xD2, 0x49, 0xF8, + 0x24, 0x10, 0x64, 0x1C, 0xE8, 0x78, 0xA0, 0x42, 0xE5, 0xD2, 0x30, 0x46, + 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xFF, 0x4F, 0xFF, 0x48, 0x4F, 0xF0, + 0x00, 0x0B, 0x83, 0xB0, 0x00, 0x68, 0x89, 0x46, 0x4F, 0xF4, 0x80, 0x3A, + 0xB0, 0xF8, 0x4C, 0x01, 0x00, 0x90, 0xD8, 0x46, 0x5D, 0x46, 0x5F, 0x46, + 0x5E, 0x46, 0x8C, 0x78, 0x26, 0xE0, 0x05, 0x98, 0xE1, 0xB2, 0x00, 0x69, + 0x09, 0xF0, 0x46, 0xFB, 0x99, 0xF8, 0x00, 0x10, 0x4F, 0xF0, 0x01, 0x0E, + 0x99, 0xF8, 0x01, 0x30, 0x17, 0xE0, 0x03, 0x9A, 0x52, 0xF8, 0x24, 0xC0, + 0x0E, 0xFA, 0x01, 0xF2, 0x1C, 0xEA, 0x02, 0x0F, 0x0E, 0xD0, 0x30, 0xF9, + 0x11, 0x20, 0xDD, 0xF8, 0x00, 0xC0, 0x62, 0x45, 0x08, 0xDD, 0x01, 0xFB, + 0x02, 0x77, 0x04, 0xFB, 0x02, 0x66, 0x15, 0x44, 0x08, 0xF1, 0x01, 0x02, + 0x1F, 0xFA, 0x82, 0xF8, 0x49, 0x1C, 0x8B, 0x42, 0xE5, 0xDA, 0x64, 0x1C, + 0x99, 0xF8, 0x03, 0x00, 0xA0, 0x42, 0xD4, 0xDA, 0xE1, 0x48, 0x00, 0x21, + 0xF3, 0x17, 0x02, 0x68, 0x91, 0x46, 0x10, 0x8B, 0xA0, 0xFB, 0x06, 0x24, + 0x01, 0xFB, 0x06, 0x41, 0x00, 0xFB, 0x03, 0x14, 0xA2, 0xFB, 0x0A, 0x06, + 0x04, 0xFB, 0x0A, 0x61, 0x02, 0xFB, 0x0B, 0x11, 0x99, 0xF8, 0x15, 0x20, + 0x52, 0x1E, 0x85, 0xFB, 0x02, 0x23, 0x07, 0xF0, 0xDF, 0xFB, 0x0E, 0x46, + 0x04, 0x46, 0x02, 0x22, 0x00, 0x23, 0x50, 0x46, 0x59, 0x46, 0x07, 0xF0, + 0xD7, 0xFB, 0xCD, 0xE9, 0x00, 0x10, 0x00, 0x19, 0x71, 0x41, 0x52, 0x46, + 0x5B, 0x46, 0x07, 0xF0, 0xCF, 0xFB, 0x02, 0x90, 0xB9, 0xF8, 0x16, 0x00, + 0x00, 0x21, 0xA0, 0xFB, 0x07, 0x24, 0x01, 0xFB, 0x07, 0x41, 0xFB, 0x17, + 0x00, 0xFB, 0x03, 0x14, 0xA2, 0xFB, 0x0A, 0x06, 0x04, 0xFB, 0x0A, 0x61, + 0x02, 0xFB, 0x0B, 0x11, 0x99, 0xF8, 0x14, 0x20, 0x52, 0x1E, 0x85, 0xFB, + 0x02, 0x23, 0x07, 0xF0, 0xB7, 0xFB, 0x03, 0x46, 0x0A, 0x46, 0xDD, 0xE9, + 0x00, 0x10, 0xC0, 0x18, 0x51, 0x41, 0x52, 0x46, 0x5B, 0x46, 0x07, 0xF0, + 0xAD, 0xFB, 0x05, 0x99, 0x0D, 0x63, 0x05, 0x9A, 0xA2, 0xF8, 0x1E, 0x80, + 0x06, 0x99, 0x48, 0x60, 0x06, 0x99, 0x02, 0x98, 0x08, 0x60, 0x07, 0xB0, + 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xFF, 0x4F, 0x82, 0x46, 0xB4, 0x48, + 0x4F, 0xF0, 0x00, 0x09, 0x93, 0xB0, 0x00, 0x68, 0x14, 0x46, 0x8E, 0x46, + 0x90, 0xF8, 0xD0, 0x01, 0x4F, 0xF4, 0x80, 0x35, 0xCC, 0x46, 0x4F, 0x46, + 0x40, 0x07, 0xA1, 0xF1, 0x01, 0x06, 0x39, 0xD5, 0x11, 0x92, 0xD0, 0x46, + 0x68, 0x46, 0x73, 0x46, 0x72, 0x46, 0x04, 0xE0, 0x00, 0xEB, 0x42, 0x04, + 0x38, 0xF8, 0x12, 0x10, 0x61, 0x80, 0x52, 0x1E, 0xF8, 0xD2, 0x41, 0x88, + 0x01, 0x80, 0x00, 0xEB, 0x43, 0x02, 0x30, 0xF8, 0x13, 0x10, 0x51, 0x80, + 0x19, 0x46, 0x0C, 0xE0, 0x00, 0xEB, 0x41, 0x0B, 0x30, 0xF8, 0x11, 0x40, + 0xBB, 0xF8, 0x04, 0x20, 0x14, 0x44, 0xBB, 0xF8, 0x02, 0x20, 0x04, 0xEB, + 0x42, 0x02, 0x28, 0xF8, 0x11, 0x20, 0x49, 0x1E, 0xF0, 0xD2, 0x9A, 0x48, + 0x11, 0x9C, 0x05, 0xE0, 0x38, 0xF9, 0x13, 0x10, 0x81, 0x42, 0x01, 0xDD, + 0x08, 0x46, 0xDC, 0xB2, 0x5B, 0x1E, 0xF7, 0xD2, 0x0C, 0xB9, 0xBD, 0xF9, + 0x00, 0xC0, 0xB4, 0x42, 0x04, 0xDB, 0x69, 0x46, 0x01, 0xEB, 0x4E, 0x00, + 0x30, 0xF9, 0x02, 0x7C, 0x1C, 0xB1, 0x0A, 0xEB, 0x44, 0x00, 0x30, 0xF9, + 0x02, 0xCC, 0x3A, 0xF9, 0x14, 0xB0, 0xB4, 0x42, 0x03, 0xDA, 0x0A, 0xEB, + 0x44, 0x00, 0xB0, 0xF9, 0x02, 0x70, 0x60, 0x46, 0x3A, 0x46, 0xBC, 0x45, + 0x01, 0xDD, 0x02, 0x46, 0x38, 0x46, 0xA7, 0xEB, 0x0C, 0x01, 0xA1, 0xFB, + 0x05, 0x78, 0x4F, 0xEA, 0xE1, 0x7C, 0x0C, 0xFB, 0x05, 0x8C, 0x5A, 0x44, + 0x40, 0x42, 0x02, 0xEB, 0x40, 0x02, 0x01, 0xFB, 0x09, 0xC1, 0xD3, 0x17, + 0x38, 0x46, 0x07, 0xF0, 0x2D, 0xFB, 0xA4, 0xFB, 0x05, 0x3C, 0x00, 0x27, + 0x07, 0xFB, 0x05, 0xC7, 0x04, 0xFB, 0x09, 0x72, 0x18, 0x18, 0x4A, 0x41, + 0x16, 0x99, 0xA0, 0xFB, 0x01, 0x47, 0xCB, 0x17, 0x02, 0xFB, 0x01, 0x71, + 0x00, 0xFB, 0x03, 0x11, 0xF3, 0x17, 0x32, 0x46, 0x20, 0x46, 0x07, 0xF0, + 0x17, 0xFB, 0x04, 0x46, 0x0E, 0x46, 0x02, 0x22, 0x00, 0x23, 0x28, 0x46, + 0x49, 0x46, 0x07, 0xF0, 0x0F, 0xFB, 0x00, 0x19, 0x71, 0x41, 0x2A, 0x46, + 0x4B, 0x46, 0x07, 0xF0, 0x09, 0xFB, 0x17, 0xB0, 0x64, 0xE7, 0x2D, 0xE9, + 0xFF, 0x4F, 0x00, 0x27, 0x91, 0xB0, 0x16, 0x46, 0x0C, 0x46, 0x83, 0x46, + 0xB9, 0x46, 0xBA, 0x46, 0x8D, 0x78, 0x1A, 0xE0, 0xE9, 0xB2, 0x30, 0x69, + 0x09, 0xF0, 0x20, 0xFA, 0x21, 0x78, 0x4F, 0xF0, 0x01, 0x08, 0x94, 0xF8, + 0x01, 0xC0, 0x0D, 0xE0, 0x5B, 0xF8, 0x25, 0x20, 0x08, 0xFA, 0x01, 0xF3, + 0x1A, 0x42, 0x06, 0xD0, 0x30, 0xF9, 0x11, 0x20, 0x52, 0x45, 0x02, 0xDD, + 0x7F, 0x1C, 0x91, 0x44, 0xBF, 0xB2, 0x49, 0x1C, 0x8C, 0x45, 0xEF, 0xD2, + 0x6D, 0x1C, 0xE0, 0x78, 0xA8, 0x42, 0xE1, 0xD2, 0xC6, 0xF8, 0x30, 0x90, + 0xF7, 0x83, 0x37, 0x7A, 0x96, 0xF8, 0x09, 0x90, 0x40, 0x21, 0x68, 0x46, + 0x07, 0xF0, 0x99, 0xFB, 0xA5, 0x78, 0xE8, 0x46, 0x08, 0xE0, 0xE9, 0xB2, + 0x30, 0x69, 0x09, 0xF0, 0xF5, 0xF9, 0x30, 0xF8, 0x17, 0x10, 0x28, 0xF8, + 0x15, 0x10, 0x6D, 0x1C, 0xE0, 0x78, 0xA8, 0x42, 0xF3, 0xD2, 0x48, 0x4D, + 0x4A, 0x46, 0x28, 0x68, 0xB0, 0xF9, 0x18, 0x30, 0x41, 0x7D, 0x68, 0x46, + 0xFF, 0xF7, 0x1C, 0xFF, 0x14, 0x99, 0x08, 0x60, 0x40, 0x21, 0x68, 0x46, + 0x07, 0xF0, 0x7B, 0xFB, 0x49, 0x46, 0x30, 0x69, 0x09, 0xF0, 0xDA, 0xF9, + 0x21, 0x78, 0x42, 0x46, 0x04, 0xE0, 0x30, 0xF8, 0x11, 0x30, 0x22, 0xF8, + 0x11, 0x30, 0x49, 0x1C, 0x63, 0x78, 0x8B, 0x42, 0xF7, 0xD2, 0x28, 0x68, + 0x3A, 0x46, 0xB0, 0xF9, 0x16, 0x30, 0x01, 0x7D, 0x68, 0x46, 0xFF, 0xF7, + 0xFF, 0xFE, 0x14, 0x99, 0x48, 0x60, 0x15, 0xB0, 0xF8, 0xE6, 0xF0, 0xB5, + 0x32, 0x4C, 0x01, 0x2B, 0x24, 0x68, 0x0E, 0xD0, 0x94, 0xF8, 0x94, 0x36, + 0xB4, 0xF8, 0x90, 0x66, 0x03, 0xF0, 0x0F, 0x05, 0x1F, 0x09, 0xB4, 0xF8, + 0x92, 0x36, 0xB2, 0x42, 0x17, 0xDD, 0x9A, 0x42, 0x0B, 0xDB, 0x3D, 0x46, + 0x13, 0xE0, 0x94, 0xF8, 0xDD, 0x31, 0xB4, 0xF8, 0xDE, 0x61, 0x03, 0xF0, + 0x0F, 0x05, 0x1F, 0x09, 0xB4, 0xF8, 0xE0, 0x31, 0xEF, 0xE7, 0xB3, 0x42, + 0x00, 0xD1, 0x5B, 0x1C, 0x92, 0x1B, 0x7C, 0x1B, 0x62, 0x43, 0x9B, 0x1B, + 0x92, 0xFB, 0xF3, 0xF2, 0x15, 0x44, 0x43, 0x68, 0x4C, 0x68, 0xC5, 0xF1, + 0x10, 0x02, 0x6B, 0x43, 0x04, 0xFB, 0x02, 0x33, 0x1B, 0x11, 0x4B, 0x60, + 0x00, 0x68, 0x0B, 0x68, 0x68, 0x43, 0x03, 0xFB, 0x02, 0x00, 0x00, 0x11, + 0x08, 0x60, 0xF0, 0xBD, 0x10, 0xB5, 0x41, 0x68, 0x00, 0x23, 0x00, 0x29, + 0x00, 0xDA, 0x43, 0x60, 0x13, 0x4A, 0x44, 0x68, 0x11, 0x68, 0xC9, 0x8A, + 0x8C, 0x42, 0x00, 0xDD, 0x41, 0x60, 0x01, 0x68, 0x00, 0x29, 0x01, 0xDB, + 0x03, 0x68, 0x00, 0xE0, 0x03, 0x60, 0x11, 0x68, 0x09, 0x8B, 0x8B, 0x42, + 0x00, 0xDD, 0x01, 0x60, 0x10, 0xBD, 0x2D, 0xE9, 0xFF, 0x41, 0x14, 0x46, + 0x0E, 0x46, 0x07, 0x46, 0x02, 0xAB, 0xFF, 0xF7, 0x0B, 0xFE, 0xA0, 0x7B, + 0x05, 0x4D, 0x02, 0x28, 0x0C, 0xD0, 0x01, 0x28, 0x1E, 0xD0, 0x04, 0x28, + 0x39, 0xD0, 0xDD, 0xE9, 0x02, 0x01, 0xCD, 0xE9, 0x00, 0x01, 0x2E, 0xE0, + 0x50, 0x24, 0x10, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x28, 0x68, 0x90, 0xF8, + 0x80, 0x11, 0x89, 0x07, 0x25, 0xD5, 0xDD, 0xE9, 0x02, 0x12, 0xCD, 0xE9, + 0x00, 0x12, 0x90, 0xF8, 0xFA, 0x04, 0x80, 0x07, 0x1D, 0xD5, 0x01, 0x21, + 0x68, 0x46, 0x01, 0xF0, 0x8D, 0xF8, 0x18, 0xE0, 0x00, 0x21, 0x22, 0xE0, + 0x28, 0x68, 0x90, 0xF8, 0xD0, 0x11, 0x89, 0x07, 0xEB, 0xD5, 0x6B, 0x46, + 0x22, 0x46, 0x31, 0x46, 0x38, 0x46, 0xFF, 0xF7, 0x0A, 0xFF, 0x28, 0x68, + 0x90, 0xF8, 0xFA, 0x04, 0x80, 0x07, 0x03, 0xD5, 0x01, 0x21, 0x68, 0x46, + 0x01, 0xF0, 0x76, 0xF8, 0x68, 0x46, 0xFF, 0xF7, 0xA3, 0xFF, 0x28, 0x68, + 0x90, 0xF8, 0xFA, 0x04, 0xC0, 0x07, 0xE1, 0xD1, 0x08, 0xE0, 0x28, 0x68, + 0x90, 0xF8, 0xFA, 0x04, 0x40, 0x07, 0x03, 0xD5, 0x02, 0x21, 0x02, 0xA8, + 0x01, 0xF0, 0x64, 0xF8, 0x02, 0xA8, 0xFF, 0xF7, 0x91, 0xFF, 0xA0, 0x7B, + 0x01, 0x28, 0x0C, 0xD1, 0x28, 0x68, 0x90, 0xF8, 0xFA, 0x04, 0x81, 0x07, + 0x1E, 0xD5, 0xC0, 0x07, 0x1C, 0xD0, 0x01, 0x23, 0x69, 0x46, 0x02, 0xA8, + 0x22, 0x6B, 0xFF, 0xF7, 0x48, 0xFF, 0xA0, 0x7B, 0x02, 0x28, 0x10, 0xD1, + 0x28, 0x68, 0x90, 0xF8, 0x80, 0x11, 0x89, 0x07, 0x13, 0xD5, 0x90, 0xF8, + 0xFA, 0x04, 0x81, 0x07, 0x13, 0xD5, 0xC0, 0x07, 0x11, 0xD0, 0x00, 0x23, + 0x69, 0x46, 0x02, 0xA8, 0x22, 0x6B, 0xFF, 0xF7, 0x34, 0xFF, 0xA0, 0x7B, + 0x04, 0x28, 0x04, 0xD0, 0x28, 0x68, 0x90, 0xF8, 0x80, 0x01, 0x80, 0x07, + 0x03, 0xD4, 0x03, 0x98, 0x60, 0x80, 0x02, 0x98, 0x02, 0xE0, 0x01, 0x98, + 0x60, 0x80, 0x00, 0x98, 0x20, 0x80, 0x01, 0x98, 0xE0, 0x80, 0x00, 0x98, + 0xA0, 0x80, 0xBD, 0xE8, 0xFF, 0x81, 0x2D, 0xE9, 0xF0, 0x41, 0x06, 0x46, + 0x90, 0x7E, 0xDF, 0xF8, 0x74, 0x81, 0x5D, 0x4F, 0x8C, 0x78, 0xA0, 0xB0, + 0x0D, 0x46, 0x40, 0x07, 0x0F, 0xD5, 0x00, 0x2C, 0x51, 0xD0, 0x98, 0xF8, + 0x00, 0x00, 0xE9, 0x78, 0x40, 0x1E, 0x81, 0x42, 0x4B, 0xD0, 0x28, 0x78, + 0x00, 0x28, 0x48, 0xD0, 0x38, 0x78, 0x69, 0x78, 0x40, 0x1E, 0x81, 0x42, + 0x43, 0xD0, 0x80, 0x21, 0x68, 0x46, 0x07, 0xF0, 0x5E, 0xFA, 0x6A, 0x46, + 0xEB, 0x78, 0x1A, 0xE0, 0x56, 0xF8, 0x24, 0x10, 0xB1, 0xB1, 0x48, 0x08, + 0x40, 0xEA, 0x41, 0x00, 0x08, 0x43, 0x52, 0xF8, 0x24, 0x10, 0x01, 0x43, + 0x42, 0xF8, 0x24, 0x10, 0x02, 0xEB, 0x84, 0x01, 0xD1, 0xF8, 0x04, 0xC0, + 0x4C, 0xEA, 0x00, 0x0C, 0xC1, 0xF8, 0x04, 0xC0, 0xD1, 0xF8, 0x08, 0xC0, + 0x4C, 0xEA, 0x00, 0x0C, 0xC1, 0xF8, 0x08, 0xC0, 0x64, 0x1C, 0xA3, 0x42, + 0xE2, 0xD2, 0x20, 0x20, 0x04, 0xE0, 0x02, 0xEB, 0x80, 0x01, 0x49, 0x68, + 0x46, 0xF8, 0x20, 0x10, 0x40, 0x1E, 0xF8, 0xD2, 0x28, 0x78, 0x08, 0xB1, + 0x40, 0x1E, 0x28, 0x70, 0x39, 0x78, 0x68, 0x78, 0x49, 0x1E, 0x88, 0x42, + 0x01, 0xDA, 0x40, 0x1C, 0x68, 0x70, 0xA8, 0x78, 0x08, 0xB1, 0x40, 0x1E, + 0xA8, 0x70, 0x98, 0xF8, 0x00, 0x10, 0xE8, 0x78, 0x49, 0x1E, 0x88, 0x42, + 0x01, 0xDA, 0x40, 0x1C, 0xE8, 0x70, 0x20, 0xB0, 0xBD, 0xE8, 0xF0, 0x81, + 0x2D, 0xE9, 0xF0, 0x4F, 0xA1, 0xB0, 0x9B, 0x46, 0x15, 0x46, 0x6B, 0x46, + 0xFF, 0xF7, 0xF0, 0xFC, 0x00, 0x28, 0x4B, 0xD0, 0x29, 0x68, 0xCB, 0xF8, + 0x0A, 0x10, 0xCD, 0xF8, 0x80, 0xD0, 0xDF, 0xF8, 0xA0, 0x90, 0x0B, 0xF1, + 0x08, 0x07, 0x00, 0x26, 0xAC, 0x78, 0x4F, 0xF0, 0x01, 0x08, 0x1E, 0xE0, + 0x20, 0x98, 0xE1, 0xB2, 0x50, 0xF8, 0x24, 0xA0, 0xDB, 0xF8, 0x10, 0x00, + 0x09, 0xF0, 0x5E, 0xF8, 0x29, 0x78, 0x04, 0xF0, 0xFF, 0x0C, 0x0E, 0xE0, + 0x08, 0xFA, 0x01, 0xF2, 0x12, 0xEA, 0x0A, 0x0F, 0x08, 0xD0, 0x30, 0xF9, + 0x11, 0x20, 0x16, 0x44, 0x4A, 0x45, 0x03, 0xDD, 0x39, 0x70, 0x91, 0x46, + 0x87, 0xF8, 0x01, 0xC0, 0x49, 0x1C, 0x6A, 0x78, 0x8A, 0x42, 0xED, 0xD2, + 0x64, 0x1C, 0xE8, 0x78, 0xA0, 0x42, 0xDD, 0xD2, 0x13, 0x48, 0xCB, 0xF8, + 0x2C, 0x60, 0x00, 0x78, 0xC0, 0x06, 0x08, 0xD5, 0x11, 0x48, 0x00, 0x78, + 0x28, 0xB9, 0x59, 0x46, 0x68, 0x46, 0xFD, 0xF7, 0x74, 0xF8, 0x01, 0x28, + 0x0E, 0xD0, 0x5A, 0x46, 0x29, 0x46, 0x68, 0x46, 0xFF, 0xF7, 0x4F, 0xFF, + 0x5A, 0x46, 0x29, 0x46, 0x68, 0x46, 0xFF, 0xF7, 0xBA, 0xFE, 0x58, 0x46, + 0xFF, 0xF7, 0x64, 0xF8, 0x21, 0xB0, 0x5B, 0xE5, 0x06, 0x49, 0x81, 0xF8, + 0x00, 0x80, 0xF9, 0xE7, 0x00, 0x24, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, + 0x00, 0x80, 0xFF, 0xFF, 0x1C, 0x24, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0x6E, 0x23, 0x10, 0x00, 0x70, 0xB5, 0x8A, 0xB0, 0x0E, 0x46, 0x04, 0x46, + 0x14, 0x22, 0xFE, 0x49, 0x05, 0xA8, 0x07, 0xF0, 0x4E, 0xF9, 0xFD, 0x4D, + 0xE8, 0x68, 0x00, 0x90, 0xFC, 0x48, 0x00, 0x68, 0x01, 0x90, 0x05, 0xA8, + 0x02, 0x90, 0xFB, 0x48, 0x00, 0x68, 0xB0, 0xF8, 0x6C, 0x01, 0xAD, 0xF8, + 0x10, 0x00, 0xF9, 0x48, 0x03, 0x94, 0x00, 0x78, 0x8D, 0xF8, 0x13, 0x00, + 0xF7, 0x48, 0x00, 0x78, 0x8D, 0xF8, 0x12, 0x00, 0x68, 0x46, 0x06, 0xF0, + 0xD9, 0xF9, 0xE8, 0x68, 0x70, 0x61, 0x0A, 0xB0, 0x70, 0xBD, 0x70, 0xB5, + 0x0E, 0x46, 0xEC, 0x49, 0x8A, 0xB0, 0x04, 0x46, 0x14, 0x22, 0x14, 0x31, + 0x05, 0xA8, 0x07, 0xF0, 0x26, 0xF9, 0xE9, 0x4D, 0xE8, 0x68, 0x00, 0x90, + 0xE8, 0x48, 0x00, 0x68, 0x01, 0x90, 0x05, 0xA8, 0x02, 0x90, 0xE7, 0x48, + 0x00, 0x68, 0xB0, 0xF8, 0x6C, 0x01, 0xAD, 0xF8, 0x10, 0x00, 0xE5, 0x48, + 0x03, 0x94, 0x00, 0x78, 0x8D, 0xF8, 0x13, 0x00, 0xE3, 0x48, 0x00, 0x78, + 0x8D, 0xF8, 0x12, 0x00, 0x68, 0x46, 0x06, 0xF0, 0xB1, 0xF9, 0xE8, 0x68, + 0x30, 0x61, 0xD6, 0xE7, 0xDF, 0x49, 0x01, 0x20, 0x09, 0x78, 0xC9, 0x07, + 0x03, 0xD0, 0xDE, 0x49, 0x09, 0x78, 0x00, 0x29, 0x00, 0xD0, 0x00, 0x20, + 0x70, 0x47, 0x2D, 0xE9, 0xFF, 0x4F, 0x81, 0xB0, 0xDA, 0x48, 0x0E, 0x9D, + 0x10, 0x9F, 0x00, 0x68, 0x28, 0x60, 0x4F, 0xF0, 0x00, 0x08, 0xC7, 0xF8, + 0x2C, 0x80, 0x99, 0x46, 0x9E, 0x78, 0x47, 0xE0, 0xF1, 0xB2, 0x01, 0x98, + 0x08, 0xF0, 0xA0, 0xFF, 0x83, 0x46, 0xF1, 0xB2, 0x02, 0x98, 0x08, 0xF0, + 0x9B, 0xFF, 0x00, 0x90, 0xCC, 0x4A, 0xF0, 0xB2, 0x39, 0x69, 0x12, 0x78, + 0x99, 0xF8, 0x00, 0x40, 0x50, 0x43, 0x01, 0xEB, 0x40, 0x0A, 0x2E, 0xE0, + 0x00, 0x98, 0x01, 0x5D, 0x03, 0x98, 0x81, 0x42, 0x28, 0xD1, 0x3A, 0xF9, + 0x14, 0x10, 0x0F, 0x98, 0x81, 0x42, 0x20, 0xDD, 0x03, 0x98, 0x0B, 0xF8, + 0x04, 0x00, 0x29, 0x78, 0x20, 0x46, 0x03, 0xF0, 0x71, 0xFF, 0x28, 0x70, + 0x69, 0x78, 0x20, 0x46, 0x03, 0xF0, 0x70, 0xFF, 0x68, 0x70, 0xA9, 0x78, + 0x30, 0x46, 0x03, 0xF0, 0x67, 0xFF, 0xA8, 0x70, 0xE9, 0x78, 0x30, 0x46, + 0x03, 0xF0, 0x66, 0xFF, 0xE8, 0x70, 0x08, 0xF1, 0x01, 0x00, 0x1F, 0xFA, + 0x80, 0xF8, 0x3A, 0xF9, 0x14, 0x00, 0xF9, 0x6A, 0x08, 0x44, 0xF8, 0x62, + 0x02, 0xE0, 0x00, 0x20, 0x0B, 0xF8, 0x04, 0x00, 0x64, 0x1C, 0x99, 0xF8, + 0x01, 0x00, 0xA0, 0x42, 0xCC, 0xD2, 0x76, 0x1C, 0x99, 0xF8, 0x03, 0x00, + 0xB0, 0x42, 0xB3, 0xD2, 0xA7, 0xF8, 0x1C, 0x80, 0x05, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, 0xFF, 0x47, 0x15, 0x46, 0x0C, 0xAA, 0x81, 0x46, + 0x92, 0xE8, 0x41, 0x01, 0xA9, 0x4A, 0x00, 0x27, 0xB2, 0xF9, 0x00, 0x20, + 0x9A, 0x42, 0x2B, 0xDA, 0x03, 0xAA, 0x00, 0x92, 0xCD, 0xE9, 0x01, 0x30, + 0x05, 0xEB, 0x81, 0x04, 0x0A, 0x46, 0x49, 0x46, 0x23, 0x46, 0x08, 0x46, + 0xFF, 0xF7, 0x87, 0xFF, 0x03, 0x98, 0x20, 0x60, 0xB8, 0xF1, 0x01, 0x0F, + 0x1A, 0xD1, 0x95, 0xF8, 0x0E, 0x41, 0x03, 0x98, 0x28, 0x60, 0x94, 0x48, + 0x01, 0x27, 0x10, 0x30, 0xCD, 0xE9, 0x00, 0x70, 0x3B, 0x46, 0x22, 0x46, + 0x29, 0x46, 0x48, 0x46, 0x00, 0xF0, 0x62, 0xFD, 0x04, 0xE0, 0x30, 0x68, + 0x07, 0xFA, 0x04, 0xF1, 0x08, 0x43, 0x30, 0x60, 0x95, 0xF8, 0x0E, 0x01, + 0x64, 0x1C, 0xA0, 0x42, 0xF5, 0xD2, 0x01, 0x27, 0x04, 0xB0, 0x38, 0x46, + 0xBD, 0xE8, 0xF0, 0x87, 0x10, 0xB5, 0x88, 0x4A, 0x8E, 0x4C, 0x13, 0x68, + 0x24, 0x78, 0x93, 0xF8, 0x66, 0x21, 0x01, 0x2C, 0x01, 0xD1, 0x93, 0xF8, + 0xCF, 0x20, 0x8B, 0x4C, 0x24, 0x78, 0x44, 0xB1, 0x89, 0x7E, 0x89, 0x07, + 0x02, 0xD5, 0x93, 0xF8, 0x68, 0x11, 0x01, 0xE0, 0x93, 0xF8, 0x67, 0x11, + 0x0A, 0x44, 0x90, 0x42, 0x01, 0xD9, 0x01, 0x20, 0x10, 0xBD, 0x00, 0x20, + 0x10, 0xBD, 0x2D, 0xE9, 0xF0, 0x47, 0x88, 0x46, 0x81, 0x46, 0x50, 0x78, + 0x11, 0x78, 0x77, 0x4F, 0x15, 0x46, 0x42, 0x1A, 0x38, 0x68, 0x73, 0x4E, + 0x52, 0x1C, 0x90, 0xF8, 0xEB, 0x10, 0x1C, 0x46, 0x8A, 0x42, 0x0D, 0xDC, + 0xEA, 0x78, 0xAB, 0x78, 0xD2, 0x1A, 0x52, 0x1C, 0x8A, 0x42, 0x07, 0xDC, + 0xB0, 0xF8, 0xEE, 0x20, 0xE1, 0x6A, 0x91, 0x42, 0x02, 0xDC, 0xA1, 0x8B, + 0x04, 0x29, 0x01, 0xD2, 0x00, 0x20, 0x0C, 0xE0, 0x31, 0x78, 0x59, 0xB9, + 0x90, 0xF8, 0xD4, 0x00, 0x40, 0x07, 0x07, 0xD5, 0x6F, 0x48, 0x21, 0x46, + 0xB0, 0xF9, 0x00, 0x20, 0x28, 0x46, 0x01, 0xF0, 0xA4, 0xFC, 0x30, 0x70, + 0x6C, 0x48, 0xA1, 0x8B, 0x03, 0x46, 0x01, 0x82, 0x62, 0x49, 0x2A, 0x46, + 0x09, 0x78, 0x81, 0x74, 0x04, 0xF1, 0x22, 0x01, 0x01, 0x60, 0x89, 0x1C, + 0x41, 0x60, 0x89, 0x1C, 0x81, 0x60, 0x0D, 0x39, 0xC1, 0x60, 0x41, 0x46, + 0x48, 0x46, 0x05, 0xF0, 0x8C, 0xFF, 0x38, 0x68, 0xA1, 0x8B, 0x90, 0xF8, + 0x69, 0x21, 0x91, 0x42, 0x32, 0xD8, 0x02, 0x21, 0xA1, 0x73, 0x90, 0xF8, + 0x64, 0x21, 0x00, 0x21, 0x93, 0x07, 0x01, 0xD4, 0x53, 0x07, 0x00, 0xD5, + 0x01, 0x21, 0x63, 0x8C, 0xB0, 0xF8, 0x6A, 0x01, 0x83, 0x42, 0x00, 0xD2, + 0x00, 0x21, 0x20, 0x8C, 0x03, 0x28, 0x00, 0xD2, 0x00, 0x21, 0x30, 0x78, + 0x01, 0x28, 0x00, 0xD1, 0x00, 0x21, 0x50, 0x48, 0x00, 0x78, 0x01, 0x28, + 0x0E, 0xD0, 0x69, 0xB1, 0xD0, 0x07, 0x03, 0xD0, 0x21, 0x46, 0x28, 0x46, + 0xFF, 0xF7, 0x80, 0xFE, 0x23, 0x46, 0x2A, 0x46, 0x41, 0x46, 0x48, 0x46, + 0xBD, 0xE8, 0xF0, 0x47, 0x01, 0xF0, 0xD7, 0xBC, 0x23, 0x46, 0x2A, 0x46, + 0x41, 0x46, 0x48, 0x46, 0xBD, 0xE8, 0xF0, 0x47, 0xFF, 0xF7, 0x08, 0xBE, + 0x90, 0xF8, 0x7B, 0x21, 0x03, 0x20, 0x91, 0x42, 0xA0, 0x73, 0x0D, 0xD8, + 0x23, 0x46, 0x2A, 0x46, 0x41, 0x46, 0x48, 0x46, 0x01, 0xF0, 0xC3, 0xFC, + 0xA0, 0x7B, 0x03, 0x28, 0x03, 0xD1, 0x60, 0x8B, 0x40, 0xF0, 0x20, 0x00, + 0x60, 0x83, 0x4F, 0xE7, 0x60, 0x8B, 0x40, 0xF0, 0x20, 0x00, 0x60, 0x83, + 0xDE, 0xE7, 0x37, 0x49, 0x10, 0xB5, 0x09, 0x78, 0xC9, 0xB1, 0x2D, 0x49, + 0x09, 0x68, 0x91, 0xF8, 0x7A, 0x11, 0x0A, 0x07, 0x13, 0xD0, 0x02, 0x78, + 0x43, 0x78, 0x9A, 0x42, 0x03, 0xD1, 0xC2, 0x78, 0x80, 0x78, 0x12, 0x1A, + 0x04, 0xE0, 0x84, 0x78, 0xC0, 0x78, 0x84, 0x42, 0x07, 0xD1, 0x9A, 0x1A, + 0x01, 0xF0, 0x0F, 0x00, 0x40, 0x1E, 0x82, 0x42, 0x01, 0xDD, 0x00, 0x20, + 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0x1D, 0x48, 0x10, 0xB5, 0x41, 0x68, + 0x4F, 0xF4, 0x88, 0x62, 0x27, 0x48, 0x06, 0xF0, 0x41, 0xFF, 0xBD, 0xE8, + 0x10, 0x40, 0x25, 0x48, 0xFC, 0xF7, 0x92, 0xB8, 0x2D, 0xE9, 0xF0, 0x5F, + 0x83, 0x46, 0x00, 0x20, 0xD8, 0x62, 0x1D, 0x46, 0x17, 0x46, 0x8A, 0x46, + 0x20, 0x4E, 0x03, 0xF1, 0x08, 0x08, 0x94, 0x78, 0x47, 0xE0, 0xE1, 0xB2, + 0x58, 0x46, 0x08, 0xF0, 0x25, 0xFE, 0x12, 0x4B, 0xE1, 0xB2, 0x2A, 0x69, + 0x1B, 0x78, 0x04, 0xF0, 0xFF, 0x09, 0x59, 0x43, 0x02, 0xEB, 0x41, 0x03, + 0x39, 0x78, 0x34, 0xE0, 0x10, 0xF8, 0x01, 0xC0, 0xD4, 0x45, 0x2F, 0xD1, + 0x33, 0xF9, 0x11, 0x20, 0xD5, 0xF8, 0x2C, 0xC0, 0xB2, 0x42, 0x94, 0x44, + 0xC5, 0xF8, 0x2C, 0xC0, 0x26, 0xDD, 0x16, 0x46, 0x20, 0xE0, 0x00, 0x00, + 0x38, 0x1D, 0x01, 0x00, 0x9C, 0x23, 0x10, 0x00, 0x10, 0x24, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, + 0x1C, 0x24, 0x10, 0x00, 0xE0, 0x23, 0x10, 0x00, 0x60, 0x1D, 0x01, 0x00, + 0xD2, 0x23, 0x10, 0x00, 0x6E, 0x23, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0xCC, 0x23, 0x10, 0x00, 0x40, 0x50, 0x10, 0x00, 0xBC, 0x8F, 0x01, 0x20, + 0x00, 0x80, 0xFF, 0xFF, 0x88, 0xF8, 0x00, 0x10, 0x88, 0xF8, 0x01, 0x90, + 0x49, 0x1C, 0x7A, 0x78, 0x8A, 0x42, 0xC7, 0xD2, 0x64, 0x1C, 0xF8, 0x78, + 0xA0, 0x42, 0xB4, 0xD2, 0x2E, 0x85, 0xBD, 0xE8, 0xF0, 0x9F, 0x10, 0xB5, + 0xA0, 0xB0, 0x14, 0x46, 0x6B, 0x46, 0xFF, 0xF7, 0x59, 0xFA, 0x00, 0x28, + 0x0C, 0xD0, 0xFE, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x82, 0x00, 0x40, 0x07, + 0x05, 0xD5, 0x21, 0x46, 0x68, 0x46, 0xFC, 0xF7, 0x85, 0xFE, 0x00, 0x28, + 0x00, 0xD0, 0x01, 0x20, 0x20, 0xB0, 0x10, 0xBD, 0xF8, 0x49, 0xF7, 0x48, + 0xF8, 0x4A, 0xC8, 0x60, 0x00, 0xF5, 0x08, 0x60, 0x48, 0x60, 0x00, 0xF5, + 0x88, 0x60, 0x88, 0x60, 0x00, 0x20, 0x10, 0x70, 0xF4, 0x4A, 0x10, 0x70, + 0xF4, 0x4A, 0x10, 0x70, 0x08, 0x70, 0xF4, 0x49, 0x08, 0x70, 0xF4, 0x49, + 0x08, 0x60, 0xF4, 0x49, 0x08, 0x70, 0xEB, 0x48, 0xEC, 0x49, 0x00, 0x68, + 0x10, 0x31, 0x42, 0x7D, 0x0A, 0x70, 0x00, 0x7D, 0x48, 0x70, 0xF0, 0x48, + 0x48, 0x60, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x4F, 0xE6, 0x4D, 0xDD, 0xB0, + 0x4F, 0xF4, 0x88, 0x61, 0xA8, 0x68, 0x06, 0xF0, 0x1E, 0xFF, 0xEB, 0x48, + 0xB0, 0xF9, 0x00, 0x00, 0x41, 0x00, 0xE8, 0x68, 0x06, 0xF0, 0x17, 0xFF, + 0x00, 0x21, 0xCD, 0xE9, 0x54, 0x11, 0x56, 0x91, 0x05, 0xF1, 0x10, 0x00, + 0xCD, 0xE9, 0x00, 0x10, 0x58, 0x91, 0x57, 0x91, 0x89, 0x46, 0x0A, 0x46, + 0x01, 0x23, 0x03, 0xA9, 0x68, 0x68, 0x00, 0xF0, 0xBD, 0xFB, 0xE0, 0x49, + 0x9D, 0xF8, 0x1A, 0x01, 0x08, 0x72, 0x01, 0x28, 0x01, 0xD9, 0x00, 0x20, + 0x28, 0x70, 0x01, 0x24, 0x2A, 0xE2, 0x03, 0xA8, 0x00, 0xEB, 0x84, 0x06, + 0x00, 0xEB, 0x44, 0x00, 0xE1, 0xB2, 0xB0, 0xF8, 0xB4, 0x20, 0x47, 0xA8, + 0x00, 0xF0, 0x67, 0xFA, 0xE1, 0xB2, 0x47, 0xAB, 0x32, 0x46, 0x68, 0x68, + 0xFF, 0xF7, 0x30, 0xFF, 0x9D, 0xF8, 0x24, 0x01, 0x60, 0xB1, 0xD2, 0x49, + 0x09, 0x78, 0x49, 0x1E, 0x88, 0x42, 0x07, 0xD0, 0x9D, 0xF8, 0x25, 0x01, + 0x20, 0xB1, 0xCF, 0x49, 0x09, 0x78, 0x49, 0x1E, 0x88, 0x42, 0x05, 0xD1, + 0xBD, 0xF8, 0x36, 0x01, 0x40, 0xF0, 0x01, 0x00, 0xAD, 0xF8, 0x36, 0x01, + 0x30, 0x78, 0x28, 0xB1, 0xC7, 0x48, 0x71, 0x78, 0x00, 0x78, 0x40, 0x1E, + 0x81, 0x42, 0x0D, 0xD1, 0xB0, 0x78, 0x28, 0xB1, 0xC4, 0x48, 0xF1, 0x78, + 0x00, 0x78, 0x40, 0x1E, 0x81, 0x42, 0x05, 0xD1, 0xBD, 0xF8, 0x36, 0x01, + 0x40, 0xF0, 0x02, 0x00, 0xAD, 0xF8, 0x36, 0x01, 0x4F, 0xF0, 0x00, 0x0A, + 0x01, 0x20, 0x54, 0x99, 0x00, 0xFA, 0x04, 0xF8, 0x57, 0x46, 0xD3, 0x46, + 0x18, 0xEA, 0x01, 0x0F, 0x64, 0xD1, 0xBA, 0x48, 0x00, 0x68, 0x81, 0x07, + 0x60, 0xD5, 0xAA, 0x49, 0xB8, 0x4B, 0x09, 0x68, 0x1B, 0x68, 0x91, 0xF8, + 0xD8, 0x21, 0x9A, 0x42, 0xB6, 0x4A, 0x12, 0x78, 0x01, 0xD2, 0xEA, 0xBB, + 0x01, 0xE0, 0x02, 0x2A, 0x52, 0xD2, 0xAB, 0x4A, 0x12, 0x78, 0x01, 0x2A, + 0x4E, 0xD0, 0xAC, 0x4A, 0x10, 0xF0, 0x30, 0x0F, 0x13, 0x7C, 0x43, 0xF0, + 0x01, 0x03, 0x13, 0x74, 0x0A, 0xD0, 0x91, 0xF8, 0xE7, 0x01, 0xFB, 0xF7, + 0x12, 0xFE, 0xE1, 0xB2, 0x47, 0xAB, 0x32, 0x46, 0x68, 0x68, 0xFF, 0xF7, + 0x2C, 0xFF, 0x18, 0xB3, 0xA2, 0x48, 0x47, 0xAA, 0x33, 0x46, 0x01, 0x7C, + 0x41, 0xF0, 0x02, 0x01, 0x01, 0x74, 0x52, 0x98, 0x5B, 0x90, 0xBD, 0xF8, + 0x38, 0x01, 0xA3, 0x49, 0x5A, 0x90, 0x59, 0xA8, 0xB1, 0xF9, 0x00, 0x10, + 0x8D, 0xE8, 0x07, 0x00, 0xD5, 0xE9, 0x01, 0x10, 0xE2, 0xB2, 0xFF, 0xF7, + 0x3C, 0xFD, 0xE1, 0xB2, 0x47, 0xAB, 0x59, 0xAA, 0xA8, 0x68, 0x00, 0xF0, + 0xF5, 0xF9, 0x01, 0x28, 0x17, 0xD1, 0x82, 0x46, 0x58, 0x90, 0xE1, 0xB2, + 0x59, 0xAA, 0x00, 0xE0, 0x16, 0xE0, 0x4F, 0xF0, 0x01, 0x0C, 0xA8, 0x68, + 0x8D, 0xF8, 0x2A, 0xC1, 0x4F, 0xF0, 0x02, 0x0C, 0xAD, 0xF8, 0x40, 0xC1, + 0xAD, 0xF8, 0x42, 0xC1, 0x4F, 0xF0, 0x00, 0x0C, 0x47, 0xAB, 0x8D, 0xF8, + 0x35, 0xC1, 0xFF, 0xF7, 0x5B, 0xFC, 0x5B, 0x98, 0x52, 0x90, 0x5A, 0x98, + 0xAD, 0xF8, 0x38, 0x01, 0x30, 0x46, 0xFF, 0xF7, 0x64, 0xFE, 0x00, 0x28, + 0x74, 0xD0, 0xBA, 0xF1, 0x00, 0x0F, 0x72, 0xD1, 0x87, 0x48, 0xBD, 0xF9, + 0x44, 0x11, 0xB0, 0xF9, 0x00, 0x00, 0x81, 0x42, 0x5D, 0xDD, 0x78, 0x48, + 0x00, 0x78, 0xE8, 0xBB, 0xDF, 0xF8, 0xEC, 0xA1, 0x7D, 0x48, 0x9A, 0xF8, + 0x11, 0x10, 0x41, 0xF0, 0x01, 0x01, 0x8A, 0xF8, 0x11, 0x10, 0x00, 0x78, + 0x10, 0xF0, 0x30, 0x0F, 0x13, 0xD0, 0x6A, 0x48, 0x00, 0x68, 0xB0, 0xF9, + 0xA8, 0x00, 0xFB, 0xF7, 0xAA, 0xFD, 0xE1, 0xB2, 0x47, 0xAB, 0x32, 0x46, + 0x68, 0x68, 0xFF, 0xF7, 0xC4, 0xFE, 0x00, 0x28, 0xD6, 0xD0, 0x9A, 0xF8, + 0x11, 0x10, 0x41, 0xF0, 0x02, 0x01, 0x8A, 0xF8, 0x11, 0x10, 0x60, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0xD4, 0x00, 0x40, 0x07, 0x06, 0xD5, 0xE1, 0xB2, + 0x47, 0xAB, 0x32, 0x46, 0x68, 0x68, 0x00, 0xF0, 0xD3, 0xF9, 0x28, 0x70, + 0x54, 0x99, 0x18, 0xEA, 0x01, 0x0F, 0x21, 0xD1, 0x57, 0x48, 0xBD, 0xF8, + 0x38, 0x11, 0x00, 0x68, 0x90, 0xF8, 0x69, 0x21, 0x91, 0x42, 0x07, 0xD8, + 0x90, 0xF8, 0x64, 0x01, 0x81, 0x07, 0x00, 0xE0, 0x1B, 0xE0, 0x03, 0xD4, + 0x40, 0x07, 0x01, 0xD4, 0x4F, 0xF0, 0x01, 0x0B, 0x54, 0xA9, 0x47, 0xAA, + 0xCD, 0xE9, 0x00, 0x21, 0x5E, 0x48, 0xCD, 0xF8, 0x08, 0xB0, 0xE1, 0xB2, + 0xB0, 0xF9, 0x00, 0x30, 0x03, 0xAA, 0x68, 0x68, 0xFF, 0xF7, 0x13, 0xFD, + 0x07, 0x00, 0x06, 0xD1, 0xBD, 0xF8, 0x38, 0x01, 0x47, 0xA9, 0xFF, 0xF7, + 0x49, 0xFD, 0x01, 0x28, 0x0E, 0xD0, 0xDF, 0xF8, 0x58, 0xB1, 0xBD, 0xF9, + 0x44, 0x11, 0xBB, 0xF9, 0x00, 0x00, 0x81, 0x42, 0x32, 0xDD, 0xFF, 0xF7, + 0x95, 0xFC, 0x01, 0x28, 0x63, 0xD0, 0x2D, 0xE0, 0xDD, 0xE0, 0x5F, 0xE0, + 0x9A, 0xF8, 0x11, 0x10, 0x4E, 0x4F, 0x41, 0xF0, 0x04, 0x01, 0x8A, 0xF8, + 0x11, 0x10, 0xBD, 0xF9, 0x44, 0x01, 0xB7, 0xF9, 0x00, 0x10, 0x4F, 0xF0, + 0x01, 0x09, 0x88, 0x42, 0x01, 0xDD, 0x01, 0x20, 0x55, 0x90, 0xE1, 0xB2, + 0x47, 0xAB, 0x32, 0x46, 0x68, 0x68, 0xFF, 0xF7, 0x3E, 0xFD, 0x42, 0x48, + 0xB0, 0xF9, 0x00, 0x20, 0xFF, 0xF7, 0x74, 0xFC, 0x01, 0x28, 0x01, 0xD1, + 0xB7, 0xF9, 0x00, 0x20, 0xBD, 0xF9, 0x44, 0x01, 0x90, 0x42, 0x01, 0xDD, + 0xFD, 0xF7, 0x91, 0xFE, 0xBD, 0xF9, 0x44, 0x01, 0xB7, 0xF9, 0x00, 0x10, + 0x88, 0x42, 0x33, 0xDA, 0x33, 0x48, 0x00, 0x78, 0x80, 0x06, 0x2F, 0xD5, + 0x38, 0x48, 0xBD, 0xF9, 0x44, 0x11, 0xB0, 0xF9, 0x00, 0x30, 0x99, 0x42, + 0x28, 0xDD, 0x2B, 0x4F, 0x35, 0x48, 0x39, 0x7C, 0x41, 0xF0, 0x10, 0x01, + 0x39, 0x74, 0xB0, 0xF9, 0x00, 0x00, 0x98, 0x42, 0x09, 0xDA, 0x00, 0x22, + 0x54, 0xA9, 0x47, 0xA8, 0x8D, 0xE8, 0x07, 0x00, 0xE1, 0xB2, 0x03, 0xAA, + 0x68, 0x68, 0xFF, 0xF7, 0xB0, 0xFC, 0x2D, 0x49, 0xDF, 0xF8, 0x54, 0xA0, + 0xBD, 0xF8, 0x38, 0x01, 0x88, 0x76, 0xDA, 0xF8, 0x00, 0x10, 0x91, 0xF8, + 0xD9, 0x10, 0x88, 0x42, 0x08, 0xD9, 0x38, 0x7C, 0x40, 0xF0, 0x20, 0x00, + 0x38, 0x74, 0x30, 0x46, 0xFB, 0xF7, 0xAD, 0xF8, 0x01, 0x28, 0x7D, 0xD0, + 0x9B, 0xE0, 0xDF, 0xF8, 0x58, 0xA0, 0x18, 0x48, 0x9A, 0xF8, 0x11, 0x10, + 0x41, 0xF0, 0x10, 0x01, 0x8A, 0xF8, 0x11, 0x10, 0x00, 0x78, 0x10, 0xF0, + 0x30, 0x0F, 0x43, 0xD0, 0x04, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x07, 0x02, + 0xFB, 0xF7, 0xDF, 0xFC, 0xE1, 0xB2, 0x47, 0xAB, 0x32, 0x46, 0x2F, 0xE0, + 0x50, 0x24, 0x10, 0x00, 0x60, 0x54, 0x10, 0x00, 0x9C, 0x23, 0x10, 0x00, + 0x05, 0x24, 0x10, 0x00, 0x06, 0x24, 0x10, 0x00, 0x07, 0x24, 0x10, 0x00, + 0x38, 0x23, 0x10, 0x00, 0x3C, 0x23, 0x10, 0x00, 0x6E, 0x23, 0x10, 0x00, + 0x60, 0x1D, 0x01, 0x00, 0x0C, 0x24, 0x10, 0x00, 0x58, 0x25, 0x10, 0x00, + 0x01, 0x24, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x1C, 0x24, 0x10, 0x00, + 0xC0, 0x23, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, 0xCE, 0x23, 0x10, 0x00, + 0xCC, 0x23, 0x10, 0x00, 0xD0, 0x23, 0x10, 0x00, 0xC6, 0x23, 0x10, 0x00, + 0xD6, 0x23, 0x10, 0x00, 0xD2, 0x23, 0x10, 0x00, 0x8A, 0x25, 0x10, 0x00, + 0x68, 0x68, 0xFF, 0xF7, 0xC8, 0xFD, 0x80, 0xB3, 0x9A, 0xF8, 0x11, 0x10, + 0x41, 0xF0, 0x20, 0x01, 0x8A, 0xF8, 0x11, 0x10, 0x54, 0x9A, 0x01, 0x20, + 0x18, 0xEA, 0x02, 0x0F, 0x0C, 0xD1, 0x47, 0xAA, 0x00, 0x92, 0x54, 0xA9, + 0xCD, 0xE9, 0x01, 0x10, 0xE1, 0xB2, 0xBB, 0xF9, 0x00, 0x30, 0x03, 0xAA, + 0x68, 0x68, 0xFF, 0xF7, 0x38, 0xFC, 0x07, 0x46, 0x00, 0x2F, 0x7F, 0xF4, + 0x36, 0xAF, 0xBD, 0xF8, 0x38, 0x01, 0x47, 0xA9, 0x00, 0xF0, 0x08, 0xF9, + 0x01, 0x28, 0xF6, 0xD1, 0x9A, 0xF8, 0x11, 0x10, 0x47, 0xAB, 0x41, 0xF0, + 0x40, 0x01, 0x8A, 0xF8, 0x11, 0x10, 0x57, 0x90, 0xE1, 0xB2, 0x32, 0x46, + 0x68, 0x68, 0x00, 0xF0, 0x19, 0xF9, 0x20, 0xE0, 0x00, 0xE0, 0x22, 0xE0, + 0x38, 0x7C, 0x40, 0xF0, 0x40, 0x00, 0x38, 0x74, 0xDA, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0xA5, 0x13, 0x05, 0x20, 0x02, 0xF0, 0x9E, 0xF9, 0xBD, 0xF8, + 0x36, 0x01, 0x4F, 0xF0, 0x01, 0x09, 0x40, 0xF0, 0x10, 0x00, 0xAD, 0xF8, + 0x36, 0x01, 0xBA, 0x48, 0xCD, 0xF8, 0x58, 0x91, 0x00, 0x78, 0x01, 0x28, + 0x05, 0xD1, 0xE1, 0xB2, 0x47, 0xAB, 0x32, 0x46, 0x68, 0x68, 0x00, 0xF0, + 0x36, 0xF9, 0x54, 0x99, 0x48, 0xEA, 0x01, 0x00, 0x54, 0x90, 0x64, 0x1C, + 0x9D, 0xF8, 0x1A, 0x01, 0xA0, 0x42, 0xBF, 0xF4, 0xD0, 0xAD, 0xB0, 0x49, + 0x55, 0x98, 0x60, 0xB9, 0x56, 0x98, 0x50, 0xB1, 0xAE, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0xDD, 0x20, 0x08, 0x68, 0x82, 0x42, 0x07, 0xDA, 0xA9, 0x49, + 0x01, 0x20, 0x08, 0x70, 0x05, 0xE0, 0xA7, 0x4A, 0x00, 0x20, 0x10, 0x70, + 0x00, 0xE0, 0x40, 0x1C, 0x08, 0x60, 0xA7, 0x49, 0x58, 0x98, 0x08, 0x70, + 0xA6, 0x49, 0x81, 0xF8, 0x00, 0x90, 0xA6, 0x49, 0x57, 0x98, 0x08, 0x70, + 0xA5, 0x49, 0x52, 0x98, 0x48, 0x81, 0xA5, 0x48, 0xA5, 0x49, 0x02, 0x78, + 0x88, 0x7A, 0x62, 0xF3, 0x41, 0x00, 0x2A, 0x78, 0x62, 0xF3, 0x45, 0x10, + 0x88, 0x72, 0x5D, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x9A, 0x49, 0x00, 0x20, + 0x08, 0x70, 0x9A, 0x49, 0x08, 0x70, 0x9A, 0x49, 0x08, 0x70, 0x9D, 0x49, + 0x08, 0x70, 0x99, 0x49, 0x08, 0x72, 0x48, 0x81, 0x91, 0x49, 0x08, 0x70, + 0x91, 0x49, 0x08, 0x60, 0x96, 0x49, 0x08, 0x70, 0x70, 0x47, 0x70, 0xB5, + 0x0E, 0x46, 0x15, 0x46, 0x04, 0x46, 0x34, 0x21, 0x06, 0xF0, 0xA5, 0xFC, + 0x00, 0x20, 0xA0, 0x73, 0x93, 0x48, 0x00, 0x68, 0x20, 0x61, 0x60, 0x61, + 0x26, 0x76, 0x25, 0x84, 0xA5, 0x83, 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, + 0x86, 0x4C, 0xDF, 0xF8, 0x3C, 0xC2, 0x00, 0x25, 0x24, 0x68, 0x9C, 0xF8, + 0x00, 0xC0, 0x9F, 0x8B, 0x94, 0xF8, 0xD9, 0x61, 0xBC, 0xF1, 0x00, 0x0F, + 0x02, 0xD0, 0x94, 0xF8, 0xDA, 0xC1, 0x66, 0x44, 0x93, 0xF8, 0x1A, 0xE0, + 0x94, 0xF8, 0xDB, 0xC1, 0x5F, 0xEA, 0xCE, 0x7E, 0x01, 0xD0, 0x94, 0xF8, + 0xDC, 0xC1, 0xB7, 0x42, 0x1B, 0xD9, 0x67, 0x45, 0x19, 0xD8, 0x82, 0x4E, + 0xB3, 0xF9, 0x28, 0x70, 0xB6, 0xF9, 0x00, 0x60, 0xB7, 0x42, 0x12, 0xDD, + 0xB4, 0xF8, 0xD6, 0x41, 0xDE, 0x6A, 0xA6, 0x42, 0x0D, 0xDA, 0x76, 0x4C, + 0x26, 0x7C, 0x46, 0xF0, 0x04, 0x06, 0x26, 0x74, 0x00, 0xF0, 0x63, 0xFA, + 0x01, 0x28, 0x04, 0xD1, 0x20, 0x7C, 0x01, 0x25, 0x40, 0xF0, 0x08, 0x00, + 0x20, 0x74, 0x28, 0x46, 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xF0, 0x41, + 0x68, 0x4E, 0x0F, 0x46, 0x80, 0x46, 0x31, 0x68, 0x14, 0x46, 0x6D, 0x48, + 0x1D, 0x46, 0xDA, 0x6A, 0xB1, 0xF8, 0xEE, 0x30, 0x00, 0x78, 0x9A, 0x42, + 0x01, 0xDD, 0x00, 0x20, 0xEC, 0xE7, 0xB1, 0xF8, 0xEC, 0x20, 0x00, 0x2A, + 0xE8, 0xD0, 0x6A, 0x4B, 0xB3, 0xF9, 0x00, 0x30, 0x9A, 0x42, 0xE3, 0xDA, + 0xAA, 0x8B, 0x04, 0x2A, 0xE0, 0xD9, 0x00, 0x28, 0xDE, 0xD1, 0xB1, 0xF9, + 0xEC, 0x20, 0x29, 0x46, 0x20, 0x46, 0x01, 0xF0, 0x6C, 0xF8, 0x00, 0x28, + 0xD6, 0xD1, 0x61, 0x78, 0x22, 0x78, 0x8A, 0x1A, 0x31, 0x68, 0x52, 0x1C, + 0x91, 0xF8, 0xEB, 0x10, 0x8A, 0x42, 0xCD, 0xDC, 0xE2, 0x78, 0xA3, 0x78, + 0xD2, 0x1A, 0x52, 0x1C, 0x8A, 0x42, 0xC7, 0xDC, 0x2B, 0x46, 0x22, 0x46, + 0x39, 0x46, 0x40, 0x46, 0xBD, 0xE8, 0xF0, 0x41, 0x00, 0xF0, 0x69, 0xBF, + 0x89, 0x7E, 0x4A, 0x4A, 0xCB, 0x07, 0x55, 0x49, 0x12, 0x68, 0x09, 0x78, + 0x06, 0xD0, 0x11, 0xB1, 0x92, 0xF8, 0xFC, 0x11, 0x05, 0xE0, 0x92, 0xF8, + 0xFD, 0x11, 0x02, 0xE0, 0x59, 0xB1, 0x92, 0xF8, 0xFA, 0x11, 0x4B, 0x4B, + 0x1B, 0x78, 0x13, 0xB1, 0x92, 0xF8, 0xFE, 0x21, 0x11, 0x44, 0x88, 0x42, + 0x04, 0xD9, 0x01, 0x20, 0x70, 0x47, 0x92, 0xF8, 0xFB, 0x11, 0xF2, 0xE7, + 0x00, 0x20, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0x0E, 0x46, 0x80, 0x46, + 0x04, 0x20, 0x45, 0x49, 0x98, 0x73, 0x98, 0x8B, 0x08, 0x82, 0x44, 0x48, + 0x1C, 0x46, 0x15, 0x46, 0x00, 0x78, 0x88, 0x74, 0x03, 0xF1, 0x22, 0x00, + 0x08, 0x60, 0x80, 0x1C, 0x48, 0x60, 0x04, 0xF1, 0x26, 0x00, 0x88, 0x60, + 0x0D, 0x38, 0xC8, 0x60, 0x0B, 0x46, 0x31, 0x46, 0x40, 0x46, 0x05, 0xF0, + 0x14, 0xFB, 0x2C, 0x4F, 0x38, 0x68, 0x90, 0xF8, 0xEA, 0x01, 0x81, 0x07, + 0x04, 0xD5, 0x21, 0x46, 0x28, 0x46, 0xFF, 0xF7, 0x4E, 0xFA, 0x05, 0xE0, + 0x40, 0x07, 0x03, 0xD5, 0x21, 0x46, 0x28, 0x46, 0xFF, 0xF7, 0x20, 0xFA, + 0x38, 0x68, 0x23, 0x46, 0x2A, 0x46, 0x90, 0xF8, 0xEA, 0x01, 0x31, 0x46, + 0x00, 0x07, 0x40, 0x46, 0x03, 0xD5, 0xBD, 0xE8, 0xF0, 0x41, 0x01, 0xF0, + 0x72, 0xB8, 0xBD, 0xE8, 0xF0, 0x41, 0xFF, 0xF7, 0xA7, 0xB9, 0x2D, 0xE9, + 0xF0, 0x41, 0x07, 0x46, 0x02, 0x20, 0x98, 0x73, 0x27, 0x48, 0x15, 0x46, + 0x0E, 0x46, 0xB3, 0xF9, 0x28, 0x10, 0xB0, 0xF9, 0x00, 0x20, 0x58, 0x8B, + 0x1C, 0x46, 0x91, 0x42, 0x02, 0xDD, 0x20, 0xF0, 0x10, 0x00, 0x01, 0xE0, + 0x40, 0xF0, 0x10, 0x00, 0x60, 0x83, 0x1D, 0x48, 0xA1, 0x8B, 0x03, 0x46, + 0x01, 0x82, 0x1C, 0x49, 0x2A, 0x46, 0x09, 0x78, 0x81, 0x74, 0x04, 0xF1, + 0x22, 0x01, 0x01, 0x60, 0x89, 0x1C, 0x41, 0x60, 0x89, 0x1C, 0x81, 0x60, + 0x0D, 0x39, 0xC1, 0x60, 0x31, 0x46, 0x38, 0x46, 0x05, 0xF0, 0xC7, 0xFA, + 0x23, 0x46, 0x2A, 0x46, 0x31, 0x46, 0x38, 0x46, 0xBD, 0xE8, 0xF0, 0x41, + 0xFF, 0xF7, 0x74, 0xB9, 0x38, 0x23, 0x10, 0x00, 0x3C, 0x23, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x05, 0x24, 0x10, 0x00, 0x06, 0x24, 0x10, 0x00, + 0x07, 0x24, 0x10, 0x00, 0x58, 0x25, 0x10, 0x00, 0x6E, 0x23, 0x10, 0x00, + 0x8A, 0x25, 0x10, 0x00, 0x9C, 0x23, 0x10, 0x00, 0x10, 0x24, 0x10, 0x00, + 0xB8, 0x23, 0x10, 0x00, 0xCE, 0x23, 0x10, 0x00, 0xCC, 0x23, 0x10, 0x00, + 0x2A, 0x24, 0x10, 0x00, 0x40, 0x50, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, + 0xC6, 0x23, 0x10, 0x00, 0x2D, 0xE9, 0xFF, 0x4F, 0x91, 0xB0, 0x0F, 0x46, + 0xDD, 0xE9, 0x1E, 0x29, 0x01, 0x2A, 0x99, 0xF8, 0x00, 0x10, 0x99, 0xF8, + 0x01, 0x00, 0x07, 0xD0, 0x00, 0x22, 0x82, 0x46, 0x0C, 0xAB, 0x10, 0x46, + 0x83, 0xE8, 0x07, 0x00, 0x2D, 0x20, 0x12, 0xE0, 0x3A, 0x78, 0x0E, 0x92, + 0x7A, 0x78, 0x82, 0x42, 0x00, 0xD2, 0x50, 0x1C, 0x00, 0xF0, 0xFF, 0x0A, + 0xB8, 0x78, 0x0C, 0x90, 0xF8, 0x78, 0x88, 0x42, 0x00, 0xD2, 0x41, 0x1C, + 0xC8, 0xB2, 0x0D, 0x90, 0xEC, 0xE7, 0x0D, 0xF8, 0x00, 0x00, 0x40, 0x1E, + 0xFB, 0xD2, 0x13, 0x98, 0x0C, 0x9A, 0x40, 0x1C, 0xC5, 0xB2, 0x99, 0xF8, + 0x01, 0x00, 0x11, 0x99, 0x80, 0x1C, 0x02, 0xFB, 0x00, 0x16, 0x50, 0x1C, + 0x3C, 0xE0, 0x99, 0xF8, 0x01, 0x00, 0x11, 0x99, 0xB3, 0x46, 0x80, 0x1C, + 0x08, 0xFB, 0x00, 0x16, 0x0E, 0x9C, 0x2E, 0xE0, 0x31, 0x19, 0x08, 0x68, + 0x80, 0xB1, 0x30, 0x5D, 0x48, 0xB3, 0x11, 0xF8, 0x01, 0x1C, 0x00, 0x20, + 0x01, 0xB1, 0x08, 0x46, 0x1B, 0xF8, 0x04, 0x10, 0x11, 0xB1, 0x6A, 0x46, + 0x00, 0xF0, 0xBB, 0xF8, 0x14, 0x99, 0x01, 0x29, 0x02, 0xD0, 0x10, 0xE0, + 0xE4, 0x1C, 0x18, 0xE0, 0x0B, 0xEB, 0x04, 0x01, 0x0F, 0x91, 0x11, 0xF8, + 0x01, 0x1C, 0x11, 0xB1, 0x6A, 0x46, 0x00, 0xF0, 0xAC, 0xF8, 0x0F, 0x99, + 0x49, 0x78, 0x11, 0xB1, 0x6A, 0x46, 0x00, 0xF0, 0xA6, 0xF8, 0x38, 0xB9, + 0x2D, 0x2D, 0x03, 0xD2, 0x28, 0x46, 0x6D, 0x1C, 0xED, 0xB2, 0x01, 0xE0, + 0x68, 0x1E, 0xC0, 0xB2, 0x30, 0x55, 0x64, 0x1C, 0x54, 0x45, 0xCD, 0xD9, + 0x08, 0xF1, 0x01, 0x00, 0x0D, 0x99, 0x80, 0x46, 0x88, 0x42, 0xBE, 0xD9, + 0x69, 0x46, 0x2C, 0x46, 0x07, 0xE0, 0xE8, 0xB2, 0x0B, 0x46, 0x00, 0xE0, + 0x10, 0x46, 0x1A, 0x5C, 0x82, 0x42, 0xFB, 0xD1, 0x48, 0x55, 0x6D, 0x1E, + 0xF5, 0xD2, 0x00, 0x23, 0x18, 0x46, 0x09, 0xE0, 0x0A, 0x5C, 0x82, 0x42, + 0x03, 0xD1, 0x0B, 0x54, 0x5B, 0x1C, 0xDB, 0xB2, 0x01, 0xE0, 0x8A, 0x5C, + 0x0A, 0x54, 0x40, 0x1C, 0xA0, 0x42, 0xF3, 0xD3, 0x00, 0x22, 0x13, 0x98, + 0x09, 0xE0, 0xD9, 0xF8, 0x04, 0x40, 0x07, 0xEB, 0x80, 0x01, 0x24, 0x68, + 0x0C, 0x60, 0x07, 0xEB, 0x40, 0x01, 0xA1, 0xF8, 0xB4, 0x20, 0x40, 0x1C, + 0x98, 0x42, 0xF2, 0xD3, 0x0C, 0x9A, 0x31, 0xE0, 0x99, 0xF8, 0x01, 0x00, + 0x54, 0x1C, 0x11, 0x99, 0x80, 0x1C, 0x04, 0xFB, 0x00, 0x14, 0x0E, 0x99, + 0x25, 0xE0, 0x65, 0x18, 0x01, 0x20, 0x40, 0x59, 0x68, 0xB3, 0x68, 0x78, + 0xF0, 0xB1, 0x1D, 0xF8, 0x00, 0x00, 0x68, 0x70, 0x07, 0xEB, 0x40, 0x05, + 0x07, 0xEB, 0x80, 0x00, 0x35, 0xF8, 0xB4, 0x6F, 0x76, 0x1C, 0x2E, 0x80, + 0x05, 0x78, 0xA9, 0x42, 0x00, 0xD2, 0x0D, 0x46, 0x05, 0x70, 0x45, 0x78, + 0xA9, 0x42, 0x00, 0xD9, 0x0D, 0x46, 0x45, 0x70, 0x85, 0x78, 0xAA, 0x42, + 0x00, 0xD2, 0x15, 0x46, 0x85, 0x70, 0xC5, 0x78, 0xAA, 0x42, 0x00, 0xD9, + 0x15, 0x46, 0xC5, 0x70, 0x49, 0x1C, 0x51, 0x45, 0xD7, 0xD3, 0x52, 0x1C, + 0x0D, 0x98, 0x82, 0x42, 0xCA, 0xD3, 0x5B, 0x1E, 0x87, 0xF8, 0x0E, 0x31, + 0x15, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0xC9, 0x1C, 0xF0, 0xE7, 0x2D, 0xE9, + 0xF0, 0x43, 0x02, 0xEB, 0x81, 0x04, 0x81, 0x46, 0xA5, 0x78, 0x4F, 0xF0, + 0x00, 0x08, 0x12, 0xE0, 0x58, 0x78, 0x6F, 0x1C, 0x80, 0x1C, 0x07, 0xFB, + 0x00, 0x9C, 0x20, 0x78, 0x07, 0xE0, 0x0C, 0xEB, 0x00, 0x06, 0x77, 0x78, + 0x8F, 0x42, 0x01, 0xD1, 0x86, 0xF8, 0x01, 0x80, 0x40, 0x1C, 0x66, 0x78, + 0x86, 0x42, 0xF4, 0xD2, 0x6D, 0x1C, 0xE0, 0x78, 0xA8, 0x42, 0xE9, 0xD2, + 0x58, 0x68, 0x00, 0x68, 0x20, 0x60, 0x02, 0xEB, 0x41, 0x00, 0xA0, 0xF8, + 0xB4, 0x80, 0xBD, 0xE8, 0xF0, 0x83, 0x38, 0xB1, 0x88, 0x42, 0x02, 0xD2, + 0x0B, 0x46, 0x01, 0x46, 0x18, 0x46, 0x88, 0x42, 0x00, 0xD0, 0x11, 0x54, + 0x08, 0x46, 0x70, 0x47, 0xF0, 0xB5, 0x1E, 0x4A, 0x87, 0xB0, 0x12, 0x68, + 0x02, 0xF5, 0xA0, 0x66, 0x99, 0xB1, 0x01, 0x29, 0x1B, 0xD0, 0x02, 0x29, + 0x23, 0xD0, 0x00, 0x21, 0x0D, 0x46, 0x4A, 0x19, 0xC8, 0x2A, 0x08, 0xDC, + 0x05, 0x97, 0xCD, 0xE9, 0x00, 0x14, 0x02, 0xAA, 0x69, 0x46, 0x82, 0xE8, + 0x28, 0x10, 0x05, 0xF0, 0x3C, 0xF9, 0x07, 0xB0, 0xF0, 0xBD, 0x92, 0xF8, + 0xFB, 0x14, 0x92, 0xF8, 0xFC, 0x54, 0x33, 0x46, 0x06, 0xEB, 0x41, 0x07, + 0x92, 0xF8, 0xFD, 0x24, 0x12, 0xE0, 0x92, 0xF8, 0xFB, 0x14, 0x92, 0xF8, + 0xFC, 0x54, 0x33, 0x46, 0x06, 0xEB, 0x41, 0x07, 0x92, 0xF8, 0xFE, 0x24, + 0x08, 0xE0, 0x92, 0xF8, 0xFB, 0x14, 0x92, 0xF8, 0xFC, 0x54, 0x92, 0xF8, + 0xFF, 0x24, 0x33, 0x46, 0x06, 0xEB, 0x41, 0x07, 0x03, 0xEB, 0x42, 0x04, + 0x04, 0xEB, 0x41, 0x0C, 0xCF, 0xE7, 0x00, 0x00, 0x50, 0x24, 0x10, 0x00, + 0x30, 0xB4, 0x11, 0x4B, 0x04, 0x46, 0x0F, 0x48, 0x1B, 0x68, 0xB0, 0xF9, + 0x00, 0x00, 0x93, 0xF8, 0xD0, 0x31, 0x1B, 0x07, 0x03, 0xD5, 0x20, 0x46, + 0x30, 0xBC, 0x00, 0xF0, 0x17, 0xB8, 0x30, 0xBC, 0x70, 0x47, 0x30, 0xB4, + 0x00, 0x25, 0x5D, 0x85, 0x07, 0x4D, 0x04, 0x46, 0x01, 0x20, 0x2D, 0x68, + 0x95, 0xF8, 0xD0, 0x51, 0x2D, 0x07, 0xF2, 0xD5, 0x20, 0x46, 0x30, 0xBC, + 0x00, 0xF0, 0x79, 0xB8, 0xED, 0xE7, 0x00, 0x00, 0xC8, 0x23, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x30, 0xB5, 0x64, 0x49, 0x65, 0x4C, 0xB1, 0xF9, + 0x00, 0x30, 0x24, 0x68, 0x62, 0x49, 0xB4, 0xF9, 0xE2, 0x51, 0xB1, 0xF9, + 0x00, 0x10, 0xB4, 0xF9, 0xE4, 0x41, 0x2A, 0xB9, 0x60, 0x49, 0xB1, 0xF9, + 0x00, 0x30, 0x60, 0x49, 0xB1, 0xF9, 0x00, 0x10, 0xA0, 0x42, 0x01, 0xDD, + 0x08, 0x46, 0x30, 0xBD, 0xA8, 0x42, 0x01, 0xDA, 0x18, 0x46, 0x30, 0xBD, + 0x20, 0x1A, 0xCA, 0x1A, 0x50, 0x43, 0x62, 0x1B, 0x90, 0xFB, 0xF2, 0xF0, + 0x08, 0x1A, 0x00, 0xB2, 0x30, 0xBD, 0x2D, 0xE9, 0xF0, 0x47, 0x47, 0x7A, + 0x89, 0x46, 0x06, 0x46, 0x04, 0x7A, 0x39, 0x46, 0x00, 0x69, 0x08, 0xF0, + 0x25, 0xF8, 0x80, 0x46, 0x78, 0x1E, 0xC1, 0xB2, 0x30, 0x69, 0x08, 0xF0, + 0x1F, 0xF8, 0x05, 0x46, 0x7F, 0x1C, 0xF9, 0xB2, 0x30, 0x69, 0x08, 0xF0, + 0x19, 0xF8, 0x03, 0x46, 0x38, 0xF9, 0x14, 0x60, 0x4F, 0xF0, 0xFF, 0x31, + 0x0A, 0x46, 0x5F, 0xEA, 0x09, 0x00, 0x07, 0xD0, 0x01, 0x28, 0x0A, 0xD0, + 0x02, 0x28, 0x0D, 0xD0, 0xB9, 0xF1, 0x03, 0x0F, 0x1B, 0xD1, 0x12, 0xE0, + 0x35, 0xF9, 0x14, 0x10, 0x33, 0xF9, 0x14, 0x20, 0x15, 0xE0, 0x08, 0xEB, + 0x44, 0x00, 0x30, 0xF9, 0x02, 0x1C, 0x0E, 0xE0, 0x05, 0xEB, 0x44, 0x00, + 0xB0, 0xF9, 0x02, 0x10, 0x03, 0xEB, 0x44, 0x00, 0x30, 0xF9, 0x02, 0x2C, + 0x07, 0xE0, 0x05, 0xEB, 0x44, 0x00, 0x30, 0xF9, 0x02, 0x1C, 0x03, 0xEB, + 0x44, 0x00, 0xB0, 0xF9, 0x02, 0x20, 0x48, 0x1C, 0x00, 0xD1, 0x11, 0x46, + 0x50, 0x1C, 0x00, 0xD1, 0x0A, 0x46, 0x50, 0x18, 0xC0, 0xEB, 0x46, 0x00, + 0x00, 0xB2, 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xFC, 0x5F, 0x00, 0x26, + 0x00, 0x96, 0x99, 0x7E, 0x1D, 0x46, 0xB2, 0x46, 0xB0, 0x46, 0x30, 0x46, + 0x89, 0x07, 0x07, 0xD0, 0x27, 0x49, 0x09, 0x68, 0xB1, 0xF8, 0xE4, 0x11, + 0x89, 0x1C, 0x0C, 0xB2, 0x5C, 0x85, 0x35, 0xE0, 0x01, 0x21, 0x18, 0x46, + 0xFF, 0xF7, 0x9B, 0xFF, 0x82, 0x46, 0x00, 0x21, 0x28, 0x46, 0xFF, 0xF7, + 0x96, 0xFF, 0x80, 0x46, 0x50, 0x44, 0x00, 0xB2, 0x01, 0x90, 0x68, 0x7A, + 0x2F, 0x7A, 0x44, 0x1E, 0x07, 0xF1, 0x01, 0x09, 0x00, 0xF1, 0x01, 0x0B, + 0x0E, 0xE0, 0xE1, 0xB2, 0x28, 0x69, 0x07, 0xF0, 0xB5, 0xFF, 0x01, 0x46, + 0x78, 0x1E, 0x04, 0xE0, 0x31, 0xF8, 0x10, 0x20, 0x40, 0x1C, 0x32, 0x44, + 0x16, 0xB2, 0x48, 0x45, 0xF8, 0xDD, 0x64, 0x1C, 0x5C, 0x45, 0xEE, 0xDD, + 0x01, 0x98, 0xFA, 0x21, 0x48, 0x43, 0x90, 0xFB, 0xF6, 0xF0, 0x04, 0xB2, + 0x6C, 0x85, 0x69, 0x8B, 0x01, 0x22, 0x20, 0x46, 0xFF, 0xF7, 0x48, 0xFF, + 0xB5, 0xF9, 0x28, 0x10, 0x81, 0x42, 0x01, 0xDD, 0x01, 0x21, 0x00, 0x91, + 0x0A, 0x49, 0x8C, 0x80, 0xCE, 0x80, 0x08, 0x81, 0xA1, 0xF8, 0x0A, 0x80, + 0xA1, 0xF8, 0x0C, 0xA0, 0x00, 0x98, 0xBD, 0xE8, 0xFC, 0x9F, 0x00, 0x00, + 0xCC, 0x23, 0x10, 0x00, 0xCE, 0x23, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, + 0xC6, 0x23, 0x10, 0x00, 0xC8, 0x23, 0x10, 0x00, 0xA8, 0x25, 0x10, 0x00, + 0x2D, 0xE9, 0xFF, 0x5F, 0x47, 0xF6, 0xFF, 0x76, 0x9B, 0x46, 0x17, 0x46, + 0x89, 0x46, 0xF5, 0x43, 0x94, 0x78, 0xDD, 0xF8, 0x3C, 0xA0, 0x1C, 0xE0, + 0xE1, 0xB2, 0x00, 0x98, 0x07, 0xF0, 0x64, 0xFF, 0x80, 0x46, 0xE1, 0xB2, + 0xDA, 0xF8, 0x14, 0x00, 0x07, 0xF0, 0x68, 0xFF, 0x39, 0x78, 0x7B, 0x78, + 0x0C, 0xE0, 0x18, 0xF8, 0x01, 0xC0, 0xCC, 0x45, 0x07, 0xD1, 0x30, 0xF9, + 0x11, 0x20, 0xB2, 0x42, 0x00, 0xDA, 0x16, 0x46, 0xAA, 0x42, 0x00, 0xDD, + 0x15, 0x46, 0x49, 0x1C, 0x8B, 0x42, 0xF0, 0xD2, 0x64, 0x1C, 0xF8, 0x78, + 0xA0, 0x42, 0xDF, 0xD2, 0xAB, 0xF8, 0x00, 0x60, 0x0E, 0x98, 0x05, 0x80, + 0xBD, 0xE8, 0xFF, 0x9F, 0x2D, 0xE9, 0xFF, 0x4F, 0xFF, 0x48, 0x85, 0xB0, + 0x1E, 0x46, 0x00, 0x68, 0x15, 0x46, 0x03, 0xAB, 0x90, 0xF8, 0x6E, 0x41, + 0x02, 0xA8, 0xCD, 0xE9, 0x00, 0x06, 0xDD, 0xE9, 0x05, 0x01, 0xFF, 0xF7, + 0xBD, 0xFF, 0xBD, 0xF9, 0x08, 0x10, 0xBD, 0xF9, 0x0C, 0x00, 0x64, 0x22, + 0x09, 0x1A, 0x61, 0x43, 0x91, 0xFB, 0xF2, 0xF1, 0xF4, 0x4F, 0x0C, 0x18, + 0x4F, 0xF4, 0x88, 0x61, 0x38, 0x68, 0x06, 0xF0, 0xA6, 0xF8, 0x05, 0x98, + 0x01, 0x90, 0x38, 0x68, 0x00, 0x90, 0x0F, 0xFA, 0x84, 0xF8, 0xAC, 0x78, + 0x4F, 0xF0, 0x01, 0x0B, 0x06, 0x9F, 0x1F, 0xE0, 0xE1, 0xB2, 0x01, 0x98, + 0x07, 0xF0, 0x12, 0xFF, 0x82, 0x46, 0xE1, 0xB2, 0x00, 0x98, 0x07, 0xF0, + 0x0D, 0xFF, 0x81, 0x46, 0xE1, 0xB2, 0x70, 0x69, 0x07, 0xF0, 0x12, 0xFF, + 0x29, 0x78, 0x5B, 0x46, 0x0A, 0xE0, 0x1A, 0xF8, 0x01, 0x20, 0xBA, 0x42, + 0x05, 0xD1, 0x30, 0xF9, 0x11, 0xC0, 0xC4, 0x45, 0x01, 0xDD, 0x09, 0xF8, + 0x01, 0x30, 0x49, 0x1C, 0x6A, 0x78, 0x8A, 0x42, 0xF1, 0xD2, 0x64, 0x1C, + 0xE8, 0x78, 0xA0, 0x42, 0xDC, 0xD2, 0xDB, 0x4C, 0x00, 0x20, 0xDB, 0x49, + 0x84, 0xF8, 0x0E, 0x01, 0x28, 0x68, 0x20, 0x60, 0xCD, 0xE9, 0x00, 0xB1, + 0xD5, 0x4F, 0x01, 0x23, 0x00, 0x22, 0x21, 0x46, 0x38, 0x68, 0xFF, 0xF7, + 0x1F, 0xFD, 0xD5, 0x49, 0x06, 0xF1, 0x24, 0x00, 0x48, 0x60, 0x80, 0x1C, + 0x88, 0x60, 0x94, 0xF8, 0x0E, 0x01, 0x01, 0x28, 0x1F, 0xD9, 0x01, 0x25, + 0xB9, 0x46, 0x88, 0x46, 0x14, 0xE0, 0x04, 0xEB, 0x45, 0x00, 0xCD, 0x49, + 0xB0, 0xF8, 0xB4, 0x00, 0xB0, 0x83, 0xA8, 0xF8, 0x10, 0x00, 0x04, 0xEB, + 0x85, 0x00, 0x07, 0x46, 0x04, 0xF0, 0x80, 0xFF, 0x3A, 0x46, 0xE9, 0xB2, + 0x33, 0x46, 0xD9, 0xF8, 0x00, 0x00, 0xFE, 0xF7, 0x4D, 0xFE, 0x6D, 0x1C, + 0x94, 0xF8, 0x0E, 0x01, 0xA8, 0x42, 0xE6, 0xD2, 0x09, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0xDD, 0xE9, 0x05, 0x01, 0x33, 0x46, 0x2A, 0x46, 0xFE, 0xF7, + 0x3F, 0xFE, 0xF5, 0xE7, 0x2D, 0xE9, 0xF0, 0x5F, 0x91, 0x46, 0x02, 0x46, + 0xB6, 0x48, 0x0C, 0x46, 0xBA, 0x49, 0x00, 0x68, 0xDD, 0xE9, 0x0B, 0xB7, + 0x09, 0x78, 0xDD, 0xF8, 0x28, 0x80, 0x90, 0xF8, 0x70, 0xA1, 0x1E, 0x46, + 0x09, 0x07, 0x01, 0xD5, 0x90, 0xF8, 0xB0, 0xA2, 0x41, 0x46, 0x10, 0x46, + 0x07, 0xF0, 0x9A, 0xFE, 0x05, 0x46, 0x41, 0x46, 0x20, 0x46, 0x07, 0xF0, + 0x95, 0xFE, 0x04, 0x46, 0x41, 0x46, 0x78, 0x69, 0x07, 0xF0, 0x9A, 0xFE, + 0x07, 0x46, 0x30, 0x78, 0x73, 0x78, 0x48, 0xB9, 0x29, 0x78, 0x49, 0x45, + 0x05, 0xD1, 0xB7, 0xF9, 0x00, 0x10, 0x59, 0x45, 0x01, 0xDD, 0x01, 0x20, + 0x20, 0x70, 0x01, 0x20, 0xDF, 0xF8, 0x98, 0x82, 0x98, 0xF8, 0x00, 0x10, + 0x49, 0x1E, 0x8B, 0x42, 0x3B, 0xD1, 0xEA, 0x5C, 0x4A, 0x45, 0x05, 0xD1, + 0x37, 0xF9, 0x13, 0x20, 0x5A, 0x45, 0x01, 0xDD, 0x01, 0x21, 0xE1, 0x54, + 0x5B, 0x1E, 0xDB, 0xB2, 0x2F, 0xE0, 0x2A, 0x5C, 0x4A, 0x45, 0x2B, 0xD1, + 0x37, 0xF9, 0x10, 0x10, 0x59, 0x45, 0x27, 0xDD, 0x07, 0xEB, 0x40, 0x02, + 0x51, 0x44, 0x32, 0xF9, 0x02, 0xEC, 0x00, 0x26, 0x09, 0xB2, 0x8E, 0x45, + 0x04, 0xDD, 0xB2, 0xF9, 0x02, 0xC0, 0x8C, 0x45, 0x00, 0xDD, 0x01, 0x26, + 0x01, 0x28, 0x08, 0xD9, 0x32, 0xF9, 0x04, 0xCC, 0x8C, 0x45, 0x04, 0xDD, + 0xB2, 0xF9, 0x02, 0xC0, 0x8C, 0x45, 0x00, 0xDD, 0x01, 0x26, 0x98, 0xF8, + 0x00, 0xC0, 0xAC, 0xF1, 0x02, 0x0C, 0x60, 0x45, 0x05, 0xD2, 0x8E, 0x45, + 0x03, 0xDD, 0xB2, 0xF9, 0x04, 0x20, 0x8A, 0x42, 0x02, 0xDC, 0x0E, 0xB9, + 0x01, 0x21, 0x21, 0x54, 0x40, 0x1C, 0x98, 0x42, 0xCD, 0xD9, 0xBD, 0xE8, + 0xF0, 0x9F, 0x2D, 0xE9, 0xFF, 0x4F, 0x7C, 0x4C, 0x8F, 0xB0, 0x82, 0x4D, + 0x20, 0x68, 0xDD, 0xF8, 0x70, 0xB0, 0x9A, 0x46, 0x90, 0xF8, 0x72, 0x01, + 0xC0, 0xB1, 0x04, 0xA8, 0xCD, 0xE9, 0x00, 0x0B, 0x05, 0xAB, 0x52, 0x46, + 0x11, 0x99, 0x0F, 0x98, 0xFF, 0xF7, 0xB2, 0xFE, 0xBD, 0xF9, 0x10, 0x00, + 0xBD, 0xF9, 0x14, 0x10, 0x40, 0x1A, 0x21, 0x68, 0x91, 0xF8, 0x72, 0x11, + 0x48, 0x43, 0x64, 0x21, 0x90, 0xFB, 0xF1, 0xF0, 0x29, 0x88, 0x08, 0x44, + 0x00, 0xB2, 0x01, 0xE0, 0xB5, 0xF9, 0x00, 0x00, 0x03, 0x90, 0x20, 0x68, + 0x27, 0x46, 0x90, 0xF8, 0x70, 0x11, 0x06, 0x91, 0x90, 0xF8, 0x71, 0x11, + 0x05, 0x91, 0x6A, 0x49, 0x09, 0x78, 0x09, 0x07, 0x05, 0xD5, 0x90, 0xF8, + 0xB0, 0x12, 0x06, 0x91, 0x90, 0xF8, 0xB1, 0x02, 0x05, 0x90, 0x9A, 0xF8, + 0x02, 0x40, 0x9A, 0xF8, 0x03, 0x00, 0x04, 0x90, 0x5C, 0xB9, 0x03, 0x99, + 0x0F, 0xAD, 0xCD, 0xE9, 0x00, 0x41, 0xCD, 0xF8, 0x08, 0xB0, 0x95, 0xE8, + 0x07, 0x00, 0x53, 0x46, 0xFF, 0xF7, 0x3E, 0xFF, 0x01, 0x24, 0x5F, 0x49, + 0x04, 0x98, 0x09, 0x78, 0x49, 0x1E, 0x88, 0x42, 0x0F, 0xD1, 0xDD, 0xE9, + 0x03, 0x12, 0xCD, 0xE9, 0x00, 0x21, 0x0F, 0xAD, 0xCD, 0xF8, 0x08, 0xB0, + 0x95, 0xE8, 0x07, 0x00, 0x53, 0x46, 0xFF, 0xF7, 0x2B, 0xFF, 0x04, 0x98, + 0x40, 0x1E, 0xC0, 0xB2, 0x04, 0x90, 0x60, 0x1E, 0xC1, 0xB2, 0xDB, 0xF8, + 0x14, 0x00, 0x07, 0xF0, 0xDD, 0xFD, 0x06, 0x46, 0x01, 0x2C, 0x07, 0xD9, + 0xA0, 0x1E, 0xC1, 0xB2, 0xDB, 0xF8, 0x14, 0x00, 0x07, 0xF0, 0xD4, 0xFD, + 0x01, 0x90, 0x00, 0xE0, 0x01, 0x96, 0x21, 0x46, 0xDB, 0xF8, 0x14, 0x00, + 0x07, 0xF0, 0xCC, 0xFD, 0x00, 0x90, 0x60, 0x1C, 0xC1, 0xB2, 0xDB, 0xF8, + 0x14, 0x00, 0x07, 0xF0, 0xC5, 0xFD, 0x05, 0x46, 0x01, 0x20, 0x09, 0x90, + 0x38, 0x68, 0x90, 0xF8, 0xD4, 0x10, 0x49, 0x07, 0x17, 0xD5, 0x9A, 0xF8, + 0x01, 0x20, 0x9A, 0xF8, 0x00, 0x10, 0x52, 0x1A, 0x90, 0xF8, 0xEB, 0x10, + 0x52, 0x1C, 0x8A, 0x42, 0x0D, 0xDC, 0x9A, 0xF8, 0x03, 0x30, 0x9A, 0xF8, + 0x02, 0x20, 0x9A, 0x1A, 0x52, 0x1C, 0x8A, 0x42, 0x05, 0xDC, 0xB0, 0xF8, + 0xEE, 0x00, 0xDB, 0xF8, 0x2C, 0x10, 0x81, 0x42, 0x01, 0xDD, 0x00, 0x20, + 0x09, 0x90, 0x00, 0x20, 0x08, 0x90, 0xD4, 0xE0, 0xE1, 0xB2, 0x0F, 0x98, + 0x07, 0xF0, 0x92, 0xFD, 0x0E, 0x90, 0xE1, 0xB2, 0x10, 0x98, 0x07, 0xF0, + 0x8D, 0xFD, 0x07, 0x90, 0xA0, 0x1C, 0xC1, 0xB2, 0xDB, 0xF8, 0x14, 0x00, + 0x07, 0xF0, 0x90, 0xFD, 0x86, 0x46, 0x9A, 0xF8, 0x00, 0x20, 0xB6, 0xE0, + 0x0E, 0x98, 0x81, 0x5C, 0x11, 0x98, 0x81, 0x42, 0x7E, 0xD1, 0x00, 0x98, + 0xDD, 0xF8, 0x14, 0xC0, 0x00, 0x21, 0x30, 0xF9, 0x12, 0x30, 0x06, 0x98, + 0x02, 0x93, 0x18, 0x44, 0x63, 0x44, 0x0F, 0xFA, 0x83, 0xFC, 0x36, 0xF9, + 0x12, 0x30, 0x00, 0xB2, 0x89, 0x46, 0x0F, 0x46, 0x0D, 0x93, 0x83, 0x42, + 0x05, 0xDD, 0x35, 0xF9, 0x12, 0x30, 0x83, 0x42, 0x01, 0xDD, 0x01, 0x21, + 0x0F, 0x46, 0x9A, 0xB3, 0x16, 0x4B, 0x1B, 0x78, 0x0C, 0x93, 0x5B, 0x1E, + 0x9A, 0x42, 0x7A, 0xD0, 0x00, 0x9B, 0x03, 0xEB, 0x42, 0x03, 0x33, 0xF9, + 0x02, 0x8C, 0xCD, 0xF8, 0x2C, 0x80, 0x80, 0x45, 0x05, 0xDD, 0xB3, 0xF9, + 0x02, 0x80, 0x80, 0x45, 0x01, 0xDD, 0x01, 0x21, 0x89, 0x46, 0x06, 0xEB, + 0x42, 0x08, 0xCD, 0xF8, 0x28, 0x80, 0x38, 0xF9, 0x02, 0x8C, 0xE0, 0x45, + 0x1A, 0xDD, 0x05, 0xEB, 0x42, 0x08, 0xB8, 0xF9, 0x02, 0x80, 0x12, 0xE0, + 0x50, 0x24, 0x10, 0x00, 0xA4, 0x23, 0x10, 0x00, 0x54, 0x50, 0x10, 0x00, + 0xAC, 0x23, 0x10, 0x00, 0x40, 0x50, 0x10, 0x00, 0x1C, 0x24, 0x10, 0x00, + 0x01, 0x24, 0x10, 0x00, 0xCC, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, + 0x4B, 0xE0, 0xE0, 0x45, 0x00, 0xDD, 0x01, 0x21, 0xDD, 0xF8, 0x28, 0x80, + 0xB8, 0xF9, 0x02, 0x80, 0xE0, 0x45, 0x05, 0xDD, 0x05, 0xEB, 0x42, 0x08, + 0x38, 0xF9, 0x02, 0x8C, 0xE0, 0x45, 0x47, 0xDC, 0xE9, 0xBB, 0x01, 0x2C, + 0x0B, 0xD9, 0xDD, 0xF8, 0x04, 0xC0, 0x3C, 0xF9, 0x12, 0xC0, 0x84, 0x45, + 0x05, 0xDD, 0x35, 0xF9, 0x12, 0xC0, 0x84, 0x45, 0x01, 0xDD, 0x01, 0x21, + 0x0F, 0x46, 0xDF, 0xF8, 0x78, 0xC5, 0x9C, 0xF8, 0x00, 0xC0, 0xAC, 0xF1, + 0x02, 0x0C, 0x64, 0x45, 0x0B, 0xD2, 0xDD, 0xF8, 0x34, 0xC0, 0x84, 0x45, + 0x07, 0xDD, 0x3E, 0xF9, 0x12, 0xC0, 0x84, 0x45, 0x03, 0xDD, 0x00, 0xE0, + 0x30, 0xE0, 0x01, 0x21, 0x0F, 0x46, 0x01, 0x2A, 0x09, 0xD9, 0x33, 0xF9, + 0x04, 0xCC, 0x84, 0x45, 0x05, 0xDD, 0xB3, 0xF9, 0x02, 0xC0, 0x84, 0x45, + 0x01, 0xDD, 0x01, 0x21, 0x89, 0x46, 0xDD, 0xF8, 0x30, 0xC0, 0xAC, 0xF1, + 0x02, 0x0C, 0x62, 0x45, 0x07, 0xD2, 0xDD, 0xF8, 0x2C, 0xC0, 0x84, 0x45, + 0x03, 0xDD, 0xB3, 0xF9, 0x04, 0x30, 0x83, 0x42, 0x0D, 0xDC, 0x01, 0xB9, + 0x00, 0xE0, 0x07, 0xE0, 0xDD, 0xE9, 0x02, 0x01, 0x88, 0x42, 0x03, 0xDD, + 0x07, 0x98, 0x01, 0x21, 0x81, 0x54, 0x09, 0xE0, 0xB9, 0xF1, 0x00, 0x0F, + 0x06, 0xD0, 0x2F, 0xB1, 0x09, 0x98, 0x18, 0xB1, 0x07, 0x99, 0x01, 0x20, + 0x88, 0x54, 0x08, 0x90, 0x52, 0x1C, 0x9A, 0xF8, 0x01, 0x00, 0x90, 0x42, + 0xBF, 0xF4, 0x44, 0xAF, 0x01, 0x96, 0x00, 0x9E, 0x00, 0x95, 0x75, 0x46, + 0x64, 0x1C, 0x04, 0x98, 0x84, 0x42, 0x7F, 0xF6, 0x27, 0xAF, 0x08, 0x98, + 0x13, 0xB0, 0xFE, 0xE5, 0x2D, 0xE9, 0xF0, 0x47, 0x04, 0x46, 0x91, 0x42, + 0x60, 0xD0, 0x07, 0xD9, 0x01, 0xEB, 0x02, 0x00, 0xA0, 0xEB, 0x02, 0x01, + 0xCA, 0xB2, 0xA0, 0xEB, 0x02, 0x00, 0xC1, 0xB2, 0x01, 0xEB, 0x81, 0x00, + 0x00, 0xEB, 0xC1, 0x00, 0x04, 0xEB, 0x80, 0x01, 0x02, 0xEB, 0x82, 0x00, + 0x00, 0xEB, 0xC2, 0x00, 0x04, 0xEB, 0x80, 0x09, 0xB1, 0xF9, 0x2C, 0x50, + 0xB9, 0xF9, 0x2C, 0x00, 0x09, 0xF1, 0x04, 0x03, 0x01, 0xF1, 0x04, 0x01, + 0x85, 0x42, 0x00, 0xDA, 0x08, 0x85, 0x88, 0x8B, 0x9D, 0x8B, 0x28, 0x44, + 0x88, 0x83, 0xC8, 0x8B, 0xDD, 0x8B, 0x28, 0x44, 0xC8, 0x83, 0x08, 0x8C, + 0x1D, 0x8C, 0x28, 0x44, 0x08, 0x84, 0xC8, 0x6A, 0xDD, 0x6A, 0x28, 0x44, + 0xC8, 0x62, 0x1D, 0x6B, 0x0E, 0x6B, 0x0F, 0x68, 0x1B, 0x68, 0x4F, 0xEA, + 0x27, 0x4C, 0x4F, 0xEA, 0x23, 0x48, 0x0C, 0xFB, 0x06, 0xFC, 0x05, 0xFB, + 0x08, 0xCC, 0x70, 0x19, 0x9C, 0xFB, 0xF0, 0xFC, 0x6C, 0xF3, 0x1F, 0x47, + 0x0F, 0xFA, 0x87, 0xFC, 0x0C, 0xFB, 0x06, 0xFC, 0x1B, 0xB2, 0x05, 0xFB, + 0x03, 0xC3, 0x93, 0xFB, 0xF0, 0xF3, 0x63, 0xF3, 0x0F, 0x07, 0x0F, 0x60, + 0x08, 0x63, 0x20, 0x78, 0x41, 0x1E, 0x91, 0x42, 0x0B, 0xD0, 0x00, 0xEB, + 0x80, 0x01, 0x01, 0xEB, 0xC0, 0x00, 0x04, 0xEB, 0x80, 0x01, 0x09, 0xF1, + 0x04, 0x00, 0x34, 0x22, 0x30, 0x39, 0x05, 0xF0, 0xA2, 0xFD, 0x20, 0x78, + 0x40, 0x1E, 0x20, 0x70, 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xFC, 0x5F, + 0x07, 0x46, 0xFF, 0x48, 0x00, 0x24, 0x0D, 0x46, 0x02, 0x68, 0xB2, 0xF8, + 0x74, 0x01, 0x40, 0x43, 0x01, 0x90, 0xFC, 0x48, 0x00, 0x78, 0x00, 0x07, + 0x03, 0xD5, 0xB2, 0xF8, 0xB2, 0x02, 0x40, 0x43, 0x01, 0x90, 0x4F, 0xF0, + 0x64, 0x08, 0x84, 0xE0, 0x01, 0x20, 0xA8, 0x40, 0x2E, 0x46, 0x00, 0x90, + 0x5A, 0xE0, 0x4F, 0xF0, 0x01, 0x0E, 0x00, 0x99, 0x0E, 0xFA, 0x06, 0xF0, + 0x08, 0x42, 0x52, 0xD0, 0x06, 0xEB, 0x86, 0x00, 0x00, 0xEB, 0xC6, 0x00, + 0x07, 0xEB, 0x80, 0x00, 0xD0, 0xF8, 0x04, 0x90, 0x28, 0x46, 0x3D, 0xE0, + 0xB0, 0x42, 0x3A, 0xD0, 0x00, 0xEB, 0x80, 0x01, 0x01, 0xEB, 0xC0, 0x01, + 0x07, 0xEB, 0x81, 0x01, 0x4A, 0x68, 0x13, 0x14, 0xA3, 0xEB, 0x29, 0x41, + 0xE4, 0x4B, 0x09, 0xB2, 0xA2, 0xEB, 0x09, 0x02, 0x1B, 0x68, 0x61, 0xF3, + 0x1F, 0x44, 0x12, 0xB2, 0x93, 0xF8, 0x64, 0xC1, 0x62, 0xF3, 0x0F, 0x04, + 0xE2, 0x46, 0x5F, 0xEA, 0x8C, 0x6C, 0x08, 0xD5, 0x93, 0xF8, 0x73, 0xC1, + 0x02, 0xFB, 0x0C, 0xFC, 0x9C, 0xFB, 0xF8, 0xFC, 0x62, 0x44, 0x62, 0xF3, + 0x0F, 0x04, 0x5F, 0xEA, 0xCA, 0x62, 0x07, 0xD5, 0x93, 0xF8, 0x73, 0x21, + 0x4A, 0x43, 0x92, 0xFB, 0xF8, 0xF2, 0x11, 0x44, 0x61, 0xF3, 0x1F, 0x44, + 0x22, 0x14, 0x52, 0x43, 0x21, 0xB2, 0x01, 0xFB, 0x01, 0x21, 0x01, 0x9A, + 0x91, 0x42, 0x04, 0xD2, 0x00, 0x9A, 0x0E, 0xFA, 0x00, 0xF1, 0x11, 0x43, + 0x00, 0x91, 0x40, 0x1C, 0x83, 0x45, 0xBF, 0xD8, 0x04, 0x21, 0x68, 0x46, + 0x07, 0xF0, 0x42, 0xFE, 0x01, 0x28, 0x02, 0xD1, 0x00, 0x98, 0x40, 0x00, + 0x00, 0x90, 0x76, 0x1C, 0x38, 0x78, 0x83, 0x46, 0xB0, 0x42, 0xA0, 0xD8, + 0x04, 0x21, 0x68, 0x46, 0x07, 0xF0, 0x34, 0xFE, 0x01, 0x28, 0x1E, 0xD0, + 0x00, 0x99, 0x01, 0x22, 0x02, 0xFA, 0x05, 0xF0, 0x08, 0x42, 0x02, 0xD1, + 0x6D, 0x1C, 0xED, 0xB2, 0xF8, 0xE7, 0x3E, 0x78, 0x91, 0x46, 0x09, 0xE0, + 0x00, 0x99, 0x09, 0xFA, 0x06, 0xF0, 0x08, 0x42, 0x04, 0xD0, 0xF2, 0xB2, + 0x29, 0x46, 0x38, 0x46, 0xFF, 0xF7, 0x02, 0xFF, 0x76, 0x1E, 0xAE, 0x42, + 0xF2, 0xD8, 0x6D, 0x1C, 0xED, 0xB2, 0x38, 0x78, 0xA8, 0x42, 0x3F, 0xF6, + 0x77, 0xAF, 0xBD, 0xE8, 0xFC, 0x9F, 0x2D, 0xE9, 0xFF, 0x4F, 0xDF, 0xF8, + 0xC4, 0xA2, 0x83, 0xB0, 0x1D, 0x46, 0x4F, 0xF4, 0x88, 0x61, 0x54, 0x46, + 0xDA, 0xF8, 0x00, 0x00, 0x05, 0xF0, 0x29, 0xFD, 0x00, 0x95, 0xDD, 0xE9, + 0x04, 0x23, 0x21, 0x68, 0x03, 0x98, 0xFF, 0xF7, 0x62, 0xFD, 0x06, 0x46, + 0xA8, 0x4F, 0x00, 0x20, 0xA8, 0x49, 0x87, 0xF8, 0x0E, 0x01, 0x05, 0x98, + 0x4F, 0xF0, 0x01, 0x08, 0x33, 0x46, 0x00, 0x68, 0x38, 0x60, 0xCD, 0xE9, + 0x00, 0x81, 0x00, 0x22, 0x39, 0x46, 0x20, 0x68, 0xFF, 0xF7, 0xC6, 0xF9, + 0x01, 0x2E, 0x02, 0xD1, 0xA0, 0x49, 0x81, 0xF8, 0x00, 0x80, 0x97, 0xF8, + 0x0E, 0x31, 0x00, 0x26, 0x01, 0x2B, 0x63, 0xD9, 0xDF, 0xF8, 0x5C, 0x92, + 0x01, 0x21, 0x08, 0x46, 0xD9, 0xF8, 0x00, 0x20, 0x09, 0xE0, 0x07, 0xEB, + 0x40, 0x04, 0x92, 0xF8, 0x69, 0xC1, 0xB4, 0xF8, 0xB4, 0x40, 0x64, 0x45, + 0x00, 0xD9, 0x00, 0x21, 0x40, 0x1C, 0x83, 0x42, 0xF3, 0xD2, 0x89, 0xB3, + 0x02, 0x20, 0xA8, 0x73, 0xFD, 0xF7, 0xC2, 0xFB, 0xDF, 0xF8, 0x44, 0x82, + 0x05, 0xF1, 0x24, 0x01, 0x83, 0x46, 0xC8, 0xF8, 0x04, 0x10, 0x89, 0x1C, + 0x01, 0x24, 0xC8, 0xF8, 0x08, 0x10, 0x2E, 0xE0, 0xD9, 0xF8, 0x00, 0x10, + 0x07, 0xEB, 0x44, 0x00, 0xB0, 0xF8, 0xB4, 0x20, 0x91, 0xF8, 0x6F, 0x11, + 0x8A, 0x42, 0x18, 0xD9, 0x69, 0x8B, 0x41, 0xF0, 0x04, 0x01, 0x69, 0x83, + 0xB0, 0xF8, 0xB4, 0x00, 0xA8, 0x83, 0xA8, 0xF8, 0x10, 0x00, 0x07, 0xEB, + 0x84, 0x00, 0x06, 0x46, 0x80, 0x49, 0x04, 0xF0, 0xFB, 0xFB, 0x32, 0x46, + 0xE1, 0xB2, 0x2B, 0x46, 0xDA, 0xF8, 0x00, 0x00, 0xFE, 0xF7, 0xC8, 0xFA, + 0x01, 0x26, 0x0B, 0xE0, 0x1A, 0xE0, 0x7B, 0x48, 0x00, 0x78, 0x01, 0x28, + 0x06, 0xD1, 0x7A, 0x48, 0x00, 0x78, 0x18, 0xB9, 0x07, 0xEB, 0x84, 0x00, + 0xFB, 0xF7, 0x18, 0xFC, 0x64, 0x1C, 0x97, 0xF8, 0x0E, 0x01, 0xA0, 0x42, + 0xCC, 0xD2, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x64, 0x01, 0x00, 0x07, + 0x04, 0xD5, 0xFD, 0xF7, 0x7E, 0xFB, 0x59, 0x46, 0xFF, 0xF7, 0xCC, 0xFE, + 0x00, 0x2E, 0x08, 0xD1, 0x03, 0xAC, 0x2B, 0x46, 0x94, 0xE8, 0x07, 0x00, + 0x07, 0xB0, 0xBD, 0xE8, 0xF0, 0x4F, 0xFE, 0xF7, 0x9F, 0xBA, 0x07, 0xB0, + 0x55, 0xE4, 0x2D, 0xE9, 0xFF, 0x4F, 0x5F, 0x48, 0x87, 0xB0, 0xDF, 0xF8, + 0x74, 0x81, 0x00, 0x68, 0x90, 0xF8, 0x70, 0x01, 0x06, 0x90, 0x94, 0x78, + 0xD0, 0x78, 0x02, 0x90, 0x10, 0x78, 0x00, 0x90, 0x98, 0xF8, 0x00, 0x00, + 0x92, 0xF8, 0x01, 0xB0, 0x40, 0x1E, 0x84, 0x42, 0x01, 0xDA, 0x64, 0x1C, + 0xE4, 0xB2, 0x02, 0x98, 0x10, 0xB1, 0x40, 0x1E, 0xC0, 0xB2, 0x02, 0x90, + 0x5A, 0x49, 0x00, 0x98, 0x09, 0x78, 0x49, 0x1E, 0x88, 0x42, 0x02, 0xDA, + 0x40, 0x1C, 0xC0, 0xB2, 0x00, 0x90, 0xBB, 0xF1, 0x00, 0x0F, 0x03, 0xD0, + 0xAB, 0xF1, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x0B, 0x0A, 0x98, 0x21, 0x46, + 0x40, 0x69, 0x07, 0xF0, 0xE9, 0xFA, 0x05, 0x46, 0x84, 0xB1, 0x60, 0x1E, + 0xC1, 0xB2, 0x0A, 0x98, 0x40, 0x69, 0x07, 0xF0, 0xE1, 0xFA, 0x81, 0x46, + 0x01, 0x2C, 0x08, 0xD9, 0xA0, 0x1E, 0xC1, 0xB2, 0x0A, 0x98, 0x40, 0x69, + 0x07, 0xF0, 0xD8, 0xFA, 0x82, 0x46, 0x01, 0xE0, 0x81, 0x46, 0xCA, 0x46, + 0x98, 0xF8, 0x00, 0x00, 0x40, 0x1E, 0x84, 0x42, 0x07, 0xDA, 0x60, 0x1C, + 0xC1, 0xB2, 0x0A, 0x98, 0x40, 0x69, 0x07, 0xF0, 0xC9, 0xFA, 0x06, 0x46, + 0x00, 0xE0, 0x2E, 0x46, 0x00, 0x20, 0x03, 0x90, 0x89, 0xE0, 0xE1, 0xB2, + 0x07, 0x98, 0x07, 0xF0, 0xB5, 0xFA, 0x01, 0x90, 0xA0, 0x1C, 0xC1, 0xB2, + 0x0A, 0x98, 0x40, 0x69, 0x07, 0xF0, 0xB8, 0xFA, 0x86, 0x46, 0x00, 0x9A, + 0x74, 0xE0, 0x01, 0x98, 0x81, 0x5C, 0x08, 0x98, 0x81, 0x42, 0x6E, 0xD1, + 0x35, 0xF8, 0x12, 0x10, 0x06, 0x98, 0x00, 0x27, 0x08, 0x44, 0x39, 0xF9, + 0x12, 0x10, 0x00, 0xB2, 0x3B, 0x46, 0x05, 0x91, 0x81, 0x42, 0x04, 0xDD, + 0x36, 0xF9, 0x12, 0x10, 0x81, 0x42, 0x00, 0xDD, 0x01, 0x23, 0x05, 0xEB, + 0x42, 0x01, 0x31, 0xF9, 0x02, 0xCC, 0xCD, 0xF8, 0x10, 0xC0, 0x84, 0x45, + 0x04, 0xDD, 0xB1, 0xF9, 0x02, 0xC0, 0x84, 0x45, 0x00, 0xDD, 0x01, 0x27, + 0x01, 0x2C, 0x08, 0xDD, 0x3A, 0xF9, 0x12, 0xC0, 0x84, 0x45, 0x04, 0xDD, + 0x36, 0xF9, 0x12, 0xC0, 0x84, 0x45, 0x00, 0xDD, 0x01, 0x23, 0x98, 0xF8, + 0x00, 0xC0, 0xAC, 0xF1, 0x02, 0x0C, 0x64, 0x45, 0x08, 0xDA, 0xDD, 0xF8, + 0x14, 0xC0, 0x84, 0x45, 0x04, 0xDD, 0x3E, 0xF9, 0x12, 0xC0, 0x84, 0x45, + 0x00, 0xDD, 0x01, 0x23, 0x01, 0x2A, 0x08, 0xDD, 0x31, 0xF9, 0x04, 0xCC, + 0x84, 0x45, 0x04, 0xDD, 0xB1, 0xF9, 0x02, 0xC0, 0x84, 0x45, 0x00, 0xDD, + 0x01, 0x27, 0xDF, 0xF8, 0x48, 0xC0, 0x9C, 0xF8, 0x00, 0xC0, 0xAC, 0xF1, + 0x02, 0x0C, 0x62, 0x45, 0x07, 0xDA, 0xDD, 0xF8, 0x10, 0xC0, 0x84, 0x45, + 0x03, 0xDD, 0xB1, 0xF9, 0x04, 0x10, 0x81, 0x42, 0x18, 0xDC, 0x16, 0xE0, + 0x00, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x1C, 0x24, 0x10, 0x00, + 0xA4, 0x23, 0x10, 0x00, 0x54, 0x50, 0x10, 0x00, 0xAC, 0x23, 0x10, 0x00, + 0x9C, 0x23, 0x10, 0x00, 0x40, 0x50, 0x10, 0x00, 0x71, 0x23, 0x10, 0x00, + 0x6D, 0x23, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, 0x03, 0xE0, 0x17, 0xB1, + 0x0B, 0xB1, 0x01, 0x20, 0x03, 0x90, 0x52, 0x1C, 0x5A, 0x45, 0x88, 0xDD, + 0xCA, 0x46, 0xA9, 0x46, 0x35, 0x46, 0x76, 0x46, 0x64, 0x1C, 0x02, 0x98, + 0x84, 0x42, 0x7F, 0xF7, 0x72, 0xAF, 0x03, 0x98, 0x0B, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, 0xF7, 0x4F, 0x04, 0x46, 0x40, 0x78, 0x21, 0x78, + 0x86, 0x4F, 0x41, 0x1A, 0x49, 0x1C, 0x38, 0x68, 0x82, 0xB0, 0x91, 0x46, + 0x90, 0xF8, 0xEB, 0x00, 0x81, 0x42, 0x67, 0xDC, 0xE1, 0x78, 0xA2, 0x78, + 0x89, 0x1A, 0x49, 0x1C, 0x81, 0x42, 0x61, 0xDC, 0xDF, 0xF8, 0xFC, 0xA1, + 0x4F, 0xF4, 0x88, 0x61, 0xDA, 0xF8, 0x00, 0x00, 0x05, 0xF0, 0x8F, 0xFB, + 0x00, 0x26, 0xA5, 0x78, 0x4F, 0xF0, 0x01, 0x0B, 0x1F, 0xE0, 0xE9, 0xB2, + 0xDA, 0xF8, 0x00, 0x00, 0x07, 0xF0, 0x00, 0xFA, 0x80, 0x46, 0x03, 0x98, + 0xE9, 0xB2, 0x40, 0x69, 0x07, 0xF0, 0x04, 0xFA, 0x03, 0x46, 0x20, 0x78, + 0xDC, 0x46, 0x0C, 0xE0, 0x3A, 0x68, 0x33, 0xF9, 0x10, 0x10, 0xB2, 0xF8, + 0x4C, 0x21, 0x91, 0x42, 0x04, 0xDD, 0x49, 0x45, 0x02, 0xDA, 0x08, 0xF8, + 0x00, 0xC0, 0x01, 0x26, 0x40, 0x1C, 0x61, 0x78, 0x81, 0x42, 0xEF, 0xD2, + 0x6D, 0x1C, 0xE0, 0x78, 0xA8, 0x42, 0xDC, 0xD2, 0x01, 0x2E, 0x2F, 0xD1, + 0x67, 0x4E, 0x00, 0x20, 0x67, 0x49, 0x86, 0xF8, 0x0E, 0x01, 0x20, 0x68, + 0x30, 0x60, 0xCD, 0xE9, 0x00, 0xB1, 0x00, 0x23, 0x1A, 0x46, 0x31, 0x46, + 0xDA, 0xF8, 0x00, 0x00, 0xFF, 0xF7, 0x0C, 0xF8, 0x01, 0x25, 0x19, 0xE0, + 0x16, 0xF8, 0x25, 0x00, 0x21, 0x78, 0x88, 0x42, 0x13, 0xD9, 0x06, 0xEB, + 0x85, 0x00, 0x62, 0x78, 0x41, 0x78, 0x91, 0x42, 0x0D, 0xD2, 0x81, 0x78, + 0xA2, 0x78, 0x91, 0x42, 0x09, 0xD9, 0xC1, 0x78, 0xE2, 0x78, 0x91, 0x42, + 0x05, 0xD2, 0x4A, 0x46, 0x03, 0x99, 0x00, 0xF0, 0x51, 0xF8, 0x01, 0x28, + 0x05, 0xD0, 0x6D, 0x1C, 0x96, 0xF8, 0x0E, 0x01, 0xA8, 0x42, 0xE1, 0xD2, + 0x00, 0x20, 0x05, 0xB0, 0x83, 0xE7, 0x2D, 0xE9, 0xF0, 0x41, 0x1C, 0x46, + 0x15, 0x46, 0x0E, 0x46, 0x07, 0x46, 0x00, 0xF0, 0x1E, 0xF8, 0xA1, 0x7B, + 0x04, 0x29, 0x05, 0xD0, 0x46, 0x49, 0x09, 0x68, 0x91, 0xF8, 0x64, 0x11, + 0x89, 0x07, 0x07, 0xD5, 0x23, 0x46, 0x2A, 0x46, 0x31, 0x46, 0x38, 0x46, + 0xBD, 0xE8, 0xF0, 0x41, 0xFF, 0xF7, 0x52, 0xBA, 0x01, 0x28, 0x23, 0x46, + 0x2A, 0x46, 0x31, 0x46, 0x38, 0x46, 0x03, 0xD0, 0xBD, 0xE8, 0xF0, 0x41, + 0xFE, 0xF7, 0x16, 0xB9, 0xBD, 0xE8, 0xF0, 0x41, 0xD5, 0xE5, 0x39, 0x49, + 0x01, 0x20, 0x09, 0x68, 0x91, 0xF8, 0x64, 0x21, 0x53, 0x07, 0x0B, 0xD5, + 0x52, 0x06, 0x0A, 0xD5, 0x38, 0x4A, 0x12, 0x78, 0x8A, 0xB1, 0xB1, 0xF8, + 0x78, 0x21, 0x37, 0x4B, 0x52, 0x43, 0x1B, 0x68, 0x93, 0x42, 0x00, 0xDD, + 0x00, 0x20, 0x91, 0xF8, 0xB0, 0x10, 0x09, 0x06, 0x04, 0xD5, 0x33, 0x49, + 0x09, 0x78, 0x00, 0x29, 0x00, 0xD0, 0x00, 0x20, 0x70, 0x47, 0xB1, 0xF8, + 0x76, 0x21, 0xEC, 0xE7, 0x2D, 0xE9, 0xF0, 0x47, 0x05, 0x46, 0x90, 0xF8, + 0x02, 0x80, 0x0F, 0x46, 0x48, 0x69, 0x14, 0x46, 0x01, 0x26, 0x41, 0x46, + 0x07, 0xF0, 0x64, 0xF9, 0x81, 0x46, 0xA8, 0xF1, 0x01, 0x00, 0xC1, 0xB2, + 0x78, 0x69, 0x07, 0xF0, 0x5D, 0xF9, 0x29, 0x78, 0x09, 0xEB, 0x41, 0x02, + 0x32, 0xF9, 0x02, 0x2C, 0xA2, 0x42, 0x03, 0xDB, 0x30, 0xF9, 0x11, 0x10, + 0xA1, 0x42, 0x00, 0xDA, 0x00, 0x26, 0x69, 0x78, 0x09, 0xEB, 0x41, 0x02, + 0xB2, 0xF9, 0x02, 0x20, 0xA2, 0x42, 0x03, 0xDB, 0x30, 0xF9, 0x11, 0x00, + 0xA0, 0x42, 0x00, 0xDA, 0x00, 0x26, 0x95, 0xF8, 0x03, 0x80, 0x78, 0x69, + 0x41, 0x46, 0x07, 0xF0, 0x3F, 0xF9, 0x81, 0x46, 0x08, 0xF1, 0x01, 0x00, + 0xC1, 0xB2, 0x78, 0x69, 0x07, 0xF0, 0x38, 0xF9, 0x29, 0x78, 0x09, 0xEB, + 0x41, 0x02, 0x32, 0xF9, 0x02, 0x2C, 0xA2, 0x42, 0x03, 0xDB, 0x30, 0xF9, + 0x11, 0x10, 0xA1, 0x42, 0x00, 0xDA, 0x00, 0x26, 0x69, 0x78, 0x09, 0xEB, + 0x41, 0x02, 0xB2, 0xF9, 0x02, 0x20, 0xA2, 0x42, 0x03, 0xDB, 0x30, 0xF9, + 0x11, 0x00, 0xA0, 0x42, 0x00, 0xDA, 0x00, 0x26, 0x30, 0x46, 0xBF, 0xE4, + 0x50, 0x24, 0x10, 0x00, 0xA4, 0x23, 0x10, 0x00, 0x54, 0x50, 0x10, 0x00, + 0xAC, 0x23, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, 0xDC, 0x23, 0x10, 0x00, + 0x6E, 0x23, 0x10, 0x00, 0xEE, 0x49, 0xEF, 0x4A, 0x00, 0x20, 0x08, 0x70, + 0xEE, 0x4B, 0x10, 0x70, 0x01, 0x22, 0x1A, 0x70, 0x88, 0x70, 0x70, 0x47, + 0x2D, 0xE9, 0xF0, 0x5F, 0x81, 0x46, 0xEB, 0x48, 0xDF, 0xF8, 0xAC, 0xE3, + 0x4F, 0xF0, 0x00, 0x0C, 0x01, 0x78, 0xDE, 0xF8, 0x00, 0x00, 0x05, 0x29, + 0x11, 0xD0, 0xB0, 0xF9, 0x4A, 0x70, 0xB0, 0xF9, 0x4C, 0x60, 0x90, 0xF8, + 0x46, 0x00, 0xDF, 0xF8, 0x98, 0xB3, 0x00, 0xF0, 0x0F, 0x02, 0xE3, 0x48, + 0xC2, 0xF1, 0x10, 0x05, 0x00, 0x21, 0x00, 0x68, 0xBB, 0xF9, 0x00, 0xA0, + 0x15, 0xE0, 0x30, 0xF9, 0x4E, 0x7F, 0xB0, 0xF9, 0x02, 0x60, 0x10, 0xF8, + 0x07, 0x0C, 0xEC, 0xE7, 0x30, 0xF9, 0x11, 0x40, 0x39, 0xF9, 0x11, 0x30, + 0x04, 0xEB, 0x07, 0x08, 0x98, 0x45, 0x02, 0xDB, 0xA4, 0x1B, 0x9C, 0x42, + 0x01, 0xDD, 0x4F, 0xF0, 0x01, 0x0C, 0x49, 0x1C, 0x09, 0xB2, 0x5C, 0x46, + 0x51, 0x45, 0xED, 0xDB, 0xBC, 0xF1, 0x00, 0x0F, 0x20, 0xD1, 0x00, 0x21, + 0x0B, 0xE0, 0x30, 0xF9, 0x11, 0x60, 0x39, 0xF9, 0x11, 0x30, 0x56, 0x43, + 0x03, 0xFB, 0x05, 0x63, 0x1B, 0x11, 0x20, 0xF8, 0x11, 0x30, 0x49, 0x1C, + 0x09, 0xB2, 0xB4, 0xF9, 0x00, 0x30, 0x99, 0x42, 0xEF, 0xDB, 0xDE, 0xF8, + 0x00, 0x10, 0x91, 0xF8, 0x30, 0x11, 0x89, 0x07, 0x08, 0xD5, 0xC8, 0x49, + 0xC6, 0x4B, 0x0A, 0x78, 0xC7, 0x49, 0x09, 0x78, 0xBD, 0xE8, 0xF0, 0x5F, + 0x02, 0xF0, 0xE4, 0xB9, 0xBD, 0xE8, 0xF0, 0x9F, 0x2D, 0xE9, 0xF0, 0x0F, + 0x80, 0x46, 0xBD, 0x48, 0x8C, 0x46, 0x99, 0x46, 0x00, 0x68, 0x01, 0x21, + 0xDD, 0xF8, 0x20, 0xA0, 0x90, 0xF8, 0x48, 0x00, 0x00, 0x23, 0x00, 0xF0, + 0x0F, 0x06, 0xC6, 0xF1, 0x10, 0x00, 0xDF, 0xF8, 0xF0, 0xB2, 0x13, 0xE0, + 0x3C, 0xF9, 0x13, 0x40, 0x38, 0xF9, 0x13, 0x50, 0x04, 0xEB, 0x09, 0x07, + 0xAF, 0x42, 0x16, 0xDB, 0xA4, 0xEB, 0x0A, 0x07, 0xAF, 0x42, 0x12, 0xDC, + 0x74, 0x43, 0x05, 0xFB, 0x00, 0x44, 0x25, 0x11, 0x2B, 0xF8, 0x13, 0x50, + 0x5B, 0x1C, 0x1B, 0xB2, 0x93, 0x42, 0xE9, 0xDB, 0x00, 0x29, 0x06, 0xD0, + 0xBD, 0xE8, 0xF0, 0x0F, 0x52, 0x00, 0xAD, 0x49, 0x60, 0x46, 0x05, 0xF0, + 0x71, 0xB9, 0xBD, 0xE8, 0xF0, 0x0F, 0x70, 0x47, 0x2D, 0xE9, 0xFF, 0x4F, + 0xA2, 0x4D, 0x9F, 0xB0, 0xDF, 0xF8, 0x94, 0x92, 0x28, 0x68, 0x2C, 0x9C, + 0xA4, 0x4E, 0x90, 0xF8, 0x04, 0x01, 0x93, 0x46, 0x0F, 0x46, 0xC0, 0x07, + 0x45, 0xD0, 0xE0, 0x06, 0x43, 0xD5, 0x4F, 0xF0, 0x00, 0x08, 0x33, 0x78, + 0xA0, 0x4A, 0x10, 0xA8, 0x02, 0xF0, 0x2F, 0xF8, 0x99, 0xF8, 0x00, 0x30, + 0x9E, 0x4A, 0x59, 0x46, 0x68, 0x46, 0x02, 0xF0, 0x28, 0xF8, 0x28, 0x68, + 0x31, 0x78, 0x90, 0xF8, 0x0D, 0x31, 0xB0, 0xF9, 0x05, 0x21, 0x10, 0xA8, + 0xFA, 0xF7, 0x8B, 0xFC, 0x82, 0x46, 0x28, 0x68, 0x99, 0xF8, 0x00, 0x10, + 0x90, 0xF8, 0x0E, 0x31, 0xB0, 0xF9, 0x07, 0x21, 0x68, 0x46, 0xFA, 0xF7, + 0x80, 0xFC, 0xBA, 0xF1, 0x01, 0x0F, 0x17, 0xD0, 0x01, 0x28, 0x15, 0xD0, + 0x28, 0x68, 0x31, 0x78, 0xB0, 0xF9, 0x09, 0x21, 0x10, 0xA8, 0xFA, 0xF7, + 0x86, 0xFC, 0x82, 0x46, 0x28, 0x68, 0x99, 0xF8, 0x00, 0x10, 0xB0, 0xF9, + 0x0B, 0x21, 0x68, 0x46, 0xFA, 0xF7, 0x7D, 0xFC, 0xBA, 0xF1, 0x01, 0x0F, + 0x04, 0xD0, 0x01, 0x28, 0x02, 0xD0, 0x03, 0xE0, 0x01, 0x20, 0x00, 0xE0, + 0x02, 0x20, 0x80, 0x46, 0x40, 0x46, 0x00, 0xF0, 0xA7, 0xF8, 0x00, 0xF0, + 0x28, 0xFE, 0xE8, 0xB9, 0xE0, 0x07, 0x02, 0xD0, 0x1F, 0x98, 0xFF, 0xF7, + 0x0F, 0xFF, 0xE0, 0x06, 0x16, 0xD5, 0x28, 0x68, 0xB0, 0xF9, 0x54, 0x10, + 0x00, 0x91, 0xB0, 0xF9, 0x52, 0x30, 0x32, 0x78, 0x77, 0x49, 0x38, 0x46, + 0xFF, 0xF7, 0x60, 0xFF, 0x28, 0x68, 0xB0, 0xF9, 0x58, 0x10, 0x00, 0x91, + 0xB0, 0xF9, 0x56, 0x30, 0x99, 0xF8, 0x00, 0x20, 0x72, 0x49, 0x58, 0x46, + 0xFF, 0xF7, 0x54, 0xFF, 0x00, 0xF0, 0x1A, 0xFE, 0xA0, 0xBB, 0xE0, 0x06, + 0x5A, 0xD5, 0x28, 0x68, 0x4F, 0xF0, 0x00, 0x0A, 0x90, 0xF8, 0x04, 0x01, + 0x80, 0x07, 0x19, 0xD5, 0xDF, 0xF8, 0x7C, 0x81, 0x98, 0xF8, 0x00, 0x00, + 0x01, 0x28, 0x01, 0xD0, 0x02, 0x28, 0x11, 0xD1, 0x67, 0x49, 0x08, 0x68, + 0x40, 0xF0, 0x22, 0x00, 0x08, 0x60, 0x2B, 0x21, 0x01, 0xF0, 0xB4, 0xFE, + 0x4F, 0xF4, 0x00, 0x71, 0x00, 0x20, 0x03, 0xF0, 0xC7, 0xFE, 0x54, 0x48, + 0x88, 0xF8, 0x00, 0xA0, 0x80, 0xF8, 0x00, 0xA0, 0x28, 0x68, 0x90, 0xF8, + 0x82, 0x10, 0xC9, 0x06, 0x34, 0xD5, 0x51, 0x49, 0x09, 0x78, 0xE9, 0xB1, + 0xB0, 0xF9, 0xA2, 0xC0, 0xB0, 0xF9, 0x9E, 0x80, 0x30, 0x78, 0x01, 0x22, + 0xB9, 0x46, 0x13, 0x46, 0x90, 0xB1, 0x0B, 0xE0, 0x26, 0xE0, 0x39, 0xF9, + 0x10, 0x10, 0x61, 0x45, 0x00, 0xDD, 0x00, 0x23, 0x41, 0x45, 0x00, 0xDA, + 0x00, 0x22, 0x52, 0xEA, 0x03, 0x01, 0x05, 0xD0, 0x40, 0x1E, 0xF2, 0xD2, + 0x01, 0x20, 0xA0, 0xB1, 0x00, 0x20, 0x13, 0xE0, 0x50, 0x46, 0xFA, 0xE7, + 0xB0, 0xF9, 0xA4, 0x20, 0xB0, 0xF9, 0xA0, 0x30, 0x30, 0x78, 0x58, 0xB1, + 0x07, 0xE0, 0x37, 0xF9, 0x10, 0x10, 0x99, 0x42, 0x01, 0xDA, 0x91, 0x42, + 0x01, 0xDC, 0x50, 0x46, 0x02, 0xE0, 0x40, 0x1E, 0xF5, 0xD2, 0x01, 0x20, + 0x00, 0xF0, 0x4F, 0xF8, 0x28, 0x68, 0x90, 0xF8, 0xB0, 0x10, 0x09, 0x07, + 0x1D, 0xD5, 0x40, 0x4D, 0x2B, 0x68, 0x13, 0xF0, 0x30, 0x0F, 0x18, 0xD0, + 0xE1, 0x06, 0x16, 0xD5, 0xB0, 0xF9, 0xCD, 0x40, 0x00, 0x22, 0x36, 0x78, + 0x10, 0x46, 0x09, 0xE0, 0x37, 0xF9, 0x10, 0x10, 0xA1, 0x42, 0x01, 0xDA, + 0x00, 0x29, 0x01, 0xDC, 0x01, 0x22, 0x03, 0xE0, 0x40, 0x1C, 0x00, 0xB2, + 0xB0, 0x42, 0xF3, 0xDB, 0x00, 0x2A, 0x02, 0xD0, 0x23, 0xF0, 0x30, 0x00, + 0x28, 0x60, 0x23, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x30, 0xB4, 0x21, 0x49, + 0x24, 0x4A, 0x21, 0x4C, 0x0B, 0x78, 0x12, 0x68, 0x98, 0x42, 0x0C, 0xD1, + 0x20, 0x78, 0x98, 0x42, 0x48, 0x78, 0x14, 0xD0, 0x20, 0xB1, 0x40, 0x1E, + 0x10, 0xF0, 0xFF, 0x00, 0x48, 0x70, 0x10, 0xD1, 0x23, 0x70, 0x30, 0xBC, + 0x70, 0x47, 0x10, 0xB1, 0x92, 0xF8, 0x0F, 0x31, 0x00, 0xE0, 0x00, 0x23, + 0x4B, 0x70, 0x08, 0x70, 0x13, 0xF0, 0xFF, 0x0F, 0x03, 0xD1, 0x20, 0x70, + 0xF1, 0xE7, 0x00, 0x28, 0xEF, 0xD0, 0x92, 0xF8, 0xA8, 0x13, 0x30, 0xBC, + 0x07, 0x20, 0x00, 0xF0, 0x7C, 0xBD, 0x0F, 0x4B, 0x0C, 0x4A, 0x19, 0x78, + 0x81, 0x42, 0x07, 0xD0, 0x91, 0x78, 0x21, 0xB1, 0x49, 0x1E, 0x11, 0xF0, + 0xFF, 0x01, 0x91, 0x70, 0x00, 0xD1, 0x18, 0x70, 0x19, 0x78, 0x81, 0x42, + 0x08, 0xD1, 0x09, 0x48, 0x00, 0x68, 0x11, 0xB1, 0x90, 0xF8, 0xA6, 0x00, + 0x01, 0xE0, 0x90, 0xF8, 0xA7, 0x00, 0x90, 0x70, 0x70, 0x47, 0x00, 0x00, + 0xB4, 0x23, 0x10, 0x00, 0x6C, 0x23, 0x10, 0x00, 0x5A, 0x23, 0x10, 0x00, + 0x60, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x18, 0x24, 0x10, 0x00, + 0x0C, 0x24, 0x10, 0x00, 0x48, 0x53, 0x10, 0x00, 0x01, 0x24, 0x10, 0x00, + 0x00, 0x24, 0x10, 0x00, 0x5E, 0x48, 0x10, 0x00, 0xFC, 0x22, 0x01, 0x20, + 0x38, 0x23, 0x01, 0x20, 0x64, 0x24, 0x10, 0x00, 0x1C, 0x24, 0x10, 0x00, + 0xF0, 0xB5, 0xFE, 0x4B, 0x40, 0xF6, 0xFF, 0x74, 0x1D, 0x78, 0x00, 0x23, + 0x13, 0x80, 0xFC, 0x4B, 0x1B, 0x78, 0x04, 0x2B, 0x5B, 0xD1, 0xFB, 0x4E, + 0x00, 0x23, 0xB6, 0xF9, 0x00, 0x70, 0x06, 0xE0, 0x31, 0xF9, 0x13, 0x60, + 0xA6, 0x42, 0x00, 0xDA, 0x34, 0x46, 0x5B, 0x1C, 0x1B, 0xB2, 0xBB, 0x42, + 0xF6, 0xDB, 0xF5, 0x4B, 0x1E, 0x68, 0xB6, 0xF8, 0x58, 0x31, 0xA3, 0x42, + 0x47, 0xDC, 0x00, 0x23, 0x43, 0xE0, 0x31, 0xF8, 0x13, 0x40, 0x30, 0xF8, + 0x13, 0xC0, 0xA4, 0xEB, 0x0C, 0x04, 0x96, 0xF8, 0x4B, 0xC1, 0x24, 0xB2, + 0xBC, 0xF1, 0x64, 0x0F, 0x0A, 0xD9, 0x93, 0xFB, 0xF5, 0xFE, 0x05, 0xFB, + 0x1E, 0x3E, 0xCC, 0xF1, 0x64, 0x0C, 0x0E, 0xFB, 0x0C, 0xFE, 0x9E, 0xFB, + 0xF5, 0xFC, 0x0B, 0xE0, 0x93, 0xFB, 0xF5, 0xFE, 0x05, 0xFB, 0x1E, 0x3E, + 0xAC, 0xF1, 0x64, 0x0C, 0x0E, 0xFB, 0x0C, 0xFE, 0x9E, 0xFB, 0xF5, 0xFE, + 0xAC, 0xEB, 0x0E, 0x0C, 0x0C, 0xF1, 0x64, 0x0C, 0x00, 0x2C, 0x0B, 0xDD, + 0x04, 0xFB, 0x0C, 0xF4, 0x4F, 0xF0, 0x64, 0x0C, 0x94, 0xFB, 0xFC, 0xF4, + 0xB6, 0xF9, 0x5A, 0xC1, 0x24, 0xB2, 0xA4, 0x45, 0x0C, 0xDB, 0x0E, 0xE0, + 0x01, 0xDB, 0xA4, 0x46, 0x01, 0xE0, 0xC4, 0xF1, 0x00, 0x0C, 0xB6, 0xF8, + 0x5C, 0xE1, 0xF4, 0x45, 0x05, 0xDD, 0x00, 0x2C, 0x00, 0xDA, 0x64, 0x42, + 0x14, 0x80, 0x00, 0x20, 0xF0, 0xBD, 0x5B, 0x1C, 0x1B, 0xB2, 0xBB, 0x42, + 0xB9, 0xDB, 0x01, 0x20, 0xF0, 0xBD, 0x2D, 0xE9, 0xF7, 0x4F, 0xDF, 0xF8, + 0x30, 0xB3, 0x81, 0x46, 0x00, 0x26, 0xDB, 0xF8, 0x00, 0x00, 0xDF, 0xF8, + 0x20, 0xA3, 0xC9, 0x4F, 0x90, 0xF8, 0x31, 0x01, 0x82, 0xB0, 0x35, 0x46, + 0xB0, 0x46, 0x00, 0x07, 0x17, 0xD5, 0x00, 0x24, 0x11, 0xE0, 0xBA, 0xF9, + 0x00, 0x00, 0xDD, 0xE9, 0x03, 0x12, 0x60, 0x43, 0x09, 0xEB, 0x40, 0x00, + 0xFF, 0xF7, 0x7A, 0xFF, 0x01, 0x28, 0x04, 0xD1, 0xA0, 0x40, 0x30, 0x43, + 0x6D, 0x1C, 0xC6, 0xB2, 0xED, 0xB2, 0x64, 0x1C, 0x24, 0xB2, 0x38, 0x78, + 0x84, 0x42, 0xEA, 0xDB, 0x01, 0xE0, 0x3D, 0x78, 0xFF, 0x26, 0xB9, 0x4A, + 0x00, 0x23, 0x55, 0xB1, 0x53, 0x70, 0x00, 0x95, 0xBA, 0xF8, 0x00, 0x00, + 0x33, 0x46, 0x82, 0xB2, 0x39, 0x78, 0x48, 0x46, 0x01, 0xF0, 0xDB, 0xFF, + 0x30, 0xE0, 0xDB, 0xF8, 0x00, 0x00, 0x4F, 0xF0, 0x01, 0x08, 0x5C, 0x46, + 0x90, 0xF8, 0x31, 0x11, 0xC9, 0x06, 0x21, 0xD5, 0x51, 0x78, 0x49, 0x1C, + 0xC9, 0xB2, 0x51, 0x70, 0x90, 0xF8, 0x60, 0x51, 0x8D, 0x42, 0x19, 0xD2, + 0x53, 0x70, 0xAA, 0x4A, 0x11, 0x68, 0x41, 0xF0, 0x02, 0x01, 0x11, 0x60, + 0x90, 0xF8, 0xB0, 0x30, 0xDB, 0x07, 0x03, 0xD1, 0x90, 0xF8, 0xD4, 0x00, + 0xC0, 0x07, 0x02, 0xD0, 0x41, 0xF0, 0x20, 0x00, 0x10, 0x60, 0x10, 0x21, + 0x10, 0x68, 0x01, 0xF0, 0x1F, 0xFD, 0x4F, 0xF4, 0x00, 0x71, 0x00, 0x20, + 0x03, 0xF0, 0x32, 0xFD, 0x20, 0x68, 0x90, 0xF8, 0xA8, 0x13, 0x07, 0x20, + 0x00, 0xF0, 0x6F, 0xFC, 0x40, 0x46, 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x99, 0x48, 0x10, 0xB5, 0x01, 0x21, 0x01, 0x80, 0x41, 0x88, 0x95, 0x4A, + 0x49, 0x1C, 0x41, 0x80, 0x96, 0x49, 0x09, 0x78, 0x41, 0x71, 0x11, 0x89, + 0xC1, 0x80, 0x95, 0x49, 0x0B, 0x78, 0x01, 0x79, 0x63, 0xF3, 0x04, 0x11, + 0x93, 0x4B, 0x1B, 0x78, 0x63, 0xF3, 0xC7, 0x11, 0x92, 0x4B, 0x1B, 0x78, + 0x63, 0xF3, 0x86, 0x11, 0x91, 0x4B, 0x1B, 0x78, 0x63, 0xF3, 0x45, 0x11, + 0x01, 0x71, 0x11, 0x79, 0xC2, 0x7B, 0x61, 0xF3, 0x03, 0x02, 0x8E, 0x49, + 0xC2, 0x73, 0x0A, 0x78, 0x81, 0x7C, 0x62, 0xF3, 0x03, 0x01, 0x8C, 0x4A, + 0x12, 0x78, 0x62, 0xF3, 0x04, 0x11, 0x8B, 0x4A, 0x12, 0x78, 0x62, 0xF3, + 0x45, 0x11, 0x8A, 0x4A, 0x12, 0x78, 0x62, 0xF3, 0x86, 0x11, 0x81, 0x74, + 0xFA, 0xF7, 0x60, 0xFF, 0x87, 0x48, 0x04, 0x21, 0x78, 0x4C, 0x01, 0x80, + 0x41, 0x88, 0x86, 0x4A, 0x49, 0x1C, 0x41, 0x80, 0x20, 0x68, 0x51, 0x7E, + 0x90, 0xF8, 0x10, 0x31, 0x90, 0xF8, 0x25, 0x00, 0x63, 0xF3, 0x00, 0x01, + 0x80, 0x08, 0x60, 0xF3, 0x41, 0x01, 0x51, 0x76, 0xFD, 0xF7, 0xA1, 0xFF, + 0x20, 0x68, 0x90, 0xF8, 0xE8, 0x17, 0xC9, 0x07, 0x14, 0xD0, 0x90, 0xF8, + 0xE9, 0x07, 0x7B, 0x4B, 0x01, 0x28, 0x10, 0xD0, 0x02, 0x28, 0x11, 0xD0, + 0x03, 0x28, 0x12, 0xD0, 0x04, 0x28, 0x13, 0xD0, 0x05, 0x28, 0x14, 0xD0, + 0x07, 0x28, 0x05, 0xD1, 0x75, 0x49, 0x18, 0x68, 0x0A, 0x68, 0x02, 0x60, + 0x49, 0x68, 0x41, 0x60, 0x10, 0xBD, 0x14, 0x22, 0x64, 0x49, 0x0A, 0xE0, + 0x1E, 0x22, 0x6E, 0x49, 0x07, 0xE0, 0x1E, 0x22, 0x6F, 0x49, 0x04, 0xE0, + 0x14, 0x22, 0x6A, 0x49, 0x01, 0xE0, 0x6E, 0x49, 0x14, 0x22, 0x18, 0x68, + 0xBD, 0xE8, 0x10, 0x40, 0x04, 0xF0, 0xBC, 0xBE, 0x10, 0xB5, 0xFA, 0xF7, + 0xAB, 0xF8, 0xFA, 0xF7, 0xAC, 0xFD, 0xBD, 0xE8, 0x10, 0x40, 0xF9, 0xF7, + 0x5F, 0xBA, 0x10, 0xB5, 0xFB, 0xF7, 0x1F, 0xFD, 0xFD, 0xF7, 0xE4, 0xFF, + 0xFC, 0xF7, 0xAE, 0xFD, 0xFF, 0xF7, 0xEE, 0xFF, 0x14, 0x21, 0x52, 0x48, + 0x04, 0xF0, 0x25, 0xFF, 0x1E, 0x21, 0x5B, 0x48, 0x04, 0xF0, 0x21, 0xFF, + 0x1E, 0x21, 0x5C, 0x48, 0x04, 0xF0, 0x1D, 0xFF, 0x14, 0x21, 0x56, 0x48, + 0x04, 0xF0, 0x19, 0xFF, 0x14, 0x21, 0x59, 0x48, 0x04, 0xF0, 0x37, 0xFF, + 0x55, 0x49, 0x00, 0x20, 0x08, 0x60, 0x48, 0x60, 0x44, 0x49, 0x48, 0x70, + 0x88, 0x70, 0x10, 0xBD, 0x10, 0xB5, 0x80, 0xEA, 0x01, 0x04, 0xE0, 0x06, + 0x01, 0xD5, 0xFA, 0xF7, 0x7C, 0xFD, 0xA0, 0x06, 0x03, 0xD5, 0xBD, 0xE8, + 0x10, 0x40, 0xF9, 0xF7, 0x2D, 0xBA, 0x10, 0xBD, 0x2D, 0xE9, 0xFF, 0x4F, + 0x81, 0xB0, 0x00, 0x25, 0x9A, 0x46, 0x91, 0x46, 0x83, 0x46, 0x2C, 0x46, + 0xAD, 0xF8, 0x00, 0x50, 0x00, 0xF0, 0xC7, 0xF8, 0x33, 0x4F, 0xDF, 0xF8, + 0x1C, 0x81, 0x34, 0x4E, 0x38, 0x68, 0x90, 0xF8, 0x31, 0x01, 0x80, 0x06, + 0x1C, 0xD5, 0x02, 0x20, 0x03, 0xF0, 0x09, 0xFD, 0x42, 0x48, 0x6A, 0x46, + 0x01, 0x68, 0x58, 0x46, 0xFF, 0xF7, 0xBB, 0xFE, 0x05, 0x46, 0x02, 0x20, + 0x03, 0xF0, 0x1A, 0xFD, 0x75, 0xB1, 0xBD, 0xF8, 0x00, 0x00, 0x00, 0x21, + 0xC3, 0xB2, 0xC0, 0xF3, 0x07, 0x22, 0x09, 0x20, 0x01, 0xF0, 0x13, 0xFC, + 0x2F, 0x49, 0x30, 0x78, 0x09, 0x78, 0x08, 0x43, 0x7B, 0xD0, 0x9A, 0xE0, + 0xB4, 0x70, 0x38, 0x68, 0x90, 0xF8, 0x10, 0x01, 0xC0, 0x07, 0x09, 0xD0, + 0x33, 0x48, 0x34, 0x49, 0x00, 0x78, 0x09, 0x78, 0x08, 0x43, 0x40, 0x07, + 0x02, 0xD5, 0x02, 0x98, 0x00, 0xF0, 0x17, 0xFA, 0x30, 0x4C, 0x20, 0x78, + 0x10, 0xF0, 0x30, 0x0F, 0x11, 0xD0, 0x38, 0x68, 0x90, 0xF8, 0x78, 0x00, + 0xC0, 0x07, 0x0C, 0xD0, 0x2C, 0x48, 0x01, 0x78, 0x0E, 0x98, 0xFA, 0xF7, + 0x27, 0xF8, 0x98, 0xF8, 0x00, 0x00, 0x01, 0x28, 0x03, 0xD9, 0x20, 0x68, + 0x20, 0xF0, 0x30, 0x00, 0x20, 0x60, 0x38, 0x68, 0x90, 0xF8, 0x31, 0x01, + 0x40, 0x07, 0x05, 0xD5, 0x24, 0x48, 0x25, 0x49, 0x80, 0x78, 0x09, 0x78, + 0x88, 0x42, 0x69, 0xD1, 0x58, 0x46, 0x49, 0x46, 0x52, 0x46, 0xFB, 0xF7, + 0x97, 0xFC, 0x04, 0x46, 0x01, 0x28, 0x44, 0xD0, 0xF4, 0xB3, 0x3F, 0xE0, + 0x01, 0x24, 0x10, 0x00, 0x04, 0x24, 0x10, 0x00, 0x0C, 0x24, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x03, 0x24, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0x64, 0x24, 0x10, 0x00, 0x58, 0x25, 0x10, 0x00, 0x08, 0x24, 0x10, 0x00, + 0x29, 0x24, 0x10, 0x00, 0x06, 0x24, 0x10, 0x00, 0x05, 0x24, 0x10, 0x00, + 0x07, 0x24, 0x10, 0x00, 0x55, 0x23, 0x10, 0x00, 0x57, 0x23, 0x10, 0x00, + 0x6D, 0x23, 0x10, 0x00, 0x38, 0x23, 0x10, 0x00, 0xA8, 0x25, 0x10, 0x00, + 0x6C, 0x25, 0x10, 0x00, 0x74, 0x24, 0x10, 0x00, 0x00, 0x22, 0x10, 0x00, + 0x8A, 0x25, 0x10, 0x00, 0xBC, 0x25, 0x10, 0x00, 0x4C, 0x23, 0x10, 0x00, + 0x18, 0x24, 0x10, 0x00, 0x24, 0x24, 0x10, 0x00, 0x20, 0x24, 0x10, 0x00, + 0x1C, 0x24, 0x10, 0x00, 0x61, 0x24, 0x10, 0x00, 0x6C, 0x24, 0x10, 0x00, + 0x02, 0x24, 0x10, 0x00, 0x06, 0xE0, 0x14, 0xE0, 0x02, 0x2C, 0x08, 0xD0, + 0x0B, 0xE0, 0xFD, 0xF7, 0x25, 0xFF, 0x06, 0xE0, 0xFE, 0xF7, 0xAC, 0xF9, + 0xFC, 0xF7, 0xDF, 0xFC, 0x03, 0xE0, 0xF9, 0xF7, 0xBB, 0xFA, 0xFC, 0xF7, + 0xEA, 0xFC, 0x20, 0x48, 0x01, 0x79, 0x64, 0xF3, 0x03, 0x01, 0x01, 0x71, + 0x0A, 0xE0, 0xB0, 0x78, 0x40, 0x1C, 0xC0, 0xB2, 0xB0, 0x70, 0x02, 0x28, + 0x04, 0xD9, 0x01, 0x20, 0x30, 0x70, 0x88, 0xF8, 0x00, 0x00, 0xB4, 0x70, + 0xFF, 0xF7, 0x78, 0xFE, 0x28, 0x46, 0x72, 0xE6, 0x70, 0x47, 0x70, 0xB5, + 0x14, 0x4D, 0x14, 0x21, 0x28, 0x46, 0x6C, 0x88, 0x04, 0xF0, 0x25, 0xFE, + 0x6C, 0x80, 0x12, 0x4D, 0x1E, 0x21, 0x28, 0x46, 0x6C, 0x88, 0x04, 0xF0, + 0x1E, 0xFE, 0x6C, 0x80, 0x0F, 0x4D, 0x1E, 0x21, 0x28, 0x46, 0x6C, 0x88, + 0x04, 0xF0, 0x17, 0xFE, 0x6C, 0x80, 0x0D, 0x4D, 0x14, 0x21, 0x28, 0x46, + 0x6C, 0x88, 0x04, 0xF0, 0x10, 0xFE, 0x6C, 0x80, 0x0A, 0x4D, 0x14, 0x21, + 0x28, 0x46, 0x6C, 0x88, 0x04, 0xF0, 0x2B, 0xFE, 0x08, 0x48, 0x6C, 0x80, + 0x00, 0x21, 0x42, 0x88, 0x01, 0x60, 0x41, 0x60, 0x42, 0x80, 0x70, 0xBD, + 0x58, 0x25, 0x10, 0x00, 0x6C, 0x25, 0x10, 0x00, 0x8A, 0x25, 0x10, 0x00, + 0xA8, 0x25, 0x10, 0x00, 0xBC, 0x25, 0x10, 0x00, 0x00, 0x22, 0x10, 0x00, + 0xF4, 0x48, 0x00, 0x21, 0xF4, 0x4A, 0x01, 0x70, 0xF4, 0x48, 0x51, 0x60, + 0x91, 0x60, 0x11, 0x70, 0x00, 0x68, 0x90, 0xF8, 0x11, 0x31, 0x03, 0xF0, + 0x03, 0x03, 0x53, 0x70, 0x91, 0x70, 0xD1, 0x60, 0x11, 0x61, 0x51, 0x61, + 0x91, 0x61, 0x90, 0xF8, 0x3B, 0x11, 0xED, 0x4A, 0x09, 0x09, 0x11, 0x60, + 0x90, 0xF8, 0x10, 0x11, 0xCA, 0x07, 0xEB, 0x49, 0x02, 0xD0, 0x90, 0xF8, + 0x12, 0x01, 0x01, 0xE0, 0x90, 0xF8, 0x81, 0x07, 0x88, 0x70, 0x70, 0x47, + 0xF0, 0xB5, 0xE4, 0x49, 0xE4, 0x4E, 0x09, 0x68, 0x91, 0xF8, 0x30, 0x21, + 0xD2, 0x07, 0x4F, 0xD0, 0xDE, 0x4A, 0x17, 0x78, 0xE2, 0x4A, 0x12, 0x78, + 0xAF, 0xB1, 0x01, 0x2A, 0x09, 0xD0, 0x91, 0xF8, 0x3B, 0x31, 0xB1, 0xF8, + 0x38, 0x41, 0x03, 0xF0, 0x0F, 0x02, 0x1D, 0x09, 0xB1, 0xF8, 0x36, 0x31, + 0x14, 0xE0, 0x91, 0xF8, 0xF5, 0x31, 0xB1, 0xF8, 0xF2, 0x41, 0x03, 0xF0, + 0x0F, 0x02, 0x1D, 0x09, 0xB1, 0xF8, 0xF0, 0x31, 0x0A, 0xE0, 0x01, 0x2A, + 0x10, 0xD0, 0x91, 0xF8, 0x3A, 0x31, 0xB1, 0xF8, 0x34, 0x41, 0x03, 0xF0, + 0x0F, 0x02, 0x1D, 0x09, 0xB1, 0xF8, 0x32, 0x31, 0xDF, 0xF8, 0x44, 0xC3, + 0x9C, 0xF8, 0x00, 0xC0, 0xBC, 0xF1, 0x03, 0x0F, 0x0A, 0xD0, 0x10, 0xE0, + 0x91, 0xF8, 0xF4, 0x31, 0xB1, 0xF8, 0xEE, 0x41, 0x03, 0xF0, 0x0F, 0x02, + 0x1D, 0x09, 0xB1, 0xF8, 0xEC, 0x31, 0xED, 0xE7, 0x91, 0xF8, 0x3C, 0x11, + 0x0F, 0xB1, 0x0A, 0x09, 0x01, 0xE0, 0x01, 0xF0, 0x0F, 0x02, 0x98, 0x42, + 0x01, 0xDC, 0x32, 0x60, 0xF0, 0xBD, 0xA0, 0x42, 0x01, 0xDB, 0x35, 0x60, + 0xF0, 0xBD, 0xC0, 0x1A, 0xA9, 0x1A, 0x48, 0x43, 0xE1, 0x1A, 0x90, 0xFB, + 0xF1, 0xF0, 0x10, 0x44, 0x30, 0x60, 0xF0, 0xBD, 0x00, 0x20, 0xFB, 0xE7, + 0x70, 0xB5, 0xBC, 0x49, 0x00, 0x22, 0x0B, 0x78, 0xB5, 0x49, 0x01, 0x2B, + 0x09, 0x68, 0x02, 0xD1, 0xB9, 0x4B, 0x1B, 0x78, 0x4B, 0xB1, 0xB1, 0xF8, + 0x20, 0x31, 0x83, 0x42, 0x18, 0xD2, 0x91, 0xF8, 0x10, 0x31, 0x1B, 0x07, + 0x14, 0xD5, 0x03, 0x22, 0x25, 0xE0, 0xB1, 0xF8, 0x2D, 0x31, 0x83, 0x42, + 0x03, 0xD2, 0x91, 0xF8, 0x10, 0x31, 0x1B, 0x07, 0xF5, 0xD4, 0xB1, 0xF8, + 0x2B, 0x31, 0x83, 0x42, 0x03, 0xD2, 0x91, 0xF8, 0x10, 0x31, 0x5B, 0x07, + 0x0A, 0xD4, 0xB1, 0xF8, 0x29, 0x31, 0x0B, 0xE0, 0xB1, 0xF8, 0x1E, 0x31, + 0x83, 0x42, 0x05, 0xD2, 0x91, 0xF8, 0x10, 0x31, 0x5B, 0x07, 0x01, 0xD5, + 0x02, 0x22, 0x08, 0xE0, 0xB1, 0xF8, 0x1C, 0x31, 0x83, 0x42, 0x04, 0xD2, + 0x91, 0xF8, 0x10, 0x01, 0x80, 0x07, 0x00, 0xD5, 0x01, 0x22, 0x99, 0x4B, + 0x97, 0x4C, 0x18, 0x78, 0x82, 0x42, 0x11, 0xD1, 0x25, 0x78, 0x85, 0x42, + 0x1A, 0xD0, 0x9D, 0x4E, 0x36, 0x78, 0x30, 0x43, 0x07, 0xD0, 0x98, 0x68, + 0x40, 0x1E, 0x98, 0x60, 0x00, 0x28, 0x11, 0xDC, 0x18, 0x78, 0x20, 0x70, + 0x0E, 0xE0, 0x1D, 0x70, 0x98, 0x68, 0xF7, 0xE7, 0x0A, 0xB3, 0x02, 0x2A, + 0x01, 0xD1, 0x01, 0x28, 0x1D, 0xD0, 0x91, 0xF8, 0x1A, 0x01, 0x98, 0x60, + 0x1A, 0x70, 0x00, 0x28, 0x00, 0xDC, 0x22, 0x70, 0x20, 0x78, 0x02, 0x43, + 0x04, 0xD0, 0x91, 0xF8, 0xA7, 0x13, 0x06, 0x20, 0x00, 0xF0, 0xC5, 0xF9, + 0x8C, 0x49, 0x20, 0x78, 0x0A, 0x7C, 0x60, 0xF3, 0x03, 0x02, 0x0A, 0x74, + 0x8A, 0x4A, 0xD3, 0x7C, 0x60, 0xF3, 0x03, 0x03, 0x89, 0x48, 0xD3, 0x74, + 0x00, 0x78, 0x08, 0x76, 0x70, 0xBD, 0x91, 0xF8, 0x1B, 0x01, 0xE0, 0xE7, + 0xF0, 0xB5, 0xDF, 0xF8, 0xE8, 0xE1, 0x00, 0x24, 0x0E, 0xF1, 0x14, 0x0C, + 0x9E, 0xF8, 0x02, 0x00, 0x40, 0xF6, 0xFF, 0x71, 0x3C, 0xF8, 0x10, 0x30, + 0x75, 0x48, 0x06, 0x68, 0x96, 0xF8, 0x27, 0x21, 0x96, 0xF8, 0x28, 0x01, + 0x17, 0x09, 0x15, 0x07, 0x11, 0xD0, 0x02, 0xF0, 0x0F, 0x02, 0x70, 0x4D, + 0x83, 0x42, 0x2D, 0x68, 0x95, 0xF8, 0x26, 0x51, 0x04, 0xDB, 0x18, 0x1A, + 0x78, 0x43, 0x90, 0xFB, 0xF2, 0xF0, 0x05, 0x44, 0x9E, 0xF8, 0x01, 0x70, + 0x00, 0x20, 0x7F, 0x1C, 0x08, 0xE0, 0x01, 0x22, 0xED, 0xE7, 0x3C, 0xF8, + 0x10, 0x20, 0x91, 0x42, 0x01, 0xDD, 0x11, 0x46, 0x04, 0x46, 0x40, 0x1C, + 0xB8, 0x42, 0xF6, 0xDB, 0x5A, 0x1A, 0x64, 0x48, 0xAA, 0x42, 0x06, 0xDD, + 0x06, 0xF5, 0x89, 0x76, 0x32, 0x5D, 0x82, 0x70, 0x01, 0x80, 0x8E, 0xF8, + 0x02, 0x40, 0x81, 0x78, 0x65, 0x48, 0x02, 0x22, 0x01, 0x73, 0x43, 0x73, + 0x9E, 0xF8, 0x02, 0x10, 0x81, 0x73, 0x61, 0x48, 0x02, 0x80, 0x42, 0x88, + 0x52, 0x1C, 0x42, 0x80, 0xBC, 0xF8, 0x00, 0x20, 0x82, 0x80, 0xBC, 0xF8, + 0x02, 0x20, 0xC2, 0x80, 0xBC, 0xF8, 0x04, 0x20, 0x02, 0x81, 0xBC, 0xF8, + 0x06, 0x20, 0x42, 0x81, 0x85, 0x81, 0xC1, 0x81, 0xF0, 0xBD, 0x2D, 0xE9, + 0xFC, 0x5F, 0x83, 0x46, 0x58, 0x48, 0x4C, 0x49, 0x10, 0x26, 0x05, 0x78, + 0x48, 0x68, 0x00, 0x24, 0x90, 0xFB, 0xF6, 0xF2, 0x06, 0xFB, 0x12, 0x07, + 0x88, 0x46, 0x28, 0xE0, 0x04, 0xFB, 0x05, 0xF0, 0x0B, 0xEB, 0x40, 0x09, + 0x04, 0xFB, 0x06, 0x70, 0x50, 0x4A, 0x68, 0x43, 0x02, 0xEB, 0x40, 0x0A, + 0x42, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x10, 0x31, 0x9B, 0x06, 0x12, 0xD5, + 0xD8, 0xF8, 0x04, 0x10, 0x00, 0x29, 0x0E, 0xDD, 0x04, 0xFB, 0x06, 0xF1, + 0x69, 0x43, 0x02, 0xEB, 0x41, 0x02, 0xB0, 0xF9, 0x24, 0x11, 0xCD, 0xE9, + 0x00, 0x15, 0xB0, 0xF9, 0x22, 0x31, 0x49, 0x46, 0x08, 0x46, 0x01, 0xF0, + 0x8B, 0xFC, 0x6A, 0x00, 0x49, 0x46, 0x50, 0x46, 0x04, 0xF0, 0xF0, 0xFB, + 0x64, 0x1C, 0x98, 0xF8, 0x01, 0x00, 0x41, 0x46, 0x40, 0x1C, 0x84, 0x42, + 0xD0, 0xDB, 0x48, 0x68, 0x4F, 0xF0, 0xFF, 0x32, 0x40, 0x1C, 0x02, 0xEB, + 0x46, 0x02, 0x48, 0x60, 0x82, 0x42, 0x00, 0xDA, 0x4E, 0x60, 0xD8, 0xF8, + 0x04, 0x00, 0xB0, 0x42, 0x04, 0xDB, 0xBD, 0xE8, 0xFC, 0x5F, 0x34, 0x48, + 0x00, 0xF0, 0x02, 0xB8, 0xBD, 0xE8, 0xFC, 0x9F, 0x2D, 0xE9, 0xF0, 0x5F, + 0xDF, 0xF8, 0xBC, 0xA0, 0x00, 0x25, 0xDF, 0xF8, 0x88, 0x80, 0x83, 0x46, + 0x9A, 0xF8, 0x00, 0x70, 0x10, 0x26, 0x2C, 0x46, 0x08, 0xF1, 0x14, 0x08, + 0xDF, 0xF8, 0x74, 0x90, 0x23, 0xE0, 0x04, 0xFB, 0x06, 0xF0, 0x78, 0x43, + 0x0B, 0xEB, 0x40, 0x00, 0x9A, 0xF8, 0x00, 0x20, 0x31, 0x46, 0x01, 0xF0, + 0x09, 0xFD, 0x17, 0x49, 0x17, 0x4B, 0x0C, 0x31, 0x21, 0xF8, 0x14, 0x00, + 0x1B, 0x68, 0x38, 0xF8, 0x14, 0x10, 0x93, 0xF8, 0x10, 0x31, 0xDB, 0x06, + 0x07, 0xD5, 0x43, 0x1A, 0x00, 0x2B, 0x04, 0xDC, 0xC1, 0xEB, 0xC1, 0x11, + 0x08, 0x44, 0xC0, 0xF3, 0xCF, 0x10, 0x28, 0xF8, 0x14, 0x00, 0xA8, 0x42, + 0x00, 0xD9, 0x05, 0x46, 0x64, 0x1C, 0x99, 0xF8, 0x01, 0x00, 0x40, 0x1C, + 0x84, 0x42, 0xD6, 0xDB, 0xFF, 0xF7, 0x18, 0xFF, 0x99, 0xF8, 0x02, 0x10, + 0x38, 0xF8, 0x11, 0x40, 0x20, 0x46, 0xFF, 0xF7, 0x99, 0xFE, 0x20, 0x46, + 0xBD, 0xE8, 0xF0, 0x5F, 0x3A, 0xE6, 0x00, 0x00, 0xB8, 0x23, 0x10, 0x00, + 0xE4, 0x23, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0xBC, 0x23, 0x10, 0x00, + 0x6C, 0x24, 0x10, 0x00, 0x2A, 0x24, 0x10, 0x00, 0x04, 0x24, 0x10, 0x00, + 0x05, 0x24, 0x10, 0x00, 0x06, 0x24, 0x10, 0x00, 0x29, 0x24, 0x10, 0x00, + 0x6C, 0x25, 0x10, 0x00, 0x58, 0x25, 0x10, 0x00, 0x88, 0x23, 0x10, 0x00, + 0x01, 0x24, 0x10, 0x00, 0x7C, 0x0B, 0x01, 0x20, 0x70, 0xB5, 0xA8, 0x4E, + 0xA8, 0x4D, 0x74, 0x6A, 0x28, 0x6A, 0x44, 0x40, 0xE0, 0x07, 0x01, 0xD0, + 0xFF, 0xF7, 0xB1, 0xFC, 0xE0, 0x06, 0x01, 0xD5, 0xFF, 0xF7, 0xA4, 0xFC, + 0xE9, 0x69, 0xF0, 0x69, 0xFF, 0xF7, 0xCE, 0xFC, 0xE8, 0x69, 0xF0, 0x61, + 0x28, 0x6A, 0x70, 0x62, 0x68, 0x6A, 0x30, 0x62, 0xBD, 0xE8, 0x70, 0x40, + 0x00, 0xF0, 0xDA, 0xB8, 0xF8, 0xB5, 0x00, 0x24, 0x03, 0x20, 0x03, 0xF0, + 0xEE, 0xF9, 0xFF, 0xF7, 0xDD, 0xFF, 0x97, 0x4E, 0x97, 0x4D, 0x96, 0xF8, + 0x24, 0x00, 0x96, 0xF8, 0x20, 0x10, 0x08, 0x43, 0xC0, 0x06, 0x0C, 0xD5, + 0x30, 0x78, 0xA9, 0x68, 0x42, 0x00, 0x93, 0x48, 0x04, 0xF0, 0x3C, 0xFB, + 0x70, 0x78, 0xE9, 0x68, 0x42, 0x00, 0x90, 0x48, 0x3C, 0x30, 0x04, 0xF0, + 0x35, 0xFB, 0x96, 0xF8, 0x20, 0x10, 0x70, 0x6A, 0x01, 0x43, 0xC9, 0x07, + 0x0A, 0xD0, 0x28, 0x69, 0x00, 0x90, 0x8A, 0x4B, 0xD5, 0xE9, 0x00, 0x01, + 0x3C, 0x33, 0x88, 0x4A, 0xFF, 0xF7, 0xA6, 0xFC, 0x04, 0x46, 0x1D, 0xE0, + 0xC0, 0x06, 0x1B, 0xD5, 0x85, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x11, 0x02, + 0x40, 0x06, 0x15, 0xD5, 0x83, 0x48, 0x81, 0x49, 0x02, 0x78, 0x3C, 0x31, + 0x7F, 0x48, 0xF9, 0xF7, 0x79, 0xFE, 0x01, 0x28, 0x0C, 0xD1, 0x80, 0x49, + 0x08, 0x68, 0x40, 0xF0, 0x22, 0x00, 0x08, 0x60, 0x23, 0x21, 0x01, 0xF0, + 0xE3, 0xF8, 0x4F, 0xF4, 0x00, 0x71, 0x00, 0x20, 0x03, 0xF0, 0xF6, 0xF8, + 0x30, 0x6A, 0x78, 0xB1, 0x74, 0xB9, 0x02, 0x20, 0x03, 0xF0, 0xA1, 0xF9, + 0x30, 0x6A, 0x00, 0x90, 0x72, 0x4A, 0x72, 0x49, 0x3C, 0x32, 0x6B, 0x69, + 0x28, 0x68, 0xFF, 0xF7, 0x89, 0xF9, 0x02, 0x20, 0x03, 0xF0, 0xB0, 0xF9, + 0xBD, 0xE8, 0xF8, 0x40, 0x03, 0x20, 0x03, 0xF0, 0xAB, 0xB9, 0x69, 0x48, + 0x41, 0x79, 0x01, 0x29, 0x0C, 0xD0, 0x81, 0x79, 0x01, 0x29, 0x09, 0xD0, + 0xC1, 0x79, 0x01, 0x29, 0x06, 0xD0, 0x6A, 0x49, 0x09, 0x78, 0x01, 0x29, + 0x02, 0xD0, 0x00, 0x7A, 0x00, 0x28, 0x00, 0xD0, 0x01, 0x20, 0x70, 0x47, + 0x5F, 0x48, 0x41, 0x79, 0x01, 0x29, 0x08, 0xD0, 0x81, 0x79, 0x01, 0x29, + 0x05, 0xD0, 0xC1, 0x79, 0x01, 0x29, 0x02, 0xD0, 0x00, 0x7A, 0x00, 0x28, + 0x00, 0xD0, 0x01, 0x20, 0x70, 0x47, 0x58, 0x4A, 0x93, 0x7A, 0x99, 0x42, + 0x01, 0xD9, 0x91, 0x72, 0xD0, 0x72, 0x70, 0x47, 0x54, 0x48, 0x08, 0xB5, + 0x00, 0x21, 0xC1, 0x61, 0x41, 0x62, 0x01, 0x62, 0x00, 0xF0, 0x20, 0xF8, + 0x6B, 0x46, 0x00, 0x22, 0x07, 0x21, 0x02, 0x20, 0x03, 0xF0, 0xF8, 0xF8, + 0x00, 0x98, 0xC1, 0x07, 0x06, 0xD0, 0x01, 0x21, 0x02, 0x20, 0x03, 0xF0, + 0xD0, 0xF8, 0xFF, 0xF7, 0x5D, 0xFF, 0xEF, 0xE7, 0x81, 0x07, 0x06, 0xD5, + 0x02, 0x21, 0x08, 0x46, 0x03, 0xF0, 0xC7, 0xF8, 0x00, 0xF0, 0x55, 0xF8, + 0xE6, 0xE7, 0x40, 0x07, 0xE4, 0xD5, 0x04, 0x21, 0x02, 0x20, 0x03, 0xF0, + 0xBE, 0xF8, 0xDF, 0xE7, 0x43, 0x48, 0x10, 0xB5, 0x01, 0x68, 0x3F, 0x48, + 0x4A, 0x7D, 0x02, 0x70, 0x09, 0x7D, 0x41, 0x70, 0x4A, 0x43, 0x82, 0x81, + 0x42, 0x4A, 0x92, 0x78, 0x82, 0x70, 0x01, 0x22, 0xC2, 0x70, 0x02, 0x71, + 0x00, 0x22, 0xC2, 0x81, 0x42, 0x72, 0x3F, 0x48, 0x02, 0x70, 0xC1, 0xF1, + 0x20, 0x00, 0x3E, 0x49, 0x01, 0xEB, 0x40, 0x00, 0x3D, 0x49, 0x08, 0x60, + 0x3D, 0x48, 0x02, 0x70, 0xFF, 0xF7, 0xCB, 0xFB, 0xFF, 0xF7, 0x06, 0xFD, + 0xBD, 0xE8, 0x10, 0x40, 0xFF, 0xF7, 0x6C, 0xB8, 0x2E, 0x49, 0x2D, 0x48, + 0x91, 0xF8, 0x2F, 0x20, 0x02, 0x70, 0x91, 0xF8, 0x30, 0x30, 0x43, 0x70, + 0x5A, 0x43, 0x82, 0x81, 0x91, 0xF8, 0x33, 0x20, 0x82, 0x70, 0x11, 0xF8, + 0x2C, 0x2F, 0x02, 0x71, 0x8A, 0x78, 0xC2, 0x70, 0x49, 0x78, 0x49, 0xB1, + 0x26, 0x49, 0x09, 0x68, 0x91, 0xF8, 0x80, 0x21, 0x12, 0x07, 0x03, 0xD5, + 0x2B, 0x4A, 0x91, 0xF8, 0xAC, 0x11, 0x11, 0x70, 0x27, 0x49, 0x40, 0x31, + 0x01, 0x61, 0xA1, 0xF5, 0xF8, 0x61, 0x41, 0x61, 0x27, 0x49, 0x81, 0x61, + 0x70, 0x47, 0x38, 0xB5, 0x03, 0x20, 0x03, 0xF0, 0xEE, 0xF8, 0xFF, 0xF7, + 0xDD, 0xFE, 0x17, 0x4C, 0x17, 0x4D, 0x94, 0xF8, 0x24, 0x00, 0x94, 0xF8, + 0x20, 0x10, 0x08, 0x43, 0xC0, 0x06, 0x0C, 0xD5, 0x20, 0x78, 0xA9, 0x68, + 0x42, 0x00, 0x13, 0x48, 0x04, 0xF0, 0x3C, 0xFA, 0x60, 0x78, 0xE9, 0x68, + 0x42, 0x00, 0x10, 0x48, 0x3C, 0x30, 0x04, 0xF0, 0x35, 0xFA, 0x94, 0xF8, + 0x20, 0x00, 0x61, 0x6A, 0x08, 0x43, 0xC0, 0x07, 0x02, 0xD0, 0x28, 0x68, + 0xFF, 0xF7, 0x7E, 0xFC, 0x20, 0x6A, 0x38, 0xB1, 0x00, 0x90, 0x08, 0x4A, + 0x07, 0x49, 0x3C, 0x32, 0x6B, 0x69, 0x28, 0x68, 0xFF, 0xF7, 0xB4, 0xF8, + 0xBD, 0xE8, 0x38, 0x40, 0x03, 0x20, 0x03, 0xF0, 0xD9, 0xB8, 0x00, 0x00, + 0x00, 0x24, 0x10, 0x00, 0xE4, 0x52, 0x10, 0x00, 0xFC, 0x93, 0x01, 0x20, + 0x50, 0x24, 0x10, 0x00, 0x56, 0x23, 0x10, 0x00, 0x64, 0x24, 0x10, 0x00, + 0x57, 0x23, 0x10, 0x00, 0x6C, 0x24, 0x10, 0x00, 0xC5, 0x23, 0x10, 0x00, + 0xF8, 0x9B, 0x01, 0x20, 0x74, 0x24, 0x10, 0x00, 0x88, 0x23, 0x10, 0x00, + 0x7C, 0x1B, 0x01, 0x20, 0x0E, 0x4C, 0x08, 0xB5, 0x00, 0x25, 0x25, 0x60, + 0x20, 0x68, 0x40, 0x1C, 0x20, 0x60, 0x05, 0xF0, 0xF9, 0xFE, 0x01, 0xF0, + 0xB8, 0xF8, 0x05, 0xF0, 0x43, 0xFA, 0x10, 0xB9, 0x06, 0xF0, 0x1C, 0xFB, + 0x20, 0xB1, 0x05, 0xF0, 0xAF, 0xFE, 0xF4, 0xF7, 0xF7, 0xFE, 0xED, 0xE7, + 0x00, 0x95, 0x00, 0xBF, 0x00, 0x98, 0x40, 0x1C, 0x00, 0x90, 0x14, 0x28, + 0xF9, 0xDB, 0xF4, 0xE7, 0x2C, 0x24, 0x10, 0x00, 0x85, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x85, 0x49, 0x08, 0x60, 0x01, 0xF0, 0xF3, 0xBC, 0xD0, 0x21, + 0x4D, 0xF6, 0xC0, 0x62, 0xBA, 0x20, 0x00, 0xF0, 0x70, 0xBC, 0x70, 0xB5, + 0x4F, 0xF4, 0x00, 0x63, 0x98, 0x42, 0x02, 0xD2, 0x7E, 0x4C, 0x24, 0x68, + 0x07, 0xE0, 0xA0, 0xF5, 0x80, 0x50, 0x80, 0xB2, 0x4F, 0xF4, 0x80, 0x53, + 0x98, 0x42, 0x0C, 0xD2, 0x7A, 0x4C, 0x85, 0x18, 0x9D, 0x42, 0x00, 0xDD, + 0x1A, 0x1A, 0x00, 0x23, 0x03, 0xE0, 0xCD, 0x5C, 0x1E, 0x18, 0x5B, 0x1C, + 0xA5, 0x55, 0x93, 0x42, 0xF9, 0xDB, 0x70, 0xBD, 0xB0, 0xF5, 0x80, 0x5F, + 0x04, 0xD2, 0x0A, 0x46, 0x01, 0x46, 0x70, 0x48, 0x00, 0x68, 0x0E, 0xE0, + 0xB0, 0xF5, 0xFE, 0x5F, 0x05, 0xD2, 0x0A, 0x46, 0x01, 0x46, 0x6D, 0x48, + 0x40, 0x30, 0x00, 0xF0, 0xA0, 0xBC, 0xA0, 0xF5, 0xFE, 0x50, 0x80, 0xB2, + 0x0A, 0x46, 0x01, 0x46, 0x68, 0x48, 0x00, 0xF0, 0x6D, 0xBC, 0x01, 0xF0, + 0x9E, 0xBD, 0x10, 0xB5, 0x66, 0x4C, 0x4F, 0xF4, 0x00, 0x21, 0x20, 0x60, + 0x00, 0x20, 0x00, 0xF0, 0xB2, 0xF8, 0x00, 0x20, 0x20, 0x60, 0x10, 0xBD, + 0x10, 0xB5, 0x4F, 0xF4, 0x00, 0x61, 0x00, 0x20, 0x00, 0xF0, 0xA9, 0xF8, + 0x00, 0x23, 0xBD, 0xE8, 0x10, 0x40, 0x1A, 0x46, 0x19, 0x46, 0x03, 0x20, + 0x00, 0xF0, 0x43, 0xBF, 0x10, 0xB5, 0x4F, 0xF4, 0x80, 0x51, 0x00, 0x20, + 0x00, 0xF0, 0x9B, 0xF8, 0x00, 0x23, 0xBD, 0xE8, 0x10, 0x40, 0x1A, 0x46, + 0x19, 0x46, 0x04, 0x20, 0x00, 0xF0, 0x35, 0xBF, 0x4F, 0x49, 0x0A, 0x68, + 0x82, 0x43, 0x0A, 0x60, 0x02, 0x21, 0x01, 0x20, 0x00, 0xF0, 0x8B, 0xB8, + 0x4B, 0x49, 0x0A, 0x68, 0x02, 0x43, 0x0A, 0x60, 0x02, 0x21, 0x01, 0x20, + 0x00, 0xF0, 0x83, 0xB8, 0x48, 0x49, 0x0A, 0x68, 0x82, 0x43, 0x0A, 0x60, + 0x04, 0x21, 0x01, 0x20, 0x00, 0xF0, 0x7B, 0xB8, 0x44, 0x49, 0x0A, 0x68, + 0x02, 0x43, 0x0A, 0x60, 0x04, 0x21, 0x01, 0x20, 0x00, 0xF0, 0x73, 0xB8, + 0x05, 0xF0, 0x86, 0xBF, 0x05, 0xF0, 0x72, 0xBF, 0x10, 0xB5, 0xC0, 0x07, + 0x1B, 0xD0, 0x3E, 0x49, 0x02, 0x20, 0x09, 0x68, 0x91, 0xF8, 0xB0, 0x20, + 0xD2, 0x07, 0x03, 0xD1, 0x91, 0xF8, 0xD4, 0x10, 0xC9, 0x07, 0x00, 0xD0, + 0x22, 0x20, 0x3B, 0x49, 0x0A, 0x68, 0x02, 0x43, 0x42, 0xF0, 0x01, 0x00, + 0x08, 0x60, 0x00, 0x21, 0x00, 0xF0, 0x12, 0xFF, 0xBD, 0xE8, 0x10, 0x40, + 0x4F, 0xF4, 0x00, 0x71, 0x00, 0x20, 0x00, 0xF0, 0x50, 0xB8, 0x10, 0xBD, + 0x10, 0xB5, 0x04, 0x46, 0xC0, 0x07, 0x09, 0xD0, 0x00, 0x23, 0x1A, 0x46, + 0x01, 0x21, 0x10, 0x20, 0x00, 0xF0, 0xE7, 0xFE, 0x04, 0x21, 0x00, 0x20, + 0x00, 0xF0, 0x41, 0xF8, 0xE0, 0x06, 0x0B, 0xD5, 0x00, 0x23, 0x10, 0x21, + 0x1A, 0x46, 0x08, 0x46, 0x00, 0xF0, 0xDB, 0xFE, 0x10, 0x21, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x20, 0x00, 0xF0, 0x33, 0xB8, 0x10, 0xBD, 0x25, 0x49, + 0x08, 0x80, 0x4F, 0xF4, 0x80, 0x71, 0x00, 0x20, 0x00, 0xF0, 0x2B, 0xB8, + 0xC0, 0x07, 0x03, 0xD0, 0x80, 0x21, 0x00, 0x20, 0x00, 0xF0, 0x25, 0xB8, + 0x70, 0x47, 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, 0x97, 0x20, 0x00, 0xF0, + 0x03, 0xFF, 0x00, 0x23, 0x01, 0x22, 0x19, 0x46, 0x10, 0x20, 0x00, 0xF0, + 0xBA, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0x4F, 0xF4, 0x80, 0x61, 0x00, 0x20, + 0x00, 0xF0, 0x11, 0xB8, 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, 0xAD, 0x20, + 0x00, 0xF0, 0xF0, 0xFE, 0x10, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x01, 0x20, + 0x00, 0xF0, 0x05, 0xB8, 0x4F, 0xF4, 0x00, 0x31, 0x00, 0x20, 0xAF, 0xF3, + 0x00, 0x80, 0x38, 0xB5, 0x0C, 0x46, 0x05, 0x46, 0x02, 0xF0, 0xCE, 0xFE, + 0x69, 0x46, 0x28, 0x46, 0x02, 0xF0, 0x09, 0xFF, 0x00, 0x9A, 0x22, 0x42, + 0xF8, 0xD1, 0x38, 0xBD, 0x58, 0x24, 0x10, 0x00, 0x5C, 0x24, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x68, 0x24, 0x10, 0x00, + 0x64, 0x24, 0x10, 0x00, 0x0A, 0x22, 0x10, 0x00, 0x70, 0xB5, 0x20, 0x21, + 0xF6, 0x48, 0x04, 0xF0, 0x42, 0xF9, 0xF5, 0x48, 0x05, 0xF0, 0x98, 0xFE, + 0xF3, 0x4D, 0x44, 0x1E, 0x15, 0xF8, 0x01, 0x1B, 0xA1, 0xF1, 0x90, 0x02, + 0x1F, 0x2A, 0x03, 0xD8, 0xF0, 0x48, 0x50, 0xF8, 0x22, 0x20, 0x07, 0xE0, + 0xA1, 0xF1, 0xC0, 0x00, 0x0F, 0x28, 0x08, 0xD8, 0xEC, 0x49, 0x80, 0x31, + 0x51, 0xF8, 0x20, 0x20, 0x29, 0x46, 0x20, 0x46, 0xBD, 0xE8, 0x70, 0x40, + 0x10, 0x47, 0xB8, 0x29, 0x3E, 0xD0, 0x11, 0xDC, 0xB0, 0x29, 0x1E, 0xD0, + 0xB2, 0x29, 0x2B, 0xD0, 0xB7, 0x29, 0x4E, 0xD1, 0x02, 0x2C, 0x4C, 0xD1, + 0x22, 0x46, 0x29, 0x46, 0xB7, 0x20, 0x00, 0xF0, 0x99, 0xFE, 0x28, 0x88, + 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, 0xF5, 0xBE, 0xFB, 0x29, 0x35, 0xD0, + 0xFC, 0x29, 0x3E, 0xD1, 0x00, 0x2C, 0x3C, 0xD1, 0x00, 0x22, 0x11, 0x46, + 0xFC, 0x20, 0x00, 0xF0, 0x89, 0xFE, 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, + 0xFF, 0xBE, 0x02, 0x2C, 0x31, 0xDD, 0x22, 0x46, 0x29, 0x46, 0xB0, 0x20, + 0x00, 0xF0, 0x7E, 0xFE, 0x28, 0x88, 0xA2, 0x1E, 0xA9, 0x1C, 0xBD, 0xE8, + 0x70, 0x40, 0x40, 0xBA, 0xFF, 0xF7, 0x9F, 0xBE, 0x03, 0x2C, 0x22, 0xD1, + 0x22, 0x46, 0x29, 0x46, 0xB2, 0x20, 0x00, 0xF0, 0x6F, 0xFE, 0x28, 0x88, + 0xA9, 0x78, 0xBD, 0xE8, 0x70, 0x40, 0x40, 0xBA, 0xFF, 0xF7, 0xAE, 0xBE, + 0x02, 0x2C, 0x14, 0xD1, 0x22, 0x46, 0x29, 0x46, 0xB8, 0x20, 0x00, 0xF0, + 0x61, 0xFE, 0x28, 0x88, 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, 0xBB, 0xBE, + 0x00, 0x2C, 0x08, 0xD1, 0x00, 0x22, 0x11, 0x46, 0xFB, 0x20, 0x00, 0xF0, + 0x55, 0xFE, 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, 0xBD, 0xBE, 0x70, 0xBD, + 0x0E, 0xB5, 0x01, 0x28, 0x1C, 0xD1, 0x0A, 0x78, 0x01, 0x2A, 0x19, 0xD1, + 0x02, 0x46, 0xC8, 0x20, 0x00, 0xF0, 0x46, 0xFE, 0x01, 0x20, 0x8D, 0xF8, + 0x01, 0x00, 0xB6, 0x48, 0x00, 0x78, 0x8D, 0xF8, 0x02, 0x00, 0xB5, 0x48, + 0x00, 0x78, 0x8D, 0xF8, 0x03, 0x00, 0xB4, 0x48, 0x00, 0x78, 0x8D, 0xF8, + 0x04, 0x00, 0xB3, 0x48, 0x00, 0x78, 0x8D, 0xF8, 0x05, 0x00, 0x68, 0x46, + 0x00, 0xF0, 0xC3, 0xFA, 0x0E, 0xBD, 0x7C, 0xB5, 0x00, 0x25, 0x0C, 0x46, + 0x00, 0x95, 0x01, 0x21, 0x8D, 0xF8, 0x01, 0x10, 0xFF, 0x21, 0x01, 0x95, + 0x8D, 0xF8, 0x02, 0x10, 0x05, 0x28, 0x15, 0xD1, 0x21, 0x78, 0x01, 0x29, + 0x12, 0xD1, 0x02, 0x46, 0x21, 0x46, 0xC7, 0x20, 0x00, 0xF0, 0x1A, 0xFE, + 0xA1, 0x49, 0x60, 0x78, 0x08, 0x70, 0xA1, 0x49, 0xA0, 0x78, 0x08, 0x70, + 0xA0, 0x49, 0xE0, 0x78, 0x08, 0x70, 0xA0, 0x49, 0x20, 0x79, 0x08, 0x60, + 0x8D, 0xF8, 0x02, 0x50, 0x68, 0x46, 0x00, 0xF0, 0x9F, 0xFA, 0x7C, 0xBD, + 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, 0x93, 0x20, 0x00, 0xF0, 0x02, 0xFE, + 0x01, 0x20, 0xFF, 0xF7, 0x8F, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0x02, 0x20, + 0xFF, 0xF7, 0x9A, 0xBE, 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, 0x92, 0x20, + 0x00, 0xF0, 0xF4, 0xFD, 0x01, 0x20, 0xFF, 0xF7, 0x79, 0xFE, 0xBD, 0xE8, + 0x10, 0x40, 0x02, 0x20, 0xFF, 0xF7, 0x84, 0xBE, 0x70, 0x47, 0x70, 0x47, + 0x70, 0x47, 0x70, 0x47, 0x70, 0x47, 0x70, 0x47, 0x10, 0xB5, 0x00, 0x22, + 0x11, 0x46, 0xA8, 0x20, 0x00, 0xF0, 0xE0, 0xFD, 0xBD, 0xE8, 0x10, 0x40, + 0x08, 0x20, 0xFF, 0xF7, 0x7B, 0xBE, 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, + 0xAB, 0x20, 0x00, 0xF0, 0xD5, 0xFD, 0xBD, 0xE8, 0x10, 0x40, 0x08, 0x20, + 0xFF, 0xF7, 0x68, 0xBE, 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, 0xA2, 0x20, + 0x00, 0xF0, 0xCA, 0xFD, 0xBD, 0xE8, 0x10, 0x40, 0x3F, 0x20, 0xFF, 0xF7, + 0x71, 0xBE, 0x70, 0xB5, 0x04, 0x46, 0x02, 0x46, 0x0D, 0x46, 0xA3, 0x20, + 0x00, 0xF0, 0xBE, 0xFD, 0x77, 0x48, 0x4C, 0xB1, 0x01, 0x2C, 0x09, 0xD0, + 0xBD, 0xE8, 0x70, 0x40, 0xD0, 0x21, 0x4D, 0xF6, 0xC0, 0x62, 0xBA, 0x20, + 0x00, 0xF0, 0x4B, 0xBA, 0x00, 0x21, 0x00, 0xE0, 0x29, 0x78, 0x01, 0x70, + 0x83, 0x20, 0xFF, 0xF7, 0x77, 0xFE, 0x00, 0x23, 0xBD, 0xE8, 0x70, 0x40, + 0x1A, 0x46, 0x19, 0x46, 0x01, 0x20, 0x00, 0xF0, 0x60, 0xBD, 0x10, 0xB5, + 0x00, 0x22, 0x11, 0x46, 0xA4, 0x20, 0x00, 0xF0, 0x9D, 0xFD, 0x38, 0x20, + 0xFF, 0xF7, 0x66, 0xFE, 0x00, 0x23, 0xBD, 0xE8, 0x10, 0x40, 0x1A, 0x46, + 0x19, 0x46, 0x02, 0x20, 0x00, 0xF0, 0x4F, 0xBD, 0x10, 0xB5, 0x00, 0x22, + 0x11, 0x46, 0xAA, 0x20, 0x00, 0xF0, 0x8C, 0xFD, 0xBD, 0xE8, 0x10, 0x40, + 0x00, 0xF0, 0xBC, 0xBC, 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, 0xAE, 0x20, + 0x00, 0xF0, 0x82, 0xFD, 0xBD, 0xE8, 0x10, 0x40, 0x01, 0x20, 0xFF, 0xF7, + 0x6D, 0xBE, 0x70, 0xB5, 0x0C, 0x46, 0x4D, 0xF6, 0xC0, 0x65, 0x01, 0x28, + 0x05, 0xDB, 0x02, 0x46, 0xC0, 0x20, 0x00, 0xF0, 0x73, 0xFD, 0x20, 0x78, + 0x30, 0xB1, 0x2A, 0x46, 0xBD, 0xE8, 0x70, 0x40, 0xD0, 0x21, 0xBA, 0x20, + 0x00, 0xF0, 0x03, 0xBA, 0x4E, 0x48, 0x00, 0x21, 0x00, 0x68, 0x40, 0xF8, + 0x36, 0x1F, 0x61, 0x78, 0x01, 0x60, 0xA2, 0x78, 0x41, 0xEA, 0x02, 0x21, + 0x01, 0x60, 0xE2, 0x78, 0x41, 0xEA, 0x02, 0x41, 0x01, 0x60, 0x22, 0x79, + 0x41, 0xEA, 0x02, 0x61, 0x01, 0x60, 0x70, 0xBD, 0x70, 0xB5, 0x05, 0x46, + 0x00, 0x20, 0x02, 0x46, 0x03, 0x46, 0x05, 0xE0, 0xCC, 0x5C, 0xDE, 0x00, + 0xB4, 0x40, 0x22, 0x43, 0x5B, 0x1C, 0xDB, 0xB2, 0xAB, 0x42, 0xF7, 0xDB, + 0xD1, 0x07, 0x01, 0xD0, 0x40, 0xF0, 0x01, 0x00, 0x91, 0x07, 0x01, 0xD5, + 0x40, 0xF0, 0x02, 0x00, 0x51, 0x07, 0x01, 0xD5, 0x40, 0xF0, 0x04, 0x00, + 0x11, 0x07, 0x01, 0xD5, 0x40, 0xF0, 0x08, 0x00, 0xD1, 0x06, 0x01, 0xD5, + 0x40, 0xF4, 0x80, 0x70, 0x91, 0x06, 0x01, 0xD5, 0x40, 0xF4, 0x00, 0x70, + 0xD1, 0x03, 0x01, 0xD5, 0x40, 0xF4, 0x80, 0x30, 0x70, 0xBD, 0x70, 0xB5, + 0x0D, 0x46, 0x04, 0x46, 0x01, 0x28, 0x0B, 0xDB, 0x02, 0x46, 0xC1, 0x20, + 0x00, 0xF0, 0x22, 0xFD, 0x29, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0xC8, 0xFF, + 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, 0xBA, 0xBD, 0x70, 0xBD, 0x70, 0xB5, + 0x0D, 0x46, 0x04, 0x46, 0x01, 0x28, 0x0B, 0xDB, 0x02, 0x46, 0xC2, 0x20, + 0x00, 0xF0, 0x10, 0xFD, 0x29, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0xB6, 0xFF, + 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, 0xA0, 0xBD, 0x70, 0xBD, 0x10, 0xB5, + 0x00, 0x22, 0x11, 0x46, 0xA5, 0x20, 0x00, 0xF0, 0x01, 0xFD, 0xBD, 0xE8, + 0x10, 0x40, 0xFF, 0xF7, 0x13, 0xBE, 0x10, 0xB5, 0x00, 0x22, 0x11, 0x46, + 0x91, 0x20, 0x00, 0xF0, 0xF7, 0xFC, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, + 0xE6, 0xBC, 0x70, 0xB5, 0x04, 0x46, 0x02, 0x46, 0x0D, 0x46, 0xA7, 0x20, + 0x00, 0xF0, 0xEC, 0xFC, 0x00, 0x20, 0x4C, 0xB1, 0x02, 0x2C, 0x0B, 0xD0, + 0xBD, 0xE8, 0x70, 0x40, 0xD0, 0x21, 0x4D, 0xF6, 0xC0, 0x62, 0xBA, 0x20, + 0x00, 0xF0, 0x79, 0xB9, 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, 0xC5, 0xBD, + 0x28, 0x88, 0xF9, 0xE7, 0x63, 0x51, 0x10, 0x00, 0x64, 0x1D, 0x01, 0x00, + 0xE6, 0x23, 0x10, 0x00, 0xB8, 0x23, 0x10, 0x00, 0x4C, 0x23, 0x10, 0x00, + 0xBC, 0x23, 0x10, 0x00, 0x9C, 0x22, 0x10, 0x00, 0x54, 0x24, 0x10, 0x00, + 0x08, 0xB5, 0xFF, 0xF7, 0xE3, 0xFC, 0x00, 0xF0, 0xCD, 0xFA, 0x1F, 0x21, + 0x6B, 0x46, 0x00, 0x22, 0x04, 0x20, 0x02, 0xF0, 0xFD, 0xFC, 0x00, 0x98, + 0xC0, 0x07, 0x07, 0xD0, 0xFF, 0xF7, 0xF0, 0xFD, 0x01, 0x21, 0x04, 0x20, + 0x02, 0xF0, 0xD3, 0xFC, 0x05, 0xF0, 0x84, 0xFC, 0x00, 0x98, 0x80, 0x07, + 0x0C, 0xD5, 0x08, 0x20, 0xFF, 0xF7, 0x4E, 0xFD, 0x02, 0x21, 0x04, 0x20, + 0x02, 0xF0, 0xC7, 0xFC, 0x00, 0x23, 0x1A, 0x46, 0x19, 0x46, 0xCC, 0x20, + 0x00, 0xF0, 0x61, 0xFC, 0x00, 0x98, 0x40, 0x07, 0xDB, 0xD5, 0x08, 0x20, + 0xFF, 0xF7, 0x36, 0xFD, 0x04, 0x21, 0x08, 0x46, 0x02, 0xF0, 0xB7, 0xFC, + 0x00, 0x23, 0x1A, 0x46, 0x19, 0x46, 0xCD, 0x20, 0x00, 0xF0, 0x51, 0xFC, + 0xCD, 0xE7, 0x01, 0x21, 0x04, 0x20, 0x02, 0xF0, 0x7D, 0xBC, 0x00, 0x00, + 0x30, 0x4D, 0x2F, 0x4E, 0x04, 0x46, 0xC5, 0xE9, 0x00, 0x61, 0x00, 0x68, + 0xA8, 0x60, 0x60, 0x68, 0xE8, 0x60, 0xA0, 0x68, 0x28, 0x61, 0xE0, 0x68, + 0x68, 0x61, 0x20, 0x69, 0xA8, 0x61, 0x60, 0x69, 0xE8, 0x61, 0xA0, 0x69, + 0x28, 0x62, 0xE0, 0x69, 0x68, 0x62, 0x05, 0xF1, 0x28, 0x00, 0x05, 0xF0, + 0x49, 0xFB, 0x02, 0xF0, 0xCA, 0xFD, 0x44, 0x35, 0x85, 0xE8, 0x51, 0x00, + 0xFE, 0xE7, 0x10, 0xB5, 0x01, 0x28, 0x4F, 0xF0, 0x00, 0x02, 0x05, 0xD0, + 0x01, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0x02, 0x20, 0x00, 0xF0, 0xFD, 0xB8, + 0x01, 0x21, 0x02, 0x20, 0x00, 0xF0, 0xF9, 0xF8, 0xFE, 0xE7, 0x01, 0x28, + 0x06, 0xD0, 0x02, 0x28, 0x07, 0xD1, 0x00, 0x22, 0x04, 0x21, 0x02, 0x20, + 0x00, 0xF0, 0xEF, 0xB8, 0x00, 0x22, 0x02, 0x21, 0xF9, 0xE7, 0x70, 0x47, + 0x10, 0xB5, 0x04, 0x46, 0x01, 0x46, 0x00, 0x22, 0x03, 0x20, 0x00, 0xF0, + 0xE4, 0xF8, 0x01, 0x2C, 0x01, 0xD0, 0x02, 0x2C, 0x00, 0xD1, 0xFE, 0xE7, + 0x10, 0xBD, 0x0A, 0x46, 0x01, 0x46, 0x08, 0x20, 0x00, 0xF0, 0xD9, 0xB8, + 0x42, 0xEA, 0x01, 0x42, 0x01, 0x46, 0x09, 0x20, 0x00, 0xF0, 0xD3, 0xB8, + 0x00, 0x22, 0x01, 0x46, 0x06, 0x20, 0x00, 0xF0, 0xCE, 0xB8, 0x01, 0x46, + 0x00, 0x22, 0x07, 0x20, 0x00, 0xF0, 0xC9, 0xF8, 0xFE, 0xE7, 0x00, 0x00, + 0xAF, 0x05, 0x50, 0xFA, 0x5C, 0x00, 0x01, 0x20, 0x10, 0xB5, 0x05, 0xF0, + 0x75, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0x4F, 0xF4, 0x80, 0x41, 0x00, 0x20, + 0x02, 0xF0, 0x0E, 0xBC, 0x48, 0x49, 0x00, 0x20, 0x81, 0xF8, 0x00, 0x04, + 0x81, 0xF8, 0x01, 0x04, 0x81, 0xF8, 0x02, 0x04, 0x45, 0x49, 0x08, 0x70, + 0x70, 0x47, 0x10, 0xB5, 0x04, 0x46, 0x01, 0x20, 0x02, 0xF0, 0xAF, 0xFC, + 0x40, 0x48, 0x90, 0xF8, 0x02, 0x14, 0x00, 0xEB, 0xC1, 0x02, 0x00, 0x21, + 0x63, 0x5C, 0x53, 0x54, 0x49, 0x1C, 0xC9, 0xB2, 0x08, 0x29, 0xF9, 0xD3, + 0x90, 0xF8, 0x00, 0x14, 0x3A, 0x4A, 0x49, 0x1C, 0xC9, 0xB2, 0x80, 0xF8, + 0x00, 0x14, 0x80, 0x29, 0x04, 0xD1, 0x7F, 0x21, 0x80, 0xF8, 0x00, 0x14, + 0x01, 0x21, 0x11, 0x70, 0x90, 0xF8, 0x02, 0x14, 0x49, 0x1C, 0x01, 0xF0, + 0x7F, 0x01, 0x80, 0xF8, 0x02, 0x14, 0x11, 0x78, 0x01, 0x29, 0x06, 0xD1, + 0x90, 0xF8, 0x01, 0x24, 0x52, 0x1C, 0x02, 0xF0, 0x7F, 0x02, 0x80, 0xF8, + 0x01, 0x24, 0x90, 0xF8, 0x00, 0x04, 0x01, 0x28, 0x06, 0xD1, 0x29, 0xB9, + 0x2A, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x26, 0x00, 0x00, 0xF0, 0x49, 0xF8, + 0xBD, 0xE8, 0x10, 0x40, 0x01, 0x20, 0x02, 0xF0, 0x8F, 0xBC, 0x70, 0xB5, + 0x01, 0x20, 0x02, 0xF0, 0x70, 0xFC, 0x00, 0x25, 0x20, 0x4C, 0x21, 0x4E, + 0x12, 0xE0, 0x94, 0xF8, 0x01, 0x04, 0x04, 0xEB, 0xC0, 0x00, 0x05, 0xF0, + 0xFF, 0xFB, 0x94, 0xF8, 0x01, 0x04, 0x40, 0x1C, 0x00, 0xF0, 0x7F, 0x00, + 0x84, 0xF8, 0x01, 0x04, 0x94, 0xF8, 0x00, 0x04, 0x40, 0x1E, 0x84, 0xF8, + 0x00, 0x04, 0x35, 0x70, 0x94, 0xF8, 0x00, 0x04, 0x88, 0xB1, 0x05, 0xF0, + 0xAD, 0xFB, 0x3E, 0x28, 0xE5, 0xDB, 0x94, 0xF8, 0x00, 0x04, 0x50, 0xB1, + 0x12, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x26, 0x00, 0x00, 0xF0, 0x19, 0xF8, + 0xBD, 0xE8, 0x70, 0x40, 0x01, 0x20, 0x02, 0xF0, 0x5F, 0xBC, 0x84, 0xF8, + 0x01, 0x54, 0x84, 0xF8, 0x02, 0x54, 0xF5, 0xE7, 0x10, 0xB5, 0x00, 0x24, + 0x01, 0x20, 0x02, 0xF0, 0x3A, 0xFC, 0x06, 0x48, 0x90, 0xF8, 0x00, 0x04, + 0x00, 0xB9, 0x01, 0x24, 0x01, 0x20, 0x02, 0xF0, 0x4D, 0xFC, 0x20, 0x46, + 0x10, 0xBD, 0x04, 0x49, 0x05, 0xF0, 0xC6, 0xBD, 0x34, 0xA4, 0x01, 0x20, + 0x30, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x9D, 0xBD, 0x00, 0x00, + 0x10, 0xB5, 0x04, 0x46, 0x05, 0xF0, 0x7A, 0xFB, 0x3E, 0x28, 0x0D, 0xDA, + 0xFF, 0xF7, 0xDE, 0xFF, 0x50, 0xB1, 0x01, 0x20, 0x02, 0xF0, 0x19, 0xFC, + 0x20, 0x46, 0x05, 0xF0, 0xAF, 0xFB, 0xBD, 0xE8, 0x10, 0x40, 0x01, 0x20, + 0x02, 0xF0, 0x2C, 0xBC, 0x20, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0xFF, 0xF7, + 0x58, 0xBF, 0x17, 0x21, 0x01, 0x70, 0xE3, 0xE7, 0x18, 0x21, 0x01, 0x70, + 0xE0, 0xE7, 0x1C, 0xB5, 0x0F, 0x23, 0x8D, 0xF8, 0x00, 0x30, 0x8D, 0xF8, + 0x01, 0x00, 0x8D, 0xF8, 0x02, 0x10, 0x08, 0x0A, 0x8D, 0xF8, 0x03, 0x00, + 0x8D, 0xF8, 0x04, 0x20, 0x10, 0x0A, 0x8D, 0xF8, 0x05, 0x00, 0x10, 0x0C, + 0x8D, 0xF8, 0x06, 0x00, 0x10, 0x0E, 0xF8, 0x49, 0x8D, 0xF8, 0x07, 0x00, + 0x01, 0x20, 0x08, 0x70, 0x68, 0x46, 0x05, 0xF0, 0x83, 0xFB, 0x1C, 0xBD, + 0x1C, 0xB5, 0x00, 0x23, 0x00, 0x93, 0x01, 0x93, 0x0F, 0x23, 0x8D, 0xF8, + 0x00, 0x30, 0x8D, 0xF8, 0x01, 0x00, 0xEF, 0x48, 0x8D, 0xF8, 0x02, 0x10, + 0x8D, 0xF8, 0x03, 0x20, 0x00, 0x78, 0x80, 0x01, 0x8D, 0xF8, 0x07, 0x00, + 0x68, 0x46, 0x05, 0xF0, 0x6D, 0xFB, 0x1C, 0xBD, 0x2D, 0xE9, 0xFC, 0x41, + 0x00, 0x27, 0x06, 0x46, 0x00, 0x97, 0x12, 0x20, 0x15, 0x46, 0x0C, 0x46, + 0x01, 0x97, 0x8D, 0xF8, 0x00, 0x00, 0x1A, 0xE0, 0x20, 0x0A, 0x8D, 0xF8, + 0x01, 0x00, 0xC4, 0xF3, 0x0B, 0x00, 0x8D, 0xF8, 0x02, 0x40, 0x31, 0x5C, + 0x30, 0x44, 0x8D, 0xF8, 0x03, 0x10, 0x41, 0x78, 0x8D, 0xF8, 0x04, 0x10, + 0x81, 0x78, 0x8D, 0xF8, 0x05, 0x10, 0xC0, 0x78, 0x8D, 0xF8, 0x06, 0x00, + 0x8D, 0xF8, 0x07, 0x70, 0x68, 0x46, 0xFF, 0xF7, 0x89, 0xFF, 0x24, 0x1D, + 0x2D, 0x1F, 0x00, 0x2D, 0xE2, 0xDC, 0xBD, 0xE8, 0xFC, 0x81, 0x2D, 0xE9, + 0xF7, 0x4F, 0x86, 0xB0, 0x00, 0x20, 0x02, 0x90, 0x03, 0x90, 0x12, 0x20, + 0x92, 0x46, 0x88, 0x46, 0x8D, 0xF8, 0x08, 0x00, 0xA1, 0xF5, 0x80, 0x57, + 0x01, 0xF0, 0x55, 0xFC, 0x81, 0x46, 0x4F, 0xF0, 0x01, 0x0B, 0xA7, 0xE0, + 0x4F, 0xEA, 0x18, 0x20, 0x8D, 0xF8, 0x09, 0x00, 0x30, 0x20, 0xB7, 0xFB, + 0xF0, 0xF0, 0xC5, 0xB2, 0xB9, 0x00, 0x03, 0x20, 0xB1, 0xFB, 0xF0, 0xF0, + 0x00, 0xF0, 0x3F, 0x04, 0x0B, 0xFA, 0x05, 0xF0, 0x8D, 0xF8, 0x0A, 0x80, + 0x49, 0x46, 0x10, 0xEA, 0x09, 0x0F, 0x03, 0xD1, 0x28, 0x46, 0x00, 0xF0, + 0xFA, 0xFA, 0x05, 0x46, 0x00, 0x26, 0x22, 0x2C, 0x1B, 0xD2, 0x20, 0x2D, + 0x19, 0xD2, 0x1F, 0x20, 0x5B, 0x46, 0xCD, 0xF8, 0x10, 0x90, 0x06, 0xE0, + 0x04, 0x9A, 0x03, 0xFA, 0x00, 0xF1, 0x8A, 0x43, 0x40, 0x1E, 0xC0, 0xB2, + 0x04, 0x92, 0xA8, 0x42, 0xF6, 0xD8, 0x04, 0x21, 0x04, 0xA8, 0x05, 0xF0, + 0x1D, 0xFE, 0x40, 0x1E, 0xC1, 0xB2, 0x22, 0x46, 0x06, 0x98, 0x03, 0xF0, + 0xA5, 0xFE, 0x0D, 0xF8, 0x06, 0x00, 0x3F, 0x2C, 0x04, 0xD0, 0x64, 0x1C, + 0x14, 0xF0, 0xFF, 0x04, 0x02, 0xD0, 0x06, 0xE0, 0x00, 0x24, 0xF9, 0xE7, + 0x49, 0x46, 0x28, 0x46, 0x00, 0xF0, 0xCD, 0xFA, 0x05, 0x46, 0x76, 0x1C, + 0xF6, 0xB2, 0x06, 0x2E, 0xCF, 0xD3, 0x03, 0x20, 0xB7, 0xFB, 0xF0, 0xF1, + 0x00, 0xFB, 0x11, 0x70, 0x20, 0xB1, 0x01, 0x28, 0x25, 0xD0, 0x02, 0x28, + 0x47, 0xD1, 0x58, 0xE0, 0x9D, 0xF8, 0x01, 0x00, 0x9D, 0xF8, 0x00, 0x10, + 0x00, 0xF0, 0x03, 0x02, 0x62, 0xF3, 0x9F, 0x11, 0x8D, 0xF8, 0x0B, 0x10, + 0xC0, 0xF3, 0x83, 0x01, 0x9D, 0xF8, 0x02, 0x00, 0x41, 0xEA, 0x00, 0x11, + 0x8D, 0xF8, 0x0C, 0x10, 0xC0, 0xF3, 0x01, 0x11, 0x9D, 0xF8, 0x03, 0x00, + 0x41, 0xEA, 0x80, 0x00, 0x8D, 0xF8, 0x0D, 0x00, 0x9D, 0xF8, 0x05, 0x00, + 0x00, 0xF0, 0x03, 0x01, 0x9D, 0xF8, 0x04, 0x00, 0x61, 0xF3, 0x9F, 0x10, + 0x21, 0xE0, 0x9D, 0xF8, 0x00, 0x00, 0xC0, 0xF3, 0x83, 0x01, 0x9D, 0xF8, + 0x01, 0x00, 0x41, 0xEA, 0x00, 0x11, 0x8D, 0xF8, 0x0B, 0x10, 0xC0, 0xF3, + 0x01, 0x11, 0x9D, 0xF8, 0x02, 0x00, 0x41, 0xEA, 0x80, 0x00, 0x8D, 0xF8, + 0x0C, 0x00, 0x9D, 0xF8, 0x04, 0x00, 0x9D, 0xF8, 0x03, 0x10, 0x00, 0xF0, + 0x03, 0x02, 0x62, 0xF3, 0x9F, 0x11, 0x8D, 0xF8, 0x0D, 0x10, 0xC0, 0xF3, + 0x83, 0x01, 0x9D, 0xF8, 0x05, 0x00, 0x41, 0xEA, 0x00, 0x10, 0x8D, 0xF8, + 0x0E, 0x00, 0x00, 0x20, 0x8D, 0xF8, 0x0F, 0x00, 0x02, 0xA8, 0xFF, 0xF7, + 0xCD, 0xFE, 0x08, 0xF1, 0x04, 0x08, 0xAA, 0xF1, 0x04, 0x0A, 0x3F, 0x1D, + 0xBA, 0xF1, 0x00, 0x0F, 0x3F, 0xF7, 0x54, 0xAF, 0x09, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x9D, 0xF8, 0x00, 0x00, 0xC0, 0xF3, 0x01, 0x11, 0x9D, 0xF8, + 0x01, 0x00, 0x41, 0xEA, 0x80, 0x00, 0x8D, 0xF8, 0x0B, 0x00, 0x9D, 0xF8, + 0x03, 0x00, 0x9D, 0xF8, 0x02, 0x10, 0x00, 0xF0, 0x03, 0x02, 0x62, 0xF3, + 0x9F, 0x11, 0x8D, 0xF8, 0x0C, 0x10, 0xC0, 0xF3, 0x83, 0x01, 0x9D, 0xF8, + 0x04, 0x00, 0x41, 0xEA, 0x00, 0x11, 0x8D, 0xF8, 0x0D, 0x10, 0xC0, 0xF3, + 0x01, 0x11, 0x9D, 0xF8, 0x05, 0x00, 0x41, 0xEA, 0x80, 0x00, 0xC8, 0xE7, + 0x1C, 0xB5, 0x00, 0x24, 0x01, 0x94, 0x00, 0x94, 0x13, 0x24, 0x8D, 0xF8, + 0x00, 0x40, 0x8D, 0xF8, 0x01, 0x00, 0x00, 0x0A, 0x8D, 0xF8, 0x02, 0x00, + 0x8D, 0xF8, 0x04, 0x20, 0x8D, 0xF8, 0x03, 0x10, 0x8D, 0xF8, 0x05, 0x30, + 0x68, 0x46, 0xFF, 0xF7, 0x89, 0xFE, 0x1C, 0xBD, 0x1C, 0xB5, 0x00, 0x24, + 0x01, 0x94, 0x00, 0x94, 0x25, 0x24, 0x8D, 0xF8, 0x00, 0x40, 0x8D, 0xF8, + 0x01, 0x00, 0x00, 0x0A, 0x8D, 0xF8, 0x02, 0x00, 0x8D, 0xF8, 0x04, 0x20, + 0x8D, 0xF8, 0x03, 0x10, 0x8D, 0xF8, 0x05, 0x30, 0x68, 0x46, 0xFF, 0xF7, + 0x73, 0xFE, 0x1C, 0xBD, 0x1C, 0xB5, 0x00, 0x20, 0x4A, 0x49, 0x00, 0x90, + 0x01, 0x90, 0x09, 0x68, 0x10, 0x23, 0x4A, 0x78, 0x89, 0x78, 0x8D, 0xF8, + 0x00, 0x30, 0x8D, 0xF8, 0x04, 0x00, 0x8D, 0xF8, 0x01, 0x00, 0x8D, 0xF8, + 0x05, 0x20, 0x8D, 0xF8, 0x02, 0x00, 0x8D, 0xF8, 0x06, 0x10, 0x8D, 0xF8, + 0x03, 0x00, 0x8D, 0xF8, 0x07, 0x00, 0x68, 0x46, 0xFF, 0xF7, 0x56, 0xFE, + 0x1C, 0xBD, 0x2D, 0xE9, 0xFE, 0x43, 0x04, 0x46, 0x00, 0x20, 0x01, 0x90, + 0x02, 0x90, 0x94, 0xF8, 0x24, 0x00, 0x03, 0x28, 0x02, 0xD0, 0x08, 0x28, + 0x71, 0xD0, 0x05, 0x20, 0x8D, 0xF8, 0x04, 0x00, 0x94, 0xF8, 0x27, 0x20, + 0x9D, 0xF8, 0x05, 0x00, 0x33, 0x4F, 0x62, 0xF3, 0x03, 0x00, 0x61, 0xF3, + 0x07, 0x10, 0x8D, 0xF8, 0x05, 0x00, 0x38, 0x68, 0xB0, 0xF9, 0x16, 0x10, + 0xB4, 0xF9, 0x16, 0x00, 0x00, 0xF0, 0xE9, 0xFC, 0x06, 0x46, 0x38, 0x68, + 0xB0, 0xF9, 0x18, 0x10, 0xB4, 0xF9, 0x14, 0x00, 0x00, 0xF0, 0xE1, 0xFC, + 0x05, 0x46, 0x38, 0x68, 0x90, 0xF8, 0x20, 0x10, 0x4A, 0x07, 0x02, 0xD5, + 0xC2, 0x8A, 0x92, 0x1B, 0x16, 0xB2, 0x8A, 0x07, 0x02, 0xD5, 0x02, 0x8B, + 0x52, 0x1B, 0x15, 0xB2, 0xC9, 0x07, 0x46, 0xD0, 0x01, 0x8B, 0xB0, 0x46, + 0x0A, 0x46, 0x28, 0x46, 0x00, 0xF0, 0xBF, 0xFC, 0x06, 0x46, 0x38, 0x68, + 0xC1, 0x8A, 0x40, 0x46, 0x0A, 0x46, 0x00, 0xF0, 0xB8, 0xFC, 0x31, 0x09, + 0x8D, 0xF8, 0x06, 0x10, 0x01, 0x09, 0x8D, 0xF8, 0x07, 0x10, 0x9D, 0xF8, + 0x08, 0x10, 0x01, 0x23, 0x66, 0xF3, 0x07, 0x11, 0x60, 0xF3, 0x03, 0x01, + 0x8D, 0xF8, 0x08, 0x10, 0xB4, 0xF8, 0x3D, 0x50, 0x3F, 0x20, 0x00, 0x90, + 0x38, 0x68, 0x25, 0x34, 0xB0, 0xF8, 0xBE, 0x21, 0xB0, 0xF8, 0xBC, 0x11, + 0x28, 0x46, 0x00, 0xF0, 0xB0, 0xFC, 0x8D, 0xF8, 0x09, 0x00, 0xE0, 0x78, + 0x00, 0x07, 0x80, 0x0E, 0x8D, 0xF8, 0x0A, 0x00, 0x21, 0x78, 0x40, 0xEA, + 0x81, 0x10, 0x8D, 0xF8, 0x0A, 0x00, 0x05, 0x48, 0x00, 0x78, 0xC0, 0x07, + 0x40, 0x0E, 0x8D, 0xF8, 0x0B, 0x00, 0x01, 0xA8, 0xFF, 0xF7, 0xDE, 0xFD, + 0xBD, 0xE8, 0xFE, 0x83, 0x31, 0x24, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, + 0xFF, 0xE7, 0x04, 0x20, 0x8C, 0xE7, 0xC1, 0x8A, 0x30, 0x46, 0x0A, 0x46, + 0x00, 0xF0, 0x79, 0xFC, 0x06, 0x46, 0x38, 0x68, 0x01, 0x8B, 0x28, 0x46, + 0x0A, 0x46, 0xB8, 0xE7, 0x2D, 0xE9, 0xFE, 0x43, 0x04, 0x46, 0x00, 0x20, + 0x01, 0x90, 0x02, 0x90, 0x94, 0xF8, 0x24, 0x00, 0x03, 0x28, 0x02, 0xD0, + 0x08, 0x28, 0x6A, 0xD0, 0x05, 0x20, 0x8D, 0xF8, 0x04, 0x00, 0x9D, 0xF8, + 0x05, 0x00, 0xB5, 0x4F, 0x20, 0xF0, 0x0F, 0x00, 0x09, 0x30, 0x61, 0xF3, + 0x07, 0x10, 0x8D, 0xF8, 0x05, 0x00, 0x38, 0x68, 0xB0, 0xF9, 0x16, 0x10, + 0xB4, 0xF9, 0x02, 0x00, 0x00, 0xF0, 0x5F, 0xFC, 0x06, 0x46, 0x38, 0x68, + 0xB0, 0xF9, 0x18, 0x10, 0xB4, 0xF9, 0x00, 0x00, 0x00, 0xF0, 0x57, 0xFC, + 0x05, 0x46, 0x38, 0x68, 0x90, 0xF8, 0x20, 0x10, 0x4A, 0x07, 0x02, 0xD5, + 0xC2, 0x8A, 0x92, 0x1B, 0x16, 0xB2, 0x8A, 0x07, 0x02, 0xD5, 0x02, 0x8B, + 0x52, 0x1B, 0x15, 0xB2, 0xC9, 0x07, 0x40, 0xD0, 0x01, 0x8B, 0xB0, 0x46, + 0x0A, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x35, 0xFC, 0x06, 0x46, 0x38, 0x68, + 0xC1, 0x8A, 0x40, 0x46, 0x0A, 0x46, 0x00, 0xF0, 0x2E, 0xFC, 0x31, 0x09, + 0x8D, 0xF8, 0x06, 0x10, 0x01, 0x09, 0x8D, 0xF8, 0x07, 0x10, 0x9D, 0xF8, + 0x08, 0x10, 0x01, 0x23, 0x66, 0xF3, 0x07, 0x11, 0x60, 0xF3, 0x03, 0x01, + 0x8D, 0xF8, 0x08, 0x10, 0xB4, 0xF8, 0x3D, 0x50, 0x3F, 0x20, 0x00, 0x90, + 0x38, 0x68, 0x25, 0x34, 0xB0, 0xF8, 0xBE, 0x21, 0xB0, 0xF8, 0xBC, 0x11, + 0x28, 0x46, 0x00, 0xF0, 0x26, 0xFC, 0x8D, 0xF8, 0x09, 0x00, 0xE0, 0x78, + 0x00, 0x07, 0x80, 0x0E, 0x8D, 0xF8, 0x0A, 0x00, 0x21, 0x78, 0x40, 0xEA, + 0x81, 0x10, 0x8D, 0xF8, 0x0A, 0x00, 0x88, 0x48, 0x00, 0x78, 0xC0, 0x07, + 0x40, 0x0E, 0x8D, 0xF8, 0x0B, 0x00, 0x01, 0xA8, 0xFF, 0xF7, 0x54, 0xFD, + 0x74, 0xE7, 0x04, 0x20, 0x93, 0xE7, 0xC1, 0x8A, 0x30, 0x46, 0x0A, 0x46, + 0x00, 0xF0, 0xF5, 0xFB, 0x06, 0x46, 0x38, 0x68, 0x01, 0x8B, 0x28, 0x46, + 0x0A, 0x46, 0xBE, 0xE7, 0x70, 0xB5, 0x88, 0xB0, 0x00, 0x24, 0x00, 0x94, + 0x14, 0x20, 0x01, 0x94, 0x8D, 0xF8, 0x00, 0x00, 0x04, 0xF0, 0x91, 0xFF, + 0x8D, 0xF8, 0x01, 0x00, 0x04, 0xF0, 0x46, 0xFA, 0x8D, 0xF8, 0x02, 0x00, + 0x49, 0x20, 0x8D, 0xF8, 0x03, 0x00, 0x72, 0x4E, 0xB5, 0x20, 0x8D, 0xF8, + 0x04, 0x00, 0x30, 0x68, 0x70, 0x4D, 0x41, 0x78, 0x8D, 0xF8, 0x05, 0x10, + 0x80, 0x78, 0x8D, 0xF8, 0x06, 0x00, 0x28, 0x78, 0x80, 0x01, 0x8D, 0xF8, + 0x07, 0x00, 0x68, 0x46, 0xFF, 0xF7, 0x20, 0xFD, 0x00, 0x94, 0x15, 0x20, + 0x01, 0x94, 0x8D, 0xF8, 0x00, 0x00, 0x30, 0x68, 0x90, 0xF8, 0xEC, 0x17, + 0x8D, 0xF8, 0x01, 0x10, 0x90, 0xF8, 0xED, 0x17, 0x8D, 0xF8, 0x02, 0x10, + 0x90, 0xF8, 0xEE, 0x17, 0x8D, 0xF8, 0x03, 0x10, 0x90, 0xF8, 0xEF, 0x07, + 0x8D, 0xF8, 0x04, 0x00, 0x28, 0x78, 0x80, 0x01, 0x8D, 0xF8, 0x07, 0x00, + 0x68, 0x46, 0xFF, 0xF7, 0x03, 0xFD, 0x00, 0x94, 0x02, 0xA8, 0x01, 0x94, + 0x04, 0xF0, 0x56, 0xFE, 0x1B, 0x20, 0x8D, 0xF8, 0x00, 0x00, 0x02, 0x98, + 0x00, 0x0E, 0x8D, 0xF8, 0x01, 0x00, 0x02, 0x98, 0x00, 0x0C, 0x8D, 0xF8, + 0x02, 0x00, 0x02, 0x98, 0x00, 0x0A, 0x8D, 0xF8, 0x03, 0x00, 0x02, 0x98, + 0x8D, 0xF8, 0x04, 0x00, 0x8D, 0xF8, 0x05, 0x40, 0x04, 0x98, 0x8D, 0xF8, + 0x06, 0x00, 0x28, 0x78, 0x80, 0x01, 0x8D, 0xF8, 0x07, 0x00, 0x68, 0x46, + 0xFF, 0xF7, 0xE0, 0xFC, 0x00, 0x94, 0x1C, 0x20, 0x8D, 0xF8, 0x00, 0x00, + 0x03, 0x98, 0x01, 0x94, 0x00, 0x0E, 0x8D, 0xF8, 0x01, 0x00, 0x03, 0x98, + 0x00, 0x0C, 0x8D, 0xF8, 0x02, 0x00, 0x03, 0x98, 0x00, 0x0A, 0x8D, 0xF8, + 0x03, 0x00, 0x03, 0x98, 0x8D, 0xF8, 0x04, 0x00, 0x05, 0x98, 0x8D, 0xF8, + 0x05, 0x00, 0x06, 0x98, 0x8D, 0xF8, 0x06, 0x00, 0x28, 0x78, 0x80, 0x01, + 0x8D, 0xF8, 0x07, 0x00, 0x68, 0x46, 0xFF, 0xF7, 0xBF, 0xFC, 0x08, 0xB0, + 0x70, 0xBD, 0x1C, 0xB5, 0x36, 0x4C, 0x24, 0x68, 0x94, 0xF8, 0xE8, 0x47, + 0xA4, 0x07, 0x10, 0xD5, 0x00, 0x24, 0x01, 0x94, 0x00, 0x94, 0x16, 0x24, + 0x8D, 0xF8, 0x00, 0x40, 0x8D, 0xF8, 0x01, 0x00, 0x8D, 0xF8, 0x02, 0x10, + 0x8D, 0xF8, 0x03, 0x20, 0x8D, 0xF8, 0x04, 0x30, 0x68, 0x46, 0xFF, 0xF7, + 0xA5, 0xFC, 0x1C, 0xBD, 0xC2, 0xB2, 0xC0, 0xF3, 0x07, 0x23, 0x05, 0x20, + 0xE1, 0xE7, 0x1C, 0xB5, 0x27, 0x4A, 0x12, 0x68, 0x92, 0xF8, 0xE8, 0x27, + 0x52, 0x07, 0x0F, 0xD5, 0x00, 0x22, 0x00, 0x92, 0x01, 0x92, 0x1D, 0x22, + 0x8D, 0xF8, 0x00, 0x20, 0x8D, 0xF8, 0x01, 0x00, 0x08, 0x0A, 0x8D, 0xF8, + 0x02, 0x00, 0x8D, 0xF8, 0x03, 0x10, 0x68, 0x46, 0xFF, 0xF7, 0x88, 0xFC, + 0x1C, 0xBD, 0x1C, 0xB5, 0x00, 0x20, 0x00, 0x90, 0x01, 0x90, 0x11, 0x21, + 0x8D, 0xF8, 0x07, 0x00, 0x8D, 0xF8, 0x00, 0x10, 0x68, 0x46, 0xFF, 0xF7, + 0x7B, 0xFC, 0x1C, 0xBD, 0x1C, 0xB5, 0x15, 0x4B, 0x1B, 0x68, 0x93, 0xF8, + 0xE8, 0x37, 0x9B, 0x06, 0x14, 0xD5, 0x00, 0x23, 0x00, 0x93, 0x01, 0x93, + 0xEC, 0x23, 0x8D, 0xF8, 0x00, 0x30, 0x8D, 0xF8, 0x01, 0x00, 0x00, 0x2A, + 0x07, 0xDD, 0x31, 0xB1, 0x06, 0x2A, 0x00, 0xDD, 0x06, 0x22, 0x0D, 0xF1, + 0x02, 0x00, 0x03, 0xF0, 0xE5, 0xF9, 0x68, 0x46, 0xFF, 0xF7, 0x5E, 0xFC, + 0x1C, 0xBD, 0x10, 0xB5, 0x00, 0x22, 0x01, 0x24, 0x40, 0x1C, 0xC0, 0xB2, + 0x04, 0xFA, 0x00, 0xF3, 0x0B, 0x42, 0x00, 0xD0, 0x02, 0x46, 0x0A, 0xB9, + 0x20, 0x28, 0xF5, 0xD3, 0x10, 0x46, 0x10, 0xBD, 0x50, 0x24, 0x10, 0x00, + 0x31, 0x24, 0x10, 0x00, 0x10, 0xB5, 0x5A, 0x21, 0x20, 0x48, 0x03, 0xF0, + 0x48, 0xFA, 0x20, 0x48, 0x20, 0x4A, 0x21, 0x4B, 0x01, 0x68, 0x08, 0x7D, + 0xA2, 0xEB, 0x40, 0x02, 0x1B, 0x48, 0x02, 0x80, 0x0A, 0x7D, 0xA3, 0xEB, + 0x42, 0x02, 0x42, 0x80, 0x0A, 0x7D, 0x1C, 0x4B, 0xA3, 0xEB, 0x42, 0x02, + 0x82, 0x80, 0x09, 0x7D, 0x1A, 0x4A, 0xA2, 0xEB, 0x41, 0x01, 0xC1, 0x80, + 0x00, 0x21, 0x01, 0x81, 0x18, 0x4A, 0x42, 0x83, 0x18, 0x4A, 0x82, 0x83, + 0x18, 0x4A, 0xC2, 0x83, 0x18, 0x4A, 0x02, 0x84, 0x18, 0x4A, 0x42, 0x84, + 0x18, 0x4A, 0x82, 0x84, 0x18, 0x4A, 0xC2, 0x84, 0x18, 0x4A, 0x02, 0x85, + 0x41, 0x87, 0x81, 0x87, 0xC1, 0x87, 0xA0, 0xF8, 0x40, 0x10, 0xA0, 0xF8, + 0x42, 0x10, 0xA0, 0xF8, 0x44, 0x10, 0xA0, 0xF8, 0x46, 0x10, 0xA0, 0xF8, + 0x48, 0x10, 0xA0, 0xF8, 0x4A, 0x10, 0xA0, 0xF8, 0x4C, 0x10, 0x10, 0x49, + 0xA0, 0xF8, 0x50, 0x10, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x50, 0x24, 0x10, 0x00, 0x38, 0xA9, 0x01, 0x20, 0x78, 0x94, 0x01, 0x20, + 0x38, 0x9C, 0x01, 0x20, 0x7C, 0x1B, 0x01, 0x20, 0xFC, 0x93, 0x01, 0x20, + 0x38, 0x94, 0x01, 0x20, 0x04, 0x0A, 0x01, 0x20, 0x40, 0x0A, 0x01, 0x20, + 0x80, 0x0A, 0x01, 0x20, 0xBC, 0x0A, 0x01, 0x20, 0xFC, 0x22, 0x01, 0x20, + 0x38, 0x23, 0x01, 0x20, 0x00, 0x80, 0x01, 0x20, 0x70, 0xB5, 0x0D, 0x46, + 0xFC, 0x4C, 0x4A, 0x00, 0x01, 0x46, 0x20, 0x46, 0x03, 0xF0, 0x64, 0xF9, + 0x00, 0x21, 0x6E, 0x1E, 0x0F, 0xE0, 0x48, 0x1C, 0x0A, 0xE0, 0x34, 0xF9, + 0x10, 0x20, 0x34, 0xF9, 0x11, 0x30, 0x9A, 0x42, 0x03, 0xDA, 0x24, 0xF8, + 0x11, 0x20, 0x24, 0xF8, 0x10, 0x30, 0x40, 0x1C, 0xA8, 0x42, 0xF2, 0xD3, + 0x49, 0x1C, 0xB1, 0x42, 0xED, 0xD3, 0xE9, 0x07, 0x25, 0xF0, 0x01, 0x00, + 0x01, 0xD0, 0x20, 0x5E, 0x70, 0xBD, 0x21, 0x5E, 0x20, 0x44, 0x30, 0xF9, + 0x02, 0x0C, 0x08, 0x44, 0x00, 0xEB, 0xD0, 0x70, 0x40, 0x10, 0x00, 0xB2, + 0x70, 0xBD, 0x10, 0xB5, 0xF3, 0xF7, 0x40, 0xFE, 0x04, 0xF0, 0x2E, 0xFE, + 0x08, 0xB1, 0x04, 0xF0, 0x56, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0xF3, 0xF7, + 0x35, 0xBE, 0x10, 0xB5, 0xF3, 0xF7, 0x34, 0xFE, 0x04, 0xF0, 0x22, 0xFE, + 0x08, 0xB9, 0x04, 0xF0, 0x33, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0xF3, 0xF7, + 0x29, 0xBE, 0x30, 0xB5, 0x06, 0xE0, 0x32, 0xF8, 0x13, 0x40, 0x31, 0xF8, + 0x13, 0x50, 0x64, 0x1B, 0x20, 0xF8, 0x13, 0x40, 0x5B, 0x1E, 0xF6, 0xD2, + 0x30, 0xBD, 0x88, 0x42, 0x00, 0xD3, 0x08, 0x46, 0x90, 0x42, 0x00, 0xD3, + 0x10, 0x46, 0x70, 0x47, 0x88, 0x42, 0x00, 0xD3, 0x08, 0x46, 0x70, 0x47, + 0x88, 0x42, 0x00, 0xD8, 0x08, 0x46, 0x70, 0x47, 0x2D, 0xE9, 0xF1, 0x4F, + 0x86, 0xB0, 0xF0, 0x21, 0x06, 0x98, 0x03, 0xF0, 0xA6, 0xF9, 0x01, 0xF0, + 0x60, 0xF8, 0x02, 0x90, 0x01, 0xF0, 0x46, 0xF8, 0xCD, 0xE9, 0x00, 0x01, + 0x04, 0x21, 0x02, 0xA8, 0x05, 0xF0, 0x54, 0xFA, 0x83, 0x46, 0x08, 0x21, + 0x68, 0x46, 0x05, 0xF0, 0x4F, 0xFA, 0x00, 0x26, 0xB0, 0x46, 0x4F, 0xF0, + 0x01, 0x0A, 0x04, 0x90, 0x2D, 0xE0, 0x42, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x03, 0xF0, 0xB3, 0xF8, 0xDD, 0xE9, 0x00, 0x23, 0x10, 0x40, 0x19, 0x40, + 0x08, 0x43, 0x20, 0xD0, 0x00, 0x27, 0xBA, 0x48, 0x3C, 0x46, 0x3D, 0x46, + 0x0A, 0xFA, 0x06, 0xF9, 0x03, 0x90, 0x15, 0xE0, 0x02, 0x99, 0x0A, 0xFA, + 0x05, 0xF0, 0x08, 0x42, 0x0F, 0xD0, 0x42, 0x46, 0x21, 0x46, 0x03, 0x98, + 0x03, 0xF0, 0xBA, 0xFA, 0xB8, 0x42, 0x06, 0xD0, 0x06, 0x99, 0x01, 0xEB, + 0xC4, 0x01, 0x0B, 0x68, 0x43, 0xEA, 0x09, 0x03, 0x0B, 0x60, 0x07, 0x46, + 0x64, 0x1C, 0x6D, 0x1C, 0x5C, 0x45, 0xE7, 0xD3, 0x76, 0x1C, 0x08, 0xF1, + 0x01, 0x08, 0x04, 0x98, 0x86, 0x42, 0xCE, 0xD3, 0x07, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, 0xF0, 0x4F, 0x00, 0x25, 0x9B, 0x46, 0x03, 0xF1, + 0x28, 0x09, 0x8E, 0x46, 0x2C, 0x46, 0x01, 0x23, 0xA2, 0xF1, 0x01, 0x0A, + 0x1C, 0xE0, 0x02, 0x21, 0xAE, 0xF1, 0x01, 0x0C, 0x15, 0xE0, 0x59, 0xF8, + 0x31, 0x70, 0x01, 0x26, 0x9E, 0x40, 0x37, 0x40, 0x3F, 0x00, 0x0D, 0xD0, + 0x4F, 0x1E, 0x01, 0xFB, 0x02, 0x36, 0x07, 0xFB, 0x02, 0x37, 0x30, 0xF9, + 0x16, 0x60, 0x30, 0xF9, 0x17, 0x70, 0xF6, 0x1B, 0x00, 0xD5, 0x76, 0x42, + 0x35, 0x44, 0x64, 0x1C, 0x49, 0x1C, 0x61, 0x45, 0xE7, 0xD3, 0x5B, 0x1C, + 0x53, 0x45, 0xE0, 0xD3, 0x04, 0xB9, 0x01, 0x24, 0x95, 0xFB, 0xF4, 0xF1, + 0xCB, 0xF8, 0x20, 0x10, 0xCB, 0xE7, 0x2D, 0xE9, 0xF0, 0x4F, 0x8D, 0x4C, + 0x88, 0xB0, 0x24, 0x68, 0xDD, 0xE9, 0x11, 0x65, 0x94, 0xF8, 0x42, 0x71, + 0x01, 0x2D, 0x07, 0xF0, 0x0F, 0x0A, 0x01, 0xD1, 0x4F, 0xF0, 0x00, 0x0A, + 0xCA, 0xF1, 0x10, 0x05, 0x49, 0x1E, 0x02, 0x95, 0x04, 0x91, 0x94, 0xF8, + 0x40, 0x51, 0x01, 0xF0, 0xFF, 0x0E, 0x8D, 0x42, 0x00, 0xDA, 0xAE, 0x46, + 0x51, 0x1E, 0x03, 0x91, 0xC9, 0xB2, 0x06, 0x91, 0x94, 0xF8, 0x41, 0x11, + 0x03, 0x9C, 0xA1, 0x42, 0x00, 0xDA, 0x06, 0x91, 0x31, 0x6A, 0x05, 0x91, + 0x00, 0x21, 0x00, 0x91, 0x88, 0x46, 0x89, 0x46, 0x01, 0x91, 0x07, 0x91, + 0x06, 0xF1, 0x28, 0x0B, 0x01, 0x21, 0x7A, 0xE0, 0x00, 0x26, 0x35, 0x46, + 0x02, 0x24, 0x20, 0xE0, 0x5B, 0xF8, 0x34, 0xC0, 0x01, 0x27, 0x8F, 0x40, + 0x0C, 0xEA, 0x07, 0x0C, 0x04, 0xFB, 0x02, 0x17, 0x5F, 0xEA, 0x0C, 0x0C, + 0x30, 0xF9, 0x17, 0xC0, 0xA4, 0xF1, 0x01, 0x07, 0x07, 0xFB, 0x02, 0x17, + 0x30, 0xF9, 0x17, 0x70, 0xAC, 0xEB, 0x07, 0x07, 0x06, 0xD0, 0x00, 0x2F, + 0x00, 0xDA, 0x7F, 0x42, 0xDD, 0xF8, 0x14, 0xC0, 0xA7, 0xEB, 0x0C, 0x07, + 0x00, 0x2F, 0x00, 0xDA, 0x7F, 0x42, 0x3E, 0x44, 0x64, 0x1C, 0x77, 0x46, + 0x74, 0x45, 0xDB, 0xD3, 0x3C, 0x46, 0x20, 0xE0, 0x5B, 0xF8, 0x34, 0xC0, + 0x01, 0x27, 0x8F, 0x40, 0x0C, 0xEA, 0x07, 0x0C, 0x04, 0xFB, 0x02, 0x17, + 0x5F, 0xEA, 0x0C, 0x0C, 0x30, 0xF9, 0x17, 0xC0, 0xA4, 0xF1, 0x01, 0x07, + 0x07, 0xFB, 0x02, 0x17, 0x30, 0xF9, 0x17, 0x70, 0xAC, 0xEB, 0x07, 0x07, + 0x06, 0xD0, 0x00, 0x2F, 0x00, 0xDA, 0x7F, 0x42, 0xDD, 0xF8, 0x14, 0xC0, + 0xA7, 0xEB, 0x0C, 0x07, 0x00, 0x2F, 0x00, 0xDA, 0x7F, 0x42, 0x3D, 0x44, + 0x64, 0x1C, 0x04, 0x9F, 0xBC, 0x42, 0xDB, 0xD3, 0x07, 0x9C, 0x4C, 0xB1, + 0x00, 0x9C, 0xA4, 0x1B, 0x00, 0xD5, 0x64, 0x42, 0xA0, 0x44, 0x01, 0x9C, + 0x64, 0x1B, 0x00, 0xD5, 0x64, 0x42, 0xA1, 0x44, 0xCD, 0xE9, 0x00, 0x65, + 0x06, 0x9C, 0xA1, 0x42, 0x18, 0xD1, 0x1C, 0x68, 0x02, 0x9D, 0x04, 0xFB, + 0x0A, 0xF4, 0x05, 0xFB, 0x08, 0x44, 0xE5, 0x17, 0x04, 0xEB, 0x15, 0x74, + 0x24, 0x11, 0x1C, 0x60, 0x9C, 0x68, 0x02, 0x9D, 0x04, 0xFB, 0x0A, 0xF4, + 0x05, 0xFB, 0x09, 0x44, 0xE5, 0x17, 0x04, 0xEB, 0x15, 0x74, 0x24, 0x11, + 0x4F, 0xF0, 0x00, 0x08, 0xC1, 0x46, 0x9C, 0x60, 0x01, 0x24, 0x49, 0x1C, + 0x07, 0x94, 0x03, 0x9C, 0xA1, 0x42, 0x81, 0xD3, 0x58, 0x68, 0x02, 0x99, + 0x00, 0xFB, 0x0A, 0xF0, 0x01, 0xFB, 0x08, 0x00, 0xC1, 0x17, 0x00, 0xEB, + 0x11, 0x70, 0x00, 0x11, 0x58, 0x60, 0xD8, 0x68, 0x02, 0x99, 0x00, 0xFB, + 0x0A, 0xF0, 0x01, 0xFB, 0x09, 0x00, 0xC1, 0x17, 0x00, 0xEB, 0x11, 0x70, + 0x00, 0x11, 0xD8, 0x60, 0x08, 0xB0, 0x04, 0xE7, 0xFC, 0xB5, 0x1C, 0x46, + 0x15, 0x46, 0x0E, 0x46, 0x07, 0x46, 0xFF, 0xF7, 0x00, 0xFF, 0x01, 0x20, + 0xCD, 0xE9, 0x00, 0x40, 0x2A, 0x46, 0x31, 0x46, 0x38, 0x46, 0x04, 0xF1, + 0x10, 0x03, 0xFF, 0xF7, 0x28, 0xFF, 0xFC, 0xBD, 0xF0, 0xB5, 0x00, 0x24, + 0x06, 0x46, 0x25, 0x46, 0x20, 0x46, 0x0D, 0xE0, 0x36, 0xF9, 0x11, 0x30, + 0x00, 0x2B, 0x01, 0xDB, 0x1F, 0x46, 0x00, 0xE0, 0x5F, 0x42, 0x97, 0x42, + 0x04, 0xDD, 0x00, 0x2B, 0x01, 0xDD, 0x1D, 0x44, 0x00, 0xE0, 0x1C, 0x44, + 0x49, 0x1E, 0xEF, 0xD2, 0x00, 0x2C, 0x00, 0xDA, 0x64, 0x42, 0xAC, 0x42, + 0x00, 0xDD, 0x01, 0x20, 0xF0, 0xBD, 0x2D, 0xE9, 0xF0, 0x43, 0x15, 0x46, + 0x4F, 0xF0, 0x01, 0x08, 0xDD, 0xF8, 0x1C, 0x90, 0x16, 0xE0, 0x00, 0xEB, + 0x45, 0x0C, 0x00, 0x26, 0x0C, 0x46, 0x0B, 0xE0, 0x08, 0xFA, 0x04, 0xF7, + 0x1F, 0x42, 0x07, 0xD0, 0x04, 0xFB, 0x02, 0xF7, 0x00, 0xEB, 0x47, 0x07, + 0x37, 0xF8, 0x15, 0x70, 0x3E, 0x44, 0x36, 0xB2, 0x64, 0x1E, 0xF1, 0xD2, + 0x96, 0xFB, 0xF9, 0xF4, 0xAC, 0xF8, 0x00, 0x40, 0x6D, 0x1E, 0xE6, 0xD2, + 0xBD, 0xE8, 0xF0, 0x83, 0xE0, 0x5C, 0x10, 0x00, 0x40, 0x10, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x4F, 0x47, 0xF6, 0xFF, 0x76, + 0xDD, 0xE9, 0x09, 0xB5, 0xF4, 0x43, 0x9A, 0x46, 0x01, 0x27, 0x81, 0x46, + 0xBC, 0x46, 0x2B, 0x46, 0x14, 0xE0, 0x32, 0xF8, 0x13, 0x00, 0x31, 0xF8, + 0x13, 0x80, 0xA0, 0xEB, 0x08, 0x00, 0x00, 0xB2, 0xB0, 0x42, 0x00, 0xDA, + 0x06, 0x46, 0xA0, 0x42, 0x00, 0xDD, 0x04, 0x46, 0x00, 0x28, 0x02, 0xDD, + 0x4F, 0xF0, 0x00, 0x0C, 0x02, 0xE0, 0x01, 0xDA, 0x4F, 0xF0, 0x00, 0x07, + 0x5B, 0x1E, 0xE8, 0xD2, 0x27, 0xB1, 0xBC, 0xF1, 0x00, 0x0F, 0x01, 0xD0, + 0x00, 0x20, 0x09, 0xE0, 0x57, 0xEA, 0x0C, 0x00, 0x06, 0xD0, 0x27, 0xB9, + 0xBC, 0xF1, 0x00, 0x0F, 0x01, 0xD0, 0x20, 0x46, 0x00, 0xE0, 0x30, 0x46, + 0x00, 0x23, 0x1E, 0x46, 0x2C, 0x46, 0xCB, 0xF1, 0x00, 0x0C, 0x0D, 0xE0, + 0x32, 0xF8, 0x14, 0x70, 0x31, 0xF8, 0x14, 0x80, 0xA7, 0xEB, 0x08, 0x07, + 0x3F, 0x1A, 0x3F, 0xB2, 0x57, 0x45, 0x03, 0xDA, 0x67, 0x45, 0x01, 0xDD, + 0x3B, 0x44, 0x76, 0x1C, 0x64, 0x1E, 0xEF, 0xD2, 0x06, 0xB9, 0x01, 0x26, + 0x93, 0xFB, 0xF6, 0xF2, 0x13, 0x18, 0x04, 0xE0, 0x31, 0xF8, 0x15, 0x00, + 0x18, 0x44, 0x29, 0xF8, 0x15, 0x00, 0x6D, 0x1E, 0xF8, 0xD2, 0x54, 0xE6, + 0xF0, 0xB4, 0xDD, 0xE9, 0x04, 0x64, 0x01, 0x2C, 0x02, 0xD0, 0x0B, 0xB1, + 0xF5, 0x1A, 0x0E, 0xE0, 0xF0, 0xBC, 0x52, 0x00, 0x02, 0xF0, 0x06, 0xBF, + 0x30, 0xF9, 0x12, 0x40, 0x31, 0xF9, 0x12, 0x70, 0x5C, 0x43, 0x07, 0xFB, + 0x05, 0x44, 0x94, 0xFB, 0xF6, 0xF4, 0x20, 0xF8, 0x12, 0x40, 0x52, 0x1E, + 0xF2, 0xD2, 0xF0, 0xBC, 0x70, 0x47, 0xF0, 0xB5, 0x16, 0x46, 0x15, 0xE0, + 0x00, 0x27, 0x3D, 0x46, 0xF4, 0x1A, 0x06, 0xEB, 0x03, 0x0C, 0x08, 0xE0, + 0x00, 0x2C, 0x05, 0xDB, 0x94, 0x42, 0x03, 0xDA, 0x31, 0xF9, 0x14, 0xE0, + 0x6D, 0x1C, 0x77, 0x44, 0x64, 0x1C, 0xA4, 0x45, 0xF4, 0xDA, 0x1D, 0xB1, + 0x97, 0xFB, 0xF5, 0xF4, 0x20, 0xF8, 0x16, 0x40, 0x76, 0x1E, 0xE7, 0xD2, + 0xF0, 0xBD, 0x91, 0x42, 0x01, 0xD1, 0x00, 0xB2, 0x70, 0x47, 0x50, 0x43, + 0xB0, 0xFB, 0xF1, 0xF0, 0x00, 0xB2, 0x90, 0x42, 0xF7, 0xD9, 0x10, 0x46, + 0xF5, 0xE7, 0x88, 0x42, 0x01, 0xDD, 0x08, 0x46, 0x70, 0x47, 0x00, 0x28, + 0xFC, 0xDA, 0x00, 0x20, 0x70, 0x47, 0x30, 0xB5, 0x04, 0x46, 0x00, 0x20, + 0x03, 0x9D, 0x8C, 0x42, 0x01, 0xD2, 0x18, 0x46, 0x30, 0xBD, 0x94, 0x42, + 0x01, 0xD9, 0x28, 0x46, 0x30, 0xBD, 0x8A, 0x42, 0xFC, 0xD0, 0x60, 0x1A, + 0xEC, 0x1A, 0x60, 0x43, 0x51, 0x1A, 0x90, 0xFB, 0xF1, 0xF0, 0x18, 0x44, + 0xC0, 0xB2, 0x30, 0xBD, 0xF0, 0xB5, 0xA1, 0xB0, 0x0E, 0x46, 0x07, 0x46, + 0x15, 0x46, 0x80, 0x21, 0x68, 0x46, 0x02, 0xF0, 0x48, 0xFF, 0x2C, 0x46, + 0x6B, 0x46, 0x14, 0xE0, 0x00, 0x20, 0x31, 0x46, 0x09, 0xE0, 0x00, 0xEB, + 0x04, 0x0C, 0x53, 0xF8, 0x24, 0x20, 0x37, 0xF9, 0x1C, 0xC0, 0x28, 0x44, + 0x62, 0x44, 0x43, 0xF8, 0x24, 0x20, 0x49, 0x1E, 0xF3, 0xD2, 0x53, 0xF8, + 0x24, 0x00, 0x90, 0xFB, 0xF6, 0xF0, 0x43, 0xF8, 0x24, 0x00, 0x64, 0x1E, + 0xE8, 0xD2, 0x00, 0x24, 0x29, 0x46, 0x13, 0xE0, 0x00, 0x20, 0x32, 0x46, + 0x0B, 0xE0, 0x02, 0xFB, 0x05, 0x1C, 0x53, 0xF8, 0x21, 0xE0, 0x37, 0xF9, + 0x1C, 0xC0, 0xBC, 0xEB, 0x0E, 0x0C, 0x01, 0xD5, 0xCC, 0xF1, 0x00, 0x0C, + 0x60, 0x44, 0x52, 0x1E, 0xF1, 0xD2, 0xA0, 0x42, 0x00, 0xDD, 0x04, 0x46, + 0x49, 0x1E, 0xE9, 0xD2, 0x94, 0xFB, 0xF6, 0xF0, 0xB0, 0xF5, 0x80, 0x3F, + 0x03, 0xDB, 0x4F, 0xF6, 0xFF, 0x70, 0x21, 0xB0, 0xF0, 0xBD, 0x80, 0xB2, + 0xFB, 0xE7, 0x2D, 0xE9, 0xF0, 0x43, 0x06, 0x46, 0x00, 0x20, 0x07, 0x9F, + 0x04, 0x46, 0x4F, 0xF0, 0x01, 0x09, 0x7F, 0x1E, 0x1B, 0xE0, 0x31, 0xF8, + 0x14, 0x50, 0x36, 0xF8, 0x14, 0xC0, 0x06, 0xEB, 0x44, 0x08, 0xA5, 0xEB, + 0x0C, 0x05, 0x01, 0xEB, 0x44, 0x0C, 0xB8, 0xF8, 0x02, 0x80, 0xBC, 0xF8, + 0x02, 0xC0, 0xAC, 0xEB, 0x08, 0x0C, 0xA5, 0xEB, 0x0C, 0x05, 0x2D, 0xB2, + 0x95, 0x42, 0x05, 0xDA, 0x1D, 0x44, 0x00, 0x2D, 0x02, 0xDD, 0x09, 0xFA, + 0x04, 0xF5, 0x28, 0x43, 0x64, 0x1C, 0xBC, 0x42, 0xE1, 0xDB, 0xCD, 0xE6, + 0x03, 0xE0, 0x00, 0xEA, 0x40, 0x03, 0x03, 0xEA, 0x50, 0x00, 0x49, 0x1E, + 0xF9, 0xD2, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x43, 0x04, 0x46, 0x85, 0xB0, + 0xA5, 0x20, 0x04, 0xF8, 0x01, 0x0B, 0x4F, 0xF6, 0xFF, 0x79, 0xDF, 0xF8, + 0x28, 0x84, 0xA4, 0xF8, 0x00, 0x90, 0x01, 0x27, 0xE7, 0x70, 0x98, 0xF8, + 0x00, 0x00, 0xA0, 0x70, 0x04, 0xF0, 0xF1, 0xFA, 0x05, 0x0A, 0x04, 0xF0, + 0xEE, 0xFA, 0x45, 0xEA, 0x00, 0x20, 0xA0, 0x80, 0x04, 0xF0, 0xED, 0xFA, + 0xA0, 0x71, 0x03, 0xF0, 0x9D, 0xFD, 0xE0, 0x71, 0x03, 0xF0, 0xA0, 0xFD, + 0xFE, 0x4D, 0x20, 0x72, 0x28, 0x68, 0x90, 0xF8, 0xEC, 0x17, 0xA1, 0x72, + 0x90, 0xF8, 0xED, 0x17, 0xE1, 0x72, 0x90, 0xF8, 0xEE, 0x17, 0x21, 0x73, + 0x90, 0xF8, 0xEF, 0x17, 0x61, 0x73, 0x90, 0xF8, 0xF0, 0x17, 0xA1, 0x73, + 0x90, 0xF8, 0xF1, 0x17, 0xE1, 0x73, 0x90, 0xF8, 0xF2, 0x17, 0x21, 0x74, + 0x90, 0xF8, 0xF3, 0x07, 0x60, 0x74, 0x68, 0x46, 0x04, 0xF0, 0xCC, 0xF9, + 0x00, 0x98, 0x00, 0x26, 0x00, 0x0E, 0xA0, 0x74, 0x00, 0x98, 0x00, 0x0C, + 0xE0, 0x74, 0x00, 0x98, 0x00, 0x0A, 0x20, 0x75, 0x00, 0x98, 0x60, 0x75, + 0xA6, 0x75, 0x02, 0x98, 0xE0, 0x75, 0x01, 0x98, 0x00, 0x0E, 0x20, 0x76, + 0x01, 0x98, 0x00, 0x0C, 0x60, 0x76, 0x01, 0x98, 0x00, 0x0A, 0xA0, 0x76, + 0x01, 0x98, 0xE0, 0x76, 0x03, 0x98, 0x20, 0x77, 0x04, 0x98, 0x60, 0x77, + 0x02, 0x20, 0x60, 0x72, 0x4B, 0xF2, 0x49, 0x50, 0xE0, 0x83, 0x28, 0x68, + 0xB0, 0xF8, 0x01, 0x10, 0x21, 0x84, 0xDD, 0x49, 0xC4, 0xF8, 0x22, 0x10, + 0xC1, 0x8A, 0xE1, 0x84, 0x01, 0x8B, 0x21, 0x85, 0x41, 0x7D, 0x84, 0xF8, + 0x2A, 0x10, 0x01, 0x7D, 0x84, 0xF8, 0x2B, 0x10, 0x01, 0x7B, 0x42, 0x7B, + 0x43, 0x7C, 0x41, 0xEA, 0x02, 0x21, 0x82, 0x7B, 0x41, 0xEA, 0x02, 0x41, + 0xC2, 0x7B, 0x41, 0xEA, 0x02, 0x61, 0x02, 0x7C, 0x42, 0xEA, 0x03, 0x22, + 0x83, 0x7C, 0x42, 0xEA, 0x03, 0x42, 0xC3, 0x7C, 0xE1, 0x62, 0x42, 0xEA, + 0x03, 0x62, 0x22, 0x63, 0x01, 0x79, 0x42, 0x79, 0x43, 0x7A, 0x41, 0xEA, + 0x02, 0x21, 0x82, 0x79, 0x41, 0xEA, 0x02, 0x41, 0xC2, 0x79, 0x41, 0xEA, + 0x02, 0x61, 0x02, 0x7A, 0x42, 0xEA, 0x03, 0x22, 0x83, 0x7A, 0x42, 0xEA, + 0x03, 0x42, 0xC3, 0x7A, 0x61, 0x63, 0x42, 0xEA, 0x03, 0x62, 0xA2, 0x63, + 0x01, 0x21, 0x00, 0xF2, 0xA3, 0x60, 0x04, 0xF0, 0xED, 0xFE, 0x84, 0xF8, + 0x3C, 0x00, 0x28, 0x68, 0x90, 0xF8, 0x9F, 0x16, 0xD0, 0xF8, 0xA0, 0x26, + 0x41, 0xEA, 0x02, 0x21, 0xCA, 0x17, 0xC4, 0xF8, 0x3D, 0x10, 0xC4, 0xF8, + 0x41, 0x20, 0x90, 0xF8, 0x9B, 0x16, 0xD0, 0xF8, 0x9C, 0x26, 0x00, 0xF2, + 0xE9, 0x60, 0x41, 0xEA, 0x02, 0x21, 0xCA, 0x17, 0xC4, 0xF8, 0x45, 0x10, + 0xC4, 0xF8, 0x49, 0x20, 0x01, 0x21, 0x04, 0xF0, 0xCF, 0xFE, 0x84, 0xF8, + 0x4D, 0x00, 0x28, 0x68, 0x90, 0xF8, 0xE5, 0x16, 0xD0, 0xF8, 0xE6, 0x26, + 0x41, 0xEA, 0x02, 0x21, 0xCA, 0x17, 0xC4, 0xF8, 0x4E, 0x10, 0xC4, 0xF8, + 0x52, 0x20, 0x90, 0xF8, 0xE1, 0x16, 0xD0, 0xF8, 0xE2, 0x26, 0x41, 0xEA, + 0x02, 0x21, 0xCA, 0x17, 0xC4, 0xF8, 0x56, 0x10, 0xC4, 0xF8, 0x5A, 0x20, + 0x90, 0xF8, 0xC1, 0x16, 0x84, 0xF8, 0x5E, 0x10, 0x90, 0xF8, 0xC2, 0x16, + 0x84, 0xF8, 0x5F, 0x10, 0x90, 0xF8, 0xD5, 0x16, 0xD0, 0xF8, 0xD6, 0x26, + 0x41, 0xEA, 0x02, 0x21, 0xCA, 0x17, 0x21, 0x66, 0x62, 0x66, 0x90, 0xF8, + 0xD0, 0x16, 0xD0, 0xF8, 0xD1, 0x26, 0x41, 0xEA, 0x02, 0x21, 0xCA, 0x17, + 0xA1, 0x66, 0x44, 0xF8, 0x6C, 0x2F, 0x90, 0xF8, 0x29, 0x17, 0x21, 0x71, + 0x90, 0xF8, 0x31, 0x17, 0x61, 0x71, 0x90, 0xF8, 0x39, 0x17, 0xA1, 0x71, + 0x90, 0xF8, 0x41, 0x17, 0xE1, 0x71, 0x90, 0xF8, 0x4D, 0x17, 0x21, 0x72, + 0x90, 0xF8, 0x5D, 0x17, 0x61, 0x72, 0x90, 0xF8, 0x6D, 0x17, 0xA1, 0x72, + 0x90, 0xF8, 0x11, 0x07, 0xE0, 0x72, 0x40, 0xF2, 0xFF, 0x31, 0x89, 0x48, + 0x6D, 0x3C, 0x04, 0xF0, 0x9B, 0xFE, 0x87, 0x49, 0xD1, 0xF8, 0xFC, 0x1F, + 0x81, 0x42, 0x1F, 0xD1, 0x85, 0x48, 0x00, 0x68, 0x01, 0x78, 0x84, 0xF8, + 0x79, 0x10, 0x81, 0x78, 0x84, 0xF8, 0x7A, 0x10, 0x01, 0x79, 0x84, 0xF8, + 0x7B, 0x10, 0x81, 0x79, 0x84, 0xF8, 0x7C, 0x10, 0x01, 0x7C, 0x84, 0xF8, + 0x7D, 0x10, 0x01, 0x7E, 0x84, 0xF8, 0x7E, 0x10, 0x90, 0xF8, 0x28, 0x10, + 0x84, 0xF8, 0x7F, 0x10, 0x90, 0xF8, 0x30, 0x10, 0x84, 0xF8, 0x80, 0x10, + 0xD0, 0xF8, 0x36, 0x00, 0xC4, 0xF8, 0x81, 0x00, 0x1B, 0x20, 0xC4, 0xF8, + 0x85, 0x00, 0xC4, 0xF8, 0x89, 0x60, 0x28, 0x68, 0x90, 0xF8, 0xE8, 0x07, + 0x80, 0x06, 0x01, 0xD5, 0xC4, 0xF8, 0x89, 0x70, 0x00, 0x23, 0x1A, 0x46, + 0x98, 0xF8, 0x00, 0x10, 0x48, 0x46, 0xFF, 0xF7, 0xC9, 0xF8, 0x05, 0xB0, + 0xBD, 0xE8, 0xF0, 0x83, 0x6B, 0x48, 0xAD, 0xE6, 0x2D, 0xE9, 0xFF, 0x5F, + 0x04, 0x21, 0x68, 0x46, 0x12, 0x9F, 0x04, 0xF0, 0x39, 0xFE, 0x06, 0x46, + 0x08, 0x21, 0x02, 0xA8, 0x04, 0xF0, 0x34, 0xFE, 0x00, 0x25, 0x82, 0x46, + 0x2C, 0x46, 0xA9, 0x46, 0xA8, 0x46, 0x4F, 0xF0, 0x01, 0x0B, 0x19, 0xE0, + 0x00, 0x99, 0x0B, 0xFA, 0x09, 0xF0, 0x08, 0x42, 0x10, 0xD0, 0x00, 0x22, + 0x49, 0x46, 0x0E, 0x98, 0x02, 0xF0, 0xB6, 0xFE, 0x78, 0x55, 0x00, 0x22, + 0x49, 0x46, 0x0F, 0x98, 0x02, 0xF0, 0xAC, 0xFE, 0xAA, 0x19, 0x02, 0xEB, + 0x0A, 0x01, 0x6D, 0x1C, 0x78, 0x54, 0xED, 0xB2, 0x09, 0xF1, 0x01, 0x00, + 0x00, 0xF0, 0xFF, 0x09, 0xB5, 0x42, 0xE3, 0xDB, 0x21, 0xE0, 0x42, 0x46, + 0x01, 0x20, 0x00, 0x21, 0x02, 0xF0, 0x79, 0xFC, 0xDD, 0xE9, 0x02, 0x23, + 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x12, 0xD0, 0x01, 0x22, 0x41, 0x46, + 0x10, 0x98, 0x02, 0xF0, 0x93, 0xFE, 0xA1, 0x19, 0x01, 0x22, 0x78, 0x54, + 0x41, 0x46, 0x11, 0x98, 0x02, 0xF0, 0x88, 0xFE, 0x04, 0xEB, 0x46, 0x02, + 0x02, 0xEB, 0x0A, 0x01, 0x64, 0x1C, 0x78, 0x54, 0xE4, 0xB2, 0x08, 0xF1, + 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x08, 0x54, 0x45, 0xDB, 0xDB, 0xBD, 0xE8, + 0xFF, 0x9F, 0xF0, 0xB5, 0x89, 0xB0, 0x04, 0x46, 0x00, 0xF0, 0xF5, 0xFD, + 0x08, 0x90, 0x00, 0xF0, 0xDB, 0xFD, 0xCD, 0xE9, 0x06, 0x01, 0x04, 0x21, + 0x08, 0xA8, 0x04, 0xF0, 0xDB, 0xFD, 0x05, 0x46, 0x08, 0x21, 0x06, 0xA8, + 0x04, 0xF0, 0xD6, 0xFD, 0xA5, 0x21, 0x21, 0x70, 0x2F, 0x4F, 0x20, 0x21, + 0xA4, 0xF8, 0x01, 0x10, 0xEE, 0xB2, 0x39, 0x78, 0xC5, 0xB2, 0xE1, 0x70, + 0x2F, 0x48, 0x26, 0x71, 0x65, 0x71, 0x00, 0x68, 0x01, 0x7C, 0x21, 0x72, + 0x81, 0x7C, 0x61, 0x72, 0xC1, 0x7C, 0xA1, 0x72, 0x41, 0x7C, 0xE1, 0x72, + 0x41, 0x7C, 0x21, 0x73, 0x01, 0x7D, 0x61, 0x73, 0x40, 0x7D, 0xA0, 0x73, + 0x28, 0x48, 0x10, 0x34, 0xA0, 0xF1, 0x22, 0x01, 0xCD, 0xE9, 0x02, 0x10, + 0xA0, 0xF1, 0x3A, 0x02, 0x5A, 0x38, 0xCD, 0xE9, 0x00, 0x02, 0x04, 0x94, + 0xDD, 0xE9, 0x07, 0x30, 0x06, 0x9A, 0xFF, 0xF7, 0x69, 0xFF, 0x2B, 0x46, + 0x32, 0x46, 0x39, 0x78, 0x20, 0x20, 0xFF, 0xF7, 0x27, 0xF8, 0x09, 0xB0, + 0xF0, 0xBD, 0x2D, 0xE9, 0xFF, 0x5F, 0x04, 0x21, 0x68, 0x46, 0xDD, 0xF8, + 0x3C, 0xA0, 0x04, 0xF0, 0x99, 0xFD, 0x83, 0x46, 0x08, 0x21, 0x02, 0xA8, + 0x04, 0xF0, 0x94, 0xFD, 0x00, 0x26, 0x80, 0x46, 0x37, 0x46, 0x33, 0xE0, + 0x01, 0x20, 0x00, 0x99, 0xB8, 0x40, 0x08, 0x42, 0x2D, 0xD0, 0x00, 0x24, + 0x25, 0x46, 0x06, 0xFB, 0x08, 0xF9, 0x25, 0xE0, 0x2A, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x02, 0xF0, 0xF0, 0xFB, 0xDD, 0xE9, 0x02, 0x23, 0x10, 0x40, + 0x19, 0x40, 0x08, 0x43, 0x19, 0xD0, 0x2A, 0x46, 0x31, 0x46, 0x0E, 0x98, + 0x02, 0xF0, 0x04, 0xFE, 0x0E, 0xE0, 0x00, 0x00, 0x32, 0x24, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x00, 0x39, 0x01, 0x30, 0x00, 0x10, 0x10, 0x00, + 0x54, 0x24, 0x10, 0x00, 0x00, 0x80, 0x01, 0x20, 0x8E, 0x17, 0x10, 0x00, + 0x0A, 0xEB, 0x04, 0x02, 0x64, 0x1C, 0x09, 0xF8, 0x02, 0x00, 0x6D, 0x1C, + 0x44, 0x45, 0xD7, 0xDB, 0x76, 0x1C, 0x7F, 0x1C, 0x5E, 0x45, 0xC9, 0xDB, + 0x6D, 0xE7, 0x10, 0xB5, 0x04, 0x46, 0x4F, 0xF4, 0xF1, 0x61, 0x6C, 0x48, + 0x02, 0xF0, 0x77, 0xFC, 0x6B, 0x48, 0x01, 0x78, 0x49, 0x1C, 0x01, 0x70, + 0xE0, 0x43, 0x00, 0x04, 0x00, 0x0C, 0x06, 0xD0, 0xE0, 0x07, 0x08, 0xD0, + 0xBD, 0xE8, 0x10, 0x40, 0x64, 0x48, 0x00, 0xF0, 0x9C, 0xB8, 0xBD, 0xE8, + 0x10, 0x40, 0x62, 0x48, 0xAC, 0xE5, 0xA0, 0x07, 0x04, 0xD5, 0xBD, 0xE8, + 0x10, 0x40, 0x5F, 0x48, 0x00, 0xF0, 0x49, 0xB8, 0x60, 0x07, 0x04, 0xD5, + 0xBD, 0xE8, 0x10, 0x40, 0x5B, 0x48, 0x00, 0xF0, 0x07, 0xB8, 0xA0, 0x06, + 0x03, 0xD5, 0xBD, 0xE8, 0x10, 0x40, 0x58, 0x48, 0x41, 0xE7, 0x10, 0xBD, + 0xF0, 0xB5, 0x85, 0xB0, 0x04, 0x46, 0x00, 0xF0, 0x28, 0xFB, 0x04, 0x90, + 0x00, 0xF0, 0x0E, 0xFB, 0xCD, 0xE9, 0x02, 0x01, 0x04, 0x21, 0x04, 0xA8, + 0x04, 0xF0, 0x1C, 0xFD, 0x05, 0x46, 0x08, 0x21, 0x02, 0xA8, 0x04, 0xF0, + 0x17, 0xFD, 0xA5, 0x21, 0x04, 0xF8, 0x01, 0x1B, 0x4C, 0x4F, 0x04, 0x21, + 0x24, 0xF8, 0x02, 0x1B, 0x39, 0x78, 0xEE, 0xB2, 0x04, 0xF8, 0x01, 0x1B, + 0xC5, 0xB2, 0x04, 0xF8, 0x04, 0x6B, 0x48, 0x48, 0x04, 0xF8, 0x03, 0x5C, + 0x00, 0x68, 0x81, 0x78, 0x04, 0xF8, 0x08, 0x1B, 0xC0, 0x78, 0x04, 0xF8, + 0x07, 0x0C, 0x44, 0x48, 0xCD, 0xE9, 0x00, 0x04, 0xDD, 0xE9, 0x03, 0x30, + 0x02, 0x9A, 0xFF, 0xF7, 0x56, 0xFF, 0x2B, 0x46, 0x32, 0x46, 0x39, 0x78, + 0x04, 0x20, 0xFE, 0xF7, 0x75, 0xFF, 0x05, 0xB0, 0xF0, 0xBD, 0xF0, 0xB5, + 0x85, 0xB0, 0x04, 0x46, 0x4F, 0xF4, 0xF1, 0x61, 0x02, 0xF0, 0x0D, 0xFC, + 0x00, 0xF0, 0xE9, 0xFA, 0x04, 0x90, 0x00, 0xF0, 0xCF, 0xFA, 0xCD, 0xE9, + 0x02, 0x01, 0x36, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x7E, 0x07, 0x40, 0x07, + 0x03, 0xD5, 0x04, 0x98, 0xF4, 0xF7, 0x64, 0xFB, 0x04, 0x90, 0x04, 0x21, + 0x04, 0xA8, 0x04, 0xF0, 0xD3, 0xFC, 0x05, 0x46, 0x08, 0x21, 0x02, 0xA8, + 0x04, 0xF0, 0xCE, 0xFC, 0xA5, 0x21, 0x04, 0xF8, 0x01, 0x1B, 0x28, 0x4F, + 0x02, 0x21, 0x24, 0xF8, 0x02, 0x1B, 0x39, 0x78, 0xEE, 0xB2, 0x04, 0xF8, + 0x01, 0x1B, 0xC5, 0xB2, 0x04, 0xF8, 0x04, 0x6B, 0x23, 0x48, 0x04, 0xF8, + 0x03, 0x5C, 0x00, 0x68, 0x01, 0x78, 0x04, 0xF8, 0x08, 0x1B, 0x40, 0x78, + 0x04, 0xF8, 0x07, 0x0C, 0x21, 0x48, 0xCD, 0xE9, 0x00, 0x04, 0xDD, 0xE9, + 0x03, 0x30, 0x02, 0x9A, 0xFF, 0xF7, 0x0D, 0xFF, 0x2B, 0x46, 0x32, 0x46, + 0x39, 0x78, 0x02, 0x20, 0xFE, 0xF7, 0x2C, 0xFF, 0xB5, 0xE7, 0x30, 0xB4, + 0xA5, 0x21, 0x01, 0x70, 0x01, 0x21, 0xA0, 0xF8, 0x01, 0x10, 0x13, 0x49, + 0x4F, 0xF0, 0xFF, 0x32, 0x09, 0x78, 0xC1, 0x70, 0x82, 0x60, 0x12, 0x0C, + 0x82, 0x81, 0x10, 0x4A, 0x12, 0x68, 0x92, 0xF8, 0x20, 0x30, 0x55, 0x2B, + 0x08, 0xD1, 0x92, 0xF8, 0x23, 0x30, 0x1C, 0x04, 0x92, 0xF8, 0x21, 0x30, + 0x44, 0xEA, 0x03, 0x24, 0x1C, 0x43, 0x84, 0x60, 0x92, 0xF8, 0x24, 0x30, + 0x55, 0x2B, 0x02, 0xD1, 0x92, 0xF8, 0x25, 0x20, 0x82, 0x81, 0x00, 0x23, + 0x30, 0xBC, 0x1A, 0x46, 0x01, 0x20, 0xFE, 0xF7, 0x01, 0xBF, 0x00, 0x00, + 0x00, 0x80, 0x01, 0x20, 0x32, 0x24, 0x10, 0x00, 0x54, 0x24, 0x10, 0x00, + 0x80, 0x13, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x40, 0x10, 0x10, 0x00, + 0x30, 0xB5, 0xC1, 0x49, 0x0A, 0x68, 0x92, 0xF8, 0x7D, 0x37, 0x02, 0x78, + 0x63, 0xF3, 0x01, 0x02, 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0x7D, 0x37, + 0x9B, 0x08, 0x63, 0xF3, 0x82, 0x02, 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, + 0x7D, 0x37, 0xDB, 0x08, 0x63, 0xF3, 0xC3, 0x02, 0x02, 0x70, 0x0B, 0x68, + 0x93, 0xF8, 0x7D, 0x37, 0x1B, 0x09, 0x63, 0xF3, 0x04, 0x12, 0x02, 0x70, + 0x0B, 0x68, 0x93, 0xF8, 0x7D, 0x37, 0x5B, 0x09, 0x63, 0xF3, 0x45, 0x12, + 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0x7D, 0x37, 0x9B, 0x09, 0x63, 0xF3, + 0x86, 0x12, 0x02, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0x7D, 0x37, 0xDB, 0x09, + 0x63, 0xF3, 0xC7, 0x12, 0x02, 0x70, 0x0A, 0x68, 0x92, 0xF8, 0xA0, 0x37, + 0x42, 0x78, 0x63, 0xF3, 0x01, 0x02, 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, + 0xA0, 0x37, 0x9B, 0x08, 0x63, 0xF3, 0x82, 0x02, 0x42, 0x70, 0x0B, 0x68, + 0x93, 0xF8, 0xA0, 0x37, 0xDB, 0x08, 0x63, 0xF3, 0xC3, 0x02, 0x42, 0x70, + 0x0B, 0x68, 0x93, 0xF8, 0xA0, 0x37, 0x1B, 0x09, 0x63, 0xF3, 0x04, 0x12, + 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xA0, 0x37, 0x5B, 0x09, 0x63, 0xF3, + 0x45, 0x12, 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xA0, 0x37, 0x9B, 0x09, + 0x63, 0xF3, 0x86, 0x12, 0x42, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xA0, 0x37, + 0xDB, 0x09, 0x63, 0xF3, 0xC7, 0x12, 0x42, 0x70, 0x0A, 0x68, 0x92, 0xF8, + 0xC0, 0x24, 0x93, 0x08, 0x82, 0x78, 0x63, 0xF3, 0x04, 0x12, 0x82, 0x70, + 0x0B, 0x68, 0x93, 0xF8, 0xC0, 0x34, 0xDB, 0x08, 0x63, 0xF3, 0x45, 0x12, + 0x82, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xC2, 0x34, 0x1B, 0x09, 0x63, 0xF3, + 0x87, 0x12, 0x82, 0x70, 0x0B, 0x68, 0x93, 0xF8, 0xC3, 0x34, 0x1C, 0x09, + 0xC3, 0x78, 0x64, 0xF3, 0x01, 0x03, 0xC3, 0x70, 0x0C, 0x68, 0x94, 0xF8, + 0xC1, 0x54, 0xC5, 0x71, 0x94, 0xF8, 0xC0, 0x44, 0x64, 0x08, 0x64, 0xF3, + 0xC3, 0x02, 0x82, 0x70, 0x0A, 0x68, 0x92, 0xF8, 0xC2, 0x44, 0x04, 0xF0, + 0x0F, 0x04, 0x04, 0x71, 0x92, 0xF8, 0xC3, 0x24, 0x62, 0xF3, 0x84, 0x03, + 0xC3, 0x70, 0x42, 0x79, 0x22, 0xF0, 0x7F, 0x02, 0x42, 0x71, 0x4F, 0xF6, + 0x05, 0x22, 0x82, 0x81, 0x09, 0x68, 0xB1, 0xF8, 0xC8, 0x24, 0xC2, 0x81, + 0xB1, 0xF8, 0xCA, 0x24, 0x02, 0x82, 0xB1, 0xF8, 0xCC, 0x24, 0x42, 0x82, + 0xB1, 0xF8, 0xCE, 0x24, 0x82, 0x82, 0xB1, 0xF8, 0xD0, 0x24, 0xC2, 0x82, + 0xB1, 0xF8, 0xD2, 0x24, 0x02, 0x83, 0xB1, 0xF8, 0xD4, 0x24, 0x42, 0x83, + 0xB1, 0xF8, 0xD6, 0x24, 0x82, 0x83, 0xB1, 0xF8, 0xC4, 0x24, 0x02, 0x81, + 0xB1, 0xF8, 0xC6, 0x14, 0x41, 0x81, 0x30, 0xBD, 0x10, 0xB5, 0x63, 0x48, + 0xFF, 0xF7, 0x3E, 0xFF, 0x61, 0x48, 0x03, 0xF0, 0x11, 0xFA, 0x00, 0xF0, + 0xB9, 0xF9, 0x00, 0xF0, 0xC7, 0xF8, 0x00, 0xF0, 0x9B, 0xFB, 0x5E, 0x48, + 0x00, 0x21, 0x41, 0x70, 0x01, 0x70, 0x81, 0x60, 0x10, 0xBD, 0x00, 0x21, + 0x01, 0x60, 0x41, 0x60, 0x81, 0x60, 0xC1, 0x60, 0x70, 0x47, 0xF0, 0xB5, + 0x58, 0x4E, 0x00, 0x23, 0x15, 0x46, 0x4F, 0xF4, 0x00, 0x4C, 0x77, 0x0C, + 0x12, 0xE0, 0x31, 0xF9, 0x13, 0x40, 0x31, 0xF9, 0x15, 0xE0, 0x74, 0x44, + 0x64, 0x45, 0x02, 0xDB, 0x20, 0xF8, 0x13, 0x70, 0x06, 0xE0, 0xB4, 0x42, + 0x02, 0xDA, 0x20, 0xF8, 0x13, 0x60, 0x01, 0xE0, 0x20, 0xF8, 0x13, 0x40, + 0x5B, 0x1C, 0x6D, 0x1C, 0x93, 0x42, 0xEA, 0xDB, 0xF0, 0xBD, 0x70, 0xB5, + 0x06, 0x46, 0x48, 0x4C, 0x49, 0x4D, 0x08, 0xE0, 0x55, 0xF8, 0x21, 0x10, + 0x30, 0x46, 0x88, 0x47, 0x00, 0x28, 0x06, 0xD0, 0x60, 0x68, 0x40, 0x1C, + 0x60, 0x60, 0x61, 0x68, 0x07, 0x29, 0xF3, 0xDB, 0x01, 0x20, 0x70, 0xBD, + 0x10, 0xB5, 0x04, 0x46, 0x00, 0x79, 0x18, 0xB9, 0x20, 0x7A, 0x08, 0xB9, + 0xA0, 0x79, 0xE8, 0xB1, 0x3A, 0x48, 0x44, 0x21, 0x44, 0x38, 0x02, 0xF0, + 0xA2, 0xFA, 0x38, 0x49, 0x1D, 0xCC, 0x54, 0x39, 0x81, 0xE8, 0x1D, 0x00, + 0x08, 0x46, 0x00, 0xF0, 0x83, 0xF9, 0x34, 0x48, 0x54, 0x38, 0x00, 0xF0, + 0x6D, 0xFB, 0x32, 0x48, 0x54, 0x38, 0x00, 0xF0, 0xE1, 0xF8, 0x31, 0x49, + 0x00, 0x20, 0x48, 0x60, 0x2E, 0x48, 0x54, 0x38, 0xFF, 0xF7, 0xC9, 0xFF, + 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, + 0x29, 0x4E, 0x2A, 0x4C, 0xA6, 0xF1, 0x44, 0x06, 0x05, 0x00, 0x4F, 0xF0, + 0x00, 0x07, 0xA6, 0xF1, 0x10, 0x08, 0x0E, 0xD0, 0x03, 0xF0, 0x3E, 0xFB, + 0xFF, 0xF7, 0x7E, 0xFF, 0x26, 0x48, 0x01, 0x78, 0x00, 0x79, 0x03, 0xF0, + 0x85, 0xFB, 0x86, 0xF8, 0x42, 0x50, 0xA0, 0x68, 0xF0, 0x63, 0x67, 0x60, + 0x15, 0xE0, 0x20, 0x48, 0x61, 0x68, 0x1C, 0x30, 0x50, 0xF8, 0x21, 0x10, + 0x1A, 0x48, 0x54, 0x38, 0x88, 0x47, 0x01, 0x28, 0x13, 0xD1, 0x60, 0x68, + 0x40, 0x1C, 0x60, 0x60, 0x16, 0x48, 0x54, 0x38, 0xFF, 0xF7, 0x99, 0xFF, + 0x01, 0x28, 0x0A, 0xD1, 0x67, 0x60, 0xA0, 0x68, 0xF0, 0x63, 0x12, 0x48, + 0xD8, 0xF8, 0x00, 0x10, 0x44, 0x38, 0x88, 0x47, 0xA0, 0x68, 0x40, 0x1C, + 0xA0, 0x60, 0xBD, 0xE8, 0xF0, 0x81, 0x01, 0x20, 0x70, 0x47, 0x10, 0xB5, + 0x02, 0xF0, 0xE4, 0xFE, 0x08, 0xB1, 0x01, 0x20, 0x10, 0xBD, 0xAF, 0xF2, + 0x87, 0x00, 0x02, 0xF0, 0xFD, 0xFE, 0x00, 0x20, 0x10, 0xBD, 0x05, 0x48, + 0x10, 0xB5, 0x00, 0x68, 0x90, 0xF8, 0x30, 0x00, 0x40, 0x07, 0x01, 0xD5, + 0x02, 0xF0, 0xDA, 0xFE, 0x01, 0x20, 0x10, 0xBD, 0x50, 0x24, 0x10, 0x00, + 0xD8, 0x51, 0x10, 0x00, 0x34, 0x24, 0x10, 0x00, 0x00, 0x80, 0xFF, 0xFF, + 0x24, 0x1E, 0x01, 0x00, 0xC8, 0x52, 0x10, 0x00, 0x5E, 0x48, 0x5F, 0x49, + 0x00, 0x68, 0x90, 0xF8, 0x11, 0x01, 0x00, 0xF0, 0x03, 0x00, 0x48, 0x70, + 0x70, 0x47, 0x5A, 0x48, 0x01, 0x68, 0x08, 0x79, 0x4A, 0x79, 0x4B, 0x7A, + 0x40, 0xEA, 0x02, 0x20, 0x8A, 0x79, 0x40, 0xEA, 0x02, 0x40, 0xCA, 0x79, + 0x40, 0xEA, 0x02, 0x60, 0x0A, 0x7A, 0x42, 0xEA, 0x03, 0x22, 0x8B, 0x7A, + 0xC9, 0x7A, 0x42, 0xEA, 0x03, 0x42, 0x42, 0xEA, 0x01, 0x61, 0x70, 0x47, + 0x10, 0xB5, 0x04, 0x46, 0x40, 0x21, 0x02, 0xF0, 0x02, 0xFA, 0x4E, 0x48, + 0x01, 0xC4, 0x4E, 0x48, 0x01, 0xC4, 0x01, 0x20, 0x20, 0x60, 0xFF, 0xF7, + 0xDC, 0xFF, 0xC4, 0xE9, 0x02, 0x01, 0x00, 0x20, 0x20, 0x74, 0x46, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x7D, 0x17, 0x61, 0x74, 0x90, 0xF8, 0x80, 0x17, + 0xA1, 0x74, 0x90, 0xF8, 0x81, 0x17, 0xE1, 0x74, 0x90, 0xF8, 0x82, 0x17, + 0x21, 0x75, 0x90, 0xF8, 0x16, 0x11, 0x61, 0x75, 0x90, 0xF8, 0x84, 0x17, + 0xA1, 0x75, 0x90, 0xF8, 0x17, 0x11, 0x01, 0xF0, 0x0F, 0x01, 0x21, 0x76, + 0x90, 0xF8, 0x19, 0x11, 0x01, 0xF0, 0x3F, 0x01, 0x61, 0x76, 0x90, 0xF8, + 0x18, 0x11, 0xC1, 0xF3, 0x01, 0x11, 0xA1, 0x76, 0x90, 0xF8, 0x18, 0x11, + 0x89, 0x09, 0xE1, 0x76, 0x90, 0xF8, 0x18, 0x11, 0x01, 0xF0, 0x07, 0x01, + 0x21, 0x77, 0x90, 0xF8, 0x19, 0x11, 0x89, 0x09, 0x61, 0x77, 0x32, 0x49, + 0x09, 0x68, 0x49, 0x78, 0xE1, 0x77, 0x90, 0xF8, 0x7F, 0x17, 0xA1, 0x77, + 0x4F, 0xF4, 0x00, 0x61, 0x61, 0x85, 0x90, 0xF8, 0x7E, 0x17, 0xE0, 0x6A, + 0x61, 0xF3, 0x82, 0x00, 0x20, 0xF0, 0x18, 0x00, 0x40, 0xF0, 0x02, 0x00, + 0xE0, 0x62, 0x10, 0xBD, 0x10, 0xB5, 0x80, 0x79, 0x00, 0x28, 0x0B, 0xD0, + 0x22, 0x4C, 0x00, 0x20, 0x20, 0x70, 0x25, 0x48, 0xFF, 0xF7, 0xA0, 0xFF, + 0x23, 0x48, 0x08, 0x21, 0x10, 0x30, 0x04, 0xF0, 0x5B, 0xFA, 0xA0, 0x70, + 0x10, 0xBD, 0x10, 0xB5, 0x1A, 0x4A, 0x80, 0x79, 0x12, 0x68, 0x01, 0x28, + 0x02, 0xF5, 0x89, 0x72, 0x09, 0xD1, 0x18, 0x4B, 0x19, 0x4C, 0x18, 0x78, + 0x9B, 0x78, 0x43, 0x43, 0x04, 0xEB, 0x43, 0x03, 0x4B, 0x60, 0x10, 0x5C, + 0xC8, 0x76, 0x10, 0xBD, 0x10, 0xB5, 0x81, 0x79, 0x39, 0xB1, 0x15, 0x49, + 0xFF, 0xF7, 0xE7, 0xFF, 0x13, 0x48, 0x02, 0xF0, 0xF5, 0xFC, 0x00, 0x20, + 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0x0C, 0x4A, 0x10, 0xB5, 0x11, 0x78, + 0x53, 0x78, 0x99, 0x42, 0x0B, 0xD2, 0x49, 0x1C, 0x11, 0x70, 0x0C, 0x49, + 0xFF, 0xF7, 0xD5, 0xFF, 0x0A, 0x48, 0xC1, 0x7E, 0x40, 0x68, 0x02, 0xF0, + 0xAB, 0xFC, 0x00, 0x20, 0x10, 0xBD, 0x08, 0x49, 0x04, 0x48, 0x48, 0x60, + 0x01, 0x20, 0x10, 0xBD, 0x50, 0x24, 0x10, 0x00, 0x40, 0x24, 0x10, 0x00, + 0x21, 0xD6, 0x00, 0x00, 0x38, 0xA8, 0x01, 0x20, 0x54, 0x24, 0x10, 0x00, + 0xF8, 0x51, 0x10, 0x00, 0x94, 0x51, 0x10, 0x00, 0xF9, 0x48, 0x01, 0x68, + 0x08, 0x79, 0x4A, 0x79, 0x4B, 0x7A, 0x40, 0xEA, 0x02, 0x20, 0x8A, 0x79, + 0x40, 0xEA, 0x02, 0x40, 0xCA, 0x79, 0x40, 0xEA, 0x02, 0x60, 0x0A, 0x7A, + 0x42, 0xEA, 0x03, 0x22, 0x8B, 0x7A, 0xC9, 0x7A, 0x42, 0xEA, 0x03, 0x42, + 0x42, 0xEA, 0x01, 0x61, 0x70, 0x47, 0xEE, 0x48, 0x00, 0x68, 0x01, 0x7B, + 0x42, 0x7B, 0x41, 0xEA, 0x02, 0x21, 0x82, 0x7B, 0xC0, 0x7B, 0x41, 0xEA, + 0x02, 0x41, 0x41, 0xEA, 0x00, 0x60, 0x70, 0x47, 0x3E, 0xB5, 0xFF, 0xF7, + 0xF0, 0xFF, 0x02, 0x90, 0xFF, 0xF7, 0xD6, 0xFF, 0xCD, 0xE9, 0x00, 0x01, + 0x04, 0x21, 0x02, 0xA8, 0x04, 0xF0, 0xE4, 0xF9, 0xE2, 0x4C, 0x08, 0x21, + 0xA0, 0x70, 0x68, 0x46, 0x04, 0xF0, 0xDE, 0xF9, 0xE0, 0x70, 0x01, 0x20, + 0x60, 0x71, 0xDD, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x10, 0x11, 0xC9, 0x07, + 0x03, 0xD1, 0xDC, 0x49, 0x90, 0xF8, 0x81, 0x07, 0x88, 0x70, 0x3E, 0xBD, + 0xD8, 0x49, 0x00, 0x20, 0x48, 0x70, 0x70, 0x47, 0x10, 0xB5, 0x04, 0x46, + 0x40, 0x21, 0x02, 0xF0, 0x0E, 0xF9, 0xD6, 0x48, 0x20, 0x60, 0xD6, 0x48, + 0x60, 0x60, 0xFF, 0xF7, 0xC4, 0xFF, 0x44, 0xF8, 0x08, 0x0F, 0xFF, 0xF7, + 0xA9, 0xFF, 0xC4, 0xE9, 0x02, 0x01, 0xCD, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0x7D, 0x17, 0x61, 0x74, 0x90, 0xF8, 0x80, 0x17, 0xA1, 0x74, 0xCB, 0x49, + 0x89, 0x78, 0xE1, 0x74, 0x90, 0xF8, 0x82, 0x17, 0x21, 0x75, 0x90, 0xF8, + 0x83, 0x17, 0x61, 0x75, 0x90, 0xF8, 0x84, 0x17, 0xA1, 0x75, 0x90, 0xF8, + 0x85, 0x17, 0x01, 0xF0, 0x0F, 0x01, 0x21, 0x76, 0x90, 0xF8, 0x87, 0x17, + 0x01, 0xF0, 0x3F, 0x01, 0x61, 0x76, 0x90, 0xF8, 0x86, 0x17, 0xC1, 0xF3, + 0x01, 0x11, 0xA1, 0x76, 0x90, 0xF8, 0x86, 0x17, 0x89, 0x09, 0xE1, 0x76, + 0x90, 0xF8, 0x86, 0x17, 0x01, 0xF0, 0x07, 0x01, 0x21, 0x77, 0x90, 0xF8, + 0x87, 0x17, 0x89, 0x09, 0x61, 0x77, 0xBA, 0x49, 0x09, 0x68, 0x49, 0x78, + 0xE1, 0x77, 0x90, 0xF8, 0x7F, 0x17, 0xA1, 0x77, 0xB0, 0xF8, 0x2C, 0x17, + 0x61, 0x85, 0x90, 0xF8, 0x7E, 0x27, 0x54, 0xF8, 0x2C, 0x1F, 0x62, 0xF3, + 0x82, 0x01, 0x44, 0xF8, 0x34, 0x19, 0x90, 0xF8, 0x7E, 0x27, 0x52, 0x07, + 0x02, 0xD5, 0x41, 0xF0, 0x18, 0x01, 0x61, 0x63, 0x00, 0x21, 0x21, 0x76, + 0x90, 0xF8, 0x88, 0x07, 0x00, 0xF0, 0x03, 0x01, 0xA6, 0x48, 0x01, 0x70, + 0x01, 0x21, 0x01, 0x71, 0x10, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x05, 0x46, + 0x0C, 0x46, 0x08, 0x46, 0xFF, 0xF7, 0x94, 0xFF, 0x28, 0x79, 0x00, 0x23, + 0x9E, 0x4A, 0x9F, 0x49, 0x08, 0x28, 0x72, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, + 0x71, 0x04, 0x0E, 0x15, 0x55, 0x55, 0x15, 0x86, 0xA8, 0x7B, 0x01, 0x28, + 0x69, 0xD1, 0x10, 0x68, 0x90, 0xF8, 0x88, 0x07, 0xC0, 0xF3, 0x81, 0x00, + 0x08, 0x70, 0x62, 0xE0, 0x84, 0xF8, 0x20, 0x30, 0xA8, 0x7B, 0x01, 0x28, + 0x5D, 0xD1, 0x0B, 0x70, 0x5B, 0xE0, 0x10, 0x68, 0x90, 0xF8, 0x89, 0x67, + 0x66, 0x77, 0x90, 0xF8, 0x8A, 0x67, 0xA6, 0x77, 0x90, 0xF8, 0x8B, 0x67, + 0x06, 0xF0, 0x0F, 0x06, 0x84, 0xF8, 0x20, 0x60, 0x90, 0xF8, 0x8D, 0x67, + 0x06, 0xF0, 0x3F, 0x06, 0x84, 0xF8, 0x21, 0x60, 0x90, 0xF8, 0x8C, 0x67, + 0xC6, 0xF3, 0x01, 0x16, 0x84, 0xF8, 0x22, 0x60, 0x90, 0xF8, 0x8C, 0x67, + 0xB6, 0x09, 0x84, 0xF8, 0x23, 0x60, 0x90, 0xF8, 0x8C, 0x67, 0x06, 0xF0, + 0x07, 0x06, 0x84, 0xF8, 0x24, 0x60, 0x90, 0xF8, 0x8D, 0x67, 0xB6, 0x09, + 0x84, 0xF8, 0x25, 0x60, 0x81, 0x4E, 0x36, 0x68, 0xF6, 0x78, 0x84, 0xF8, + 0x27, 0x60, 0x0B, 0x70, 0x66, 0x6B, 0x26, 0xF0, 0x18, 0x06, 0x66, 0x63, + 0x2F, 0x79, 0x06, 0x2F, 0x07, 0xD1, 0x90, 0xF8, 0x81, 0x07, 0xE0, 0x76, + 0x84, 0xF8, 0x20, 0x30, 0x26, 0xF0, 0x04, 0x00, 0x60, 0x63, 0x0B, 0x71, + 0x1B, 0xE0, 0x10, 0x68, 0x90, 0xF8, 0x81, 0x67, 0xE6, 0x76, 0x84, 0xF8, + 0x20, 0x30, 0x66, 0x6B, 0x0B, 0x70, 0x26, 0xF0, 0x14, 0x06, 0x66, 0x63, + 0x0B, 0x71, 0x2B, 0x79, 0x05, 0x2B, 0x0C, 0xD1, 0x90, 0xF8, 0x20, 0x37, + 0x23, 0x77, 0x90, 0xF8, 0x22, 0x37, 0x03, 0xF0, 0x0F, 0x03, 0x84, 0xF8, + 0x26, 0x30, 0x90, 0xF8, 0x23, 0x07, 0x00, 0x01, 0x60, 0x86, 0x01, 0x20, + 0x48, 0x71, 0x28, 0x79, 0x04, 0x28, 0x0D, 0xD0, 0x05, 0x28, 0x0B, 0xD0, + 0x10, 0x68, 0x94, 0xF8, 0x20, 0x20, 0x90, 0xF8, 0x85, 0x07, 0x00, 0xF0, + 0x0F, 0x00, 0x40, 0x1C, 0x52, 0x1C, 0xB0, 0xFB, 0xF2, 0xF0, 0x48, 0x71, + 0xBD, 0xE8, 0xF0, 0x81, 0x4F, 0xF6, 0xFF, 0x70, 0xA0, 0x60, 0x10, 0x68, + 0x90, 0xF8, 0x16, 0x61, 0x66, 0x77, 0x2E, 0x7B, 0xE6, 0x76, 0x90, 0xF8, + 0x19, 0x61, 0x06, 0xF0, 0x3F, 0x06, 0x84, 0xF8, 0x21, 0x60, 0x90, 0xF8, + 0x18, 0x61, 0xC6, 0xF3, 0x01, 0x16, 0x84, 0xF8, 0x22, 0x60, 0x90, 0xF8, + 0x18, 0x61, 0xB6, 0x09, 0x84, 0xF8, 0x23, 0x60, 0x90, 0xF8, 0x18, 0x01, + 0x00, 0xF0, 0x07, 0x00, 0x84, 0xF8, 0x24, 0x00, 0x6B, 0xE7, 0xF8, 0xB5, + 0x4D, 0x4F, 0x02, 0x26, 0x03, 0x28, 0x39, 0x78, 0x25, 0xD0, 0x01, 0x22, + 0x04, 0x28, 0x05, 0xD0, 0x05, 0x28, 0x03, 0xD0, 0x06, 0x28, 0x03, 0xD0, + 0x01, 0x29, 0x1E, 0xD0, 0x3A, 0x70, 0x00, 0xE0, 0x3E, 0x70, 0xFF, 0xF7, + 0xA0, 0xFE, 0x84, 0x46, 0xFF, 0xF7, 0x86, 0xFE, 0x0D, 0x46, 0x04, 0x46, + 0x39, 0x78, 0x00, 0x20, 0x02, 0x29, 0x11, 0xD0, 0x39, 0x49, 0x40, 0x4F, + 0x09, 0x68, 0x91, 0xF8, 0x7E, 0x17, 0x49, 0x07, 0x10, 0xD5, 0x60, 0x46, + 0xF3, 0xF7, 0x16, 0xFF, 0x01, 0x46, 0x22, 0x46, 0x2B, 0x46, 0x00, 0x96, + 0x0C, 0xE0, 0x02, 0x29, 0xE2, 0xD1, 0xF8, 0xBD, 0x00, 0x90, 0x22, 0x46, + 0x2B, 0x46, 0x61, 0x46, 0x36, 0x48, 0x04, 0xE0, 0x22, 0x46, 0x2B, 0x46, + 0x61, 0x46, 0x00, 0x90, 0x38, 0x46, 0x02, 0xF0, 0x37, 0xF9, 0xF8, 0xBD, + 0x10, 0xB5, 0x04, 0x46, 0x00, 0x79, 0x58, 0xB1, 0x30, 0x49, 0x20, 0x46, + 0xFF, 0xF7, 0x05, 0xFF, 0x20, 0x79, 0xFF, 0xF7, 0xB8, 0xFF, 0x2D, 0x48, + 0x02, 0xF0, 0x1E, 0xFB, 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, + 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0x20, 0x4E, 0x00, 0x24, 0x22, 0x4F, + 0x30, 0x78, 0x41, 0x1C, 0xCD, 0xB2, 0x71, 0x78, 0x49, 0x1C, 0xCB, 0xB2, + 0x31, 0x46, 0x73, 0x70, 0xCA, 0x78, 0x89, 0x78, 0x83, 0x42, 0x07, 0xD8, + 0x4A, 0x43, 0x5A, 0x43, 0x07, 0xEB, 0x42, 0x00, 0x00, 0x21, 0x02, 0xF0, + 0xDD, 0xFA, 0x24, 0xE0, 0x30, 0x79, 0x1D, 0x4C, 0x01, 0x28, 0x10, 0xD0, + 0x27, 0x60, 0x84, 0xF8, 0x40, 0x50, 0x02, 0xF0, 0x7B, 0xFE, 0x84, 0xF8, + 0x41, 0x00, 0x73, 0x79, 0x01, 0x2B, 0x15, 0xD0, 0xB0, 0x78, 0xF1, 0x78, + 0x48, 0x43, 0x68, 0x43, 0x85, 0xB2, 0x00, 0x20, 0x0C, 0xE0, 0x0D, 0x48, + 0xFF, 0xF7, 0xCE, 0xFF, 0x20, 0x60, 0xEA, 0xE7, 0x21, 0x68, 0x31, 0xF8, + 0x10, 0x20, 0x5A, 0x43, 0x21, 0xF8, 0x10, 0x20, 0x40, 0x1C, 0x80, 0xB2, + 0xA8, 0x42, 0xF5, 0xD3, 0x01, 0x24, 0x20, 0x46, 0x4C, 0xE7, 0x00, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x43, 0x24, 0x10, 0x00, 0x6C, 0x24, 0x10, 0x00, + 0x21, 0xD6, 0x00, 0x00, 0x38, 0xA9, 0x01, 0x20, 0x54, 0x24, 0x10, 0x00, + 0x35, 0x24, 0x10, 0x00, 0x40, 0x10, 0x10, 0x00, 0x80, 0x13, 0x10, 0x00, + 0x38, 0x52, 0x10, 0x00, 0x94, 0x51, 0x10, 0x00, 0xFB, 0x49, 0x01, 0x20, + 0xC8, 0x70, 0x70, 0x47, 0xFA, 0x48, 0x01, 0x68, 0x08, 0x79, 0x4A, 0x79, + 0x4B, 0x7A, 0x40, 0xEA, 0x02, 0x20, 0x8A, 0x79, 0x40, 0xEA, 0x02, 0x40, + 0xCA, 0x79, 0x40, 0xEA, 0x02, 0x60, 0x0A, 0x7A, 0x42, 0xEA, 0x03, 0x22, + 0x8B, 0x7A, 0xC9, 0x7A, 0x42, 0xEA, 0x03, 0x42, 0x42, 0xEA, 0x01, 0x61, + 0x70, 0x47, 0xEF, 0x48, 0x00, 0x68, 0x01, 0x7B, 0x42, 0x7B, 0x41, 0xEA, + 0x02, 0x21, 0x82, 0x7B, 0xC0, 0x7B, 0x41, 0xEA, 0x02, 0x41, 0x41, 0xEA, + 0x00, 0x60, 0x70, 0x47, 0x3E, 0xB5, 0x00, 0x7A, 0x00, 0x28, 0x11, 0xD0, + 0xFF, 0xF7, 0xED, 0xFF, 0x02, 0x90, 0xFF, 0xF7, 0xD3, 0xFF, 0xCD, 0xE9, + 0x00, 0x01, 0x04, 0x21, 0x02, 0xA8, 0x03, 0xF0, 0xD3, 0xFF, 0xE0, 0x4C, + 0x08, 0x21, 0x20, 0x70, 0x68, 0x46, 0x03, 0xF0, 0xCD, 0xFF, 0x60, 0x70, + 0x3E, 0xBD, 0x10, 0xB5, 0x04, 0x46, 0x50, 0x21, 0x01, 0xF0, 0x0D, 0xFF, + 0xDB, 0x48, 0x20, 0x60, 0xDB, 0x48, 0x60, 0x60, 0xFF, 0xF7, 0xD1, 0xFF, + 0xA0, 0x60, 0x14, 0x34, 0xFF, 0xF7, 0xB6, 0xFF, 0x44, 0xE9, 0x01, 0x01, + 0xD4, 0x48, 0x00, 0x22, 0x22, 0x71, 0x00, 0x68, 0xB0, 0xF8, 0xA2, 0x17, + 0xE1, 0x80, 0xB0, 0xF8, 0xA4, 0x17, 0x21, 0x81, 0xB0, 0xF8, 0xA6, 0x17, + 0x61, 0x81, 0x90, 0xF8, 0xAB, 0x17, 0x21, 0x73, 0x90, 0xF8, 0xAC, 0x17, + 0x01, 0xF0, 0x0F, 0x01, 0xA1, 0x73, 0x90, 0xF8, 0xAE, 0x17, 0x01, 0xF0, + 0x3F, 0x01, 0xE1, 0x73, 0x90, 0xF8, 0xAD, 0x17, 0xC1, 0xF3, 0x01, 0x11, + 0x21, 0x74, 0x90, 0xF8, 0xAD, 0x17, 0x89, 0x09, 0x61, 0x74, 0x90, 0xF8, + 0xAD, 0x17, 0x01, 0xF0, 0x07, 0x01, 0xA1, 0x74, 0x90, 0xF8, 0xAE, 0x17, + 0x89, 0x09, 0xE1, 0x74, 0x90, 0xF8, 0xB0, 0x17, 0x01, 0xF0, 0x3F, 0x01, + 0x21, 0x75, 0x90, 0xF8, 0xAF, 0x17, 0xC1, 0xF3, 0x01, 0x11, 0x61, 0x75, + 0x90, 0xF8, 0xAF, 0x17, 0x89, 0x09, 0xA1, 0x75, 0x90, 0xF8, 0xAF, 0x17, + 0x01, 0xF0, 0x07, 0x01, 0xE1, 0x75, 0x90, 0xF8, 0xB0, 0x17, 0x89, 0x09, + 0x21, 0x76, 0x90, 0xF8, 0xA8, 0x17, 0x61, 0x76, 0xB4, 0x49, 0x09, 0x68, + 0x4B, 0x7C, 0xA3, 0x76, 0x8B, 0x7C, 0xE3, 0x76, 0xCB, 0x7C, 0x23, 0x77, + 0x0B, 0x7D, 0x63, 0x77, 0x49, 0x7D, 0xA1, 0x77, 0x90, 0xF8, 0xA9, 0x17, + 0xE1, 0x77, 0x90, 0xF8, 0xAA, 0x17, 0x84, 0xF8, 0x20, 0x10, 0xB0, 0xF8, + 0x54, 0x17, 0xE1, 0x85, 0x90, 0xF8, 0xA1, 0x17, 0xCB, 0x09, 0x21, 0x6B, + 0x63, 0xF3, 0x10, 0x41, 0x21, 0x63, 0x90, 0xF8, 0xA1, 0x37, 0x9B, 0x09, + 0x63, 0xF3, 0x51, 0x41, 0x21, 0x63, 0x90, 0xF8, 0xA1, 0x07, 0x60, 0xF3, + 0x92, 0x41, 0x9D, 0x48, 0x21, 0x63, 0x01, 0x21, 0x81, 0x70, 0x02, 0x71, + 0x10, 0xBD, 0x9B, 0x49, 0x10, 0xB5, 0x09, 0x68, 0x91, 0xF8, 0x78, 0x10, + 0xC9, 0x07, 0x10, 0xD0, 0x01, 0x7A, 0x71, 0xB1, 0x03, 0x29, 0x0C, 0xD0, + 0x04, 0x29, 0x0A, 0xD0, 0x05, 0x29, 0x08, 0xD0, 0x97, 0x49, 0x00, 0xF0, + 0xD5, 0xF8, 0x96, 0x49, 0x04, 0x20, 0x02, 0xF0, 0xA1, 0xFB, 0x00, 0x20, + 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x92, 0x4C, 0x21, 0x78, + 0x02, 0x29, 0x02, 0xD1, 0x00, 0x7A, 0x05, 0x28, 0x10, 0xD1, 0x90, 0x48, + 0x02, 0xF0, 0x02, 0xF8, 0x8E, 0x48, 0x38, 0x30, 0x02, 0xF0, 0x3E, 0xF8, + 0x8C, 0x48, 0x20, 0x30, 0x01, 0xF0, 0xF6, 0xFF, 0x8A, 0x48, 0x5A, 0x30, + 0x02, 0xF0, 0x32, 0xF8, 0x02, 0x20, 0x20, 0x70, 0x10, 0xBD, 0x10, 0xB5, + 0x04, 0x46, 0x00, 0x7A, 0x80, 0xB1, 0x20, 0x46, 0xFF, 0xF7, 0xDF, 0xFF, + 0x81, 0x49, 0x20, 0x46, 0x00, 0xF0, 0xD0, 0xF8, 0x7A, 0x48, 0x7F, 0x49, + 0x80, 0x78, 0x01, 0x28, 0x06, 0xD0, 0x00, 0x20, 0x02, 0xF0, 0x70, 0xFB, + 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0x02, 0x20, 0xF7, 0xE7, + 0x73, 0x48, 0x10, 0xB5, 0x00, 0x78, 0x75, 0x49, 0x42, 0x00, 0x01, 0xEB, + 0x40, 0x01, 0x73, 0x48, 0x3C, 0x38, 0x01, 0xF0, 0x95, 0xFD, 0x71, 0x48, + 0x75, 0x49, 0x3C, 0x38, 0x08, 0x62, 0x01, 0x20, 0x10, 0xBD, 0x70, 0x47, + 0x70, 0xB5, 0x6A, 0x4C, 0x05, 0x46, 0x22, 0x46, 0x6B, 0x49, 0xA0, 0x78, + 0x12, 0x78, 0x48, 0xB1, 0xA1, 0xF1, 0xB8, 0x00, 0xFF, 0xF7, 0x43, 0xFB, + 0x28, 0x7A, 0x02, 0x28, 0x0B, 0xD0, 0x03, 0x28, 0x09, 0xD0, 0x0E, 0xE0, + 0x50, 0x00, 0x01, 0xEB, 0x42, 0x01, 0x02, 0x46, 0x62, 0x48, 0xB8, 0x38, + 0x01, 0xF0, 0x74, 0xFD, 0xF0, 0xE7, 0x60, 0x48, 0xE2, 0x78, 0x21, 0x78, + 0xB8, 0x38, 0x00, 0xF0, 0x58, 0xF8, 0x20, 0x79, 0x61, 0x4D, 0x01, 0x28, + 0x04, 0xD0, 0x5B, 0x48, 0xB8, 0x38, 0xA8, 0x61, 0x01, 0x20, 0x70, 0xBD, + 0x58, 0x48, 0x00, 0x22, 0x21, 0x78, 0xB8, 0x38, 0xFF, 0xF7, 0xCF, 0xFF, + 0xF5, 0xE7, 0x10, 0xB5, 0x01, 0x7A, 0x79, 0xB1, 0x03, 0x29, 0x0D, 0xD0, + 0x04, 0x29, 0x0B, 0xD0, 0xFF, 0xF7, 0x81, 0xFF, 0x4D, 0x48, 0x52, 0x49, + 0x80, 0x78, 0x01, 0x28, 0x06, 0xD0, 0x01, 0x20, 0x02, 0xF0, 0x16, 0xFB, + 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, 0x03, 0x20, 0xF7, 0xE7, + 0x70, 0xB5, 0x46, 0x4C, 0x05, 0x46, 0x22, 0x46, 0x47, 0x49, 0xA0, 0x78, + 0x52, 0x78, 0x48, 0xB1, 0xA1, 0xF1, 0x7C, 0x00, 0xFF, 0xF7, 0xFB, 0xFA, + 0x28, 0x7A, 0x02, 0x28, 0x0B, 0xD0, 0x03, 0x28, 0x09, 0xD0, 0x0E, 0xE0, + 0x50, 0x00, 0x01, 0xEB, 0x42, 0x01, 0x02, 0x46, 0x3E, 0x48, 0x7C, 0x38, + 0x01, 0xF0, 0x2C, 0xFD, 0xF0, 0xE7, 0x3C, 0x48, 0xE2, 0x78, 0x61, 0x78, + 0x7C, 0x38, 0x00, 0xF0, 0x10, 0xF8, 0x20, 0x79, 0x3D, 0x4D, 0x01, 0x28, + 0x04, 0xD0, 0x37, 0x48, 0x7C, 0x38, 0xE8, 0x61, 0x01, 0x20, 0x70, 0xBD, + 0x34, 0x48, 0x01, 0x22, 0x61, 0x78, 0x7C, 0x38, 0xFF, 0xF7, 0x87, 0xFF, + 0xF5, 0xE7, 0x10, 0xB5, 0x01, 0x2A, 0x0A, 0xD0, 0x00, 0x23, 0x06, 0xE0, + 0x30, 0xF8, 0x13, 0x40, 0x54, 0x43, 0x20, 0xF8, 0x13, 0x40, 0x5B, 0x1C, + 0x1B, 0xB2, 0x8B, 0x42, 0xF6, 0xDB, 0x10, 0xBD, 0x10, 0xB5, 0x0C, 0x46, + 0x08, 0x46, 0xFF, 0xF7, 0x90, 0xFE, 0x25, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0xB2, 0x17, 0x04, 0xF8, 0x20, 0x1F, 0x90, 0xF8, 0xB3, 0x17, 0x01, 0xF0, + 0x0F, 0x01, 0xA1, 0x70, 0x90, 0xF8, 0xB5, 0x17, 0x01, 0xF0, 0x3F, 0x01, + 0xE1, 0x70, 0x90, 0xF8, 0xB4, 0x17, 0xC1, 0xF3, 0x01, 0x11, 0x21, 0x71, + 0x90, 0xF8, 0xB4, 0x17, 0x89, 0x09, 0x61, 0x71, 0x90, 0xF8, 0xB4, 0x17, + 0x01, 0xF0, 0x07, 0x01, 0xA1, 0x71, 0x90, 0xF8, 0xB5, 0x07, 0x80, 0x09, + 0xE0, 0x71, 0x10, 0xBD, 0x70, 0xB5, 0x05, 0x46, 0x0C, 0x46, 0x08, 0x46, + 0xFF, 0xF7, 0x67, 0xFE, 0x2A, 0x7A, 0x10, 0x48, 0x0E, 0x49, 0x06, 0x2A, + 0x18, 0xD2, 0xDF, 0xE8, 0x02, 0xF0, 0x17, 0x17, 0x03, 0x03, 0x2B, 0x33, + 0x00, 0x68, 0x90, 0xF8, 0xB1, 0x27, 0x02, 0xF0, 0x0F, 0x02, 0x84, 0xF8, + 0x22, 0x20, 0x90, 0xF8, 0xAC, 0x27, 0x90, 0xF8, 0xB1, 0x07, 0x02, 0xF0, + 0x0F, 0x02, 0x00, 0xF0, 0x0F, 0x00, 0x40, 0x1C, 0x52, 0x1C, 0xB2, 0xFB, + 0xF0, 0xF0, 0xC8, 0x70, 0x70, 0xBD, 0x00, 0x00, 0x49, 0x24, 0x10, 0x00, + 0x50, 0x24, 0x10, 0x00, 0x21, 0xD6, 0x00, 0x00, 0xF0, 0xC7, 0x01, 0x20, + 0x54, 0x24, 0x10, 0x00, 0x78, 0x52, 0x10, 0x00, 0x34, 0x24, 0x10, 0x00, + 0x34, 0x17, 0x10, 0x00, 0x94, 0x51, 0x10, 0x00, 0x00, 0x68, 0x90, 0xF8, + 0xB1, 0x07, 0x00, 0xF0, 0x0F, 0x00, 0x84, 0xF8, 0x22, 0x00, 0xE3, 0xE7, + 0x00, 0x20, 0x84, 0xF8, 0x22, 0x00, 0x88, 0x70, 0x08, 0x71, 0xDD, 0xE7, + 0x08, 0xB5, 0x03, 0xF0, 0x55, 0xF9, 0x00, 0x20, 0x00, 0x90, 0x00, 0xBF, + 0x40, 0x1C, 0x00, 0x90, 0x14, 0x28, 0xFA, 0xDB, 0xBD, 0xE8, 0x08, 0x40, + 0xFE, 0xF7, 0x57, 0xBB, 0x01, 0xF1, 0xB4, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, + 0xED, 0xFF, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, 0xE9, 0xFF, 0x10, 0xBD, + 0x10, 0xB5, 0xFF, 0xF7, 0xE5, 0xFF, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, + 0xE1, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0xFD, 0xF7, 0xE4, 0xBD, 0x10, 0xB5, + 0xFF, 0xF7, 0xDA, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0x02, 0xF0, 0xE4, 0xBB, + 0x10, 0xB5, 0xFF, 0xF7, 0xD3, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0x03, 0xF0, + 0x37, 0xBC, 0x10, 0xB5, 0xFF, 0xF7, 0xCC, 0xFF, 0xBD, 0xE8, 0x10, 0x40, + 0x03, 0xF0, 0x6A, 0xBC, 0x10, 0xB5, 0xFF, 0xF7, 0xC5, 0xFF, 0xBD, 0xE8, + 0x10, 0x40, 0x03, 0xF0, 0xB5, 0xBC, 0x10, 0xB5, 0xFF, 0xF7, 0xBE, 0xFF, + 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, 0xBA, 0xFF, 0xBD, 0xE8, 0x10, 0x40, + 0x03, 0xF0, 0xD4, 0xBA, 0x10, 0xB5, 0xFF, 0xF7, 0xB3, 0xFF, 0xBD, 0xE8, + 0x10, 0x40, 0x03, 0xF0, 0x1F, 0xBB, 0x10, 0xB5, 0xFF, 0xF7, 0xAC, 0xFF, + 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, 0xA8, 0xFF, 0x10, 0xBD, 0x10, 0xB5, + 0xFF, 0xF7, 0xA4, 0xFF, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, 0xA0, 0xFF, + 0xBD, 0xE8, 0x10, 0x40, 0x03, 0xF0, 0xB2, 0xB9, 0x10, 0xB5, 0xFF, 0xF7, + 0x99, 0xFF, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, 0x95, 0xFF, 0x10, 0xBD, + 0x10, 0xB5, 0xFF, 0xF7, 0x91, 0xFF, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, + 0x8D, 0xFF, 0x10, 0xBD, 0x70, 0x47, 0x70, 0x47, 0x10, 0xB5, 0xFF, 0xF7, + 0x87, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0x03, 0xF0, 0x4D, 0xBD, 0x70, 0x47, + 0x70, 0x47, 0x70, 0x47, 0x70, 0x47, 0x10, 0xB5, 0xFF, 0xF7, 0x7C, 0xFF, + 0xBD, 0xE8, 0x10, 0x40, 0x02, 0xF0, 0x86, 0xBB, 0x70, 0x47, 0x00, 0x00, + 0x70, 0xB5, 0x88, 0xB0, 0x0E, 0x26, 0x01, 0x25, 0x34, 0x46, 0x68, 0x46, + 0x02, 0xF0, 0xF6, 0xFF, 0x00, 0x98, 0xC0, 0x1C, 0x30, 0xD0, 0x00, 0x98, + 0x40, 0x1C, 0x2F, 0xD0, 0x00, 0x98, 0x80, 0x1C, 0x2C, 0xD0, 0x00, 0x98, + 0xC6, 0xB2, 0x01, 0x98, 0xC0, 0x1C, 0x2B, 0xD0, 0x01, 0x98, 0x40, 0x1C, + 0x2A, 0xD0, 0x01, 0x98, 0x80, 0x1C, 0x27, 0xD0, 0x01, 0x98, 0xC5, 0xB2, + 0x02, 0x98, 0xC0, 0x1C, 0x26, 0xD0, 0x02, 0x98, 0x40, 0x1C, 0x25, 0xD0, + 0x02, 0x98, 0x80, 0x1C, 0x22, 0xD0, 0x02, 0x98, 0xC4, 0xB2, 0x31, 0x46, + 0x28, 0x46, 0x02, 0xF0, 0x95, 0xFD, 0x20, 0x46, 0x03, 0xF0, 0xF6, 0xF8, + 0x6B, 0x4A, 0x16, 0x60, 0xC2, 0xE9, 0x01, 0x54, 0x04, 0x98, 0x10, 0x61, + 0x05, 0x98, 0x50, 0x61, 0x06, 0x98, 0x90, 0x61, 0x08, 0xB0, 0x70, 0xBD, + 0x11, 0x20, 0x00, 0xE0, 0x10, 0x20, 0xFD, 0xF7, 0x95, 0xFD, 0xD0, 0xE7, + 0x21, 0x20, 0x00, 0xE0, 0x20, 0x20, 0xFD, 0xF7, 0x8F, 0xFD, 0xD5, 0xE7, + 0x31, 0x20, 0x00, 0xE0, 0x30, 0x20, 0xFD, 0xF7, 0x89, 0xFD, 0xDA, 0xE7, + 0x70, 0xB5, 0x01, 0x24, 0x4B, 0xF2, 0x49, 0x50, 0x03, 0xF0, 0xC8, 0xF8, + 0x03, 0xF0, 0x1E, 0xF9, 0xE2, 0x02, 0x59, 0x49, 0x59, 0x48, 0x01, 0xF0, + 0xF8, 0xFB, 0x22, 0x03, 0x91, 0x01, 0x58, 0x48, 0x01, 0xF0, 0xF3, 0xFB, + 0x03, 0xF0, 0xAC, 0xF9, 0x01, 0x20, 0x03, 0xF0, 0xD1, 0xF9, 0x02, 0xF0, + 0x6B, 0xFF, 0xFF, 0xF7, 0x4D, 0xF9, 0x02, 0xF0, 0x27, 0xFB, 0x46, 0x28, + 0x02, 0xD0, 0x01, 0x20, 0xFD, 0xF7, 0x31, 0xFD, 0xFF, 0xF7, 0x8E, 0xFF, + 0x03, 0xF0, 0xA2, 0xF8, 0x03, 0xF0, 0x60, 0xF8, 0x02, 0x21, 0x01, 0x20, + 0x03, 0xF0, 0xBA, 0xF8, 0x4A, 0x4D, 0x28, 0x68, 0x00, 0x78, 0xA5, 0x28, + 0x02, 0xD0, 0x01, 0x20, 0xFD, 0xF7, 0x3C, 0xFD, 0x40, 0xF2, 0xFF, 0x11, + 0x28, 0x68, 0x03, 0xF0, 0xFF, 0xFC, 0x29, 0x68, 0xD1, 0xF8, 0xFC, 0x27, + 0x82, 0x42, 0x03, 0xD0, 0x02, 0x20, 0xFD, 0xF7, 0x2F, 0xFD, 0x04, 0xE0, + 0xB1, 0xF8, 0x01, 0x00, 0x4F, 0xF0, 0x00, 0x51, 0x48, 0x81, 0x40, 0xF2, + 0xFF, 0x31, 0x3B, 0x48, 0x03, 0xF0, 0xEC, 0xFC, 0x39, 0x49, 0xD1, 0xF8, + 0xFC, 0x1F, 0x81, 0x42, 0x03, 0xD0, 0x00, 0x24, 0x03, 0x20, 0xFD, 0xF7, + 0x1B, 0xFD, 0x03, 0xF0, 0x02, 0xF9, 0x02, 0xF0, 0xF7, 0xFA, 0x40, 0xF2, + 0x72, 0x42, 0x01, 0x46, 0x90, 0x42, 0x02, 0xD0, 0x01, 0x20, 0xFD, 0xF7, + 0x21, 0xFD, 0x03, 0xF0, 0xFB, 0xFB, 0x30, 0x4E, 0x34, 0xB3, 0x30, 0x48, + 0x01, 0x68, 0x91, 0xF8, 0x20, 0x00, 0x55, 0x28, 0x22, 0xD0, 0x70, 0x20, + 0xFD, 0xF7, 0x1A, 0xFD, 0x30, 0x46, 0x03, 0xF0, 0x91, 0xFC, 0x01, 0x20, + 0x03, 0xF0, 0x5E, 0xF8, 0x00, 0x22, 0x11, 0x46, 0x10, 0x46, 0x03, 0xF0, + 0x02, 0xF9, 0x01, 0x22, 0x00, 0x21, 0x10, 0x46, 0x03, 0xF0, 0xFD, 0xF8, + 0x28, 0x68, 0x90, 0xF8, 0xAF, 0x02, 0xC1, 0x07, 0x0F, 0xD0, 0x81, 0x07, + 0x0D, 0xD5, 0xBD, 0xE8, 0x70, 0x40, 0x40, 0x09, 0x01, 0x22, 0x00, 0x21, + 0x03, 0xF0, 0xEF, 0xB8, 0x71, 0x20, 0xDD, 0xE7, 0xD1, 0xF8, 0x21, 0x00, + 0x20, 0xF0, 0x7F, 0x46, 0xDA, 0xE7, 0x70, 0xBD, 0x10, 0xB5, 0xF2, 0xF7, + 0x2D, 0xF8, 0xFF, 0xF7, 0x67, 0xFF, 0xFE, 0xF7, 0x57, 0xF9, 0x13, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x30, 0x01, 0x80, 0x07, 0x02, 0xD5, 0x13, 0x48, + 0xFE, 0xF7, 0x10, 0xFA, 0xFD, 0xF7, 0xFC, 0xFC, 0x01, 0xF0, 0xF0, 0xF9, + 0x4F, 0xF4, 0x58, 0x72, 0x01, 0x46, 0x90, 0x42, 0x02, 0xD0, 0x02, 0x20, + 0xFD, 0xF7, 0xD2, 0xFC, 0x01, 0xF0, 0xE4, 0xF9, 0x00, 0xF0, 0x16, 0xF8, + 0xF2, 0xF7, 0x0C, 0xF8, 0x00, 0xF0, 0x7E, 0xF8, 0x01, 0x20, 0x10, 0xBD, + 0xC8, 0x52, 0x10, 0x00, 0xF0, 0xEF, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x50, 0x24, 0x10, 0x00, 0x00, 0x77, 0x01, 0x00, + 0x54, 0x24, 0x10, 0x00, 0x70, 0x53, 0x10, 0x00, 0x7F, 0xB5, 0x00, 0xF0, + 0x41, 0xF8, 0x1F, 0x4D, 0x00, 0x24, 0x05, 0xEB, 0x44, 0x10, 0x00, 0xF1, + 0x10, 0x02, 0x01, 0x7F, 0x4C, 0xCA, 0xCD, 0xE9, 0x02, 0x61, 0xCD, 0xE9, + 0x00, 0x23, 0x0F, 0xC8, 0x00, 0xF0, 0xDA, 0xF9, 0x10, 0xB1, 0x02, 0x20, + 0xFD, 0xF7, 0xAD, 0xFC, 0x64, 0x1C, 0x06, 0x2C, 0xEB, 0xDB, 0x14, 0x4D, + 0x00, 0x24, 0xC0, 0x35, 0x04, 0xEB, 0x44, 0x00, 0x05, 0xEB, 0x80, 0x01, + 0x55, 0xF8, 0x20, 0x00, 0x8A, 0x68, 0x49, 0x68, 0x00, 0xF0, 0x46, 0xF9, + 0x10, 0xB1, 0x02, 0x20, 0xFD, 0xF7, 0x99, 0xFC, 0x64, 0x1C, 0x05, 0x2C, + 0xEE, 0xDB, 0x0A, 0x4D, 0x00, 0x24, 0xFC, 0x35, 0x05, 0xEB, 0xC4, 0x00, + 0x41, 0x68, 0x55, 0xF8, 0x34, 0x00, 0x00, 0xF0, 0x8F, 0xF8, 0x10, 0xB1, + 0x02, 0x20, 0xFD, 0xF7, 0x88, 0xFC, 0x64, 0x1C, 0x05, 0x2C, 0xF1, 0xDB, + 0x7F, 0xBD, 0x03, 0x20, 0xFD, 0xF7, 0x81, 0xBC, 0x5C, 0x1E, 0x01, 0x00, + 0x1A, 0x49, 0x10, 0xB5, 0x01, 0x20, 0x08, 0x70, 0x19, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x19, 0x49, 0x08, 0x60, 0x19, 0x49, 0x08, 0x60, 0x19, 0x49, + 0x08, 0x60, 0x00, 0xF0, 0x7D, 0xF9, 0x00, 0xF0, 0x0F, 0xF9, 0x00, 0xF0, + 0x67, 0xF8, 0x00, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x12, 0x48, 0x01, 0xF0, + 0x9C, 0xF9, 0x10, 0x49, 0x08, 0x60, 0x0E, 0x49, 0x09, 0x68, 0x88, 0x42, + 0x07, 0xD0, 0x0B, 0x48, 0x00, 0x78, 0x02, 0x28, 0x03, 0xD1, 0xBD, 0xE8, + 0x10, 0x40, 0xF1, 0xF7, 0x47, 0xBF, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, + 0xEA, 0xFF, 0x05, 0x49, 0x02, 0x20, 0x08, 0x70, 0xF1, 0xF7, 0x22, 0xFF, + 0x4F, 0xF0, 0xFF, 0x30, 0x10, 0xBD, 0x05, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x44, 0x25, 0x10, 0x00, 0x48, 0x25, 0x10, 0x00, 0x4C, 0x25, 0x10, 0x00, + 0x50, 0x25, 0x10, 0x00, 0x54, 0x25, 0x10, 0x00, 0x70, 0xB5, 0x0D, 0x46, + 0x04, 0x46, 0xC1, 0x69, 0x80, 0x68, 0xCC, 0x22, 0x01, 0xF0, 0xE1, 0xFA, + 0xE1, 0x69, 0xA0, 0x68, 0x08, 0x44, 0x40, 0x38, 0x4F, 0xF0, 0x80, 0x71, + 0xC1, 0x63, 0x21, 0x6A, 0x81, 0x63, 0x00, 0x21, 0x41, 0x63, 0x01, 0x63, + 0xC1, 0x62, 0x81, 0x62, 0xC0, 0xE9, 0x08, 0x51, 0x4F, 0xF0, 0x0B, 0x31, + 0xC1, 0x61, 0x4F, 0xF0, 0x0A, 0x31, 0x81, 0x61, 0x4F, 0xF0, 0x09, 0x31, + 0x41, 0x61, 0x4F, 0xF0, 0x08, 0x31, 0x01, 0x61, 0x4F, 0xF0, 0x07, 0x31, + 0xC1, 0x60, 0x4F, 0xF0, 0x06, 0x31, 0x81, 0x60, 0x4F, 0xF0, 0x05, 0x31, + 0x41, 0x60, 0x4F, 0xF0, 0x04, 0x31, 0x01, 0x60, 0x70, 0xBD, 0x82, 0x69, + 0x81, 0x68, 0x91, 0x42, 0x02, 0xD8, 0x40, 0x68, 0xFF, 0xF7, 0x83, 0xBF, + 0x70, 0x47, 0x00, 0x00, 0x10, 0xB5, 0x4F, 0xF4, 0xB4, 0x71, 0x4F, 0x48, + 0x01, 0xF0, 0xD3, 0xFA, 0x00, 0x20, 0x10, 0xBD, 0x70, 0xB5, 0x05, 0x46, + 0x00, 0xEB, 0xC5, 0x01, 0x4A, 0x48, 0x00, 0xEB, 0x81, 0x04, 0xF1, 0xF7, + 0x1B, 0xFF, 0x06, 0x46, 0x20, 0x78, 0x28, 0xB1, 0x30, 0x46, 0xF1, 0xF7, + 0x1D, 0xFF, 0x6F, 0xF0, 0x01, 0x00, 0x70, 0xBD, 0x03, 0x20, 0x20, 0x70, + 0x00, 0x20, 0x65, 0x60, 0x20, 0x62, 0x04, 0xF1, 0x1C, 0x00, 0x01, 0xF0, + 0xF1, 0xF8, 0x30, 0x46, 0xF1, 0xF7, 0x0E, 0xFF, 0x00, 0x20, 0x70, 0xBD, + 0x2D, 0xE9, 0xF0, 0x41, 0x0F, 0x46, 0x3B, 0x49, 0x00, 0x25, 0x00, 0xEB, + 0xC0, 0x00, 0x01, 0xEB, 0x80, 0x04, 0x2E, 0x46, 0xF1, 0xF7, 0xF8, 0xFE, + 0x80, 0x46, 0x20, 0x6A, 0x38, 0x43, 0x20, 0x62, 0x04, 0xF1, 0x1C, 0x00, + 0x07, 0x46, 0x01, 0xF0, 0xFE, 0xF8, 0x0C, 0xE0, 0x21, 0x6A, 0x02, 0x6C, + 0x11, 0x42, 0x03, 0xD0, 0x00, 0xF0, 0x26, 0xF9, 0x01, 0x26, 0x00, 0xE0, + 0x6D, 0x1C, 0x29, 0x46, 0x38, 0x46, 0x01, 0xF0, 0xE6, 0xF8, 0x00, 0x28, + 0xF0, 0xD1, 0x01, 0x2E, 0x01, 0xD1, 0xFF, 0xF7, 0x4A, 0xFF, 0x40, 0x46, + 0xF1, 0xF7, 0xE0, 0xFE, 0x00, 0x20, 0xBD, 0xE8, 0xF0, 0x81, 0x70, 0xB5, + 0x0D, 0x46, 0x24, 0x49, 0x00, 0xEB, 0xC0, 0x00, 0x01, 0xEB, 0x80, 0x04, + 0xF1, 0xF7, 0xCC, 0xFE, 0x21, 0x6A, 0xA9, 0x43, 0x21, 0x62, 0xF1, 0xF7, + 0xCF, 0xFE, 0x00, 0x20, 0x70, 0xBD, 0x70, 0xB5, 0x0C, 0x46, 0x1C, 0x49, + 0x00, 0xEB, 0xC0, 0x00, 0x01, 0xEB, 0x80, 0x05, 0x00, 0x20, 0x20, 0x60, + 0xF1, 0xF7, 0xBA, 0xFE, 0x29, 0x6A, 0x21, 0x60, 0xF1, 0xF7, 0xBE, 0xFE, + 0x00, 0x20, 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x0D, 0x46, 0x13, 0x49, + 0x00, 0xEB, 0xC0, 0x00, 0x1E, 0x46, 0x90, 0x46, 0x01, 0xEB, 0x80, 0x04, + 0xF1, 0xF7, 0xA8, 0xFE, 0x07, 0x46, 0x0F, 0x48, 0x00, 0x68, 0x05, 0x64, + 0x21, 0x6A, 0x29, 0x42, 0x03, 0xD1, 0x04, 0xF1, 0x1C, 0x01, 0x00, 0xF0, + 0xED, 0xF8, 0x38, 0x46, 0xF1, 0xF7, 0xA2, 0xFE, 0xF1, 0xF7, 0x98, 0xFE, + 0x21, 0x6A, 0x29, 0x40, 0x31, 0x60, 0xB8, 0xF1, 0x01, 0x0F, 0x02, 0xD1, + 0x21, 0x6A, 0xA9, 0x43, 0x21, 0x62, 0xF1, 0xF7, 0x95, 0xFE, 0x00, 0x20, + 0xB3, 0xE7, 0x00, 0x00, 0xF0, 0x7B, 0x10, 0x00, 0x48, 0x25, 0x10, 0x00, + 0x10, 0xB5, 0x4F, 0xF4, 0xC8, 0x71, 0x32, 0x48, 0x01, 0xF0, 0x2D, 0xFA, + 0x00, 0x20, 0x10, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x88, 0x46, 0x05, 0x46, + 0x2D, 0x49, 0x00, 0xEB, 0x85, 0x00, 0x17, 0x46, 0x01, 0xEB, 0xC0, 0x04, + 0xF1, 0xF7, 0x72, 0xFE, 0x06, 0x46, 0x20, 0x78, 0x30, 0xB1, 0x30, 0x46, + 0xF1, 0xF7, 0x74, 0xFE, 0x6F, 0xF0, 0x01, 0x00, 0xBD, 0xE8, 0xF0, 0x81, + 0x02, 0x20, 0x20, 0x70, 0xE7, 0x61, 0x14, 0x22, 0x41, 0x46, 0x04, 0xF1, + 0x08, 0x00, 0x65, 0x60, 0x01, 0xF0, 0x6A, 0xF9, 0x00, 0x20, 0x20, 0x62, + 0x04, 0xF1, 0x24, 0x00, 0x01, 0xF0, 0x40, 0xF8, 0x30, 0x46, 0xF1, 0xF7, + 0x5D, 0xFE, 0x00, 0x20, 0xE8, 0xE7, 0x70, 0xB5, 0x19, 0x49, 0x00, 0xEB, + 0x80, 0x00, 0x01, 0xEB, 0xC0, 0x04, 0xF1, 0xF7, 0x4B, 0xFE, 0x05, 0x46, + 0xD4, 0xE9, 0x07, 0x01, 0x81, 0x42, 0x05, 0xDB, 0x14, 0x48, 0x04, 0xF1, + 0x24, 0x01, 0x00, 0x68, 0x00, 0xF0, 0x90, 0xF8, 0x20, 0x6A, 0x40, 0x1C, + 0x20, 0x62, 0x28, 0x46, 0xF1, 0xF7, 0x42, 0xFE, 0x00, 0x20, 0x70, 0xBD, + 0x70, 0xB5, 0x0C, 0x49, 0x00, 0xEB, 0x80, 0x00, 0x01, 0xEB, 0xC0, 0x04, + 0xF1, 0xF7, 0x30, 0xFE, 0x05, 0x46, 0x20, 0x6A, 0x08, 0xB1, 0x40, 0x1E, + 0x20, 0x62, 0x04, 0xF1, 0x24, 0x00, 0x01, 0xF0, 0x36, 0xF8, 0x10, 0xB1, + 0x60, 0x6A, 0x00, 0xF0, 0x11, 0xF8, 0x28, 0x46, 0xF1, 0xF7, 0x28, 0xFE, + 0x00, 0x20, 0x70, 0xBD, 0x58, 0x7D, 0x10, 0x00, 0x48, 0x25, 0x10, 0x00, + 0x10, 0xB5, 0x4F, 0xF4, 0x2A, 0x71, 0x40, 0x48, 0x01, 0xF0, 0xC1, 0xF9, + 0x00, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x04, 0x46, 0x90, 0xF8, 0x38, 0x00, + 0x02, 0x28, 0x0B, 0xD0, 0x20, 0x46, 0x01, 0xF0, 0x1A, 0xF8, 0x02, 0x20, + 0x84, 0xF8, 0x38, 0x00, 0x38, 0x49, 0x20, 0x46, 0x00, 0xF0, 0xEE, 0xFF, + 0xFF, 0xF7, 0x6F, 0xFE, 0x00, 0x20, 0x10, 0xBD, 0x2D, 0xE9, 0xF0, 0x5F, + 0x8B, 0x46, 0x06, 0x46, 0x31, 0x49, 0x00, 0xEB, 0x06, 0x10, 0x90, 0x46, + 0xDD, 0xE9, 0x0A, 0xA9, 0x11, 0xF8, 0x20, 0x20, 0x1D, 0x46, 0x1A, 0xB1, + 0x6F, 0xF0, 0x01, 0x00, 0xBD, 0xE8, 0xF0, 0x9F, 0x01, 0xEB, 0x80, 0x04, + 0xF1, 0xF7, 0xE8, 0xFD, 0x07, 0x46, 0x01, 0x20, 0x20, 0x70, 0xC4, 0xE9, + 0x01, 0x65, 0xC4, 0xE9, 0x05, 0x85, 0xC4, 0xE9, 0x07, 0xA9, 0x14, 0x22, + 0x59, 0x46, 0x04, 0xF1, 0x24, 0x00, 0x01, 0xF0, 0xE5, 0xF8, 0x00, 0x20, + 0x84, 0xF8, 0x37, 0x00, 0x84, 0xF8, 0x38, 0x00, 0x61, 0x69, 0xE0, 0x60, + 0x20, 0x61, 0xC4, 0xE9, 0x0F, 0x10, 0x20, 0x46, 0x0C, 0x99, 0xFF, 0xF7, + 0x65, 0xFE, 0xA0, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0xB5, 0xFF, 0x38, 0x46, + 0xF1, 0xF7, 0xCC, 0xFD, 0x00, 0x20, 0xD3, 0xE7, 0x10, 0xB5, 0x04, 0x46, + 0x90, 0xF8, 0x38, 0x00, 0x02, 0x28, 0x09, 0xD0, 0x20, 0x46, 0x00, 0xF0, + 0xCA, 0xFF, 0x02, 0x20, 0x84, 0xF8, 0x38, 0x00, 0x10, 0x49, 0x20, 0x46, + 0x00, 0xF0, 0x9E, 0xFF, 0x00, 0x20, 0x10, 0xBD, 0x70, 0xB5, 0x04, 0x46, + 0x90, 0xF8, 0x38, 0x00, 0x0D, 0x46, 0x02, 0x28, 0x0B, 0xD1, 0x20, 0x46, + 0x00, 0xF0, 0xB7, 0xFF, 0x03, 0x20, 0x84, 0xF8, 0x38, 0x00, 0x29, 0x46, + 0x20, 0x46, 0x00, 0xF0, 0x8B, 0xFF, 0xFF, 0xF7, 0x0C, 0xFE, 0x00, 0x20, + 0x70, 0xBD, 0x04, 0x48, 0x00, 0x68, 0x40, 0x68, 0x70, 0x47, 0x00, 0x00, + 0xE8, 0x7E, 0x10, 0x00, 0x50, 0x25, 0x10, 0x00, 0x48, 0x25, 0x10, 0x00, + 0xF0, 0xB5, 0xD1, 0xE9, 0x00, 0x52, 0x8E, 0x68, 0x11, 0x88, 0x03, 0x68, + 0x99, 0x42, 0x01, 0xDD, 0x31, 0x88, 0x2E, 0xE0, 0x02, 0xEB, 0x45, 0x01, + 0x31, 0xF8, 0x02, 0x4C, 0x9C, 0x42, 0x04, 0xDA, 0x06, 0xEB, 0x45, 0x01, + 0x31, 0xF8, 0x02, 0x1C, 0x23, 0xE0, 0x00, 0x24, 0x21, 0x46, 0x6F, 0x1E, + 0x0C, 0xE0, 0x32, 0xF8, 0x11, 0xC0, 0x9C, 0x45, 0x07, 0xDC, 0x02, 0xEB, + 0x41, 0x0C, 0xBC, 0xF8, 0x02, 0xC0, 0x9C, 0x45, 0x01, 0xDB, 0x0C, 0x46, + 0x29, 0x46, 0x49, 0x1C, 0xB9, 0x42, 0xF0, 0xDB, 0x32, 0xF8, 0x14, 0x10, + 0x02, 0xEB, 0x44, 0x02, 0x5D, 0x1A, 0x06, 0xEB, 0x44, 0x03, 0x52, 0x88, + 0x5F, 0x88, 0x36, 0xF8, 0x14, 0x30, 0xFE, 0x1A, 0x6E, 0x43, 0x51, 0x1A, + 0x96, 0xFB, 0xF1, 0xF1, 0x19, 0x44, 0x01, 0x60, 0xF0, 0xBD, 0x70, 0xB5, + 0x05, 0x46, 0x08, 0x68, 0x0C, 0x46, 0x01, 0x28, 0x02, 0xDD, 0x28, 0x1D, + 0xFF, 0xF7, 0xBC, 0xFF, 0xE0, 0x68, 0x01, 0x28, 0x05, 0xDD, 0x28, 0x46, + 0x04, 0xF1, 0x0C, 0x01, 0xBD, 0xE8, 0x70, 0x40, 0xB2, 0xE7, 0x70, 0xBD, + 0x30, 0xB5, 0x42, 0x78, 0x03, 0x78, 0x0D, 0x8A, 0xD2, 0x1A, 0xC3, 0x78, + 0x80, 0x78, 0x52, 0x1C, 0x1B, 0x1A, 0x5B, 0x1C, 0x05, 0xFB, 0x02, 0xF0, + 0xD4, 0x18, 0x5D, 0x43, 0x40, 0x00, 0x6A, 0x00, 0xB0, 0xFB, 0xF4, 0xF0, + 0xB2, 0xFB, 0xF4, 0xF2, 0x4B, 0x68, 0x90, 0x42, 0x03, 0xD9, 0x18, 0x80, + 0x88, 0x68, 0x02, 0x80, 0x30, 0xBD, 0x1A, 0x80, 0x89, 0x68, 0x08, 0x80, + 0x30, 0xBD, 0x2D, 0xE9, 0xFF, 0x4F, 0x00, 0x26, 0x83, 0xB0, 0x91, 0x46, + 0x35, 0x46, 0x34, 0x46, 0xB4, 0x46, 0x37, 0x46, 0xB0, 0x46, 0x93, 0x78, + 0x92, 0xF8, 0x03, 0xA0, 0x28, 0xE0, 0x06, 0x9A, 0x03, 0x99, 0x58, 0x1C, + 0x92, 0x7C, 0x49, 0x1C, 0x92, 0x1C, 0x00, 0xFB, 0x02, 0x1B, 0x01, 0x90, + 0x99, 0xF8, 0x00, 0x00, 0x00, 0x90, 0x99, 0xF8, 0x01, 0xE0, 0x15, 0xE0, + 0x1B, 0xF8, 0x00, 0x20, 0x04, 0x99, 0x8A, 0x42, 0x0E, 0xD1, 0x00, 0x9A, + 0xAA, 0xEB, 0x03, 0x01, 0x82, 0x1A, 0x52, 0x1C, 0x49, 0x1C, 0x02, 0xFB, + 0x02, 0x77, 0x02, 0xFB, 0x01, 0xCC, 0x01, 0xFB, 0x01, 0x88, 0x15, 0x44, + 0x0C, 0x44, 0x76, 0x1C, 0x40, 0x1C, 0xC0, 0xB2, 0x86, 0x45, 0xE7, 0xD2, + 0x01, 0x98, 0xC3, 0xB2, 0x9A, 0x45, 0xD4, 0xD2, 0x4F, 0xF4, 0x7A, 0x70, + 0x4F, 0xF4, 0x7A, 0x71, 0x68, 0x43, 0x61, 0x43, 0x90, 0xFB, 0xF6, 0xF0, + 0x91, 0xFB, 0xF6, 0xF2, 0x00, 0xFB, 0x04, 0xF3, 0x4F, 0xF4, 0x7A, 0x71, + 0x68, 0x43, 0x90, 0xFB, 0xF1, 0xF0, 0x62, 0x43, 0x93, 0xFB, 0xF1, 0xF3, + 0x3D, 0x1A, 0x92, 0xFB, 0xF1, 0xF0, 0xA8, 0xEB, 0x00, 0x07, 0xAC, 0xEB, + 0x03, 0x06, 0x74, 0x00, 0xE9, 0x1B, 0x88, 0x46, 0x20, 0x46, 0x00, 0xF0, + 0x40, 0xF8, 0x40, 0x10, 0xB8, 0xF1, 0x00, 0x0F, 0x02, 0xD0, 0x0D, 0xDD, + 0x4C, 0xB1, 0x16, 0xE0, 0x00, 0x2C, 0x06, 0xD0, 0x02, 0xDD, 0x4F, 0xF0, + 0x2D, 0x00, 0x10, 0xE0, 0x4F, 0xF0, 0x5A, 0x00, 0x0D, 0xE0, 0x4F, 0xF0, + 0x00, 0x00, 0x0A, 0xE0, 0x00, 0x2C, 0x03, 0xD0, 0x05, 0xDD, 0x00, 0xF1, + 0x5A, 0x00, 0x04, 0xE0, 0x6F, 0xF0, 0x59, 0x00, 0x01, 0xE0, 0xA0, 0xF1, + 0x5A, 0x00, 0x06, 0x99, 0xC9, 0x68, 0x08, 0x70, 0x5F, 0xEA, 0x08, 0x00, + 0x01, 0xD5, 0xC8, 0xF1, 0x00, 0x00, 0x00, 0x2E, 0x00, 0xDA, 0x76, 0x42, + 0x00, 0xEB, 0x46, 0x00, 0xE9, 0x19, 0x0A, 0x18, 0x08, 0x1A, 0x52, 0x10, + 0x40, 0x10, 0x4F, 0xF4, 0x7A, 0x71, 0x48, 0x43, 0x90, 0xFB, 0xF2, 0xF0, + 0x06, 0x99, 0xC0, 0xF5, 0x7A, 0x70, 0x09, 0x68, 0x08, 0x80, 0x06, 0x99, + 0x07, 0xB0, 0x48, 0x46, 0xBD, 0xE8, 0xF0, 0x4F, 0x46, 0xE7, 0x4F, 0xF4, + 0x7A, 0x72, 0x50, 0x43, 0x90, 0xFB, 0xF1, 0xF1, 0x10, 0xB5, 0x42, 0xF6, + 0xA6, 0x40, 0xC1, 0x42, 0x02, 0xDA, 0x6F, 0xF0, 0x59, 0x00, 0x10, 0xBD, + 0x42, 0xF6, 0xA6, 0x40, 0x81, 0x42, 0x01, 0xDD, 0x5A, 0x20, 0x10, 0xBD, + 0x00, 0x22, 0x11, 0x4B, 0x10, 0x46, 0x33, 0xF9, 0x10, 0x40, 0x8C, 0x42, + 0x07, 0xDC, 0x03, 0xEB, 0x40, 0x04, 0xB4, 0xF9, 0x02, 0x40, 0x8C, 0x42, + 0x01, 0xDB, 0x02, 0x46, 0x46, 0x20, 0x40, 0x1C, 0x45, 0x28, 0xF0, 0xD3, + 0x33, 0xF9, 0x12, 0x00, 0x02, 0xEB, 0x82, 0x04, 0x03, 0xEB, 0x42, 0x02, + 0x09, 0x1A, 0xB2, 0xF9, 0x02, 0x20, 0x01, 0xEB, 0x81, 0x01, 0x10, 0x1A, + 0x91, 0xFB, 0xF0, 0xF0, 0x55, 0x3C, 0x20, 0x44, 0x10, 0xBD, 0x00, 0x00, + 0x80, 0x1F, 0x01, 0x00, 0x2D, 0xE9, 0xF8, 0x4F, 0x86, 0x7C, 0xD0, 0xE9, + 0x01, 0xAE, 0x02, 0xFB, 0x06, 0xFC, 0x0A, 0xEB, 0x4C, 0x04, 0x00, 0x25, + 0xB0, 0xF9, 0x10, 0x70, 0x34, 0xF9, 0x11, 0x40, 0xAB, 0x46, 0x2B, 0x46, + 0xBC, 0x42, 0x2C, 0xDD, 0x54, 0x1E, 0x52, 0x1C, 0x64, 0xB2, 0x00, 0x92, + 0x22, 0xE0, 0x00, 0x2C, 0x1B, 0xDB, 0xC2, 0x7C, 0xA2, 0x42, 0x18, 0xDD, + 0xE7, 0xB2, 0x77, 0x43, 0x4A, 0x1E, 0x0A, 0xEB, 0x47, 0x09, 0x52, 0xB2, + 0x4F, 0x1C, 0x0D, 0xE0, 0x00, 0x2A, 0x07, 0xDB, 0x96, 0x42, 0x05, 0xDD, + 0x39, 0xF9, 0x12, 0x80, 0x3E, 0xF9, 0x13, 0xC0, 0x08, 0xFB, 0x0C, 0x55, + 0x5B, 0x1C, 0x52, 0x1C, 0xDB, 0xB2, 0x52, 0xB2, 0xBA, 0x42, 0xEF, 0xDD, + 0x01, 0xE0, 0xDB, 0x1C, 0xDB, 0xB2, 0x64, 0x1C, 0x00, 0x9A, 0x64, 0xB2, + 0x94, 0x42, 0xDA, 0xDD, 0xBB, 0xF1, 0x00, 0x0F, 0x01, 0xD1, 0x4F, 0xF0, + 0x01, 0x0B, 0x95, 0xFB, 0xFB, 0xF0, 0x00, 0xB2, 0xBD, 0xE8, 0xF8, 0x8F, + 0x2D, 0xE9, 0xF0, 0x4F, 0x05, 0x46, 0x85, 0xB0, 0xD5, 0xE9, 0x00, 0xA1, + 0xC0, 0x68, 0x01, 0x91, 0xAC, 0x68, 0x01, 0x78, 0x09, 0xB1, 0x4A, 0x1E, + 0x02, 0x70, 0x89, 0x46, 0x81, 0x78, 0x09, 0xB1, 0x4A, 0x1E, 0x82, 0x70, + 0x8B, 0x46, 0xAA, 0x7C, 0x41, 0x78, 0x53, 0x1E, 0x99, 0x42, 0x02, 0xDA, + 0x4A, 0x1C, 0x42, 0x70, 0x00, 0xE0, 0x51, 0x1E, 0xCE, 0xB2, 0xEA, 0x7C, + 0xC1, 0x78, 0x53, 0x1E, 0x99, 0x42, 0x02, 0xDA, 0x4A, 0x1C, 0xC2, 0x70, + 0x00, 0xE0, 0x51, 0x1E, 0xCF, 0xB2, 0xB9, 0xF1, 0x00, 0x0F, 0x1A, 0xD1, + 0xD8, 0x46, 0x12, 0xE0, 0xAA, 0x7C, 0x08, 0xFB, 0x02, 0xF1, 0x0A, 0xEB, + 0x41, 0x00, 0x00, 0x90, 0x42, 0x46, 0x49, 0x46, 0x28, 0x46, 0xFF, 0xF7, + 0x83, 0xFF, 0x01, 0x46, 0x00, 0x98, 0x20, 0xF8, 0x19, 0x10, 0x08, 0xF1, + 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x08, 0xB8, 0x45, 0xEA, 0xD9, 0x09, 0xF1, + 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x09, 0xA8, 0x7C, 0x40, 0x1E, 0xB0, 0x42, + 0x18, 0xD1, 0xD8, 0x46, 0x12, 0xE0, 0xAA, 0x7C, 0x31, 0x46, 0x08, 0xFB, + 0x02, 0xF0, 0x0A, 0xEB, 0x40, 0x00, 0x00, 0x90, 0x42, 0x46, 0x28, 0x46, + 0xFF, 0xF7, 0x64, 0xFF, 0x01, 0x46, 0x00, 0x98, 0x20, 0xF8, 0x16, 0x10, + 0x08, 0xF1, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x08, 0xB8, 0x45, 0xEA, 0xD9, + 0x76, 0x1E, 0xF6, 0xB2, 0xBB, 0xF1, 0x00, 0x0F, 0x13, 0xD1, 0x48, 0x46, + 0xC8, 0x46, 0x0A, 0xE0, 0x01, 0x46, 0x5A, 0x46, 0x28, 0x46, 0xFF, 0xF7, + 0x4D, 0xFF, 0x2A, 0xF8, 0x18, 0x00, 0x08, 0xF1, 0x01, 0x00, 0xC0, 0xB2, + 0x80, 0x46, 0xB0, 0x45, 0xF2, 0xD9, 0x0B, 0xF1, 0x01, 0x00, 0x00, 0xF0, + 0xFF, 0x0B, 0xE8, 0x7C, 0x40, 0x1E, 0xB8, 0x42, 0x17, 0xD1, 0xA9, 0x7C, + 0xC8, 0x46, 0x79, 0x43, 0x0A, 0xEB, 0x41, 0x00, 0x00, 0x90, 0x48, 0x46, + 0x0B, 0xE0, 0x01, 0x46, 0x3A, 0x46, 0x28, 0x46, 0xFF, 0xF7, 0x30, 0xFF, + 0x00, 0x99, 0x21, 0xF8, 0x18, 0x00, 0x08, 0xF1, 0x01, 0x00, 0xC0, 0xB2, + 0x80, 0x46, 0xB0, 0x45, 0xF1, 0xD9, 0x7F, 0x1E, 0xFF, 0xB2, 0xB4, 0xF9, + 0x12, 0x00, 0x5F, 0xEA, 0x00, 0x0E, 0x01, 0xD1, 0x4F, 0xF0, 0x01, 0x0E, + 0x5A, 0x46, 0x64, 0xE0, 0xA9, 0x7C, 0x01, 0x9B, 0x02, 0xFB, 0x01, 0xFC, + 0x0A, 0xEB, 0x4C, 0x00, 0x00, 0x90, 0x50, 0x1E, 0xC0, 0xB2, 0x48, 0x43, + 0x03, 0xEB, 0x40, 0x00, 0x04, 0x90, 0x03, 0xEB, 0x4C, 0x00, 0x03, 0x90, + 0x48, 0x46, 0x4E, 0xE0, 0x03, 0x99, 0xB5, 0xF9, 0x10, 0x30, 0x31, 0xF9, + 0x10, 0x10, 0x8B, 0x42, 0x42, 0xDA, 0x04, 0x99, 0xB4, 0xF9, 0x00, 0x80, + 0x01, 0xEB, 0x40, 0x01, 0x00, 0x23, 0x31, 0xF9, 0x02, 0xCD, 0x0C, 0xFB, + 0x08, 0x33, 0xB1, 0xF9, 0x02, 0xC0, 0xB4, 0xF9, 0x02, 0x80, 0x0C, 0xFB, + 0x08, 0x33, 0xB1, 0xF9, 0x04, 0xC0, 0xB4, 0xF9, 0x04, 0x80, 0x0C, 0xFB, + 0x08, 0x3B, 0xAB, 0x7C, 0x02, 0x93, 0x01, 0xEB, 0x43, 0x01, 0xB4, 0xF9, + 0x06, 0x80, 0xB1, 0xF9, 0x00, 0xC0, 0x0C, 0xFB, 0x08, 0xB3, 0xB1, 0xF9, + 0x02, 0xC0, 0xB4, 0xF9, 0x08, 0x80, 0x0C, 0xFB, 0x08, 0x33, 0xB1, 0xF9, + 0x04, 0xC0, 0xB4, 0xF9, 0x0A, 0x80, 0x0C, 0xFB, 0x08, 0x3B, 0x02, 0x9B, + 0xB4, 0xF9, 0x0C, 0x80, 0x01, 0xEB, 0x43, 0x01, 0xB1, 0xF9, 0x00, 0xC0, + 0x0C, 0xFB, 0x08, 0xB3, 0xB1, 0xF9, 0x02, 0xC0, 0xB4, 0xF9, 0x0E, 0x80, + 0xB1, 0xF9, 0x04, 0x10, 0x0C, 0xFB, 0x08, 0x33, 0xB4, 0xF9, 0x10, 0xC0, + 0x01, 0xFB, 0x0C, 0x31, 0x91, 0xFB, 0xFE, 0xF1, 0x00, 0x9B, 0x23, 0xF8, + 0x10, 0x10, 0x40, 0x1C, 0xC0, 0xB2, 0xB0, 0x42, 0xAE, 0xD9, 0x52, 0x1C, + 0xD2, 0xB2, 0xBA, 0x42, 0x98, 0xD9, 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0xC7, 0x49, 0xC6, 0x48, 0x48, 0x60, 0x70, 0x47, 0xC5, 0x49, 0x02, 0x78, + 0x0A, 0x70, 0x40, 0x78, 0x48, 0x70, 0x70, 0x47, 0xC0, 0xEB, 0x00, 0x10, + 0xC2, 0x4B, 0x10, 0xB5, 0x00, 0xEB, 0x40, 0x00, 0x18, 0x44, 0x92, 0x78, + 0x80, 0xF8, 0x2C, 0x20, 0x4A, 0x88, 0x42, 0x80, 0x0A, 0x88, 0x02, 0x80, + 0xCA, 0x88, 0xC2, 0x80, 0x8A, 0x88, 0x82, 0x80, 0x4A, 0x89, 0x42, 0x81, + 0x0A, 0x89, 0x02, 0x81, 0xB1, 0xF9, 0x02, 0x20, 0xD3, 0x17, 0x1B, 0x04, + 0x43, 0xEA, 0x12, 0x43, 0x12, 0x04, 0x42, 0x61, 0x83, 0x61, 0xB1, 0xF9, + 0x00, 0x20, 0xD3, 0x17, 0x1B, 0x04, 0x43, 0xEA, 0x12, 0x43, 0x12, 0x04, + 0xC2, 0x60, 0x03, 0x61, 0xB1, 0xF9, 0x02, 0x20, 0xD3, 0x17, 0x1B, 0x04, + 0x43, 0xEA, 0x12, 0x43, 0x12, 0x04, 0x42, 0x62, 0x83, 0x62, 0xB1, 0xF9, + 0x00, 0x10, 0xCA, 0x17, 0x12, 0x04, 0x42, 0xEA, 0x11, 0x42, 0x09, 0x04, + 0xC1, 0x61, 0x02, 0x62, 0x10, 0xBD, 0x2D, 0xE9, 0xF0, 0x4F, 0x8A, 0x46, + 0xC0, 0xEB, 0x00, 0x10, 0xA3, 0x49, 0x00, 0xEB, 0x40, 0x00, 0x00, 0xEB, + 0x01, 0x09, 0xD0, 0x78, 0x8B, 0xB0, 0x81, 0x07, 0x02, 0xD0, 0x00, 0xF0, + 0x03, 0x00, 0x00, 0xE0, 0x01, 0x20, 0x53, 0x79, 0x12, 0x79, 0xB9, 0xF8, + 0x06, 0x40, 0xA9, 0xF8, 0x0A, 0x40, 0xB9, 0xF8, 0x04, 0x40, 0xA9, 0xF8, + 0x08, 0x40, 0xB9, 0xF8, 0x02, 0x40, 0xA9, 0xF8, 0x06, 0x40, 0xB9, 0xF8, + 0x00, 0x40, 0xA9, 0xF8, 0x04, 0x40, 0xBA, 0xF8, 0x02, 0x40, 0xA9, 0xF8, + 0x02, 0x40, 0xBA, 0xF8, 0x00, 0x40, 0xA9, 0xF8, 0x00, 0x40, 0xB9, 0xF8, + 0x02, 0x50, 0xB9, 0xF8, 0x06, 0x10, 0x5B, 0x43, 0x69, 0x1A, 0xB9, 0xF8, + 0x04, 0x50, 0x09, 0xB2, 0x64, 0x1B, 0x24, 0xB2, 0x64, 0x43, 0x01, 0xFB, + 0x01, 0x41, 0x52, 0x43, 0x87, 0x4C, 0x99, 0x42, 0x09, 0xDB, 0x99, 0xF8, + 0x2C, 0x10, 0x08, 0x44, 0x94, 0xF9, 0x01, 0x10, 0x88, 0x42, 0x0D, 0xDA, + 0x89, 0xF8, 0x2C, 0x00, 0x0C, 0xE0, 0x91, 0x42, 0x0A, 0xDC, 0x94, 0xF9, + 0x00, 0x10, 0x99, 0xF8, 0x2C, 0x20, 0x0B, 0x18, 0x9A, 0x42, 0x01, 0xDD, + 0x10, 0x1A, 0xF1, 0xE7, 0x89, 0xF8, 0x2C, 0x10, 0x99, 0xF8, 0x2C, 0x00, + 0x61, 0x68, 0x01, 0xEB, 0x80, 0x00, 0xD0, 0xF8, 0x88, 0x20, 0x0A, 0x92, + 0xD4, 0x17, 0xD0, 0xF8, 0xCC, 0x20, 0xD7, 0x17, 0xCD, 0xE9, 0x04, 0x27, + 0x01, 0x68, 0xCD, 0x17, 0xCD, 0xE9, 0x02, 0x15, 0x41, 0x6C, 0xCA, 0x17, + 0xCD, 0xE9, 0x00, 0x12, 0xD9, 0xF8, 0x18, 0x30, 0xD9, 0xF8, 0x14, 0x20, + 0xCD, 0xE9, 0x06, 0x23, 0x02, 0x99, 0xA2, 0xFB, 0x01, 0x06, 0x03, 0xFB, + 0x01, 0x61, 0x02, 0xFB, 0x05, 0x11, 0x4F, 0xF4, 0x80, 0x32, 0x00, 0x23, + 0x00, 0xF0, 0x28, 0xFD, 0xB9, 0xF9, 0x02, 0x30, 0xB9, 0xF9, 0x0A, 0x20, + 0x13, 0x44, 0xDD, 0xE9, 0x04, 0x27, 0xA3, 0xFB, 0x02, 0x6C, 0xDD, 0x17, + 0x05, 0xFB, 0x02, 0xC2, 0x03, 0xFB, 0x07, 0x25, 0xB9, 0xF9, 0x06, 0x30, + 0x0A, 0x9A, 0xDF, 0x17, 0xA3, 0xFB, 0x02, 0xC8, 0x07, 0xFB, 0x02, 0x82, + 0x03, 0xFB, 0x04, 0x22, 0x16, 0xEB, 0x0C, 0x03, 0x55, 0x41, 0x1E, 0x1A, + 0x65, 0xEB, 0x01, 0x05, 0xDD, 0xE9, 0x00, 0x12, 0xD9, 0xF8, 0x24, 0x30, + 0xD9, 0xF8, 0x28, 0x70, 0xA3, 0xFB, 0x01, 0x0C, 0x07, 0xFB, 0x01, 0xC1, + 0x03, 0xFB, 0x02, 0x11, 0x4F, 0xF4, 0x80, 0x32, 0x00, 0x23, 0x00, 0xF0, + 0xF9, 0xFC, 0x32, 0x1A, 0x08, 0x92, 0x65, 0xEB, 0x01, 0x0B, 0xDD, 0xE9, + 0x02, 0x15, 0xD9, 0xF8, 0x0C, 0x20, 0xD9, 0xF8, 0x10, 0x30, 0xA2, 0xFB, + 0x01, 0x06, 0x03, 0xFB, 0x01, 0x61, 0x02, 0xFB, 0x05, 0x11, 0x4F, 0xF4, + 0x80, 0x32, 0x00, 0x23, 0x00, 0xF0, 0xE4, 0xFC, 0xB9, 0xF9, 0x00, 0x30, + 0xB9, 0xF9, 0x08, 0x20, 0x13, 0x44, 0xDD, 0xE9, 0x04, 0x27, 0xA3, 0xFB, + 0x02, 0x5C, 0xDE, 0x17, 0x06, 0xFB, 0x02, 0xC2, 0x03, 0xFB, 0x07, 0x26, + 0xB9, 0xF9, 0x04, 0x30, 0x0A, 0x9A, 0xDF, 0x17, 0xA3, 0xFB, 0x02, 0xC8, + 0x07, 0xFB, 0x02, 0x82, 0x03, 0xFB, 0x04, 0x22, 0x15, 0xEB, 0x0C, 0x03, + 0x56, 0x41, 0x1C, 0x1A, 0x66, 0xEB, 0x01, 0x06, 0xDD, 0xE9, 0x00, 0x12, + 0xD9, 0xF8, 0x1C, 0x30, 0xD9, 0xF8, 0x20, 0x50, 0xA3, 0xFB, 0x01, 0x07, + 0x05, 0xFB, 0x01, 0x71, 0x03, 0xFB, 0x02, 0x11, 0x4F, 0xF4, 0x80, 0x32, + 0x00, 0x23, 0x00, 0xF0, 0xB5, 0xFC, 0xDD, 0xE9, 0x06, 0x23, 0xC9, 0xF8, + 0x24, 0x20, 0x24, 0x1A, 0xC9, 0xF8, 0x28, 0x30, 0x66, 0xEB, 0x01, 0x06, + 0xD9, 0xF8, 0x0C, 0x10, 0xD9, 0xF8, 0x10, 0x20, 0xC9, 0xF8, 0x1C, 0x10, + 0xC9, 0xF8, 0x20, 0x20, 0x08, 0x9A, 0xC9, 0xF8, 0x14, 0x20, 0xC9, 0xF8, + 0x18, 0xB0, 0xC9, 0xF8, 0x0C, 0x40, 0xC9, 0xF8, 0x10, 0x60, 0x08, 0x9A, + 0x4F, 0xF4, 0x00, 0x40, 0x10, 0x18, 0x4B, 0xF1, 0x00, 0x01, 0x4F, 0xF4, + 0x80, 0x32, 0x00, 0x23, 0x00, 0xF0, 0x90, 0xFC, 0xAA, 0xF8, 0x02, 0x00, + 0x4F, 0xF4, 0x00, 0x40, 0x20, 0x18, 0x46, 0xF1, 0x00, 0x01, 0x4F, 0xF4, + 0x80, 0x32, 0x00, 0x23, 0x00, 0xF0, 0x84, 0xFC, 0xAA, 0xF8, 0x00, 0x00, + 0x0B, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0xC0, 0xEB, 0x00, 0x10, 0x12, 0x4A, + 0x00, 0xEB, 0x40, 0x00, 0x10, 0xB5, 0x10, 0x44, 0x42, 0x69, 0x83, 0x69, + 0x42, 0x62, 0x83, 0x62, 0xC2, 0x68, 0x03, 0x69, 0xC2, 0x61, 0x03, 0x62, + 0xB1, 0xF9, 0x02, 0x20, 0xD3, 0x17, 0x1B, 0x04, 0x43, 0xEA, 0x12, 0x43, + 0x12, 0x04, 0x42, 0x61, 0x83, 0x61, 0xB1, 0xF9, 0x00, 0x10, 0xCA, 0x17, + 0x12, 0x04, 0x42, 0xEA, 0x11, 0x42, 0x09, 0x04, 0xC1, 0x60, 0x02, 0x61, + 0x10, 0xBD, 0x00, 0x00, 0xD8, 0x20, 0x01, 0x00, 0x78, 0x24, 0x10, 0x00, + 0x90, 0x81, 0x10, 0x00, 0xC2, 0xF1, 0x10, 0x03, 0x50, 0x43, 0x01, 0xFB, + 0x03, 0x00, 0x00, 0x11, 0x70, 0x47, 0x50, 0x21, 0x20, 0x48, 0x00, 0xF0, + 0xEE, 0xBC, 0x1F, 0x4A, 0x02, 0xEB, 0xC0, 0x00, 0x00, 0xF0, 0x27, 0xB8, + 0x7C, 0xB5, 0x0E, 0x46, 0x1B, 0x49, 0x15, 0x46, 0x01, 0xEB, 0xC0, 0x04, + 0x31, 0x46, 0x68, 0x46, 0x00, 0xF0, 0x1D, 0xF8, 0x01, 0x99, 0x60, 0x68, + 0x2A, 0x46, 0xFF, 0xF7, 0xE1, 0xFF, 0x60, 0x60, 0x00, 0x99, 0x20, 0x68, + 0x2A, 0x46, 0xFF, 0xF7, 0xDB, 0xFF, 0x20, 0x60, 0x21, 0x46, 0x30, 0x46, + 0x00, 0xF0, 0x16, 0xF8, 0x7C, 0xBD, 0x0F, 0x4A, 0x02, 0xEB, 0xC0, 0x00, + 0x00, 0xF0, 0x07, 0xB8, 0x0A, 0x46, 0x0C, 0x49, 0x01, 0xEB, 0xC0, 0x01, + 0x10, 0x46, 0x00, 0xF0, 0x09, 0xB8, 0xB1, 0xF9, 0x02, 0x20, 0x12, 0x02, + 0x42, 0x60, 0xB1, 0xF9, 0x00, 0x10, 0x09, 0x02, 0x01, 0x60, 0x70, 0x47, + 0x4A, 0x68, 0x80, 0x32, 0x12, 0x12, 0x42, 0x80, 0x09, 0x68, 0x80, 0x31, + 0x09, 0x12, 0x01, 0x80, 0x70, 0x47, 0x00, 0x00, 0x52, 0x83, 0x10, 0x00, + 0x38, 0xB5, 0x04, 0x46, 0x00, 0x88, 0xFF, 0x49, 0x80, 0x08, 0x02, 0x23, + 0x00, 0x90, 0x1A, 0x46, 0xA1, 0xF1, 0x10, 0x00, 0x00, 0xF0, 0x59, 0xFB, + 0xFA, 0x49, 0x60, 0x68, 0x5C, 0x39, 0x08, 0x60, 0x38, 0xBD, 0xF9, 0x4A, + 0x10, 0xB5, 0x00, 0xEB, 0x00, 0x10, 0x02, 0xEB, 0x80, 0x00, 0x4A, 0x88, + 0x42, 0x80, 0x0A, 0x88, 0x20, 0xF8, 0x06, 0x2B, 0xCA, 0x88, 0x20, 0xF8, + 0x02, 0x29, 0x8A, 0x88, 0x20, 0xF8, 0x06, 0x2B, 0x4A, 0x89, 0x20, 0xF8, + 0x0A, 0x2B, 0x0A, 0x89, 0x20, 0xF8, 0x0C, 0x2C, 0x00, 0x22, 0xC2, 0x62, + 0x82, 0x62, 0xB1, 0xF9, 0x02, 0x30, 0x40, 0xE9, 0x02, 0x32, 0xB1, 0xF9, + 0x00, 0x10, 0xE0, 0xE8, 0x02, 0x12, 0xE7, 0x49, 0x5C, 0x39, 0x01, 0xF1, + 0x5C, 0x02, 0x89, 0x6E, 0x1C, 0xCA, 0x1C, 0xC0, 0x1E, 0xC0, 0x01, 0x60, + 0x10, 0xBD, 0x70, 0xB5, 0xB0, 0xF9, 0x02, 0x30, 0xB0, 0xF9, 0x06, 0x20, + 0xB0, 0xF9, 0x0A, 0x40, 0x9B, 0x1A, 0x12, 0x1B, 0x9C, 0x1A, 0xB0, 0xF9, + 0x00, 0x30, 0xB0, 0xF9, 0x04, 0x20, 0xB0, 0xF9, 0x08, 0x50, 0x9B, 0x1A, + 0x52, 0x1B, 0x9D, 0x1A, 0x02, 0x6C, 0xC1, 0xF1, 0x80, 0x03, 0x4A, 0x43, + 0xD6, 0x17, 0x5C, 0x43, 0x02, 0xEB, 0x56, 0x62, 0x04, 0xEB, 0xE2, 0x12, + 0x02, 0x64, 0xC2, 0x6B, 0x6B, 0x43, 0x4A, 0x43, 0xD1, 0x17, 0x02, 0xEB, + 0x51, 0x61, 0x03, 0xEB, 0xE1, 0x11, 0xC1, 0x63, 0x70, 0xBD, 0x70, 0xB5, + 0x15, 0x46, 0xCE, 0x4A, 0x00, 0xEB, 0x00, 0x10, 0x02, 0xEB, 0x80, 0x04, + 0x20, 0x46, 0x00, 0xF0, 0x84, 0xFA, 0x29, 0x7A, 0x20, 0x46, 0xFF, 0xF7, + 0xCA, 0xFF, 0xB4, 0xF9, 0x02, 0x00, 0xE0, 0x60, 0xB4, 0xF9, 0x06, 0x10, + 0x40, 0x1A, 0x20, 0x61, 0xB4, 0xF9, 0x00, 0x00, 0x60, 0x61, 0xB4, 0xF9, + 0x04, 0x10, 0x40, 0x1A, 0xA0, 0x61, 0x70, 0xBD, 0x2D, 0xE9, 0xF7, 0x4F, + 0x8E, 0xB0, 0x00, 0x25, 0x05, 0x95, 0x06, 0x95, 0x07, 0x95, 0x02, 0x24, + 0x08, 0x95, 0x09, 0x95, 0x0A, 0x95, 0x0B, 0x95, 0x05, 0xAF, 0x0C, 0x95, + 0xCD, 0xE9, 0x01, 0x47, 0x8C, 0x46, 0x4F, 0xF0, 0x01, 0x0A, 0xB6, 0x4B, + 0x06, 0x46, 0x03, 0x94, 0x50, 0x3B, 0x52, 0x46, 0x21, 0x46, 0x60, 0x46, + 0xCD, 0xF8, 0x00, 0xA0, 0x04, 0x94, 0x00, 0xF0, 0x24, 0xFB, 0xB0, 0x48, + 0x05, 0xAA, 0x02, 0x23, 0x11, 0x46, 0x20, 0x38, 0x00, 0x94, 0x00, 0xF0, + 0xFA, 0xFA, 0x0D, 0xF1, 0x24, 0x08, 0xCD, 0xE9, 0x01, 0x48, 0x06, 0xF1, + 0x1C, 0x03, 0x02, 0x22, 0x03, 0x94, 0x04, 0x94, 0x9B, 0x46, 0x11, 0x46, + 0x05, 0xA8, 0x00, 0x94, 0x00, 0xF0, 0x0D, 0xFB, 0x4F, 0xF4, 0x7A, 0x79, + 0x02, 0x23, 0x1A, 0x46, 0x59, 0x46, 0x09, 0xA8, 0xCD, 0xF8, 0x00, 0x90, + 0x00, 0xF0, 0x87, 0xFA, 0x05, 0x95, 0x06, 0x95, 0x07, 0x95, 0x08, 0x95, + 0x09, 0x95, 0x0A, 0x95, 0x0B, 0x95, 0x0C, 0x95, 0xCD, 0xE9, 0x01, 0x47, + 0x9A, 0x4B, 0x03, 0x94, 0x50, 0x3B, 0x01, 0x22, 0x02, 0x21, 0xCD, 0xF8, + 0x00, 0xA0, 0x04, 0x94, 0x10, 0x98, 0x00, 0xF0, 0xEE, 0xFA, 0x95, 0x48, + 0x05, 0xAA, 0x02, 0x23, 0x11, 0x46, 0x20, 0x38, 0x00, 0x94, 0x00, 0xF0, + 0xC4, 0xFA, 0xCD, 0xE9, 0x01, 0x48, 0x03, 0x94, 0x04, 0x94, 0x06, 0xF1, + 0x2C, 0x03, 0x00, 0x94, 0x02, 0x22, 0x1C, 0x46, 0x11, 0x46, 0x05, 0xA8, + 0x00, 0xF0, 0xD9, 0xFA, 0x02, 0x23, 0x1A, 0x46, 0x21, 0x46, 0x09, 0xA8, + 0xCD, 0xF8, 0x00, 0x90, 0x00, 0xF0, 0x55, 0xFA, 0x11, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, 0xF0, 0x4F, 0x89, 0xB0, 0x00, 0x26, 0x01, 0x24, + 0x05, 0x96, 0x06, 0x96, 0x0D, 0xF1, 0x14, 0x0A, 0x07, 0x96, 0xCD, 0xE9, + 0x01, 0x4A, 0x05, 0x46, 0x00, 0xF1, 0x0C, 0x03, 0x4F, 0xF0, 0x02, 0x09, + 0x7C, 0x48, 0x93, 0x46, 0x0F, 0x46, 0x03, 0x94, 0x98, 0x46, 0x4A, 0x46, + 0x21, 0x46, 0x50, 0x38, 0xCD, 0xF8, 0x00, 0x90, 0x04, 0x94, 0x00, 0xF0, + 0xB0, 0xFA, 0xB5, 0xF9, 0x02, 0x00, 0x05, 0x99, 0x01, 0x23, 0x40, 0x1A, + 0x05, 0x90, 0x00, 0x90, 0x02, 0x22, 0x06, 0xA9, 0x38, 0x46, 0x00, 0xF0, + 0x44, 0xFA, 0x4F, 0xF4, 0x7A, 0x77, 0x41, 0x46, 0x01, 0x23, 0x02, 0x22, + 0x08, 0x46, 0x00, 0x97, 0x00, 0xF0, 0x3B, 0xFA, 0x42, 0x46, 0x02, 0x23, + 0x06, 0xA9, 0x10, 0x46, 0x00, 0x94, 0x00, 0xF0, 0x50, 0xFA, 0x41, 0x46, + 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0x00, 0x97, 0x00, 0xF0, 0x11, 0xFA, + 0x05, 0x96, 0x06, 0x96, 0x07, 0x96, 0xCD, 0xE9, 0x01, 0x4A, 0x62, 0x48, + 0x05, 0xF1, 0x14, 0x03, 0x03, 0x94, 0x1E, 0x46, 0x02, 0x22, 0x01, 0x21, + 0x50, 0x38, 0xCD, 0xF8, 0x00, 0x90, 0x04, 0x94, 0x00, 0xF0, 0x7B, 0xFA, + 0xB5, 0xF9, 0x00, 0x00, 0x05, 0x99, 0x01, 0x23, 0x40, 0x1A, 0x05, 0x90, + 0x00, 0x90, 0x02, 0x22, 0x06, 0xA9, 0x58, 0x46, 0x00, 0xF0, 0x0F, 0xFA, + 0x31, 0x46, 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0x00, 0x97, 0x00, 0xF0, + 0x08, 0xFA, 0x32, 0x46, 0x02, 0x23, 0x06, 0xA9, 0x10, 0x46, 0x00, 0x94, + 0x00, 0xF0, 0x1D, 0xFA, 0x31, 0x46, 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, + 0x00, 0x97, 0x00, 0xF0, 0xDE, 0xF9, 0x09, 0xB0, 0x87, 0xE7, 0x2D, 0xE9, + 0xF7, 0x4F, 0x8A, 0xB0, 0x00, 0x26, 0x05, 0x96, 0x06, 0x96, 0x08, 0x96, + 0x09, 0x96, 0x01, 0x24, 0x0D, 0xF1, 0x14, 0x08, 0x07, 0x96, 0x02, 0x25, + 0xCD, 0xE9, 0x01, 0x48, 0xCD, 0xE9, 0x03, 0x54, 0x07, 0x46, 0x41, 0x4B, + 0x07, 0xF1, 0x1C, 0x00, 0x48, 0x3B, 0x2A, 0x46, 0x29, 0x46, 0x82, 0x46, + 0x00, 0x95, 0x00, 0xF0, 0x3C, 0xFA, 0x0D, 0xF1, 0x20, 0x09, 0xCD, 0xE9, + 0x01, 0x59, 0x3A, 0x48, 0xCD, 0xE9, 0x03, 0x45, 0x53, 0x46, 0x02, 0x22, + 0x01, 0x21, 0x50, 0x38, 0x00, 0x95, 0x00, 0xF0, 0x2E, 0xFA, 0x07, 0xA8, + 0xCD, 0xE9, 0x01, 0x40, 0x33, 0x4B, 0x03, 0x94, 0x48, 0x3B, 0x02, 0x22, + 0x01, 0x21, 0x08, 0xA8, 0x00, 0x95, 0x04, 0x94, 0x00, 0xF0, 0x21, 0xFA, + 0xDF, 0xF8, 0xB8, 0xA0, 0x07, 0x99, 0xAA, 0xF1, 0x5C, 0x0A, 0x4F, 0xF4, + 0x7A, 0x7B, 0xDA, 0xF8, 0x00, 0x00, 0x01, 0x23, 0x08, 0x44, 0x05, 0xA9, + 0x07, 0x90, 0x02, 0x22, 0xCD, 0xF8, 0x00, 0xB0, 0x08, 0x46, 0x00, 0xF0, + 0xAE, 0xF9, 0x07, 0x98, 0x00, 0x90, 0x01, 0x23, 0x02, 0x22, 0x05, 0xA8, + 0x0B, 0x99, 0x00, 0xF0, 0x8A, 0xF9, 0x08, 0x96, 0x09, 0x96, 0x05, 0x96, + 0x06, 0x96, 0x07, 0x96, 0xCD, 0xE9, 0x01, 0x48, 0xCD, 0xE9, 0x03, 0x54, + 0x02, 0x22, 0x07, 0xF1, 0x2C, 0x00, 0x0A, 0xF1, 0x14, 0x03, 0x11, 0x46, + 0x06, 0x46, 0x00, 0x95, 0x00, 0xF0, 0xF3, 0xF9, 0xCD, 0xE9, 0x01, 0x59, + 0xCD, 0xE9, 0x03, 0x45, 0x33, 0x46, 0x02, 0x22, 0x01, 0x21, 0x0A, 0xF1, + 0x0C, 0x00, 0x00, 0x95, 0x00, 0xF0, 0xE7, 0xF9, 0x07, 0xA8, 0xCD, 0xE9, + 0x01, 0x40, 0x03, 0x94, 0x0A, 0xF1, 0x14, 0x03, 0x02, 0x22, 0x01, 0x21, + 0x08, 0xA8, 0x00, 0x95, 0x04, 0x94, 0x00, 0xF0, 0xDA, 0xF9, 0x07, 0x99, + 0xDA, 0xF8, 0x00, 0x00, 0x01, 0x23, 0x08, 0x44, 0x05, 0xA9, 0x07, 0x90, + 0x02, 0x22, 0xCD, 0xF8, 0x00, 0xB0, 0x08, 0x46, 0x00, 0xF0, 0x6D, 0xF9, + 0x07, 0x98, 0x00, 0x90, 0x01, 0x23, 0x02, 0x22, 0x05, 0xA8, 0x0C, 0x99, + 0x00, 0xF0, 0x49, 0xF9, 0x0D, 0xB0, 0xF2, 0xE6, 0xDC, 0x24, 0x10, 0x00, + 0xA4, 0x83, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x43, 0x8D, 0xB0, 0x00, 0x25, + 0x02, 0x24, 0x05, 0x95, 0x06, 0x95, 0x07, 0x95, 0x08, 0x95, 0x09, 0x95, + 0x0A, 0x95, 0x0B, 0x95, 0x05, 0xAF, 0x0C, 0x95, 0xCD, 0xE9, 0x01, 0x47, + 0x06, 0x46, 0x06, 0xF1, 0x1C, 0x03, 0x03, 0x94, 0x04, 0x94, 0x99, 0x46, + 0x22, 0x46, 0x21, 0x46, 0x70, 0x48, 0x00, 0x94, 0x00, 0xF0, 0xA3, 0xF9, + 0x0D, 0xF1, 0x24, 0x08, 0xCD, 0xE9, 0x01, 0x48, 0x6C, 0x4B, 0x02, 0x22, + 0x03, 0x94, 0x04, 0x94, 0x10, 0x33, 0x11, 0x46, 0x05, 0xA8, 0x00, 0x94, + 0x00, 0xF0, 0x95, 0xF9, 0x67, 0x49, 0x02, 0x23, 0x4A, 0x46, 0x40, 0x31, + 0x09, 0xA8, 0x00, 0x94, 0x00, 0xF0, 0x49, 0xF9, 0x05, 0x95, 0x06, 0x95, + 0x07, 0x95, 0x08, 0x95, 0x09, 0x95, 0x0A, 0x95, 0x0B, 0x95, 0x0C, 0x95, + 0xCD, 0xE9, 0x01, 0x47, 0x06, 0xF1, 0x2C, 0x03, 0x02, 0x22, 0x03, 0x94, + 0x04, 0x94, 0x1D, 0x46, 0x11, 0x46, 0x5B, 0x48, 0x00, 0x94, 0x00, 0xF0, + 0x78, 0xF9, 0xCD, 0xE9, 0x01, 0x48, 0x58, 0x4B, 0x02, 0x22, 0x03, 0x94, + 0x04, 0x94, 0x10, 0x33, 0x11, 0x46, 0x05, 0xA8, 0x00, 0x94, 0x00, 0xF0, + 0x6C, 0xF9, 0x53, 0x49, 0x02, 0x23, 0x2A, 0x46, 0x40, 0x31, 0x09, 0xA8, + 0x00, 0x94, 0x00, 0xF0, 0x20, 0xF9, 0x0D, 0xB0, 0xBD, 0xE8, 0xF0, 0x83, + 0x2D, 0xE9, 0xF0, 0x47, 0x8A, 0xB0, 0x00, 0x26, 0x07, 0x96, 0x08, 0x96, + 0x05, 0x96, 0x01, 0x25, 0x0D, 0xF1, 0x1C, 0x08, 0x06, 0x96, 0x02, 0x27, + 0xCD, 0xE9, 0x01, 0x58, 0xCD, 0xE9, 0x03, 0x75, 0x00, 0xF1, 0x0C, 0x03, + 0x04, 0x46, 0x9A, 0x46, 0x3A, 0x46, 0x39, 0x46, 0x42, 0x48, 0x00, 0x97, + 0x00, 0xF0, 0x47, 0xF9, 0x20, 0x6C, 0x00, 0x90, 0x3F, 0x48, 0x01, 0x23, + 0x02, 0x22, 0x05, 0xA9, 0x18, 0x38, 0x00, 0xF0, 0xDE, 0xF8, 0x4F, 0xF4, + 0x80, 0x79, 0x05, 0xA9, 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0xCD, 0xF8, + 0x00, 0x90, 0x00, 0xF0, 0xB8, 0xF8, 0x02, 0x23, 0x52, 0x46, 0x05, 0xA9, + 0x07, 0xA8, 0x00, 0x95, 0x00, 0xF0, 0xE9, 0xF8, 0x07, 0x96, 0x08, 0x96, + 0x05, 0x96, 0x06, 0x96, 0xCD, 0xE9, 0x01, 0x58, 0xCD, 0xE9, 0x03, 0x75, + 0x04, 0xF1, 0x14, 0x03, 0x02, 0x22, 0x1E, 0x46, 0x11, 0x46, 0x2D, 0x48, + 0x00, 0x97, 0x00, 0xF0, 0x1C, 0xF9, 0xE0, 0x6B, 0x00, 0x90, 0x2A, 0x48, + 0x01, 0x23, 0x02, 0x22, 0x05, 0xA9, 0x18, 0x38, 0x00, 0xF0, 0xB3, 0xF8, + 0x05, 0xA9, 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0xCD, 0xF8, 0x00, 0x90, + 0x00, 0xF0, 0x8F, 0xF8, 0x02, 0x23, 0x32, 0x46, 0x05, 0xA9, 0x07, 0xA8, + 0x00, 0x95, 0x00, 0xF0, 0xC0, 0xF8, 0x0A, 0xB0, 0xBD, 0xE8, 0xF0, 0x87, + 0x7F, 0xB5, 0x0D, 0x46, 0x1D, 0x49, 0x00, 0xEB, 0x00, 0x10, 0x01, 0xEB, + 0x80, 0x04, 0x16, 0x46, 0x29, 0x46, 0x20, 0x46, 0x00, 0xF0, 0x23, 0xF8, + 0x31, 0x7A, 0x20, 0x46, 0xFF, 0xF7, 0x69, 0xFD, 0x20, 0x46, 0xFF, 0xF7, + 0x8D, 0xFF, 0x20, 0x46, 0xFF, 0xF7, 0x2C, 0xFF, 0x00, 0x20, 0x00, 0x90, + 0x01, 0x90, 0x02, 0x90, 0x03, 0x90, 0x02, 0xAA, 0x69, 0x46, 0x20, 0x46, + 0xFF, 0xF7, 0x89, 0xFE, 0x02, 0xAA, 0x69, 0x46, 0x20, 0x46, 0xFF, 0xF7, + 0x0E, 0xFE, 0x02, 0xAA, 0x69, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0x94, 0xFD, + 0xA0, 0x89, 0x68, 0x80, 0xA0, 0x8A, 0x28, 0x80, 0x7F, 0xBD, 0xC2, 0x88, + 0x42, 0x81, 0x82, 0x88, 0x02, 0x81, 0x42, 0x88, 0xC2, 0x80, 0x02, 0x88, + 0x82, 0x80, 0x4A, 0x88, 0x42, 0x80, 0x09, 0x88, 0x01, 0x80, 0x70, 0x47, + 0x9C, 0x24, 0x10, 0x00, 0xA4, 0x83, 0x10, 0x00, 0x00, 0x20, 0x70, 0x47, + 0x4F, 0xF4, 0x58, 0x70, 0x70, 0x47, 0x00, 0x00, 0x00, 0x21, 0x01, 0x60, + 0x08, 0x46, 0x70, 0x47, 0x30, 0xB5, 0x0A, 0x68, 0x12, 0xB1, 0x0B, 0x46, + 0xC4, 0x6B, 0x09, 0xE0, 0x00, 0x22, 0x08, 0x60, 0xC0, 0xE9, 0x03, 0x21, + 0x0B, 0xE0, 0x00, 0xBF, 0x02, 0xF1, 0x0C, 0x03, 0xD2, 0x68, 0x12, 0xB1, + 0xD5, 0x6B, 0xA5, 0x42, 0xF8, 0xDC, 0x1A, 0x68, 0xC0, 0xE9, 0x03, 0x21, + 0x18, 0x60, 0x00, 0x20, 0x30, 0xBD, 0x00, 0x68, 0x00, 0x22, 0x01, 0xE0, + 0xC0, 0x68, 0x52, 0x1C, 0x8A, 0x42, 0x01, 0xD0, 0x00, 0x28, 0xF9, 0xD1, + 0x70, 0x47, 0x00, 0x68, 0x70, 0x47, 0x02, 0x69, 0x0A, 0xB1, 0x11, 0x68, + 0x31, 0xB9, 0x6F, 0xF0, 0x04, 0x00, 0x70, 0x47, 0x01, 0xF1, 0x0C, 0x02, + 0xC9, 0x68, 0x39, 0xB1, 0x81, 0x42, 0xF9, 0xD1, 0xC9, 0x68, 0x11, 0x60, + 0x00, 0x21, 0x01, 0x61, 0x08, 0x46, 0x70, 0x47, 0x6F, 0xF0, 0x03, 0x00, + 0x70, 0x47, 0xF0, 0xB5, 0x00, 0x25, 0xDD, 0xF8, 0x14, 0xE0, 0x13, 0xE0, + 0x05, 0xFB, 0x03, 0xF6, 0x00, 0xEB, 0x86, 0x0C, 0x00, 0x24, 0x01, 0xEB, + 0x86, 0x06, 0x07, 0xE0, 0x5C, 0xF8, 0x24, 0x70, 0x97, 0xFB, 0xFE, 0xF7, + 0x46, 0xF8, 0x24, 0x70, 0x64, 0x1C, 0x64, 0xB2, 0x9C, 0x42, 0xF5, 0xDB, + 0x6D, 0x1C, 0x6D, 0xB2, 0x95, 0x42, 0xE9, 0xDB, 0xF0, 0xBD, 0xF0, 0xB5, + 0x00, 0x25, 0xDD, 0xF8, 0x14, 0xE0, 0x13, 0xE0, 0x05, 0xFB, 0x03, 0xF6, + 0x00, 0xEB, 0x86, 0x0C, 0x00, 0x24, 0x01, 0xEB, 0x86, 0x06, 0x07, 0xE0, + 0x5C, 0xF8, 0x24, 0x70, 0x07, 0xFB, 0x0E, 0xF7, 0x46, 0xF8, 0x24, 0x70, + 0x64, 0x1C, 0x64, 0xB2, 0x9C, 0x42, 0xF5, 0xDB, 0x6D, 0x1C, 0x6D, 0xB2, + 0x95, 0x42, 0xE9, 0xDB, 0xF0, 0xBD, 0x2D, 0xE9, 0xF0, 0x47, 0x9A, 0x46, + 0x91, 0x46, 0x00, 0x23, 0x08, 0x9E, 0x16, 0xE0, 0x03, 0xFB, 0x06, 0xF4, + 0x00, 0xEB, 0x84, 0x07, 0x01, 0xEB, 0x84, 0x05, 0x00, 0x22, 0x09, 0xEB, + 0x84, 0x04, 0x08, 0xE0, 0x57, 0xF8, 0x22, 0xC0, 0x55, 0xF8, 0x22, 0x80, + 0xC4, 0x44, 0x44, 0xF8, 0x22, 0xC0, 0x52, 0x1C, 0x52, 0xB2, 0xB2, 0x42, + 0xF4, 0xDB, 0x5B, 0x1C, 0x5B, 0xB2, 0x53, 0x45, 0xE6, 0xDB, 0xBD, 0xE8, + 0xF0, 0x87, 0x2D, 0xE9, 0xF0, 0x47, 0x9A, 0x46, 0x91, 0x46, 0x00, 0x23, + 0x08, 0x9E, 0x17, 0xE0, 0x03, 0xFB, 0x06, 0xF4, 0x00, 0xEB, 0x84, 0x07, + 0x01, 0xEB, 0x84, 0x05, 0x00, 0x22, 0x09, 0xEB, 0x84, 0x04, 0x09, 0xE0, + 0x57, 0xF8, 0x22, 0xC0, 0x55, 0xF8, 0x22, 0x80, 0xAC, 0xEB, 0x08, 0x0C, + 0x44, 0xF8, 0x22, 0xC0, 0x52, 0x1C, 0x52, 0xB2, 0xB2, 0x42, 0xF3, 0xDB, + 0x5B, 0x1C, 0x5B, 0xB2, 0x53, 0x45, 0xE5, 0xDB, 0xDB, 0xE7, 0x2D, 0xE9, + 0xF0, 0x4F, 0x82, 0x46, 0xDD, 0xE9, 0x09, 0x06, 0x00, 0x24, 0xDD, 0xF8, + 0x34, 0xE0, 0x8B, 0x46, 0x82, 0x42, 0x23, 0xD0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x04, 0xFB, 0x02, 0xF1, 0x0A, 0xEB, 0x81, 0x09, 0x0B, 0x9D, 0x04, 0xFB, + 0x0E, 0xF1, 0x00, 0x20, 0x05, 0xEB, 0x81, 0x05, 0x13, 0xE0, 0x00, 0x21, + 0x0E, 0xE0, 0x01, 0xFB, 0x06, 0xF8, 0x03, 0xEB, 0x88, 0x08, 0x59, 0xF8, + 0x21, 0xC0, 0x55, 0xF8, 0x20, 0x70, 0x58, 0xF8, 0x20, 0x80, 0x49, 0x1C, + 0x0C, 0xFB, 0x08, 0x77, 0x45, 0xF8, 0x20, 0x70, 0x91, 0x42, 0xEE, 0xDB, + 0x40, 0x1C, 0xB0, 0x42, 0xE9, 0xDB, 0x64, 0x1C, 0x5C, 0x45, 0xDB, 0xDB, + 0xD8, 0xE7, 0xB2, 0xF1, 0x20, 0x03, 0x0A, 0xD5, 0xC2, 0xF1, 0x20, 0x03, + 0x01, 0xFA, 0x02, 0xF1, 0x20, 0xFA, 0x03, 0xF3, 0x00, 0xFA, 0x02, 0xF0, + 0x41, 0xEA, 0x03, 0x01, 0x70, 0x47, 0x00, 0xFA, 0x03, 0xF1, 0x4F, 0xF0, + 0x00, 0x00, 0x70, 0x47, 0x10, 0xB5, 0x4C, 0x10, 0x84, 0xEA, 0x53, 0x04, + 0x04, 0xD5, 0x40, 0x42, 0xC1, 0xF1, 0x00, 0x01, 0x38, 0xBF, 0x49, 0x1E, + 0x1B, 0x42, 0x04, 0xD5, 0x52, 0x42, 0xC3, 0xF1, 0x00, 0x03, 0x38, 0xBF, + 0x5B, 0x1E, 0x00, 0xF0, 0xD9, 0xF8, 0x14, 0xF0, 0x80, 0x4F, 0x04, 0xD0, + 0x40, 0x42, 0xC1, 0xF1, 0x00, 0x01, 0x38, 0xBF, 0x49, 0x1E, 0x14, 0xF0, + 0x00, 0x4F, 0x04, 0xD0, 0x52, 0x42, 0xC3, 0xF1, 0x00, 0x03, 0x38, 0xBF, + 0x5B, 0x1E, 0x10, 0xBD, 0x03, 0x2A, 0x40, 0xF2, 0x30, 0x80, 0x10, 0xF0, + 0x03, 0x0C, 0x00, 0xF0, 0x15, 0x80, 0x11, 0xF8, 0x01, 0x3B, 0xBC, 0xF1, + 0x02, 0x0F, 0x62, 0x44, 0x98, 0xBF, 0x11, 0xF8, 0x01, 0xCB, 0x00, 0xF8, + 0x01, 0x3B, 0x38, 0xBF, 0x11, 0xF8, 0x01, 0x3B, 0xA2, 0xF1, 0x04, 0x02, + 0x98, 0xBF, 0x00, 0xF8, 0x01, 0xCB, 0x38, 0xBF, 0x00, 0xF8, 0x01, 0x3B, + 0x11, 0xF0, 0x03, 0x03, 0x00, 0xF0, 0x25, 0x80, 0x08, 0x3A, 0xC0, 0xF0, + 0x08, 0x80, 0x51, 0xF8, 0x04, 0x3B, 0x08, 0x3A, 0x51, 0xF8, 0x04, 0xCB, + 0xA0, 0xE8, 0x08, 0x10, 0xF5, 0xE7, 0x12, 0x1D, 0x5C, 0xBF, 0x51, 0xF8, + 0x04, 0x3B, 0x40, 0xF8, 0x04, 0x3B, 0xAF, 0xF3, 0x00, 0x80, 0xD2, 0x07, + 0x24, 0xBF, 0x11, 0xF8, 0x01, 0x3B, 0x11, 0xF8, 0x01, 0xCB, 0x48, 0xBF, + 0x11, 0xF8, 0x01, 0x2B, 0x24, 0xBF, 0x00, 0xF8, 0x01, 0x3B, 0x00, 0xF8, + 0x01, 0xCB, 0x48, 0xBF, 0x00, 0xF8, 0x01, 0x2B, 0x70, 0x47, 0x10, 0xB5, + 0x20, 0x3A, 0xC0, 0xF0, 0x0B, 0x80, 0xB1, 0xE8, 0x18, 0x50, 0x20, 0x3A, + 0xA0, 0xE8, 0x18, 0x50, 0xB1, 0xE8, 0x18, 0x50, 0xA0, 0xE8, 0x18, 0x50, + 0xBF, 0xF4, 0xF5, 0xAF, 0x5F, 0xEA, 0x02, 0x7C, 0x24, 0xBF, 0xB1, 0xE8, + 0x18, 0x50, 0xA0, 0xE8, 0x18, 0x50, 0x44, 0xBF, 0x18, 0xC9, 0x18, 0xC0, + 0xBD, 0xE8, 0x10, 0x40, 0x5F, 0xEA, 0x82, 0x7C, 0x24, 0xBF, 0x51, 0xF8, + 0x04, 0x3B, 0x40, 0xF8, 0x04, 0x3B, 0x08, 0xBF, 0x70, 0x47, 0xD2, 0x07, + 0x28, 0xBF, 0x31, 0xF8, 0x02, 0x3B, 0x48, 0xBF, 0x11, 0xF8, 0x01, 0x2B, + 0x28, 0xBF, 0x20, 0xF8, 0x02, 0x3B, 0x48, 0xBF, 0x00, 0xF8, 0x01, 0x2B, + 0x70, 0x47, 0x02, 0xF0, 0xFF, 0x03, 0x43, 0xEA, 0x03, 0x22, 0x42, 0xEA, + 0x02, 0x42, 0x00, 0xF0, 0x02, 0xB8, 0x4F, 0xF0, 0x00, 0x02, 0x04, 0x29, + 0xC0, 0xF0, 0x12, 0x80, 0x10, 0xF0, 0x03, 0x0C, 0x00, 0xF0, 0x1B, 0x80, + 0xCC, 0xF1, 0x04, 0x0C, 0xBC, 0xF1, 0x02, 0x0F, 0x18, 0xBF, 0x00, 0xF8, + 0x01, 0x2B, 0xA8, 0xBF, 0x20, 0xF8, 0x02, 0x2B, 0xA1, 0xEB, 0x0C, 0x01, + 0x00, 0xF0, 0x0D, 0xB8, 0x5F, 0xEA, 0xC1, 0x7C, 0x24, 0xBF, 0x00, 0xF8, + 0x01, 0x2B, 0x00, 0xF8, 0x01, 0x2B, 0x48, 0xBF, 0x00, 0xF8, 0x01, 0x2B, + 0x70, 0x47, 0x4F, 0xF0, 0x00, 0x02, 0x00, 0xB5, 0x13, 0x46, 0x94, 0x46, + 0x96, 0x46, 0x20, 0x39, 0x22, 0xBF, 0xA0, 0xE8, 0x0C, 0x50, 0xA0, 0xE8, + 0x0C, 0x50, 0xB1, 0xF1, 0x20, 0x01, 0xBF, 0xF4, 0xF7, 0xAF, 0x09, 0x07, + 0x28, 0xBF, 0xA0, 0xE8, 0x0C, 0x50, 0x48, 0xBF, 0x0C, 0xC0, 0x5D, 0xF8, + 0x04, 0xEB, 0x89, 0x00, 0x28, 0xBF, 0x40, 0xF8, 0x04, 0x2B, 0x08, 0xBF, + 0x70, 0x47, 0x48, 0xBF, 0x20, 0xF8, 0x02, 0x2B, 0x11, 0xF0, 0x80, 0x4F, + 0x18, 0xBF, 0x00, 0xF8, 0x01, 0x2B, 0x70, 0x47, 0x53, 0xEA, 0x02, 0x0C, + 0x00, 0xF0, 0x69, 0x80, 0x2D, 0xE9, 0xF0, 0x4B, 0x4F, 0xF0, 0x00, 0x06, + 0x00, 0x2B, 0x1F, 0xBF, 0xB3, 0xFA, 0x83, 0xF5, 0x03, 0xFA, 0x05, 0xF4, + 0x24, 0xFA, 0x05, 0xF6, 0x5E, 0x40, 0x12, 0xBF, 0x16, 0x43, 0xB2, 0xFA, + 0x82, 0xF5, 0x02, 0xFA, 0x05, 0xF4, 0xC5, 0xF1, 0x20, 0x05, 0x1E, 0xBF, + 0x22, 0xFA, 0x05, 0xFC, 0x44, 0xEA, 0x0C, 0x04, 0x20, 0x35, 0x56, 0xEA, + 0x04, 0x4C, 0x4F, 0xEA, 0x14, 0x44, 0x18, 0xBF, 0x64, 0x1C, 0x4F, 0xF0, + 0x00, 0x08, 0x4F, 0xF0, 0x00, 0x09, 0x90, 0x42, 0x71, 0xEB, 0x03, 0x0C, + 0x39, 0xD3, 0x00, 0x29, 0x19, 0xBF, 0xB1, 0xFA, 0x81, 0xF7, 0x01, 0xFA, + 0x07, 0xF6, 0xB0, 0xFA, 0x80, 0xF7, 0x00, 0xFA, 0x07, 0xF6, 0xC7, 0xF1, + 0x20, 0x07, 0x1E, 0xBF, 0x20, 0xFA, 0x07, 0xFC, 0x46, 0xEA, 0x0C, 0x06, + 0x20, 0x37, 0xB6, 0xFB, 0xF4, 0xFC, 0xA7, 0xEB, 0x05, 0x07, 0x10, 0x3F, + 0x07, 0xF0, 0x1F, 0x0B, 0xCB, 0xF1, 0x20, 0x06, 0x0C, 0xFA, 0x0B, 0xFB, + 0x2C, 0xFA, 0x06, 0xF6, 0x44, 0xBF, 0xB3, 0x46, 0x00, 0x26, 0x20, 0x2F, + 0xA4, 0xBF, 0x5E, 0x46, 0x4F, 0xF0, 0x00, 0x0B, 0x5B, 0xEA, 0x06, 0x0C, + 0x08, 0xBF, 0x4F, 0xF0, 0x01, 0x0B, 0x19, 0xEB, 0x0B, 0x09, 0xAB, 0xFB, + 0x02, 0x7C, 0x48, 0xEB, 0x06, 0x08, 0xC0, 0x1B, 0x06, 0xFB, 0x02, 0xCC, + 0x0B, 0xFB, 0x03, 0xCC, 0x71, 0xEB, 0x0C, 0x01, 0xC1, 0xE7, 0x0B, 0x46, + 0x02, 0x46, 0x41, 0x46, 0x48, 0x46, 0xBD, 0xE8, 0xF0, 0x8B, 0x13, 0xB5, + 0x4F, 0xF0, 0x00, 0x00, 0x4F, 0xF0, 0x00, 0x01, 0xAF, 0xF3, 0x00, 0x80, + 0xBD, 0xE8, 0x1C, 0x40, 0x70, 0x47, 0x04, 0x46, 0xAF, 0xF3, 0x00, 0x80, + 0x20, 0x46, 0xF0, 0xF7, 0xAB, 0xFA, 0x70, 0x47, 0x01, 0x49, 0x18, 0x20, + 0xAB, 0xBE, 0xFE, 0xE7, 0x26, 0x00, 0x02, 0x00, 0x2D, 0xE9, 0xF0, 0x47, + 0x88, 0xB0, 0x16, 0x46, 0x8A, 0x46, 0x05, 0x46, 0x1F, 0x46, 0xFF, 0x22, + 0x1A, 0x21, 0x68, 0x46, 0xFF, 0xF7, 0x1F, 0xFF, 0x00, 0x24, 0x22, 0x46, + 0x01, 0x20, 0x00, 0x21, 0xFF, 0xF7, 0x6B, 0xFE, 0x80, 0x46, 0x89, 0x46, + 0x30, 0x40, 0x39, 0x40, 0x08, 0x43, 0x0C, 0xD0, 0x00, 0x23, 0x22, 0x46, + 0x19, 0x46, 0x68, 0x46, 0x00, 0xF0, 0x49, 0xF9, 0x26, 0xEA, 0x08, 0x06, + 0x27, 0xEA, 0x09, 0x07, 0x56, 0xEA, 0x07, 0x00, 0x02, 0xD0, 0x64, 0x1C, + 0x40, 0x2C, 0xE4, 0xD3, 0x00, 0x22, 0x10, 0x46, 0x4F, 0xF0, 0x01, 0x08, + 0xEC, 0x46, 0x08, 0xFA, 0x02, 0xF1, 0x11, 0xEA, 0x0A, 0x0F, 0x0F, 0xD0, + 0x00, 0xEB, 0x80, 0x03, 0x00, 0x21, 0x03, 0xEB, 0xC0, 0x07, 0x01, 0xEB, + 0x47, 0x03, 0x1C, 0xF8, 0x01, 0x60, 0xEC, 0x5C, 0x49, 0x1C, 0x34, 0x40, + 0xEC, 0x54, 0x1A, 0x29, 0xF5, 0xD3, 0x40, 0x1C, 0x52, 0x1C, 0x20, 0x2A, + 0xE7, 0xD3, 0x08, 0xB0, 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xF0, 0x47, + 0x00, 0x25, 0x99, 0x46, 0x16, 0x46, 0x0F, 0x46, 0x82, 0x46, 0x2C, 0x46, + 0x4F, 0xF0, 0x01, 0x08, 0x10, 0xE0, 0x07, 0xEB, 0xE4, 0x00, 0x04, 0xF0, + 0x07, 0x01, 0x00, 0x78, 0x08, 0xFA, 0x01, 0xF2, 0x02, 0x42, 0x06, 0xD0, + 0x4B, 0x46, 0x00, 0x22, 0x21, 0x46, 0x50, 0x46, 0x00, 0xF0, 0x07, 0xF9, + 0x6D, 0x1C, 0x64, 0x1C, 0xB5, 0x42, 0xEC, 0xDB, 0xBD, 0xE8, 0xF0, 0x87, + 0x30, 0xB5, 0x11, 0x24, 0x92, 0xFB, 0xF4, 0xF3, 0x03, 0xEB, 0x83, 0x05, + 0x05, 0xEB, 0xC3, 0x05, 0x92, 0xFB, 0xF4, 0xF3, 0x04, 0xFB, 0x13, 0x22, + 0xD3, 0x17, 0x02, 0xEB, 0x93, 0x73, 0x9C, 0x10, 0x23, 0xF0, 0x03, 0x03, + 0xD2, 0x1A, 0x01, 0xEB, 0x81, 0x03, 0x04, 0xEB, 0x44, 0x04, 0x03, 0xEB, + 0xC1, 0x01, 0x2C, 0x44, 0x00, 0xEB, 0x41, 0x00, 0x12, 0xF0, 0xFF, 0x01, + 0x00, 0x59, 0x07, 0xD0, 0x01, 0x29, 0x08, 0xD0, 0x02, 0x29, 0x09, 0xD0, + 0x03, 0x29, 0x0A, 0xD0, 0x00, 0x20, 0x30, 0xBD, 0x00, 0xF0, 0x3F, 0x00, + 0x30, 0xBD, 0xC0, 0xF3, 0x85, 0x10, 0x30, 0xBD, 0xC0, 0xF3, 0x05, 0x30, + 0x30, 0xBD, 0xC0, 0xF3, 0x85, 0x40, 0x30, 0xBD, 0xFF, 0xF7, 0xCA, 0xBF, + 0x0A, 0x46, 0x00, 0x21, 0xFF, 0xF7, 0xC6, 0xBF, 0x40, 0x5C, 0x70, 0x47, + 0x2D, 0xE9, 0xF0, 0x5F, 0x00, 0x25, 0x17, 0x46, 0x89, 0x46, 0x80, 0x46, + 0x2C, 0x46, 0x11, 0x4E, 0xAA, 0x46, 0x4F, 0xF0, 0x5D, 0x0B, 0x18, 0xE0, + 0x00, 0x20, 0x51, 0x46, 0xBC, 0x42, 0x05, 0xDC, 0x83, 0x19, 0x18, 0xF8, + 0x04, 0x20, 0x83, 0xF8, 0x60, 0x20, 0x02, 0xE0, 0x82, 0x19, 0x82, 0xF8, + 0x60, 0x10, 0x40, 0x1C, 0x64, 0x1C, 0x0D, 0x28, 0xF0, 0xDB, 0x01, 0xF0, + 0x6D, 0xF9, 0x09, 0xEB, 0x05, 0x00, 0xB0, 0x76, 0x86, 0xF8, 0x1B, 0xB0, + 0x6D, 0x1C, 0xBC, 0x42, 0xE4, 0xDB, 0xBD, 0xE8, 0xF0, 0x9F, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x20, 0x2D, 0xE9, 0xF0, 0x47, 0x20, 0x27, 0x00, 0x25, + 0x88, 0x46, 0x06, 0x46, 0x2C, 0x46, 0x4F, 0xF0, 0x01, 0x09, 0x09, 0xFA, + 0x04, 0xF0, 0x10, 0xEA, 0x08, 0x0F, 0x06, 0xD0, 0xE9, 0xB2, 0x1A, 0x22, + 0x30, 0x46, 0xFF, 0xF7, 0xC1, 0xFF, 0xAD, 0x1C, 0x1A, 0x36, 0x64, 0x1C, + 0xBC, 0x42, 0xF0, 0xDB, 0xBD, 0xE8, 0xF0, 0x87, 0x18, 0x22, 0x80, 0x21, + 0xFF, 0xF7, 0xB6, 0xBF, 0x20, 0x22, 0x82, 0x21, 0xAF, 0xF3, 0x00, 0x80, + 0x2D, 0xE9, 0xF7, 0x4F, 0x00, 0x27, 0x93, 0x46, 0x81, 0x46, 0x3E, 0x46, + 0x3C, 0x46, 0x08, 0x25, 0xB8, 0x46, 0xDF, 0xF8, 0x58, 0xA0, 0x25, 0xE0, + 0x00, 0x20, 0x01, 0x46, 0x5C, 0x45, 0x0A, 0xDC, 0x19, 0xF8, 0x04, 0x20, + 0xEE, 0x40, 0x02, 0xFA, 0x08, 0xF2, 0x16, 0x43, 0x00, 0xEB, 0x0A, 0x02, + 0x82, 0xF8, 0x60, 0x60, 0x03, 0xE0, 0x00, 0xEB, 0x0A, 0x02, 0x82, 0xF8, + 0x60, 0x10, 0x19, 0xF8, 0x04, 0x60, 0x40, 0x1C, 0x64, 0x1C, 0x0D, 0x28, + 0xE8, 0xDB, 0xAD, 0x1E, 0xC5, 0xF1, 0x08, 0x08, 0x01, 0xF0, 0x14, 0xF9, + 0x01, 0x98, 0xC1, 0x19, 0x8A, 0xF8, 0x1A, 0x10, 0x5D, 0x21, 0x8A, 0xF8, + 0x1B, 0x10, 0x7F, 0x1C, 0x5C, 0x45, 0xD7, 0xDB, 0xBD, 0xE8, 0xFE, 0x8F, + 0x00, 0x00, 0x07, 0x20, 0x1A, 0x22, 0x85, 0x21, 0xFF, 0xF7, 0x76, 0xBF, + 0x22, 0x22, 0x87, 0x21, 0xFF, 0xF7, 0xC0, 0xBF, 0x70, 0xB5, 0x11, 0x25, + 0x92, 0xFB, 0xF5, 0xF4, 0x04, 0xEB, 0x84, 0x06, 0x06, 0xEB, 0xC4, 0x06, + 0x92, 0xFB, 0xF5, 0xF4, 0x05, 0xFB, 0x14, 0x22, 0xD4, 0x17, 0x02, 0xEB, + 0x94, 0x74, 0xA5, 0x10, 0x24, 0xF0, 0x03, 0x04, 0x14, 0x1B, 0x05, 0xEB, + 0x45, 0x05, 0x01, 0xEB, 0x81, 0x02, 0x35, 0x44, 0x02, 0xEB, 0xC1, 0x01, + 0x05, 0xEB, 0x41, 0x01, 0x0A, 0x18, 0x14, 0xF0, 0xFF, 0x01, 0x10, 0x68, + 0x06, 0xD0, 0x01, 0x29, 0x07, 0xD0, 0x02, 0x29, 0x08, 0xD0, 0x03, 0x29, + 0x0B, 0xD1, 0x08, 0xE0, 0x63, 0xF3, 0x05, 0x00, 0x07, 0xE0, 0x63, 0xF3, + 0x8B, 0x10, 0x04, 0xE0, 0x63, 0xF3, 0x11, 0x30, 0x01, 0xE0, 0x63, 0xF3, + 0x97, 0x40, 0x10, 0x60, 0x70, 0xBD, 0xFF, 0xF7, 0xC9, 0xBF, 0x13, 0x46, + 0x0A, 0x46, 0x00, 0x21, 0xFF, 0xF7, 0xC4, 0xBF, 0x42, 0x54, 0x70, 0x47, + 0xFE, 0xB5, 0x04, 0x46, 0x00, 0x23, 0x01, 0x22, 0x01, 0xA9, 0x18, 0x46, + 0x00, 0xF0, 0x68, 0xFE, 0x9D, 0xF8, 0x04, 0x10, 0x01, 0x20, 0x21, 0xF0, + 0x07, 0x01, 0xA0, 0x40, 0x8D, 0xF8, 0x04, 0x10, 0x02, 0x90, 0x04, 0x22, + 0x02, 0xA9, 0x2B, 0x20, 0x01, 0xF0, 0xD2, 0xF8, 0x9D, 0xF8, 0x04, 0x00, + 0x01, 0x22, 0x40, 0xF0, 0x06, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x69, 0x46, + 0x00, 0x20, 0x01, 0xF0, 0xC7, 0xF8, 0x17, 0x4E, 0xE0, 0x25, 0x35, 0x73, + 0x4F, 0xF4, 0x20, 0x77, 0x38, 0x46, 0x01, 0xF0, 0x8B, 0xFE, 0x00, 0x23, + 0x01, 0x22, 0x69, 0x46, 0x89, 0x20, 0x00, 0xF0, 0x41, 0xFE, 0x9D, 0xF8, + 0x00, 0x00, 0x01, 0x22, 0x00, 0xF0, 0x01, 0x04, 0x9D, 0xF8, 0x04, 0x00, + 0x69, 0x46, 0x40, 0xF0, 0x07, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x00, 0x20, + 0x01, 0xF0, 0xAA, 0xF8, 0x35, 0x73, 0x38, 0x46, 0x01, 0xF0, 0x72, 0xFE, + 0x00, 0x23, 0x01, 0x22, 0x69, 0x46, 0x89, 0x20, 0x00, 0xF0, 0x28, 0xFE, + 0x9D, 0xF8, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x44, 0xEA, 0x40, 0x00, + 0xFE, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, 0x2D, 0xE9, 0xFF, 0x41, + 0x04, 0x46, 0x00, 0x23, 0x01, 0x22, 0x01, 0xA9, 0x18, 0x46, 0x00, 0xF0, + 0x15, 0xFE, 0x9D, 0xF8, 0x04, 0x10, 0x22, 0x46, 0x21, 0xF0, 0x07, 0x01, + 0x8D, 0xF8, 0x04, 0x10, 0x01, 0x20, 0x00, 0x21, 0xFF, 0xF7, 0xA7, 0xFC, + 0xCD, 0xE9, 0x02, 0x01, 0x05, 0x22, 0x02, 0xA9, 0x96, 0x20, 0x01, 0xF0, + 0x7B, 0xF8, 0x9D, 0xF8, 0x04, 0x00, 0x01, 0x22, 0x40, 0xF0, 0x06, 0x00, + 0x8D, 0xF8, 0x00, 0x00, 0x69, 0x46, 0x00, 0x20, 0x01, 0xF0, 0x70, 0xF8, + 0x17, 0x4E, 0xE0, 0x25, 0x35, 0x73, 0x4F, 0xF4, 0x20, 0x77, 0x38, 0x46, + 0x01, 0xF0, 0x34, 0xFE, 0x00, 0x23, 0x01, 0x22, 0x69, 0x46, 0x89, 0x20, + 0x00, 0xF0, 0xEA, 0xFD, 0x9D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x00, 0xF0, + 0x01, 0x04, 0x9D, 0xF8, 0x04, 0x00, 0x69, 0x46, 0x40, 0xF0, 0x07, 0x00, + 0x8D, 0xF8, 0x00, 0x00, 0x00, 0x20, 0x01, 0xF0, 0x53, 0xF8, 0x35, 0x73, + 0x38, 0x46, 0x01, 0xF0, 0x1B, 0xFE, 0x00, 0x23, 0x01, 0x22, 0x69, 0x46, + 0x89, 0x20, 0x00, 0xF0, 0xD1, 0xFD, 0x9D, 0xF8, 0x00, 0x00, 0x04, 0xB0, + 0x00, 0xF0, 0x01, 0x00, 0x44, 0xEA, 0x40, 0x00, 0xBD, 0xE8, 0xF0, 0x81, + 0x00, 0x00, 0x07, 0x20, 0x1C, 0xB5, 0x00, 0x23, 0x01, 0x22, 0x01, 0xA9, + 0x18, 0x46, 0x00, 0xF0, 0xBF, 0xFD, 0x9D, 0xF8, 0x04, 0x00, 0x01, 0x22, + 0x20, 0xF0, 0x07, 0x00, 0x8D, 0xF8, 0x04, 0x00, 0x01, 0xA9, 0x00, 0x20, + 0x01, 0xF0, 0x2C, 0xF8, 0x00, 0x20, 0x00, 0x90, 0x04, 0x22, 0x69, 0x46, + 0x2B, 0x20, 0x01, 0xF0, 0x25, 0xF8, 0x04, 0x22, 0x69, 0x46, 0x96, 0x20, + 0x01, 0xF0, 0x20, 0xF8, 0x01, 0x22, 0x69, 0x46, 0x9A, 0x20, 0x01, 0xF0, + 0x1B, 0xF8, 0x1C, 0xBD, 0x34, 0x21, 0x01, 0x70, 0x0C, 0x21, 0x41, 0x70, + 0x3C, 0x21, 0x81, 0x70, 0x40, 0x21, 0xC1, 0x70, 0x1F, 0x21, 0xC1, 0x71, + 0x01, 0x21, 0x01, 0x72, 0x02, 0x22, 0x42, 0x72, 0x81, 0x72, 0x00, 0x21, + 0xC1, 0x72, 0x41, 0x73, 0x5C, 0x21, 0x81, 0x73, 0x70, 0x47, 0x01, 0xB5, + 0x01, 0x22, 0x69, 0x46, 0x0B, 0x20, 0x00, 0xF0, 0xFF, 0xFF, 0x08, 0xBD, + 0x01, 0x48, 0x01, 0x68, 0x01, 0x20, 0x08, 0x47, 0x70, 0x86, 0x10, 0x00, + 0x0E, 0x48, 0x10, 0xB5, 0x4F, 0xF0, 0x00, 0x52, 0x81, 0x88, 0xA2, 0xF8, + 0xEB, 0x10, 0x90, 0xF8, 0x34, 0x00, 0xE1, 0x24, 0x80, 0x07, 0x00, 0xD5, + 0xE3, 0x24, 0x09, 0x49, 0x03, 0x20, 0x08, 0x70, 0x09, 0x49, 0x08, 0x48, + 0x08, 0x60, 0x00, 0xF0, 0xB3, 0xFF, 0x08, 0x48, 0x04, 0x73, 0xBD, 0xE8, + 0x10, 0x40, 0x07, 0x49, 0x32, 0x20, 0x01, 0xF0, 0x99, 0xBA, 0x00, 0x00, + 0x70, 0x86, 0x10, 0x00, 0x26, 0x25, 0x10, 0x00, 0xE5, 0x01, 0x01, 0x00, + 0x2C, 0x25, 0x10, 0x00, 0x00, 0x00, 0x07, 0x20, 0x39, 0x01, 0x01, 0x00, + 0x01, 0x49, 0x48, 0x60, 0xFF, 0xF7, 0xD2, 0xBF, 0x70, 0x86, 0x10, 0x00, + 0x38, 0xB5, 0x07, 0x4A, 0x04, 0x46, 0x0C, 0x20, 0xD1, 0x76, 0x8D, 0xF8, + 0x00, 0x10, 0x01, 0x22, 0x69, 0x46, 0x00, 0xF0, 0xBD, 0xFF, 0x20, 0x46, + 0xFF, 0xF7, 0xEC, 0xFF, 0x38, 0xBD, 0x00, 0x00, 0x70, 0x86, 0x10, 0x00, + 0x10, 0xB5, 0x04, 0x46, 0x04, 0x48, 0x01, 0x76, 0x08, 0x46, 0x00, 0xF0, + 0xCC, 0xFD, 0x20, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0xFF, 0xF7, 0xDC, 0xBF, + 0x70, 0x86, 0x10, 0x00, 0x10, 0xB5, 0x01, 0xF0, 0x7B, 0xFA, 0x06, 0x49, + 0x02, 0x20, 0x08, 0x70, 0x05, 0x49, 0x00, 0x20, 0x08, 0x60, 0x05, 0x48, + 0x01, 0x68, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x20, 0x08, 0x47, 0x00, 0x00, + 0x26, 0x25, 0x10, 0x00, 0x2C, 0x25, 0x10, 0x00, 0x70, 0x86, 0x10, 0x00, + 0x10, 0xB5, 0x01, 0x46, 0x40, 0x22, 0x04, 0x48, 0xFF, 0xF7, 0x2D, 0xFC, + 0x00, 0xF0, 0x46, 0xF8, 0xBD, 0xE8, 0x10, 0x40, 0xFF, 0xF7, 0x8E, 0xBF, + 0x70, 0x86, 0x10, 0x00, 0x7C, 0xB5, 0x1C, 0x4E, 0x04, 0x21, 0xB0, 0x68, + 0xC0, 0x00, 0x80, 0x08, 0x01, 0x90, 0x01, 0xA8, 0x01, 0xF0, 0x30, 0xFD, + 0x18, 0x4D, 0x04, 0x46, 0x01, 0x98, 0x68, 0x73, 0x01, 0x0A, 0xA9, 0x73, + 0x01, 0x0C, 0xE9, 0x73, 0x00, 0x0E, 0x28, 0x74, 0x04, 0x22, 0x01, 0xA9, + 0x2B, 0x20, 0x00, 0xF0, 0x6B, 0xFF, 0x70, 0x6B, 0xC1, 0x07, 0x03, 0xD0, + 0x60, 0x1C, 0x64, 0x1C, 0x00, 0x94, 0x0F, 0xE0, 0x01, 0x07, 0x0B, 0xD5, + 0xE1, 0x1C, 0x21, 0xF0, 0x01, 0x01, 0x00, 0x91, 0xC0, 0x06, 0x03, 0xD5, + 0x64, 0x1C, 0x24, 0xF0, 0x01, 0x00, 0x03, 0xE0, 0x08, 0x46, 0x01, 0xE0, + 0x20, 0x46, 0x00, 0x94, 0xA8, 0x74, 0x06, 0x49, 0x01, 0x22, 0x08, 0x70, + 0x69, 0x46, 0x2F, 0x20, 0x00, 0xF0, 0x4C, 0xFF, 0x7C, 0xBD, 0x00, 0x00, + 0x70, 0x86, 0x10, 0x00, 0x00, 0x00, 0x07, 0x20, 0x30, 0x25, 0x10, 0x00, + 0xF0, 0xB5, 0x85, 0xB0, 0x6B, 0x4C, 0xFF, 0xF7, 0xBB, 0xFF, 0x21, 0x46, + 0x4F, 0xF0, 0x00, 0x55, 0x08, 0x69, 0x03, 0x90, 0x09, 0x7D, 0x01, 0xF0, + 0x03, 0x01, 0x04, 0x91, 0x85, 0xF8, 0xE0, 0x00, 0x02, 0x0A, 0x85, 0xF8, + 0xE1, 0x20, 0x02, 0x0C, 0x85, 0xF8, 0xE2, 0x20, 0x00, 0x0E, 0x85, 0xF8, + 0xE3, 0x00, 0x85, 0xF8, 0xE4, 0x10, 0x04, 0x22, 0x03, 0xA9, 0x96, 0x20, + 0x00, 0xF0, 0x24, 0xFF, 0x01, 0x22, 0x04, 0xA9, 0x9A, 0x20, 0x00, 0xF0, + 0x1F, 0xFF, 0x94, 0xF8, 0x34, 0x10, 0x00, 0x20, 0xC9, 0x07, 0x00, 0xD0, + 0x80, 0x20, 0x8D, 0xF8, 0x00, 0x00, 0x94, 0xF8, 0x20, 0x00, 0x00, 0x27, + 0x00, 0xF0, 0x0F, 0x00, 0x40, 0xF0, 0xA0, 0x00, 0x8D, 0xF8, 0x01, 0x00, + 0x60, 0x7F, 0x8D, 0xF8, 0x02, 0x00, 0x94, 0xF8, 0x21, 0x00, 0x8D, 0xF8, + 0x03, 0x00, 0xA0, 0x7E, 0x8D, 0xF8, 0x04, 0x00, 0x8D, 0xF8, 0x05, 0x70, + 0x14, 0xF8, 0x19, 0x0F, 0x8D, 0xF8, 0x06, 0x00, 0x07, 0x22, 0x69, 0x46, + 0x05, 0x20, 0x00, 0xF0, 0xF9, 0xFE, 0xA0, 0x78, 0x8D, 0xF8, 0x00, 0x00, + 0x8D, 0xF8, 0x01, 0x70, 0xE0, 0x78, 0x8D, 0xF8, 0x02, 0x00, 0x8D, 0xF8, + 0x03, 0x70, 0x60, 0x7A, 0x30, 0x21, 0x01, 0xEA, 0x00, 0x10, 0xA1, 0x7A, + 0x09, 0x22, 0x40, 0xEA, 0x81, 0x10, 0xE1, 0x7A, 0x01, 0xF0, 0x03, 0x01, + 0x08, 0x43, 0x8D, 0xF8, 0x04, 0x00, 0xA1, 0x7B, 0x20, 0x7B, 0x01, 0xF0, + 0x0F, 0x01, 0x41, 0xEA, 0x80, 0x10, 0x8D, 0xF8, 0x05, 0x00, 0x8D, 0xF8, + 0x06, 0x70, 0x60, 0x7B, 0x8D, 0xF8, 0x07, 0x00, 0x60, 0x79, 0x8D, 0xF8, + 0x08, 0x00, 0x69, 0x46, 0x0C, 0x20, 0x00, 0xF0, 0xCD, 0xFE, 0x32, 0x48, + 0x00, 0x68, 0x80, 0x79, 0x00, 0xF0, 0x03, 0x00, 0x40, 0xF0, 0x40, 0x00, + 0x8D, 0xF8, 0x00, 0x00, 0xE1, 0x7E, 0x19, 0x3C, 0x49, 0x07, 0x03, 0xD5, + 0x40, 0xF0, 0x0C, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, + 0xB1, 0x20, 0x00, 0xF0, 0xB7, 0xFE, 0x8D, 0xF8, 0x00, 0x70, 0x94, 0xF8, + 0x34, 0x00, 0x4F, 0xF0, 0x01, 0x06, 0x00, 0x07, 0x01, 0xD5, 0x8D, 0xF8, + 0x00, 0x60, 0x01, 0x22, 0x69, 0x46, 0xAA, 0x20, 0x00, 0xF0, 0xA8, 0xFE, + 0x8D, 0xF8, 0x00, 0x70, 0x8D, 0xF8, 0x01, 0x70, 0x60, 0x6B, 0xC1, 0x07, + 0x05, 0xD1, 0x01, 0x07, 0x03, 0xD5, 0xC0, 0x06, 0x01, 0xD5, 0x8D, 0xF8, + 0x00, 0x60, 0x20, 0x20, 0x8D, 0xF8, 0x01, 0x00, 0x02, 0x22, 0x69, 0x46, + 0x30, 0x20, 0x00, 0xF0, 0x73, 0xFE, 0x94, 0xF8, 0x34, 0x00, 0x00, 0x07, + 0x04, 0xD5, 0x95, 0xF8, 0xDF, 0x00, 0x20, 0xF0, 0xF0, 0x00, 0x05, 0xE0, + 0x94, 0xF8, 0x20, 0x10, 0x95, 0xF8, 0xDF, 0x00, 0x61, 0xF3, 0x07, 0x10, + 0x85, 0xF8, 0xDF, 0x00, 0xE0, 0x7F, 0x85, 0xF8, 0xED, 0x00, 0x60, 0x8E, + 0xF0, 0x21, 0x00, 0xF5, 0x80, 0x70, 0x01, 0xEA, 0x10, 0x10, 0x8D, 0xF8, + 0x00, 0x00, 0x00, 0x09, 0x8D, 0xF8, 0x01, 0x00, 0x02, 0x22, 0x69, 0x46, + 0x40, 0x20, 0x00, 0xF0, 0x4F, 0xFE, 0x20, 0x7E, 0x00, 0xF0, 0x89, 0xFC, + 0x05, 0xB0, 0xF0, 0xBD, 0x70, 0x86, 0x10, 0x00, 0x28, 0x25, 0x10, 0x00, + 0x02, 0x48, 0x00, 0x78, 0x00, 0x28, 0x00, 0xD0, 0x01, 0x20, 0x70, 0x47, + 0x26, 0x25, 0x10, 0x00, 0x38, 0xB5, 0x08, 0x49, 0x04, 0x20, 0x08, 0x73, + 0x00, 0x24, 0x8D, 0xF8, 0x00, 0x40, 0x01, 0x22, 0x69, 0x46, 0x03, 0x20, + 0x00, 0xF0, 0x52, 0xFE, 0x03, 0x48, 0x04, 0x70, 0x03, 0x48, 0x04, 0x60, + 0x38, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, 0x26, 0x25, 0x10, 0x00, + 0x34, 0x25, 0x10, 0x00, 0x05, 0x4A, 0x01, 0x21, 0x11, 0x70, 0x05, 0x49, + 0x08, 0x60, 0x05, 0x49, 0xE4, 0x20, 0x08, 0x73, 0x04, 0x49, 0x03, 0x20, + 0x01, 0xF0, 0x42, 0xBA, 0x26, 0x25, 0x10, 0x00, 0x34, 0x25, 0x10, 0x00, + 0x00, 0x00, 0x07, 0x20, 0xD1, 0x04, 0x01, 0x00, 0x07, 0x48, 0x01, 0x78, + 0x01, 0x29, 0x08, 0xD1, 0x02, 0x21, 0x01, 0x70, 0x05, 0x48, 0x01, 0x68, + 0x19, 0xB1, 0x00, 0x22, 0x02, 0x60, 0x10, 0x46, 0x08, 0x47, 0x00, 0x20, + 0x00, 0xF0, 0xF0, 0xBD, 0x26, 0x25, 0x10, 0x00, 0x34, 0x25, 0x10, 0x00, + 0x01, 0x48, 0x01, 0x68, 0x01, 0x20, 0x08, 0x47, 0xB0, 0x86, 0x10, 0x00, + 0x0D, 0x4B, 0x30, 0xB4, 0x58, 0x78, 0x0D, 0x49, 0x0D, 0x4C, 0x01, 0x28, + 0x0D, 0xD0, 0x04, 0x22, 0x0A, 0x70, 0x0C, 0x49, 0x21, 0x60, 0x0C, 0x4A, + 0x19, 0x78, 0x11, 0x73, 0x00, 0x28, 0x08, 0xD1, 0x30, 0xBC, 0x0A, 0x49, + 0x32, 0x20, 0x01, 0xF0, 0xC1, 0xB8, 0x00, 0x22, 0x0A, 0x70, 0x22, 0x60, + 0xF1, 0xE7, 0x30, 0xBC, 0x70, 0x47, 0x00, 0x00, 0x38, 0x25, 0x10, 0x00, + 0x26, 0x25, 0x10, 0x00, 0x2C, 0x25, 0x10, 0x00, 0x55, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x07, 0x20, 0xF9, 0x04, 0x01, 0x00, 0x10, 0xB5, 0x01, 0xF0, + 0xC3, 0xF8, 0x06, 0x49, 0x02, 0x20, 0x08, 0x70, 0x05, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x05, 0x48, 0x01, 0x68, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x20, + 0x08, 0x47, 0x00, 0x00, 0x26, 0x25, 0x10, 0x00, 0x2C, 0x25, 0x10, 0x00, + 0xB0, 0x86, 0x10, 0x00, 0x70, 0xB5, 0x0B, 0x4C, 0x05, 0x46, 0x0E, 0x46, + 0x60, 0x71, 0x50, 0x22, 0x09, 0x48, 0xFF, 0xF7, 0x72, 0xFA, 0x70, 0x6C, + 0x00, 0x03, 0x01, 0xD5, 0x01, 0x20, 0x00, 0xE0, 0x00, 0x20, 0x60, 0x70, + 0x28, 0x46, 0x00, 0xF0, 0x09, 0xF9, 0xBD, 0xE8, 0x70, 0x40, 0xFF, 0xF7, + 0xAB, 0xBF, 0x00, 0x00, 0x38, 0x25, 0x10, 0x00, 0xB0, 0x86, 0x10, 0x00, + 0x2D, 0xE9, 0xF0, 0x41, 0x7A, 0x4C, 0x88, 0xB0, 0x80, 0x46, 0x02, 0x25, + 0x20, 0x46, 0x8D, 0xF8, 0x0C, 0x50, 0x80, 0x68, 0x04, 0x21, 0xC0, 0x00, + 0x80, 0x08, 0x05, 0x90, 0x05, 0xA8, 0x01, 0xF0, 0x63, 0xFB, 0x74, 0x4A, + 0x06, 0x90, 0x05, 0x99, 0x51, 0x73, 0x0B, 0x0A, 0x93, 0x73, 0x0B, 0x0C, + 0xD3, 0x73, 0x09, 0x0E, 0x11, 0x74, 0x90, 0x74, 0x04, 0x22, 0x05, 0xA9, + 0x2B, 0x20, 0x00, 0xF0, 0x9D, 0xFD, 0x01, 0x22, 0x06, 0xA9, 0x2F, 0x20, + 0x00, 0xF0, 0x98, 0xFD, 0x21, 0x46, 0x2E, 0x07, 0x08, 0x69, 0x05, 0x90, + 0x09, 0x7D, 0x01, 0xF0, 0x03, 0x01, 0x06, 0x91, 0x06, 0xF8, 0xE0, 0x0F, + 0x02, 0x0A, 0x72, 0x70, 0x02, 0x0C, 0xB2, 0x70, 0x00, 0x0E, 0xF0, 0x70, + 0x31, 0x71, 0x04, 0x22, 0x05, 0xA9, 0x96, 0x20, 0x00, 0xF0, 0x82, 0xFD, + 0x01, 0x22, 0x06, 0xA9, 0x9A, 0x20, 0x00, 0xF0, 0x7D, 0xFD, 0xA0, 0x88, + 0xA6, 0xF8, 0x0B, 0x00, 0x00, 0x27, 0x8D, 0xF8, 0x00, 0x70, 0x94, 0xF8, + 0x22, 0x00, 0x8D, 0xF8, 0x01, 0x00, 0x94, 0xF8, 0x20, 0x00, 0x8D, 0xF8, + 0x02, 0x00, 0x03, 0x22, 0x69, 0x46, 0x05, 0x20, 0xE0, 0x3E, 0x00, 0xF0, + 0x69, 0xFD, 0x60, 0x8B, 0x8D, 0xF8, 0x00, 0x00, 0x00, 0x0A, 0x8D, 0xF8, + 0x01, 0x00, 0x02, 0x22, 0x69, 0x46, 0x09, 0x20, 0x00, 0xF0, 0x5E, 0xFD, + 0x60, 0x6C, 0xC0, 0xF3, 0x00, 0x41, 0xC0, 0xF3, 0x40, 0x42, 0xC9, 0x01, + 0x41, 0xEA, 0x82, 0x11, 0xC0, 0xF3, 0x80, 0x40, 0x01, 0x43, 0x41, 0xF0, + 0x08, 0x00, 0x41, 0x46, 0x8D, 0xF8, 0x10, 0x00, 0x4F, 0xF0, 0x01, 0x08, + 0x0B, 0x29, 0x1D, 0xD2, 0xDF, 0xE8, 0x01, 0xF0, 0x06, 0x06, 0x1C, 0x1C, + 0x09, 0x09, 0x1C, 0x1C, 0x1C, 0x0C, 0x0C, 0x00, 0x8D, 0xF8, 0x0C, 0x50, + 0x08, 0xE0, 0x40, 0xF0, 0x02, 0x00, 0x11, 0xE0, 0x40, 0xF0, 0x04, 0x00, + 0x8D, 0xF8, 0x10, 0x00, 0x8D, 0xF8, 0x0C, 0x80, 0x01, 0x22, 0x04, 0xA9, + 0x17, 0x20, 0x00, 0xF0, 0x31, 0xFD, 0x37, 0x48, 0x40, 0x78, 0x01, 0x28, + 0x05, 0xD0, 0x06, 0xE0, 0x40, 0xF0, 0x04, 0x00, 0x8D, 0xF8, 0x10, 0x00, + 0xE4, 0xE7, 0x8D, 0xF8, 0x0C, 0x80, 0x01, 0x22, 0x03, 0xA9, 0x18, 0x20, + 0x00, 0xF0, 0x20, 0xFD, 0x9D, 0xF8, 0x0C, 0x00, 0x86, 0xF8, 0xEA, 0x00, + 0x94, 0xF8, 0x2D, 0x00, 0x94, 0xF8, 0x2E, 0x10, 0x00, 0xF0, 0x0F, 0x00, + 0x40, 0xEA, 0x01, 0x10, 0x8D, 0xF8, 0x00, 0x00, 0xFF, 0x20, 0x8D, 0xF8, + 0x01, 0x00, 0xA0, 0x8B, 0x8D, 0xF8, 0x02, 0x00, 0x00, 0x0A, 0x8D, 0xF8, + 0x03, 0x00, 0xE0, 0x8B, 0x8D, 0xF8, 0x04, 0x00, 0x00, 0x0A, 0x8D, 0xF8, + 0x05, 0x00, 0x06, 0x22, 0x69, 0x46, 0x19, 0x20, 0x18, 0x34, 0xDF, 0x36, + 0x00, 0xF0, 0xFC, 0xFC, 0x1D, 0x48, 0x01, 0x22, 0x69, 0x46, 0x00, 0x68, + 0x80, 0x79, 0x00, 0xF0, 0x03, 0x00, 0x40, 0xF0, 0x40, 0x00, 0x8D, 0xF8, + 0x00, 0x00, 0xB1, 0x20, 0x00, 0xF0, 0xEE, 0xFC, 0xA1, 0x7A, 0x30, 0x78, + 0x61, 0xF3, 0x07, 0x10, 0x30, 0x70, 0x60, 0x7A, 0xB0, 0x73, 0x8D, 0xF8, + 0x00, 0x70, 0x20, 0x20, 0x8D, 0xF8, 0x01, 0x00, 0x02, 0x22, 0x69, 0x46, + 0x30, 0x20, 0x00, 0xF0, 0xBD, 0xFC, 0x60, 0x8D, 0xF0, 0x21, 0x00, 0xF5, + 0x80, 0x70, 0x01, 0xEA, 0x10, 0x10, 0x8D, 0xF8, 0x00, 0x00, 0x00, 0x09, + 0x8D, 0xF8, 0x01, 0x00, 0x02, 0x22, 0x69, 0x46, 0x40, 0x20, 0x00, 0xF0, + 0xAD, 0xFC, 0x20, 0x78, 0x00, 0xF0, 0xE7, 0xFA, 0x08, 0xB0, 0xBD, 0xE8, + 0xF0, 0x81, 0x00, 0x00, 0xB0, 0x86, 0x10, 0x00, 0x00, 0x00, 0x07, 0x20, + 0x38, 0x25, 0x10, 0x00, 0x28, 0x25, 0x10, 0x00, 0x3E, 0xB5, 0x27, 0x4D, + 0x04, 0x46, 0x68, 0x78, 0x01, 0x28, 0x0E, 0xD0, 0xE1, 0x20, 0x28, 0x70, + 0x20, 0x46, 0xFF, 0xF7, 0xF5, 0xFE, 0x0B, 0x2C, 0x11, 0xD2, 0xDF, 0xE8, + 0x04, 0xF0, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x10, + 0x08, 0x00, 0x40, 0x20, 0xEF, 0xE7, 0x00, 0xF0, 0x3B, 0xF8, 0x28, 0x78, + 0x40, 0xF0, 0x0C, 0x00, 0x28, 0x70, 0xE8, 0x78, 0x2E, 0xE0, 0x19, 0x4C, + 0x01, 0x22, 0x69, 0x46, 0x14, 0xF8, 0x23, 0x0F, 0x8D, 0xF8, 0x00, 0x00, + 0x08, 0x20, 0x00, 0xF0, 0x95, 0xFC, 0x61, 0x78, 0xA0, 0x78, 0x30, 0x22, + 0x02, 0xEA, 0x01, 0x11, 0x41, 0xEA, 0x80, 0x10, 0xE1, 0x78, 0x22, 0x7C, + 0x01, 0xF0, 0x03, 0x01, 0x08, 0x43, 0x21, 0x79, 0x02, 0xF0, 0x3B, 0x02, + 0x42, 0xEA, 0x81, 0x11, 0xA2, 0x7B, 0x8D, 0xF8, 0x00, 0x20, 0x22, 0x7B, + 0x8D, 0xF8, 0x01, 0x20, 0x8D, 0xF8, 0x02, 0x00, 0x8D, 0xF8, 0x03, 0x10, + 0x04, 0x22, 0x69, 0x46, 0x1F, 0x20, 0x00, 0xF0, 0x75, 0xFC, 0x28, 0x78, + 0x40, 0xF0, 0x08, 0x00, 0x28, 0x70, 0xA8, 0x78, 0x28, 0x71, 0x3E, 0xBD, + 0x38, 0x25, 0x10, 0x00, 0xB0, 0x86, 0x10, 0x00, 0x3E, 0xB5, 0x1C, 0x4C, + 0x01, 0x22, 0x69, 0x46, 0x14, 0xF8, 0x28, 0x0F, 0x8D, 0xF8, 0x00, 0x00, + 0x08, 0x20, 0x00, 0xF0, 0x5F, 0xFC, 0x61, 0x78, 0x30, 0x22, 0xA0, 0x78, + 0x02, 0xEA, 0x01, 0x11, 0x41, 0xEA, 0x80, 0x10, 0xE1, 0x78, 0xE2, 0x7A, + 0x01, 0xF0, 0x03, 0x01, 0x08, 0x43, 0x21, 0x79, 0x02, 0xF0, 0x3B, 0x02, + 0x42, 0xEA, 0x81, 0x11, 0x8D, 0xF8, 0x00, 0x00, 0x8D, 0xF8, 0x01, 0x10, + 0x02, 0x22, 0x69, 0x46, 0x21, 0x20, 0x00, 0xF0, 0x45, 0xFC, 0x20, 0x7B, + 0xA1, 0x7A, 0x8D, 0xF8, 0x00, 0x10, 0x21, 0x7A, 0x8D, 0xF8, 0x01, 0x10, + 0x00, 0x21, 0x8D, 0xF8, 0x02, 0x10, 0x00, 0xF0, 0x3B, 0x00, 0x8D, 0xF8, + 0x03, 0x00, 0x04, 0x22, 0x69, 0x46, 0x27, 0x20, 0x00, 0xF0, 0x32, 0xFC, + 0x3E, 0xBD, 0x00, 0x00, 0xB0, 0x86, 0x10, 0x00, 0x05, 0x48, 0x10, 0xB5, + 0x80, 0x68, 0x10, 0xB1, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x47, 0x00, 0x20, + 0x00, 0xF0, 0xF2, 0xFB, 0x10, 0xBD, 0x00, 0x00, 0x24, 0x25, 0x10, 0x00, + 0x01, 0x48, 0x00, 0x78, 0x70, 0x47, 0x00, 0x00, 0x24, 0x25, 0x10, 0x00, + 0x01, 0x48, 0x40, 0x78, 0x70, 0x47, 0x00, 0x00, 0x24, 0x25, 0x10, 0x00, + 0x40, 0xF2, 0x72, 0x40, 0x70, 0x47, 0x1E, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x12, 0x49, 0x00, 0x20, 0xCA, 0x78, 0xD2, 0x07, 0x01, 0xD0, 0x40, 0xF0, + 0x01, 0x00, 0xCA, 0x78, 0x12, 0x06, 0x01, 0xD5, 0x40, 0xF0, 0x02, 0x00, + 0xCA, 0x78, 0x52, 0x06, 0x01, 0xD5, 0x40, 0xF0, 0x04, 0x00, 0xCA, 0x78, + 0x92, 0x07, 0x01, 0xD5, 0x40, 0xF0, 0x08, 0x00, 0xCA, 0x78, 0x12, 0x07, + 0x01, 0xD5, 0x40, 0xF0, 0x10, 0x00, 0xCA, 0x78, 0x52, 0x07, 0x01, 0xD5, + 0x40, 0xF0, 0x20, 0x00, 0xC9, 0x78, 0x89, 0x06, 0x01, 0xD5, 0x40, 0xF0, + 0x40, 0x00, 0x70, 0x47, 0x00, 0x00, 0x07, 0x20, 0x22, 0x20, 0x70, 0x47, + 0x38, 0xB5, 0x04, 0x46, 0x01, 0x20, 0x00, 0xF0, 0x7E, 0xFF, 0x0D, 0x20, + 0x00, 0xF0, 0x7B, 0xFF, 0x02, 0x20, 0x00, 0xF0, 0x78, 0xFF, 0x06, 0x20, + 0x00, 0xF0, 0x75, 0xFF, 0x01, 0x20, 0x00, 0xF0, 0x51, 0xFF, 0x0D, 0x20, + 0x00, 0xF0, 0x4E, 0xFF, 0x02, 0x20, 0x00, 0xF0, 0x4B, 0xFF, 0x06, 0x20, + 0x00, 0xF0, 0x48, 0xFF, 0x31, 0x48, 0x00, 0x23, 0x03, 0x60, 0x01, 0x21, + 0x01, 0x60, 0x03, 0x60, 0x2F, 0x49, 0x0A, 0x68, 0x00, 0x2A, 0xFC, 0xD1, + 0x2E, 0x49, 0x04, 0x20, 0x08, 0x73, 0x2E, 0x48, 0x01, 0x22, 0x01, 0x46, + 0x83, 0x70, 0x2B, 0x48, 0x00, 0x23, 0x00, 0x1D, 0x00, 0xF0, 0x5E, 0xF9, + 0x29, 0x49, 0x28, 0x48, 0x00, 0x23, 0x01, 0x22, 0x49, 0x1C, 0x40, 0x1D, + 0x00, 0xF0, 0x56, 0xF9, 0x21, 0x78, 0x21, 0xF0, 0x7F, 0x00, 0xC1, 0xF3, + 0x80, 0x12, 0x40, 0xEA, 0x82, 0x10, 0xC1, 0xF3, 0x00, 0x12, 0x40, 0xEA, + 0x02, 0x10, 0xC1, 0xF3, 0xC0, 0x02, 0x40, 0xEA, 0xC2, 0x00, 0xC1, 0xF3, + 0x80, 0x02, 0x40, 0xEA, 0x82, 0x00, 0x01, 0xF0, 0x03, 0x01, 0x08, 0x43, + 0x40, 0xF0, 0x80, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, + 0x0B, 0x20, 0x00, 0xF0, 0x8D, 0xFB, 0x4F, 0xF4, 0xFA, 0x60, 0x01, 0xF0, + 0x55, 0xF9, 0x20, 0x46, 0x00, 0xF0, 0x28, 0xF8, 0x00, 0xF0, 0x3C, 0xF8, + 0x05, 0x21, 0x01, 0x20, 0x00, 0xF0, 0x6B, 0xFF, 0x05, 0x21, 0x0D, 0x20, + 0x00, 0xF0, 0x67, 0xFF, 0x06, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x63, 0xFF, + 0x06, 0x21, 0x08, 0x46, 0x00, 0xF0, 0x5F, 0xFF, 0x01, 0x20, 0x00, 0xF0, + 0x31, 0xFF, 0x0D, 0x20, 0x00, 0xF0, 0x2E, 0xFF, 0x02, 0x20, 0x00, 0xF0, + 0x2B, 0xFF, 0xBD, 0xE8, 0x38, 0x40, 0x06, 0x20, 0x00, 0xF0, 0x26, 0xBF, + 0x10, 0x05, 0x00, 0x22, 0x9C, 0x1B, 0x00, 0x22, 0x00, 0x00, 0x07, 0x20, + 0x24, 0x25, 0x10, 0x00, 0x01, 0x46, 0x1E, 0x22, 0x01, 0x48, 0xFE, 0xF7, + 0xB5, 0xBF, 0x00, 0x00, 0x4C, 0x86, 0x10, 0x00, 0x05, 0x48, 0x80, 0x78, + 0x03, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x03, 0xD0, 0x05, 0x28, 0x01, 0xD0, + 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, 0x70, 0x47, 0x24, 0x25, 0x10, 0x00, + 0x1E, 0xB5, 0x00, 0x24, 0x8D, 0xF8, 0x00, 0x40, 0x01, 0x22, 0x69, 0x46, + 0x04, 0x20, 0x00, 0xF0, 0x3F, 0xFB, 0x8D, 0xF8, 0x00, 0x40, 0x01, 0x22, + 0x69, 0x46, 0x05, 0x20, 0x00, 0xF0, 0x38, 0xFB, 0x5E, 0x4C, 0x60, 0x68, + 0x00, 0x78, 0x20, 0xF0, 0x7F, 0x01, 0xC0, 0xF3, 0x80, 0x12, 0x41, 0xEA, + 0x82, 0x11, 0xC0, 0xF3, 0x00, 0x12, 0x41, 0xEA, 0x02, 0x11, 0xC0, 0xF3, + 0xC0, 0x02, 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x80, 0x02, 0x41, 0xEA, + 0x82, 0x01, 0x00, 0xF0, 0x03, 0x00, 0x01, 0x43, 0x8D, 0xF8, 0x00, 0x10, + 0x01, 0x22, 0x69, 0x46, 0x0B, 0x20, 0x00, 0xF0, 0x19, 0xFB, 0x60, 0x68, + 0x40, 0x78, 0x20, 0xF0, 0x7F, 0x01, 0xC0, 0xF3, 0x80, 0x12, 0x41, 0xEA, + 0x82, 0x11, 0xC0, 0xF3, 0x00, 0x12, 0x41, 0xEA, 0x02, 0x11, 0xC0, 0xF3, + 0xC0, 0x02, 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x80, 0x02, 0x41, 0xEA, + 0x82, 0x01, 0x00, 0xF0, 0x03, 0x00, 0x01, 0x43, 0x8D, 0xF8, 0x00, 0x10, + 0x01, 0x22, 0x69, 0x46, 0x16, 0x20, 0x00, 0xF0, 0xFB, 0xFA, 0x02, 0x20, + 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, 0x18, 0x20, 0x00, 0xF0, + 0xF3, 0xFA, 0x8B, 0x20, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, + 0xA0, 0x20, 0x00, 0xF0, 0xEB, 0xFA, 0x60, 0x68, 0x02, 0x22, 0x01, 0x7A, + 0x8D, 0xF8, 0x00, 0x10, 0x00, 0x89, 0x69, 0x46, 0x00, 0x0A, 0x8D, 0xF8, + 0x01, 0x00, 0x3F, 0x20, 0x00, 0xF0, 0xDE, 0xFA, 0x60, 0x68, 0x02, 0x22, + 0x69, 0x46, 0x40, 0x89, 0x8D, 0xF8, 0x00, 0x00, 0x00, 0x0A, 0x8D, 0xF8, + 0x01, 0x00, 0x41, 0x20, 0x00, 0xF0, 0xD2, 0xFA, 0x60, 0x68, 0x01, 0x22, + 0x69, 0x46, 0x80, 0x78, 0xC0, 0xF3, 0xC0, 0x00, 0x80, 0x01, 0x8D, 0xF8, + 0x00, 0x00, 0x44, 0x20, 0x00, 0xF0, 0xC6, 0xFA, 0x60, 0x68, 0x01, 0x22, + 0x69, 0x46, 0x00, 0x79, 0x00, 0xF0, 0x0F, 0x00, 0x8D, 0xF8, 0x00, 0x00, + 0x45, 0x20, 0x00, 0xF0, 0xBB, 0xFA, 0x60, 0x68, 0x02, 0x22, 0x69, 0x46, + 0xC0, 0x89, 0x8D, 0xF8, 0x00, 0x00, 0xC0, 0xF3, 0x00, 0x20, 0x8D, 0xF8, + 0x01, 0x00, 0x48, 0x20, 0x00, 0xF0, 0xAE, 0xFA, 0x60, 0x68, 0x01, 0x22, + 0x69, 0x46, 0xC0, 0x78, 0xC0, 0xF3, 0x82, 0x00, 0x8D, 0xF8, 0x00, 0x00, + 0xAD, 0x20, 0x00, 0xF0, 0xA3, 0xFA, 0x60, 0x68, 0x01, 0x22, 0x69, 0x46, + 0x40, 0x79, 0x00, 0xF0, 0x7F, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0xB2, 0x20, + 0x00, 0xF0, 0x98, 0xFA, 0x60, 0x68, 0x01, 0x22, 0x81, 0x78, 0xC0, 0x78, + 0xC1, 0xF3, 0x40, 0x11, 0x49, 0x00, 0x41, 0xEA, 0x80, 0x10, 0x8D, 0xF8, + 0x00, 0x00, 0x69, 0x46, 0xB3, 0x20, 0x00, 0xF0, 0x89, 0xFA, 0x60, 0x68, + 0x01, 0x22, 0x69, 0x46, 0xC0, 0x79, 0x8D, 0xF8, 0x00, 0x00, 0xB4, 0x20, + 0x00, 0xF0, 0x80, 0xFA, 0x22, 0x20, 0x01, 0x07, 0x01, 0xF8, 0xDD, 0x0F, + 0x48, 0x70, 0x1E, 0xBD, 0x24, 0x25, 0x10, 0x00, 0x0E, 0x4B, 0x10, 0xB5, + 0x98, 0x76, 0x02, 0xF0, 0x0F, 0x00, 0xD8, 0x76, 0x0C, 0x48, 0x04, 0x68, + 0x00, 0x2C, 0xFC, 0xD1, 0x01, 0x20, 0x83, 0xF8, 0xE0, 0x00, 0x09, 0x48, + 0x18, 0x30, 0x04, 0x68, 0x00, 0x2C, 0xFC, 0xD1, 0x00, 0x20, 0x05, 0xE0, + 0xC4, 0x18, 0x94, 0xF8, 0x60, 0x40, 0x0C, 0x54, 0x40, 0x1C, 0x40, 0xB2, + 0x90, 0x42, 0xF7, 0xDB, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, + 0x84, 0x1B, 0x00, 0x22, 0x30, 0xB5, 0x00, 0x23, 0x1C, 0x46, 0x03, 0xE0, + 0xC5, 0x18, 0x85, 0xF8, 0x80, 0x40, 0x5B, 0x1C, 0x93, 0x42, 0xF9, 0xDB, + 0x05, 0x4B, 0x1C, 0x68, 0x00, 0x2C, 0xFC, 0xD1, 0x00, 0x23, 0x02, 0xE0, + 0xC4, 0x5C, 0xCC, 0x54, 0x5B, 0x1C, 0x93, 0x42, 0xFA, 0xDB, 0x30, 0xBD, + 0x9C, 0x1B, 0x00, 0x22, 0x70, 0xB5, 0x01, 0x20, 0x00, 0xF0, 0xD9, 0xFD, + 0x02, 0x20, 0x00, 0xF0, 0xD6, 0xFD, 0x06, 0x20, 0x00, 0xF0, 0xD3, 0xFD, + 0x01, 0x20, 0x00, 0xF0, 0xAF, 0xFD, 0x02, 0x20, 0x00, 0xF0, 0xAC, 0xFD, + 0x06, 0x20, 0x00, 0xF0, 0xA9, 0xFD, 0x19, 0x4D, 0x00, 0x24, 0x2C, 0x60, + 0x01, 0x21, 0x29, 0x60, 0x16, 0x48, 0x08, 0x38, 0x04, 0x60, 0x01, 0x60, + 0x04, 0x60, 0x15, 0x48, 0x01, 0x68, 0x00, 0x29, 0xFC, 0xD1, 0x14, 0x48, + 0x01, 0x7B, 0x41, 0xF0, 0x20, 0x01, 0x01, 0x73, 0x01, 0x7B, 0x21, 0xF0, + 0x40, 0x01, 0x01, 0x73, 0x01, 0x7B, 0x21, 0xF0, 0x80, 0x01, 0x01, 0x73, + 0x01, 0x7B, 0x21, 0xF0, 0x20, 0x01, 0x01, 0x73, 0x00, 0xF0, 0x2A, 0xFE, + 0x00, 0xF0, 0xDE, 0xFC, 0x0A, 0x48, 0x84, 0x70, 0x84, 0x60, 0x2C, 0x60, + 0x01, 0x20, 0x00, 0xF0, 0xC1, 0xFD, 0x02, 0x20, 0x00, 0xF0, 0xBE, 0xFD, + 0xBD, 0xE8, 0x70, 0x40, 0x06, 0x20, 0x00, 0xF0, 0xB9, 0xBD, 0x00, 0x00, + 0x18, 0x05, 0x00, 0x22, 0x84, 0x1B, 0x00, 0x22, 0x00, 0x00, 0x07, 0x20, + 0x24, 0x25, 0x10, 0x00, 0x08, 0xB5, 0x00, 0xF0, 0x03, 0x00, 0x40, 0xEA, + 0xC1, 0x01, 0x8D, 0xF8, 0x00, 0x10, 0x01, 0x22, 0x69, 0x46, 0x10, 0x46, + 0x00, 0xF0, 0xE4, 0xF9, 0x08, 0xBD, 0x10, 0xB5, 0x04, 0x46, 0x00, 0xF0, + 0x05, 0xF8, 0x20, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, 0x6C, 0xB9, + 0x2D, 0xE9, 0xF8, 0x43, 0xB3, 0x4C, 0x00, 0x25, 0x02, 0x26, 0x01, 0x27, + 0x61, 0x68, 0x4F, 0xF0, 0x03, 0x09, 0x4F, 0xF0, 0x80, 0x08, 0x15, 0x28, + 0x7A, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x0B, 0xBD, 0x21, 0x24, 0x24, 0x4B, + 0x4B, 0x62, 0x62, 0x7A, 0x7A, 0xBF, 0xBF, 0xD6, 0xD6, 0xFA, 0xFA, 0xF8, + 0xF8, 0xF7, 0xF7, 0x00, 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, + 0x89, 0x0E, 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, + 0x80, 0x00, 0x20, 0xF0, 0x10, 0x00, 0x40, 0xF0, 0x40, 0x00, 0x8D, 0xF8, + 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, 0x02, 0x20, 0x00, 0xF0, 0xAA, 0xF9, + 0x8D, 0xF8, 0x00, 0x50, 0xC7, 0xE0, 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, + 0x80, 0x02, 0x89, 0x0E, 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, + 0x41, 0xEA, 0x80, 0x00, 0x40, 0xF0, 0x51, 0x00, 0x8D, 0xF8, 0x00, 0x00, + 0x01, 0x22, 0x69, 0x46, 0x02, 0x20, 0x00, 0xF0, 0x93, 0xF9, 0x8D, 0xF8, + 0x00, 0x50, 0x54, 0xE0, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, + 0x44, 0x20, 0x00, 0xF0, 0x89, 0xF9, 0x60, 0x68, 0x81, 0x78, 0xC0, 0x78, + 0xC1, 0xF3, 0x40, 0x11, 0x49, 0x00, 0x41, 0xEA, 0x80, 0x10, 0x6A, 0xE0, + 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, 0x89, 0x0E, 0x41, 0xEA, + 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, 0x80, 0x00, 0x40, 0xF0, + 0x51, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, 0x02, 0x20, + 0x00, 0xF0, 0x6C, 0xF9, 0x8D, 0xF8, 0x00, 0x50, 0x39, 0xE0, 0x88, 0x78, + 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, 0x89, 0x0E, 0x41, 0xEA, 0xC2, 0x01, + 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, 0x80, 0x00, 0x40, 0xF0, 0x53, 0x00, + 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, 0x02, 0x20, 0x00, 0xF0, + 0x55, 0xF9, 0x8D, 0xF8, 0x00, 0x70, 0x72, 0xE0, 0x42, 0xE0, 0x88, 0x78, + 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, 0x89, 0x0E, 0x41, 0xEA, 0xC2, 0x01, + 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, 0x80, 0x00, 0x40, 0xF0, 0x53, 0x00, + 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, 0x02, 0x20, 0x00, 0xF0, + 0x3D, 0xF9, 0x8D, 0xF8, 0x00, 0x70, 0x01, 0x22, 0x69, 0x46, 0x3D, 0x20, + 0x00, 0xF0, 0x36, 0xF9, 0x60, 0x68, 0x80, 0x78, 0xC0, 0xF3, 0xC0, 0x01, + 0x48, 0xEA, 0x81, 0x10, 0x9E, 0xE7, 0x01, 0x22, 0x69, 0x46, 0x3D, 0x20, + 0x00, 0xF0, 0x2A, 0xF9, 0x60, 0x68, 0x01, 0x22, 0x69, 0x46, 0x80, 0x78, + 0xC0, 0xF3, 0xC0, 0x00, 0x80, 0x01, 0x8D, 0xF8, 0x00, 0x00, 0x44, 0x20, + 0x00, 0xF0, 0x1E, 0xF9, 0x60, 0x68, 0x81, 0x78, 0xC0, 0x78, 0xC1, 0xF3, + 0x40, 0x11, 0x49, 0x00, 0x41, 0xEA, 0x80, 0x10, 0x40, 0x1C, 0x8D, 0xF8, + 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, 0xB3, 0x20, 0x00, 0xF0, 0x0E, 0xF9, + 0xBD, 0xE8, 0xF8, 0x83, 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, + 0x89, 0x0E, 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, + 0x80, 0x00, 0x40, 0xF0, 0x53, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, + 0x69, 0x46, 0x02, 0x20, 0x00, 0xF0, 0xF8, 0xF8, 0x8D, 0xF8, 0x00, 0x70, + 0xC5, 0xE7, 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, 0x89, 0x0E, + 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, 0x80, 0x00, + 0x40, 0xF0, 0x51, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, + 0x02, 0x20, 0x00, 0xF0, 0xE1, 0xF8, 0x8D, 0xF8, 0x00, 0x60, 0x01, 0x22, + 0x69, 0x46, 0x3D, 0x20, 0x00, 0xF0, 0xDA, 0xF8, 0x60, 0x68, 0x80, 0x78, + 0xC0, 0xF3, 0xC0, 0x00, 0x80, 0x01, 0x43, 0xE7, 0x4A, 0xE0, 0x32, 0xE0, + 0xFF, 0xE7, 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, 0x89, 0x0E, + 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, 0x80, 0x00, + 0x40, 0xF0, 0x51, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x69, 0x46, + 0x02, 0x20, 0x00, 0xF0, 0xBD, 0xF8, 0x8D, 0xF8, 0x00, 0x60, 0x01, 0x22, + 0x69, 0x46, 0x3D, 0x20, 0x00, 0xF0, 0xB6, 0xF8, 0x60, 0x68, 0x01, 0x22, + 0x80, 0x78, 0xC0, 0xF3, 0xC0, 0x01, 0x48, 0xEA, 0x81, 0x10, 0x8D, 0xF8, + 0x00, 0x00, 0x69, 0x46, 0x44, 0x20, 0x00, 0xF0, 0xA9, 0xF8, 0x60, 0x68, + 0x01, 0x22, 0x69, 0x46, 0x00, 0x79, 0x00, 0xF0, 0x0F, 0x00, 0x8D, 0xF8, + 0x00, 0x00, 0x45, 0x20, 0x13, 0xE7, 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, + 0x80, 0x02, 0x89, 0x0E, 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, + 0x41, 0xEA, 0x80, 0x00, 0x40, 0xF0, 0x51, 0x00, 0x8D, 0xF8, 0x00, 0x00, + 0x01, 0x22, 0x69, 0x46, 0x02, 0x20, 0x00, 0xF0, 0x8B, 0xF8, 0x8D, 0xF8, + 0x00, 0x60, 0x58, 0xE7, 0x88, 0x78, 0xC1, 0x07, 0xC0, 0xF3, 0x80, 0x02, + 0x89, 0x0E, 0x41, 0xEA, 0xC2, 0x01, 0xC0, 0xF3, 0x40, 0x00, 0x41, 0xEA, + 0x80, 0x00, 0x40, 0xF0, 0x43, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x01, 0x22, + 0x69, 0x46, 0x02, 0x20, 0x00, 0xF0, 0x74, 0xF8, 0x8D, 0xF8, 0x00, 0x90, + 0x91, 0xE7, 0x00, 0x00, 0x24, 0x25, 0x10, 0x00, 0xF0, 0xB5, 0x1B, 0x49, + 0x1B, 0x4F, 0x0A, 0x1D, 0x00, 0x25, 0x13, 0x1D, 0x01, 0x24, 0x19, 0x4E, + 0x10, 0x37, 0x15, 0x28, 0x1F, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x0B, 0x1E, + 0x1E, 0x0D, 0x21, 0x1F, 0x21, 0x13, 0x17, 0x13, 0x17, 0x13, 0x17, 0x0D, + 0x21, 0x0D, 0x19, 0x1F, 0x21, 0x23, 0x27, 0x00, 0x0D, 0x60, 0x03, 0xE0, + 0x0C, 0x60, 0x15, 0x60, 0x1C, 0x60, 0x0A, 0xE0, 0x15, 0x60, 0x15, 0xE0, + 0x0C, 0x60, 0x12, 0xE0, 0x14, 0x60, 0x0D, 0xE0, 0x0C, 0x60, 0xFB, 0xE7, + 0x0D, 0x60, 0x14, 0x60, 0x1D, 0x60, 0x35, 0x60, 0x3C, 0x60, 0xF0, 0xBD, + 0x0C, 0x60, 0xEF, 0xE7, 0x0D, 0x60, 0xF1, 0xE7, 0x0C, 0x60, 0x15, 0x60, + 0x1C, 0x60, 0xF0, 0xBD, 0x0D, 0x60, 0x14, 0x60, 0x1D, 0x60, 0xF0, 0xBD, + 0x00, 0x08, 0x00, 0x22, 0x64, 0x1B, 0x00, 0x22, 0x70, 0x47, 0x00, 0x00, + 0x02, 0x48, 0x01, 0x68, 0x01, 0x29, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00, + 0x80, 0x1B, 0x00, 0x22, 0x02, 0x48, 0x01, 0x68, 0x00, 0x29, 0xFC, 0xD1, + 0x70, 0x47, 0x00, 0x00, 0x84, 0x1B, 0x00, 0x22, 0xF0, 0xB5, 0x00, 0x23, + 0x0C, 0x4E, 0x0D, 0x4C, 0x07, 0xE0, 0x25, 0x68, 0x01, 0x2D, 0xFC, 0xD1, + 0xCD, 0x5C, 0x9F, 0x19, 0x5B, 0x1C, 0x87, 0xF8, 0x60, 0x50, 0x93, 0x42, + 0xF5, 0xDB, 0x07, 0x49, 0x09, 0x1D, 0x0B, 0x68, 0x00, 0x2B, 0xFC, 0xD1, + 0xB0, 0x76, 0x02, 0xF0, 0x0F, 0x00, 0x40, 0xF0, 0xD0, 0x00, 0xF0, 0x76, + 0xF0, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, 0x80, 0x1B, 0x00, 0x22, + 0xF0, 0xB5, 0x00, 0x23, 0x0C, 0x4E, 0x0D, 0x4C, 0x07, 0xE0, 0x25, 0x68, + 0x01, 0x2D, 0xFC, 0xD1, 0xCD, 0x5C, 0x9F, 0x19, 0x5B, 0x1C, 0x87, 0xF8, + 0x60, 0x50, 0x93, 0x42, 0xF5, 0xDB, 0x07, 0x49, 0x09, 0x1D, 0x0B, 0x68, + 0x00, 0x2B, 0xFC, 0xD1, 0xB0, 0x76, 0x02, 0xF0, 0x0F, 0x00, 0x40, 0xF0, + 0x10, 0x00, 0xF0, 0x76, 0xF0, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, + 0x80, 0x1B, 0x00, 0x22, 0x4F, 0xF0, 0xE0, 0x21, 0x4F, 0xF4, 0x80, 0x30, + 0xC1, 0xF8, 0x80, 0x01, 0xC1, 0xF8, 0x80, 0x02, 0x02, 0x49, 0xA0, 0x20, + 0x81, 0xF8, 0x00, 0x04, 0x70, 0x47, 0x00, 0x00, 0x10, 0xE0, 0x00, 0xE0, + 0x10, 0xB5, 0x04, 0x46, 0x14, 0x21, 0xFE, 0xF7, 0xC8, 0xFC, 0x0A, 0x48, + 0x00, 0x68, 0x20, 0x60, 0x08, 0x48, 0x00, 0x1F, 0x00, 0x68, 0x60, 0x60, + 0x06, 0x48, 0x00, 0x1D, 0x00, 0x68, 0xA0, 0x60, 0x04, 0x48, 0x08, 0x30, + 0x00, 0x68, 0xE0, 0x60, 0x02, 0x48, 0x0C, 0x30, 0x00, 0x68, 0x20, 0x61, + 0x10, 0xBD, 0x00, 0x00, 0x38, 0x1E, 0x04, 0x00, 0x30, 0xB5, 0x95, 0xB0, + 0x04, 0x46, 0x1C, 0x21, 0xFE, 0xF7, 0xAB, 0xFC, 0x55, 0x48, 0x00, 0x68, + 0x00, 0x90, 0x54, 0x48, 0x00, 0x1D, 0x00, 0x68, 0x01, 0x90, 0x52, 0x48, + 0x08, 0x30, 0x00, 0x68, 0x02, 0x90, 0x50, 0x48, 0x0C, 0x30, 0x00, 0x68, + 0x03, 0x90, 0x4E, 0x48, 0x10, 0x30, 0x00, 0x68, 0x04, 0x90, 0x4C, 0x48, + 0x14, 0x30, 0x00, 0x68, 0x05, 0x90, 0x4A, 0x48, 0x18, 0x30, 0x00, 0x68, + 0x06, 0x90, 0x48, 0x48, 0x1C, 0x30, 0x00, 0x68, 0x07, 0x90, 0x46, 0x48, + 0x20, 0x30, 0x00, 0x68, 0x08, 0x90, 0x44, 0x48, 0x24, 0x30, 0x00, 0x68, + 0x09, 0x90, 0x42, 0x48, 0x28, 0x30, 0x00, 0x68, 0x0A, 0x90, 0x40, 0x48, + 0x2C, 0x30, 0x00, 0x68, 0x0B, 0x90, 0x3E, 0x48, 0x30, 0x30, 0x00, 0x68, + 0x0C, 0x90, 0x3C, 0x48, 0x34, 0x30, 0x00, 0x68, 0x0D, 0x90, 0x3A, 0x48, + 0x38, 0x30, 0x00, 0x68, 0x0E, 0x90, 0x38, 0x48, 0x3C, 0x30, 0x00, 0x68, + 0x0F, 0x90, 0x36, 0x48, 0x40, 0x30, 0x00, 0x68, 0x10, 0x90, 0x34, 0x48, + 0x44, 0x30, 0x00, 0x68, 0x11, 0x90, 0x32, 0x48, 0x48, 0x30, 0x00, 0x68, + 0x12, 0x90, 0x30, 0x48, 0x30, 0x49, 0x4C, 0x30, 0x6F, 0xF0, 0x02, 0x05, + 0x00, 0x68, 0x13, 0x90, 0x00, 0x98, 0x88, 0x42, 0x4D, 0xD1, 0xA8, 0x10, + 0xA0, 0x60, 0x60, 0x60, 0x20, 0x60, 0x60, 0x61, 0xA0, 0x61, 0xE0, 0x60, + 0x20, 0x61, 0x03, 0xA8, 0x02, 0x99, 0x00, 0xF0, 0x21, 0xFD, 0x01, 0x99, + 0x81, 0x42, 0x3E, 0xD1, 0x0B, 0x98, 0x45, 0xF2, 0xAA, 0x51, 0x80, 0xB2, + 0x88, 0x42, 0x08, 0xD1, 0x04, 0x98, 0xC0, 0xB2, 0xA0, 0x60, 0x13, 0x9A, + 0x92, 0xB2, 0x8A, 0x42, 0x00, 0xD0, 0x80, 0x1D, 0xE0, 0x60, 0x0C, 0x98, + 0x80, 0xB2, 0x88, 0x42, 0x17, 0xD1, 0x03, 0x98, 0x03, 0x9A, 0x00, 0xF4, + 0x7F, 0x40, 0xD2, 0xB2, 0x40, 0xEA, 0x02, 0x40, 0x03, 0x9A, 0xC2, 0xF3, + 0x07, 0x42, 0x10, 0x43, 0x16, 0x4A, 0x90, 0x42, 0x03, 0xD3, 0x05, 0x98, + 0xC0, 0xF3, 0x01, 0x20, 0x00, 0xE0, 0x00, 0x20, 0x60, 0x60, 0x05, 0x98, + 0x00, 0xF0, 0x1F, 0x00, 0x20, 0x60, 0x0D, 0x98, 0x80, 0xB2, 0x88, 0x42, + 0x01, 0xD1, 0x06, 0x98, 0x60, 0x61, 0x0E, 0x98, 0x80, 0xB2, 0x88, 0x42, + 0x01, 0xD1, 0x07, 0x98, 0xA0, 0x61, 0x0F, 0x98, 0x80, 0xB2, 0x88, 0x42, + 0x01, 0xD1, 0x08, 0x98, 0x20, 0x61, 0x15, 0xB0, 0x30, 0xBD, 0xA5, 0x60, + 0x65, 0x60, 0x25, 0x60, 0x65, 0x61, 0xA5, 0x61, 0xE5, 0x60, 0x25, 0x61, + 0xF5, 0xE7, 0x00, 0x00, 0xB4, 0x1E, 0x04, 0x00, 0x5C, 0x00, 0x0D, 0x60, + 0x00, 0x01, 0x02, 0x00, 0x30, 0xB5, 0x0A, 0x4C, 0x01, 0x25, 0x25, 0x60, + 0xE5, 0x06, 0x03, 0xF0, 0x3F, 0x03, 0x85, 0xF8, 0x6B, 0x30, 0x00, 0x23, + 0x04, 0xE0, 0x51, 0xF8, 0x23, 0x50, 0x40, 0xF8, 0x23, 0x50, 0x5B, 0x1C, + 0x93, 0x42, 0xF8, 0xDB, 0x00, 0x20, 0x20, 0x60, 0x30, 0xBD, 0x00, 0x00, + 0x44, 0x1B, 0x00, 0x22, 0x02, 0x48, 0x01, 0x68, 0x21, 0xF0, 0x04, 0x01, + 0x01, 0x60, 0x70, 0x47, 0x10, 0xED, 0x00, 0xE0, 0x08, 0x20, 0x81, 0x06, + 0x81, 0xF8, 0x68, 0x00, 0x70, 0x47, 0x4F, 0xF0, 0x00, 0x50, 0x80, 0x88, + 0x70, 0x47, 0x4F, 0xF0, 0x00, 0x50, 0x80, 0x79, 0x70, 0x47, 0x00, 0x00, + 0x0D, 0x49, 0x09, 0x68, 0x01, 0x60, 0x0C, 0x49, 0x10, 0x39, 0x09, 0x68, + 0x41, 0x60, 0x0A, 0x49, 0x0C, 0x39, 0x09, 0x68, 0x81, 0x60, 0x08, 0x49, + 0x08, 0x39, 0x09, 0x68, 0xC1, 0x60, 0x06, 0x49, 0x09, 0x1D, 0x09, 0x68, + 0x01, 0x61, 0x04, 0x49, 0x34, 0x39, 0x09, 0x68, 0x41, 0x61, 0x02, 0x49, + 0x14, 0x39, 0x09, 0x68, 0x81, 0x61, 0x70, 0x47, 0x38, 0xED, 0x00, 0xE0, + 0x4F, 0xF0, 0x00, 0x50, 0xC0, 0x8C, 0xA0, 0xF5, 0xA0, 0x51, 0xA4, 0x39, + 0x01, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x02, 0x48, 0x01, 0x68, 0x41, 0xF0, 0x04, 0x01, 0x01, 0x60, 0x70, 0x47, + 0x10, 0xED, 0x00, 0xE0, 0x41, 0xF2, 0xA4, 0x40, 0xC1, 0x06, 0xC8, 0x84, + 0x00, 0xF0, 0x2A, 0xB8, 0x4F, 0xF0, 0x00, 0x51, 0x08, 0x81, 0x70, 0x47, + 0x01, 0x49, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, 0xBC, 0x04, 0x00, 0x22, + 0x4F, 0xF0, 0x00, 0x51, 0x8A, 0x8C, 0x60, 0xF3, 0x04, 0x02, 0x8A, 0x84, + 0x70, 0x47, 0x4F, 0xF0, 0x00, 0x51, 0x40, 0xF2, 0xA6, 0x70, 0xC8, 0x84, + 0x00, 0xF0, 0x12, 0xB8, 0x10, 0xB5, 0x4F, 0xF0, 0x00, 0x54, 0x14, 0xF8, + 0x20, 0x2F, 0x22, 0xF0, 0x87, 0x02, 0x42, 0xEA, 0xC0, 0x13, 0x04, 0x22, + 0x02, 0xEA, 0x80, 0x00, 0x03, 0x43, 0x01, 0xF0, 0x03, 0x00, 0x03, 0x43, + 0x23, 0x70, 0x10, 0xBD, 0x4F, 0xF0, 0x00, 0x51, 0xC8, 0x8C, 0x14, 0x4A, + 0x00, 0xF0, 0x03, 0x00, 0x12, 0x68, 0x7A, 0xB1, 0x01, 0x28, 0x0A, 0xD0, + 0x02, 0x28, 0x06, 0xD0, 0x03, 0x28, 0x02, 0xD0, 0x4F, 0xF4, 0x16, 0x70, + 0x0E, 0xE0, 0x46, 0x20, 0x0C, 0xE0, 0x96, 0x20, 0x0A, 0xE0, 0x4F, 0xF4, + 0x96, 0x70, 0x07, 0xE0, 0x01, 0x28, 0x0E, 0xD0, 0x02, 0x28, 0x0A, 0xD0, + 0x03, 0x28, 0x06, 0xD0, 0x4F, 0xF4, 0xE1, 0x70, 0x40, 0x1E, 0x21, 0xF8, + 0x52, 0x0F, 0x08, 0x80, 0x70, 0x47, 0x32, 0x20, 0xF8, 0xE7, 0x6E, 0x20, + 0xF6, 0xE7, 0xDC, 0x20, 0xF4, 0xE7, 0x00, 0x00, 0xDC, 0x04, 0x00, 0x22, + 0x48, 0xF6, 0x19, 0x71, 0x48, 0x07, 0x20, 0xF8, 0x2A, 0x1F, 0x0B, 0x21, + 0x80, 0xF8, 0x3E, 0x10, 0x00, 0x21, 0x41, 0x83, 0x02, 0x21, 0x80, 0xF8, + 0xC0, 0x10, 0x4F, 0xF0, 0x08, 0x51, 0x01, 0x20, 0xC1, 0xF8, 0xD4, 0x03, + 0x01, 0x49, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, 0x44, 0x04, 0x00, 0x22, + 0x10, 0xB5, 0x0D, 0x20, 0x00, 0xF0, 0xCC, 0xF9, 0x03, 0x4C, 0x20, 0x68, + 0x00, 0x28, 0x02, 0xD0, 0x80, 0x47, 0x00, 0x20, 0x20, 0x60, 0x10, 0xBD, + 0x40, 0x25, 0x10, 0x00, 0x01, 0x21, 0x81, 0x40, 0xC8, 0xB2, 0x4F, 0xF0, + 0x00, 0x51, 0x91, 0xF8, 0x37, 0x10, 0x01, 0x42, 0x01, 0xD0, 0x01, 0x20, + 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x10, 0xB5, 0x0A, 0x20, 0x00, 0xF0, + 0x9B, 0xF9, 0x0B, 0x20, 0x00, 0xF0, 0x98, 0xF9, 0x0A, 0x20, 0x00, 0xF0, + 0x74, 0xF9, 0x0B, 0x20, 0x00, 0xF0, 0x71, 0xF9, 0x05, 0x21, 0x0A, 0x20, + 0x00, 0xF0, 0xD5, 0xF9, 0x05, 0x21, 0x0B, 0x20, 0x00, 0xF0, 0xD1, 0xF9, + 0x4F, 0xF0, 0x00, 0x50, 0x7F, 0x21, 0x00, 0xF8, 0x35, 0x1F, 0x41, 0x70, + 0xC1, 0x70, 0x00, 0x21, 0xC1, 0x71, 0x01, 0x72, 0x41, 0x72, 0x81, 0x72, + 0x10, 0xBD, 0x01, 0x23, 0x83, 0x40, 0xD8, 0xB2, 0x4F, 0xF0, 0x00, 0x53, + 0x00, 0x29, 0x93, 0xF8, 0x36, 0x10, 0x01, 0xD0, 0x01, 0x43, 0x00, 0xE0, + 0x81, 0x43, 0x83, 0xF8, 0x36, 0x10, 0x13, 0xF8, 0x38, 0x1F, 0x0A, 0xB1, + 0x01, 0x43, 0x00, 0xE0, 0x81, 0x43, 0x19, 0x70, 0x70, 0x47, 0x00, 0x00, + 0x01, 0x49, 0x01, 0x20, 0x08, 0x60, 0x70, 0x47, 0x40, 0x84, 0x00, 0x22, + 0x10, 0xB5, 0x72, 0xB6, 0x08, 0x49, 0x0C, 0x78, 0x00, 0x21, 0x04, 0xE0, + 0x06, 0x4A, 0x52, 0x1C, 0x52, 0x5C, 0x42, 0x54, 0x49, 0x1C, 0xA1, 0x42, + 0xF8, 0xDB, 0x40, 0xF6, 0xFC, 0x00, 0x00, 0xF0, 0x87, 0xFB, 0x62, 0xB6, + 0x20, 0x46, 0x10, 0xBD, 0x00, 0x04, 0x00, 0x20, 0x04, 0x48, 0x00, 0x78, + 0x03, 0x49, 0x49, 0x1C, 0x09, 0x78, 0x40, 0x1A, 0x00, 0xF0, 0x3F, 0x00, + 0x70, 0x47, 0x00, 0x00, 0x20, 0x04, 0x00, 0x20, 0x4F, 0xF0, 0x00, 0x50, + 0x90, 0xF8, 0x23, 0x00, 0x40, 0x08, 0x70, 0x47, 0x00, 0xB5, 0x4F, 0xF0, + 0xE0, 0x22, 0x01, 0x23, 0xC2, 0xF8, 0x80, 0x31, 0x02, 0xF5, 0xC0, 0x72, + 0x00, 0xF0, 0x16, 0xF8, 0x00, 0xF0, 0x0C, 0xF8, 0x04, 0x48, 0x03, 0x60, + 0xA0, 0x20, 0x82, 0xF8, 0x80, 0x02, 0xC2, 0xF8, 0x00, 0x31, 0x42, 0xF8, + 0x80, 0x3C, 0x00, 0xBD, 0x8C, 0x05, 0x00, 0x22, 0x02, 0x48, 0x01, 0x21, + 0x01, 0x60, 0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x5C, 0x84, 0x00, 0x22, + 0x02, 0x48, 0x01, 0x21, 0x01, 0x60, 0x00, 0x21, 0x01, 0x60, 0x70, 0x47, + 0x94, 0x05, 0x00, 0x22, 0x03, 0x49, 0x10, 0xB1, 0x00, 0x20, 0x08, 0x60, + 0x70, 0x47, 0x01, 0x20, 0xFB, 0xE7, 0x00, 0x00, 0x8C, 0x05, 0x00, 0x22, + 0x02, 0x48, 0x01, 0x21, 0x01, 0x60, 0x00, 0x21, 0x01, 0x60, 0x70, 0x47, + 0x1C, 0x05, 0x00, 0x22, 0x4F, 0xF0, 0x00, 0x51, 0x02, 0x78, 0x81, 0xF8, + 0x00, 0x22, 0x42, 0x78, 0x81, 0xF8, 0x01, 0x22, 0x82, 0x78, 0x81, 0xF8, + 0x02, 0x22, 0xC2, 0x78, 0x81, 0xF8, 0x03, 0x22, 0x02, 0x79, 0x81, 0xF8, + 0x04, 0x22, 0x42, 0x79, 0x81, 0xF8, 0x05, 0x22, 0x82, 0x79, 0x81, 0xF8, + 0x06, 0x22, 0xC0, 0x79, 0x81, 0xF8, 0x07, 0x02, 0x70, 0x47, 0x00, 0x00, + 0x04, 0x49, 0x00, 0x22, 0x0A, 0x60, 0x4F, 0xF0, 0x00, 0x52, 0xA2, 0xF8, + 0x60, 0x00, 0x01, 0x20, 0x08, 0x60, 0x70, 0x47, 0x50, 0x09, 0x00, 0x22, + 0x0A, 0x4A, 0x51, 0x62, 0x11, 0x68, 0x48, 0x43, 0x0A, 0x21, 0xB0, 0xFB, + 0xF1, 0xF0, 0xB0, 0xF5, 0x80, 0x3F, 0x06, 0xDB, 0xA0, 0xF5, 0x7F, 0x40, + 0xFF, 0x38, 0x10, 0x62, 0x4F, 0xF6, 0xFF, 0x70, 0x02, 0xE0, 0x00, 0x21, + 0x80, 0xB2, 0x11, 0x62, 0xFF, 0xF7, 0xDE, 0xBF, 0xEC, 0x24, 0x10, 0x00, + 0x03, 0x49, 0x00, 0x20, 0x08, 0x60, 0x03, 0x49, 0x08, 0x62, 0x48, 0x62, + 0x70, 0x47, 0x00, 0x00, 0x50, 0x09, 0x00, 0x22, 0xEC, 0x24, 0x10, 0x00, + 0x0F, 0x49, 0x10, 0xB5, 0x08, 0x6A, 0xB0, 0xF5, 0x80, 0x3F, 0x06, 0xD3, + 0xA0, 0xF5, 0x7F, 0x40, 0xFF, 0x38, 0x08, 0x62, 0x4F, 0xF6, 0xFF, 0x70, + 0x03, 0xE0, 0x30, 0xB1, 0x00, 0x22, 0x80, 0xB2, 0x0A, 0x62, 0xBD, 0xE8, + 0x10, 0x40, 0xFF, 0xF7, 0xBB, 0xBF, 0x4A, 0x6A, 0xFF, 0xF7, 0xDC, 0xFF, + 0x12, 0xB1, 0xBD, 0xE8, 0x10, 0x40, 0x10, 0x47, 0x04, 0x20, 0x00, 0xF0, + 0xB3, 0xFA, 0x10, 0xBD, 0xEC, 0x24, 0x10, 0x00, 0x04, 0x49, 0x00, 0x22, + 0x0A, 0x60, 0x4F, 0xF0, 0x00, 0x52, 0xA2, 0xF8, 0x62, 0x00, 0x01, 0x20, + 0x08, 0x60, 0x70, 0x47, 0x54, 0x09, 0x00, 0x22, 0x0A, 0x4A, 0xD1, 0x62, + 0x11, 0x68, 0x48, 0x43, 0x0A, 0x21, 0xB0, 0xFB, 0xF1, 0xF0, 0xB0, 0xF5, + 0x80, 0x3F, 0x06, 0xDB, 0xA0, 0xF5, 0x7F, 0x40, 0xFF, 0x38, 0x90, 0x62, + 0x4F, 0xF6, 0xFF, 0x70, 0x02, 0xE0, 0x00, 0x21, 0x80, 0xB2, 0x91, 0x62, + 0xFF, 0xF7, 0xDE, 0xBF, 0xEC, 0x24, 0x10, 0x00, 0x03, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x03, 0x49, 0x88, 0x62, 0xC8, 0x62, 0x70, 0x47, 0x00, 0x00, + 0x54, 0x09, 0x00, 0x22, 0xEC, 0x24, 0x10, 0x00, 0x0F, 0x49, 0x10, 0xB5, + 0x88, 0x6A, 0xB0, 0xF5, 0x80, 0x3F, 0x06, 0xD3, 0xA0, 0xF5, 0x7F, 0x40, + 0xFF, 0x38, 0x88, 0x62, 0x4F, 0xF6, 0xFF, 0x70, 0x03, 0xE0, 0x30, 0xB1, + 0x00, 0x22, 0x80, 0xB2, 0x8A, 0x62, 0xBD, 0xE8, 0x10, 0x40, 0xFF, 0xF7, + 0xBB, 0xBF, 0xCA, 0x6A, 0xFF, 0xF7, 0xDC, 0xFF, 0x12, 0xB1, 0xBD, 0xE8, + 0x10, 0x40, 0x10, 0x47, 0x05, 0x20, 0x00, 0xF0, 0x61, 0xFA, 0x10, 0xBD, + 0xEC, 0x24, 0x10, 0x00, 0x03, 0x4A, 0x40, 0x1C, 0x49, 0x1C, 0x12, 0x78, + 0x92, 0x1C, 0x01, 0xFB, 0x02, 0x00, 0x70, 0x47, 0x01, 0x24, 0x10, 0x00, + 0x02, 0x4A, 0x12, 0x78, 0x51, 0x43, 0x00, 0xEB, 0x41, 0x00, 0x70, 0x47, + 0x01, 0x24, 0x10, 0x00, 0x00, 0xF0, 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, + 0x40, 0x09, 0x80, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0xC0, 0xF8, 0x80, 0x12, + 0x70, 0x47, 0x00, 0xF0, 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, 0x40, 0x09, + 0x80, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0xC0, 0xF8, 0x80, 0x12, 0x70, 0x47, + 0x00, 0xF0, 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, 0x40, 0x09, 0x80, 0x00, + 0x00, 0xF1, 0xE0, 0x20, 0xC0, 0xF8, 0x80, 0x12, 0x70, 0x47, 0x00, 0xF0, + 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, 0x40, 0x09, 0x80, 0x00, 0x00, 0xF1, + 0xE0, 0x20, 0xC0, 0xF8, 0x80, 0x11, 0x70, 0x47, 0x00, 0xF0, 0x1F, 0x02, + 0x01, 0x21, 0x91, 0x40, 0x40, 0x09, 0x80, 0x00, 0x00, 0xF1, 0xE0, 0x20, + 0xC0, 0xF8, 0x80, 0x11, 0x70, 0x47, 0x00, 0xF0, 0x1F, 0x02, 0x01, 0x21, + 0x91, 0x40, 0x40, 0x09, 0x80, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0xC0, 0xF8, + 0x80, 0x11, 0x70, 0x47, 0x00, 0xF0, 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, + 0x40, 0x09, 0x80, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0xC0, 0xF8, 0x80, 0x11, + 0x70, 0x47, 0x00, 0xF0, 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, 0x40, 0x09, + 0x80, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0xC0, 0xF8, 0x00, 0x11, 0x70, 0x47, + 0x00, 0xF0, 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, 0x40, 0x09, 0x80, 0x00, + 0x00, 0xF1, 0xE0, 0x20, 0xC0, 0xF8, 0x00, 0x11, 0x70, 0x47, 0x49, 0x07, + 0x09, 0x0E, 0x00, 0x28, 0x06, 0xDA, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xF1, + 0xE0, 0x20, 0x80, 0xF8, 0x14, 0x1D, 0x70, 0x47, 0x00, 0xF1, 0xE0, 0x20, + 0x80, 0xF8, 0x00, 0x14, 0x70, 0x47, 0x49, 0x07, 0x09, 0x0E, 0x00, 0x28, + 0x06, 0xDA, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0x80, 0xF8, + 0x14, 0x1D, 0x70, 0x47, 0x00, 0xF1, 0xE0, 0x20, 0x80, 0xF8, 0x00, 0x14, + 0x70, 0x47, 0x49, 0x07, 0x09, 0x0E, 0x00, 0x28, 0x06, 0xDA, 0x00, 0xF0, + 0x0F, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0x80, 0xF8, 0x14, 0x1D, 0x70, 0x47, + 0x00, 0xF1, 0xE0, 0x20, 0x80, 0xF8, 0x00, 0x14, 0x70, 0x47, 0x00, 0x00, + 0x04, 0x49, 0x00, 0x22, 0x0A, 0x60, 0x4F, 0xF0, 0x00, 0x52, 0xA2, 0xF8, + 0x58, 0x00, 0x01, 0x20, 0x08, 0x60, 0x70, 0x47, 0x40, 0x09, 0x00, 0x22, + 0x0A, 0x4A, 0xD1, 0x60, 0x64, 0x21, 0x48, 0x43, 0x0A, 0x21, 0x90, 0xFB, + 0xF1, 0xF0, 0xB0, 0xF5, 0x80, 0x3F, 0x06, 0xDB, 0xA0, 0xF5, 0x7F, 0x40, + 0xFF, 0x38, 0x90, 0x60, 0x4F, 0xF6, 0xFF, 0x70, 0x02, 0xE0, 0x00, 0x21, + 0x80, 0xB2, 0x91, 0x60, 0xFF, 0xF7, 0xDE, 0xBF, 0xEC, 0x24, 0x10, 0x00, + 0x03, 0x49, 0x00, 0x20, 0x08, 0x60, 0x03, 0x49, 0x88, 0x60, 0xC8, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x40, 0x09, 0x00, 0x22, 0xEC, 0x24, 0x10, 0x00, + 0x0F, 0x49, 0x10, 0xB5, 0x88, 0x68, 0xB0, 0xF5, 0x80, 0x3F, 0x06, 0xDB, + 0xA0, 0xF5, 0x7F, 0x40, 0xFF, 0x38, 0x88, 0x60, 0x4F, 0xF6, 0xFF, 0x70, + 0x03, 0xE0, 0x30, 0xB1, 0x00, 0x22, 0xC0, 0xB2, 0x8A, 0x60, 0xBD, 0xE8, + 0x10, 0x40, 0xFF, 0xF7, 0xBB, 0xBF, 0xCA, 0x68, 0xFF, 0xF7, 0xDC, 0xFF, + 0x12, 0xB1, 0xBD, 0xE8, 0x10, 0x40, 0x10, 0x47, 0x00, 0x20, 0x00, 0xF0, + 0x69, 0xF9, 0x10, 0xBD, 0xEC, 0x24, 0x10, 0x00, 0x04, 0x49, 0x00, 0x22, + 0x0A, 0x60, 0xCA, 0x06, 0xA2, 0xF8, 0x5A, 0x00, 0x01, 0x20, 0x08, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x44, 0x09, 0x00, 0x22, 0x03, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x03, 0x49, 0x08, 0x61, 0x48, 0x61, 0x70, 0x47, 0x00, 0x00, + 0x44, 0x09, 0x00, 0x22, 0xEC, 0x24, 0x10, 0x00, 0x10, 0x49, 0x10, 0xB5, + 0x08, 0x69, 0xB0, 0xF5, 0x80, 0x3F, 0x06, 0xDB, 0xA0, 0xF5, 0x7F, 0x40, + 0xFF, 0x38, 0x08, 0x61, 0x4F, 0xF6, 0xFF, 0x70, 0x04, 0xE0, 0x00, 0x28, + 0x06, 0xDD, 0x00, 0x22, 0x80, 0xB2, 0x0A, 0x61, 0xBD, 0xE8, 0x10, 0x40, + 0xFF, 0xF7, 0xD2, 0xBF, 0x4A, 0x69, 0xFF, 0xF7, 0xDB, 0xFF, 0x12, 0xB1, + 0xBD, 0xE8, 0x10, 0x40, 0x10, 0x47, 0x01, 0x20, 0x00, 0xF0, 0x2E, 0xF9, + 0x10, 0xBD, 0x00, 0x00, 0xEC, 0x24, 0x10, 0x00, 0x04, 0x49, 0x00, 0x22, + 0x0A, 0x60, 0x8A, 0x06, 0xA2, 0xF8, 0x5C, 0x00, 0x01, 0x20, 0x08, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x48, 0x09, 0x00, 0x22, 0x09, 0x4A, 0xD1, 0x61, + 0x64, 0x21, 0x48, 0x43, 0x0A, 0x21, 0x90, 0xFB, 0xF1, 0xF0, 0xB0, 0xF5, + 0x80, 0x3F, 0x03, 0xDB, 0xFF, 0x38, 0x90, 0x61, 0xFF, 0x20, 0x02, 0xE0, + 0x00, 0x21, 0x80, 0xB2, 0x91, 0x61, 0xFF, 0xF7, 0xE1, 0xBF, 0x00, 0x00, + 0xEC, 0x24, 0x10, 0x00, 0x03, 0x49, 0x00, 0x20, 0x08, 0x60, 0x03, 0x49, + 0x88, 0x61, 0xC8, 0x61, 0x70, 0x47, 0x00, 0x00, 0x48, 0x09, 0x00, 0x22, + 0xEC, 0x24, 0x10, 0x00, 0x0F, 0x49, 0x10, 0xB5, 0x88, 0x69, 0xB0, 0xF5, + 0x80, 0x3F, 0x06, 0xDB, 0xA0, 0xF5, 0x7F, 0x40, 0xFF, 0x38, 0x88, 0x61, + 0x4F, 0xF6, 0xFF, 0x70, 0x03, 0xE0, 0x30, 0xB1, 0x00, 0x22, 0x80, 0xB2, + 0x8A, 0x61, 0xBD, 0xE8, 0x10, 0x40, 0xFF, 0xF7, 0xBD, 0xBF, 0xCA, 0x69, + 0xFF, 0xF7, 0xDC, 0xFF, 0x12, 0xB1, 0xBD, 0xE8, 0x10, 0x40, 0x10, 0x47, + 0x02, 0x20, 0x00, 0xF0, 0xDD, 0xF8, 0x10, 0xBD, 0xEC, 0x24, 0x10, 0x00, + 0x10, 0xB5, 0x04, 0x46, 0x0F, 0x20, 0xFF, 0xF7, 0x89, 0xFE, 0x0F, 0x20, + 0xFF, 0xF7, 0xD3, 0xFE, 0x09, 0x48, 0x44, 0x60, 0x4F, 0xF0, 0x00, 0x50, + 0xC1, 0x8C, 0x09, 0x06, 0x02, 0xD5, 0x4E, 0xF6, 0x60, 0x21, 0x01, 0xE0, + 0x4A, 0xF6, 0xC8, 0x71, 0x41, 0x65, 0x04, 0x49, 0x01, 0x20, 0x08, 0x60, + 0x09, 0x1D, 0x08, 0x60, 0x10, 0xBD, 0x00, 0x00, 0xEC, 0x24, 0x10, 0x00, + 0xF8, 0x09, 0x00, 0x22, 0x10, 0xB5, 0x02, 0x20, 0xFF, 0xF7, 0x8B, 0xFE, + 0x03, 0x20, 0xFF, 0xF7, 0x88, 0xFE, 0x04, 0x20, 0xFF, 0xF7, 0x85, 0xFE, + 0x05, 0x20, 0xFF, 0xF7, 0x82, 0xFE, 0x06, 0x20, 0xFF, 0xF7, 0x7F, 0xFE, + 0x07, 0x20, 0xFF, 0xF7, 0x7C, 0xFE, 0x08, 0x20, 0xFF, 0xF7, 0x79, 0xFE, + 0x09, 0x20, 0xFF, 0xF7, 0x76, 0xFE, 0x0F, 0x20, 0xFF, 0xF7, 0x73, 0xFE, + 0x4F, 0xF0, 0x00, 0x51, 0x00, 0x20, 0x01, 0xF8, 0x4A, 0x0F, 0x88, 0x71, + 0xF0, 0x22, 0xCA, 0x71, 0x0F, 0x22, 0x4A, 0x70, 0x31, 0x49, 0x08, 0x62, + 0x88, 0x62, 0x08, 0x63, 0x88, 0x60, 0x08, 0x61, 0x88, 0x61, 0xC8, 0x60, + 0x48, 0x61, 0xC8, 0x61, 0x48, 0x62, 0xC8, 0x62, 0x48, 0x63, 0x06, 0x21, + 0x02, 0x20, 0xFF, 0xF7, 0x9A, 0xFE, 0x06, 0x21, 0x03, 0x20, 0xFF, 0xF7, + 0x96, 0xFE, 0x06, 0x21, 0x04, 0x20, 0xFF, 0xF7, 0x92, 0xFE, 0x06, 0x21, + 0x05, 0x20, 0xFF, 0xF7, 0x8E, 0xFE, 0x06, 0x21, 0x08, 0x46, 0xFF, 0xF7, + 0x8A, 0xFE, 0x06, 0x21, 0x07, 0x20, 0xFF, 0xF7, 0x86, 0xFE, 0x06, 0x21, + 0x08, 0x20, 0xFF, 0xF7, 0x82, 0xFE, 0x04, 0x21, 0x09, 0x20, 0xFF, 0xF7, + 0x7E, 0xFE, 0x06, 0x21, 0x0F, 0x20, 0xFF, 0xF7, 0x7A, 0xFE, 0x02, 0x20, + 0xFF, 0xF7, 0x14, 0xFE, 0x03, 0x20, 0xFF, 0xF7, 0x11, 0xFE, 0x04, 0x20, + 0xFF, 0xF7, 0x0E, 0xFE, 0x05, 0x20, 0xFF, 0xF7, 0x0B, 0xFE, 0x06, 0x20, + 0xFF, 0xF7, 0x08, 0xFE, 0x07, 0x20, 0xFF, 0xF7, 0x05, 0xFE, 0x08, 0x20, + 0xFF, 0xF7, 0x02, 0xFE, 0x09, 0x20, 0xFF, 0xF7, 0xFF, 0xFD, 0x02, 0x20, + 0xFF, 0xF7, 0x49, 0xFE, 0x03, 0x20, 0xFF, 0xF7, 0x46, 0xFE, 0x04, 0x20, + 0xFF, 0xF7, 0x43, 0xFE, 0x05, 0x20, 0xFF, 0xF7, 0x40, 0xFE, 0x06, 0x20, + 0xFF, 0xF7, 0x3D, 0xFE, 0x07, 0x20, 0xFF, 0xF7, 0x3A, 0xFE, 0x08, 0x20, + 0xFF, 0xF7, 0x37, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0x09, 0x20, 0xFF, 0xF7, + 0x32, 0xBE, 0x00, 0x00, 0xEC, 0x24, 0x10, 0x00, 0x4F, 0xF0, 0x00, 0x50, + 0x90, 0xF8, 0x4A, 0x00, 0x00, 0x07, 0x00, 0xD0, 0x01, 0x20, 0x70, 0x47, + 0x10, 0xB5, 0x0F, 0x20, 0xFF, 0xF7, 0xF7, 0xFD, 0x4F, 0xF0, 0x00, 0x50, + 0xC0, 0x6C, 0x03, 0x49, 0x20, 0xF0, 0x7F, 0x40, 0x49, 0x68, 0xBD, 0xE8, + 0x10, 0x40, 0x08, 0x47, 0xEC, 0x24, 0x10, 0x00, 0x4F, 0xF0, 0x00, 0x51, + 0xC9, 0x8C, 0x4F, 0xEA, 0x90, 0x10, 0x0A, 0x06, 0x04, 0x49, 0x01, 0xD5, + 0x04, 0x4A, 0x00, 0xE0, 0x04, 0x4A, 0xB2, 0xFB, 0xF0, 0xF0, 0x08, 0x60, + 0x70, 0x47, 0x00, 0x00, 0xEC, 0x24, 0x10, 0x00, 0xC0, 0x27, 0x09, 0x00, + 0xD0, 0xDD, 0x06, 0x00, 0x70, 0x47, 0x00, 0x00, 0x70, 0xB5, 0x05, 0x46, + 0x00, 0x20, 0x02, 0x46, 0x07, 0x4C, 0x09, 0xE0, 0xAB, 0x5C, 0x52, 0x1C, + 0x03, 0xF0, 0x0F, 0x06, 0x04, 0xEB, 0x13, 0x13, 0xA6, 0x5D, 0x1B, 0x78, + 0x30, 0x44, 0x18, 0x44, 0x8A, 0x42, 0xF3, 0xDB, 0x70, 0xBD, 0x00, 0x00, + 0xE8, 0x21, 0x01, 0x00, 0x08, 0xB5, 0x00, 0x90, 0x40, 0x1E, 0x00, 0x90, + 0x00, 0x28, 0xFB, 0xDC, 0x08, 0xBD, 0x00, 0x00, 0xF0, 0xB5, 0x07, 0x46, + 0x00, 0x20, 0x03, 0x46, 0x0A, 0x4E, 0x10, 0xE0, 0x57, 0xF8, 0x23, 0x50, + 0x03, 0x22, 0x4F, 0xEA, 0xC2, 0x0C, 0x45, 0xFA, 0x0C, 0xF4, 0x84, 0xEA, + 0x20, 0x64, 0xE4, 0xB2, 0x52, 0x1E, 0x56, 0xF8, 0x24, 0x40, 0x84, 0xEA, + 0x00, 0x20, 0xF2, 0xD5, 0x5B, 0x1C, 0x8B, 0x42, 0xEC, 0xDB, 0xF0, 0xBD, + 0xF8, 0x21, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x10, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x10, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0x37, 0xB6, 0x00, 0x00, 0xF7, 0xBB, 0x00, 0x00, 0xFD, 0xB9, 0x00, 0x00, + 0xE1, 0xB9, 0x00, 0x00, 0x1B, 0xBA, 0x00, 0x00, 0x19, 0xBA, 0x00, 0x00, + 0x37, 0xB6, 0x00, 0x00, 0xDF, 0xB7, 0x00, 0x00, 0x23, 0xBA, 0x00, 0x00, + 0x21, 0xBA, 0x00, 0x00, 0x1D, 0xBA, 0x00, 0x00, 0x1F, 0xBA, 0x00, 0x00, + 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, + 0x37, 0xB6, 0x00, 0x00, 0x41, 0xB7, 0x00, 0x00, 0x45, 0xB7, 0x00, 0x00, + 0x51, 0xBA, 0x00, 0x00, 0x67, 0xBA, 0x00, 0x00, 0xAB, 0xBA, 0x00, 0x00, + 0xE3, 0xBB, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0x0B, 0xBC, 0x00, 0x00, + 0x25, 0xBA, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0xCD, 0xBA, 0x00, 0x00, + 0x3B, 0xBA, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0x05, 0xB8, 0x00, 0x00, + 0xE1, 0xBA, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0xF7, 0xBA, 0x00, 0x00, + 0x9B, 0xBB, 0x00, 0x00, 0xBF, 0xBB, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, + 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, + 0x93, 0xB9, 0x00, 0x00, 0x51, 0xB9, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, + 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, + 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, 0x37, 0xB6, 0x00, 0x00, + 0x9B, 0xD6, 0x00, 0x00, 0x15, 0xD8, 0x00, 0x00, 0x13, 0xDE, 0x00, 0x00, + 0x7B, 0xDE, 0x00, 0x00, 0x33, 0xDF, 0x00, 0x00, 0xB9, 0xDB, 0x00, 0x00, + 0xB3, 0xD6, 0x00, 0x00, 0x97, 0xD6, 0x00, 0x00, 0x2F, 0xD8, 0x00, 0x00, + 0xAD, 0xDE, 0x00, 0x00, 0xD1, 0xDE, 0x00, 0x00, 0x61, 0xDF, 0x00, 0x00, + 0xDF, 0xDB, 0x00, 0x00, 0x97, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x26, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x65, 0x10, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x0D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x26, 0x01, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x60, 0x69, 0x10, 0x00, 0x40, 0x06, 0x00, 0x00, + 0xDD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x98, 0x26, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xA0, 0x6F, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x61, 0xB4, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xF8, 0x25, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xA0, 0x77, 0x10, 0x00, + 0xD0, 0x02, 0x00, 0x00, 0xDD, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x26, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x70, 0x7A, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x5D, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x30, 0x26, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x70, 0x7B, 0x10, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE9, 0xB5, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x26, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x6C, 0x26, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x80, 0x26, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x48, 0x26, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x3C, 0x26, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x26, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x26, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x90, 0x26, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x78, 0x26, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x26, 0x01, 0x00, + 0x5A, 0xD3, 0xD9, 0xE9, 0x6C, 0xF1, 0x45, 0xF5, 0x9F, 0xF7, 0x3C, 0xF9, + 0x6C, 0xFA, 0x58, 0xFB, 0x18, 0xFC, 0xB9, 0xFC, 0x44, 0xFD, 0xBF, 0xFD, + 0x2E, 0xFE, 0x94, 0xFE, 0xF4, 0xFE, 0x50, 0xFF, 0xA9, 0xFF, 0x00, 0x00, + 0x57, 0x00, 0xB0, 0x00, 0x0C, 0x01, 0x6C, 0x01, 0xD2, 0x01, 0x41, 0x02, + 0xBC, 0x02, 0x47, 0x03, 0xE8, 0x03, 0xA8, 0x04, 0x94, 0x05, 0xC4, 0x06, + 0x61, 0x08, 0xBB, 0x0A, 0x94, 0x0E, 0x27, 0x16, 0xA6, 0x2C, 0x00, 0x00, + 0xBE, 0x94, 0xFE, 0xFF, 0x27, 0xC4, 0xFE, 0xFF, 0x59, 0xF2, 0xFE, 0xFF, + 0x6B, 0x1F, 0xFF, 0xFF, 0x81, 0x4B, 0xFF, 0xFF, 0xC5, 0x76, 0xFF, 0xFF, + 0x67, 0xA1, 0xFF, 0xFF, 0x97, 0xCB, 0xFF, 0xFF, 0x88, 0xF5, 0xFF, 0xFF, + 0x6C, 0x1F, 0x00, 0x00, 0x76, 0x49, 0x00, 0x00, 0xD9, 0x73, 0x00, 0x00, + 0xC6, 0x9E, 0x00, 0x00, 0x6D, 0xCA, 0x00, 0x00, 0xFC, 0xF6, 0x00, 0x00, + 0x9A, 0x24, 0x01, 0x00, 0x66, 0x53, 0x01, 0x00, 0xA3, 0x8D, 0x00, 0x00, + 0x71, 0x74, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0xA2, 0x4F, 0x00, 0x00, + 0xC8, 0x42, 0x00, 0x00, 0x0C, 0x39, 0x00, 0x00, 0x21, 0x32, 0x00, 0x00, + 0xD2, 0x2D, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x9B, 0x2C, 0x00, 0x00, + 0xA9, 0x2F, 0x00, 0x00, 0x40, 0x35, 0x00, 0x00, 0x8B, 0x3D, 0x00, 0x00, + 0xCB, 0x48, 0x00, 0x00, 0x59, 0x57, 0x00, 0x00, 0xAD, 0x69, 0x00, 0x00, + 0x65, 0x80, 0x00, 0x00, 0x30, 0x11, 0x00, 0x00, 0x4C, 0x1C, 0x00, 0x00, + 0x2D, 0x29, 0x00, 0x00, 0x86, 0x37, 0x00, 0x00, 0x24, 0x47, 0x00, 0x00, + 0xE8, 0x57, 0x00, 0x00, 0xC4, 0x69, 0x00, 0x00, 0xB5, 0x7C, 0x00, 0x00, + 0xC4, 0x90, 0x00, 0x00, 0x03, 0xA6, 0x00, 0x00, 0x8F, 0xBC, 0x00, 0x00, + 0x8C, 0xD4, 0x00, 0x00, 0x29, 0xEE, 0x00, 0x00, 0x9C, 0x09, 0x01, 0x00, + 0x2B, 0x27, 0x01, 0x00, 0x24, 0x47, 0x01, 0x00, 0xE6, 0x69, 0x01, 0x00, + 0x98, 0x08, 0x00, 0x00, 0x26, 0x0E, 0x00, 0x00, 0x97, 0x14, 0x00, 0x00, + 0xC3, 0x1B, 0x00, 0x00, 0x92, 0x23, 0x00, 0x00, 0xF4, 0x2B, 0x00, 0x00, + 0xE2, 0x34, 0x00, 0x00, 0x5A, 0x3E, 0x00, 0x00, 0x62, 0x48, 0x00, 0x00, + 0x02, 0x53, 0x00, 0x00, 0x48, 0x5E, 0x00, 0x00, 0x46, 0x6A, 0x00, 0x00, + 0x14, 0x77, 0x00, 0x00, 0xCE, 0x84, 0x00, 0x00, 0x95, 0x93, 0x00, 0x00, + 0x92, 0xA3, 0x00, 0x00, 0xF3, 0xB4, 0x00, 0x00, 0x61, 0x70, 0xFE, 0xFF, + 0xBE, 0x94, 0xFE, 0xFF, 0x6A, 0xB8, 0xFE, 0xFF, 0x66, 0xDB, 0xFE, 0xFF, + 0xB7, 0xFD, 0xFE, 0xFF, 0x6B, 0x1F, 0xFF, 0xFF, 0x91, 0x40, 0xFF, 0xFF, + 0x3A, 0x61, 0xFF, 0xFF, 0x7B, 0x81, 0xFF, 0xFF, 0x67, 0xA1, 0xFF, 0xFF, + 0x13, 0xC1, 0xFF, 0xFF, 0x94, 0xE0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x6C, 0x1F, 0x00, 0x00, 0xED, 0x3E, 0x00, 0x00, 0x99, 0x5E, 0x00, 0x00, + 0x85, 0x7E, 0x00, 0x00, 0x30, 0xA4, 0x00, 0x00, 0xA3, 0x8D, 0x00, 0x00, + 0x43, 0x7A, 0x00, 0x00, 0xAD, 0x69, 0x00, 0x00, 0x8E, 0x5B, 0x00, 0x00, + 0xA2, 0x4F, 0x00, 0x00, 0xB0, 0x45, 0x00, 0x00, 0x8B, 0x3D, 0x00, 0x00, + 0x10, 0x37, 0x00, 0x00, 0x21, 0x32, 0x00, 0x00, 0xAA, 0x2E, 0x00, 0x00, + 0x9B, 0x2C, 0x00, 0x00, 0xEC, 0x2B, 0x00, 0x00, 0x9B, 0x2C, 0x00, 0x00, + 0xAA, 0x2E, 0x00, 0x00, 0x21, 0x32, 0x00, 0x00, 0x10, 0x37, 0x00, 0x00, + 0x48, 0x0A, 0x00, 0x00, 0x30, 0x11, 0x00, 0x00, 0x57, 0x19, 0x00, 0x00, + 0x89, 0x22, 0x00, 0x00, 0xA3, 0x2C, 0x00, 0x00, 0x86, 0x37, 0x00, 0x00, + 0x20, 0x43, 0x00, 0x00, 0x63, 0x4F, 0x00, 0x00, 0x45, 0x5C, 0x00, 0x00, + 0xC4, 0x69, 0x00, 0x00, 0xDE, 0x77, 0x00, 0x00, 0x97, 0x86, 0x00, 0x00, + 0xF6, 0x95, 0x00, 0x00, 0x03, 0xA6, 0x00, 0x00, 0xCB, 0xB6, 0x00, 0x00, + 0x5D, 0xC8, 0x00, 0x00, 0xCB, 0xDA, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, + 0x98, 0x08, 0x00, 0x00, 0xAB, 0x0C, 0x00, 0x00, 0x45, 0x11, 0x00, 0x00, + 0x51, 0x16, 0x00, 0x00, 0xC3, 0x1B, 0x00, 0x00, 0x90, 0x21, 0x00, 0x00, + 0xB1, 0x27, 0x00, 0x00, 0x23, 0x2E, 0x00, 0x00, 0xE2, 0x34, 0x00, 0x00, + 0xEF, 0x3B, 0x00, 0x00, 0x4C, 0x43, 0x00, 0x00, 0xFB, 0x4A, 0x00, 0x00, + 0x02, 0x53, 0x00, 0x00, 0x66, 0x5B, 0x00, 0x00, 0x2F, 0x64, 0x00, 0x00, + 0x65, 0x6D, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x80, 0x2D, 0x00, 0x00, 0x80, 0x36, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x80, 0x5A, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x80, 0x99, 0x00, 0x00, 0x80, 0x82, 0x00, 0x00, 0x00, + 0xB4, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x80, 0xD8, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x00, 0x80, 0xF5, 0x00, 0x00, 0x80, 0xEE, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x80, 0x32, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x1F, 0x01, 0x00, 0x80, 0x68, 0x01, 0x00, 0x00, 0x73, 0x01, 0x00, 0x80, + 0x45, 0x01, 0x00, 0x80, 0x5E, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xAB, 0x01, 0x00, 0x80, 0x9D, 0x01, 0x00, 0x80, 0x86, 0x01, 0x00, 0x00, + 0xF1, 0x01, 0x00, 0x80, 0xEA, 0x01, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, + 0xC7, 0x01, 0x00, 0x80, 0x49, 0x02, 0x00, 0x80, 0x52, 0x02, 0x00, 0x00, + 0x64, 0x02, 0x00, 0x00, 0x7F, 0x02, 0x00, 0x80, 0x08, 0x02, 0x00, 0x00, + 0x13, 0x02, 0x00, 0x80, 0x25, 0x02, 0x00, 0x80, 0x3E, 0x02, 0x00, 0x00, + 0xD0, 0x02, 0x00, 0x00, 0xCB, 0x02, 0x00, 0x80, 0xFD, 0x02, 0x00, 0x80, + 0xE6, 0x02, 0x00, 0x00, 0x91, 0x02, 0x00, 0x80, 0x8A, 0x02, 0x00, 0x00, + 0xBC, 0x02, 0x00, 0x00, 0xA7, 0x02, 0x00, 0x80, 0x60, 0x03, 0x00, 0x00, + 0x7B, 0x03, 0x00, 0x80, 0x4D, 0x03, 0x00, 0x80, 0x56, 0x03, 0x00, 0x00, + 0x21, 0x03, 0x00, 0x80, 0x3A, 0x03, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, + 0x17, 0x03, 0x00, 0x80, 0xF9, 0x03, 0x00, 0x80, 0xE2, 0x03, 0x00, 0x00, + 0xD4, 0x03, 0x00, 0x00, 0xCF, 0x03, 0x00, 0x80, 0xB8, 0x03, 0x00, 0x00, + 0xA3, 0x03, 0x00, 0x80, 0x95, 0x03, 0x00, 0x80, 0x8E, 0x03, 0x00, 0x00, + 0x89, 0x04, 0x00, 0x80, 0x92, 0x04, 0x00, 0x00, 0xA4, 0x04, 0x00, 0x00, + 0xBF, 0x04, 0x00, 0x80, 0xC8, 0x04, 0x00, 0x00, 0xD3, 0x04, 0x00, 0x80, + 0xE5, 0x04, 0x00, 0x80, 0xFE, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, + 0x0B, 0x04, 0x00, 0x80, 0x3D, 0x04, 0x00, 0x80, 0x26, 0x04, 0x00, 0x00, + 0x51, 0x04, 0x00, 0x80, 0x4A, 0x04, 0x00, 0x00, 0x7C, 0x04, 0x00, 0x00, + 0x67, 0x04, 0x00, 0x80, 0xA0, 0x05, 0x00, 0x00, 0xBB, 0x05, 0x00, 0x80, + 0x8D, 0x05, 0x00, 0x80, 0x96, 0x05, 0x00, 0x00, 0xE1, 0x05, 0x00, 0x80, + 0xFA, 0x05, 0x00, 0x00, 0xCC, 0x05, 0x00, 0x00, 0xD7, 0x05, 0x00, 0x80, + 0x39, 0x05, 0x00, 0x80, 0x22, 0x05, 0x00, 0x00, 0x14, 0x05, 0x00, 0x00, + 0x0F, 0x05, 0x00, 0x80, 0x78, 0x05, 0x00, 0x00, 0x63, 0x05, 0x00, 0x80, + 0x55, 0x05, 0x00, 0x80, 0x4E, 0x05, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, + 0xDB, 0x06, 0x00, 0x80, 0xED, 0x06, 0x00, 0x80, 0xF6, 0x06, 0x00, 0x00, + 0x81, 0x06, 0x00, 0x80, 0x9A, 0x06, 0x00, 0x00, 0xAC, 0x06, 0x00, 0x00, + 0xB7, 0x06, 0x00, 0x80, 0x59, 0x06, 0x00, 0x80, 0x42, 0x06, 0x00, 0x00, + 0x74, 0x06, 0x00, 0x00, 0x6F, 0x06, 0x00, 0x80, 0x18, 0x06, 0x00, 0x00, + 0x03, 0x06, 0x00, 0x80, 0x35, 0x06, 0x00, 0x80, 0x2E, 0x06, 0x00, 0x00, + 0xE9, 0x07, 0x00, 0x80, 0xF2, 0x07, 0x00, 0x00, 0xC4, 0x07, 0x00, 0x00, + 0xDF, 0x07, 0x00, 0x80, 0xA8, 0x07, 0x00, 0x00, 0xB3, 0x07, 0x00, 0x80, + 0x85, 0x07, 0x00, 0x80, 0x9E, 0x07, 0x00, 0x00, 0x70, 0x07, 0x00, 0x00, + 0x6B, 0x07, 0x00, 0x80, 0x5D, 0x07, 0x00, 0x80, 0x46, 0x07, 0x00, 0x00, + 0x31, 0x07, 0x00, 0x80, 0x2A, 0x07, 0x00, 0x00, 0x1C, 0x07, 0x00, 0x00, + 0x07, 0x07, 0x00, 0x80, 0x09, 0x09, 0x00, 0x80, 0x12, 0x09, 0x00, 0x00, + 0x24, 0x09, 0x00, 0x00, 0x3F, 0x09, 0x00, 0x80, 0x48, 0x09, 0x00, 0x00, + 0x53, 0x09, 0x00, 0x80, 0x65, 0x09, 0x00, 0x80, 0x7E, 0x09, 0x00, 0x00, + 0x90, 0x09, 0x00, 0x00, 0x8B, 0x09, 0x00, 0x80, 0xBD, 0x09, 0x00, 0x80, + 0xA6, 0x09, 0x00, 0x00, 0xD1, 0x09, 0x00, 0x80, 0xCA, 0x09, 0x00, 0x00, + 0xFC, 0x09, 0x00, 0x00, 0xE7, 0x09, 0x00, 0x80, 0x20, 0x08, 0x00, 0x00, + 0x3B, 0x08, 0x00, 0x80, 0x0D, 0x08, 0x00, 0x80, 0x16, 0x08, 0x00, 0x00, + 0x61, 0x08, 0x00, 0x80, 0x7A, 0x08, 0x00, 0x00, 0x4C, 0x08, 0x00, 0x00, + 0x57, 0x08, 0x00, 0x80, 0xB9, 0x08, 0x00, 0x80, 0xA2, 0x08, 0x00, 0x00, + 0x94, 0x08, 0x00, 0x00, 0x8F, 0x08, 0x00, 0x80, 0xF8, 0x08, 0x00, 0x00, + 0xE3, 0x08, 0x00, 0x80, 0xD5, 0x08, 0x00, 0x80, 0xCE, 0x08, 0x00, 0x00, + 0x40, 0x0B, 0x00, 0x00, 0x5B, 0x0B, 0x00, 0x80, 0x6D, 0x0B, 0x00, 0x80, + 0x76, 0x0B, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x80, 0x1A, 0x0B, 0x00, 0x00, + 0x2C, 0x0B, 0x00, 0x00, 0x37, 0x0B, 0x00, 0x80, 0xD9, 0x0B, 0x00, 0x80, + 0xC2, 0x0B, 0x00, 0x00, 0xF4, 0x0B, 0x00, 0x00, 0xEF, 0x0B, 0x00, 0x80, + 0x98, 0x0B, 0x00, 0x00, 0x83, 0x0B, 0x00, 0x80, 0xB5, 0x0B, 0x00, 0x80, + 0xAE, 0x0B, 0x00, 0x00, 0x69, 0x0A, 0x00, 0x80, 0x72, 0x0A, 0x00, 0x00, + 0x44, 0x0A, 0x00, 0x00, 0x5F, 0x0A, 0x00, 0x80, 0x28, 0x0A, 0x00, 0x00, + 0x33, 0x0A, 0x00, 0x80, 0x05, 0x0A, 0x00, 0x80, 0x1E, 0x0A, 0x00, 0x00, + 0xF0, 0x0A, 0x00, 0x00, 0xEB, 0x0A, 0x00, 0x80, 0xDD, 0x0A, 0x00, 0x80, + 0xC6, 0x0A, 0x00, 0x00, 0xB1, 0x0A, 0x00, 0x80, 0xAA, 0x0A, 0x00, 0x00, + 0x9C, 0x0A, 0x00, 0x00, 0x87, 0x0A, 0x00, 0x80, 0x80, 0x0D, 0x00, 0x00, + 0x9B, 0x0D, 0x00, 0x80, 0xAD, 0x0D, 0x00, 0x80, 0xB6, 0x0D, 0x00, 0x00, + 0xC1, 0x0D, 0x00, 0x80, 0xDA, 0x0D, 0x00, 0x00, 0xEC, 0x0D, 0x00, 0x00, + 0xF7, 0x0D, 0x00, 0x80, 0x19, 0x0D, 0x00, 0x80, 0x02, 0x0D, 0x00, 0x00, + 0x34, 0x0D, 0x00, 0x00, 0x2F, 0x0D, 0x00, 0x80, 0x58, 0x0D, 0x00, 0x00, + 0x43, 0x0D, 0x00, 0x80, 0x75, 0x0D, 0x00, 0x80, 0x6E, 0x0D, 0x00, 0x00, + 0xA9, 0x0C, 0x00, 0x80, 0xB2, 0x0C, 0x00, 0x00, 0x84, 0x0C, 0x00, 0x00, + 0x9F, 0x0C, 0x00, 0x80, 0xE8, 0x0C, 0x00, 0x00, 0xF3, 0x0C, 0x00, 0x80, + 0xC5, 0x0C, 0x00, 0x80, 0xDE, 0x0C, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, + 0x2B, 0x0C, 0x00, 0x80, 0x1D, 0x0C, 0x00, 0x80, 0x06, 0x0C, 0x00, 0x00, + 0x71, 0x0C, 0x00, 0x80, 0x6A, 0x0C, 0x00, 0x00, 0x5C, 0x0C, 0x00, 0x00, + 0x47, 0x0C, 0x00, 0x80, 0xC9, 0x0F, 0x00, 0x80, 0xD2, 0x0F, 0x00, 0x00, + 0xE4, 0x0F, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x80, 0x88, 0x0F, 0x00, 0x00, + 0x93, 0x0F, 0x00, 0x80, 0xA5, 0x0F, 0x00, 0x80, 0xBE, 0x0F, 0x00, 0x00, + 0x50, 0x0F, 0x00, 0x00, 0x4B, 0x0F, 0x00, 0x80, 0x7D, 0x0F, 0x00, 0x80, + 0x66, 0x0F, 0x00, 0x00, 0x11, 0x0F, 0x00, 0x80, 0x0A, 0x0F, 0x00, 0x00, + 0x3C, 0x0F, 0x00, 0x00, 0x27, 0x0F, 0x00, 0x80, 0xE0, 0x0E, 0x00, 0x00, + 0xFB, 0x0E, 0x00, 0x80, 0xCD, 0x0E, 0x00, 0x80, 0xD6, 0x0E, 0x00, 0x00, + 0xA1, 0x0E, 0x00, 0x80, 0xBA, 0x0E, 0x00, 0x00, 0x8C, 0x0E, 0x00, 0x00, + 0x97, 0x0E, 0x00, 0x80, 0x79, 0x0E, 0x00, 0x80, 0x62, 0x0E, 0x00, 0x00, + 0x54, 0x0E, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x80, 0x38, 0x0E, 0x00, 0x00, + 0x23, 0x0E, 0x00, 0x80, 0x15, 0x0E, 0x00, 0x80, 0x0E, 0x0E, 0x00, 0x00, + 0x54, 0x73, 0x6B, 0x5F, 0x53, 0x53, 0x00, 0x00, 0x54, 0x73, 0x6B, 0x5F, + 0x53, 0x53, 0x76, 0x63, 0x00, 0x00, 0x00, 0x00, 0x45, 0x6C, 0x66, 0x5F, + 0x53, 0x73, 0x76, 0x63, 0x00, 0x00, 0x00, 0x00, 0x45, 0x6C, 0x66, 0x5F, + 0x43, 0x6D, 0x64, 0x00, 0x54, 0x73, 0x6B, 0x5F, 0x43, 0x6D, 0x64, 0x00, + 0x53, 0x65, 0x6D, 0x5F, 0x41, 0x66, 0x65, 0x00, 0x54, 0x73, 0x6B, 0x5F, + 0x49, 0x64, 0x6C, 0x65, 0x00, 0x00, 0x00, 0x00, 0x53, 0x65, 0x6D, 0x5F, + 0x4D, 0x53, 0x42, 0x75, 0x66, 0x66, 0x00, 0x00, 0x53, 0x65, 0x6D, 0x5F, + 0x53, 0x53, 0x42, 0x75, 0x66, 0x66, 0x00, 0x00, 0x45, 0x6C, 0x66, 0x5F, + 0x53, 0x63, 0x61, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x54, 0x73, 0x6B, 0x5F, + 0x53, 0x63, 0x61, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x53, 0x65, 0x6D, 0x5F, + 0x45, 0x76, 0x74, 0x46, 0x69, 0x66, 0x6F, 0x00, 0x45, 0x6C, 0x66, 0x5F, + 0x53, 0x73, 0x00, 0x00, 0x53, 0x65, 0x6D, 0x5F, 0x43, 0x61, 0x6C, 0x69, + 0x62, 0x46, 0x72, 0x61, 0x6D, 0x65, 0x73, 0x00, 0x45, 0x6C, 0x66, 0x5F, + 0x4D, 0x74, 0x00, 0x00, 0x54, 0x73, 0x6B, 0x5F, 0x4D, 0x74, 0x00, 0x00, + 0xD0, 0x26, 0x01, 0x00, 0x00, 0x22, 0x10, 0x00, 0x58, 0x03, 0x00, 0x00, + 0x1C, 0x01, 0x00, 0x00, 0x08, 0x27, 0x01, 0x00, 0x00, 0xFC, 0x10, 0x00, + 0xC8, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x08, 0x27, 0x01, 0x00, + 0x58, 0x25, 0x10, 0x00, 0xA8, 0x61, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, + 0x01, 0xFF, 0x01, 0xFF, 0x01, 0x54, 0x1A, 0x10, 0x03, 0x19, 0x04, 0x01, + 0x23, 0x14, 0xC8, 0x1F, 0x01, 0x14, 0x90, 0x5F, 0x01, 0x32, 0x01, 0x32, + 0x02, 0x29, 0x08, 0x41, 0xA9, 0x08, 0x09, 0x1A, 0x0C, 0xA3, 0xE8, 0x03, + 0x29, 0x0C, 0x69, 0x48, 0x29, 0x04, 0x5A, 0x04, 0x24, 0xA9, 0x10, 0x01, + 0x3C, 0x0B, 0x24, 0x4C, 0x86, 0xD4, 0x81, 0x00, 0x30, 0xB5, 0x72, 0xB6, + 0x01, 0x25, 0x0D, 0x4C, 0x25, 0x60, 0x00, 0x23, 0x6A, 0x07, 0x01, 0x29, + 0x10, 0xD0, 0x82, 0xF8, 0x6B, 0x30, 0x00, 0xF0, 0x3F, 0x00, 0x82, 0xF8, + 0x6A, 0x00, 0x08, 0x48, 0x05, 0x60, 0x01, 0x68, 0x00, 0x29, 0xFC, 0xD1, + 0x05, 0x48, 0x1C, 0x30, 0x03, 0x60, 0x23, 0x60, 0x62, 0xB6, 0x30, 0xBD, + 0x40, 0x21, 0x82, 0xF8, 0x6B, 0x10, 0xEC, 0xE7, 0x40, 0x1B, 0x00, 0x22, + 0x5C, 0x0D, 0x00, 0x22, 0x3C, 0xB5, 0x4F, 0xF0, 0x00, 0x51, 0xD0, 0x20, + 0x81, 0xF8, 0xC3, 0x00, 0x40, 0xF2, 0xA7, 0x70, 0xC8, 0x84, 0x45, 0x20, + 0xA1, 0xF8, 0x52, 0x00, 0xA1, 0xF8, 0x52, 0x00, 0x40, 0x23, 0x81, 0xF8, + 0x20, 0x30, 0x44, 0x24, 0x81, 0xF8, 0x20, 0x40, 0x4F, 0xF4, 0x96, 0x75, + 0x00, 0x22, 0xCD, 0xE9, 0x00, 0x52, 0x00, 0x98, 0x40, 0x1E, 0x00, 0x90, + 0x01, 0x98, 0x40, 0x1C, 0x01, 0x90, 0xFA, 0x28, 0xF7, 0xDB, 0x0D, 0xE0, + 0x81, 0xF8, 0x20, 0x30, 0x81, 0xF8, 0x20, 0x40, 0xCD, 0xE9, 0x00, 0x52, + 0x00, 0x98, 0x40, 0x1E, 0x00, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x01, 0x90, + 0xFA, 0x28, 0xF7, 0xDB, 0x91, 0xF8, 0x20, 0x00, 0xC0, 0x06, 0xED, 0xD4, + 0x81, 0xF8, 0xC3, 0x20, 0x41, 0xF2, 0xA4, 0x40, 0xC8, 0x84, 0x40, 0xF2, + 0x57, 0x20, 0x21, 0xF8, 0x52, 0x0F, 0x21, 0xF8, 0x32, 0x09, 0x4C, 0x20, + 0x08, 0x70, 0x3C, 0xBD, 0xA5, 0x02, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x07, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x16, 0xFF, 0x09, 0x9F, 0x05, 0xFF, 0x09, 0x9F, 0x05, 0x03, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0B, 0x0B, 0x0C, 0x37, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x02, 0x32, 0x00, + 0x64, 0x00, 0x64, 0x00, 0x64, 0x10, 0x00, 0x00, 0x03, 0xA9, 0x2A, 0x10, + 0x20, 0x30, 0x0A, 0x08, 0x88, 0x88, 0x00, 0x01, 0x80, 0x00, 0xFF, 0x07, + 0xFF, 0x07, 0xFF, 0x7F, 0x70, 0x00, 0xFF, 0x7F, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, + 0xB0, 0x00, 0x80, 0x00, 0x10, 0x00, 0x40, 0x00, 0x10, 0x00, 0x18, 0x21, + 0x55, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x90, 0x01, 0x00, 0x00, 0x10, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xC8, 0x00, 0xA0, 0x00, 0x00, 0x01, + 0xFF, 0x7F, 0x40, 0x00, 0x20, 0x00, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xC8, 0x00, 0x10, 0x14, + 0x64, 0x00, 0x50, 0x02, 0x00, 0x02, 0xC8, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x11, 0x00, 0xFF, 0x7F, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0xA0, 0x00, + 0x20, 0x04, 0x90, 0x01, 0x02, 0x05, 0x20, 0x03, 0x00, 0x02, 0x04, 0x00, + 0x80, 0x00, 0x50, 0x00, 0x00, 0x01, 0x14, 0x0A, 0x78, 0x00, 0x00, 0x27, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xC8, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x09, 0x14, 0x20, 0x00, 0x00, 0x00, + 0x33, 0x02, 0x13, 0x14, 0x15, 0x00, 0x19, 0x00, 0x53, 0x8D, 0x02, 0x64, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x10, 0x11, + 0x15, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x02, 0x10, 0x00, + 0x40, 0x00, 0x10, 0x00, 0xC0, 0x00, 0x40, 0x62, 0x00, 0x00, 0x80, 0x03, + 0x08, 0x0E, 0x0A, 0x08, 0x01, 0x0A, 0x00, 0x01, 0x08, 0x01, 0x5A, 0x55, + 0x05, 0x00, 0x00, 0x01, 0x30, 0x00, 0x80, 0x01, 0x50, 0x00, 0x20, 0x03, + 0xDC, 0x05, 0xB0, 0x04, 0x78, 0x05, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x4C, 0x00, 0x00, 0x02, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x32, 0x50, 0x14, 0x09, 0xEC, 0x00, 0x40, 0x01, 0x50, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x02, 0xF7, 0x00, 0x01, 0x64, 0x00, 0x00, 0x03, + 0x0A, 0x10, 0x51, 0x5A, 0x10, 0x1F, 0x08, 0x01, 0x0A, 0x0A, 0x20, 0x20, + 0x10, 0x64, 0x10, 0x50, 0xF0, 0xF0, 0x00, 0xA4, 0x20, 0x00, 0xC0, 0x00, + 0x02, 0x02, 0x02, 0x02, 0x10, 0x00, 0xC0, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x02, 0xA4, 0x08, 0x10, 0x84, 0x00, 0x10, 0x1A, 0x16, 0x0A, 0x20, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x08, 0x08, 0x10, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x80, 0x00, 0x14, 0x00, 0xB0, 0x04, 0x06, 0x00, 0x00, 0x06, + 0x04, 0xF0, 0x10, 0x02, 0xC0, 0x02, 0x50, 0x00, 0x70, 0x00, 0xA4, 0x32, + 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x40, 0x00, 0x10, 0x00, 0x2C, 0x01, + 0x40, 0x84, 0x64, 0x00, 0x5A, 0x00, 0x01, 0x04, 0x00, 0x02, 0x00, 0x03, + 0x11, 0x00, 0x10, 0x00, 0x64, 0x00, 0xD2, 0x32, 0xC2, 0x01, 0x00, 0x01, + 0x30, 0x00, 0x00, 0x00, 0x7F, 0x3E, 0x64, 0x00, 0x64, 0x00, 0x14, 0x14, + 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x02, 0x14, 0x00, 0x01, 0x00, 0xAA, + 0x20, 0x03, 0x32, 0x03, 0x43, 0x0A, 0x32, 0x32, 0x00, 0x08, 0x00, 0x80, + 0x10, 0x10, 0x03, 0x08, 0x00, 0x04, 0xE8, 0x80, 0x20, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x11, 0x0D, 0x0D, 0x81, 0x90, 0x01, + 0x50, 0x08, 0x14, 0x01, 0x90, 0x01, 0xBC, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x40, 0x20, 0x20, 0x00, 0x10, 0xA0, 0x00, 0x00, 0xFF, 0x00, + 0x0A, 0x10, 0x10, 0x10, 0x32, 0x40, 0x00, 0x00, 0x01, 0x20, 0x64, 0x64, + 0x77, 0x30, 0x19, 0x10, 0x10, 0x00, 0x01, 0x12, 0x12, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x2C, 0x01, 0xFA, 0x00, 0x0A, 0x02, 0x00, 0x00, + 0x01, 0x20, 0x10, 0x10, 0x20, 0xC8, 0x1E, 0x01, 0x06, 0x3C, 0x50, 0x50, + 0x00, 0xFF, 0x80, 0x00, 0x03, 0x7F, 0x05, 0x0A, 0x1A, 0x06, 0x05, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x05, 0x01, 0x20, 0x30, 0xC8, 0x00, + 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0x82, 0x00, 0xFA, 0x00, 0x00, 0x08, + 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x04, + 0x96, 0x00, 0x32, 0x00, 0xFC, 0x03, 0x3E, 0x01, 0x88, 0x00, 0x31, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x31, 0x00, 0x88, 0x00, 0x3E, 0x01, + 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0xC8, 0xD9, 0xE3, 0xE8, 0xF0, 0xF5, 0xFA, 0xFA, 0xFA, 0xF5, 0xF0, + 0xE8, 0xE3, 0xD9, 0xC8, 0x78, 0x64, 0x64, 0x64, 0x64, 0x64, 0x9C, 0xB6, + 0xBC, 0xC2, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, + 0xC2, 0xBC, 0xB6, 0x9C, 0x64, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x03, 0x05, 0x0A, 0x03, + 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x00, + 0xFF, 0x0F, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x03, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x2C, 0x01, 0xC8, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x14, 0x02, 0x05, 0x03, + 0x03, 0x0A, 0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x05, 0x05, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, + 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0xA2, 0xA2, 0x10, 0x0C, 0xA0, 0x00, 0xA0, 0x00, 0x60, 0x00, + 0x5A, 0x35, 0x35, 0x81, 0xB8, 0x0B, 0x56, 0xBB, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x15, 0x00, + 0x1A, 0x00, 0x20, 0x00, 0x25, 0x00, 0x2A, 0x00, 0x0F, 0x03, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x19, 0x19, 0x00, 0x00, 0x32, + 0x00, 0x00, 0x12, 0x00, 0x24, 0x00, 0x37, 0x00, 0x49, 0x00, 0x5B, 0x00, + 0x88, 0x00, 0xB6, 0x00, 0x10, 0x01, 0xC6, 0x01, 0x7C, 0x02, 0x8C, 0x03, + 0xFF, 0x04, 0x73, 0x06, 0x83, 0x07, 0x39, 0x08, 0xEF, 0x08, 0x49, 0x09, + 0x77, 0x09, 0xA4, 0x09, 0xB6, 0x09, 0xC8, 0x09, 0xDB, 0x09, 0xEE, 0x09, + 0xFF, 0x09, 0x00, 0x00, 0x12, 0x00, 0x24, 0x00, 0x36, 0x00, 0x48, 0x00, + 0x5A, 0x00, 0x87, 0x00, 0xB4, 0x00, 0x0E, 0x01, 0x68, 0x01, 0xC2, 0x01, + 0x1C, 0x02, 0xCF, 0x02, 0x83, 0x03, 0xDD, 0x03, 0x37, 0x04, 0x91, 0x04, + 0xEB, 0x04, 0x18, 0x05, 0x45, 0x05, 0x57, 0x05, 0x69, 0x05, 0x7B, 0x05, + 0x8D, 0x05, 0x9F, 0x05, 0x00, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x2E, 0x00, + 0x3D, 0x00, 0x4C, 0x00, 0x73, 0x00, 0x99, 0x00, 0xE6, 0x00, 0xA5, 0x01, + 0x65, 0x02, 0x84, 0x03, 0xFF, 0x04, 0x7A, 0x06, 0x8F, 0x07, 0x48, 0x08, + 0x02, 0x09, 0x55, 0x09, 0x80, 0x09, 0xAA, 0x09, 0xBB, 0x09, 0xCB, 0x09, + 0xDA, 0x09, 0xEF, 0x09, 0xFF, 0x09, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, + 0x30, 0x00, 0x40, 0x00, 0x50, 0x00, 0x78, 0x00, 0xA0, 0x00, 0xF0, 0x00, + 0x4F, 0x01, 0xAF, 0x01, 0x0F, 0x02, 0xCF, 0x02, 0x8F, 0x03, 0xEF, 0x03, + 0x4F, 0x04, 0xAF, 0x04, 0xFF, 0x04, 0x27, 0x05, 0x4F, 0x05, 0x5F, 0x05, + 0x6F, 0x05, 0x7F, 0x05, 0x8F, 0x05, 0x9F, 0x05, 0x00, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x1E, 0x00, 0x28, 0x00, 0x39, 0x00, 0x64, 0x00, 0x91, 0x00, + 0xEF, 0x00, 0xAB, 0x01, 0x6C, 0x02, 0x89, 0x03, 0xFF, 0x04, 0x76, 0x06, + 0x93, 0x07, 0x54, 0x08, 0x10, 0x09, 0x6E, 0x09, 0x9B, 0x09, 0xC6, 0x09, + 0xD7, 0x09, 0xE1, 0x09, 0xEB, 0x09, 0xFB, 0x09, 0xFF, 0x09, 0x00, 0x00, + 0x04, 0x00, 0x14, 0x00, 0x1E, 0x00, 0x2A, 0x00, 0x37, 0x00, 0x66, 0x00, + 0x8A, 0x00, 0xE9, 0x00, 0x4A, 0x01, 0xB1, 0x01, 0x10, 0x02, 0xCF, 0x02, + 0x8F, 0x03, 0xEE, 0x03, 0x55, 0x04, 0xB6, 0x04, 0x15, 0x05, 0x39, 0x05, + 0x68, 0x05, 0x75, 0x05, 0x81, 0x05, 0x8B, 0x05, 0x9B, 0x05, 0x9F, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xA0, 0x0F, 0xF0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, 0x64, 0x00, + 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x02, 0x50, 0x00, + 0xD0, 0x07, 0xD0, 0x07, 0x00, 0x03, 0x21, 0x10, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x08, 0xC8, 0x00, 0x64, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, + 0x02, 0x03, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0xA0, 0x00, + 0x08, 0x0A, 0x08, 0x22, 0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x30, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x3D, 0x00, 0x38, 0x00, 0x82, 0x1A, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x03, + 0x02, 0x00, 0xAC, 0x0D, 0xF0, 0x0B, 0x01, 0x20, 0x02, 0x00, 0xAC, 0x0D, + 0xF0, 0x0B, 0x01, 0x32, 0x03, 0x00, 0xB8, 0x0B, 0xB8, 0x0B, 0x00, 0x0A, + 0x03, 0x00, 0xB8, 0x0B, 0xB8, 0x0B, 0x00, 0x0A, 0x00, 0x00, 0xE8, 0x03, + 0x0B, 0x00, 0x00, 0x03, 0x00, 0x05, 0x40, 0x06, 0x40, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x0F, 0x00, 0xB8, 0x0B, 0xB8, 0x0B, 0xB8, 0x0B, + 0xB8, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0F, 0x00, 0xB8, 0x0B, + 0xB8, 0x0B, 0xB8, 0x0B, 0xB8, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x1D, 0x00, 0x01, 0x30, 0x0F, 0x20, 0x40, 0x00, 0x01, 0x13, 0x0D, + 0x08, 0x40, 0x00, 0x00, 0x13, 0x0D, 0x00, 0x81, 0x10, 0x10, 0x92, 0x92, + 0x01, 0x60, 0x0F, 0x15, 0x90, 0x00, 0x97, 0x92, 0x0C, 0x0C, 0x00, 0x00, + 0x19, 0x01, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x01, 0x34, 0x2C, 0x0A, + 0x03, 0x63, 0x16, 0x53, 0x16, 0x00, 0x0A, 0x01, 0x12, 0x16, 0x81, 0x02, + 0x60, 0x00, 0x08, 0x01, 0xA0, 0x00, 0x35, 0x35, 0x06, 0x00, 0x16, 0x18, + 0x16, 0x18, 0x81, 0x02, 0x60, 0x00, 0x08, 0x01, 0xA0, 0x00, 0x35, 0x35, + 0x06, 0x00, 0x96, 0xD8, 0x96, 0xD8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x01, 0x00, 0x00, 0x00, 0x16, 0x09, 0x20, 0x15, 0x01, 0x00, 0x00, + 0x01, 0x39, 0x01, 0x16, 0x09, 0x20, 0x15, 0x00, 0x24, 0x01, 0xA2, 0xD9, + 0x6B, 0x9D, 0x82, 0x06, }; - #endif diff --git a/drivers/input/touchscreen/st/fts_gui.c b/drivers/input/touchscreen/st/fts_gui.c index f695137ada09c32b54c8bd666be85d6de8a3707a..ba072183a2648aa3be3aecf3c305076a0e095409 100644 --- a/drivers/input/touchscreen/st/fts_gui.c +++ b/drivers/input/touchscreen/st/fts_gui.c @@ -1,3 +1,24 @@ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + + #include #include #include @@ -12,17 +33,13 @@ #include #include #include +//#include +#include #include #include #include #include "fts.h" -#include "fts_lib/ftsCompensation.h" #include "fts_lib/ftsIO.h" -#include "fts_lib/ftsError.h" -#include "fts_lib/ftsFrame.h" -#include "fts_lib/ftsTest.h" -#include "fts_lib/ftsTime.h" -#include "fts_lib/ftsTool.h" #ifdef SCRIPTLESS @@ -31,116 +48,131 @@ unsigned char pAddress_i2c[CMD_RESULT_STR_LEN] = {0}; int byte_count_read; char Out_buff[TSP_BUF_SIZE]; + /*I2C CMd functions: functions to interface with GUI without script */ ssize_t fts_i2c_wr_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); int i; char buff[16]; + memset(Out_buff, 0x00, ARRAY_SIZE(Out_buff)); if (byte_count_read == 0) { snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); - return snprintf(buf, TSP_BUF_SIZE, "{%s}\n", Out_buff); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); } #ifdef SCRIPTLESS_DEBUG - printk("%s:DATA READ {", __func__); + pr_err("%s:DATA READ {", __func__); for (i = 0; i < byte_count_read; i++) { - printk(" %02X", (unsigned int)info->cmd_wr_result[i]); - if (i < (byte_count_read-1)) { - printk(" "); - } + pr_err(" %02X", (unsigned int)info->cmd_wr_result[i]); + if (i < (byte_count_read - 1)) + pr_err(" "); } - printk("}\n"); + pr_err("}\n"); #endif snprintf(buff, sizeof(buff), "{"); - strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); - for (i = 0; i < (byte_count_read+2); i++) { - if ((i == 0)) { - char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; - snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); } else if (i == 1) { - char temp_byte_count_read = (byte_count_read) & 0xFF; - snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + temp_byte_count_read = (byte_count_read) & 0xFF; + + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); } else { - snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i-2]); + snprintf(buff, sizeof(buff), "%02X", + info->cmd_wr_result[i-2]); } - /* snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i]); */ + //snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i]); strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); - if (i < (byte_count_read+1)) { + if (i < (byte_count_read + 1)) { snprintf(buff, sizeof(buff), " "); - strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); } } snprintf(buff, sizeof(buff), "}"); - strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); } ssize_t fts_i2c_wr_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { int ret; struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); - unsigned char pAddress[8] = {0}; - unsigned int byte_count = 0 ; - int i ; + unsigned char pAddress[9] = {0}; + unsigned int byte_count = 0; + int i; + unsigned int data[9] = {0}; - unsigned int data[8] = {0}; memset(data, 0x00, ARRAY_SIZE(data)); memset(info->cmd_wr_result, 0x00, ARRAY_SIZE(info->cmd_wr_result)); - sscanf(buf, "%x %x %x %x %x %x %x %x ", (data+7), (data), (data+1), - (data+2), (data+3), (data+4), (data+5), (data+6)); - - byte_count = data[7]; + ret = sscanf(buf, "%x %x %x %x %x %x %x %x %x ", + (data + 8), (data), (data + 1), (data + 2), (data + 3), + (data + 4), (data + 5), (data + 6), (data + 7)); + if (ret != 9) + return -EINVAL; + byte_count = data[8]; - /*if (sizeof(buf) != byte_count ) - { - printk("%s : Byte count is wrong\n",__func__); - return count; - }*/ + /** + * if(sizeof(buf) != byte_count ) + * { + * printk("%s : Byte count is wrong\n",__func__); + * return count; + * } + */ #ifdef SCRIPTLESS_DEBUG - printk("\n"); - printk("%s: Input Data 1:", __func__); + pr_err("\n"); + pr_err("%s: Input Data 1:", __func__); - for (i = 0 ; i < 7; i++) { - printk(" %02X", data[i]); + for (i = 0 ; i < byte_count; i++) { + pr_err(" %02X", data[i]); pAddress[i] = (unsigned char)data[i]; } - printk("\n"); + pr_err("\n"); #else - for (i = 0 ; i < 7; i++) { + for (i = 0 ; i < byte_count; i++) pAddress[i] = (unsigned char)data[i]; - } #endif - byte_count_read = data[byte_count-1]; + byte_count_read = (((unsigned int)data[byte_count - 2]) << 8) + | data[byte_count - 1]; ret = fts_writeCmd(pAddress, 3); msleep(20); - ret = fts_readCmd(&pAddress[3], (byte_count-4), info->cmd_wr_result, - byte_count_read); + ret = fts_readCmd(&pAddress[3], (byte_count - 5), + info->cmd_wr_result, byte_count_read); #ifdef SCRIPTLESS_DEBUG - printk("%s:DATA READ\n{", __func__); - for (i = 0; i < (2+byte_count_read); i++) { - if ((i == 0)) { - char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; - printk("%02X", (unsigned int)temp_byte_count_read); + pr_err("%s:DATA READ\n{", __func__); + for (i = 0; i < (2 + byte_count_read); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + pr_err("%02X", (unsigned int)temp_byte_count_read); } else if (i == 1) { - char temp_byte_count_read = (byte_count_read) & 0xFF; - printk("%02X", (unsigned int)temp_byte_count_read); + temp_byte_count_read = (byte_count_read) & 0xFF; + pr_err("%02X", (unsigned int)temp_byte_count_read); } else { - printk("%02X", (unsigned int)info->cmd_read_result[i-2]); - } - if (i < (byte_count_read+1)) { - printk(" "); + pr_err("%02X", + (unsigned int)info->cmd_read_result[i - 2]); } + if (i < (byte_count_read + 1)) + pr_err(" "); + } - printk("}\n"); + pr_err("}\n"); #endif if (ret) dev_err(dev, "Unable to read register\n"); @@ -148,165 +180,188 @@ ssize_t fts_i2c_wr_store(struct device *dev, struct device_attribute *attr, } ssize_t fts_i2c_read_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); - int i ; + int i; char buff[16]; memset(Out_buff, 0x00, ARRAY_SIZE(Out_buff)); if (byte_count_read == 0) { snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); - return snprintf(buf, TSP_BUF_SIZE, "{%s}\n", Out_buff); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); } #ifdef SCRIPTLESS_DEBUG - printk("%s:DATA READ {", __func__); + pr_err("%s:DATA READ {", __func__); for (i = 0; i < byte_count_read; i++) { - printk("%02X", (unsigned int)info->cmd_read_result[i]); - if (i < (byte_count_read-1)) { - printk(" "); - } + pr_err("%02X", (unsigned int)info->cmd_read_result[i]); + if (i < (byte_count_read - 1)) + pr_err(" "); } - printk("}\n"); + pr_err("}\n"); #endif snprintf(buff, sizeof(buff), "{"); - strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); - for (i = 0; i < (byte_count_read+2); i++) { - if ((i == 0)) { - char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; - snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); - } else if (i == 1) { - char temp_byte_count_read = (byte_count_read) & 0xFF; - snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); } else { - snprintf(buff, sizeof(buff), "%02X", info->cmd_read_result[i-2]); + snprintf(buff, sizeof(buff), "%02X", + info->cmd_read_result[i - 2]); } - strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); - if (i < (byte_count_read+1)) { + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + if (i < (byte_count_read + 1)) { snprintf(buff, sizeof(buff), " "); - strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); } } snprintf(buff, sizeof(buff), "}"); - strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); } ssize_t fts_i2c_read_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { int ret; struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); - unsigned char pAddress[8] = {0}; + unsigned char pAddress[9] = {0}; unsigned int byte_count = 0; - int i ; - unsigned int data[8] = {0}; + int i; + unsigned int data[9] = {0}; byte_count_read = 0; memset(data, 0x00, ARRAY_SIZE(data)); memset(info->cmd_read_result, 0x00, ARRAY_SIZE(info->cmd_read_result)); - sscanf(buf, "%x %x %x %x %x %x %x %x ", (data+7), (data), (data+1), (data+2), (data+3), (data+4), (data+5), (data+6)); - byte_count = data[7]; + ret = sscanf(buf, "%x %x %x %x %x %x %x %x %x ", + (data + 8), (data), (data + 1), (data + 2), (data + 3), + (data + 4), (data + 5), (data + 6), (data + 7)); + if (ret != 9) + return -EINVAL; + byte_count = data[8]; - if (byte_count > 7) { + if (byte_count > 8) { #ifdef SCRIPTLESS_DEBUG - printk("%s : Byte count is more than 7\n", __func__); + pr_err("%s:Byte count is more than 8\n", __func__); #endif return count; } - /*if (sizeof(buf) != byte_count ) - { - printk("%s : Byte count is wrong\n",__func__); - return count; - }*/ + /*if(sizeof(buf) != byte_count )*/ + /*{*/ + /* printk("%s : Byte count is wrong\n",__func__);*/ + /* return count;*/ + /*}*/ #ifdef SCRIPTLESS_DEBUG - printk("\n"); - printk("%s: Input Data 1:", __func__); + pr_err("\n"); + pr_err("%s: Input Data 1:", __func__); for (i = 0 ; i < byte_count; i++) { - printk(" %02X", data[i]); + pr_err("%02X", data[i]); pAddress[i] = (unsigned char)data[i]; } - printk("\n"); + pr_err("\n"); #else - for (i = 0 ; i < byte_count; i++) { + for (i = 0 ; i < byte_count; i++) pAddress[i] = (unsigned char)data[i]; - } #endif - byte_count_read = data[byte_count-1]; - ret = fts_readCmd(pAddress, (byte_count-1), info->cmd_read_result, byte_count_read); + byte_count_read = (((unsigned int)data[byte_count - 2]) << 8) + | data[byte_count - 1]; + ret = fts_readCmd(pAddress, (byte_count - 2), info->cmd_read_result, + byte_count_read); #ifdef SCRIPTLESS_DEBUG - printk("%s:DATA READ\n{", __func__); - for (i = 0; i < (byte_count_read+2); i++) { - if ((i == 0)) { - char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; - printk("%02X", (unsigned int)temp_byte_count_read); + pr_err("%s:DATA READ\n{", __func__); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + + pr_err("%02X", (unsigned int)temp_byte_count_read); } else if (i == 1) { - char temp_byte_count_read = (byte_count_read) & 0xFF; - printk("%02X", (unsigned int)temp_byte_count_read); + temp_byte_count_read = (byte_count_read) & 0xFF; + + pr_err("%02X", (unsigned int)temp_byte_count_read); } else { - printk("%02X", (unsigned int)info->cmd_read_result[i-2]); - } - if (i < (byte_count_read+1)) { - printk(" "); + pr_err("%02X", + (unsigned int)info->cmd_read_result[i - 2]); } + if (i < (byte_count_read + 1)) + pr_err(" "); } - printk("}\n"); + pr_err("}\n"); #endif if (ret) dev_err(dev, "Unable to read register\n"); return count; } -ssize_t fts_i2c_write_show(struct device *dev, struct device_attribute *attr, - char *buf) + +ssize_t fts_i2c_write_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); + return snprintf(buf, TSP_BUF_SIZE, "%s", info->cmd_write_result); } ssize_t fts_i2c_write_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { int ret; struct i2c_client *client = to_i2c_client(dev); struct fts_ts_info *info = i2c_get_clientdata(client); unsigned int byte_count = 0; - int i ; + int i; + memset(data, 0x00, ARRAY_SIZE(data)); memset(pAddress_i2c, 0x00, ARRAY_SIZE(pAddress_i2c)); - memset(info->cmd_write_result, 0x00, ARRAY_SIZE(info->cmd_write_result)); - sscanf(buf, "%x %x", data, (data + 1)); + memset(info->cmd_write_result, 0x00, + ARRAY_SIZE(info->cmd_write_result)); + ret = sscanf(buf, "%x %x", data, (data + 1)); + if (ret != 2) + return -EINVAL; byte_count = data[0] << 8 | data[1]; if (byte_count <= ARRAY_SIZE(pAddress_i2c)) { for (i = 0; i < (byte_count); i++) { - sscanf(&buf[3*(i+2)], "%x ", (data+i)); - } } else { + ret = sscanf(&buf[3 * (i + 2)], "%x ", (data + i)); + if (ret != 1) + return -EINVAL; + } + } else { #ifdef SCRIPTLESS_DEBUG - printk("%s : message size is more than allowed limit of 512 bytes\n", __func__); + pr_err("%s:message size is > allowed limit of 512 bytes\n", + __func__); #endif - snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write NOT OK}\n"); + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write NOT OK}\n"); } #ifdef SCRIPTLESS_DEBUG - printk("\n"); - printk("%s: Byte_count= %02d | Count = %02d | size of buf:%02d\n", __func__, byte_count, (int)count, (int)sizeof(buf)); - printk("%s: Input Data 1:", __func__); + pr_err("\n"); + pr_err("%s:Byte_count = %02d|Count = %02d |size of buf:%02d\n", + __func__, byte_count, (int)count, (int)sizeof(buf)); + pr_err("%s: Input Data 1:", __func__); for (i = 0 ; i < byte_count; i++) { - printk("%02X", data[i]); + pr_err(" %02X", data[i]); pAddress_i2c[i] = (unsigned char)data[i]; } - printk("\n"); + pr_err("\n"); #else - for (i = 0; i < byte_count; i++) { + for (i = 0 ; i < byte_count; i++) pAddress_i2c[i] = (unsigned char)data[i]; - } #endif if ((pAddress_i2c[0] == 0xb3) && (pAddress_i2c[3] == 0xb1)) { ret = fts_writeCmd(pAddress_i2c, 3); @@ -317,30 +372,31 @@ ssize_t fts_i2c_write_store(struct device *dev, struct device_attribute *attr, } #ifdef SCRIPTLESS_DEBUG - printk("%s:DATA :", __func__); - for (i = 0; i < byte_count; i++) { - printk(" %02X", (unsigned int)pAddress_i2c[i]); - } - printk(" byte_count: %02X\n", byte_count); + pr_err("%s:DATA :", __func__); + for (i = 0; i < byte_count; i++) + pr_err(" %02X", (unsigned int)pAddress_i2c[i]); + pr_err(" byte_count: %02X\n", byte_count); #endif if (ret < 0) { dev_err(dev, "{Write NOT OK}\n"); - snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write NOT OK}\n"); + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write NOT OK}\n"); } else { - snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write OK}\n"); + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write OK}\n"); #ifdef SCRIPTLESS_DEBUG - printk("%s : {Write OK}\n", __func__); + pr_err("%s : {Write OK}\n", __func__); #endif } return count; } -static DEVICE_ATTR(iread, (S_IWUSR|S_IWGRP), NULL, fts_i2c_read_store); -static DEVICE_ATTR(iread_result, (S_IRUSR|S_IRGRP), fts_i2c_read_show, NULL); -static DEVICE_ATTR(iwr, (S_IWUSR|S_IWGRP), NULL, fts_i2c_wr_store); -static DEVICE_ATTR(iwr_result, (S_IRUSR|S_IRGRP), fts_i2c_wr_show, NULL); -static DEVICE_ATTR(iwrite, (S_IWUSR|S_IWGRP), NULL, fts_i2c_write_store); -static DEVICE_ATTR(iwrite_result, (S_IRUSR|S_IRGRP), fts_i2c_write_show, NULL); +static DEVICE_ATTR(iread, 0664, NULL, fts_i2c_read_store); +static DEVICE_ATTR(iread_result, 0664, fts_i2c_read_show, NULL); +static DEVICE_ATTR(iwr, 0664, NULL, fts_i2c_wr_store); +static DEVICE_ATTR(iwr_result, 0664, fts_i2c_wr_show, NULL); +static DEVICE_ATTR(iwrite, 0664, NULL, fts_i2c_write_store); +static DEVICE_ATTR(iwrite_result, 0664, fts_i2c_write_show, NULL); static struct attribute *i2c_cmd_attributes[] = { &dev_attr_iread.attr, @@ -356,4 +412,4 @@ struct attribute_group i2c_cmd_attr_group = { .attrs = i2c_cmd_attributes, }; -#endif + #endif diff --git a/drivers/input/touchscreen/st/fts_lib/Makefile b/drivers/input/touchscreen/st/fts_lib/Makefile index 24eca48fc3cd119ce888a8fd30a878b6239378fc..f8bc76c19a91f177ea90065d8fd350673a479ed4 100644 --- a/drivers/input/touchscreen/st/fts_lib/Makefile +++ b/drivers/input/touchscreen/st/fts_lib/Makefile @@ -2,6 +2,7 @@ # Makefile for the FTS touchscreen driver. # -obj-$(CONFIG_TOUCHSCREEN_ST_I2C) += ftsCompensation.o \ +obj-$(CONFIG_TOUCHSCREEN_ST) += ftsCompensation.o \ ftsCrossCompile.o ftsError.o ftsFrame.o ftsIO.o ftsTest.o \ ftsTime.o ftsTool.o ftsFlash.o ftsGesture.o + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c index 2ca0067f34b0c09fe621bf2a090112c8c64c5bfc..d394ca64be4067b601f1d262ea1bec76f7032a90 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c @@ -1,24 +1,36 @@ /* -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS functions for getting Initialization Data * -* * -************************************************************************** -************************************************************************** -*/ - -#include "ftsCrossCompile.h" -#include "ftsCompensation.h" -#include "ftsError.h" -#include "ftsFrame.h" -#include "ftsHardware.h" -#include "ftsIO.h" -#include "ftsSoftware.h" -#include "ftsTool.h" + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting Initialization Data * + * * + ************************************************************************** + ************************************************************************** + */ #include #include @@ -44,23 +56,33 @@ #include #include #include -/* #include */ +//#include + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" -static char tag[8] = "[ FTS ]\0"; -chipInfo ftsInfo; +static char tag[8] = "[ FTS ]\0"; +struct chipInfo ftsInfo; int requestCompensationData(u16 type) { int retry = 0; int ret; + char *temp = NULL; u16 answer; int event_to_search[3]; u8 readEvent[FIFO_EVENT_SIZE]; - u8 cmd[3] = { FTS_CMD_REQU_COMP_DATA, 0x00, 0x00 }; - /* B8 is the command for asking compensation data */ + u8 cmd[3] = { FTS_CMD_REQU_COMP_DATA, 0x00, 0x00}; + /* B8 is the command for asking compensation data*/ u16ToU8(type, &cmd[1]); event_to_search[0] = (int)EVENTID_COMP_DATA_READ; @@ -68,16 +90,23 @@ int requestCompensationData(u16 type) event_to_search[2] = cmd[2]; while (retry < COMP_DATA_READ_RETRY) { - logError(0, "%s %s", tag, printHex("Command = ", cmd, 3)); + temp = printHex("Command = ", cmd, 3); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); ret = fts_writeFwCmd(cmd, 3); - /* send the request to the chip to load in memory the Compensation Data */ + /*send the request to the chip to load*/ + /*in memory the Compensation Data*/ if (ret < OK) { - logError(1, "%s requestCompensationData: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } - ret = pollForEvent(event_to_search, 3, readEvent, TIMEOUT_REQU_COMP_DATA); + ret = pollForEvent(event_to_search, 3, readEvent, + TIMEOUT_REQU_COMP_DATA); if (ret < OK) { - logError(0, "%s Event did not Found at %d attemp! \n", tag, retry+1); + logError(0, "%s Event did not Found at %d attemp!\n", + tag, retry + 1); retry += 1; } else { retry = 0; @@ -86,45 +115,55 @@ int requestCompensationData(u16 type) } if (retry == COMP_DATA_READ_RETRY) { - logError(1, "%s requestCompensationData: ERROR %02X\n", tag, ERROR_TIMEOUT); - return ERROR_TIMEOUT; + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; } u8ToU16_le(&readEvent[1], &answer); if (answer == type) return OK; - logError(1, "%s The event found has a different type of Compensation data ERROR %02X \n", tag, ERROR_DIFF_COMP_TYPE); + + logError(1, "%sThe event found has a different type of ", tag); + logError(1, "Compensation data %02X\n", ERROR_DIFF_COMP_TYPE); return ERROR_DIFF_COMP_TYPE; } -int readCompensationDataHeader(u16 type, DataHeader *header, u16 *address) +int readCompensationDataHeader(u16 type, struct DataHeader *header, + u16 *address) { - u16 offset = ADDR_FRAMEBUFFER_DATA; u16 answer; u8 data[COMP_DATA_HEADER]; - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, COMP_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readCompensationDataHeader: ERROR %02X \n", tag, ERROR_I2C_R); + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, COMP_DATA_HEADER, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } - logError(0, "%s Read Data Header done! \n", tag); + logError(0, "%s Read Data Header done!\n", tag); if (data[0] != HEADER_SIGNATURE) { - logError(1, "%s readCompensationDataHeader: ERROR %02X The Header Signature was wrong! %02X != %02X \n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + logError(1, "%s %s:%02X The Header Signature was wrong!", + tag, __func__, ERROR_WRONG_COMP_SIGN); + logError(1, "%02X != %02X\n", data[0], HEADER_SIGNATURE); + return ERROR_WRONG_COMP_SIGN; } + u8ToU16_le(&data[1], &answer); if (answer != type) { - logError(1, "%s readCompensationDataHeader: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; } - logError(0, "%s Type of Compensation data OK! \n", tag); + logError(0, "%s Type of Compensation data OK!\n", tag); header->type = type; header->force_node = (int)data[4]; @@ -136,110 +175,123 @@ int readCompensationDataHeader(u16 type, DataHeader *header, u16 *address) } -int readMutualSenseGlobalData(u16 *address, MutualSenseData *global) +int readMutualSenseGlobalData(u16 *address, struct MutualSenseData *global) { u8 data[COMP_DATA_GLOBAL]; - logError(0, "%s Address for Global data= %02X \n", tag, *address); + logError(0, "%s Address for Global data= %02X\n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readMutualSenseGlobalData: ERROR %02X\n", tag, ERROR_I2C_R); return ERROR_I2C_R; } - logError(0, "%s Global data Read !\n", tag); + logError(0, "%s Global data Read!\n", tag); global->tuning_ver = data[0]; global->cx1 = data[1]; - logError(0, "%s tuning_ver = %d CX1 = %d \n", tag, global->tuning_ver, global->cx1); + logError(0, "%s tuning_ver = %d CX1 = %d\n", + tag, global->tuning_ver, global->cx1); *address += COMP_DATA_GLOBAL; return OK; - } -int readMutualSenseNodeData(u16 address, MutualSenseData *node) -{ + +int readMutualSenseNodeData(u16 address, struct MutualSenseData *node) +{ int size = node->header.force_node*node->header.sense_node; - logError(0, "%s Address for Node data = %02X \n", tag, address); + logError(0, "%s Address for Node data = %02X\n", tag, address); - node->node_data = (u8 *)kmalloc(size*(sizeof(u8)), GFP_KERNEL); + node->node_data = (u8 *)kmalloc_array(size, (sizeof(u8)), GFP_KERNEL); if (node->node_data == NULL) { - logError(1, "%s readMutualSenseNodeData: ERROR %02X", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } - logError(0, "%s Node Data to read %d bytes \n", tag, size); + logError(0, "%s Node Data to read %d bytes\n", tag, size); - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, node->node_data, size, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readMutualSenseNodeData: ERROR %02X \n", tag, ERROR_I2C_R); + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, node->node_data, + size, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_I2C_R); + kfree(node->node_data); return ERROR_I2C_R; } node->node_data_size = size; - logError(0, "%s Read node data ok! \n", tag); + logError(0, "%s Read node data ok!\n", tag); return size; - } -int readMutualSenseCompensationData(u16 type, MutualSenseData *data) -{ +int readMutualSenseCompensationData(u16 type, struct MutualSenseData *data) +{ int ret; u16 address; - if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER || - type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { - logError(1, "%s readMutualSenseCompensationData: Choose a MS type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + data->node_data = NULL; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER + || type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { + logError(1, "%s %s: Choose a MS type of compensation data ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } ret = requestCompensationData(type); if (ret < 0) { - logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); return (ret|ERROR_REQU_COMP_DATA); } ret = readCompensationDataHeader(type, &(data->header), &address); if (ret < 0) { - logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); - return (ret|ERROR_COMP_DATA_HEADER); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); } ret = readMutualSenseGlobalData(&address, data); if (ret < 0) { - logError(1, "%s readMutualSenseCompensationData: ERROR %02X \n", tag, ERROR_COMP_DATA_GLOBAL); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); return (ret|ERROR_COMP_DATA_GLOBAL); } ret = readMutualSenseNodeData(address, data); if (ret < 0) { - logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_NODE); - return (ret|ERROR_COMP_DATA_NODE); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_NODE); + return (ret | ERROR_COMP_DATA_NODE); } return OK; - } -int readSelfSenseGlobalData(u16 *address, SelfSenseData *global) -{ +int readSelfSenseGlobalData(u16 *address, struct SelfSenseData *global) +{ u8 data[COMP_DATA_GLOBAL]; - logError(0, "%s Address for Global data= %02X \n", tag, *address); + logError(0, "%s Address for Global data= %02X\n", tag, *address); - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readSelfSenseGlobalData: ERROR %02X \n", tag, ERROR_I2C_R); + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } - logError(0, "%s Global data Read !\n", tag); + logError(0, "%s Global data Read!\n", tag); global->tuning_ver = data[0]; global->f_ix1 = data[1]; @@ -249,97 +301,141 @@ int readSelfSenseGlobalData(u16 *address, SelfSenseData *global) global->f_max_n = data[5]; global->s_max_n = data[6]; - logError(0, "%s tuning_ver = %d f_ix1 = %d s_ix1 = %d f_cx1 = %d s_cx1 = %d \n", tag, global->tuning_ver, global->f_ix1, global->s_ix1, global->f_cx1, global->s_cx1); - logError(0, "%s max_n = %d s_max_n = %d \n", tag, global->f_max_n, global->s_max_n); + logError(0, + "%stuning_ver = %df_ix1 = %ds_ix1 = %df_cx1 = %d s_cx1 = %d\n", + tag, global->tuning_ver, global->f_ix1, + global->s_ix1, global->f_cx1, global->s_cx1); + logError(0, "%s max_n = %d s_max_n = %d\n", + tag, global->f_max_n, global->s_max_n); *address += COMP_DATA_GLOBAL; - return OK; - } -int readSelfSenseNodeData(u16 address, SelfSenseData *node) +int readSelfSenseNodeData(u16 address, struct SelfSenseData *node) { - - int size = node->header.force_node*2+node->header.sense_node*2; + int size = node->header.force_node * 2 + node->header.sense_node * 2; u8 data[size]; - node->ix2_fm = (u8 *)kmalloc(node->header.force_node*(sizeof(u8)), GFP_KERNEL); - node->cx2_fm = (u8 *)kmalloc(node->header.force_node*(sizeof(u8)), GFP_KERNEL); - node->ix2_sn = (u8 *)kmalloc(node->header.sense_node*(sizeof(u8)), GFP_KERNEL); - node->cx2_sn = (u8 *)kmalloc(node->header.sense_node*(sizeof(u8)), GFP_KERNEL); + node->ix2_fm = (u8 *)kmalloc_array(node->header.force_node, + sizeof(u8), GFP_KERNEL); + if (node->ix2_fm == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } - if (node->ix2_fm == NULL || node->cx2_fm == NULL || node->ix2_sn == NULL - || node->cx2_sn == NULL) { - logError(1, "%s readSelfSenseNodeData: ERROR %02X", tag, ERROR_ALLOC); + node->cx2_fm = (u8 *)kmalloc_array(node->header.force_node, + sizeof(u8), GFP_KERNEL); + if (node->cx2_fm == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + return ERROR_ALLOC; + } + node->ix2_sn = (u8 *)kmalloc_array(node->header.sense_node, + sizeof(u8), GFP_KERNEL); + if (node->ix2_sn == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + return ERROR_ALLOC; + } + node->cx2_sn = (u8 *)kmalloc_array(node->header.sense_node, + sizeof(u8), GFP_KERNEL); + if (node->cx2_sn == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); return ERROR_ALLOC; } - logError(0, "%s Address for Node data = %02X \n", tag, address); + logError(0, "%s Address for Node data = %02X\n", tag, address); - logError(0, "%s Node Data to read %d bytes \n", tag, size); + logError(0, "%s Node Data to read %d bytes\n", tag, size); - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readSelfSenseNodeData: ERROR %02X\n", tag, ERROR_I2C_R); + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + kfree(node->cx2_sn); return ERROR_I2C_R; } - logError(0, "%s Read node data ok! \n", tag); + logError(0, "%s Read node data ok!\n", tag); memcpy(node->ix2_fm, data, node->header.force_node); - memcpy(node->ix2_sn, &data[node->header.force_node], node->header.sense_node); - memcpy(node->cx2_fm, &data[node->header.force_node + node->header.sense_node], node->header.force_node); - memcpy(node->cx2_sn, &data[node->header.force_node*2 + node->header.sense_node], node->header.sense_node); + memcpy(node->ix2_sn, &data[node->header.force_node], + node->header.sense_node); + memcpy(node->cx2_fm, + &data[node->header.force_node + node->header.sense_node], + node->header.force_node); + memcpy(node->cx2_sn, + &data[node->header.force_node * 2 + node->header.sense_node], + node->header.sense_node); return OK; - } -int readSelfSenseCompensationData(u16 type, SelfSenseData *data) +int readSelfSenseCompensationData(u16 type, struct SelfSenseData *data) { int ret; u16 address; - if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER || type == SS_PROXIMITY)) { - logError(1, "%s readSelfSenseCompensationData: Choose a SS type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + data->ix2_fm = NULL; + data->cx2_fm = NULL; + data->ix2_sn = NULL; + data->cx2_sn = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER + || type == SS_PROXIMITY)) { + logError(1, "%s %s:Choose a SS type of compensation data ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } ret = requestCompensationData(type); if (ret < 0) { - logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); - return (ret|ERROR_REQU_COMP_DATA); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); } ret = readCompensationDataHeader(type, &(data->header), &address); if (ret < 0) { - logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); return (ret|ERROR_COMP_DATA_HEADER); } ret = readSelfSenseGlobalData(&address, data); if (ret < 0) { - logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_GLOBAL); - return (ret|ERROR_COMP_DATA_GLOBAL); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); + return (ret | ERROR_COMP_DATA_GLOBAL); } ret = readSelfSenseNodeData(address, data); if (ret < 0) { - logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_NODE); - return (ret|ERROR_COMP_DATA_NODE); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_NODE); + return (ret | ERROR_COMP_DATA_NODE); } return OK; - } -int readGeneralGlobalData(u16 address, GeneralData *global) + +int readGeneralGlobalData(u16 address, struct GeneralData *global) { u8 data[COMP_DATA_GLOBAL]; - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readGeneralGlobalData: ERROR %02X \n", tag, ERROR_I2C_R); + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, COMP_DATA_GLOBAL, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } @@ -351,35 +447,39 @@ int readGeneralGlobalData(u16 address, GeneralData *global) global->ftsa_lp_timer_cal1 = data[5]; return OK; - } -int readGeneralCompensationData(u16 type, GeneralData *data) -{ +int readGeneralCompensationData(u16 type, struct GeneralData *data) +{ int ret; u16 address; if (!(type == GENERAL_TUNING)) { - logError(1, "%s readGeneralCompensationData: Choose a GENERAL type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s:Choose a GENERAL type of compensation data ", + tag); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } ret = requestCompensationData(type); if (ret < 0) { - logError(1, "%s readGeneralCompensationData: ERROR %02X \n", tag, ERROR_REQU_COMP_DATA); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); return ERROR_REQU_COMP_DATA; } ret = readCompensationDataHeader(type, &(data->header), &address); if (ret < 0) { - logError(1, "%s readGeneralCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); return ERROR_COMP_DATA_HEADER; } ret = readGeneralGlobalData(address, data); if (ret < 0) { - logError(1, "%s readGeneralCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_GLOBAL); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); return ERROR_COMP_DATA_GLOBAL; } @@ -387,10 +487,12 @@ int readGeneralCompensationData(u16 type, GeneralData *data) } + int defaultChipInfo(int i2cError) { int i; - logError(0, "%s Setting default Chip Info... \n", tag); + + logError(0, "%s Setting default Chip Info...\n", tag); ftsInfo.u32_echoEn = 0x00000000; ftsInfo.u8_msScrConfigTuneVer = 0; ftsInfo.u8_ssTchConfigTuneVer = 0; @@ -399,54 +501,57 @@ int defaultChipInfo(int i2cError) if (i2cError == 1) { ftsInfo.u16_fwVer = 0xFFFF; ftsInfo.u16_cfgId = 0xFFFF; - for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) ftsInfo.u8_extReleaseInfo[i] = 0xFF; - } } else { ftsInfo.u16_fwVer = 0x0000; ftsInfo.u16_cfgId = 0x0000; - for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) ftsInfo.u8_extReleaseInfo[i] = 0x00; - } } ftsInfo.u32_mpPassFlag = INIT_FIELD; - logError(0, "%s default Chip Info DONE! \n", tag); + ftsInfo.u16_errOffset = INVALID_ERROR_OFFS; + logError(0, "%s default Chip Info DONE!\n", tag); return OK; - } int readChipInfo(int doRequest) { int ret, i; u16 answer; - u8 data[CHIP_INFO_SIZE+3]; - /* +3 because need to read all the field of the struct plus the signature and 2 address bytes */ + u8 data[CHIP_INFO_SIZE + 3]; + /*+3 because need to read all the field of*/ + /*the struct plus the signature and 2 address bytes*/ int index = 0; - logError(0, "%s Starting Read Chip Info... \n", tag); + logError(0, "%s Starting Read Chip Info...\n", tag); if (doRequest == 1) { ret = requestCompensationData(CHIP_INFO); if (ret < 0) { - logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); ret = (ret | ERROR_REQU_COMP_DATA); goto FAIL; } } - logError(0, "%s Byte to read = %d bytes \n", tag, CHIP_INFO_SIZE+3); + logError(0, "%s Byte to read = %d bytes\n", tag, CHIP_INFO_SIZE + 3); - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, ADDR_FRAMEBUFFER_DATA, data, CHIP_INFO_SIZE+3, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_I2C_R); + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, ADDR_FRAMEBUFFER_DATA, data, + CHIP_INFO_SIZE + 3, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); ret = ERROR_I2C_R; goto FAIL; } - logError(0, "%s Read data ok! \n", tag); + logError(0, "%s Read data ok!\n", tag); - logError(0, "%s Starting parsing of data... \n", tag); + logError(0, "%s Starting parsing of data...\n", tag); if (data[0] != HEADER_SIGNATURE) { - logError(1, "%s readChipInfo: ERROR %02X The Header Signature was wrong! %02X != %02X \n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + logError(1, "%s %s:ERROR ", tag, __func__); + logError(1, "%02X The Header Signature is wrong!%02X != %02X\n", + ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); ret = ERROR_WRONG_COMP_SIGN; goto FAIL; } @@ -454,7 +559,8 @@ int readChipInfo(int doRequest) u8ToU16_le(&data[1], &answer); if (answer != CHIP_INFO) { - logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); ret = ERROR_DIFF_COMP_TYPE; goto FAIL; } @@ -476,19 +582,21 @@ int readChipInfo(int doRequest) } logError(1, "\n"); - for (i = 0; i < sizeof(ftsInfo.u8_custInfo); i++) { + for (i = 0; i < sizeof(ftsInfo.u8_custInfo); i++) ftsInfo.u8_custInfo[i] = data[index++]; - } u8ToU16(&data[index], &ftsInfo.u16_fwVer); index += 2; - logError(1, "%s FW VERSION = %04X \n", tag, ftsInfo.u16_fwVer); + logError(1, "%s FW VERSION = %04X\n", tag, ftsInfo.u16_fwVer); u8ToU16(&data[index], &ftsInfo.u16_cfgId); index += 2; - logError(1, "%s CONFIG ID = %04X \n", tag, ftsInfo.u16_cfgId); + logError(1, "%s CONFIG ID = %04X\n", tag, ftsInfo.u16_cfgId); - ftsInfo.u32_projId = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + ftsInfo.u32_projId = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); index += 4; u8ToU16(&data[index], &ftsInfo.u16_scrXRes); @@ -498,86 +606,117 @@ int readChipInfo(int doRequest) index += 2; ftsInfo.u8_scrForceLen = data[index++]; - logError(1, "%s Force Len = %d \n", tag, ftsInfo.u8_scrForceLen); + logError(1, "%s Force Len = %d\n", tag, ftsInfo.u8_scrForceLen); ftsInfo.u8_scrSenseLen = data[index++]; - logError(1, "%s Sense Len = %d \n", tag, ftsInfo.u8_scrSenseLen); + logError(1, "%s Sense Len = %d\n", tag, ftsInfo.u8_scrSenseLen); - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_scrForceEn[i] = data[index++]; - } - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_scrSenseEn[i] = data[index++]; - } ftsInfo.u8_msKeyLen = data[index++]; - logError(1, "%s MS Key Len = %d \n", tag, ftsInfo.u8_msKeyLen); + logError(1, "%s MS Key Len = %d\n", tag, ftsInfo.u8_msKeyLen); - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_msKeyForceEn[i] = data[index++]; - } - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_msKeySenseEn[i] = data[index++]; - } ftsInfo.u8_ssKeyLen = data[index++]; - logError(1, "%s SS Key Len = %d \n", tag, ftsInfo.u8_ssKeyLen); + logError(1, "%s SS Key Len = %d\n", tag, ftsInfo.u8_ssKeyLen); - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_ssKeyForceEn[i] = data[index++]; - } - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_ssKeySenseEn[i] = data[index++]; - } ftsInfo.u8_frcTchXLen = data[index++]; ftsInfo.u8_frcTchYLen = data[index++]; - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_frcTchForceEn[i] = data[index++]; - } - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) ftsInfo.u64_frcTchSenseEn[i] = data[index++]; - } + ftsInfo.u8_msScrConfigTuneVer = data[index++]; - logError(1, "%s CFG MS TUNING VERSION = %02X \n", tag, ftsInfo.u8_msScrConfigTuneVer); + logError(1, "%s CFG MS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_msScrConfigTuneVer); ftsInfo.u8_msScrLpConfigTuneVer = data[index++]; ftsInfo.u8_msScrHwulpConfigTuneVer = data[index++]; ftsInfo.u8_msKeyConfigTuneVer = data[index++]; ftsInfo.u8_ssTchConfigTuneVer = data[index++]; - logError(1, "%s CFG SS TUNING VERSION = %02X \n", tag, ftsInfo.u8_ssTchConfigTuneVer); + logError(1, "%s CFG SS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_ssTchConfigTuneVer); ftsInfo.u8_ssKeyConfigTuneVer = data[index++]; ftsInfo.u8_ssHvrConfigTuneVer = data[index++]; ftsInfo.u8_frcTchConfigTuneVer = data[index++]; ftsInfo.u8_msScrCxmemTuneVer = data[index++]; - logError(1, "%s CX MS TUNING VERSION = %02X \n", tag, ftsInfo.u8_msScrCxmemTuneVer); + logError(1, "%s CX MS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_msScrCxmemTuneVer); ftsInfo.u8_msScrLpCxmemTuneVer = data[index++]; ftsInfo.u8_msScrHwulpCxmemTuneVer = data[index++]; ftsInfo.u8_msKeyCxmemTuneVer = data[index++]; ftsInfo.u8_ssTchCxmemTuneVer = data[index++]; - logError(1, "%s CX SS TUNING VERSION = %02X \n", tag, ftsInfo.u8_ssTchCxmemTuneVer); + logError(1, "%s CX SS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_ssTchCxmemTuneVer); ftsInfo.u8_ssKeyCxmemTuneVer = data[index++]; ftsInfo.u8_ssHvrCxmemTuneVer = data[index++]; ftsInfo.u8_frcTchCxmemTuneVer = data[index++]; - ftsInfo.u32_mpPassFlag = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + ftsInfo.u32_mpPassFlag = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); index += 4; - logError(1, "%s MP SIGNATURE = %08X \n", tag, ftsInfo.u32_mpPassFlag); - ftsInfo.u32_featEn = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + logError(1, "%s MP SIGNATURE = %08X\n", tag, ftsInfo.u32_mpPassFlag); + ftsInfo.u32_featEn = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); index += 4; - ftsInfo.u32_echoEn = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + ftsInfo.u32_echoEn = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); index += 4; - logError(1, "%s FEATURES = %08X \n", tag, ftsInfo.u32_echoEn); + logError(1, "%s FEATURES = %08X\n", tag, ftsInfo.u32_echoEn); + ftsInfo.u8_sideTchConfigTuneVer = data[index++]; + ftsInfo.u8_sideTchCxmemTuneVer = data[index++]; + ftsInfo.u8_sideTchForceLen = data[index++]; + logError(1, "%s Side Touch Force Len = %d\n", + tag, ftsInfo.u8_sideTchForceLen); + ftsInfo.u8_sideTchSenseLen = data[index++]; + logError(1, "%s Side Touch Sense Len = %d\n", + tag, ftsInfo.u8_sideTchSenseLen); + for (i = 0; i < 8; i++) + ftsInfo.u64_sideTchForceEn[i] = data[index++]; + for (i = 0; i < 8; i++) + ftsInfo.u64_sideTchSenseEn[i] = data[index++]; + ftsInfo.u8_errSign = data[index++]; + logError(1, "%s ERROR SIGNATURE = %02X\n", tag, ftsInfo.u8_errSign); + if (ftsInfo.u8_errSign == ERROR_SIGN_HEAD) { + logError(1, "%s Correct Error Signature found!\n", tag); + u8ToU16(&data[index], &ftsInfo.u16_errOffset); + } else { + logError(1, "%s Error Signature NOT FOUND!\n", tag); + ftsInfo.u16_errOffset = INVALID_ERROR_OFFS; + } + logError(1, "%s ERROR OFFSET = %04X\n", tag, ftsInfo.u16_errOffset); + index += 2; + logError(1, "%s Parsed %d bytes!\n", tag, index); - logError(1, "%s Parsed %d bytes! \n", tag, index); if (index != CHIP_INFO_SIZE + 3) { - logError(1, "%s readChipInfo: index = %d different from %d ERROR %02X\n", tag, index, CHIP_INFO_SIZE+3, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: index = %d different from %d ERROR %02X\n", + tag, __func__, index, CHIP_INFO_SIZE + 3, + ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } @@ -587,5 +726,5 @@ int readChipInfo(int doRequest) FAIL: defaultChipInfo(isI2cError(ret)); return ret; - } + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h index c4cc5913fc18e26cf8083035e8004ccc34c4b133..983d70e312cf4b8a2962ace5a082c6f73e20be81 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h @@ -1,63 +1,95 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_COMPENSATION_H +#define __FTS_COMPENSATION_H -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS functions for getting Initialization Data * -* * -************************************************************************** -************************************************************************** - -*/ #include "ftsCrossCompile.h" #include "ftsSoftware.h" -#define COMP_DATA_READ_RETRY 2 -/* Bytes dimension of Compensation Data Format */ +#define COMP_DATA_READ_RETRY 2 + +//Bytes dimension of Compensation Data Format + +#define COMP_DATA_HEADER 8 +#define COMP_DATA_GLOBAL 8 + -#define COMP_DATA_HEADER 8 -#define COMP_DATA_GLOBAL 8 +#define HEADER_SIGNATURE 0xA5 +#define INVALID_ERROR_OFFS 0xFFFF -#define HEADER_SIGNATURE 0xA5 +//Possible Compensation/Frame Data Type +#define GENERAL_TUNING 0x0100 +#define MS_TOUCH_ACTIVE 0x0200 +#define MS_TOUCH_LOW_POWER 0x0400 +#define MS_TOUCH_ULTRA_LOW_POWER 0x0800 +#define MS_KEY 0x1000 +#define SS_TOUCH 0x2000 +#define SS_KEY 0x4000 +#define SS_HOVER 0x8000 +#define SS_PROXIMITY 0x0001 +#define CHIP_INFO 0xFFFF -/* Possible Compensation/Frame Data Type */ -#define GENERAL_TUNING 0x0100 -#define MS_TOUCH_ACTIVE 0x0200 -#define MS_TOUCH_LOW_POWER 0x0400 -#define MS_TOUCH_ULTRA_LOW_POWER 0x0800 -#define MS_KEY 0x1000 -#define SS_TOUCH 0x2000 -#define SS_KEY 0x4000 -#define SS_HOVER 0x8000 -#define SS_PROXIMITY 0x0001 -#define CHIP_INFO 0xFFFF -#define TIMEOUT_REQU_COMP_DATA 1000 /* ms */ +#define TIMEOUT_REQU_COMP_DATA 1000 //ms -/* CHIP INFO */ -#define CHIP_INFO_SIZE 138/* bytes to read from framebuffer (exclude the signature and the type because already checked during the reading) */ -#define EXTERNAL_RELEASE_INFO_SIZE 8/* bytes */ +//CHIP INFO +#define CHIP_INFO_SIZE 161 +/*bytes to read from framebuffer (exclude the signature and the type*/ +/*because already checked during the reading)*/ +#define EXTERNAL_RELEASE_INFO_SIZE 8 //bytes -typedef struct { +struct DataHeader { int force_node, sense_node; u16 type; -} DataHeader; +}; -typedef struct { - DataHeader header; + +struct MutualSenseData { + struct DataHeader header; u8 tuning_ver; u8 cx1; u8 *node_data; int node_data_size; -} MutualSenseData; +}; + -typedef struct { - DataHeader header; +struct SelfSenseData { + struct DataHeader header; u8 tuning_ver; u8 f_ix1, s_ix1; u8 f_cx1, s_cx1; @@ -67,11 +99,11 @@ typedef struct { u8 *ix2_sn; u8 *cx2_fm; u8 *cx2_sn; +}; -} SelfSenseData; -typedef struct { - DataHeader header; +struct GeneralData { + struct DataHeader header; u8 ftsd_lp_timer_cal0; u8 ftsd_lp_timer_cal1; u8 ftsd_lp_timer_cal2; @@ -79,68 +111,96 @@ typedef struct { u8 ftsd_lp_timer_cal3; u8 ftsa_lp_timer_cal0; u8 ftsa_lp_timer_cal1; - -} GeneralData; - -typedef struct { - u8 u8_loadCnt; /* 03 - Load Counter */ - u8 u8_infoVer; /* 04 - New chip info version */ - u16 u16_ftsdId; /* 05 - FTSD ID */ - u8 u8_ftsdVer; /* 07 - FTSD version */ - u8 u8_ftsaId; /* 08 - FTSA ID */ - u8 u8_ftsaVer; /* 09 - FTSA version */ - u8 u8_tchRptVer; /* 0A - Touch report version (e.g. ST, Samsung etc) */ - u8 u8_extReleaseInfo[EXTERNAL_RELEASE_INFO_SIZE]; /* 0B - External release information */ - u8 u8_custInfo[12]; /* 13 - Customer information */ - u16 u16_fwVer; /* 1F - Firmware version */ - u16 u16_cfgId; /* 21 - Configuration ID */ - u32 u32_projId; /* 23 - Project ID */ - u16 u16_scrXRes; /* 27 - X resolution on main screen */ - u16 u16_scrYRes; /* 29 - Y resolution on main screen */ - u8 u8_scrForceLen; /* 2B - Number of force channel on main screen */ - u8 u8_scrSenseLen; /* 2C - Number of sense channel on main screen */ - u8 u64_scrForceEn[8]; /* 2D - Force channel enabled on main screen */ - u8 u64_scrSenseEn[8]; /* 35 - Sense channel enabled on main screen */ - u8 u8_msKeyLen; /* 3D - Number of MS Key channel */ - u8 u64_msKeyForceEn[8]; /* 3E - MS Key force channel enable */ - u8 u64_msKeySenseEn[8]; /* 46 - MS Key sense channel enable */ - u8 u8_ssKeyLen; /* 4E - Number of SS Key channel */ - u8 u64_ssKeyForceEn[8]; /* 4F - SS Key force channel enable */ - u8 u64_ssKeySenseEn[8]; /* 57 - SS Key sense channel enable */ - u8 u8_frcTchXLen; /* 5F - Number of force touch force channel */ - u8 u8_frcTchYLen; /* 60 - Number of force touch sense channel */ - u8 u64_frcTchForceEn[8]; /* 61 - Force touch force channel enable */ - u8 u64_frcTchSenseEn[8]; /* 69 - Force touch sense channel enable */ - u8 u8_msScrConfigTuneVer; /* 71 - MS screen tuning version in config */ - u8 u8_msScrLpConfigTuneVer; /* 72 - MS screen LP mode tuning version in config */ - u8 u8_msScrHwulpConfigTuneVer; /* 73 - MS screen ultra low power mode tuning version in config */ - u8 u8_msKeyConfigTuneVer; /* 74 - MS Key tuning version in config */ - u8 u8_ssTchConfigTuneVer; /* 75 - SS touch tuning version in config */ - u8 u8_ssKeyConfigTuneVer; /* 76 - SS Key tuning version in config */ - u8 u8_ssHvrConfigTuneVer; /* 77 - SS hover tuning version in config */ - u8 u8_frcTchConfigTuneVer; /* 78 - Force touch tuning version in config */ - u8 u8_msScrCxmemTuneVer; /* 79 - MS screen tuning version in cxmem */ - u8 u8_msScrLpCxmemTuneVer; /* 7A - MS screen LP mode tuning version in cxmem */ - u8 u8_msScrHwulpCxmemTuneVer; /* 7B - MS screen ultra low power mode tuning version in cxmem */ - u8 u8_msKeyCxmemTuneVer; /* 7C - MS Key tuning version in cxmem */ - u8 u8_ssTchCxmemTuneVer; /* 7D - SS touch tuning version in cxmem */ - u8 u8_ssKeyCxmemTuneVer; /* 7E - SS Key tuning version in cxmem */ - u8 u8_ssHvrCxmemTuneVer; /* 7F - SS hover tuning version in cxmem */ - u8 u8_frcTchCxmemTuneVer; /* 80 - Force touch tuning version in cxmem */ - u32 u32_mpPassFlag; /* 81 - Mass production pass flag */ - u32 u32_featEn; /* 85 - Supported features */ - u32 u32_echoEn; /* 89 - enable of particular features: first bit is Echo Enables */ -} chipInfo; +}; + +struct chipInfo { + u8 u8_loadCnt; ///< 03 - Load Counter + u8 u8_infoVer; ///< 04 - New chip info version + u16 u16_ftsdId; ///< 05 - FTSD ID + u8 u8_ftsdVer; ///< 07 - FTSD version + u8 u8_ftsaId; ///< 08 - FTSA ID + u8 u8_ftsaVer; ///< 09 - FTSA version + u8 u8_tchRptVer; ///< 0A - Touch report version (e.g. ST, Samsung etc) + + ///< 0B - External release information + u8 u8_extReleaseInfo[EXTERNAL_RELEASE_INFO_SIZE]; + u8 u8_custInfo[12]; ///< 13 - Customer information + u16 u16_fwVer; ///< 1F - Firmware version + u16 u16_cfgId; ///< 21 - Configuration ID + u32 u32_projId; ///< 23 - Project ID + u16 u16_scrXRes; ///< 27 - X resolution on main screen + u16 u16_scrYRes; ///< 29 - Y resolution on main screen + u8 u8_scrForceLen; ///< 2B - Number of force channel on main screen + u8 u8_scrSenseLen; ///< 2C - Number of sense channel on main screen + u8 u64_scrForceEn[8]; ///< 2D - Force channel enabled on main screen + u8 u64_scrSenseEn[8]; ///< 35 - Sense channel enabled on main screen + u8 u8_msKeyLen; ///< 3D - Number of MS Key channel + u8 u64_msKeyForceEn[8]; ///< 3E - MS Key force channel enable + u8 u64_msKeySenseEn[8]; ///< 46 - MS Key sense channel enable + u8 u8_ssKeyLen; ///< 4E - Number of SS Key channel + u8 u64_ssKeyForceEn[8]; ///< 4F - SS Key force channel enable + u8 u64_ssKeySenseEn[8]; ///< 57 - SS Key sense channel enable + u8 u8_frcTchXLen; ///< 5F - Number of force touch force channel + u8 u8_frcTchYLen; ///< 60 - Number of force touch sense channel + u8 u64_frcTchForceEn[8];///< 61 - Force touch force channel enable + u8 u64_frcTchSenseEn[8];///< 69 - Force touch sense channel enable + u8 u8_msScrConfigTuneVer; ///< 71 - MS screen tuning version in config + + ///< 72 - MS screen LP mode tuning version in config + u8 u8_msScrLpConfigTuneVer; + + ///< 73 - MS screen ultra low power mode tuning version in config + u8 u8_msScrHwulpConfigTuneVer; + u8 u8_msKeyConfigTuneVer; ///< 74 - MS Key tuning version in config + u8 u8_ssTchConfigTuneVer; ///< 75 - SS touch tuning version in config + u8 u8_ssKeyConfigTuneVer; ///< 76 - SS Key tuning version in config + u8 u8_ssHvrConfigTuneVer; ///< 77 - SS hover tuning version in config + + ///< 78 - Force touch tuning version in config + u8 u8_frcTchConfigTuneVer; + u8 u8_msScrCxmemTuneVer; ///< 79 - MS screen tuning version in cxmem + + ///< 7A - MS screen LP mode tuning version in cxmem + u8 u8_msScrLpCxmemTuneVer; + + ///< 7B - MS screen ultra low power mode tuning version in cxmem + u8 u8_msScrHwulpCxmemTuneVer; + u8 u8_msKeyCxmemTuneVer; ///< 7C - MS Key tuning version in cxmem + u8 u8_ssTchCxmemTuneVer; ///< 7D - SS touch tuning version in cxmem + u8 u8_ssKeyCxmemTuneVer; ///< 7E - SS Key tuning version in cxmem + u8 u8_ssHvrCxmemTuneVer; ///< 7F - SS hover tuning version in cxmem + + ///< 80 - Force touch tuning version in cxmem + u8 u8_frcTchCxmemTuneVer; + u32 u32_mpPassFlag; ///< 81 - Mass production pass flag + u32 u32_featEn; ///< 85 - Supported features + + ///< 89 - enable of particular features: first bit is Echo Enables + u32 u32_echoEn; + + ///< 8D - Side Touch tuning version in config + u8 u8_sideTchConfigTuneVer; + u8 u8_sideTchCxmemTuneVer; ///< 8E - Side Touch tuning version in cxmem + u8 u8_sideTchForceLen; ///< 8F - Number of force channel on side touch + u8 u8_sideTchSenseLen; ///< 90 - Number of sense channel on side touch + u8 u64_sideTchForceEn[8];///< 91 - Side touch force channel enable + u8 u64_sideTchSenseEn[8];///< 99 - Side touch sense channel enable + u8 u8_errSign; ///< A1 - Signature for error field + u16 u16_errOffset; ///< A2 - Error Offset +}; int requestCompensationData(u16 type); -int readCompensationDataHeader(u16 type, DataHeader *header, u16 *address); -int readMutualSenseGlobalData(u16 *address, MutualSenseData *global); -int readMutualSenseNodeData(u16 address, MutualSenseData *node); -int readMutualSenseCompensationData(u16 type, MutualSenseData *data); -int readSelfSenseGlobalData(u16 *address, SelfSenseData *global); -int readSelfSenseNodeData(u16 address, SelfSenseData *node); -int readSelfSenseCompensationData(u16 type, SelfSenseData *data); -int readGeneralGlobalData(u16 address, GeneralData *global); -int readGeneralCompensationData(u16 type, GeneralData *data); +int readCompensationDataHeader(u16 type, struct DataHeader *header, + u16 *address); +int readMutualSenseGlobalData(u16 *address, struct MutualSenseData *global); +int readMutualSenseNodeData(u16 address, struct MutualSenseData *node); +int readMutualSenseCompensationData(u16 type, struct MutualSenseData *data); +int readSelfSenseGlobalData(u16 *address, struct SelfSenseData *global); +int readSelfSenseNodeData(u16 address, struct SelfSenseData *node); +int readSelfSenseCompensationData(u16 type, struct SelfSenseData *data); +int readGeneralGlobalData(u16 address, struct GeneralData *global); +int readGeneralCompensationData(u16 type, struct GeneralData *data); int defaultChipInfo(int i2cError); int readChipInfo(int doRequest); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c index 502dace75e4f27733842cce1f9ffa28b2e006c13..6d87148c81b7ba5b0ae0bebfe1dcaa06f1c9e1ba 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c @@ -1,5 +1,37 @@ -#include "ftsCrossCompile.h" -#include "ftsError.h" +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Cross Compile * + * * + ************************************************************************** + ************************************************************************** + * + */ #include #include @@ -23,18 +55,19 @@ #include #include #include -/* #include */ +//#include #include #include #include #include #include -/* static char tag[8]="[ FTS ]\0"; */ +#include "ftsCrossCompile.h" +#include "ftsError.h" + void *stmalloc(size_t size) { return kmalloc(size, GFP_KERNEL); - } void stfree(void *ptr) diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h index 8b287dd342f89c52692f51927ddfcd22f1156c70..400d59c25a6579b85d228e228dd259f14aedbdd6 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h @@ -1,5 +1,43 @@ -/* #define NDK */ -/* #define DEBUG */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS cross compile * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_CROSS_COMPILE_H +#define __FTS_CROSS_COMPILE_H + +//#define NDK +//#define DEBUG #include #include @@ -23,7 +61,7 @@ #include #include #include -/* #include */ +//#include #include #include #include @@ -32,3 +70,5 @@ void *stmalloc(size_t size); void stfree(void *ptr); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.c b/drivers/input/touchscreen/st/fts_lib/ftsError.c index 844e5019fec6788daec5cdff70194861aa08be71..bb2f2808c45b96f6a3cdc5fb3ef56a806c5e7345 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsError.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.c @@ -1,15 +1,37 @@ /* -*************************************************************************** -* STMicroelectronics -************************************************************************** -* marco.cali@st.com -************************************************************************** -* -* FTS error/info kernel log reporting -* -************************************************************************** -************************************************************************** -*/ + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + */ + #include #include @@ -25,6 +47,8 @@ #include #include #include +//#include +#include #include #include @@ -35,15 +59,21 @@ #include "ftsError.h" #include "ftsIO.h" #include "ftsTool.h" +#include "ftsCompensation.h" + +static char tag[8] = "[ FTS ]\0"; + void logError(int force, const char *msg, ...) { + if (force == 1 #ifdef DEBUG || 1 #endif - ) { + ) { va_list args; + va_start(args, msg); vprintk(msg, args); va_end(args); @@ -52,12 +82,55 @@ void logError(int force, const char *msg, ...) int isI2cError(int error) { - if (((error & 0x000000FF) >= (ERROR_I2C_R & 0x000000FF)) && ((error & 0x000000FF) <= (ERROR_I2C_O & 0x000000FF))) + if (((error & 0x000000FF) >= (ERROR_I2C_R & 0x000000FF)) + && ((error & 0x000000FF) <= (ERROR_I2C_O & 0x000000FF))) return 1; else return 0; } +int dumpErrorInfo(void) +{ + int ret, i; + u8 data[ERROR_INFO_SIZE] = {0}; + u32 sign = 0; + + logError(0, "%s %s: Starting dump of error info...\n", tag, __func__); + if (ftsInfo.u16_errOffset == INVALID_ERROR_OFFS) { + logError(1, "%s %s: Invalid error offset ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, ftsInfo.u16_errOffset, + data, ERROR_INFO_SIZE, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: reading data ERROR %02X\n", + tag, __func__, ret); + return ret; + } + logError(1, "%s %s: Error Info =\n", tag, __func__); + u8ToU32(data, &sign); + if (sign != ERROR_SIGNATURE) + logError(1, "%s %s:Wrong Signature! Data may be invalid!\n", + tag, __func__); + else + logError(1, "%s %s: Error Signature OK! Data are valid!\n", + tag, __func__); + + for (i = 0; i < ERROR_INFO_SIZE; i++) { + if (i % 4 == 0) + logError(1, KERN_ERR "\n%s %s: %d) ", + tag, __func__, i / 4); + logError(1, "%02X ", data[i]); + } + logError(1, "\n"); + + logError(0, "%s %s: dump of error info FINISHED!\n", tag, __func__); + return OK; + +} + int errorHandler(u8 *event, int size) { int res = OK; @@ -66,40 +139,124 @@ int errorHandler(u8 *event, int size) if (getClient() != NULL) info = i2c_get_clientdata(getClient()); - if (info != NULL && event != NULL && size > 1 && event[0] == EVENTID_ERROR_EVENT) { - logError(1, "%s errorHandler: Starting handling...\n", tag); - switch (event[1]) - /* TODO: write an error log for undefinied command subtype 0xBA*/ - { - case EVENT_TYPE_ESD_ERROR: /* esd */ + if (info == NULL || event == NULL || size <= 1 || event[0] != + EVENTID_ERROR_EVENT) { + logError(1, "%s %s: event Null or not correct size! ", + tag, __func__, ERROR_OP_NOT_ALLOW); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(1, "%s %s: Starting handling...\n", tag, __func__); + //TODO: write an error log for undefinied command subtype 0xBA + switch (event[1]) { + case EVENT_TYPE_ESD_ERROR: //esd res = fts_chip_powercycle(info); if (res < OK) { - logError(1, "%s errorHandler: Error performing powercycle ERROR %08X\n", tag, res); + logError(1, "%s %s: ", tag, res); + logError(1, "Error performing powercycle ERROR %08X\n"); } res = fts_system_reset(); - if (res < OK) { - logError(1, "%s errorHandler: Cannot reset the device ERROR %08X\n", tag, res); - } - res = (ERROR_HANDLER_STOP_PROC|res); + if (res < OK) + logError(1, "%s %s:Cannot reset device ERROR%08X\n", + tag, __func__, res); + res = (ERROR_HANDLER_STOP_PROC | res); break; - case EVENT_TYPE_WATCHDOG_ERROR: /* watchdog */ + case EVENT_TYPE_WATCHDOG_ERROR: //watchdog + dumpErrorInfo(); res = fts_system_reset(); - if (res < OK) { - logError(1, "%s errorHandler: Cannot reset the device ERROR %08X\n", tag, res); - } - res = (ERROR_HANDLER_STOP_PROC|res); + if (res < OK) + logError(1, "%s %s:Cannot reset device:ERROR%08X\n", + tag, __func__, res); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + case EVENT_TYPE_CHECKSUM_ERROR: //CRC ERRORS + switch (event[2]) { + case CRC_CONFIG_SIGNATURE: + logError(1, "%s %s: Config Signature ERROR!\n", + tag, __func__); + break; + case CRC_CONFIG: + logError(1, "%s %s:Config CRC ERROR!\n", tag, __func__); + break; + case CRC_CX_MEMORY: + logError(1, "%s %s: CX CRC ERROR!\n", tag, __func__); + break; + } break; + case EVENT_TYPE_LOCKDOWN_ERROR: + //res = (ERROR_HANDLER_STOP_PROC|res); + //stop lockdown code routines in order to retry + switch (event[2]) { + case 0x01: + logError(1, "%s %s:Lockdown code alredy ", + tag, __func__); + logError(1, "written into the IC!\n"); + break; + case 0x02: + logError(1, "%s %s:Lockdown CRC ", tag, __func__); + logError(1, "check fail during a WRITE!\n"); + break; + case 0x03: + logError(1, + "%s %s:Lockdown WRITE command format wrong!\n", + tag, __func__); + break; + case 0x04: + pr_err("Lockdown Memory Corrupted!"); + logError(1, "%s %s:Please contact ST for support!\n", + tag, __func__); + break; + case 0x11: + logError(1, + "%s %s:NO Lockdown code to READ into the IC!\n", + tag, __func__); + break; + case 0x12: + logError(1, + "%s %s:Lockdown code data corrupted\n", + tag, __func__); + break; + case 0x13: + logError(1, + "%s %s:Lockdown READ command format wrong!\n", + tag, __func__); + break; + case 0x21: + pr_err("Exceeded maximum number of "); + logError(1, + "%s %s:Lockdown code REWRITE into IC!\n", + tag, __func__); + break; + case 0x22: + logError(1, "%s %s:Lockdown CRC check", tag, __func__); + logError(1, " fail during a REWRITE!\n"); + break; + case 0x23: + logError(1, "%s %s:", tag, __func__); + logError(1, "Lockdown REWRITE command format wrong!\n"); + break; + case 0x24: + pr_err("Lockdown Memory Corrupted!"); + logError(1, "%s %s:Please contact ST for support!\n", + tag, __func__); + break; default: - logError(1, "%s errorHandler: No Action taken! \n", tag); + logError(1, "%s %s:No valid error type for LOCKDOWN!\n", + tag, __func__); + } break; - } - logError(1, "%s errorHandler: handling Finished! res = %08X\n", tag, res); - return res; + default: + logError(1, + "%s %s: No Action taken!\n", tag, __func__); + break; } - logError(1, "%s errorHandler: event Null or not correct size! ERROR %08X \n", tag, ERROR_OP_NOT_ALLOW); - return ERROR_OP_NOT_ALLOW; + logError(1, "%s %s: handling Finished! res = %08X\n", + tag, __func__, res); + return res; } + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.h b/drivers/input/touchscreen/st/fts_lib/ftsError.h index fc8fa5003158291621993c945c93b9df2135aa85..16f45a7e343cfee4f3009bb0d50d582a30643f1a 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsError.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.h @@ -1,75 +1,180 @@ /* -************************************************************************** -** STMicroelectronics -************************************************************************** -** marco.cali@st.com -************************************************************************** -* -* FTS error/info kernel log reporting -* -************************************************************************** -************************************************************************** -*/ - - -/*FIRST LEVEL ERROR CODE*/ -#define OK ((int)0x00000000) /*No ERROR*/ -#define ERROR_ALLOC ((int)0x80000001) /*allocation of memory failed*/ -#define ERROR_I2C_R ((int)0x80000002) /*i2c read failed*/ -#define ERROR_I2C_W ((int)0x80000003) /*i2c write failed*/ -#define ERROR_I2C_WR ((int)0x80000004) /*i2c write/read failed*/ -#define ERROR_I2C_O ((int)0x80000005) /*error during opening a i2c device*/ -#define ERROR_OP_NOT_ALLOW ((int)0x80000006) /*operation not allowed*/ -#define ERROR_TIMEOUT ((int)0x80000007) /*timeout expired! exceed the max number of retries or the max waiting time*/ -#define ERROR_FILE_NOT_FOUND ((int)0x80000008) /*the file that i want to open is not found*/ -#define ERROR_FILE_PARSE ((int)0x80000009) /*error during parsing the file*/ -#define ERROR_FILE_READ ((int)0x8000000A) /*error during reading the file*/ -#define ERROR_LABEL_NOT_FOUND ((int)0x8000000B) /*label not found*/ -#define ERROR_FW_NO_UPDATE ((int)0x8000000C) /*fw in the chip newer than the one in the memmh*/ -#define ERROR_FLASH_UNKNOWN ((int)0x8000000D) /*flash status busy or unknown*/ - -/*SECOND LEVEL ERROR CODE*/ -#define ERROR_DISABLE_INTER ((int)0x80000200) /*unable to disable the interrupt*/ -#define ERROR_ENABLE_INTER ((int)0x80000300) /*unable to activate the interrup*/ -#define ERROR_READ_B2 ((int)0x80000400) /*B2 command failed*/ -#define ERROR_GET_OFFSET ((int)0x80000500) /*unable to read an offset from memory*/ -#define ERROR_GET_FRAME_DATA ((int)0x80000600) /*unable to retrieve the data of a required frame*/ -#define ERROR_DIFF_COMP_TYPE ((int)0x80000700) /*FW answers with an event that has a different address respect the request done*/ -#define ERROR_WRONG_COMP_SIGN ((int)0x80000800) /*the signature of the compensation data is not A5*/ -#define ERROR_SENSE_ON_FAIL ((int)0x80000900) /*the command Sense On failed*/ -#define ERROR_SENSE_OFF_FAIL ((int)0x80000A00) /*the command Sense Off failed*/ -#define ERROR_SYSTEM_RESET_FAIL ((int)0x80000B00) /*the command SYSTEM RESET failed*/ -#define ERROR_FLASH_NOT_READY ((int)0x80000C00) /*flash status not ready within a timeout*/ -#define ERROR_FW_VER_READ ((int)0x80000D00) /*unable to retrieve fw_vers or the config_id*/ -#define ERROR_GESTURE_ENABLE_FAIL ((int)0x80000E00) /*unable to enable/disable the gesture*/ -#define ERROR_GESTURE_START_ADD ((int)0x80000F00) /*unable to start to add custom gesture*/ -#define ERROR_GESTURE_FINISH_ADD ((int)0x80001000) /*unable to finish to add custom gesture*/ -#define ERROR_GESTURE_DATA_ADD ((int)0x80001100) /*unable to add custom gesture data*/ -#define ERROR_GESTURE_REMOVE ((int)0x80001200) /*unable to remove custom gesture data*/ -#define ERROR_FEATURE_ENABLE_DISABLE ((int)0x80001300) /*unable to enable/disable a feature mode in the IC*/ -/*THIRD LEVEL ERROR CODE*/ -#define ERROR_CH_LEN ((int)0x80010000) /*unable to retrieve the force and/or sense length*/ -#define ERROR_REQU_COMP_DATA ((int)0x80020000) /*compensation data request failed*/ -#define ERROR_COMP_DATA_HEADER ((int)0x80030000) /*unable to retrieve the compensation data header*/ -#define ERROR_COMP_DATA_GLOBAL ((int)0x80040000) /*unable to retrieve the global compensation data*/ -#define ERROR_COMP_DATA_NODE ((int)0x80050000) /*unable to retrieve the compensation data for each node*/ -#define ERROR_TEST_CHECK_FAIL ((int)0x80060000) /*check of production limits or of fw answers failed*/ -#define ERROR_MEMH_READ ((int)0x80070000) /*memh reading failed*/ -#define ERROR_FLASH_BURN_FAILED ((int)0x80080000) /*flash burn failed*/ -#define ERROR_MS_TUNING ((int)0x80090000) /*ms tuning failed*/ -#define ERROR_SS_TUNING ((int)0x800A0000) /*ss tuning failed*/ -#define ERROR_LP_TIMER_TUNING ((int)0x800B0000) /*lp timer calibration failed*/ -#define ERROR_SAVE_CX_TUNING ((int)0x800C0000) /*save cx data to flash failed*/ -#define ERROR_HANDLER_STOP_PROC ((int)0x800D0000) /*stop the poll of the FIFO if particular errors are found*/ -#define ERROR_CHECK_ECHO_FAIL ((int)0x800E0000) /*unable to retrieve echo event*/ - -/*FOURTH LEVEL ERROR CODE*/ -#define ERROR_PROD_TEST_DATA ((int)0x81000000) /*production data test failed*/ -#define ERROR_FLASH_PROCEDURE ((int)0x82000000) /*complete flash procedure failed*/ -#define ERROR_PROD_TEST_ITO ((int)0x83000000) /*production ito test failed*/ -#define ERROR_PROD_TEST_INITIALIZATION ((int)0x84000000) /*production initialization test failed*/ -#define ERROR_GET_INIT_STATUS ((int)0x85000000) /*mismatch of the MS or SS tuning_version*/ + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_ERROR_H +#define __FTS_ERROR_H + + +//FIRST LEVEL ERROR CODE +#define OK (int)(0x00000000)/* No ERROR*/ +/*allocation of memory failed*/ +#define ERROR_ALLOC (int)(0x80000001) +#define ERROR_I2C_R (int)(0x80000002)//i2c read failed +#define ERROR_I2C_W (int)(0x80000003)//i2c write failed +#define ERROR_I2C_WR (int)(0x80000004)//i2c write/read failed + +//error during opening i2c device +#define ERROR_I2C_O (int)(0x80000005) +#define ERROR_OP_NOT_ALLOW (int)(0x80000006)//operation not allowed + +//timeout expired! exceed the max number of +//retries or the max waiting time +#define ERROR_TIMEOUT (int)(0x80000007) + +//the file that i want to open is not found +#define ERROR_FILE_NOT_FOUND (int)(0x80000008) +//error during parsing the file +#define ERROR_FILE_PARSE (int)(0x80000009) +//error during reading the file +#define ERROR_FILE_READ (int)(0x8000000A) +#define ERROR_LABEL_NOT_FOUND (int)(0x8000000B)//label not found + +//fw in the chip newer than the one in the memmh +#define ERROR_FW_NO_UPDATE (int)(0x8000000C) +//flash status busy or unknown +#define ERROR_FLASH_UNKNOWN (int)(0x8000000D) + +//SECOND LEVEL ERROR CODE +//unable to disable the interrupt +#define ERROR_DISABLE_INTER (int)(0x80000200) + +//unable to activate the interrupt +#define ERROR_ENABLE_INTER (int)(0x80000300) + +#define ERROR_READ_B2 (int)(0x80000400)//B2 command failed + +//unable to read an offset from memory +#define ERROR_GET_OFFSET (int)(0x80000500) + +//unable to retrieve the data of a required frame +#define ERROR_GET_FRAME_DATA (int)(0x80000600) + +//FW answers with an event that has a +//different address respect the request done +#define ERROR_DIFF_COMP_TYPE (int)(0x80000700) + +//the signature of the compensation data is not A5 +#define ERROR_WRONG_COMP_SIGN (int)(0x80000800) +//the command Sense On failed +#define ERROR_SENSE_ON_FAIL (int)(0x80000900) +//the command Sense Off failed +#define ERROR_SENSE_OFF_FAIL (int)(0x80000A00) + +//the command SYSTEM RESET failed +#define ERROR_SYSTEM_RESET_FAIL (int)(0x80000B00) + +//flash status not ready within a timeout +#define ERROR_FLASH_NOT_READY (int)(0x80000C00) + +//unable to retrieve fw_vers or the config_id +#define ERROR_FW_VER_READ (int)(0x80000D00) + +//unable to enable/disable the gesture +#define ERROR_GESTURE_ENABLE_FAIL (int)(0x80000E00) + +//unable to start to add custom gesture +#define ERROR_GESTURE_START_ADD (int)(0x80000F00) + +//unable to finish to add custom gesture +#define ERROR_GESTURE_FINISH_ADD (int)(0x80001000) + +//unable to add custom gesture data +#define ERROR_GESTURE_DATA_ADD (int)(0x80001100) + +//unable to remove custom gesture data +#define ERROR_GESTURE_REMOVE (int)(0x80001200) + +//unable to enable/disable a feature mode in the IC +#define ERROR_FEATURE_ENABLE_DISABLE (int)(0x80001300) + +//unable to set/read noise parameter in the IC +#define ERROR_NOISE_PARAMETERS (int)(0x80001400) + +//unable to write/rewrite/read lockdown code in the IC +#define ERROR_LOCKDOWN_CODE (int)(0x80001500) + +//THIRD LEVEL ERROR CODE +//unable to retrieve the force and/or sense length +#define ERROR_CH_LEN (int)(0x80010000) + +//compensation data request failed +#define ERROR_REQU_COMP_DATA (int)(0x80020000) + +//unable to retrieve the compensation data header +#define ERROR_COMP_DATA_HEADER (int)(0x80030000) + +//unable to retrieve the global compensation data +#define ERROR_COMP_DATA_GLOBAL (int)(0x80040000) + +//unable to retrieve the compensation data for each node +#define ERROR_COMP_DATA_NODE (int)(0x80050000) + +//check of production limits or of fw answers failed +#define ERROR_TEST_CHECK_FAIL (int)(0x80060000) +#define ERROR_MEMH_READ (int)(0x80070000)//memh reading failed +#define ERROR_FLASH_BURN_FAILED (int)(0x80080000)//flash burn failed +#define ERROR_MS_TUNING (int)(0x80090000)//ms tuning failed +#define ERROR_SS_TUNING (int)(0x800A0000)//ss tuning failed +//lp timer calibration failed +#define ERROR_LP_TIMER_TUNING (int)(0x800B0000) +//save cx data to flash failed +#define ERROR_SAVE_CX_TUNING (int)(0x800C0000) + +//stop the poll of the FIFO if particular errors are found +#define ERROR_HANDLER_STOP_PROC (int)(0x800D0000) +//unable to retrieve echo event +#define ERROR_CHECK_ECHO_FAIL (int)(0x800E0000) + +//FOURTH LEVEL ERROR CODE +//production data test failed +#define ERROR_PROD_TEST_DATA (int)(0x81000000) + +//complete flash procedure failed +#define ERROR_FLASH_PROCEDURE (int)(0x82000000) +//production ito test failed +#define ERROR_PROD_TEST_ITO (int)(0x83000000) + +//production initialization test failed +#define ERROR_PROD_TEST_INITIALIZATION (int)(0x84000000) + +//mismatch of the MS or SS tuning_version +#define ERROR_GET_INIT_STATUS (int)(0x85000000) + void logError(int force, const char *msg, ...); int isI2cError(int error); +int dumpErrorInfo(void); int errorHandler(u8 *event, int size); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.c b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c index f3becac79102e61d7ac9b4fb49b9e60a8ea2bb23..e667968b426a28e87610009d8b6bb47bb4aad983 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsFlash.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c @@ -1,30 +1,38 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +/** + * ************************************************************************** - ** STMicroelectronics ** + ** STMicroelectronics ** ************************************************************************** - ** marco.cali@st.com ** + ** marco.cali@st.com ** ************************************************************************** * * - * FTS API for Flashing the IC * + * FTS API for Flashing the IC * * * ************************************************************************** ************************************************************************** - + * */ -#include "ftsCrossCompile.h" -#include "ftsCompensation.h" -#include "ftsError.h" -#include "ftsFlash.h" -#include "ftsFrame.h" -#include "ftsIO.h" -#include "ftsSoftware.h" -#include "ftsTest.h" -#include "ftsTime.h" -#include "ftsTool.h" -#include "../fts.h" /* needed for the FW_H_FILE define */ - #include #include #include @@ -49,14 +57,27 @@ #include #include #include -/* #include */ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFlash.h" +#include "ftsFrame.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h"//needed for including the define FW_H_FILE #ifdef FW_H_FILE #include <../fts_fw.h> +#define LOAD_FW_FROM 1 +#else +#define LOAD_FW_FROM 0 #endif -/* static char tag[8] = "[ FTS ]\0"; */ -extern chipInfo ftsInfo; +static char tag[8] = "[ FTS ]\0"; int getFirmwareVersion(u16 *fw_vers, u16 *config_id) { @@ -64,20 +85,28 @@ int getFirmwareVersion(u16 *fw_vers, u16 *config_id) u8 confid[CONFIG_ID_BYTE]; int res; - res = readCmdU16(FTS_CMD_HW_REG_R, DCHIP_FW_VER_ADDR, fwvers, DCHIP_FW_VER_BYTE, DUMMY_HW_REG); + res = readCmdU16(FTS_CMD_HW_REG_R, DCHIP_FW_VER_ADDR, + fwvers, DCHIP_FW_VER_BYTE, DUMMY_HW_REG); if (res < OK) { - logError(1, "%s getFirmwareVersion: unable to read fw_version ERROR %02X\n", tag, ERROR_FW_VER_READ); + logError(1, + "%s %s:unable to read fw_version ERROR %02X\n", + tag, __func__, ERROR_FW_VER_READ); return (res | ERROR_FW_VER_READ); } - u8ToU16(fwvers, fw_vers); /* fw version use big endian */ - if (*fw_vers != 0) { /* if fw_version is 00 00 means that there is no firmware running in the chip therefore will be impossible find the config_id */ + u8ToU16(fwvers, fw_vers); //fw version use big endian + if (*fw_vers != 0) { + // if fw_version is 00 00 means that there is + //no firmware running in the chip therefore will be + //impossible find the config_id res = readB2(CONFIG_ID_ADDR, confid, CONFIG_ID_BYTE); if (res < OK) { - logError(1, "%s getFirmwareVersion: unable to read config_id ERROR %02X\n", tag, ERROR_FW_VER_READ); + logError(1, "%s %s:unable to read config_id ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_FW_VER_READ); return (res | ERROR_FW_VER_READ); } - u8ToU16(confid, config_id); /* config id use little endian */ + u8ToU16(confid, config_id); //config id use little endian } else { *config_id = 0x0000; } @@ -85,26 +114,136 @@ int getFirmwareVersion(u16 *fw_vers, u16 *config_id) logError(0, "%s FW VERS = %04X\n", tag, *fw_vers); logError(0, "%s CONFIG ID = %04X\n", tag, *config_id); return OK; +} + +int getFWdata(const char *pathToFile, u8 **data, int *size, int from) +{ + const struct firmware *fw = NULL; + struct device *dev = NULL; + int res; + + logError(1, "%s %s starting...\n", tag, __func__); + switch (from) { +#ifdef FW_H_FILE + case 1: + logError(1, "%s Read FW from .h file!\n", tag); + *size = FW_SIZE_NAME; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to allocate memory! ", + tag, __func__); + logError(1, "ERROR %08X\n", ERROR_ALLOC); + + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)FW_ARRAY_NAME, (*size)); + break; +#endif + default: + logError(1, "%s Read FW from BIN file!\n", tag); + dev = getDev(); + + if (dev != NULL) { + res = request_firmware(&fw, pathToFile, dev); + if (res == 0) { + *size = fw->size; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), + GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to ", + tag, __func__); + logError(1, "%allocate! %08X\n", + ERROR_ALLOC); + release_firmware(fw); + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)fw->data, (*size)); + release_firmware(fw); + } else { + logError(1, "%s %s:No File found! ERROR %08X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + } else { + logError(1, "%s %s:No device found! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + } + logError(1, "%s %s:Finshed!\n", tag, __func__); + return OK; +} + +int readFwFile(const char *path, struct Firmware *fw, int keep_cx) +{ + int res; + int orig_size; + u8 *orig_data = NULL; + + + res = getFWdata(path, &orig_data, &orig_size, LOAD_FW_FROM); + if (res < OK) { + logError(1, "%s %s:impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + + return (res | ERROR_MEMH_READ); + } + res = parseBinFile(orig_data, orig_size, fw, keep_cx); + + if (res < OK) { + logError(1, "%s %s:impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + return (res | ERROR_MEMH_READ); + } + return OK; } -#ifdef FTM3_CHIP +int flashProcedure(const char *path, int force, int keep_cx) +{ + struct Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, (res | ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(fw, force, keep_cx); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s flashing procedure Finished!\n", tag); + kfree(fw.data); + + return res; +} + +#ifdef FTM3_CHIP int flash_status(void) { u8 cmd[2] = {FLASH_CMD_READSTATUS, 0x00}; - u8 readData = 0; + u8 readData; - logError(0, "%s Reading flash_status...\n", tag); + logError(0, "%s %s:Reading ...\n", tag, __func__); if (fts_readCmd(cmd, 2, &readData, FLASH_STATUS_BYTES) < 0) { - logError(1, "%s flash_status: ERROR % 02X\n", tag, ERROR_I2C_R); + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } readData &= 0x01; - /* logError(0, "%s flash_status = %d\n", tag,readData); */ + logError(0, "%s %s = %d\n", tag, __func__, readData); return (int) readData; - } int flash_status_ready(void) @@ -113,31 +252,34 @@ int flash_status_ready(void) int status = flash_status(); if (status == ERROR_I2C_R) { - logError(1, "%s flash_status_ready: ERROR % 02X\n", tag, ERROR_I2C_R); + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } if (status != FLASH_READY) { - logError(1, "%s flash_status_ready: flash busy or unknown STATUS = % 02X\n", tag, status); + //logError(1, + //"%s %s:flash busy or unknown STATUS = % 02X\n", + //tag, status); return ERROR_FLASH_UNKNOWN; } return FLASH_READY; - } int wait_for_flash_ready(void) { int status; - int(*code)(void); + int (*code)(void); code = flash_status_ready; logError(0, "%s Waiting for flash ready...\n", tag); - status = attempt_function(code, FLASH_WAIT_BEFORE_RETRY, FLASH_RETRY_COUNT); + status = attempt_function(code, FLASH_WAIT_BEFORE_RETRY, + FLASH_RETRY_COUNT); if (status != FLASH_READY) { - logError(1, "%s wait_for_flash_ready: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); return (status | ERROR_FLASH_NOT_READY); } @@ -147,204 +289,106 @@ int wait_for_flash_ready(void) int flash_unlock(void) { - int status; - u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; /* write the comand to perform the unlock */ + //write the command to perform the unlock + u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, + FLASH_UNLOCK_CODE1}; logError(0, "%s Try to unlock flash...\n", tag); status = wait_for_flash_ready(); if (status != OK) { - logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); - return (status | ERROR_FLASH_NOT_READY); /* Flash not ready within the choosen time, better exit! */ + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, better exit! + return (status | ERROR_FLASH_NOT_READY); } logError(0, "%s Command unlock ...\n", tag); - if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { - logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } status = wait_for_flash_ready(); if (status != OK) { - logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); - return (status | ERROR_FLASH_NOT_READY); /* Flash not ready within the choosen time, better exit! */ + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); } - logError(0, "%s Unlock flash DONE!\n", tag); return OK; - } -/*int parseMemhFile(const char *pathToFile, u8** data, int* length, int dimension) +int parseBinFile(u8 *fw_data, int fw_size, Firmware *fwData, int keep_cx) { + int dimension; - int i = 0; - unsigned long ul; - u8* buff = NULL; - int fd = -1; - int n, size, pointer = 0; - char *data_file, *line; - const struct firmware *fw = NULL; - struct device *dev = NULL; - - line = (char *) kmalloc(11 * sizeof (char), GFP_KERNEL); - if (line == NULL) { - logError(1, "%s parseMemhFile: ERROR %02X\n", tag, ERROR_ALLOC); - return ERROR_ALLOC; - } - - logError(0, "%s parseMemhFile: allocating %d bytes\n", tag, dimension); - buff = (u8*) kmalloc(dimension * sizeof (u8), GFP_KERNEL); - if (buff == NULL) { - logError(1, "%s parseMemhFile: ERROR %02X\n", tag, ERROR_ALLOC); - return ERROR_ALLOC; - } - - dev = getDev(); - if (dev != NULL) - fd = request_firmware(&fw, pathToFile, dev); - - if (fd == 0) { - size = fw->size; - logError(0, "%s The size of the firmware file is %d bytes...\n", tag, size); - data_file = (char *) fw->data; - logError(0, "%s Start to reading %s...\n", tag, pathToFile); - - while (size - pointer > 0 && (i * 4 + 4) <= dimension) { - if (readLine(&data_file[pointer], &line, size - pointer, &n) < 0) { - break; - } - pointer += n; - logError(0, "%s Pointer= %d riga = %s\n", tag, pointer, line); - ul = simple_strtoul(line, NULL, 16); - - buff[i * 4] = (u8) ((ul & 0x000000FF) >> 0); - buff[i * 4 + 1] = (u8) ((ul & 0x0000FF00) >> 8); - buff[i * 4 + 2] = (u8) ((ul & 0x00FF0000) >> 16); - buff[i * 4 + 3] = (u8) ((ul & 0xFF000000) >> 24); - i++; - } - - kfree(line); - - *length = i * 4; - if (*length < dimension) { - logError(1, "%s parseMemhFile: Read only %d instead of %d... ERROR %02X\n", tag, *length, dimension, ERROR_FILE_PARSE); - release_firmware(fw); - return ERROR_FILE_PARSE; - } - *data = buff; - - logError(0, "%s READ DONE %d bytes!\n", tag, *length); - release_firmware(fw); - return OK; + if (keep_cx) { + dimension = FW_SIZE - FW_CX_SIZE; + logError(1, "%s %s: Selected 124k Configuration!\n", + tag, __func__); } else { - logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); - return ERROR_FILE_NOT_FOUND; + dimension = FW_SIZE; + logError(1, "%s %s: Selected 128k Configuration!\n", + tag, __func__); } -}*/ - -int parseBinFile(const char *pathToFile, u8 **data, int *length, int dimension) -{ - - int fd = -1; - int fw_size = 0; - u8 *fw_data = NULL; - -#ifndef FW_H_FILE - const struct firmware *fw = NULL; - struct device *dev = NULL; - dev = getDev(); - - if (dev != NULL) - fd = request_firmware(&fw, pathToFile, dev); - else { - logError(1, "%s parseBinFile: No device found! ERROR %02X\n", ERROR_FILE_PARSE); + if (fw_size - FW_HEADER_SIZE != FW_SIZE || fw_data == NULL) { + logError(1, "%s %s:Read only %d instead of %d... ERROR %02X\n", + tag, __func__, + fw_size - FW_HEADER_SIZE, + FW_SIZE, ERROR_FILE_PARSE); + kfree(fw_data); return ERROR_FILE_PARSE; } -#else - fd = 0; -#endif - - if (fd == 0) { -#ifndef FW_H_FILE - fw_size = fw->size; - fw_data = (u8 *) (fw->data); -#else - fw_size = FW_SIZE_NAME; - fw_data = (u8 *) FW_ARRAY_NAME; -#endif - if (fw_size - FW_HEADER_SIZE != FW_SIZE) { - logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, FW_SIZE, ERROR_FILE_PARSE); -#ifndef FW_H_FILE - release_firmware(fw); -#endif - return ERROR_FILE_PARSE; - } - *data = (u8 *) kmalloc(dimension * sizeof (u8), GFP_KERNEL); - if (*data == NULL) { - logError(1, "%s parseBinFile: ERROR %02X\n", tag, ERROR_ALLOC); -#ifndef FW_H_FILE - release_firmware(fw); -#endif - return ERROR_ALLOC; - } - memcpy(*data, ((u8 *) (fw_data) + FW_HEADER_SIZE), dimension); - *length = dimension; + fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8), GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); - logError(0, "%s READ FW DONE %d bytes!\n", tag, *length); -#ifndef FW_H_FILE - release_firmware(fw); -#endif - return OK; - } - logError(1, "%s parseBinFile: File Not Found! ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); - return ERROR_FILE_NOT_FOUND; -} + kfree(fw_data); + return ERROR_ALLOC; + } -int readFwFile(const char *path, Firmware *fw, int keep_cx) -{ - int res; - int size; + memcpy(fwData->data, ((u8 *)(fw_data) + FW_HEADER_SIZE), + dimension); + fwData->data_size = dimension; - if (keep_cx) { - size = FW_SIZE - FW_CX_SIZE; - logError(1, "%s readFwFile: Selected 124k Configuration!\n", tag); - } else { - size = FW_SIZE; - logError(1, "%s readFwFile: Selected 128k Configuration!\n", tag); - } + fwData->fw_ver = (u16)(((fwData->data[FW_VER_MEMH_BYTE1] & 0x00FF) << 8) + + (fwData->data[FW_VER_MEMH_BYTE0] & 0x00FF)); - /* res = parseMemhFile(path, &(fw->data), &(fw->data_size), size); */ - res = parseBinFile(path, &(fw->data), &(fw->data_size), size); - if (res < OK) { - logError(1, "%s readFwFile: ERROR %02X\n", tag, ERROR_MEMH_READ); - return (res | ERROR_MEMH_READ); - } + fwData->config_id = (u16)(((fwData->data[(FW_CODE_SIZE) + + FW_OFF_CONFID_MEMH_BYTE1] & 0x00FF) << 8) + + (fwData->data[(FW_CODE_SIZE) + + FW_OFF_CONFID_MEMH_BYTE0] & 0x00FF)); - fw->fw_ver = (u16) (((fw->data[FW_VER_MEMH_BYTE1] & 0x00FF) << 8) + (fw->data[FW_VER_MEMH_BYTE0] & 0x00FF)); - fw->config_id = (u16) (((fw->data[(FW_CODE_SIZE) + FW_OFF_CONFID_MEMH_BYTE1] & 0x00FF) << 8) + (fw->data[(FW_CODE_SIZE) + FW_OFF_CONFID_MEMH_BYTE0] & 0x00FF)); + logError(0, "%s %s: FW VERS File = %04X\n", + tag, __func__, fwData->fw_ver); + logError(0, "%s %s: CONFIG ID File = %04X\n", + tag, __func__, fwData->config_id); - logError(0, "%s FW VERS File = %04X\n", tag, fw->fw_ver); - logError(0, "%s CONFIG ID File = %04X\n", tag, fw->config_id); - return OK; + logError(0, "%s READ FW DONE %d bytes!\n", tag, fwData->data_size); + kfree(fw_data); + return OK; } int fillMemory(u32 address, u8 *data, int size) { - int remaining = size; int toWrite = 0; + int delta; - u8 *buff = (u8 *) kmalloc((MEMORY_CHUNK + 3) * sizeof (u8), GFP_KERNEL); + u8 *buff = (u8 *)kmalloc_array((MEMORY_CHUNK + 3), sizeof(u8), + GFP_KERNEL); if (buff == NULL) { - logError(1, "%s fillMemory: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } @@ -356,7 +400,8 @@ int fillMemory(u32 address, u8 *data, int size) remaining -= MEMORY_CHUNK; } else { if (address < FLASH_ADDR_SWITCH_CMD) { - int delta = FLASH_ADDR_SWITCH_CMD - address; + delta = FLASH_ADDR_SWITCH_CMD - address; + buff[0] = FLASH_CMD_WRITE_LOWER_64; toWrite = delta; remaining -= delta; @@ -373,7 +418,8 @@ int fillMemory(u32 address, u8 *data, int size) remaining = 0; } else { if (address < FLASH_ADDR_SWITCH_CMD) { - int delta = FLASH_ADDR_SWITCH_CMD - address; + delta = FLASH_ADDR_SWITCH_CMD - address; + buff[0] = FLASH_CMD_WRITE_LOWER_64; toWrite = delta; remaining -= delta; @@ -388,152 +434,154 @@ int fillMemory(u32 address, u8 *data, int size) buff[1] = (u8) ((address & 0x0000FF00) >> 8); buff[2] = (u8) (address & 0x000000FF); memcpy(buff + 3, data, toWrite); - logError(0, "%s Command = %02X , address = %02X %02X, bytes = %d\n", tag, buff[0], buff[1], buff[2], toWrite); + //logError(0, + //"%s Command = %02X , address = %02X %02X, bytes = %d\n", + //tag, buff[0], buff[1], buff[2], toWrite); if (fts_writeCmd(buff, 3 + toWrite) < 0) { - logError(1, "%s fillMemory: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); return ERROR_I2C_W; } - address += toWrite; data += toWrite; - } + kfree(buff); return OK; } -int flash_burn(Firmware fw, int force_burn) +int flash_burn(Firmware fw, int force_burn, int keep_cx) { u8 cmd; int res; - if (!force_burn && (ftsInfo.u16_fwVer >= fw.fw_ver) && (ftsInfo.u16_cfgId >= fw.config_id)) { - logError(1, "%s flash_burn: Firmware in the chip newer or equal to the one to burn! NO UPDATE ERROR %02X\n", tag, ERROR_FW_NO_UPDATE); + if (!force_burn && (ftsInfo.u16_fwVer >= fw.fw_ver) + && (ftsInfo.u16_cfgId >= fw.config_id)) { + logError(1, "Firmware in the chip newer"); + logError(1, " or equal to the one to burn! "); + logError(1, "%s %s:NO UPDATE ERROR %02X\n", + tag, __func__, ERROR_FW_NO_UPDATE); return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); } - /* programming procedure start */ + //programming procedure start - logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + logError(0, "%s Programming Procedure for flashing started:\n", tag); logError(0, "%s 1) SYSTEM RESET:\n", tag); res = fts_system_reset(); if (res < 0) { - logError(1, "%s system reset FAILED!\n", tag); - if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) /* if there is no firmware i will not get the controller ready event and there will be a timeout but i can keep going, but if there is an I2C error i have to exit */ + logError(1, "%s system reset FAILED!\n", tag); + //if there is no firmware i will not + //get the controller ready event and + //there will be a timeout but i can + //keep going, but if there is + //an I2C error i have to exit + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) return (res | ERROR_FLASH_BURN_FAILED); } else - logError(0, "%s system reset COMPLETED!\n\n", tag); + logError(0, "%s system reset COMPLETED!\n\n", tag); logError(0, "%s 2) FLASH UNLOCK:\n", tag); res = flash_unlock(); if (res < 0) { - logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s flash unlock COMPLETED!\n\n", tag); + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + - /* Write the lower part of the Program RAM */ + //Write the lower part of the Program RAM logError(0, "%s 3) PREPARING DATA FOR FLASH BURN:\n", tag); res = fillMemory(FLASH_ADDR_CODE, fw.data, fw.data_size); if (res < 0) { - logError(1, "%s Error During filling the memory! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s Error During filling the memory!%02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s Data copy COMPLETED!\n\n", tag); + logError(0, "%s Data copy COMPLETED!\n\n", tag); logError(0, "%s 4) ERASE FLASH:\n", tag); res = wait_for_flash_ready(); if (res < 0) { - logError(1, "%s Flash not ready! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s Flash not ready! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } logError(0, "%s Command erase ...\n", tag); cmd = FLASH_CMD_ERASE; if (fts_writeCmd(&cmd, 1) < 0) { - logError(1, "%s Error during erasing flash! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s Error during erasing flash! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); } res = wait_for_flash_ready(); if (res < 0) { - logError(1, "%s Flash not ready 2! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s Flash not ready 2! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s Flash erase COMPLETED!\n\n", tag); + logError(0, "%s Flash erase COMPLETED!\n\n", tag); logError(0, "%s 5) BURN FLASH:\n", tag); logError(0, "%s Command burn ...\n", tag); cmd = FLASH_CMD_BURN; if (fts_writeCmd(&cmd, 1) < 0) { - logError(1, "%s Error during burning data! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s Error during burning data! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); } res = wait_for_flash_ready(); if (res < 0) { - logError(1, "%s Flash not ready! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s Flash not ready! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s Flash burn COMPLETED!\n\n", tag); + logError(0, "%s Flash burn COMPLETED!\n\n", tag); logError(0, "%s 6) SYSTEM RESET:\n", tag); res = fts_system_reset(); if (res < 0) { - logError(1, "%s system reset FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s system reset FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s system reset COMPLETED!\n\n", tag); + logError(0, "%s system reset COMPLETED!\n\n", tag); + logError(0, "%s 7) FINAL CHECK:\n", tag); res = readChipInfo(0); if (res < 0) { - logError(1, "%s flash_burn: Unable to retrieve Chip INFO! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s %s:Unable to retrieve Chip INFO!%02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - if ((ftsInfo.u16_fwVer != fw.fw_ver) && (ftsInfo.u16_cfgId != fw.config_id)) { - logError(1, "%s Firmware in the chip different from the one that was burn! fw: %x != %x , conf: %x != %x\n", tag, ftsInfo.u16_fwVer, fw.fw_ver, ftsInfo.u16_cfgId, fw.config_id); + if ((ftsInfo.u16_fwVer != fw.fw_ver) + && (ftsInfo.u16_cfgId != fw.config_id)) { + logError(1, "Firmware in the chip different"); + logError(1, " from the one that was burn!"); + logError(1, "%s fw: %x != %x , conf: %x != %x\n", + tag, ftsInfo.u16_fwVer, + fw.fw_ver, + ftsInfo.u16_cfgId, + fw.config_id); return ERROR_FLASH_BURN_FAILED; } - logError(0, "%s Final check OK! fw: %02X , conf: %02X\n", tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + logError(0, "%s Final check OK! fw: %02X, conf: %02X\n", + tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); return OK; } -int flashProcedure(const char *path, int force, int keep_cx) -{ - Firmware fw; - int res; - - fw.data = NULL; - logError(0, "%s Reading Fw file...\n", tag); - res = readFwFile(path, &fw, keep_cx); - if (res < OK) { - logError(1, "%s flashProcedure: ERROR %02X\n", tag, (res | ERROR_FLASH_PROCEDURE)); - kfree(fw.data); - return (res | ERROR_FLASH_PROCEDURE); - } - logError(0, "%s Fw file read COMPLETED!\n", tag); - - logError(0, "%s Starting flashing procedure...\n", tag); - res = flash_burn(fw, force); - if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { - logError(1, "%s flashProcedure: ERROR %02X\n", tag, ERROR_FLASH_PROCEDURE); - kfree(fw.data); - return (res | ERROR_FLASH_PROCEDURE); - } - logError(0, "%s flashing procedure Finished!\n", tag); - kfree(fw.data); - - /* cleanUp(0); //after talking with Kusuma, the SenseOn should be issued only at the very end of the initialization process, if senso on here it can trigger autotune protection */ - return res; -} - #else int wait_for_flash_ready(u8 type) @@ -544,17 +592,19 @@ int wait_for_flash_ready(u8 type) logError(0, "%s Waiting for flash ready ...\n", tag); for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) { - if (fts_readCmd(cmd, sizeof (cmd), &readData, 1) < 0) { - logError(1, "%s wait_for_flash_ready: ERROR % 02X\n", tag, ERROR_I2C_W); - } else{ + if (fts_readCmd(cmd, sizeof(cmd), &readData, 1) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_R); + } else { res = readData & 0x80; - /* logError(0, "%s flash status = %d \n", tag, res); */ - } + //logError(0, "%s flash status = %d\n", tag, res); + } msleep(FLASH_WAIT_BEFORE_RETRY); } if (i == FLASH_RETRY_COUNT && res != 0) { - logError(1, "%s Wait for flash TIMEOUT! ERROR %02X\n", tag, ERROR_TIMEOUT); + logError(1, "%s Wait for flash TIMEOUT! ERROR %02X\n", + tag, ERROR_TIMEOUT); return ERROR_TIMEOUT; } @@ -564,12 +614,13 @@ int wait_for_flash_ready(u8 type) int fts_warm_boot(void) { + //write the command to perform the warm boot + u8 cmd[4] = {FTS_CMD_HW_REG_W, 0x00, 0x00, WARM_BOOT_VALUE}; - u8 cmd[4] = {FTS_CMD_HW_REG_W, 0x00, 0x00, WARM_BOOT_VALUE}; /* write the command to perform the warm boot */ u16ToU8_be(ADDR_WARM_BOOT, &cmd[1]); logError(0, "%s Command warm boot ...\n", tag); - if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); return ERROR_I2C_W; } @@ -579,251 +630,272 @@ int fts_warm_boot(void) return OK; } -int parseBinFile(const char *pathToFile, Firmware *fwData, int keep_cx) +int parseBinFile(u8 *data, int fw_size, + struct Firmware *fwData, int keep_cx) { - - int fd = -1; int dimension, index = 0; u32 temp; - u8 *data; - int res, i, fw_size; - -#ifndef FW_H_FILE - const struct firmware *fw = NULL; - struct device *dev = NULL; - dev = getDev(); - - if (dev != NULL) - fd = request_firmware(&fw, pathToFile, dev); - else { - logError(1, "%s parseBinFile: No device found! ERROR %02X\n", ERROR_FILE_PARSE); - return ERROR_FILE_PARSE; - } - - fw_size = fw->size; -#else -fd = 0; -fw_size = SIZE_NAME; -#endif - - if (fd == 0 && fw_size > 0) { /* the file should contain at least the header plus the content_crc */ - if (fw_size < FW_HEADER_SIZE+FW_BYTES_ALIGN) { - logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, FW_HEADER_SIZE+FW_BYTES_ALIGN, ERROR_FILE_PARSE); + int res, i; + + //the file should contain at least the header plus the content_crc + if (fw_size < FW_HEADER_SIZE+FW_BYTES_ALIGN || data == NULL) { + logError(1, "%s %s:Read only %d instead of %d...ERROR %02X\n", + tag, __func__, fw_size, + FW_HEADER_SIZE + FW_BYTES_ALIGN, + ERROR_FILE_PARSE); res = ERROR_FILE_PARSE; goto END; - } else { - /* start parsing of bytes */ -#ifndef FW_H_FILE - data = (u8 *) (fw->data); -#else - data = (u8 *) (ARRAY_NAME); -#endif + } else { + //start parsing of bytes u8ToU32(&data[index], &temp); if (temp != FW_HEADER_SIGNATURE) { - logError(1, "%s parseBinFile: Wrong Signature %08X ... ERROR %02X\n", tag, temp, ERROR_FILE_PARSE); - res = ERROR_FILE_PARSE; + logError(1, "%s %s:Wrong Signature %08X...ERROR %02X\n", + tag, __func__, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; goto END; } - logError(0, "%s parseBinFile: Fw Signature OK!\n", tag); + + logError(0, "%s %s: Fw Signature OK!\n", tag, __func__); index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); if (temp != FW_FTB_VER) { - logError(1, "%s parseBinFile: Wrong ftb_version %08X ... ERROR %02X\n", tag, temp, ERROR_FILE_PARSE); - res = ERROR_FILE_PARSE; + logError(1, "%s %s:Wrong ftb_version %08X.ERROR %02X\n", + tag, __func__, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; goto END; } - logError(0, "%s parseBinFile: ftb_version OK!\n", tag); + logError(0, "%s %s:ftb_version OK!\n", __func__, tag); index += FW_BYTES_ALIGN; if (data[index] != DCHIP_ID_0 || data[index+1] != DCHIP_ID_1) { - logError(1, "%s parseBinFile: Wrong target %02X != %02X %02X != %02X ... ERROR %08X\n", tag, data[index], DCHIP_ID_0, data[index+1], DCHIP_ID_1, ERROR_FILE_PARSE); - res = ERROR_FILE_PARSE; + logError(1, "%s %s:Wrong target %02X != %02X ", + tag, __func__, data[index]); + logError(1, "%%02X != %02X:%08X\n", + DCHIP_ID_0, data[index+1], + DCHIP_ID_1, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; goto END; } index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); - logError(1, "%s parseBinFile: Fw ID = %08X\n", tag, temp); - + logError(1, "%s %s: Fw ID = %08X\n", tag, __func__, temp); index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); fwData->fw_ver = temp; - logError(1, "%s parseBinFile: FILE Fw Version = %04X\n", tag, fwData->fw_ver); + logError(1, "%s %s:FILE Fw Version = %04X\n", + tag, __func__, fwData->fw_ver); index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); fwData->config_id = temp; - logError(1, "%s parseBinFile: FILE Config ID = %08X\n", tag, temp); + logError(1, "%s %s:FILE Config ID = %04X\n", + tag, __func__, fwData->config_id); index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); - logError(1, "%s parseBinFile: Config Version = %08X\n", tag, temp); - - index += FW_BYTES_ALIGN*2; /* skip reserved data */ - + logError(1, "%s %s:Config Version = %08X\n", + tag, __func__, temp); + //skip reserved data + index += FW_BYTES_ALIGN * 2; index += FW_BYTES_ALIGN; - logError(1, "%s parseBinFile: File External Release = ", tag); + logError(1, "%s %s:File External Release = ", + tag, __func__); for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { fwData->externalRelease[i] = data[index++]; - logError(1, "%02X ", fwData->externalRelease[i]); + logError(1, "%02X", fwData->externalRelease[i]); } logError(1, "\n"); - /* index += FW_BYTES_ALIGN; */ + //index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); fwData->sec0_size = temp; - logError(1, "%s parseBinFile: sec0_size = %08X (%d bytes)\n", tag, fwData->sec0_size, fwData->sec0_size); + logError(1, "%s %s:sec0_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec0_size, fwData->sec0_size); index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); fwData->sec1_size = temp; - logError(1, "%s parseBinFile: sec1_size = %08X (%d bytes)\n", tag, fwData->sec1_size, fwData->sec1_size); + logError(1, "%s %s:sec1_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec1_size, fwData->sec1_size); index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); fwData->sec2_size = temp; - logError(1, "%s parseBinFile: sec2_size = %08X (%d bytes)\n", tag, fwData->sec2_size, fwData->sec2_size); + logError(1, "%s %s:sec2_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec2_size, fwData->sec2_size); index += FW_BYTES_ALIGN; u8ToU32(&data[index], &temp); fwData->sec3_size = temp; - logError(1, "%s parseBinFile: sec3_size = %08X (%d bytes)\n", tag, fwData->sec3_size, fwData->sec3_size); - - index += FW_BYTES_ALIGN; /* skip header crc */ + logError(1, "%s %s:sec3_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec3_size, fwData->sec3_size); + //skip header crc + index += FW_BYTES_ALIGN; if (!keep_cx) { - dimension = fwData->sec0_size + fwData->sec1_size + fwData->sec2_size + fwData->sec3_size; - temp = fw_size; + dimension = fwData->sec0_size + fwData->sec1_size + + fwData->sec2_size + fwData->sec3_size; + temp = fw_size; } else { - dimension = fwData->sec0_size + fwData->sec1_size; /* sec2 may contain cx data (future implementation) sec3 atm not used */ - temp = fw_size - fwData->sec2_size - fwData->sec3_size; + //sec2 may contain cx data (future implementation) + //sec3 atm not used + dimension = fwData->sec0_size + fwData->sec1_size; + temp = fw_size - fwData->sec2_size - fwData->sec3_size; + fwData->sec2_size = 0; + fwData->sec3_size = 0; } - - if (dimension+FW_HEADER_SIZE+FW_BYTES_ALIGN != temp) { - logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, dimension+FW_HEADER_SIZE+FW_BYTES_ALIGN, ERROR_FILE_PARSE); + if (dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN != temp) { + logError(1, "%s %s:Read only %d instead of %d...", + tag, __func__, fw_size, + dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN); + logError(1, "ERROR %02X\n", ERROR_FILE_PARSE); res = ERROR_FILE_PARSE; goto END; } - - fwData->data = (u8 *) kmalloc(dimension * sizeof (u8), GFP_KERNEL); - if (fwData->data == NULL) { - logError(1, "%s parseBinFile: ERROR %02X\n", tag, ERROR_ALLOC); - res = ERROR_ALLOC; - goto END; - } - + fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8), + GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + res = ERROR_ALLOC; + goto END; + } index += FW_BYTES_ALIGN; - memcpy(fwData->data, &data[index], dimension); - fwData->data_size = dimension; - - logError(0, "%s READ FW DONE %d bytes!\n", tag, fwData->data_size); - res = OK; + memcpy(fwData->data, &data[index], dimension); + fwData->data_size = dimension; + logError(0, "%s READ FW DONE %d bytes!\n", + tag, fwData->data_size); + res = OK; goto END; - } - } else { - logError(1, "%s parseBinFile: File Not Found! ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); - return ERROR_FILE_NOT_FOUND; } - END: -#ifndef FW_H_FILE - release_firmware(fw); -#endif - return res; -} - -int readFwFile(const char *path, Firmware *fw, int keep_cx) -{ - int res; - - res = parseBinFile(path, fw, keep_cx); - if (res < OK) { - logError(1, "%s readFwFile: ERROR %02X\n", tag, ERROR_MEMH_READ); - return (res | ERROR_MEMH_READ); - } - - return OK; - + kfree(data); + return res; } int flash_unlock(void) { - u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; /* write the command to perform the unlock */ - + //write the command to perform the unlock + u8 cmd[3] = {FLASH_CMD_UNLOCK, + FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; logError(0, "%s Command unlock ...\n", tag); - if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { - logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } - - /* msleep(FLASH_WAIT_TIME); */ + //mdelay(FLASH_WAIT_TIME); logError(0, "%s Unlock flash DONE!\n", tag); return OK; - } int flash_erase_unlock(void) { - u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_UNLOCK_CODE0, FLASH_ERASE_UNLOCK_CODE1}; /* write the command to perform the unlock for erasing the flash */ + //write the command to perform + //the unlock for erasing the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_UNLOCK_CODE0, + FLASH_ERASE_UNLOCK_CODE1}; logError(0, "%s Try to erase unlock flash...\n", tag); logError(0, "%s Command erase unlock ...\n", tag); - if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { - logError(1, "%s flash_erase_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } logError(0, "%s Erase Unlock flash DONE!\n", tag); return OK; - } int flash_full_erase(void) { - int status; - u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, FLASH_ERASE_CODE1}; - /* write the command to erase the flash */ - - logError(0, "%s Command full erase sent ...\n", tag); - if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { - logError(1, "%s flash_full_erase: ERROR % 02X\n", tag, ERROR_I2C_W); + //write the command to erase the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, + FLASH_ERASE_CODE1}; + + logError(0, "%s Command full erase sent...\n", + tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } status = wait_for_flash_ready(FLASH_ERASE_CODE0); if (status != OK) { - logError(1, "%s flash_full_erase: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + logError(1, "%s %s:ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! return (status | ERROR_FLASH_NOT_READY); - /* Flash not ready within the chosen time, better exit! */ } logError(0, "%s Full Erase flash DONE!\n", tag); return OK; +} + +int flash_erase_page_by_page(int keep_cx) +{ + u8 status, i = 0; + //write the command to erase the flash + u8 cmd[4] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, 0x00, 0x00}; + + for (i = 0; i < FLASH_NUM_PAGE; i++) { + if (i >= FLASH_CX_PAGE_START && i <= FLASH_CX_PAGE_END + && keep_cx == 1) { + logError(0, "%s Skipping erase page %d!\n", tag, i); + continue; + } + cmd[2] = (0x3F & i) | FLASH_ERASE_START; + logError(0, "Command erase page %d sent", i); + logError(0, "%s:%02X %02X %02X %02X\n", + tag, i, cmd[0], cmd[1], cmd[2], cmd[3]); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, + "%s %s:ERROR % 08X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + if (status != OK) { + logError(1, "%s %s:ERROR % 08X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); + } + } + + logError(0, "%s Erase flash page by page DONE!\n", tag); + + return OK; } int start_flash_dma(void) { int status; - u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_DMA_CODE0, FLASH_DMA_CODE1}; - /* write the command to erase the flash */ + //write the command to erase the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_DMA_CODE0, + FLASH_DMA_CODE1}; logError(0, "%s Command flash DMA ...\n", tag); - if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { - logError(1, "%s start_flash_dma: ERROR % 02X\n", tag, ERROR_I2C_W); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } status = wait_for_flash_ready(FLASH_DMA_CODE0); if (status != OK) { - logError(1, "%s start_flash_dma: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, better exit! return (status | ERROR_FLASH_NOT_READY); - /* Flash not ready within the chosen time, better exit! */ } logError(0, "%s flash DMA DONE!\n", tag); @@ -833,7 +905,6 @@ int start_flash_dma(void) int fillFlash(u32 address, u8 *data, int size) { - int remaining = size; int toWrite = 0; int byteBlock = 0; @@ -841,26 +912,29 @@ int fillFlash(u32 address, u8 *data, int size) u32 addr = 0; int res; int delta; + u8 *buff = NULL; + u8 buff2[9] = {0}; + - u8 *buff = (u8 *) kmalloc((DMA_CHUNK + 3) * sizeof (u8), GFP_KERNEL); + buff = (u8 *)kmalloc_array((DMA_CHUNK + 3), sizeof(u8), GFP_KERNEL); if (buff == NULL) { - logError(1, "%s fillFlash: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } while (remaining > 0) { - byteBlock = 0; + byteBlock = 0; addr = 0; while (byteBlock < FLASH_CHUNK && remaining > 0) { buff[0] = FLASH_CMD_WRITE_64K; if (remaining >= DMA_CHUNK) { if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) { - /* logError(1, "%s fillFlash: 1\n", tag); */ + //logError(1, "%s fillFlash:1\n", tag); toWrite = DMA_CHUNK; remaining -= DMA_CHUNK; byteBlock += DMA_CHUNK; } else { - /* logError(1, "%s fillFlash: 2\n", tag); */ + //logError(1, "%s fillFlash:2\n", tag); delta = FLASH_CHUNK - byteBlock; toWrite = delta; remaining -= delta; @@ -868,13 +942,12 @@ int fillFlash(u32 address, u8 *data, int size) } } else { if ((byteBlock + remaining) <= FLASH_CHUNK) { - /* logError(1, "%s fillFlash: 3\n", tag); */ + //logError(1, "%s fillFlash:3\n", tag); toWrite = remaining; - byteBlock += remaining; + byteBlock += remaining; remaining = 0; - } else { - /* logError(1, "%s fillFlash: 4\n", tag); */ + //logError(1, "%s fillFlash:4\n", tag); delta = FLASH_CHUNK - byteBlock; toWrite = delta; remaining -= delta; @@ -885,187 +958,195 @@ int fillFlash(u32 address, u8 *data, int size) buff[1] = (u8) ((addr & 0x0000FF00) >> 8); buff[2] = (u8) (addr & 0x000000FF); memcpy(&buff[3], data, toWrite); + //logError(0, + //"%s Command = %02X, address = %02X %02X, + //bytes = %d, data = %02X %02X, %02X %02X\n", + //tag, buff[0], buff[1], buff[2], toWrite, + //buff[3], buff[4], buff[3 + toWrite-2], + //buff[3 + toWrite-1]); if (fts_writeCmd(buff, 3 + toWrite) < 0) { - logError(1, "%s fillFlash: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); return ERROR_I2C_W; } addr += toWrite; data += toWrite; } - - kfree(buff); - - /* configuring the DMA */ + //configuring the DMA byteBlock = byteBlock / 4 - 1; - buff = (u8 *) kmalloc((9) * sizeof (u8), GFP_KERNEL); - buff[0] = FLASH_CMD_WRITE_REGISTER; - buff[1] = FLASH_DMA_CONFIG; - buff[2] = 0x00; - buff[3] = 0x00; - - addr = address + ((wheel * FLASH_CHUNK)/4); - buff[4] = (u8) ((addr & 0x000000FF)); - buff[5] = (u8) ((addr & 0x0000FF00) >> 8); - buff[6] = (u8) (byteBlock & 0x000000FF); - buff[7] = (u8) ((byteBlock & 0x0000FF00) >> 8); - buff[8] = 0x00; - - logError(0, "%s Command = %02X , address = %02X %02X, words = %02X %02X\n", tag, buff[0], buff[5], buff[4], buff[7], buff[6]); - if (fts_writeCmd(buff, 9) < OK) { - logError(1, "%s Error during filling Flash! ERROR %02X\n", tag, ERROR_I2C_W); + buff2[0] = FLASH_CMD_WRITE_REGISTER; + buff2[1] = FLASH_DMA_CONFIG; + buff2[2] = 0x00; + buff2[3] = 0x00; + + addr = address + ((wheel * FLASH_CHUNK)/4); + buff2[4] = (u8) ((addr & 0x000000FF)); + buff2[5] = (u8) ((addr & 0x0000FF00) >> 8); + buff2[6] = (u8) (byteBlock & 0x000000FF); + buff2[7] = (u8) ((byteBlock & 0x0000FF00) >> 8); + buff2[8] = 0x00; + + logError(0, "%s:Command:%02X, address:%02X %02X, ", + tag, buff2[0], buff2[5], buff2[4]); + logError(0, "words:%02X %02X\n", buff2[7], buff2[6]); + if (fts_writeCmd(buff2, 9) < OK) { + logError(1, "%s Error during filling Flash!:%02X\n", + tag, ERROR_I2C_W); + kfree(buff); return ERROR_I2C_W; } - - /* msleep(FLASH_WAIT_TIME); */ + //mdelay(FLASH_WAIT_TIME); res = start_flash_dma(); if (res < OK) { - logError(1, "%s Error during flashing DMA! ERROR %02X\n", tag, res); + logError(1, "%s Error during flashing DMA!:%02X\n", + tag, res); + kfree(buff); return res; } wheel++; } + kfree(buff); return OK; } -int flash_burn(Firmware fw, int force_burn) +int flash_burn(struct Firmware fw, int force_burn, int keep_cx) { int res; if (!force_burn) { - for (res = EXTERNAL_RELEASE_INFO_SIZE-1; res >= 0; res--) { - if (fw.externalRelease[res] > ftsInfo.u8_extReleaseInfo[res]) - goto start; - } - logError(1, "%s flash_burn: Firmware in the chip newer or equal to the one to burn! NO UPDATE ERROR %02X\n", tag, ERROR_FW_NO_UPDATE); + for (res = EXTERNAL_RELEASE_INFO_SIZE-1; res >= 0; res--) { + if (fw.externalRelease[res] > + ftsInfo.u8_extReleaseInfo[res]) + goto start; + } + logError(1, "Firmware in the chip newer or "); + logError(1, "equal to the one to burn!"); + logError(1, "%s %s:NO UPDATE ERROR %02X\n", + tag, __func__, ERROR_FW_NO_UPDATE); return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); } - /* programming procedure start */ + //programming procedure start start: logError(0, "%s Programming Procedure for flashing started:\n\n", tag); logError(0, "%s 1) SYSTEM RESET:\n", tag); res = fts_system_reset(); if (res < 0) { - logError(1, "%s system reset FAILED!\n", tag); + logError(1, "%s system reset FAILED!\n", tag); + /** + * if there is no firmware i will not + * get the controller ready event and + * there will be a timeout but i can + * keep going, but if there is an I2C + * error i have to exit + */ if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) - /* if there is no firmware i will not get the controller - *ready event and there will be a timeout but i can keep going, - *but if there is an I2C error i have to exit - */ return (res | ERROR_FLASH_BURN_FAILED); } else - logError(0, "%s system reset COMPLETED!\n\n", tag); + logError(0, "%s system reset COMPLETED!\n\n", tag); logError(0, "%s 2) WARM BOOT:\n", tag); res = fts_warm_boot(); if (res < OK) { - logError(1, "%s warm boot FAILED!\n", tag); - return (res | ERROR_FLASH_BURN_FAILED); - } else - logError(0, "%s warm boot COMPLETED!\n\n", tag); + logError(1, "%s warm boot FAILED!\n", tag); + return (res | ERROR_FLASH_BURN_FAILED); + } /*else*/ + logError(0, "%s warm boot COMPLETED!\n\n", tag); - /* msleep(FLASH_WAIT_TIME); */ + //mdelay(FLASH_WAIT_TIME); logError(0, "%s 3) FLASH UNLOCK:\n", tag); res = flash_unlock(); if (res < OK) { - logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s flash unlock COMPLETED!\n\n", tag); + logError(0, "%s flash unlock COMPLETED!\n\n", tag); - /* msleep(200); */ + //mdelay(200); logError(0, "%s 4) FLASH ERASE UNLOCK:\n", tag); res = flash_erase_unlock(); if (res < 0) { - logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s flash unlock COMPLETED!\n\n", tag); + logError(0, "%s flash unlock COMPLETED!\n\n", tag); - /* msleep(FLASH_WAIT_TIME); */ + //mdelay(FLASH_WAIT_TIME); logError(0, "%s 5) FLASH ERASE:\n", tag); - res = flash_full_erase(); + if (keep_cx == 1) + res = flash_erase_page_by_page(keep_cx); + else + res = flash_full_erase(); if (res < 0) { - logError(1, "%s flash erase FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s flash erase FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(0, "%s flash erase COMPLETED!\n\n", tag); + logError(0, "%s flash erase COMPLETED!\n\n", tag); + - /* msleep(FLASH_WAIT_TIME); */ + //mdelay(FLASH_WAIT_TIME); logError(0, "%s 6) LOAD PROGRAM:\n", tag); - res = fillFlash(FLASH_ADDR_CODE, &fw.data[0], fw.sec0_size); + res = fillFlash(FLASH_ADDR_CODE, &fw.data[0], + fw.sec0_size); if (res < OK) { - logError(1, "%s load program ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s load program ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } logError(1, "%s load program DONE!\n", tag); - logError(0, "%s 7) LOAD CONFIG:\n", tag); - res = fillFlash(FLASH_ADDR_CONFIG, &(fw.data[fw.sec0_size]), fw.sec1_size); + res = fillFlash(FLASH_ADDR_CONFIG, + &(fw.data[fw.sec0_size]), fw.sec1_size); if (res < OK) { - logError(1, "%s load config ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s load config ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } - logError(1, "%s load config DONE!\n", tag); + logError(1, "%s load config DONE!\n", tag); logError(0, "%s Flash burn COMPLETED!\n\n", tag); logError(0, "%s 8) SYSTEM RESET:\n", tag); res = fts_system_reset(); if (res < 0) { - logError(1, "%s system reset FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s system reset FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } logError(0, "%s system reset COMPLETED!\n\n", tag); + logError(0, "%s 9) FINAL CHECK:\n", tag); res = readChipInfo(0); if (res < 0) { - logError(1, "%s flash_burn: Unable to retrieve Chip INFO! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + logError(1, "%s %s:Unable to retrieve Chip INFO!:%02X\n", + tag, __func__, ERROR_FLASH_BURN_FAILED); return (res | ERROR_FLASH_BURN_FAILED); } for (res = 0; res < EXTERNAL_RELEASE_INFO_SIZE; res++) { + ////external release is prined during readChipInfo if (fw.externalRelease[res] != ftsInfo.u8_extReleaseInfo[res]) { - /* external release is prined during readChipInfo */ - logError(1, "%s Firmware in the chip different from the one that was burn! fw: %x != %x , conf: %x != %x\n", tag, ftsInfo.u16_fwVer, fw.fw_ver, ftsInfo.u16_cfgId, fw.config_id); - return ERROR_FLASH_BURN_FAILED; + pr_err("Firmware in the chip different from"); + pr_err(" the one that was burn!"); + logError(1, "%s fw: %x != %x, conf: %x != %x\n", + tag, ftsInfo.u16_fwVer, fw.fw_ver, + ftsInfo.u16_cfgId, fw.config_id); + return ERROR_FLASH_BURN_FAILED; } } - logError(0, "%s Final check OK! fw: %02X, conf: %02X\n", tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + logError(0, "%s Final check OK! fw: %02X , conf: %02X\n", + tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); return OK; } -int flashProcedure(const char *path, int force, int keep_cx) -{ - Firmware fw; - int res; - - fw.data = NULL; - logError(0, "%s Reading Fw file...\n", tag); - res = readFwFile(path, &fw, keep_cx); - if (res < OK) { - logError(1, "%s flashProcedure: ERROR %02X\n", tag, (res | ERROR_FLASH_PROCEDURE)); - kfree(fw.data); - return (res | ERROR_FLASH_PROCEDURE); - } - logError(0, "%s Fw file read COMPLETED!\n", tag); - - logError(0, "%s Starting flashing procedure...\n", tag); - res = flash_burn(fw, force); - if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { - logError(1, "%s flashProcedure: ERROR %02X\n", tag, ERROR_FLASH_PROCEDURE); - kfree(fw.data); - return (res | ERROR_FLASH_PROCEDURE); - } - logError(0, "%s flashing procedure Finished!\n", tag); - kfree(fw.data); - return res; -} - #endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.h b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h index 69635e07a9f473f89f699ffb8b2afc4364b069db..8872e9ad2905c31c1707f197d4c4e1704b991a21 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsFlash.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h @@ -1,47 +1,72 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS API for Flashing the IC * -* * -************************************************************************** -************************************************************************** +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_FLASH_H +#define __FTS_FLASH_H -*/ #include "ftsSoftware.h" -/* Flash possible status */ -#define FLASH_READY 0 -#define FLASH_BUSY 1 -#define FLASH_UNKNOWN -1 +//Flash possible status +#define FLASH_READY 0 +#define FLASH_BUSY 1 +#define FLASH_UNKNOWN -1 +#define FLASH_STATUS_BYTES 1 -#define FLASH_STATUS_BYTES 1 -/* Flash timing parameters */ -#define FLASH_RETRY_COUNT 1000 -#define FLASH_WAIT_BEFORE_RETRY 50 /* ms */ +//Flash timing parameters +#define FLASH_RETRY_COUNT 1000 +#define FLASH_WAIT_BEFORE_RETRY 50 //ms +#define FLASH_WAIT_TIME 200 //ms -#define FLASH_WAIT_TIME 200 /* ms */ -/* PATHS FW FILES */ -/* #define PATH_FILE_FW "fw.memh" */ +//PATHS FW FILES +//#define PATH_FILE_FW "fw.memh" #ifdef FTM3_CHIP -#define PATH_FILE_FW "st_fts.bin" +#define PATH_FILE_FW "st_fts.bin" #else -#define PATH_FILE_FW "st_fts.ftb" /* new bin file structure */ +#define PATH_FILE_FW "st_fts.ftb"//new bin file structure #endif #ifndef FTM3_CHIP -#define FLASH_CHUNK (64*1024) -#define DMA_CHUNK 32 +#define FLASH_CHUNK (64 * 1024) +#define DMA_CHUNK (2 * 1024) #endif -typedef struct { + +struct Firmware { u8 *data; u16 fw_ver; u16 config_id; @@ -53,20 +78,19 @@ typedef struct { u32 sec2_size; u32 sec3_size; #endif -} Firmware; +}; #ifdef FTM3_CHIP int flash_status(void); int flash_status_ready(void); int wait_for_flash_ready(void); -int parseBinFile(const char *pathToFile, u8 **data, int *length, int dimension); -/* int parseMemhFile(const char* pathToFile, u8** data, int* length, int dimension); */ #else int wait_for_flash_ready(u8 type); int fts_warm_boot(void); -int parseBinFile(const char *pathToFile, Firmware *fw, int keep_cx); int flash_erase_unlock(void); int flash_full_erase(void); +int flash_erase_page_by_page(int keep_cx); +//int flash_erase_page_by_page_info(int page); int start_flash_dma(void); int fillFlash(u32 address, u8 *data, int size); #endif @@ -74,6 +98,10 @@ int fillFlash(u32 address, u8 *data, int size); int flash_unlock(void); int fillMemory(u32 address, u8 *data, int size); int getFirmwareVersion(u16 *fw_vers, u16 *config_id); -int readFwFile(const char *path, Firmware *fw, int keep_cx); -int flash_burn(Firmware fw, int force_burn); +int getFWdata(const char *pathToFile, u8 **data, int *size, int from); +int parseBinFile(u8 *fw_data, int fw_size, struct Firmware *fw, int keep_cx); +int readFwFile(const char *path, struct Firmware *fw, int keep_cx); +int flash_burn(struct Firmware fw, int force_burn, int keep_cx); int flashProcedure(const char *path, int force, int keep_cx); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.c b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c index c502559319a00c232f5b9e0eff7d6819d99a810a..7e6d51c064be69e41cc63b3fe004d28ec5c3151f 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsFrame.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c @@ -1,27 +1,36 @@ /* - -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS functions for getting frames * -* * -************************************************************************** -************************************************************************** - -*/ - -#include "ftsCrossCompile.h" -#include "ftsCompensation.h" -#include "ftsError.h" -#include "ftsFrame.h" -#include "ftsHardware.h" -#include "ftsIO.h" -#include "ftsSoftware.h" -#include "ftsTool.h" -#include "ftsTime.h" + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + */ #include #include @@ -47,69 +56,88 @@ #include #include #include -/* #include */ +//#include + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" +#include "ftsTime.h" +#include "../fts.h" static char tag[8] = "[ FTS ]\0"; static int sense_len, force_len; -/*int getOffsetFrame(u16 address, u16 *offset) +int getOffsetFrame(u16 address, u16 *offset) { - u8 data[2]; u8 cmd = { FTS_CMD_FRAMEBUFFER_R }; + char *temp = NULL; - if (readCmdU16(cmd, address, data, OFFSET_LENGTH, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s getOffsetFrame: ERROR %02X\n", tag, ERROR_I2C_R); + if (readCmdU16(cmd, address, data, OFFSET_LENGTH, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %S: ERROR %02X\n", tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } - else { - u8ToU16(data, offset); - logError(0, "%s %s", tag, printHex("Offest = ", data, OFFSET_LENGTH)); - return OK; - } -}*/ + u8ToU16(data, offset); + temp = printHex("Offest = ", data, OFFSET_LENGTH); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + return OK; +} int getChannelsLength(void) { - int ret; - u8 *data = (u8 *)kmalloc(2*sizeof(u8), GFP_KERNEL); + u8 *data = (u8 *)kmalloc_array(2, sizeof(u8), GFP_KERNEL); if (data == NULL) { - logError(1, "%s getChannelsLength: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } ret = readB2(ADDR_SENSE_LEN, data, 2); if (ret < OK) { - logError(1, "%s getChannelsLength: ERROR %02X\n", tag, ERROR_READ_B2); - return (ret|ERROR_READ_B2); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_READ_B2); + kfree(data); + return (ret|ERROR_READ_B2); } sense_len = (int)data[0]; force_len = (int)data[1]; - logError(0, "%s Force_len = %d Sense_Len = %d\n", tag, force_len, sense_len); - + logError(0, "%s Force_len = %d Sense_Len = %d\n", + tag, force_len, sense_len); kfree(data); return OK; } + int getFrameData(u16 address, int size, short **frame) { int i, j, ret; - u8 *data = (u8 *)kmalloc(size*sizeof(u8), GFP_KERNEL); + u8 *data = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); + if (data == NULL) { - logError(1, "%s getFrameData: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } - - ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, DUMMY_FRAMEBUFFER); + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, + data, size, DUMMY_FRAMEBUFFER); if (ret < OK) { - logError(1, "%s getFrameData: ERROR %02X\n", tag, ERROR_I2C_R); - kfree(data); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + kfree(data); return ERROR_I2C_R; } j = 0; @@ -121,238 +149,111 @@ int getFrameData(u16 address, int size, short **frame) return OK; } -/*int getMSFrame(u16 type, short **frame, int keep_first_row) + +int getMSFrame(u16 type, struct MutualSenseFrame *frame, int keep_first_row) { u16 offset; - int size, ret; + int ret; if (getSenseLen() == 0 || getForceLen() == 0) { - ret=getChannelsLength(); - if (retdata.header.sense_node) - size = data.header.force_node; - else - size = data.header.sense_node; - - *frame = (short*)kmalloc(size*sizeof(short), GFP_KERNEL); - if (frame == NULL) { - logError(1, "%s getMSKeyFrame: ERROR %02X\n", tag, ERROR_ALLOC); - return ERROR_ALLOC; - } - - ret = getFrameData(offset, size*BYTES_PER_NODE, frame); - if (retnode_data_size = ((force_len + 1) * sense_len); + frame->header.force_node = force_len + 1; + } else { + frame->node_data_size = ((force_len) * sense_len); + offset += (sense_len * BYTES_PER_NODE); + frame->header.force_node = force_len; } - } - - switch (type) { - case ADDR_RAW_HOVER_FORCE: - case ADDR_FILTER_HOVER_FORCE: - case ADDR_NORM_HOVER_FORCE: - case ADDR_CALIB_HOVER_FORCE: - case ADDR_RAW_PRX_FORCE: - case ADDR_FILTER_PRX_FORCE: - case ADDR_NORM_PRX_FORCE: - case ADDR_CALIB_PRX_FORCE: - size = ((force_len)* 1); + frame->header.sense_node = sense_len; break; - - case ADDR_RAW_HOVER_SENSE: - case ADDR_FILTER_HOVER_SENSE: - case ADDR_NORM_HOVER_SENSE: - case ADDR_CALIB_HOVER_SENSE: - case ADDR_RAW_PRX_SENSE: - case ADDR_FILTER_PRX_SENSE: - case ADDR_NORM_PRX_SENSE: - case ADDR_CALIB_PRX_SENSE: - size = ((1)*sense_len); + case ADDR_NORM_MS_KEY: + case ADDR_RAW_MS_KEY: + frame->header.force_node = 1; + frame->header.sense_node = ftsInfo.u8_msKeyLen; + frame->node_data_size = ftsInfo.u8_msKeyLen; break; - default: - logError(1, "%s getSSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; - } - - ret = getOffsetFrame(type, &offset); - if (retnode_data = (short *)kmalloc_array(frame->node_data_size, + sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } - ret = getFrameData(offset, size*BYTES_PER_NODE, frame); - if (retnode_data_size * BYTES_PER_NODE, + &(frame->node_data)); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(frame->node_data); return (ret | ERROR_GET_FRAME_DATA); } - + // if you want to access one node i,j, + //you should compute the offset like: + //offset = i * columns + j => frame[i, j] logError(0, "%s Frame acquired!\n", tag); - return size; - + //return the number of data put inside frame + return frame->node_data_size; } -int getNmsFrame(u16 type, short ***frames, int *size, int keep_first_row, int fs, int n) { - int i; - StopWatch global, local; - int temp; - - *frames = (short **)kmalloc(n*sizeof(short *), GFP_KERNEL); - - if (*frames == NULL) { - logError(1, "%s getNmsFrame: ERROR %02X\n", tag, ERROR_ALLOC); - return ERROR_ALLOC; - } - - fs = (1*1000 / fs) ; - - startStopWatch(&global); - for (i = 0; i < n; i++) { - - startStopWatch(&local); - - *size = getMSFrame(type, ((*frames)+i), keep_first_row); - if (*size < OK) { - logError(1, "%s getNFrame: getFrame failed\n",tag); - return *size; - } - - stopStopWatch(&local); - temp = elapsedMillisecond(&local); - logError(0, "%s Iteration %d performed in %d ms... the process wait for %ld ms\n\n", tag, i, temp, (unsigned long)(fs - temp)); - - if (temp < fs) - msleep((unsigned long)(fs - temp)); - - } - - stopStopWatch(&global); - temp = elapsedMillisecond(&global); - logError(0, "%s Global Iteration performed in %d ms\n", tag, temp); - temp /= n; - logError(0, "%s Mean Iteration performed in %d ms\n", tag, temp); - return (1000 / (temp)); - -}*/ - int getSenseLen(void) { int ret; + if (sense_len != 0) return sense_len; - ret = getChannelsLength(); - if (ret < OK) - return ret; - else - return sense_len; + + if (ftsInfo.u8_scrSenseLen != 0) { + sense_len = ftsInfo.u8_scrSenseLen; + } else { + ret = getChannelsLength(); + if (ret < OK) + return ret; + } + return sense_len; } int getForceLen(void) { int ret; + if (force_len != 0) return force_len; - ret = getChannelsLength(); - if (ret < OK) - return ret; - else - return force_len; + + if (ftsInfo.u8_scrForceLen != 0) { + force_len = ftsInfo.u8_scrForceLen; + } else { + ret = getChannelsLength(); + if (ret < OK) + return ret; + } + return force_len; } int requestFrame(u16 type) @@ -360,70 +261,87 @@ int requestFrame(u16 type) int retry = 0; int ret; u16 answer; + char *temp = NULL; int event_to_search[1]; u8 readEvent[FIFO_EVENT_SIZE]; - u8 cmd[3] = { FTS_CMD_REQU_FRAME_DATA, 0x00, 0x00 }; - /* B7 is the command for asking frame data */ + u8 cmd[3] = { FTS_CMD_REQU_FRAME_DATA, 0x00, 0x00}; + // B7 is the command for asking frame data event_to_search[0] = (int)EVENTID_FRAME_DATA_READ; + u16ToU8(type, &cmd[1]); while (retry < FRAME_DATA_READ_RETRY) { - logError(0, "%s %s", tag, printHex("Command = ", cmd, 3)); + temp = printHex("Command = ", cmd, 3); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + + //send the request to the chip to load in memory the Frame Data ret = fts_writeFwCmd(cmd, 3); - /* send the request to the chip to load in memory the Frame Data */ if (ret < OK) { - logError(1, "%s requestFrame: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } - ret = pollForEvent(event_to_search, 1, readEvent, TIMEOUT_REQU_COMP_DATA); + ret = pollForEvent(event_to_search, + 1, + readEvent, + TIMEOUT_REQU_COMP_DATA); if (ret < OK) { - logError(0, "%s Event did not Found at %d attemp!\n", tag, retry + 1); + logError(0, "%s Event did not Found at %d attemp!\n", + tag, retry + 1); retry += 1; } else { retry = 0; break; } } - if (retry == FRAME_DATA_READ_RETRY) { - logError(1, "%s requestFrame: ERROR %02X\n", tag, ERROR_TIMEOUT); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); return ERROR_TIMEOUT; } - u8ToU16_le(&readEvent[1], &answer); if (answer == type) return OK; - logError(1, "%s The event found has a different type of Frame data ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + + logError(1, "%s The event found has a different type of ", tag); + logError(1, "Frame data:%02X\n", ERROR_DIFF_COMP_TYPE); return ERROR_DIFF_COMP_TYPE; } -int readFrameDataHeader(u16 type, DataHeader *header) -{ +int readFrameDataHeader(u16 type, struct DataHeader *header) +{ u16 offset = ADDR_FRAMEBUFFER_DATA; u16 answer; u8 data[FRAME_DATA_HEADER]; - if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, FRAME_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { - logError(1, "%s readFrameDataHeader: ERROR %02X\n", tag, ERROR_I2C_R); + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, + FRAME_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } logError(0, "%s Read Data Header done!\n", tag); if (data[0] != FRAME_HEADER_SIGNATURE) { - logError(1, "%s readFrameDataHeader: ERROR %02X The Header Signature was wrong! %02X != %02X\n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + logError(1, "%s %s %02X Wrong Header Signature !%02X != %02X\n", + tag, __func__, ERROR_WRONG_COMP_SIGN, data[0], + HEADER_SIGNATURE); return ERROR_WRONG_COMP_SIGN; } u8ToU16_le(&data[1], &answer); if (answer != type) { - logError(1, "%s readFrameDataHeader: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); return ERROR_DIFF_COMP_TYPE; } @@ -434,28 +352,34 @@ int readFrameDataHeader(u16 type, DataHeader *header) header->sense_node = (int)data[5]; return OK; - } -int getMSFrame2(u16 type, MutualSenseFrame *frame) +int getMSFrame2(u16 type, struct MutualSenseFrame *frame) { u16 offset = ADDR_FRAMEBUFFER_DATA+FRAME_DATA_HEADER; int size, ret; - if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER || type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { - logError(1, "%s getMSFrame: Choose a MS type of frame data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + frame->node_data = NULL; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER + || type == MS_TOUCH_ULTRA_LOW_POWER + || type == MS_KEY)) { + logError(1, "%s %s:Choose a MS type of frame data ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } ret = requestFrame(type); if (ret < 0) { - logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + logError(1, "%s readMutualSenseCompensation:ERROR %02X\n", + tag, ERROR_REQU_COMP_DATA); return (ret | ERROR_REQU_COMP_DATA); } ret = readFrameDataHeader(type, &(frame->header)); if (ret < 0) { - logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + logError(1, "%s readMutualSenseCompensationData:ERROR %02X\n", + tag, ERROR_COMP_DATA_HEADER); return (ret | ERROR_COMP_DATA_HEADER); } @@ -463,62 +387,74 @@ int getMSFrame2(u16 type, MutualSenseFrame *frame) case MS_TOUCH_ACTIVE: case MS_TOUCH_LOW_POWER: case MS_TOUCH_ULTRA_LOW_POWER: - size = frame->header.force_node*frame->header.sense_node; + size = frame->header.force_node * frame->header.sense_node; break; case MS_KEY: + //or use directly the number in the ftsChip if (frame->header.force_node > frame->header.sense_node) - /* or use directly the number in the ftsChip */ size = frame->header.force_node; else size = frame->header.sense_node; - frame->header.force_node = 1; + frame->header.force_node = 1; frame->header.sense_node = size; break; default: - logError(1, "%s getMSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - frame->node_data = (short *)kmalloc(size*sizeof(short), GFP_KERNEL); + frame->node_data = (short *)kmalloc_array(size, + sizeof(short), GFP_KERNEL); if (frame->node_data == NULL) { - logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } ret = getFrameData(offset, size*BYTES_PER_NODE, &(frame->node_data)); if (ret < OK) { - logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(frame->node_data); return (ret | ERROR_GET_FRAME_DATA); } - /* if you want to access one node i,j, you should compute the offset like: offset = i*columns + j = > frame[i, j] */ - + // if you want to access one node i,j, + //you should compute the offset like: + //offset = i * columns + j = > frame[i, j] logError(0, "%s Frame acquired!\n", tag); frame->node_data_size = size; - return size; /* return the number of data put inside frame */ + return size;//return the number of data put inside frame } -int getSSFrame2(u16 type, SelfSenseFrame *frame) +int getSSFrame2(u16 type, struct SelfSenseFrame *frame) { u16 offset = ADDR_FRAMEBUFFER_DATA + FRAME_DATA_HEADER; int size, ret; short *temp = NULL; - if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER || type == SS_PROXIMITY)) { - logError(1, "%s getSSFrame: Choose a SS type of frame data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + frame->force_data = NULL; + frame->sense_data = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER + || type == SS_PROXIMITY)) { + logError(1, "%s %s:Choose a SS type of frame data ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } ret = requestFrame(type); if (ret < 0) { - logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); return (ret | ERROR_REQU_COMP_DATA); } ret = readFrameDataHeader(type, &(frame->header)); if (ret < 0) { - logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); return (ret | ERROR_COMP_DATA_HEADER); } @@ -526,44 +462,57 @@ int getSSFrame2(u16 type, SelfSenseFrame *frame) case SS_TOUCH: case SS_HOVER: case SS_PROXIMITY: - size = frame->header.force_node+frame->header.sense_node; + size = frame->header.force_node + frame->header.sense_node; break; default: - logError(1, "%s getSSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - temp = (short *)kmalloc(size*sizeof(short), GFP_KERNEL); + + temp = (short *)kmalloc_array(size, sizeof(short), GFP_KERNEL); if (temp == NULL) { - logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: temp ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } ret = getFrameData(offset, size*BYTES_PER_NODE, &temp); if (ret < OK) { - logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(temp); return (ret | ERROR_GET_FRAME_DATA); } - frame->force_data = (short *)kmalloc(frame->header.force_node*sizeof(short), GFP_KERNEL); + frame->force_data = (short *)kmalloc_array(frame->header.force_node, + sizeof(short), GFP_KERNEL); if (frame->force_data == NULL) { - logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: frame->force_data ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + kfree(temp); return ERROR_ALLOC; } - memcpy(frame->force_data, temp, frame->header.force_node*sizeof(short)); + memcpy(frame->force_data, temp, + frame->header.force_node * sizeof(short)); - frame->sense_data = (short *)kmalloc(frame->header.sense_node*sizeof(short), GFP_KERNEL); + frame->sense_data = (short *)kmalloc_array(frame->header.sense_node, + sizeof(short), GFP_KERNEL); if (frame->sense_data == NULL) { - logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: frame->sense_data ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + kfree(temp); + kfree(frame->force_data); return ERROR_ALLOC; } - memcpy(frame->sense_data, &temp[frame->header.force_node], frame->header.sense_node*sizeof(short)); + memcpy(frame->sense_data, &temp[frame->header.force_node], + frame->header.sense_node * sizeof(short)); logError(0, "%s Frame acquired!\n", tag); kfree(temp); - return size; /* return the number of data put inside frame */ - + return size; //return the number of data put inside frame } diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.h b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h index 89f4e5080a53c593552aeffccae4f0df866bc85e..8df80ef1dac9874e93480de161cecde5ff1ed7d1 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsFrame.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h @@ -1,49 +1,75 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS functions for getting frames * -* * -************************************************************************** -************************************************************************** +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + */ -*/ +#ifndef __FTS_FRAME_H +#define __FTS_FRAME_H #include "ftsSoftware.h" -/* Number of data bytes for each node */ -#define BYTES_PER_NODE 2 -#define OFFSET_LENGTH 2 -#define FRAME_DATA_HEADER 8 -#define FRAME_HEADER_SIGNATURE 0xB5 -#define FRAME_DATA_READ_RETRY 2 +//Number of data bytes for each node +#define BYTES_PER_NODE 2 +#define OFFSET_LENGTH 2 +#define FRAME_DATA_HEADER 8 +#define FRAME_HEADER_SIGNATURE 0xB5 +#define FRAME_DATA_READ_RETRY 2 -typedef struct { - DataHeader header; +struct MutualSenseFrame { + struct DataHeader header; short *node_data; int node_data_size; -} MutualSenseFrame; +}; -typedef struct { - DataHeader header; +struct SelfSenseFrame { + struct DataHeader header; short *force_data; short *sense_data; -} SelfSenseFrame; +}; -/* int getOffsetFrame(u16 address, u16 *offset); */ +int getOffsetFrame(u16 address, u16 *offset); int getChannelsLength(void); int getFrameData(u16 address, int size, short **frame); -/* int getMSFrame(u16 type, short **frame, int keep_first_row); */ -/* int getMSKeyFrame(u16 type, short **frame); */ -/* int getSSFrame(u16 type, short **frame); */ -/* int getNmsFrame(u16 type, short ***frames, int * sizes, int keep_first_row, int fs, int n); */ +int getMSFrame(u16 type, struct MutualSenseFrame *frame, int keep_first_row); +//int getMSKeyFrame(u16 type, short **frame); +//int getSSFrame(u16 type, short **frame); +//int getNmsFrame(u16 type, short ***frames, int * sizes, +//int keep_first_row, int fs, int n); int getSenseLen(void); int getForceLen(void); int requestFrame(u16 type); -int readFrameDataHeader(u16 type, DataHeader *header); -int getMSFrame2(u16 type, MutualSenseFrame *frame); -int getSSFrame2(u16 type, SelfSenseFrame *frame); +int readFrameDataHeader(u16 type, struct DataHeader *header); +int getMSFrame2(u16 type, struct MutualSenseFrame *frame); +int getSSFrame2(u16 type, struct SelfSenseFrame *frame); + +#endif + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.c b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c index ee97a417d4cb02f8d401ef5813c7fb99a58180ec..77075b4be2a5c1dadf588c4405796dc12566d560 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsGesture.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c @@ -1,17 +1,36 @@ /* - -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS Gesture Utilities * -* * -************************************************************************** -************************************************************************** - -*/ + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities * + * * + ************************************************************************** + ************************************************************************** + */ #include "ftsSoftware.h" #include "ftsError.h" @@ -19,145 +38,236 @@ #include "ftsIO.h" #include "ftsTool.h" + static char tag[8] = "[ FTS ]\0"; static u8 gesture_mask[GESTURE_MASK_SIZE] = { 0 }; static u8 custom_gestures[GESTURE_CUSTOM_NUMBER][GESTURE_CUSTOM_POINTS]; static u8 custom_gesture_index[GESTURE_CUSTOM_NUMBER] = { 0 }; +static int refreshGestureMask; + +u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX] = {0}; +u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX] = {0}; +int gesture_coords_reported = ERROR_OP_NOT_ALLOW; +struct mutex gestureMask_mutex; + + +int updateGestureMask(u8 *mask, int size, int en) +{ + u8 temp; + int i; + + if (mask == NULL) { + logError(1, "%s %s: Mask NULL! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s:Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + + if (en == FEAT_ENABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, "%s %s:setting gesture mask to enable..\n", + tag, __func__); + if (mask != NULL) { + for (i = 0; i < size; i++) { + //back up the gesture enabled + gesture_mask[i] = gesture_mask[i] | mask[i]; + } + } + refreshGestureMask = 1; + logError(0, "%s %s:gesture mask to enable SET!\n", + tag, __func__); + mutex_unlock(&gestureMask_mutex); + return OK; + } + if (en == FEAT_DISABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, "%s %s:setting gesture ", tag, __func__); + logError(0, "mask to disable...\n"); + for (i = 0; i < size; i++) { + // enabled XOR disabled + temp = gesture_mask[i] ^ mask[i]; + gesture_mask[i] = temp & gesture_mask[i]; + } + logError(0, "%s %s:gesture mask to disable SET!\n", + tag, __func__); + refreshGestureMask = 1; + mutex_unlock(&gestureMask_mutex); + return OK; + } + logError(1, "%s%s:Enable parameter Invalid%d!=%d or%d%:08X", + tag, __func__, FEAT_DISABLE, + FEAT_ENABLE, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + +} int enableGesture(u8 *mask, int size) { - u8 cmd[size+2]; - u8 readData[FIFO_EVENT_SIZE] = {0}; + u8 cmd[GESTURE_MASK_SIZE + 2]; + u8 readData[FIFO_EVENT_SIZE]; int i, res; - int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_ENABLE}; + int event_to_search[4] = { EVENTID_GESTURE, + EVENT_TYPE_ENB, 0x00, GESTURE_ENABLE }; logError(0, "%s Trying to enable gesture...\n", tag); cmd[0] = FTS_CMD_GESTURE_CMD; cmd[1] = GESTURE_ENABLE; - if (size <= GESTURE_MASK_SIZE) { + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s: Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + + mutex_lock(&gestureMask_mutex); if (mask != NULL) { for (i = 0; i < size; i++) { cmd[i + 2] = mask[i]; - gesture_mask[i] = gesture_mask[i]|mask[i]; - /* back up of the gesture enabled */ + //back up of the gesture enabled + gesture_mask[i] = gesture_mask[i] | mask[i]; } while (i < GESTURE_MASK_SIZE) { cmd[i + 2] = gesture_mask[i]; i++; } } else { - for (i = 0; i < GESTURE_MASK_SIZE; i++) { + for (i = 0; i < GESTURE_MASK_SIZE; i++) cmd[i + 2] = gesture_mask[i]; - } } res = fts_writeFwCmd(cmd, GESTURE_MASK_SIZE + 2); if (res < OK) { - logError(1, "%s enableGesture: ERROR %08X\n", tag, res); - return res; + logError(1, "%s %s: ERROR %08X\n", tag, __func__, res); + goto END; } - res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + res = pollForEvent(event_to_search, 4, + readData, GENERAL_TIMEOUT); if (res < OK) { - logError(1, "%s enableGesture: pollForEvent ERROR %08X\n", tag, res); - return res; + logError(1, "%s %s: pollForEvent ERROR %08X\n", + tag, __func__, res); + goto END; } if (readData[4] != 0x00) { - logError(1, "%s enableGesture: ERROR %08X\n", tag, ERROR_GESTURE_ENABLE_FAIL); - return ERROR_GESTURE_ENABLE_FAIL; + logError(1, "%s %s: ERROR %08X\n", + tag, __func__, ERROR_GESTURE_ENABLE_FAIL); + res = ERROR_GESTURE_ENABLE_FAIL; + goto END; } - logError(0, "%s enableGesture DONE!\n", tag); - return OK; - } else { - logError(1, "%s enableGesture: Size not valid! %d > %d ERROR %08X\n", tag, size, GESTURE_MASK_SIZE); - return ERROR_OP_NOT_ALLOW; - } + logError(0, "%s %s: DONE!\n", tag, __func__); + res = OK; +END: + mutex_unlock(&gestureMask_mutex); + return res; } + int disableGesture(u8 *mask, int size) { - u8 cmd[2+GESTURE_MASK_SIZE]; - u8 readData[FIFO_EVENT_SIZE] = {0}; + u8 cmd[2 + GESTURE_MASK_SIZE]; + u8 readData[FIFO_EVENT_SIZE]; u8 temp; int i, res; - int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_DISABLE }; + int event_to_search[4] = { EVENTID_GESTURE, + EVENT_TYPE_ENB, 0x00, GESTURE_DISABLE }; logError(0, "%s Trying to disable gesture...\n", tag); cmd[0] = FTS_CMD_GESTURE_CMD; cmd[1] = GESTURE_DISABLE; - if (size <= GESTURE_MASK_SIZE) { + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s: Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + mutex_lock(&gestureMask_mutex); if (mask != NULL) { for (i = 0; i < size; i++) { cmd[i + 2] = mask[i]; + // enabled XOR disabled temp = gesture_mask[i] ^ mask[i]; - /* enabled XOR disabled */ gesture_mask[i] = temp & gesture_mask[i]; - /* disable the gestures that were enabled */ } while (i < GESTURE_MASK_SIZE) { - cmd[i + 2] = gesture_mask[i]; - /* disable all the other gesture not specified */ - gesture_mask[i] = 0x00; + //cmd[i + 2] = gesture_mask[i]; + //gesture_mask[i] = 0x00; + + cmd[i + 2] = 0x00; + //leave untouched the gestures not specified + i++; } } else { for (i = 0; i < GESTURE_MASK_SIZE; i++) { - cmd[i + 2] = gesture_mask[i]; + //cmd[i + 2] = gesture_mask[i]; + cmd[i + 2] = 0xFF; } } res = fts_writeFwCmd(cmd, 2 + GESTURE_MASK_SIZE); if (res < OK) { - logError(1, "%s disableGesture: ERROR %08X\n", tag, res); - return res; + logError(1, "%s %s:ERROR %08X\n", tag, __func__, res); + goto END; } res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); if (res < OK) { - logError(1, "%s disableGesture: pollForEvent ERROR %08X\n", tag, res); - return res; + logError(1, "%s %s: pollForEvent ERROR %08X\n", + tag, __func__, res); + goto END; } if (readData[4] != 0x00) { - logError(1, "%s disableGesture: ERROR %08X\n", tag, ERROR_GESTURE_ENABLE_FAIL); - return ERROR_GESTURE_ENABLE_FAIL; + logError(1, "%s %s:ERROR %08X\n", + tag, __func__, ERROR_GESTURE_ENABLE_FAIL); + res = ERROR_GESTURE_ENABLE_FAIL; + goto END; } - logError(0, "%s disableGesture DONE!\n", tag); - return OK; - } else { - logError(1, "%s disableGesture: Size not valid! %d > %d ERROR %08X\n", tag, size, GESTURE_MASK_SIZE); - return ERROR_OP_NOT_ALLOW; - } + logError(0, "%s %s: DONE!\n", tag, __func__); + res = OK; +END: + mutex_unlock(&gestureMask_mutex); + return res; + } int startAddCustomGesture(u8 gestureID) { - u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_START_ADD, gestureID }; + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_START_ADD, gestureID }; int res; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_START_ADD }; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_START_ADD }; res = fts_writeFwCmd(cmd, 3); if (res < OK) { - logError(1, "%s startAddCustomGesture: Impossible to start adding custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + logError(1, + "%s%s:Impossible to start adding custom gesture ID:%02X %08X\n", + tag, __func__, gestureID, res); return res; } res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); if (res < OK) { - logError(1, "%s startAddCustomGesture: start add event not found! ERROR %08X\n", tag, res); + logError(1, "%s %s:start add event not found! ERROR %08X\n", + tag, __func__, res); return res; } - - if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ - logError(1, "%s startAddCustomGesture: start add event status not OK! ERROR %08X\n", tag, readData[4]); + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:start add event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); return ERROR_GESTURE_START_ADD; } @@ -166,79 +276,93 @@ int startAddCustomGesture(u8 gestureID) int finishAddCustomGesture(u8 gestureID) { - u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_FINISH_ADD, gestureID }; + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, + GESTURE_FINISH_ADD, gestureID }; int res; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_FINISH_ADD }; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_FINISH_ADD }; res = fts_writeFwCmd(cmd, 3); if (res < OK) { - logError(1, "%s finishAddCustomGesture: Impossible to finish adding custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + logError(1, + "%s%s:Impossible to finish adding custom gestureID:%02X %08X\n", + tag, __func__, gestureID, res); return res; } res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); if (res < OK) { - logError(1, "%s finishAddCustomGesture: finish add event not found! ERROR %08X\n", tag, res); + logError(1, "%s %s: finish add event not found! ERROR %08X\n", + tag, __func__, res); return res; } - - if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ - logError(1, "%s finishAddCustomGesture: finish add event status not OK! ERROR %08X\n", tag, readData[4]); + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, + "%s %s:finish add event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); return ERROR_GESTURE_FINISH_ADD; } return OK; - } int loadCustomGesture(u8 *template, u8 gestureID) { - int res, i; + int res, i, wheel; int remaining = GESTURE_CUSTOM_POINTS; int toWrite, offset = 0; u8 cmd[TEMPLATE_CHUNK + 5]; - int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_DATA_ADD }; - u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_DATA_ADD }; + u8 readData[FIFO_EVENT_SIZE]; logError(0, "%s Starting adding custom gesture procedure...\n", tag); res = startAddCustomGesture(gestureID); if (res < OK) { - logError(1, "%s loadCustomGesture: unable to start adding procedure! ERROR %08X\n", tag, res); + logError(1, "%s %s:unable to start adding procedure %08X\n", + tag, __func__, res); return res; } cmd[0] = FTS_CMD_GESTURE_CMD; cmd[1] = GESTURE_DATA_ADD; cmd[2] = gestureID; + wheel = 0; while (remaining > 0) { - if (remaining > TEMPLATE_CHUNK) { + if (remaining > TEMPLATE_CHUNK) toWrite = TEMPLATE_CHUNK; - } else { + else toWrite = remaining; - } cmd[3] = toWrite; cmd[4] = offset; - for (i = 0; i < toWrite; i++) { - cmd[i + 5] = template[i]; - } + for (i = 0; i < toWrite; i++) + cmd[i + 5] = template[wheel++]; res = fts_writeFwCmd(cmd, toWrite + 5); if (res < OK) { - logError(1, "%s loadCustomGesture: unable to start adding procedure! ERROR %08X\n", tag, res); + logError(1, "%s %s:unable to start ", tag, __func__); + logError(1, "adding procedure %08X\n", res); return res; } - res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + res = pollForEvent(event_to_search, + 4, + readData, + GENERAL_TIMEOUT); if (res < OK) { - logError(1, "%s loadCustomGesture: add event not found! ERROR %08X\n", tag, res); + logError(1, "%s %s: add event not found! ERROR %08X\n", + tag, __func__, res); return res; } - - if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ - logError(1, "%s loadCustomGesture: add event status not OK! ERROR %08X\n", tag, readData[4]); + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:add event status not OK! ", + tag, __func__); + logError(1, "ERROR %08X\n", readData[4]); return ERROR_GESTURE_DATA_ADD; } @@ -248,7 +372,9 @@ int loadCustomGesture(u8 *template, u8 gestureID) res = finishAddCustomGesture(gestureID); if (res < OK) { - logError(1, "%s loadCustomGesture: unable to finish adding procedure! ERROR %08X\n", tag, res); + logError(1, "%s %s:unable to finish adding procedure! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); return res; } @@ -257,6 +383,7 @@ int loadCustomGesture(u8 *template, u8 gestureID) } + int reloadCustomGesture(void) { int res, i; @@ -265,9 +392,13 @@ int reloadCustomGesture(void) for (i = 0; i < GESTURE_CUSTOM_NUMBER; i++) { if (custom_gesture_index[i] == 1) { - res = loadCustomGesture(custom_gestures[i], GESTURE_CUSTOM_OFFSET+i); + res = loadCustomGesture(custom_gestures[i], + GESTURE_CUSTOM_OFFSET + i); if (res < OK) { - logError(1, "%s reloadCustomGesture: Impossible to load custom gesture ID = %02X! ERROR %08X\n", tag, GESTURE_CUSTOM_OFFSET + i, res); + logError(1, "%s %s:Impossible load gesture ", + tag, __func__); + logError(1, "D:%02X %08X\n", + GESTURE_CUSTOM_OFFSET + i, res); return res; } } @@ -285,34 +416,48 @@ int enterGestureMode(int reload) res = fts_disableInterrupt(); if (res < OK) { - logError(1, "%s enterGestureMode: ERROR %08X\n", tag, res|ERROR_DISABLE_INTER); + logError(1, "%s %s: ERROR %08X\n", + tag, __func__, res | ERROR_DISABLE_INTER); return res | ERROR_DISABLE_INTER; } - if (reload == 1) { - - res = reloadCustomGesture(); - if (res < OK) { - logError(1, "%s enterGestureMode: impossible reload custom gesture! ERROR %08X\n", tag, res); - goto END; + if (reload == 1 || refreshGestureMask == 1) { + if (reload == 1) { + res = reloadCustomGesture(); + if (res < OK) { + logError(1, "%s %s:impossible reload ", + tag, __func__); + logError(1, "custom gesture %08X\n", res); + goto END; + } } + /** + * mandatory steps to set the correct gesture + * mask defined by the user + */ res = disableGesture(NULL, 0); if (res < OK) { - logError(1, "%s enterGestureMode: disableGesture ERROR %08X\n", tag, res); + logError(1, "%s %s:disableGesture ERROR %08X\n", + tag, res); goto END; } res = enableGesture(NULL, 0); if (res < OK) { - logError(1, "%s enterGestureMode: enableGesture ERROR %08X\n", tag, res); + logError(1, "%s %s:enableGesture ERROR %08X\n", + tag, __func__, res); goto END; } + + refreshGestureMask = 0; + /**************************************************/ } res = fts_writeFwCmd(&cmd, 1); if (res < OK) { - logError(1, "%s enterGestureMode: enter gesture mode ERROR %08X\n", tag, res); + logError(1, "%s %s:enter gesture mode ERROR %08X\n", + tag, __func__, res); goto END; } @@ -320,7 +465,8 @@ int enterGestureMode(int reload) END: ret = fts_enableInterrupt(); if (ret < OK) { - logError(1, "%s enterGestureMode: fts_enableInterrupt ERROR %08X\n", tag, res | ERROR_ENABLE_INTER); + logError(1, "%s %s:fts_enableInterrupt ERROR %08X\n", + tag, __func__, res | ERROR_ENABLE_INTER); res |= ret | ERROR_ENABLE_INTER; } @@ -334,18 +480,24 @@ int addCustomGesture(u8 *data, int size, u8 gestureID) index = gestureID - GESTURE_CUSTOM_OFFSET; logError(0, "%s Starting Custom Gesture Adding procedure...\n", tag); - if (size != GESTURE_CUSTOM_POINTS && gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 && gestureID != GES_ID_CUST4 && gestureID && GES_ID_CUST5) { - logError(1, "%s addCustomGesture: Invalid size (%d) or Custom GestureID (%02X)! ERROR %08X\n", tag, size, gestureID, ERROR_OP_NOT_ALLOW); + if (size != GESTURE_CUSTOM_POINTS || (gestureID != GES_ID_CUST1 + && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 + && gestureID != GES_ID_CUST4 && gestureID != GES_ID_CUST5)) { + logError(1, "%s %s:Invalid size(%d) or Custom GestureID ", + tag, __func__, size); + logError(1, "(%02X)!ERROR%08X\n", + size, gestureID, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - for (i = 0; i < GESTURE_CUSTOM_POINTS; i++) { + for (i = 0; i < GESTURE_CUSTOM_POINTS; i++) custom_gestures[index][i] = data[i]; - } res = loadCustomGesture(custom_gestures[index], gestureID); if (res < OK) { - logError(1, "%s addCustomGesture: impossible to load the custom gesture! ERROR %08X\n", tag, res); + logError(1, "%s %s:impossible to load the custom gesture! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); return res; } @@ -358,31 +510,40 @@ int removeCustomGesture(u8 gestureID) { int res, index; u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GETURE_REMOVE_CUSTOM, gestureID }; - int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GETURE_REMOVE_CUSTOM }; - u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GETURE_REMOVE_CUSTOM }; + u8 readData[FIFO_EVENT_SIZE]; index = gestureID - GESTURE_CUSTOM_OFFSET; logError(0, "%s Starting Custom Gesture Removing procedure...\n", tag); - if (gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 && gestureID != GES_ID_CUST4 && gestureID && GES_ID_CUST5) { - logError(1, "%s removeCustomGesture: Invalid size (%d) or Custom GestureID (%02X)! ERROR %08X\n", tag, gestureID, ERROR_OP_NOT_ALLOW); + if (gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && + gestureID != GES_ID_CUST3 && gestureID != + GES_ID_CUST4 && gestureID != GES_ID_CUST5) { + logError(1, "%s %s:Invalid Custom GestureID (%02X)! ", + tag, __func__, gestureID); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - - res = fts_writeFwCmd(cmd, 3);/* when a gesture is removed, it is also disabled automatically */ + //when a gesture is removed, it is also disabled automatically + res = fts_writeFwCmd(cmd, 3); if (res < OK) { - logError(1, "%s removeCustomGesture: Impossible to remove custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + logError(1, "%s %s:Impossible to remove custom ", + tag, __func__); + logError(1, "%gesture ID:%02X %08X\n", gestureID, res); return res; } res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); if (res < OK) { - logError(1, "%s removeCustomGesture: remove event not found! ERROR %08X\n", tag, res); + logError(1, "%s %s:remove event not found! ERROR %08X\n", + tag, __func__, res); return res; } - - if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ - logError(1, "%s removeCustomGesture: remove event status not OK! ERROR %08X\n", tag, readData[4]); + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:remove event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); return ERROR_GESTURE_REMOVE; } @@ -391,3 +552,99 @@ int removeCustomGesture(u8 gestureID) return OK; } + +int isAnyGestureActive(void) +{ + int res = 0; + /*-1 because in any case the last gesture mask byte will*/ + /*be evaluated with the following if*/ + while (res < (GESTURE_MASK_SIZE - 1) && gesture_mask[res] == 0) + res++; + + if (gesture_mask[res] == 0) { + logError(0, "%s %s: All Gestures Disabled!\n", tag, __func__); + return FEAT_DISABLE; + } + + logError(0, "%s %s:Active Gestures Found! gesture_mask[%d] = %02X!\n", + tag, __func__, res, gesture_mask[res]); + return FEAT_ENABLE; +} + +int gestureIDtoGestureMask(u8 id, u8 *mask) +{ + logError(0, "%s %s: Index = %d Position = %d!\n", + tag, __func__, ((int)((id) / 8)), (id % 8)); + mask[((int)((id) / 8))] |= 0x01 << (id % 8); + return OK; +} + + +int readGestureCoords(u8 *event) +{ + int i = 0; + u8 rCmd[3] = {FTS_CMD_FRAMEBUFFER_R, 0x00, 0x00 }; + int res; + unsigned char val[GESTURE_COORDS_REPORT_MAX * 4 + 1]; + + //the max coordinates to read are GESTURE_COORDS_REPORT_MAX*4 + //(because each coordinate is a short(*2) and we have x and y) + //+ dummy byte + if (event[0] != EVENTID_GESTURE + || event[1] != EVENT_TYPE_GESTURE_DTC2) { + + logError(1, "%s %s:The event passsed as argument is invalid! ", + tag, __func__); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + rCmd[1] = event[4]; // Offset address L + rCmd[2] = event[3]; // Offset address H + //number of coords reported L + gesture_coords_reported = event[6]; + //number of coords reported H + gesture_coords_reported = (gesture_coords_reported << 8) | event[5]; + if (gesture_coords_reported > GESTURE_COORDS_REPORT_MAX) { + logError(1, "%s%s:FW reported more than:%d points ", + tag, __func__, gesture_coords_reported); + logError(1, "for gestures!\n"); + logError(1, " Decreasing to %d\n", GESTURE_COORDS_REPORT_MAX); + gesture_coords_reported = GESTURE_COORDS_REPORT_MAX; + } + + logError(1, "%s %s: Offset: %02X %02X points = %d\n", tag, + __func__, rCmd[1], rCmd[2], gesture_coords_reported); + res = fts_readCmd(rCmd, 3, (unsigned char *)val, + 1 + (gesture_coords_reported * 2)); + if (res < OK) { + logError(1, "%s %s: Cannot read the coordinates! ERROR %08X\n", + tag, __func__, res); + gesture_coords_reported = ERROR_OP_NOT_ALLOW; + return res; + } + //all the points of the gesture are stored in val + for (i = 0; i < gesture_coords_reported; i++) { + //ignore first byte data because it is a dummy byte + gesture_coordinates_x[i] = (((u16) val[i * 2 + 1 + 1]) + & 0x0F) << 8 | (((u16) val[i * 2 + 1]) & 0xFF); + gesture_coordinates_y[i] = + (((u16)val[gesture_coords_reported * 2 + + i * 2 + 1 + 1]) & 0x0F) << 8 + | (((u16)val[gesture_coords_reported * 2 + + i * 2 + 1]) & 0xFF); + } + + logError(1, "%s %s: Reading Gesture Coordinates DONE!\n", + tag, __func__); + return OK; +} + +int getGestureCoords(u16 *x, u16 *y) +{ + x = gesture_coordinates_x; + y = gesture_coordinates_y; + logError(1, "%s %s:Number of gesture coordinates returned = %d\n", + tag, __func__, gesture_coords_reported); + return gesture_coords_reported; +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.h b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h index a9c3e3c0557343a42e86b84592442b3a3d3749bc..3cc86236ad726d217d0a8366fc00497623cd631f 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsGesture.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h @@ -1,70 +1,110 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS Gesture Utilities * -* * -************************************************************************** -************************************************************************** - -*/ - -#define GESTURE_MASK_SIZE 8 - -#define GESTURE_CUSTOM_POINTS (30*2) -/* for each custom gesture should be provided 30 points (each point is a couple of x,y) */ -#define GESTURE_CUSTOM_NUMBER 5 /* fw support up to 5 custom gestures */ - -#define TEMPLATE_CHUNK (10*2) -/* number of points to transfer with each I2C transaction */ - -/* Gesture IDs */ -#define GES_ID_DBLTAP 0x01 /* Double Tap */ -#define GES_ID_O 0x02 /* 'O' */ -#define GES_ID_C 0x03 /* 'C' */ -#define GES_ID_M 0x04 /* 'M' */ -#define GES_ID_W 0x05 /* 'W' */ -#define GES_ID_E 0x06 /* 'e' */ -#define GES_ID_HFLIP_L2R 0x07 /* Left to right line */ -#define GES_ID_HFLIP_R2L 0x08 /* Right to left line */ -#define GES_ID_VFLIP_T2D 0x09 /* Top to bottom line */ -#define GES_ID_VFLIP_D2T 0x0A /* Bottom to Top line */ -#define GES_ID_L 0x0B /* 'L' */ -#define GES_ID_F 0x0C /* 'F' */ -#define GES_ID_V 0x0D /* 'V' */ -#define GES_ID_AT 0x0E /* '@' */ -#define GES_ID_S 0x0F /* 'S' */ -#define GES_ID_Z 0x10 /* 'Z' */ -#define GES_ID_CUST1 0x11 /* Custom gesture 1 */ -#define GES_ID_CUST2 0x12 /* Custom gesture 2 */ -#define GES_ID_CUST3 0x13 /* Custom gesture 3 */ -#define GES_ID_CUST4 0x14 /* Custom gesture 4 */ -#define GES_ID_CUST5 0x15 /* Custom gesture 5 */ -#define GES_ID_LEFTBRACE 0x20 /* '<' */ -#define GES_ID_RIGHTBRACE 0x21 /* '>' */ - -#define GESTURE_CUSTOM_OFFSET GES_ID_CUST1 - -/* Command sub-type */ -#define GESTURE_ENABLE 0x01 -#define GESTURE_DISABLE 0x02 -#define GESTURE_ENB_CHECK 0x03 -#define GESTURE_START_ADD 0x10 -#define GESTURE_DATA_ADD 0x11 -#define GESTURE_FINISH_ADD 0x12 -#define GETURE_REMOVE_CUSTOM 0x13 -#define GESTURE_CHECK_CUSTOM 0x14 - -/* Event sub-type */ -#define EVENT_TYPE_ENB 0x04 -#define EVENT_TYPE_CHECK_ENB 0x03 -#define EVENT_TYPE_GESTURE_DTC1 0x01 -#define EVENT_TYPE_GESTURE_DTC2 0x02 +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities * + * * + ************************************************************************** + ************************************************************************** + */ +#ifndef _FTS_GESTURE_H_ +#define _FTS_GESTURE_H_ + +#include "ftsHardware.h" + +#define GESTURE_MASK_SIZE 8 + +//max number of gestures coordinates reported +#define GESTURE_COORDS_REPORT_MAX 32 + +// for each custom gesture should be provided 30 +//points (each point is a couple of x,y) +#define GESTURE_CUSTOM_POINTS (30 * 2) + +//fw support up to 5 custom gestures +#define GESTURE_CUSTOM_NUMBER 5 + +#ifdef FTM3 + +//number of points to transfer with each I2C transaction +#define TEMPLATE_CHUNK (10 * 2) +#else +//number of points to transfer with each I2C transaction +#define TEMPLATE_CHUNK (5 * 2) +#endif + + +//Gesture IDs +#define GES_ID_UNKNOWN 0x00 //no meaningful gesture +#define GES_ID_DBLTAP 0x01 //Double Tap +#define GES_ID_O 0x02 //'O' +#define GES_ID_C 0x03 //'C' +#define GES_ID_M 0x04 //'M' +#define GES_ID_W 0x05 //'W' +#define GES_ID_E 0x06 //'e' +#define GES_ID_HFLIP_L2R 0x07 //Left to right line +#define GES_ID_HFLIP_R2L 0x08 //Right to left line +#define GES_ID_VFLIP_T2D 0x09 //Top to bottom line +#define GES_ID_VFLIP_D2T 0x0A //Bottom to Top line +#define GES_ID_L 0x0B //'L' +#define GES_ID_F 0x0C //'F' +#define GES_ID_V 0x0D //'V' +#define GES_ID_AT 0x0E //'@' +#define GES_ID_S 0x0F //'S' +#define GES_ID_Z 0x10 //'Z' +#define GES_ID_CUST1 0x11 //Custom gesture 1 +#define GES_ID_CUST2 0x12 //Custom gesture 2 +#define GES_ID_CUST3 0x13 //Custom gesture 3 +#define GES_ID_CUST4 0x14 //Custom gesture 4 +#define GES_ID_CUST5 0x15 //Custom gesture 5 +#define GES_ID_LEFTBRACE 0x20 //'<' +#define GES_ID_RIGHTBRACE 0x21 //'>' + +#define GESTURE_CUSTOM_OFFSET GES_ID_CUST1 + +//Command sub-type +#define GESTURE_ENABLE 0x01 +#define GESTURE_DISABLE 0x02 +#define GESTURE_ENB_CHECK 0x03 +#define GESTURE_START_ADD 0x10 +#define GESTURE_DATA_ADD 0x11 +#define GESTURE_FINISH_ADD 0x12 +#define GETURE_REMOVE_CUSTOM 0x13 +#define GESTURE_CHECK_CUSTOM 0x14 + + +//Event sub-type +#define EVENT_TYPE_ENB 0x04 +#define EVENT_TYPE_CHECK_ENB 0x03 +#define EVENT_TYPE_GESTURE_DTC1 0x01 +#define EVENT_TYPE_GESTURE_DTC2 0x02 + +int updateGestureMask(u8 *mask, int size, int en); int disableGesture(u8 *mask, int size); int enableGesture(u8 *mask, int size); int startAddCustomGesture(u8 gestureID); @@ -72,3 +112,11 @@ int finishAddCustomGesture(u8 gestureID); int loadCustomGesture(u8 *template, u8 gestureID); int reloadCustomGesture(void); int enterGestureMode(int reload); +int addCustomGesture(u8 *data, int size, u8 gestureID); +int removeCustomGesture(u8 gestureID); +int isAnyGestureActive(void); +int gestureIDtoGestureMask(u8 id, u8 *mask); +int readGestureCoords(u8 *event); +int getGestureCoords(u16 *x, u16 *y); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsHardware.h b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h index 933059e671b4514d8e6f26c9e09353dee3e33889..f66555f4cd7a01638cb5e901fcfa529858441e67 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsHardware.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h @@ -1,177 +1,212 @@ /* - -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* HW related data * -* * -************************************************************************** -************************************************************************** - -*/ - -#define FTM3_CHIP - -/* DUMMY BYTES DATA */ -#define DUMMY_HW_REG 1 -#define DUMMY_FRAMEBUFFER 1 -#define DUMMY_MEMORY 1 - -/* DIGITAL CHIP INFO */ + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * HW related data ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_HARDWARE_H +#define __FTS_HARDWARE_H + +//DUMMY BYTES DATA +#define DUMMY_HW_REG 1 +#define DUMMY_FRAMEBUFFER 1 +#define DUMMY_MEMORY 1 + +//DIGITAL CHIP INFO #ifdef FTM3_CHIP -#define DCHIP_ID_0 0x39 -#define DCHIP_ID_1 0x6C +#define DCHIP_ID_0 0x39 +#define DCHIP_ID_1 0x6C #else -#define DCHIP_ID_0 0x36 -#define DCHIP_ID_1 0x70 +#define DCHIP_ID_0 0x36 +#define DCHIP_ID_1 0x70 #endif #ifdef FTM3_CHIP -#define DCHIP_ID_ADDR 0x0007 -#define DCHIP_FW_VER_ADDR 0x000A +#define DCHIP_ID_ADDR 0x0007 +#define DCHIP_FW_VER_ADDR 0x000A #else -#define DCHIP_ID_ADDR 0x0004 -#define DCHIP_FW_VER_ADDR 0x000B +#define DCHIP_ID_ADDR 0x0004 +#define DCHIP_FW_VER_ADDR 0x0008 #endif -#define DCHIP_FW_VER_BYTE 2 +#define DCHIP_FW_VER_BYTE 2 -/* CHUNKS */ -#define READ_CHUNK (2*1024) -#define WRITE_CHUNK (2*1024) -#define MEMORY_CHUNK (2*1024) +//CHUNKS +#define READ_CHUNK (2 * 1024) +#define WRITE_CHUNK (2 * 1024) +#define MEMORY_CHUNK (2 * 1024) -/* PROTOCOL INFO */ +//PROTOCOL INFO #ifdef FTM3_CHIP -#define I2C_SAD 0x49 +#define I2C_SAD 0x49 #else -#define I2C_SAD 0x48 +#define I2C_SAD 0x49 #endif -#define I2C_INTERFACE /* comment if the chip use SPI */ -#define ICR_ADDR 0x0024 -#define ICR_SPI_VALUE 0x02 +#define I2C_INTERFACE //comment if the chip use SPI +#define ICR_ADDR 0x0024 +#define ICR_SPI_VALUE 0x02 -/* SYSTEM RESET INFO */ +//SYSTEM RESET INFO #ifdef FTM3_CHIP -#define SYSTEM_RESET_ADDRESS 0x0023 -#define SYSTEM_RESET_VALUE 0x01 +#define SYSTEM_RESET_ADDRESS 0x0023 +#define SYSTEM_RESET_VALUE 0x01 #else -#define SYSTEM_RESET_ADDRESS 0x0028 -#define SYSTEM_RESET_VALUE 0x80 +#define SYSTEM_RESET_ADDRESS 0x0028 +#define SYSTEM_RESET_VALUE 0x80 #endif -/* INTERRUPT INFO */ +//INTERRUPT INFO #ifdef FTM3_CHIP -#define IER_ADDR 0x001C +#define IER_ADDR 0x001C #else -#define IER_ADDR 0x002C +#define IER_ADDR 0x002C #endif -#define IER_ENABLE 0x41 -#define IER_DISABLE 0x00 +#define IER_ENABLE 0x41 +#define IER_DISABLE 0x00 + +//FLASH COMMAND -/* FLASH COMMAND */ +#define FLASH_CMD_UNLOCK 0xF7 -#define FLASH_CMD_UNLOCK 0xF7 #ifdef FTM3_CHIP -#define FLASH_CMD_WRITE_LOWER_64 0xF0 -#define FLASH_CMD_WRITE_UPPER_64 0xF1 -#define FLASH_CMD_BURN 0xF2 -#define FLASH_CMD_ERASE 0xF3 -#define FLASH_CMD_READSTATUS 0xF4 +#define FLASH_CMD_WRITE_LOWER_64 0xF0 +#define FLASH_CMD_WRITE_UPPER_64 0xF1 +#define FLASH_CMD_BURN 0xF2 +#define FLASH_CMD_ERASE 0xF3 +#define FLASH_CMD_READSTATUS 0xF4 #else -#define FLASH_CMD_WRITE_64K 0xF8 -#define FLASH_CMD_READ_REGISTER 0xF9 -#define FLASH_CMD_WRITE_REGISTER 0xFA +#define FLASH_CMD_WRITE_64K 0xF8 +#define FLASH_CMD_READ_REGISTER 0xF9 +#define FLASH_CMD_WRITE_REGISTER 0xFA #endif -/* FLASH UNLOCK PARAMETER */ -#define FLASH_UNLOCK_CODE0 0x74 -#define FLASH_UNLOCK_CODE1 0x45 +//FLASH UNLOCK PARAMETER +#define FLASH_UNLOCK_CODE0 0x74 +#define FLASH_UNLOCK_CODE1 0x45 #ifndef FTM3_CHIP -/* FLASH ERASE and DMA PARAMETER */ -#define FLASH_ERASE_UNLOCK_CODE0 0x72 -#define FLASH_ERASE_UNLOCK_CODE1 0x03 -#define FLASH_ERASE_UNLOCK_CODE2 0x02 -#define FLASH_ERASE_CODE0 0x02 -#define FLASH_ERASE_CODE1 0xC0 -#define FLASH_DMA_CODE0 0x05 -#define FLASH_DMA_CODE1 0xC0 -#define FLASH_DMA_CONFIG 0x06 +//FLASH ERASE and DMA PARAMETER +#define FLASH_ERASE_UNLOCK_CODE0 0x72 +#define FLASH_ERASE_UNLOCK_CODE1 0x03 +#define FLASH_ERASE_UNLOCK_CODE2 0x02 +#define FLASH_ERASE_CODE0 0x02 +#define FLASH_ERASE_CODE1 0xC0 +#define FLASH_DMA_CODE0 0x05 +#define FLASH_DMA_CODE1 0xC0 +#define FLASH_DMA_CONFIG 0x06 +#define FLASH_ERASE_START 0x80 +#define FLASH_NUM_PAGE 64//number of pages +#define FLASH_CX_PAGE_START 61 +#define FLASH_CX_PAGE_END 62 #endif -/* FLASH ADDRESS */ + +//FLASH ADDRESS #ifdef FTM3_CHIP -#define FLASH_ADDR_SWITCH_CMD 0x00010000 -#define FLASH_ADDR_CODE 0x00000000 -#define FLASH_ADDR_CONFIG 0x0001E800 -#define FLASH_ADDR_CX 0x0001F000 +#define FLASH_ADDR_SWITCH_CMD 0x00010000 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0001E800 +#define FLASH_ADDR_CX 0x0001F000 #else -#define ADDR_WARM_BOOT 0x001E -#define WARM_BOOT_VALUE 0x38 -#define FLASH_ADDR_CODE 0x00000000 -#define FLASH_ADDR_CONFIG 0x0000FC00 +#define ADDR_WARM_BOOT 0x001E +#define WARM_BOOT_VALUE 0x38 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0000FC00 #endif -/* CRC ADDR */ + +//CRC ADDR #ifdef FTM3_CHIP -#define ADDR_CRC_BYTE0 0x00 -#define ADDR_CRC_BYTE1 0x86 -#define CRC_MASK 0x02 +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x86 +#define CRC_MASK 0x02 #else -#define ADDR_CRC_BYTE0 0x00 -#define ADDR_CRC_BYTE1 0x74 -#define CRC_MASK 0x03 +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x74 +#define CRC_MASK 0x03 #endif -/* SIZES FW, CODE, CONFIG, MEMH */ +//SIZES FW, CODE, CONFIG, MEMH #ifdef FTM3_CHIP -#define FW_HEADER_SIZE 32 -#define FW_SIZE (int)(128*1024) -#define FW_CODE_SIZE (int)(122*1024) -#define FW_CONFIG_SIZE (int)(2*1024) -#define FW_CX_SIZE (int)(FW_SIZE-FW_CODE_SIZE-FW_CONFIG_SIZE) -#define FW_VER_MEMH_BYTE1 193 -#define FW_VER_MEMH_BYTE0 192 -#define FW_OFF_CONFID_MEMH_BYTE1 2 -#define FW_OFF_CONFID_MEMH_BYTE0 1 -#define FW_BIN_VER_OFFSET 4 -#define FW_BIN_CONFIG_VER_OFFSET (FW_HEADER_SIZE+FW_CODE_SIZE+1) +#define FW_HEADER_SIZE 32 +#define FW_SIZE (int)(128*1024) +#define FW_CODE_SIZE (int)(122*1024) +#define FW_CONFIG_SIZE (int)(2*1024) +#define FW_CX_SIZE (int)(FW_SIZE-FW_CODE_SIZE-FW_CONFIG_SIZE) +#define FW_VER_MEMH_BYTE1 193 +#define FW_VER_MEMH_BYTE0 192 +#define FW_OFF_CONFID_MEMH_BYTE1 2 +#define FW_OFF_CONFID_MEMH_BYTE0 1 +#define FW_BIN_VER_OFFSET 4 +#define FW_BIN_CONFIG_VER_OFFSET (FW_HEADER_SIZE+FW_CODE_SIZE+1) #else -#define FW_HEADER_SIZE 64 -#define FW_HEADER_SIGNATURE 0xAA55AA55 -#define FW_FTB_VER 0x00000001 -#define FW_BYTES_ALIGN 4 -#define FW_BIN_VER_OFFSET 16 -#define FW_BIN_CONFIG_VER_OFFSET 20 +#define FW_HEADER_SIZE 64 +#define FW_HEADER_SIGNATURE 0xAA55AA55 +#define FW_FTB_VER 0x00000001 +#define FW_BYTES_ALIGN 4 +#define FW_BIN_VER_OFFSET 16 +#define FW_BIN_CONFIG_VER_OFFSET 20 #endif -/* FIFO */ -#define FIFO_EVENT_SIZE 8 +//FIFO +#define FIFO_EVENT_SIZE 8 + #ifdef FTM3_CHIP -#define FIFO_DEPTH 32 +#define FIFO_DEPTH 32 #else -#define FIFO_DEPTH 64 +#define FIFO_DEPTH 64 #endif -#define FIFO_CMD_READONE 0x85 -#define FIFO_CMD_READALL 0x86 -#define FIFO_CMD_LAST 0x87 -#define FIFO_CMD_FLUSH 0xA1 +#define FIFO_CMD_READONE 0x85 +#define FIFO_CMD_READALL 0x86 +#define FIFO_CMD_LAST 0x87 +#define FIFO_CMD_FLUSH 0xA1 -/* CONSTANT TOTAL CX */ -#define CX1_WEIGHT 4 -#define CX2_WEIGHT 1 -/* OP CODES FOR MEMORY (based on protocol) */ +//CONSTANT TOTAL CX +#ifdef FTM3_CHIP +#define CX1_WEIGHT 4 +#define CX2_WEIGHT 1 +#else +#define CX1_WEIGHT 8 +#define CX2_WEIGHT 1 +#endif -#define FTS_CMD_HW_REG_R 0xB6 -#define FTS_CMD_HW_REG_W 0xB6 -#define FTS_CMD_FRAMEBUFFER_R 0xD0 -#define FTS_CMD_FRAMEBUFFER_W 0xD0 +//OP CODES FOR MEMORY (based on protocol) + +#define FTS_CMD_HW_REG_R 0xB6 +#define FTS_CMD_HW_REG_W 0xB6 +#define FTS_CMD_FRAMEBUFFER_R 0xD0 +#define FTS_CMD_FRAMEBUFFER_W 0xD0 + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.c b/drivers/input/touchscreen/st/fts_lib/ftsIO.c index 96ca8cfecd8ac5091581e6e1bde9fea904189489..a1f3862d61f4a2036765ec52596202c5a59089a1 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsIO.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.c @@ -1,20 +1,37 @@ /* - -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* I2C/SPI Communication * -* * -************************************************************************** -************************************************************************** - -*/ - -#include "ftsSoftware.h" -#include "ftsCrossCompile.h" + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication ** + * * + ************************************************************************** + ************************************************************************** + * + */ #include #include @@ -40,29 +57,31 @@ #include #include #include -/* #include */ #include #include #include -static struct i2c_client *client; -static u16 I2CSAD; +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" #include "ftsError.h" #include "ftsHardware.h" #include "ftsIO.h" #include "ftsTool.h" static char tag[8] = "[ FTS ]\0"; +static struct i2c_client *client; +static u16 I2CSAD; + int openChannel(struct i2c_client *clt) { client = clt; I2CSAD = clt->addr; - logError(1, "%s openChannel: SAD: %02X\n", tag, I2CSAD); + logError(1, "%s %s: SAD: %02X\n", tag, __func__, I2CSAD); return OK; } -struct device *getDev(void) +struct device *getDev() { if (client != NULL) return &(client->dev); @@ -70,7 +89,7 @@ struct device *getDev(void) return NULL; } -struct i2c_client *getClient(void) +struct i2c_client *getClient() { if (client != NULL) return client; @@ -82,15 +101,15 @@ int fts_readCmd(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) { int ret = -1; int retry = 0; - struct i2c_msg I2CMsg[2]; + struct i2c_msg I2CMsg[2]; - /* write msg */ + //write msg I2CMsg[0].addr = (__u16)I2CSAD; I2CMsg[0].flags = (__u16)0; I2CMsg[0].len = (__u16)cmdLength; I2CMsg[0].buf = (__u8 *)cmd; - /* read msg */ + //read msg I2CMsg[1].addr = (__u16)I2CSAD; I2CMsg[1].flags = I2C_M_RD; I2CMsg[1].len = byteToRead; @@ -100,13 +119,13 @@ int fts_readCmd(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) return ERROR_I2C_O; while (retry < I2C_RETRY && ret < OK) { ret = i2c_transfer(client->adapter, I2CMsg, 2); - if (ret >= OK) - break; retry++; - msleep(I2C_WAIT_BEFORE_RETRY); + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); } if (ret < 0) { - logError(1, "%s fts_readCmd: ERROR %02X\n", tag, ERROR_I2C_R); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } return OK; @@ -116,7 +135,7 @@ int fts_writeCmd(u8 *cmd, int cmdLength) { int ret = -1; int retry = 0; - struct i2c_msg I2CMsg[2]; + struct i2c_msg I2CMsg[1]; I2CMsg[0].addr = (__u16)I2CSAD; I2CMsg[0].flags = (__u16)0; @@ -124,17 +143,16 @@ int fts_writeCmd(u8 *cmd, int cmdLength) I2CMsg[0].buf = (__u8 *)cmd; if (client == NULL) -return ERROR_I2C_O; + return ERROR_I2C_O; while (retry < I2C_RETRY && ret < OK) { ret = i2c_transfer(client->adapter, I2CMsg, 1); - if (ret >= OK) - break; retry++; - msleep(I2C_WAIT_BEFORE_RETRY); - /* logError(1, "%s fts_writeCmd: attempt %d\n", tag, retry); */ + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + //logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); } if (ret < 0) { - logError(1, "%s fts_writeCmd: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } return OK; @@ -145,7 +163,7 @@ int fts_writeFwCmd(u8 *cmd, int cmdLength) int ret = -1; int ret2 = -1; int retry = 0; - struct i2c_msg I2CMsg[2]; + struct i2c_msg I2CMsg[1]; I2CMsg[0].addr = (__u16)I2CSAD; I2CMsg[0].flags = (__u16)0; @@ -153,48 +171,49 @@ int fts_writeFwCmd(u8 *cmd, int cmdLength) I2CMsg[0].buf = (__u8 *)cmd; if (client == NULL) -return ERROR_I2C_O; + return ERROR_I2C_O; while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) { ret = i2c_transfer(client->adapter, I2CMsg, 1); retry++; - if (ret >= 0) { + if (ret >= 0) ret2 = checkEcho(cmd, cmdLength); - break; - } - msleep(I2C_WAIT_BEFORE_RETRY); - /* logError(1, "%s fts_writeCmd: attempt %d\n", tag, retry); */ + if (ret < OK || ret2 < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + //logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); } if (ret < 0) { - logError(1, "%s fts_writeFwCmd: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } if (ret2 < OK) { - logError(1, "%s fts_writeFwCmd: check echo ERROR %02X\n", tag, ret2); - return (ret|ERROR_I2C_W); + logError(1, "%s %s: check echo ERROR %02X\n", + tag, __func__, ret2); + return (ret | ERROR_I2C_W); } return OK; } + int writeReadCmd(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, int readCmdLength, u8 *outBuf, int byteToRead) { int ret = -1; int retry = 0; - struct i2c_msg I2CMsg[3]; - - /* write msg */ + struct i2c_msg I2CMsg[3]; + //write msg I2CMsg[0].addr = (__u16)I2CSAD; I2CMsg[0].flags = (__u16)0; I2CMsg[0].len = (__u16)writeCmdLength; I2CMsg[0].buf = (__u8 *)writeCmd1; - /* write msg */ + //write msg I2CMsg[1].addr = (__u16)I2CSAD; I2CMsg[1].flags = (__u16)0; I2CMsg[1].len = (__u16)readCmdLength; I2CMsg[1].buf = (__u8 *)readCmd1; - /* read msg */ + //read msg I2CMsg[2].addr = (__u16)I2CSAD; I2CMsg[2].flags = I2C_M_RD; I2CMsg[2].len = byteToRead; @@ -204,30 +223,31 @@ int writeReadCmd(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, return ERROR_I2C_O; while (retry < I2C_RETRY && ret < OK) { ret = i2c_transfer(client->adapter, I2CMsg, 3); - if (ret >= OK) - break; retry++; - msleep(I2C_WAIT_BEFORE_RETRY); + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); } if (ret < 0) { - logError(1, "%s writeReadCmd: ERROR %02X\n", tag, ERROR_I2C_WR); - return ERROR_I2C_WR; + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_WR); + return ERROR_I2C_WR; } return OK; - } -int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte) -{ +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, + int hasDummyByte) +{ int remaining = byteToRead; int toRead = 0; u8 rCmd[3] = { cmd, 0x00, 0x00 }; - u8 *buff = (u8 *)kmalloc((READ_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + u8 *buff = (u8 *)kmalloc_array(READ_CHUNK + 1, sizeof(u8), GFP_KERNEL); + if (buff == NULL) { - logError(1, "%s readCmdU16: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } @@ -245,7 +265,9 @@ int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte if (hasDummyByte) { if (fts_readCmd(rCmd, 3, buff, toRead + 1) < 0) { - logError(1, "%s readCmdU16: ERROR %02X\n", tag, ERROR_I2C_R); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + kfree(buff); return ERROR_I2C_R; } memcpy(outBuf, buff + 1, toRead); @@ -256,24 +278,23 @@ int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte } address += toRead; - outBuf += toRead; - } kfree(buff); return OK; } + int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite) { - int remaining = byteToWrite; int toWrite = 0; - u8 *buff = (u8 *)kmalloc((WRITE_CHUNK + 3)*sizeof(u8), GFP_KERNEL); + u8 *buff = (u8 *)kmalloc_array(WRITE_CHUNK + 3, sizeof(u8), GFP_KERNEL); + if (buff == NULL) { - logError(1, "%s writeCmdU16: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } @@ -292,31 +313,38 @@ int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite) buff[2] = (u8)(address & 0xFF); memcpy(buff + 3, dataToWrite, toWrite); if (fts_writeCmd(buff, 3 + toWrite) < 0) { - logError(1, "%s writeCmdU16: ERROR %02\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); return ERROR_I2C_W; } address += toWrite; dataToWrite += toWrite; - } + kfree(buff); return OK; } -int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, int byteToWrite) +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, + int byteToWrite) { - int remaining = byteToWrite; int toWrite = 0; + int ret; u8 buff1[3] = { writeCmd1, 0x00, 0x00 }; - u8 *buff2 = (u8 *)kmalloc((WRITE_CHUNK + 3)*sizeof(u8), GFP_KERNEL); + u8 *buff2 = (u8 *)kmalloc_array(WRITE_CHUNK + 3, + sizeof(u8), GFP_KERNEL); + if (buff2 == NULL) { - logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } buff2[0] = writeCmd2; + while (remaining > 0) { if (remaining >= WRITE_CHUNK) { toWrite = WRITE_CHUNK; @@ -333,33 +361,41 @@ int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, int by memcpy(buff2 + 3, dataToWrite, toWrite); if (fts_writeCmd(buff1, 3) < 0) { - logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_W); - return ERROR_I2C_W; + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + ret = ERROR_I2C_W; + goto END; } if (fts_writeCmd(buff2, 3 + toWrite) < 0) { - logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_W); - return ERROR_I2C_W; + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + ret = ERROR_I2C_W; + goto END; } address += toWrite; dataToWrite += toWrite; - } - return OK; + ret = OK; +END: + kfree(buff2); + return ret; } -int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, int hasDummyByte) +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, + int byteToRead, int hasDummyByte) { - int remaining = byteToRead; int toRead = 0; u8 reaCmd[3]; u8 wriCmd[3]; - u8 *buff = (u8 *)kmalloc((READ_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + u8 *buff = (u8 *)kmalloc_array(READ_CHUNK + 1, sizeof(u8), GFP_KERNEL); + if (buff == NULL) { - logError(1, "%s writereadCmd32: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s writereadCmd32: ERROR %02X\n", + tag, ERROR_ALLOC); return ERROR_ALLOC; } @@ -382,22 +418,25 @@ int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, i reaCmd[2] = (u8)(address & 0x000000FF); if (hasDummyByte) { - if (writeReadCmd(wriCmd, 3, reaCmd, 3, buff, toRead + 1) < 0) { - logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_WR); + if (writeReadCmd(wriCmd, 3, reaCmd, 3, + buff, toRead + 1) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", + tag, ERROR_I2C_WR); + kfree(buff); return ERROR_I2C_WR; } memcpy(outBuf, buff + 1, toRead); } else { - if (writeReadCmd(wriCmd, 3, reaCmd, 3, buff, toRead) < 0) + if (writeReadCmd(wriCmd, 3, reaCmd, + 3, buff, toRead) < 0) return ERROR_I2C_WR; memcpy(outBuf, buff, toRead); } address += toRead; - outBuf += toRead; - } + kfree(buff); return OK; } diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.h b/drivers/input/touchscreen/st/fts_lib/ftsIO.h index 7bdeda2f9a2d98227c9463e11205d14ece9d7a86..8e84104e5014bdacf4b2bc9d77a2d8f28b684923 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsIO.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.h @@ -1,26 +1,48 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* I2C/SPI Communication * -* * -************************************************************************** -************************************************************************** +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication ** + * * + ************************************************************************** + ************************************************************************** + */ -*/ - -#include "ftsSoftware.h" -#include "ftsCrossCompile.h" +#ifndef __FTS_IO_H +#define __FTS_IO_H #include #include -#define I2C_RETRY 3 /* number */ -#define I2C_WAIT_BEFORE_RETRY 10 /* ms */ +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" + +#define I2C_RETRY 3 //number of retry +#define I2C_WAIT_BEFORE_RETRY 2 //ms int openChannel(struct i2c_client *clt); struct device *getDev(void); @@ -28,8 +50,14 @@ struct i2c_client *getClient(void); int fts_readCmd(u8 *cmd, int cmdLenght, u8 *outBuf, int byteToRead); int fts_writeCmd(u8 *cmd, int cmdLenght); int fts_writeFwCmd(u8 *cmd, int cmdLenght); -int writeReadCmd(u8 *writeCmd, int writeCmdLenght, u8 *readCmd, int readCmdLenght, u8 *outBuf, int byteToRead); -int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte); +int writeReadCmd(u8 *writeCmd, int writeCmdLenght, u8 *readCmd, + int readCmdLenght, u8 *outBuf, int byteToRead); +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, + int hasDummyByte); int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite); -int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, int byteToWrite); -int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, int hasDummyByte); +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, + int byteToWrite); +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, + int hasDummyByte); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h index c8bbc8e18d28175f507c5b7cdbc7fe2f166bce05..82b1d18571cd82fe7278d77bfe0f8094509ae0c7 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h @@ -1,131 +1,190 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FW related data * -* * -************************************************************************** -************************************************************************** +/** + * + ************************************************************************* + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FW related data * + * * + ************************************************************************** + ************************************************************************** + * + */ -*/ +#ifndef __FTS_SOFTWARE_H +#define __FTS_SOFTWARE_H + +#include #include "ftsHardware.h" -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#define ECHO_ENABLED 0x00000001 - -/* chipInfo ftsInfo; */ - -/* FTS FW COMAND */ -#define FTS_CMD_MS_MT_SENSE_OFF 0x92 -#define FTS_CMD_MS_MT_SENSE_ON 0x93 -#define FTS_CMD_SS_HOVER_OFF 0x94 -#define FTS_CMD_SS_HOVER_ON 0x95 -#define FTS_CMD_LP_TIMER_CALIB 0x97 -#define FTS_CMD_MS_KEY_OFF 0x9A -#define FTS_CMD_MS_KEY_ON 0x9B -#define FTS_CMD_MS_COMP_TUNING 0xA3 -#define FTS_CMD_SS_COMP_TUNING 0xA4 -#define FTS_CMD_FULL_INITIALIZATION 0xA5 -#define FTS_CMD_ITO_CHECK 0xA7 -#define FTS_CMD_RELEASE_INFO 0xAA -#define FTS_CMD_GESTURE_MODE 0xAD -#define FTS_CMD_REQU_FW_CONF 0xB2 -#define FTS_CMD_REQU_FRAME_DATA 0xB7 -#define FTS_CMD_REQU_COMP_DATA 0xB8 -#define FTS_CMD_WRITE_MP_FLAG 0xC0 -#define FTS_CMD_FEATURE_ENABLE 0xC1 -#define FTS_CMD_FEATURE_DISABLE 0xC2 -#define FTS_CMD_GESTURE_CMD 0xC3 -#define FTS_CMD_SAVE_CX_TUNING 0xFC - -/* Event ID */ -#define EVENTID_NO_EVENT 0x00 -#define EVENTID_ERROR_EVENT 0x0F -#define EVENTID_CONTROL_READY 0x10 -#define EVENTID_FW_CONFIGURATION 0x12 -#define EVENTID_COMP_DATA_READ 0x13 -#define EVENTID_STATUS_UPDATE 0x16 -#define EVENTID_RELEASE_INFO 0x1C -#define EVENTID_ENTER_POINTER 0x03 -#define EVENTID_LEAVE_POINTER 0x04 -#define EVENTID_MOTION_POINTER 0x05 -#define EVENTID_HOVER_ENTER_POINTER 0x07 -#define EVENTID_HOVER_LEAVE_POINTER 0x08 -#define EVENTID_HOVER_MOTION_POINTER 0x09 -#define EVENTID_PROXIMITY_ENTER 0x0B -#define EVENTID_PROXIMITY_LEAVE 0x0C -#define EVENTID_KEY_STATUS 0x0E -#define EVENTID_GESTURE 0x22 -#define EVENTID_FRAME_DATA_READ 0x25 -#define EVENTID_ECHO 0xEC -#define EVENTID_LAST (EVENTID_FRAME_DATA_READ+1) - -/* EVENT TYPE */ -#define EVENT_TYPE_MS_TUNING_CMPL 0x01 -#define EVENT_TYPE_SS_TUNING_CMPL 0x02 -#define EVENT_TYPE_COMP_DATA_SAVED 0x04 -#define EVENT_TYPE_ITO 0x05 -#define EVENT_TYPE_FULL_INITIALIZATION 0x07 -#define EVENT_TYPE_LPTIMER_TUNING_CMPL 0x20 -#define EVENT_TYPE_ESD_ERROR 0x0A -#define EVENT_TYPE_WATCHDOG_ERROR 0x01 - -/* CONFIG ID INFO */ -#define CONFIG_ID_ADDR 0x0001 -#define CONFIG_ID_BYTE 2 - -/* ADDRESS OFFSET IN SYSINFO */ -#define ADDR_RAW_TOUCH 0x0000 -#define ADDR_FILTER_TOUCH 0x0002 -#define ADDR_NORM_TOUCH 0x0004 -#define ADDR_CALIB_TOUCH 0x0006 -#define ADDR_RAW_HOVER_FORCE 0x000A -#define ADDR_RAW_HOVER_SENSE 0x000C -#define ADDR_FILTER_HOVER_FORCE 0x000E -#define ADDR_FILTER_HOVER_SENSE 0x0010 -#define ADDR_NORM_HOVER_FORCE 0x0012 -#define ADDR_NORM_HOVER_SENSE 0x0014 -#define ADDR_CALIB_HOVER_FORCE 0x0016 -#define ADDR_CALIB_HOVER_SENSE 0x0018 -#define ADDR_RAW_PRX_FORCE 0x001A -#define ADDR_RAW_PRX_SENSE 0x001C -#define ADDR_FILTER_PRX_FORCE 0x001E -#define ADDR_FILTER_PRX_SENSE 0x0020 -#define ADDR_NORM_PRX_FORCE 0x0022 -#define ADDR_NORM_PRX_SENSE 0x0024 -#define ADDR_CALIB_PRX_FORCE 0x0026 -#define ADDR_CALIB_PRX_SENSE 0x0028 -#define ADDR_RAW_MS_KEY 0x0032 -#define ADDR_COMP_DATA 0x0050 -#define ADDR_FRAMEBUFFER_DATA 0x8000 - -/* ADDRESS FW REGISTER */ -#define ADDR_SENSE_LEN 0x0014 -#define ADDR_FORCE_LEN 0x0015 -#define ADDR_MS_TUNING_VER 0x0729 -#define ADDR_SS_TUNING_VER 0x074E - -/* B2 INFO */ -#define B2_DATA_BYTES 4 -#define B2_CHUNK ((FIFO_DEPTH/2)*B2_DATA_BYTES) /* number of bytes */ - -/* FEATURES */ -#define FEAT_GESTURE 0x00 -#define FEAT_GLOVE 0x01 -#define FEAT_STYLUS 0x02 -#define FEAT_COVER 0x04 -#define FEAT_CHARGER 0x08 -#define FEAT_VR 0x10 -#define FEAT_EDGE_REJECTION 0x20 - -/* MP_FLAG_VALUE */ -#define INIT_MP 0xA5A5A501 -#define INIT_FIELD 0xA5A5A502 +#define ECHO_ENABLED 0x00000001 + +//FTS FW COMMAND +#define FTS_CMD_MS_MT_SENSE_OFF 0x92 +#define FTS_CMD_MS_MT_SENSE_ON 0x93 +#define FTS_CMD_SS_HOVER_OFF 0x94 +#define FTS_CMD_SS_HOVER_ON 0x95 +#define FTS_CMD_LP_TIMER_CALIB 0x97 +#define FTS_CMD_MS_KEY_OFF 0x9A +#define FTS_CMD_MS_KEY_ON 0x9B +#define FTS_CMD_MS_COMP_TUNING 0xA3 +#define FTS_CMD_SS_COMP_TUNING 0xA4 +#define FTS_CMD_FULL_INITIALIZATION 0xA5 +#define FTS_CMD_ITO_CHECK 0xA7 +#define FTS_CMD_RELEASE_INFO 0xAA +#define FTS_CMD_GESTURE_MODE 0xAD +#define FTS_CMD_REQU_FW_CONF 0xB2 +#define FTS_CMD_REQU_FRAME_DATA 0xB7 +#define FTS_CMD_REQU_COMP_DATA 0xB8 +#define FTS_CMD_WRITE_MP_FLAG 0xC0 +#define FTS_CMD_FEATURE_ENABLE 0xC1 +#define FTS_CMD_FEATURE_DISABLE 0xC2 +#define FTS_CMD_GESTURE_CMD 0xC3 +#define FTS_CMD_LOCKDOWN_CMD 0xC4 +#define FTS_CMD_NOISE_WRITE 0xC7 +#define FTS_CMD_NOISE_READ 0xC8 +#define FTS_CMD_LOCKDOWN_FILL 0xCA +#define FTS_CMD_LOCKDOWN_WRITE 0xCB +#define FTS_CMD_LOCKDOWN_READ 0xCC +#define FTS_CMD_SAVE_CX_TUNING 0xFC + +//Event ID +#define EVENTID_NO_EVENT 0x00 +#define EVENTID_ERROR_EVENT 0x0F +#define EVENTID_CONTROL_READY 0x10 +#define EVENTID_FW_CONFIGURATION 0x12 +#define EVENTID_COMP_DATA_READ 0x13 +#define EVENTID_STATUS_UPDATE 0x16 +#define EVENTID_RELEASE_INFO 0x1C +#define EVENTID_LOCKDOWN_INFO 0x1E +#define EVENTID_ENTER_POINTER 0x03 +#define EVENTID_LEAVE_POINTER 0x04 +#define EVENTID_MOTION_POINTER 0x05 +#define EVENTID_HOVER_ENTER_POINTER 0x07 +#define EVENTID_HOVER_LEAVE_POINTER 0x08 +#define EVENTID_HOVER_MOTION_POINTER 0x09 +#define EVENTID_PROXIMITY_ENTER 0x0B +#define EVENTID_PROXIMITY_LEAVE 0x0C +#define EVENTID_KEY_STATUS 0x0E +#define EVENTID_LOCKDOWN_INFO_READ 0x1E +#define EVENTID_NOISE_READ 0x17 +#define EVENTID_NOISE_WRITE 0x18 +#define EVENTID_GESTURE 0x22 +#define EVENTID_FRAME_DATA_READ 0x25 +#define EVENTID_ECHO 0xEC +#define EVENTID_LAST (EVENTID_FRAME_DATA_READ+1) + +//EVENT TYPE +#define EVENT_TYPE_MS_TUNING_CMPL 0x01 +#define EVENT_TYPE_SS_TUNING_CMPL 0x02 +#define EVENT_TYPE_COMP_DATA_SAVED 0x04 +#define EVENT_TYPE_ITO 0x05 +#define EVENT_TYPE_FULL_INITIALIZATION 0x07 +#define EVENT_TYPE_LPTIMER_TUNING_CMPL 0x20 +#define EVENT_TYPE_ESD_ERROR 0x0A +#define EVENT_TYPE_WATCHDOG_ERROR 0x01 +#define EVENT_TYPE_CHECKSUM_ERROR 0x03 +#define EVENT_TYPE_LOCKDOWN 0x0A +#define EVENT_TYPE_LOCKDOWN_WRITE 0x08 +#define EVENT_TYPE_LOCKDOWN_ERROR 0x0B + +//CRC type +#define CRC_CONFIG_SIGNATURE 0x01 +#define CRC_CONFIG 0x02 +#define CRC_CX_MEMORY 0x03 + +// CONFIG ID INFO +#define CONFIG_ID_ADDR 0x0001 +#define CONFIG_ID_BYTE 2 + +//ADDRESS OFFSET IN SYSINFO +#define ADDR_RAW_TOUCH 0x0000 +#define ADDR_FILTER_TOUCH 0x0002 +#define ADDR_NORM_TOUCH 0x0004 +#define ADDR_CALIB_TOUCH 0x0006 +#define ADDR_RAW_HOVER_FORCE 0x000A +#define ADDR_RAW_HOVER_SENSE 0x000C +#define ADDR_FILTER_HOVER_FORCE 0x000E +#define ADDR_FILTER_HOVER_SENSE 0x0010 +#define ADDR_NORM_HOVER_FORCE 0x0012 +#define ADDR_NORM_HOVER_SENSE 0x0014 +#define ADDR_CALIB_HOVER_FORCE 0x0016 +#define ADDR_CALIB_HOVER_SENSE 0x0018 +#define ADDR_RAW_PRX_FORCE 0x001A +#define ADDR_RAW_PRX_SENSE 0x001C +#define ADDR_FILTER_PRX_FORCE 0x001E +#define ADDR_FILTER_PRX_SENSE 0x0020 +#define ADDR_NORM_PRX_FORCE 0x0022 +#define ADDR_NORM_PRX_SENSE 0x0024 +#define ADDR_CALIB_PRX_FORCE 0x0026 +#define ADDR_CALIB_PRX_SENSE 0x0028 +#define ADDR_RAW_MS_KEY 0x0032 +#define ADDR_NORM_MS_KEY 0x0036 +#define ADDR_COMP_DATA 0x0050 +#define ADDR_FRAMEBUFFER_DATA 0x8000 + +//ADDRESS FW REGISTER +#define ADDR_SENSE_LEN 0x0014 +#define ADDR_FORCE_LEN 0x0015 +#define ADDR_MS_TUNING_VER 0x0729 +#define ADDR_SS_TUNING_VER 0x074E + +//B2 INFO +#define B2_DATA_BYTES 4 +//number of bytes +#define B2_CHUNK ((FIFO_DEPTH/2)*B2_DATA_BYTES) + +//FEATURES +#define FEAT_GLOVE 0x00000001 +#define FEAT_STYLUS 0x00000002 +#define FEAT_COVER 0x00000004 +#define FEAT_CHARGER 0x00000008 +#define FEAT_VR 0x00000010 +#define FEAT_EDGE_REJECTION 0x00000020 +#define FEAT_CORNER_REJECTION 0x00000040 +#define FEAT_EDGE_PALM_REJECTION 0x00000100 + +//TOUCH SIZE +#define STYLUS_SIZE 0x01 +#define FINGER_SIZE 0x02 +#define LARGE_SIZE 0x03 + +//C7/C8 parameters +#define NOISE_PARAMETERS 0x01 + +//MP_FLAG_VALUE +#define INIT_MP 0xA5A5A501 +#define INIT_FIELD 0xA5A5A502 + +//NOISE PARAMS +#define NOISE_PARAMETERS_SIZE 4 //bytes + +//ERROR INFO +#define ERROR_INFO_SIZE (20*4) // bytes +#define ERROR_SIGNATURE 0xFA5005AF +#define ERROR_SIGN_HEAD 0xA5 + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.c b/drivers/input/touchscreen/st/fts_lib/ftsTest.c index 3810fd02001aac271ee478ad267ef15cb096fd87..edcdee2d1dc158a73e6de0fa088597ef5fc6dbc2 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTest.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.c @@ -1,30 +1,38 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +/* + * ************************************************************************** - ** STMicroelectronics ** + ** STMicroelectronics ** ************************************************************************** - ** marco.cali@st.com ** + ** marco.cali@st.com ** ************************************************************************** * * - * FTS API for MP test * + * FTS API for MP test *** * * ************************************************************************** ************************************************************************** - + * */ -#include "ftsCrossCompile.h" -#include "ftsCompensation.h" -#include "ftsError.h" -#include "ftsFrame.h" -#include "ftsHardware.h" -#include "ftsIO.h" -#include "ftsSoftware.h" -#include "ftsTest.h" -#include "ftsTime.h" -#include "ftsTool.h" -#include "../fts.h" - #include #include #include @@ -49,13 +57,25 @@ #include #include #include -/* #include */ +//#include + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" #ifdef LIMITS_H_FILE #include <../fts_limits.h> #endif -/* static char tag[8] = "[ FTS ]\0"; */ +static char tag[8] = "[ FTS ]\0"; int computeAdjHoriz(u8 *data, int row, int column, u8 **result) { @@ -63,24 +83,26 @@ int computeAdjHoriz(u8 *data, int row, int column, u8 **result) int size = row * (column - 1); if (column < 2) { - logError(1, "%s computeAdjHoriz: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - *result = (u8 *) kmalloc(size * sizeof (u8), GFP_KERNEL); + *result = (u8 *) kmalloc_array(size, sizeof(u8), GFP_KERNEL); if (*result == NULL) { - logError(1, "%s computeAdjHoriz: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } for (i = 0; i < row; i++) { for (j = 1; j < column; j++) { - *(*result + (i * (column - 1) + (j - 1))) = abs(data[i * column + j] - data[i * column + (j - 1)]); - } + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - + data[i * column + (j - 1)]); + } } - return OK; - } int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result) @@ -89,45 +111,52 @@ int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result) int size = row * (column - 1); if (column < 2) { - logError(1, "%s computeAdjHorizTotal: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); if (*result == NULL) { - logError(1, "%s computeAdjHorizTotal: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } for (i = 0; i < row; i++) { for (j = 1; j < column; j++) { - *(*result + (i * (column - 1) + (j - 1))) = abs(data[i * column + j] - data[i * column + (j - 1)]); + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - + data[i * column + (j - 1)]); } } return OK; - } int computeAdjVert(u8 *data, int row, int column, u8 **result) { int i, j; - int size = (row - 1)*(column); + int size = (row - 1) * (column); if (row < 2) { - logError(1, "%s computeAdjVert: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - *result = (u8 *) kmalloc(size * sizeof (u8), GFP_KERNEL); + *result = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); if (*result == NULL) { - logError(1, "%s computeAdjVert: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } for (i = 1; i < row; i++) { for (j = 0; j < column; j++) { - *(*result + ((i - 1) * column + j)) = abs(data[i * column + j] - data[(i - 1) * column + j]); + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - + data[(i - 1) * column + j]); } } @@ -137,42 +166,49 @@ int computeAdjVert(u8 *data, int row, int column, u8 **result) int computeAdjVertTotal(u16 *data, int row, int column, u16 **result) { int i, j; - int size = (row - 1)*(column); + int size = (row - 1) * (column); if (row < 2) { - logError(1, "%s computeAdjVertTotal: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); if (*result == NULL) { - logError(1, "%s computeAdjVertTotal: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } for (i = 1; i < row; i++) { for (j = 0; j < column; j++) { - *(*result + ((i - 1) * column + j)) = abs(data[i * column + j] - data[(i - 1) * column + j]); + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - + data[(i - 1) * column + j]); } } return OK; } -int computeTotal(u8 *data, u8 main, int row, int column, int m, int n, u16 **result) +int computeTotal(u8 *data, u8 main, int row, int column, + int m, int n, u16 **result) { int i, j; - int size = (row)*(column); + int size = (row) * (column); - *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); if (*result == NULL) { - logError(1, "%s computeTotal : ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s : ERROR %02X\n", + tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } for (i = 0; i < row; i++) { for (j = 0; j < column; j++) { - *(*result + (i * column + j)) = m * main + n * data[i * column + j]; + *(*result + (i * column + j)) = + m * main + n * data[i * column + j]; } } @@ -186,14 +222,17 @@ int checkLimitsMinMax(short *data, int row, int column, int min, int max) for (i = 0; i < row; i++) { for (j = 0; j < column; j++) { - if (data[i * column + j] < min || data[i * column + j] > max) { - logError(1, "%s checkLimitsMinMax: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min, max); + if (data[i * column + j] < min + || data[i * column + j] > max) { + logError(1, "%s %s:Node[%d,%d] = %d ", tag, + __func__, i, j, data[i * column + j]); + logError(1, "exceed limit [%d,%d]\n", min, max); count++; } } } - return count; /* if count is 0 = OK, test completed successfully */ + return count;//if count is 0 = OK, test completed successfully } int checkLimitsGap(short *data, int row, int column, int threshold) @@ -203,7 +242,10 @@ int checkLimitsGap(short *data, int row, int column, int threshold) int max_node; if (row == 0 || column == 0) { - logError(1, "%s checkLimitsGap: invalid number of rows = %d or columns = %d ERROR %02\n", tag, row, column, ERROR_OP_NOT_ALLOW); + logError(1, "%s %s:invalid number of rows = %d ", + tag, __func__, row); + logError(1, "or columns = %d %02\n", + column, ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } @@ -222,11 +264,11 @@ int checkLimitsGap(short *data, int row, int column, int threshold) } if (max_node - min_node > threshold) { - logError(1, "%s checkLimitsGap: GAP = %d exceed limit %d\n", tag, max_node - min_node, threshold); + logError(1, "%s %s: GAP = %d exceed limit %d\n", + tag, __func__, max_node - min_node, threshold); return ERROR_TEST_CHECK_FAIL; - } else - return OK; - + } + return OK; } int checkLimitsMap(u8 *data, int row, int column, int *min, int *max) @@ -236,14 +278,21 @@ int checkLimitsMap(u8 *data, int row, int column, int *min, int *max) for (i = 0; i < row; i++) { for (j = 0; j < column; j++) { - if (data[i * column + j] < min[i * column + j] || data[i * column + j] > max[i * column + j]) { - logError(1, "%s checkLimitsMap: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min[i * column + j], max[i * column + j]); + if (data[i * column + j] < min[i * column + j] + || data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s: Node[%d,%d] = %d ", + tag, __func__, + i, j, + data[i * column + j]); + logError(1, "exceed limit [%d, %d]\n", + min[i * column + j], + max[i * column + j]); count++; } } } - return count; /* if count is 0 = OK, test completed successfully */ + return count; //if count is 0 = OK, test completed successfully } int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max) @@ -253,14 +302,20 @@ int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max) for (i = 0; i < row; i++) { for (j = 0; j < column; j++) { - if (data[i * column + j] < min[i * column + j] || data[i * column + j] > max[i * column + j]) { - logError(1, "%s checkLimitsMapTotal: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min[i * column + j], max[i * column + j]); + if (data[i * column + j] < min[i * column + j] + || data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s:Node[%d,%d] = %d\n", + tag, __func__, i, j, + data[i * column + j]); + logError(1, "exceed limit [%d, %d]\n", + min[i * column + j], + max[i * column + j]); count++; } } } - return count; /* if count is 0 = OK, test completed successfully */ + return count; //if count is 0 = OK, test completed successfully } int checkLimitsMapAdj(u8 *data, int row, int column, int *max) @@ -271,13 +326,17 @@ int checkLimitsMapAdj(u8 *data, int row, int column, int *max) for (i = 0; i < row; i++) { for (j = 0; j < column; j++) { if (data[i * column + j] > max[i * column + j]) { - logError(1, "%s checkLimitsMapAdj: Node[%d,%d] = %d exceed limit > %d\n", tag, i, j, data[i * column + j], max[i * column + j]); + logError(1, "%s %s:Node[%d,%d] = %d ", + tag, __func__, i, j); + logError(1, "exceed limit > %d\n", + data[i * column + j], + max[i * column + j]); count++; } } } - - return count; /* if count is 0 = OK, test completed successfully */ + //if count is 0 = OK, test completed successfully + return count; } int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max) @@ -288,27 +347,33 @@ int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max) for (i = 0; i < row; i++) { for (j = 0; j < column; j++) { if (data[i * column + j] > max[i * column + j]) { - logError(1, "%s checkLimitsMapAdjTotal: Node[%d,%d] = %d exceed limit > %d\n", tag, i, j, data[i * column + j], max[i * column + j]); + logError(1, "%s %s:Node[%d,%d] = %d ", + tag, __func__, i, j); + logError(1, "exceed limit > %d\n", + data[i * column + j], + max[i * column + j]); count++; } } } - - return count; /* if count is 0 = OK, test completed successfully */ + //if count is 0 = OK, test completed successfully + return count; } int production_test_ito(void) { int res = OK; u8 cmd; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int eventToSearch[2] = {EVENTID_ERROR_EVENT, EVENT_TYPE_ITO}; /* look for ito event */ + u8 readData[FIFO_EVENT_SIZE]; + //look for ito event + int eventToSearch[2] = {EVENTID_ERROR_EVENT, EVENT_TYPE_ITO}; logError(0, "%s ITO Production test is starting...\n", tag); res = fts_system_reset(); if (res < 0) { - logError(1, "%s production_test_ito: ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); return (res | ERROR_PROD_TEST_ITO); } @@ -316,28 +381,33 @@ int production_test_ito(void) logError(0, "%s ITO Check command sent...\n", tag); if (fts_writeFwCmd(&cmd, 1) < 0) { - logError(1, "%s production_test_ito: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_PROD_TEST_ITO)); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_PROD_TEST_ITO)); return (ERROR_I2C_W | ERROR_PROD_TEST_ITO); } logError(0, "%s Looking for ITO Event...\n", tag); - res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_ITO_TEST_RESULT); + res = pollForEvent(eventToSearch, 2, + readData, TIMEOUT_ITO_TEST_RESULT); if (res < 0) { - logError(1, "%s production_test_ito: ITO Production test failed... ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + logError(1, "%s %s: ITO Production test failed ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); return (res | ERROR_PROD_TEST_ITO); } if (readData[2] != 0x00 || readData[3] != 0x00) { - logError(0, "%s ITO Production testes finished!.................FAILED ERROR %02X\n", tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO)); + logError(0, "%s ITO Production testes finished! ERROR %02X\n", + tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO)); res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO); } else { - logError(0, "%s ITO Production test finished!.................OK\n", tag); + logError(0, "%s ITO Production test finished!..OK\n", tag); res = OK; } res |= fts_system_reset(); if (res < 0) { - logError(1, "%s production_test_ito: ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); res = (res | ERROR_PROD_TEST_ITO); } return res; @@ -347,78 +417,94 @@ int production_test_initialization(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_FULL_INITIALIZATION}; + u8 readData[FIFO_EVENT_SIZE]; - logError(0, "%s INITIALIZATION Production test is starting...\n", tag); + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_FULL_INITIALIZATION}; + logError(0, "%s INITIALIZATION Production test is starting\n", tag); res = fts_system_reset(); if (res < 0) { - logError(1, "%s production_test_initialization: ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); return (res | ERROR_PROD_TEST_INITIALIZATION); } logError(0, "%s INITIALIZATION command sent...\n", tag); cmd = FTS_CMD_FULL_INITIALIZATION; if (fts_writeFwCmd(&cmd, 1) < 0) { - logError(1, "%s production_test_initialization: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION)); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION)); return (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION); } logError(0, "%s Looking for INITIALIZATION Event...\n", tag); - res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + res = pollForEvent(eventToSearch, 2, readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); if (res < 0) { - logError(1, "%s production_test_initialization: INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + logError(1, "%s %s: INITIALIZATION Production ", tag, __func__); + logError(1, "test failed %02X\n", + ERROR_PROD_TEST_INITIALIZATION); return (res | ERROR_PROD_TEST_INITIALIZATION); } if (readData[2] != 0x00) { - logError(0, "%s INITIALIZATION Production testes finished!.................FAILED ERROR %02X\n", tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION)); + logError(0, "%sINITIALIZATION Production ", tag); + logError(0, "testes finished! FAILED %02X\n", + (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION)); res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION); } else { - logError(0, "%s INITIALIZATION Production test.................OK\n", tag); + logError(0, "%s INITIALIZATION Production test...OK\n", tag); res = OK; } logError(0, "%s Refresh Chip Info...\n", tag); + //need to update the chipInfo in order to refresh the tuning_versione res |= readChipInfo(1); - /* need to update the chipInfo in order to refresh the tuning_versione */ if (res < 0) { - logError(1, "%s production_test_initialization: read chip info ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); - res = (res | ERROR_PROD_TEST_INITIALIZATION); + logError(1, "%s %s: read chip info ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); } return res; - } int ms_compensation_tuning(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_MS_TUNING_CMPL}; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_MS_TUNING_CMPL}; logError(0, "%s MS INITIALIZATION command sent...\n", tag); cmd = FTS_CMD_MS_COMP_TUNING; if (fts_writeFwCmd(&cmd, 1) < 0) { - logError(1, "%s ms_compensation_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_MS_TUNING)); + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_MS_TUNING)); return (ERROR_I2C_W | ERROR_MS_TUNING); } logError(0, "%s Looking for MS INITIALIZATION Event...\n", tag); - res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + res = pollForEvent(eventToSearch, 2, readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); if (res < 0) { - logError(1, "%s ms_compensation_tuning: MS INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_MS_TUNING); + logError(1, "%s %s:MS INITIALIZATION Production\n", + tag, __func__); + logError(1, "test failed %02X\n", ERROR_MS_TUNING); return (res | ERROR_MS_TUNING); } if (readData[2] != 0x00 || readData[3] != 0x00) { - logError(0, "%s MS INITIALIZATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_MS_TUNING); + logError(0, "%s MS INITIALIZATION Production ", tag); + logError(0, "test finished! FAILED %02X\n", ERROR_MS_TUNING); res = ERROR_MS_TUNING; } else { - logError(0, "%s MS INITIALIZATION Production test finished!.................OK\n", tag); + logError(0, + "%s MS INITIALIZATION Production test finished! OK\n", + tag); res = OK; } @@ -429,28 +515,37 @@ int ss_compensation_tuning(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_SS_TUNING_CMPL}; + u8 readData[FIFO_EVENT_SIZE]; + + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_SS_TUNING_CMPL}; logError(0, "%s SS INITIALIZATION command sent...\n", tag); cmd = FTS_CMD_SS_COMP_TUNING; if (fts_writeFwCmd(&cmd, 1) < 0) { - logError(1, "%s ss_compensation_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_SS_TUNING)); + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_SS_TUNING)); return (ERROR_I2C_W | ERROR_SS_TUNING); } logError(0, "%s Looking for SS INITIALIZATION Event...\n", tag); - res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); if (res < 0) { - logError(1, "%s ms_compensation_tuning: SS INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_SS_TUNING); + logError(1, "%s %s:SS INITIALIZATION Production ", + tag, __func__); + logError(1, "test failed %02X\n", ERROR_SS_TUNING); return (res | ERROR_SS_TUNING); } - + logError(0, "%s SS INITIALIZATION Production test finished!", tag); if (readData[2] != 0x00 || readData[3] != 0x00) { - logError(0, "%s SS INITIALIZATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_SS_TUNING); + logError(0, "%s.................FAILED ERROR %02X\n", + tag, ERROR_SS_TUNING); res = ERROR_SS_TUNING; } else { - logError(0, "%s SS INITIALIZATION Production test finished!.................OK\n", tag); + logError(0, "%s.................OK\n", tag); res = OK; } @@ -461,28 +556,39 @@ int lp_timer_calibration(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_LPTIMER_TUNING_CMPL}; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LPTIMER_TUNING_CMPL}; logError(0, "%s LP TIMER CALIBRATION command sent...\n", tag); cmd = FTS_CMD_LP_TIMER_CALIB; if (fts_writeFwCmd(&cmd, 1) < 0) { - logError(1, "%s lp_timer_calibration 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_LP_TIMER_TUNING)); + logError(1, "%s %s 2:ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_LP_TIMER_TUNING)); return (ERROR_I2C_W | ERROR_LP_TIMER_TUNING); } logError(0, "%s Looking for LP TIMER CALIBRATION Event...\n", tag); - res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { - logError(1, "%s lp_timer_calibration: LP TIMER CALIBRATION Production test failed... ERROR %02X\n", tag, ERROR_LP_TIMER_TUNING); + logError(1, "%s:LP TIMER CALIBRATION Production test failed\n", + tag); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_LP_TIMER_TUNING); return (res | ERROR_LP_TIMER_TUNING); } + logError(0, "LP TIMER CALIBRATION Production test finished!"); if (readData[2] != 0x00 || readData[3] != 0x01) { - logError(0, "%s LP TIMER CALIBRATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_LP_TIMER_TUNING); + logError(0, "%s........FAILED ERROR %02X\n", + tag, ERROR_LP_TIMER_TUNING); res = ERROR_LP_TIMER_TUNING; } else { - logError(0, "%s LP TIMER CALIBRATION Production test finished!.................OK\n", tag); + logError(0, "%s.................OK\n", tag); res = OK; } @@ -493,25 +599,32 @@ int save_cx_tuning(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE] = {0}; - int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_COMP_DATA_SAVED}; + u8 readData[FIFO_EVENT_SIZE]; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_COMP_DATA_SAVED}; logError(0, "%s SAVE CX command sent...\n", tag); cmd = FTS_CMD_SAVE_CX_TUNING; if (fts_writeCmd(&cmd, 1) < 0) { - logError(1, "%s save_cx_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_SAVE_CX_TUNING)); + logError(1, "%s %s 2:ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_SAVE_CX_TUNING)); return (ERROR_I2C_W | ERROR_SAVE_CX_TUNING); } logError(0, "%s Looking for SAVE CX Event...\n", tag); - res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); if (res < 0) { - logError(1, "%s save_cx_tuning: SAVE CX failed... ERROR %02X\n", tag, ERROR_SAVE_CX_TUNING); + logError(1, "%s %s: SAVE CX failed... ERROR %02X\n", + tag, ERROR_SAVE_CX_TUNING); return (res | ERROR_SAVE_CX_TUNING); } if (readData[2] != 0x00 || readData[3] != 0x00) { - logError(0, "%s SAVE CX finished!.................FAILED ERROR %02X\n", tag, ERROR_SAVE_CX_TUNING); + logError(0, "%s SAVE CX finished! FAILED ERROR %02X\n", + tag, ERROR_SAVE_CX_TUNING); res = ERROR_SAVE_CX_TUNING; } else { logError(0, "%s SAVE CX finished!.................OK\n", tag); @@ -521,105 +634,115 @@ int save_cx_tuning(void) return res; } -int production_test_splited_initialization(int saveToFlash) +int production_test_split_initialization(int saveToFlash) { int res; - logError(0, "%s Splitted Initialization test is starting...\n", tag); + logError(0, "%s Split Initialization test is starting...\n", tag); res = fts_system_reset(); if (res < 0) { - logError(1, "%s production_test_initialization: ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); - logError(0, "%s LP INITIALIZATION TEST:\n", tag); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, + ERROR_PROD_TEST_INITIALIZATION); return (res | ERROR_PROD_TEST_INITIALIZATION); } logError(0, "%s MS INITIALIZATION TEST:\n", tag); res = ms_compensation_tuning(); if (res < 0) { - logError(0, "%s production_test_splited_initialization: MS INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + logError(0, "%s %s:MS INITIALIZATION TEST FAILED! ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); return (res | ERROR_PROD_TEST_INITIALIZATION); } - logError(0, "%s MS INITIALIZATION TEST OK!\n", tag); + logError(0, "%s MS INITIALIZATION TEST OK!\n", tag); logError(0, "%s\n", tag); - logError(0, "%s SS INITIALIZATION TEST:\n", tag); res = ss_compensation_tuning(); if (res < 0) { - logError(0, "%s production_test_splited_initialization: SS INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + logError(0, "%s %s: SS INITIALIZATION TEST FAILED! ", + tag, __func__); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_INITIALIZATION); return (res | ERROR_PROD_TEST_INITIALIZATION); } - logError(0, "%s SS INITIALIZATION TEST OK!\n", tag); + logError(0, "%s SS INITIALIZATION TEST OK!\n", tag); logError(0, "%s\n", tag); - logError(0, "%s LP INITIALIZATION TEST:\n", tag); res = lp_timer_calibration(); if (res < 0) { - logError(0, "%s production_test_splited_initialization: LP INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + logError(0, "%s %s: LP INITIALIZATION TEST FAILED! ", + tag, __func__); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_INITIALIZATION); return (res | ERROR_PROD_TEST_INITIALIZATION); } + logError(0, "%s LP INITIALIZATION TEST OK!\n", tag); if (saveToFlash) { logError(0, "%s\n", tag); logError(0, "%s SAVE CX TEST:\n", tag); res = save_cx_tuning(); if (res < 0) { - logError(0, "%s production_test_splited_initialization: SAVE CX TEST FAILED! ERROR %02X\n", tag, res); + logError(0, "%s %s: SAVE CX TEST FAILED! ERROR %02X\n", + tag, __func__, res); return (res | ERROR_PROD_TEST_INITIALIZATION); } logError(0, "%s SAVE CX TEST OK!\n", tag); } + logError(0, "%s Refresh Chip Info...\n", tag); res |= readChipInfo(1); if (res < 0) { - logError(1, "%s production_test_initialization: read chip info ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); - res = (res | ERROR_PROD_TEST_INITIALIZATION); - } else - logError(0, "%s Splited Initialization test finished!.................OK\n", tag); + logError(1, "%s %s: read chip info ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } else { + logError(0, "%s Split Initialization test finished! OK\n", tag); + } return res; + } -int production_test_main(char *pathThresholds, int stop_on_fail, - int saveInit, TestToDo *todo, u32 signature) +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + struct TestToDo *todo, u32 signature) { int res, ret; logError(0, "%s MAIN Production test is starting...\n", tag); - logError(0, "%s\n", tag); - logError(0, "%s ITO TEST:\n", tag); + res = production_test_ito(); if (res < 0) { - logError(0, "%s Error during ITO TEST! ERROR %02X\n", tag, (u8) res); - goto END; /* in case of ITO TEST failure is no sense keep going */ - } else { - logError(0, "%s ITO TEST OK!\n", tag); + logError(0, "%s Error during ITO TEST! ERROR %08X\n", tag, res); + //in case of ITO TEST failure is no sense keep going + goto END; } + logError(0, "%s ITO TEST OK!\n", tag); - logError(0, "%s\n", tag); + logError(0, "%s:\n", tag); + logError(0, "%s INITIALIZATION TEST:\n", tag); - logError(0, "%s INITIALIZATION TEST :\n", tag); if (saveInit == 1) { res = production_test_initialization(); - if (res < 0) { - logError(0, "%s Error during INITIALIZATION TEST! ERROR %02X\n", tag, (u8) res); + if (res < 0) { + logError(0, "%s Error during INITIALIZATION TEST!", + tag); + logError(0, "ERROR %08X\n", res); if (stop_on_fail) goto END; } else { logError(0, "%s INITIALIZATION TEST OK!\n", tag); } } else - logError(0, "%s INITIALIZATION TEST :................. SKIPPED\n", tag); + logError(0, "%s INITIALIZATION TEST:..SKIPPED\n", tag); logError(0, "%s\n", tag); - if (saveInit == 1) { logError(0, "%s Cleaning up...\n", tag); ret = cleanUp(0); if (ret < 0) { - logError(1, "%s production_test_main: clean up ERROR %02X\n", tag, ret); + logError(1, "%s %s: clean up ERROR %02X\n", + tag, __func__, ret); res |= ret; if (stop_on_fail) goto END; @@ -630,172 +753,257 @@ int production_test_main(char *pathThresholds, int stop_on_fail, logError(0, "%s PRODUCTION DATA TEST:\n", tag); ret = production_test_data(pathThresholds, stop_on_fail, todo); if (ret < 0) { - logError(0, "%s Error during PRODUCTION DATA TEST! ERROR %02X\n", tag, ret); + logError(0, "%sError during PRODUCTION DATA TEST %08X\n", + tag, ret); } else { logError(0, "%s PRODUCTION DATA TEST OK!\n", tag); } - res |= ret; - /* the OR is important because if the data test is OK but the inizialization test fail, - * the main production test result should = FAIL - */ + // the OR is important because if + //the data test is OK but the inizialization + //test fail, the main production + //test result should = FAIL if (ret == OK && saveInit == 1) { - logError(0, "%s SAVE FLAG:\n", tag); - ret = save_mp_flag(signature); - if (ret < OK) - logError(0, "%s SAVE FLAG:................. FAIL! ERROR %08X\n", tag, ret); - else - logError(0, "%s SAVE FLAG:................. OK!\n", tag); - res |= ret; + logError(0, "%s SAVE FLAG:\n", tag); + ret = save_mp_flag(signature); + if (ret < OK) + logError(0, "%s SAVE FLAG:FAIL! ERROR %08X\n", + tag, ret); + else + logError(0, "%s SAVE FLAG:OK!\n", tag); + res |= ret; + // need to update the MP Flag + ret = readChipInfo(1); + if (ret < OK) + logError(1, "%s %s:read chip info ERROR %08X\n", + tag, __func__, ret); + res |= ret; } - logError(0, "%s\n", tag); END: if (res < 0) { - logError(0, "%s MAIN Production test finished.................FAILED\n", tag); + logError(0, "%s MAIN Production test finished..FAILED\n", tag); return res; } - logError(0, "%s MAIN Production test finished.................OK\n", tag); + logError(0, "%s MAIN Production test finished..OK\n", tag); return OK; } -int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +int production_test_ms_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo) { - int ret, count_fail = 0; - MutualSenseFrame msRawFrame; + struct MutualSenseFrame msRawFrame; int *thresholds = NULL; int trows, tcolumns; - /*********************** Mutual Sense Test **********************/ + //****** Mutual Sense Test ************/ logError(0, "%s\n", tag); logError(0, "%s MS RAW DATA TEST is starting...\n", tag); if (todo->MutualRaw == 1 || todo->MutualRawGap == 1) { - ret = getMSFrame2(MS_TOUCH_ACTIVE, &msRawFrame); if (ret < 0) { - logError(1, "%s production_test_data: getMSFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s %s:getMSFrame failed... ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } logError(0, "%s MS RAW MIN MAX TEST:\n", tag); if (todo->MutualRaw == 1) { - ret = parseProductionTestLimits(path_limits, MS_RAW_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + MS_RAW_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_RAW_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s:MS_RAW_MIN_MAX failed...", + tag, __func__); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMinMax(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0], thresholds[1]); + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax MS RAW failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS RAW MIN MAX TEST:.................FAIL\n\n", tag); - count_fail += 1; + logError(1, "%s %s:MS RAW failed...", + tag, __func__); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS RAW MIN MAX TEST:...", tag); + logError(0, "FAIL\n\n", tag); + count_fail += 1; if (stop_on_fail == 1) goto ERROR; - } + } else + logError(0, "%s MS RAW MIN MAX TEST:OK\n", tag); kfree(thresholds); - logError(0, "%s MS RAW MIN MAX TEST:.................OK\n", tag); + thresholds = NULL; } else - logError(0, "%s MS RAW MIN MAX TEST:.................SKIPPED\n", tag); + logError(0, "%s MS RAW MIN MAX TEST:SKIPPED\n", tag); logError(0, "%s\n", tag); logError(0, "%s MS RAW GAP TEST:\n", tag); if (todo->MutualRawGap == 1) { - ret = parseProductionTestLimits(path_limits, MS_RAW_GAP, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + MS_RAW_GAP, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_RAW_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s: MS_RAW_GAP failed... ", + tag, __func__); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsGap(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0]); + ret = checkLimitsGap(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsGap MS RAW failed... ERROR = %02X\n", tag, ret); - count_fail += 1; + logError(1, "%s %s:checkLimitsGap MS RAW ", + tag, __func__); + logError(1, "failed ERROR:%02X\n", ret); + count_fail += 1; if (stop_on_fail == 1) goto ERROR; - } else - logError(0, "%s MS RAW GAP TEST:.................OK\n\n", tag); + logError(0, "%s MS RAW GAP TEST:....OK\n\n", + tag); kfree(thresholds); + thresholds = NULL; } else - logError(0, "%s MS RAW GAP TEST:.................SKIPPED\n", tag); - - kfree(msRawFrame.node_data); + logError(0, "%s MS RAW GAP TEST:..SKIPPED\n", tag); } else - logError(0, "%s MS RAW FRAME TEST:.................SKIPPED\n", tag); + logError(0, "%s MS RAW FRAME TEST:.SKIPPED\n", tag); logError(0, "%s\n", tag); logError(0, "%s MS KEY RAW TEST:\n", tag); if (todo->MutualKeyRaw == 1) { ret = production_test_ms_key_raw(path_limits); if (ret < 0) { - logError(1, "%s production_test_data: production_test_ms_key_raw failed... ERROR = %02X\n", tag, ret); + logError(1, "%s %s:production_test_ms_key_raw ", + tag, __func__); + logError(1, "failed ERROR:%02X\n", ret); count_fail += 1; + if (count_fail == 1) { + logError(0, "%s MS RAW DATA TEST:FAIL ", tag); + logError(0, "fails_count:%d\n\n", count_fail); + goto ERROR_LIMITS; + } } } else - logError(0, "%s MS KEY RAW TEST:.................SKIPPED\n", tag); - + logError(0, "%s MS KEY RAW TEST:....SKIPPED\n", tag); ERROR: logError(0, "%s\n", tag); if (count_fail == 0) { - logError(0, "%s MS RAW DATA TEST finished!.................OK\n", tag); + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + logError(0, "%s MS RAW DATA TEST finished!.OK\n", tag); return OK; } - print_frame_short("MS Raw frame =", array1dTo2d_short(msRawFrame.node_data, msRawFrame.node_data_size, msRawFrame.header.sense_node), msRawFrame.header.force_node, msRawFrame.header.sense_node); - logError(0, "%s MS RAW DATA TEST:.................FAIL fails_count = %d\n\n", tag, count_fail); + print_frame_short("MS Raw frame =", + array1dTo2d_short(msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + logError(0, "%s MS RAW DATA TEST: FAIL fails_count = %d\n\n", + tag, count_fail); return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + +ERROR_LIMITS: + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + return ret; } int production_test_ms_key_raw(char *path_limits) { - int ret; - MutualSenseFrame msRawFrame; + struct MutualSenseFrame msRawFrame; int *thresholds = NULL; int trows, tcolumns; - /******************************* Mutual Sense Test *******************************/ + //************* Mutual Sense Test ************/ logError(0, "%s MS KEY RAW DATA TEST is starting...\n", tag); ret = getMSFrame2(MS_KEY, &msRawFrame); if (ret < 0) { - logError(1, "%s production_test_data: getMSKeyFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s %s:getMSKeyFrame failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } - ret = parseProductionTestLimits(path_limits, MS_KEY_RAW_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + MS_KEY_RAW_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_RAW_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s: MS_KEY_RAW_MIN_MAX failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMinMax(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0], thresholds[1]); + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax MS KEY RAW failed... ERROR COUNT = %d\n", tag, ret); + logError(1, "%s %s:checkLimitsMinMax failed..ERROR COUNT:%d\n", + tag, __func__, ret); goto ERROR; } else logError(0, "%s MS KEY RAW TEST:.................OK\n\n", tag); kfree(thresholds); + thresholds = NULL; kfree(msRawFrame.node_data); - + msRawFrame.node_data = NULL; return OK; - ERROR: - print_frame_short("MS Key Raw frame =", array1dTo2d_short(msRawFrame.node_data, msRawFrame.node_data_size, msRawFrame.header.sense_node), msRawFrame.header.force_node, msRawFrame.header.sense_node); - logError(0, "%s MS KEY RAW TEST:.................FAIL\n\n", tag); + print_frame_short("MS Key Raw frame =", + array1dTo2d_short(msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + logError(0, "%s MS KEY RAW TEST:......FAIL\n\n", tag); return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); - +ERROR_LIMITS: + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + return ret; } -int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +int production_test_ms_cx(char *path_limits, + int stop_on_fail, struct TestToDo *todo) { - int ret; int count_fail = 0; @@ -804,7 +1012,7 @@ int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo) int *thresholds_max = NULL; int trows, tcolumns; - MutualSenseData msCompData; + struct MutualSenseData msCompData; u8 *adjhor = NULL; @@ -815,264 +1023,515 @@ int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo) u16 *total_adjhor = NULL; u16 *total_adjvert = NULL; - /* MS CX TEST */ + //MS CX TEST logError(0, "%s\n", tag); logError(0, "%s MS CX Testes are starting...\n", tag); - - ret = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &msCompData); /* read MS compensation data */ + //read MS compensation data + ret = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &msCompData); if (ret < 0) { - logError(1, "%s production_test_data: readMutualSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s %s:readMutualSenseCompensationData ", + tag, __func__); + logError(1, "failed %02X\n", ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } logError(0, "%s MS CX1 TEST:\n", tag); if (todo->MutualCx1 == 1) { - - ret = parseProductionTestLimits(path_limits, MS_CX1_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + MS_CX1_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_CX1_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s:parseProductionTestLimits failed ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - container = (u16) msCompData.cx1; - ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + container = (u16)msCompData.cx1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS CX1 TEST:.................FAIL\n\n", tag); + logError(1, "%s %s:checkLimitsMinMax MS CX1 failed ", + tag, __func__); + logError(1, "ERROR COUNT:%d\n", ret); + logError(0, "%s MS CX1 TEST:.........FAIL\n\n", tag); count_fail += 1; if (stop_on_fail) goto ERROR; } else - logError(0, "%s MS CX1 TEST:.................OK\n\n", tag); + logError(0, "%s MS CX1 TEST:..........OK\n\n", tag); } else - logError(0, "%s MS CX1 TEST:.................SKIPPED\n\n", tag); + logError(0, "%s MS CX1 TEST:.......SKIPPED\n\n", tag); kfree(thresholds); + thresholds = NULL; logError(0, "%s MS CX2 MIN MAX TEST:\n", tag); if (todo->MutualCx2 == 1) { - ret = parseProductionTestLimits(path_limits, MS_CX2_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ - if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + MS_CX2_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); //load min thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, MS_CX2_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ - if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + MS_CX2_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load max thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s: MS_CX2_MAP_MAX failed ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMap(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, thresholds_min, thresholds_max); /* check the limits */ + ret = checkLimitsMap(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, + thresholds_max);//check the limits if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap MS CX2 MIN MAX failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS CX2 MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s %s:checkLimitsMap MS CX2 MIN MAX ", + tag, __func__); + logError(1, "failed ERR_COUNT:%d\n", ret); + logError(0, "%s MS CX2 MIN MAX TEST:......FAIL\n\n", + tag); count_fail += 1; if (stop_on_fail) goto ERROR; } else - logError(0, "%s MS CX2 MIN MAX TEST:.................OK\n\n", tag); + logError(0, "%s MS CX2 MIN MAX TEST:....OK\n\n", tag); kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); + thresholds_max = NULL; } else - logError(0, "%s MS CX2 MIN MAX TEST:.................SKIPPED\n\n", tag); + logError(0, "%s MS CX2 MIN MAX TEST:....SKIPPED\n\n", tag); logError(0, "%s MS CX2 ADJ TEST:\n", tag); if (todo->MutualCx2Adj == 1) { - /* MS CX2 ADJ HORIZ */ + //MS CX2 ADJ HORIZ logError(0, "%s MS CX2 ADJ HORIZ TEST:\n", tag); - ret = computeAdjHoriz(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, &adjhor); + ret = computeAdjHoriz(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjhor); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjHoriz failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s:computeAdjHoriz failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s MS CX2 ADJ HORIZ computed!\n", tag); - ret = parseProductionTestLimits(path_limits, MS_CX2_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); - if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node - 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + MS_CX2_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node - 1)) { + + logError(1, "%s %s: MS_CX2_ADJH_MAP_MAX failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdj(adjhor, msCompData.header.force_node, msCompData.header.sense_node - 1, thresholds_max); + ret = checkLimitsMapAdj(adjhor, + msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj CX2 ADJH failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", tag); + logError(1, "%s %s:checkLimitsMapAdj CX2 ADJH failed ", + tag, __func__); + logError(1, "ERROR COUNT:%d\n", ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:..FAIL\n\n", tag); count_fail += 1; if (stop_on_fail) goto ERROR; } else - logError(0, "%s MS CX2 ADJ HORIZ TEST:.................OK\n\n", tag); + logError(0, "%s MS CX2 ADJ HORIZ TEST:..OK\n\n", tag); kfree(thresholds_max); + thresholds_max = NULL; kfree(adjhor); + adjhor = NULL; - /* MS CX2 ADJ VERT */ + //MS CX2 ADJ VERT logError(0, "%s MS CX2 ADJ VERT TEST:\n", tag); - - ret = computeAdjVert(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, &adjvert); + ret = computeAdjVert(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjvert); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjVert failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s:computeAdjVert failed... ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s MS CX2 ADJ VERT computed!\n", tag); - ret = parseProductionTestLimits(path_limits, MS_CX2_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); - if (ret < 0 || (trows != msCompData.header.force_node - 1 || tcolumns != msCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + MS_CX2_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:MS_CX2_ADJV_MAP_MAX failed ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdj(adjvert, msCompData.header.force_node - 1, msCompData.header.sense_node - 1, thresholds_max); + ret = checkLimitsMapAdj(adjvert, + msCompData.header.force_node - 1, + msCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj CX2 ADJV failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", tag); + logError(1, "%s %s:checkLimitsMapAdj CX2 ADJV failed ", + tag, __func__); + logError(1, "COUNT:%d\n", ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:FAIL\n\n", tag); count_fail += 1; if (stop_on_fail) goto ERROR; } else - logError(0, "%s MS CX2 ADJ VERT TEST:.................OK\n\n", tag); + logError(0, "%s MS CX2 ADJ VERT TEST:OK\n\n", tag); kfree(thresholds_max); + thresholds_max = NULL; kfree(adjvert); + adjvert = NULL; } else - logError(0, "%s MS CX2 ADJ TEST:.................SKIPPED\n\n", tag); + logError(0, "%s MS CX2 ADJ TEST:SKIPPED\n\n", tag); - /* START OF TOTAL CHECK */ + //START OF TOTAL CHECK logError(0, "%s MS TOTAL CX TEST:\n", tag); if (todo->MutualCxTotal == 1 || todo->MutualCxTotalAdj == 1) { - ret = computeTotal(msCompData.node_data, msCompData.cx1, msCompData.header.force_node, msCompData.header.sense_node, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + ret = computeTotal(msCompData.node_data, + msCompData.cx1, + msCompData.header.force_node, + msCompData.header.sense_node, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); if (ret < 0) { - logError(1, "%s production_test_data: computeTotalCx failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s:computeTotalCx failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s MS TOTAL CX MIN MAX TEST:\n", tag); if (todo->MutualCxTotal == 1) { - ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ - if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns);//load min thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - - ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ - if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:MS_TOTAL_CX_MAP_MAX failed", + tag, __func__); + logError(1, "...ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, thresholds_min, thresholds_max); /* check the limits */ + ret = checkLimitsMapTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, + thresholds_max);//check the limits if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap MS TOTAL CX TEST failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS TOTAL CX MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s %s:MS TOTAL CX TEST failed ", + tag, __func__); + logError(1, "COUNT:%d\n", ret); + logError(0, "%s MS TOTAL CX MIN MAX ", tag); + logError(0, "TEST:FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s MS TOTAL CX MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s MS TOTAL CX MIN MAX TEST", tag); + logError(0, ":OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); + thresholds_max = NULL; } else - logError(0, "%s MS TOTAL CX MIN MAX TEST:.................SKIPPED\n\n", tag); + logError(0, "%s MS TOTAL CX MIN MAX TEST:SKIPPED\n\n", + tag); + logError(0, "%s MS TOTAL CX ADJ TEST:\n", tag); if (todo->MutualCxTotalAdj == 1) { - /* MS TOTAL CX ADJ HORIZ */ + //MS TOTAL CX ADJ HORIZ logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:\n", tag); - /* thresholds_max = NULL; */ - ret = computeAdjHorizTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, &total_adjhor); + //thresholds_max = NULL; + ret = computeAdjHorizTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + &total_adjhor); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjHoriz failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - logError(0, "%s MS TOTAL CX ADJ HORIZ computed!\n", tag); - - ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); - if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node - 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(0, "%s MS TOTAL CX ADJ HORIZ computed!\n", + tag); + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_TOTAL_CX_ADJH_MAP_MAX "); + logError(1, "failed...RROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdjTotal(total_adjhor, msCompData.header.force_node, msCompData.header.sense_node - 1, thresholds_max); + ret = checkLimitsMapAdjTotal(total_adjhor, + msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJH failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj MS TOTAL "); + logError(1, "CX ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................OK\n\n", tag); + } else { + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(total_adjhor); + total_adjhor = NULL; - /* MS TOTAL CX ADJ VERT */ + //MS TOTAL CX ADJ VERT logError(0, "%s MS TOTAL CX ADJ VERT TEST:\n", tag); - ret = computeAdjVertTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, &total_adjvert); + ret = computeAdjVertTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + &total_adjvert); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjVert failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - logError(0, "%s MS TOTAL CX ADJ VERT computed!\n", tag); - - ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); - if (ret < 0 || (trows != msCompData.header.force_node - 1 || tcolumns != msCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(0, "%s MS TOTAL CX ADJ VERT computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || + (trows != msCompData.header.force_node - 1 + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_TOTAL_CX_ADJV_MAP_MAX failed"); + logError(1, "... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdjTotal(total_adjvert, msCompData.header.force_node - 1, msCompData.header.sense_node - 1, thresholds_max); + ret = checkLimitsMapAdjTotal(total_adjvert, + msCompData.header.force_node - 1, + msCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJV failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj MS TOTAL "); + logError(1, "CX ADJV failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................FAIL\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s MS TOTAL CX ADJ VERT TEST:.................OK\n", tag); + } else { + logError(0, "%s MS TOTAL CX ADJ VERT ", tag); + logError(0, "TEST:.................OK\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(total_adjvert); - } else - logError(0, "%s MS TOTAL CX ADJ TEST:.................SKIPPED\n", tag); + total_adjvert = NULL; + } else { + logError(0, "%s MS TOTAL CX ADJ ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } kfree(total_cx); + total_cx = NULL; } else - logError(0, "%s MS TOTAL CX TEST:.................SKIPPED\n", tag); + logError(0, "%s MS TOTAL CX TEST:.................SKIPPED\n", + tag); + + if ((todo->MutualKeyCx1 + | todo->MutualKeyCx2 | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, + stop_on_fail, + todo); + if (ret < 0) { + count_fail += 1; + logError(1, "%s production_test_data: ", tag); + logError(1, "production_test_ms_key_cx failed..."); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s MS CX testes finished!", tag); + logError(0, ".................FAILED "); + logError(0, "fails_count = %d\n\n", count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:.................SKIPPED\n", + tag); - kfree(msCompData.node_data); + if ((todo->MutualKeyCx1 | todo->MutualKeyCx2 + | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, + stop_on_fail, + todo); - if ((todo->MutualKeyCx1 | todo->MutualKeyCx2 | todo->MutualKeyCxTotal) == 1) { - ret = production_test_ms_key_cx(path_limits, stop_on_fail, todo); if (ret < 0) { count_fail += 1; - logError(1, "%s production_test_data: production_test_ms_key_cx failed... ERROR = %02X\n", tag, ret); - logError(0, "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + logError(1, "%s %s:production_test_ms_key_cx ", + tag, __func__); + logError(1, "failed :%02X\n", ret); + logError(0, "%s MS CX testes finished! ", tag); + logError(0, "fails_count = %d\n\n", count_fail); return ret; } } else - logError(0, "%s MS KEY CX TEST:.................SKIPPED\n", tag); - + logError(0, "%s MS KEY CX TEST:..SKIPPED\n", tag); ERROR: logError(0, "%s\n", tag); if (count_fail == 0) { - logError(0, "%s MS CX testes finished!.................OK\n", tag); + logError(0, "%s MS CX testes finished! OK\n", tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; return OK; } - print_frame_u8("MS Init Data (Cx2) =", array1dTo2d_u8(msCompData.node_data, msCompData.node_data_size, msCompData.header.sense_node), msCompData.header.force_node, msCompData.header.sense_node); - logError(0, "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + print_frame_u8("MS Init Data (Cx2) =", + array1dTo2d_u8(msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + msCompData.header.force_node, + msCompData.header.sense_node); + logError(0, "%s MS CX testes finished! fails_count = %d\n\n", + tag, count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (total_cx != NULL) + kfree(total_cx); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (total_cx != NULL) + kfree(total_cx); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + return ret; } -int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo) { - int ret; int count_fail = 0; int num_keys = 0; @@ -1082,22 +1541,27 @@ int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *tod int *thresholds_max = NULL; int trows, tcolumns; - MutualSenseData msCompData; + struct MutualSenseData msCompData; u16 container; u16 *total_cx = NULL; - /* MS CX TEST */ + + //MS CX TEST logError(0, "%s MS KEY CX Testes are starting...\n", tag); - ret = readMutualSenseCompensationData(MS_KEY, &msCompData); /* read MS compensation data */ + //read MS compensation data + ret = readMutualSenseCompensationData(MS_KEY, &msCompData); if (ret < 0) { - logError(1, "%s production_test_data: readMutualSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "readMutualSenseCompensationData failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } + //the meaningful data are only in the first row, the other rows are + // only a copy of the first one if (msCompData.header.force_node > msCompData.header.sense_node) -/* the meaningful data are only in the first row, the other rows are only a copy of the first one */ num_keys = msCompData.header.force_node; else num_keys = msCompData.header.sense_node; @@ -1105,264 +1569,525 @@ int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *tod logError(0, "%s MS KEY CX1 TEST:\n", tag); if (todo->MutualKeyCx1 == 1) { - ret = parseProductionTestLimits(path_limits, MS_KEY_CX1_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX1_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX1_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_CX1_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } container = (u16) msCompData.cx1; - ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS KEY CX1 TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax MS CX1 failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS KEY CX1 TEST:................", tag); + logError(0, ".FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s MS KEY CX1 TEST:.................OK\n\n", tag); + } else { + logError(0, "%s MS KEY CX1 TEST:................", tag); + logError(0, ".OK\n\n"); + } } else - logError(0, "%s MS KEY CX1 TEST:.................SKIPPED\n\n", tag); + logError(0, "%s MS KEY CX1 TEST:.................SKIPPED\n\n", + tag); kfree(thresholds); + thresholds = NULL; logError(0, "%s MS KEY CX2 TEST:\n", tag); if (todo->MutualKeyCx2 == 1) { - ret = parseProductionTestLimits(path_limits, MS_KEY_CX2_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + //load min thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX2_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_CX2_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, MS_KEY_CX2_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX2_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_CX2_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMap(msCompData.node_data, 1, num_keys, thresholds_min, thresholds_max); /* check the limits */ + //check the limits + ret = checkLimitsMap(msCompData.node_data, + 1, + num_keys, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap MS KEY CX2 failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS KEY CX2 TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap MS KEY CX2 failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS KEY CX2 TEST:................", tag); + logError(0, ".FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s MS KEY CX2 TEST:.................OK\n\n", tag); + } else { + logError(0, "%s MS KEY CX2 TEST:...............", tag); + logError(0, "..OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); + thresholds_max = NULL; } else - logError(0, "%s MS CX2 TEST:.................SKIPPED\n\n", tag); + logError(0, "%s MS CX2 TEST:.................SKIPPED\n\n", + tag); - /* START OF TOTAL CHECK */ + //START OF TOTAL CHECK logError(0, "%s MS KEY TOTAL CX TEST:\n", tag); if (todo->MutualKeyCxTotal == 1) { - ret = computeTotal(msCompData.node_data, msCompData.cx1, 1, num_keys, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + ret = computeTotal(msCompData.node_data, + msCompData.cx1, 1, + num_keys, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); if (ret < 0) { - logError(1, "%s production_test_data: computeTotalCx failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotalCx failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, MS_KEY_TOTAL_CX_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + //load min thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_TOTAL_CX_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "%parseProductionTestLimits "); + logError(1, "MS_KEY_TOTAL_CX_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, MS_KEY_TOTAL_CX_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_TOTAL_CX_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { - logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_TOTAL_CX_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapTotal(total_cx, 1, num_keys, thresholds_min, thresholds_max); /* check the limits */ + //check the limits + ret = checkLimitsMapTotal(total_cx, + 1, + num_keys, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap MS TOTAL KEY CX TEST failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s MS KEY TOTAL CX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap MS TOTAL "); + logError(1, "KEY CX TEST failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s MS KEY TOTAL CX TEST:...........", tag); + logError(0, "......FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s MS KEY TOTAL CX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s MS KEY TOTAL CX TEST:...........", tag); + logError(0, "......OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); + thresholds_max = NULL; kfree(total_cx); - } else - logError(0, "%s MS KEY TOTAL CX TEST:.................SKIPPED\n", tag); + total_cx = NULL; + } else { + logError(0, "%s MS KEY TOTAL CX TEST:.................", tag); + logError(0, "SKIPPED\n"); + } - kfree(msCompData.node_data); ERROR: logError(0, "%s\n", tag); if (count_fail == 0) { - logError(0, "%s MS KEY CX testes finished!.................OK\n", tag); - return OK; - } - print_frame_u8("MS Key Init Data (Cx2) =", array1dTo2d_u8(msCompData.node_data, msCompData.node_data_size, msCompData.header.sense_node), 1, msCompData.header.sense_node); - logError(0, "%s MS Key CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); - return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + logError(0, + "%s MS KEY CX testes finished! OK\n", tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } + print_frame_u8("MS Key Init Data (Cx2) =", + array1dTo2d_u8(msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + 1, + msCompData.header.sense_node); + logError(0, "%s MS Key CX testes finished!..............", tag); + logError(0, "...FAILED fails_count = %d\n\n", count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + if (total_cx != NULL) + kfree(total_cx); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + if (total_cx != NULL) + kfree(total_cx); + return ret; } -int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +int production_test_ss_raw(char *path_limits, + int stop_on_fail, struct TestToDo *todo) { int ret; int count_fail = 0; int rows, columns; - /* short *ssRawFrame = NULL; */ - SelfSenseFrame ssRawFrame; + //short *ssRawFrame = NULL; + struct SelfSenseFrame ssRawFrame; int *thresholds = NULL; int trows, tcolumns; - /* MS SS TEST */ + //MS SS TEST logError(0, "%s\n", tag); logError(0, "%s SS RAW Testes are starting...\n", tag); - /******************************* Self Sense Test *******************************/ - + //******* Self Sense Test ***************/ logError(0, "%s Getting SS Frame...\n", tag); ret = getSSFrame2(SS_TOUCH, &ssRawFrame); - if (ret < 0) { - logError(1, "%s production_test_data: getSSFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); - } + if (ret < 0) { + logError(1, "%s %s:getSSFrame failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } - /* SS RAW (PROXIMITY) FORCE TEST */ + //SS RAW (PROXIMITY) FORCE TEST logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:\n", tag); if (todo->SelfForceRaw == 1 || todo->SelfForceRawGap == 1) { - + //there are no data for the sense + //channels due to the fact that + //the force frame is analized columns = 1; - /* there are no data for the sense channels due to the fact that the force frame is analized */ rows = ssRawFrame.header.force_node; logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:\n", tag); if (todo->SelfForceRaw == 1) { - - ret = parseProductionTestLimits(path_limits, SS_RAW_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_RAW_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", + ERROR_PROD_TEST_DATA); + //return (ret | ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMinMax(ssRawFrame.force_data, rows, columns, thresholds[0], thresholds[1]); + ret = checkLimitsMinMax(ssRawFrame.force_data, + rows, columns, + thresholds[0], + thresholds[1]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax SS RAW (PROXIMITY) FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s %s:checkLimitsMinMax ", + tag, __func__); + logError(1, "failed ERROR COUNT:%d\n", ret); + logError(0, "%s SS RAW (PROXIMITY) FORCE", tag); + logError(0, " MIN MAX TEST:FAIL\n\n"); count_fail += 1; - print_frame_short("SS Raw force frame =", array1dTo2d_short(ssRawFrame.force_data, rows*columns, columns), rows, columns); - if (stop_on_fail) - return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); - } else - logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................OK\n\n", tag); + print_frame_short("SS Raw force frame =", + array1dTo2d_short(ssRawFrame.force_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE MIN MAX TEST:............."); + logError(0, "....OK\n\n"); + } kfree(thresholds); - } else - logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................SKIPPED\n\n", tag); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE MIN MAX TEST:................."); + logError(0, "SKIPPED\n\n"); + } logError(0, "%s\n", tag); logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:\n", tag); if (todo->SelfForceRawGap == 1) { - - ret = parseProductionTestLimits(path_limits, SS_RAW_FORCE_GAP, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_RAW_FORCE_GAP, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_FORCE_GAP failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsGap(ssRawFrame.force_data, rows, columns, thresholds[0]); + ret = checkLimitsGap(ssRawFrame.force_data, + rows, + columns, + thresholds[0]); + if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsGap SS RAW (PROXIMITY) FORCE GAP failed... ERROR = %02X\n", tag, ret); - logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsGap SS RAW "); + logError(1, "(PROXIMITY) FORCE GAP failed..."); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "FAIL\n\n"); count_fail += 1; - print_frame_short("SS Raw force frame =", array1dTo2d_short(ssRawFrame.force_data, rows*columns, columns), rows, columns); - if (stop_on_fail) - return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); - } else - logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................OK\n\n", tag); + print_frame_short("SS Raw force frame =", + array1dTo2d_short(ssRawFrame.force_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "OK\n\n"); + } kfree(thresholds); - } else - logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................SKIPPED\n\n", tag); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "SKIPPED\n\n"); + } kfree(ssRawFrame.force_data); - } else - logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:.................SKIPPED\n\n", tag); + ssRawFrame.force_data = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) FORCE ", tag); + logError(0, "TEST:.................SKIPPED\n\n"); + } logError(0, "%s\n", tag); - /* SS RAW (PROXIMITY) SENSE TEST */ + //SS RAW (PROXIMITY) SENSE TEST logError(0, "%s SS RAW (PROXIMITY) SENSE TEST:\n", tag); if (todo->SelfSenseRaw == 1 || todo->SelfSenseRawGap == 1) { columns = ssRawFrame.header.sense_node; - rows = 1; /* there are no data for the force channels due to the fact that the sense frame is analized */ + // there are no data for the force channels due + // to the fact that the sense frame is analized + rows = 1; logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:\n", tag); if (todo->SelfSenseRaw == 1) { - ret = parseProductionTestLimits(path_limits, SS_RAW_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_RAW_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_SENSE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMinMax(ssRawFrame.sense_data, rows, columns, thresholds[0], thresholds[1]); + ret = checkLimitsMinMax(ssRawFrame.sense_data, + rows, + columns, + thresholds[0], + thresholds[1]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax SS RAW (PROXIMITY) SENSE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................FAIL\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS RAW "); + logError(1, "(PROXIMITY) SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE MIN MAX TEST:............."); + logError(0, "....FAIL\n"); count_fail += 1; - print_frame_short("SS Raw sense frame =", array1dTo2d_short(ssRawFrame.sense_data, rows*columns, columns), rows, columns); - if (stop_on_fail) - return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); - } else - logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................OK\n", tag); + print_frame_short("SS Raw sense frame =", + array1dTo2d_short(ssRawFrame.sense_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE MIN MAX TEST:............."); + logError(0, "....OK\n"); + } kfree(thresholds); - } else - logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................SKIPPED\n", tag); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX", tag); + logError(0, " TEST:.................SKIPPED\n"); + } logError(0, "%s\n", tag); logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:\n", tag); if (todo->SelfSenseRawGap == 1) { - ret = parseProductionTestLimits(path_limits, SS_RAW_SENSE_GAP, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_RAW_SENSE_GAP, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_SENSE_GAP failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsGap(ssRawFrame.sense_data, rows, columns, thresholds[0]); + ret = checkLimitsGap(ssRawFrame.sense_data, + rows, + columns, + thresholds[0]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsGap SS RAW (PROXIMITY) SENSE GAP failed... ERROR = %02X\n", tag, ret); - logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................FAIL\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsGap SS RAW "); + logError(1, "(PROXIMITY) SENSE GAP failed... "); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE GAP TEST:................."); + logError(0, "FAIL\n"); count_fail += 1; - print_frame_short("SS Raw sense frame =", array1dTo2d_short(ssRawFrame.sense_data, rows*columns, columns), rows, columns); - if (stop_on_fail) - return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); - } else - logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................OK\n", tag); + print_frame_short("SS Raw sense frame =", + array1dTo2d_short(ssRawFrame.sense_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE", tag); + logError(0, " GAP TEST:.................OK\n"); + } kfree(thresholds); - } else - logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................SKIPPED\n", tag); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } kfree(ssRawFrame.sense_data); + ssRawFrame.sense_data = NULL; } logError(0, "%s\n", tag); if (count_fail == 0) { - logError(0, "%s SS RAW testes finished!.................OK\n\n", tag); + logError(0, "%s SS RAW testes finished!.................OK\n\n", + tag); return OK; } - logError(0, "%s SS RAW testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + logError(0, "%s SS RAW testes finished!.................", tag); + logError(0, "FAILED fails_count = %d\n\n", count_fail); return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + +ERROR_LIMITS: + if (ssRawFrame.force_data != NULL) + kfree(ssRawFrame.force_data); + if (ssRawFrame.sense_data != NULL) + kfree(ssRawFrame.sense_data); + if (thresholds != NULL) + kfree(thresholds); + return ret; + } -int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo) { - int ret; int count_fail = 0; @@ -1371,13 +2096,14 @@ int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo int *thresholds_min = NULL; int *thresholds_max = NULL; - SelfSenseData ssCompData; + struct SelfSenseData ssCompData; u8 *adjhor = NULL; u8 *adjvert = NULL; u16 container; - int *ix1_w, *ix2_w; + int *ix1_w = NULL; + int *ix2_w = NULL; u16 *total_ix = NULL; u16 *total_cx = NULL; @@ -1386,728 +2112,1546 @@ int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo logError(0, "%s\n", tag); logError(0, "%s SS IX CX testes are starting...\n", tag); - ret = readSelfSenseCompensationData(SS_TOUCH, &ssCompData); /* read the SS compensation data */ + + //read the SS compensation data + ret = readSelfSenseCompensationData(SS_TOUCH, &ssCompData); if (ret < 0) { - logError(1, "%s production_test_data: readSelfSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "readSelfSenseCompensationData failed... ", tag); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } - /*************************** SS FORCE IX *************************************/ - /* SS IX1 FORCE TEST */ + //********************** SS FORCE IX *********************************/ + //SS IX1 FORCE TEST logError(0, "%s SS IX1 FORCE TEST:\n", tag); if (todo->SelfForceIx1 == 1) { - - ret = parseProductionTestLimits(path_limits, SS_IX1_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_IX1_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } container = (u16) ssCompData.f_ix1; - ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + + //check the limits + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax SS IX1 FORCE TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax "); + logError(1, "SS IX1 FORCE TEST failed... "); + logError(1, "ERROR COUNT = %d\n", ret); count_fail += 1; if (stop_on_fail) goto ERROR; } else - logError(0, "%s SS IX1 FORCE TEST:.................OK\n\n", tag); - } else - logError(0, "%s SS IX1 FORCE TEST:.................SKIPPED\n\n", tag); + logError(0, "%s SS IX1 FORCE TEST:......OK\n\n", tag); + } kfree(thresholds); - /* SS IX2 FORCE TEST */ + thresholds = NULL; + //SS IX2 FORCE TEST logError(0, "%s SS IX2 FORCE MIN MAX TEST:\n", tag); if (todo->SelfForceIx2 == 1) { - ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits"); + logError(1, "SS_IX2_FORCE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMap(ssCompData.ix2_fm, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMap(ssCompData.ix2_fm, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.........."); + logError(0, "FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.....", tag); + logError(0, "OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................SKIPPED\n\n", tag); + thresholds_max = NULL; + } else { + logError(0, "%s SS IX2 FORCE MIN MAX TEST:...........", tag); + logError(0, "KIPPED\n\n"); + } logError(0, "%s SS IX2 FORCE ADJ TEST:\n", tag); if (todo->SelfForceIx2Adj == 1) { - /* SS IX2 FORCE ADJV TEST */ + //SS IX2 FORCE ADJV TEST logError(0, "%s SS IX2 FORCE ADJVERT TEST:\n", tag); - ret = computeAdjVert(ssCompData.ix2_fm, ssCompData.header.force_node, 1, &adjvert); + ret = computeAdjVert(ssCompData.ix2_fm, + ssCompData.header.force_node, + 1, + &adjvert); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjVert SS IX2 FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS IX2 FORCE ADJV "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s SS IX2 FORCE ADJV computed!\n", tag); - ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_FORCE_ADJV_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); - /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdj(adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS IX2 FORCE ADJV TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE failed... "); + logError(0, "FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS IX2 FORCE ADJV TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS IX2 FORCE ADJV TEST:", tag); + logError(0, ".................OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(adjvert); + adjvert = NULL; - } else - logError(0, "%s SS IX2 FORCE ADJ TEST:.................SKIPPED\n\n", tag); + } else { + logError(0, "%s SS IX2 FORCE ADJ TEST:", tag); + logError(0, ".................SKIPPED\n\n"); + } - /* SS TOTAL FORCE IX */ + //SS TOTAL FORCE IX logError(0, "%s SS TOTAL IX FORCE TEST:\n", tag); if (todo->SelfForceIxTotal == 1 || todo->SelfForceIxTotalAdj == 1) { logError(0, "%s Reading TOTAL IX FORCE Weights...\n", tag); - ret = parseProductionTestLimits(path_limits, SS_IX1_FORCE_W, &ix1_w, &trows, &tcolumns); /* load the IX1 weight */ + + //load the IX1 weight + ret = parseProductionTestLimits(path_limits, + SS_IX1_FORCE_W, + &ix1_w, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_W failed... ERROR %02X\n", + tag, ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } - ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_W, &ix2_w, &trows, &tcolumns); /* load the IX2 weight */ + //load the IX2 weight + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_W, + &ix2_w, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_W failed... ERROR %02X\n", + tag, ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } - logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", tag, *ix1_w, *ix2_w); - - ret = computeTotal(ssCompData.ix2_fm, ssCompData.f_ix1, ssCompData.header.force_node, 1, *ix1_w, *ix2_w, &total_ix); + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", + tag, *ix1_w, *ix2_w); + ret = computeTotal(ssCompData.ix2_fm, ssCompData.f_ix1, + ssCompData.header.force_node, + 1, + *ix1_w, + *ix2_w, + &total_ix); if (ret < 0) { - logError(1, "%s production_test_data: computeTotal Ix Force failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Ix Force failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } + kfree(ix1_w); + ix1_w = NULL; + kfree(ix2_w); + ix2_w = NULL; + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:\n", tag); if (todo->SelfForceIxTotal == 1) { - ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapTotal(total_ix, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapTotal(total_ix, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX FORCE"); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL IX FORCE MIN MAX ", + tag); + logError(0, "TEST:.................FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS TOTAL IX FORCE MIN MAX ", + tag); + logError(0, "TEST:.................OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................SKIPPED\n", tag); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } logError(0, "%s SS TOTAL IX FORCE ADJ TEST:\n", tag); if (todo->SelfForceIxTotalAdj == 1) { - /* SS TOTAL IX FORCE ADJV TEST */ - logError(0, "%s SS TOTAL IX FORCE ADJVERT TEST:\n", tag); - ret = computeAdjVertTotal(total_ix, ssCompData.header.force_node, 1, &total_adjvert); + //SS TOTAL IX FORCE ADJV TEST + logError(0, "%s SS TOTAL IX FORCE ADJVERT TEST:\n", + tag); + ret = computeAdjVertTotal(total_ix, + ssCompData.header.force_node, + 1, + &total_adjvert); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjVert SS TOTAL IX FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS TOTAL IX "); + logError(1, "FORCE ADJV failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - logError(0, "%s SS TOTAL IX FORCE ADJV computed!\n", tag); - - ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_ADJV_MAP_MAX... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(0, "%s SS TOTAL IX FORCE ADJV computed!\n", + tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 + || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_ADJV_MAP_MAX"); + logError(1, "... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdjTotal(total_adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL IX FORCE ADJV TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX "); + logError(1, "FORCE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:", + tag); + logError(0, ".................FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS TOTAL IX FORCE ADJV TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:", + tag); + logError(0, ".................OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(total_adjvert); - } else - logError(0, "%s SS TOTAL IX FORCE ADJ TEST:.................SKIPPED\n", tag); + total_adjvert = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:"); + logError(0, ".................SKIPPED\n"); + } kfree(total_ix); - } else - logError(0, "%s SS TOTAL IX FORCE TEST:.................SKIPPED\n\n", tag); + total_ix = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE TEST:", tag); + logError(0, ".................SKIPPED\n\n"); + } - /******************* SS SENSE IX **************************/ - /* SS IX1 SENSE TEST */ + + //************** SS SENSE IX *******************/ + //SS IX1 SENSE TEST logError(0, "%s SS IX1 SENSE TEST:\n", tag); if (todo->SelfSenseIx1 == 1) { - - ret = parseProductionTestLimits(path_limits, SS_IX1_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_IX1_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } container = (u16) ssCompData.s_ix1; - ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax SS IX1 SENSE TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS IX1 SENSE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS IX1 SENSE TEST:.................OK\n\n", tag); - } else - logError(0, "%s SS IX1 SENSE TEST:.................SKIPPED\n\n", tag); + } else { + logError(0, "%s SS IX1 SENSE TEST:..............", tag); + logError(0, "...OK\n\n"); + } + } else { + logError(0, "%s SS IX1 SENSE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } kfree(thresholds); - /* SS IX2 SENSE TEST */ + thresholds = NULL; + //SS IX2 SENSE TEST logError(0, "%s SS IX2 SENSE MIN MAX TEST:\n", tag); if (todo->SelfSenseIx2 == 1) { - ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); //load the min thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "S_IX2_SENSE_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMap(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMap(ssCompData.ix2_sn, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS IX2 SENSE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.....", tag); + logError(0, "............FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS IX2 SENSE MIN MAX TEST:", tag); + logError(0, ".................OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................SKIPPED\n\n", tag); + thresholds_max = NULL; + } else { + logError(0, "%s SS IX2 SENSE MIN MAX TEST:..............", tag); + logError(0, "...SKIPPED\n\n"); + } logError(0, "%s SS IX2 SENSE ADJ TEST:\n", tag); if (todo->SelfSenseIx2Adj == 1) { - /* SS IX2 SENSE ADJH TEST */ + //SS IX2 SENSE ADJH TEST logError(0, "%s SS IX2 SENSE ADJHORIZ TEST:\n", tag); - ret = computeAdjHoriz(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, &adjhor); + ret = computeAdjHoriz(ssCompData.ix2_sn, + 1, + ssCompData.header.sense_node, + &adjhor); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjHoriz SS IX2 SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS IX2 SENSE ADJH "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s SS IX2 SENSE ADJ HORIZ computed!\n", tag); - ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdj(adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdj(adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj SS IX2 SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS IX2 SENSE ADJH TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS IX2 SENSE ADJH "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 SENSE ADJH TEST:.......", tag); + logError(0, "..........FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS IX2 SENSE ADJH TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS IX2 SENSE ADJH TEST:........", tag); + logError(0, ".........OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(adjhor); - } else - logError(0, "%s SS IX2 SENSE ADJ TEST:.................SKIPPED\n", tag); + adjhor = NULL; + } else { + logError(0, "%s SS IX2 SENSE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n", tag); + } - /* SS TOTAL IX SENSE */ + //SS TOTAL IX SENSE logError(0, "%s SS TOTAL IX SENSE TEST:\n", tag); if (todo->SelfSenseIxTotal == 1 || todo->SelfSenseIxTotalAdj == 1) { logError(0, "%s Reading TOTAL IX SENSE Weights...\n", tag); - ret = parseProductionTestLimits(path_limits, SS_IX1_SENSE_W, &ix1_w, &trows, &tcolumns); /* load the IX1 weight */ + //load the IX1 weight + ret = parseProductionTestLimits(path_limits, + SS_IX1_SENSE_W, + &ix1_w, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_W failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_W, &ix2_w, &trows, &tcolumns); /* load the IX2 weight */ + //load the IX2 weight + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_W, + &ix2_w, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_W failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", tag, *ix1_w, *ix2_w); + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", + tag, *ix1_w, *ix2_w); - ret = computeTotal(ssCompData.ix2_sn, ssCompData.s_ix1, 1, ssCompData.header.sense_node, *ix1_w, *ix2_w, &total_ix); + ret = computeTotal(ssCompData.ix2_sn, + ssCompData.s_ix1, + 1, + ssCompData.header.sense_node, + *ix1_w, + *ix2_w, + &total_ix); if (ret < 0) { - logError(1, "%s production_test_data: computeTotal Ix Sense failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Ix Sense "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } + kfree(ix1_w); + ix1_w = NULL; + kfree(ix2_w); + ix2_w = NULL; + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:\n", tag); + //load the min thresholds if (todo->SelfSenseIxTotal == 1) { - ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || + tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapTotal(total_ix, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapTotal(total_ix, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX SENSE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX SENSE"); + logError(1, " failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", + tag); + logError(0, "TEST:.................FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", + tag); + logError(0, "TEST:.................OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................SKIPPED\n", tag); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:\n", tag); if (todo->SelfSenseIxTotalAdj == 1) { - /* SS TOTAL IX SENSE ADJH TEST */ - logError(0, "%s SS TOTAL IX SENSE ADJHORIZ TEST:\n", tag); - ret = computeAdjHorizTotal(total_ix, 1, ssCompData.header.sense_node, &total_adjhor); + //SS TOTAL IX SENSE ADJH TEST + logError(0, "%s SS TOTAL IX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(total_ix, + 1, + ssCompData.header.sense_node, + &total_adjhor); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjHoriz SS TOTAL IX SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS TOTAL "); + logError(1, "IXSENSE ADJH failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - logError(0, "%s SS TOTAL IX SENSE ADJ HORIZ computed!\n", tag); - - ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(0, "%s SS TOTAL IX SENSE ADJ HORIZ ", tag); + logError(0, "computed!\n"); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdjTotal(total_adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj SS TOTAL IX SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL IX SENSE ADJH TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS TOTAL "); + logError(1, "IX SENSE ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL IX SENSE ADJH ", tag); + logError(0, "TEST:.................FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS TOTAL IX SENSE ADJH TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS TOTAL IX SENSE ADJH ", tag); + logError(0, "TEST:.................OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(total_adjhor); - } else - logError(0, "%s SS TOTAL IX SENSE ADJ TEST:.................SKIPPED\n", tag); + total_adjhor = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:.....", tag); + logError(0, "............SKIPPED\n"); + } kfree(total_ix); - } else - logError(0, "%s SS TOTAL IX SENSE TEST:.................SKIPPED\n", tag); + total_ix = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE TEST:............", tag); + logError(0, ".....SKIPPED\n"); + } - /*********************** SS SENSE CX ******************************/ - /* SS CX1 FORCE TEST */ + //************************ SS SENSE CX *******************************/ + //SS CX1 FORCE TEST logError(0, "%s SS CX1 FORCE TEST:\n", tag); if (todo->SelfForceCx1 == 1) { - ret = parseProductionTestLimits(path_limits, SS_CX1_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_CX1_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_CX1_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX1_FORCE_MIN_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } + //check the limits container = (u16) ssCompData.f_cx1; - ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax SS CX1 FORCE TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS CX1 FORCE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); count_fail += 1; - /* if (stop_on_fail) return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); */ if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS CX1 FORCE TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS CX1 FORCE TEST:.............", tag); + logError(0, "....OK\n\n"); + } kfree(thresholds); - } else - logError(0, "%s SS CX1 FORCE TEST:.................SKIPPED\n\n", tag); + thresholds = NULL; + } else { + logError(0, "%s SS CX1 FORCE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } - /* SS CX2 FORCE TEST */ + //SS CX2 FORCE TEST logError(0, "%s SS CX2 FORCE MIN MAX TEST:\n", tag); if (todo->SelfForceCx2 == 1) { - ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "SS_CX2_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMap(ssCompData.cx2_fm, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMap(ssCompData.cx2_fm, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS CX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "%checkLimitsMap SS CX2 FORCE "); + logError(1, "%failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.....", tag); + logError(0, "............FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS CX2 FORCE MIN MAX TEST:......", tag); + logError(0, "...........OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................SKIPPED\n", tag); + thresholds_max = NULL; + } else { + logError(0, "%s SS CX2 FORCE MIN MAX TEST:..............", tag); + logError(0, "...SKIPPED\n"); + } logError(0, "%s SS CX2 FORCE ADJ TEST:\n", tag); if (todo->SelfForceCx2Adj == 1) { - /* SS CX2 FORCE ADJV TEST */ + //SS CX2 FORCE ADJV TEST logError(0, "%s SS CX2 FORCE ADJVERT TEST:\n", tag); - ret = computeAdjVert(ssCompData.cx2_fm, ssCompData.header.force_node, - 1, &adjvert); /* comepute the ADJV for CX2 FORCE */ + //comepute the ADJV for CX2 FORCE + ret = computeAdjVert(ssCompData.cx2_fm, + ssCompData.header.force_node, + 1, + &adjvert); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjVert SS CX2 FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS CX2 FORCE ADJV "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s SS CX2 FORCE ADJV computed!\n", tag); - ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_FORCE_ADJV_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdj(adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS CX2 FORCE ADJV TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 FORCE ADJV TEST:......", tag); + logError(0, "...........FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS CX2 FORCE ADJV TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS CX2 FORCE ADJV TEST:.....", tag); + logError(0, "............OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(adjvert); - } else - logError(0, "%s SS CX2 FORCE ADJ TEST:.................SKIPPED\n\n", tag); - - /* SS TOTAL CX FORCE */ + adjvert = NULL; + } else { + logError(0, "%s SS CX2 FORCE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + //SS TOTAL CX FORCE logError(0, "%s SS TOTAL CX FORCE TEST:\n", tag); if (todo->SelfForceCxTotal == 1 || todo->SelfForceCxTotalAdj == 1) { - ret = computeTotal(ssCompData.cx2_fm, ssCompData.f_cx1, ssCompData.header.force_node, 1, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + ret = computeTotal(ssCompData.cx2_fm, + ssCompData.f_cx1, + ssCompData.header.force_node, + 1, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); if (ret < 0) { - logError(1, "%s production_test_data: computeTotal Cx Force failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Cx Force failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); return (ret | ERROR_PROD_TEST_DATA); } logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:\n", tag); + //load the min thresholds if (todo->SelfForceCxTotal == 1) { - ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapTotal(total_cx, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapTotal(total_cx, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS TOTAL FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL FORCE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL FORCE "); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL FORCE MIN MAX ", tag); + logError(0, "TEST:.................FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS TOTAL FORCE MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS TOTAL FORCE MIN MAX ", tag); + logError(0, "TEST:.................OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:.................SKIPPED\n", tag); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } - /* SS TOTAL CX FORCE ADJV TEST */ + //SS TOTAL CX FORCE ADJV TEST logError(0, "%s SS TOTAL CX FORCE ADJ TEST:\n", tag); if (todo->SelfForceCxTotalAdj == 1) { - logError(0, "%s SS TOTAL CX FORCE ADJVERT TEST:\n", tag); - ret = computeAdjVertTotal(total_cx, ssCompData.header.force_node, 1, &total_adjvert); /* comepute the ADJV for CX2 FORCE */ + logError(0, "%s SS TOTAL CX FORCE ADJVERT ", tag); + logError(0, "TEST:\n"); + + //comepute the ADJV for CX2 FORCE + ret = computeAdjVertTotal(total_cx, + ssCompData.header.force_node, + 1, + &total_adjvert); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjVert SS TOTAL CX FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS TOTAL CX FORCE"); + logError(1, " ADJV failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - logError(0, "%s SS TOTAL CX FORCE ADJV computed!\n", tag); - - ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(0, "%s SS TOTAL CX FORCE ADJV computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 + || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdjTotal(total_adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS TOTAL CX FORCE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL CX FORCE ADJV TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL CX FORCE"); + logError(1, " failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL CX FORCE ADJV ", tag); + logError(0, "TEST:.................FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS TOTAL CX FORCE ADJV TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS TOTAL CX FORCE ADJV ", tag); + logError(0, "TEST:.................OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(total_adjvert); + total_adjvert = NULL; - } else - logError(0, "%s SS TOTAL CX FORCE ADJ TEST:.................SKIPPED\n", tag); + } else { + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:......", + tag); + logError(0, "..........SKIPPED\n"); + } kfree(total_cx); - } else - logError(0, "%s SS TOTAL CX FORCE TEST:.................SKIPPED\n\n", tag); + total_cx = NULL; + } else { + logError(0, "%s SS TOTAL CX FORCE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + - /* ********************** SS SENSE CX ************************************/ - /* SS CX1 SENSE TEST */ + //**************** SS SENSE CX **************************************/ + //SS CX1 SENSE TEST logError(0, "%s SS CX1 SENSE TEST:\n", tag); if (todo->SelfSenseCx1 == 1) { - - ret = parseProductionTestLimits(path_limits, SS_CX1_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + ret = parseProductionTestLimits(path_limits, + SS_CX1_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); if (ret < 0 || (trows != 1 || tcolumns != 2)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_CX1_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX1_SENSE_MIN_MAX failed"); + logError(1, "... ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } container = (u16) ssCompData.s_cx1; - ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + //check the limits + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMinMax SS CX1 SENSE TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS CX1 SENSE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS CX1 SENSE TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS CX1 SENSE TEST:..............", tag); + logError(0, "...OK\n\n"); + } kfree(thresholds); - } else - logError(0, "%s SS CX1 SENSE TEST:.................SKIPPED\n\n", tag); + thresholds = NULL; + } else { + logError(0, "%s SS CX1 SENSE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + - /* SS CX2 SENSE TEST */ + //SS CX2 SENSE TEST logError(0, "%s SS CX2 SENSE MIN MAX TEST:\n", tag); if (todo->SelfSenseCx2 == 1) { - ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_SENSE_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMap(ssCompData.cx2_sn, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMap(ssCompData.cx2_sn, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS CX2 SENSE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS CX2 SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 SENSE MIN MAX TEST:......", tag); + logError(0, "...........FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS CX2 SENSE MIN MAX TEST:", tag); + logError(0, ".................OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................SKIPPED\n", tag); - + thresholds_max = NULL; + } else { + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.........", tag); + logError(0, "........SKIPPED\n"); + } logError(0, "%s SS CX2 SENSE ADJ TEST:\n", tag); if (todo->SelfSenseCx2Adj == 1) { - /* SS CX2 SENSE ADJH TEST */ + //SS CX2 SENSE ADJH TEST logError(0, "%s SS CX2 SENSE ADJHORIZ TEST:\n", tag); - ret = computeAdjHoriz(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, &adjhor); + ret = computeAdjHoriz(ssCompData.cx2_sn, + 1, + ssCompData.header.sense_node, + &adjhor); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjHoriz SS CX2 SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS CX2 SENSE ADJH "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s SS CX2 SENSE ADJH computed!\n", tag); - ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdj(adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdj(adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj SS CX2 SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS CX2 SENSE ADJH TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS CX2 SENSE ADJH "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 SENSE ADJH TEST:.........", tag); + logError(0, "........FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS CX2 SENSE ADJH TEST:.................OK\n", tag); + } else { + logError(0, "%s SS CX2 SENSE ADJH TEST:.........", tag); + logError(0, "........OK\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(adjhor); - } else - logError(0, "%s SS CX2 SENSE ADJ TEST:.................SKIPPED\n\n", tag); + adjhor = NULL; + } else { + logError(0, "%s SS CX2 SENSE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } - /* SS TOTAL CX SENSE */ + //SS TOTAL CX SENSE logError(0, "%s SS TOTAL CX SENSE TEST:\n", tag); if (todo->SelfSenseCxTotal == 1 || todo->SelfSenseCxTotalAdj == 1) { - ret = computeTotal(ssCompData.cx2_sn, ssCompData.s_cx1, 1, ssCompData.header.sense_node, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + ret = computeTotal(ssCompData.cx2_sn, + ssCompData.s_cx1, + 1, + ssCompData.header.sense_node, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); if (ret < 0) { - logError(1, "%s production_test_data: computeTotal Cx Sense failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Cx Sense failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:\n", tag); + //load the min thresholds if (todo->SelfSenseCxTotal == 1) { - ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - - ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapTotal(total_cx, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapTotal(total_cx, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMap SS TOTAL CX SENSE failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "heckLimitsMap SS TOTAL CX SENSE "); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL CX SENSE MIN ", tag); + logError(0, "MAX TEST:................."); + logError(0, "FAIL\n\n"); count_fail += 1; - if (stop_on_fail) - goto ERROR; - } else - logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................OK\n\n", tag); + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL CX SENSE MIN ", tag); + logError(0, "MAX TEST:................OK\n\n"); + } kfree(thresholds_min); + thresholds_min = NULL; kfree(thresholds_max); - } else - logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................SKIPPED\n", tag); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } - /* SS TOTAL IX SENSE ADJH TEST */ + + //SS TOTAL IX SENSE ADJH TEST logError(0, "%s SS TOTAL CX SENSE ADJ TEST:\n", tag); if (todo->SelfSenseCxTotalAdj == 1) { - logError(0, "%s SS TOTAL CX SENSE ADJHORIZ TEST:\n", tag); - ret = computeAdjHorizTotal(total_cx, 1, ssCompData.header.sense_node, &total_adjhor); + logError(0, "%s SS TOTAL CX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(total_cx, + 1, + ssCompData.header.sense_node, + &total_adjhor); if (ret < 0) { - logError(1, "%s production_test_data: computeAdjHoriz SS TOTAL CX SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS TOTAL CX "); + logError(1, "SENSE ADJH failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - logError(0, "%s SS TOTAL CX SENSE ADJ HORIZ computed!\n", tag); - - ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ - if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { - logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); - return (ret | ERROR_PROD_TEST_DATA); + logError(0, "%s SS TOTAL CX SENSE ADJ HORIZ ", tag); + logError(0, "computed!\n"); + + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 || + tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; } - ret = checkLimitsMapAdjTotal(total_adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); if (ret != OK) { - logError(1, "%s production_test_data: checkLimitsMapAdj SS TOTAL CX SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); - logError(0, "%s SS TOTAL CX SENSE ADJH TEST:.................FAIL\n\n", tag); + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS TOTAL "); + logError(1, "CX SENSE ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL CX SENSE ADJH ", tag); + logError(0, "TEST:...FAIL\n\n"); count_fail += 1; if (stop_on_fail) goto ERROR; - } else - logError(0, "%s SS TOTAL CX SENSE ADJH TEST:.................OK\n\n", tag); + } else { + logError(0, "%s SS TOTAL CX SENSE ADJH TEST:", + tag); + logError(0, ".................OK\n\n"); + } kfree(thresholds_max); + thresholds_max = NULL; kfree(total_adjhor); - } else - logError(0, "%s SS TOTAL CX SENSE ADJ TEST:.................SKIPPED\n", tag); + total_adjhor = NULL; + } else { + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:.", tag); + logError(0, "SKIPPED\n"); + } kfree(total_cx); + total_cx = NULL; } else - logError(0, "%s SS TOTAL CX SENSE TEST:.................SKIPPED\n", tag); - - /* SS compensation data are no usefull anymore */ + logError(0, "%s SS TOTAL CX SENSE TEST:.....SKIPPED\n", tag); - kfree(ssCompData.ix2_fm); - kfree(ssCompData.ix2_sn); - kfree(ssCompData.cx2_fm); - kfree(ssCompData.cx2_sn); ERROR: logError(0, "%s\n", tag); if (count_fail == 0) { - logError(0, "%s SS IX CX testes finished!.................OK\n\n", tag); - return OK; - } - /* print all kind of data in just one row for readability reason */ - print_frame_u8("SS Init Data Ix2_fm = ", array1dTo2d_u8(ssCompData.ix2_fm, ssCompData.header.force_node, ssCompData.header.force_node), 1, ssCompData.header.force_node); - print_frame_u8("SS Init Data Cx2_fm = ", array1dTo2d_u8(ssCompData.cx2_fm, ssCompData.header.force_node, ssCompData.header.force_node), 1, ssCompData.header.force_node); - print_frame_u8("SS Init Data Ix2_sn = ", array1dTo2d_u8(ssCompData.ix2_sn, ssCompData.header.sense_node, ssCompData.header.sense_node), 1, ssCompData.header.sense_node); - print_frame_u8("SS Init Data Cx2_sn = ", array1dTo2d_u8(ssCompData.cx2_sn, ssCompData.header.sense_node, ssCompData.header.sense_node), 1, ssCompData.header.sense_node); - logError(0, "%s SS IX CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); - return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + kfree(ssCompData.ix2_fm); + ssCompData.ix2_fm = NULL; + kfree(ssCompData.ix2_sn); + ssCompData.ix2_sn = NULL; + kfree(ssCompData.cx2_fm); + ssCompData.cx2_fm = NULL; + kfree(ssCompData.cx2_sn); + ssCompData.cx2_sn = NULL; + logError(0, "%s SS IX CX testes finished!........OK\n\n", tag); + ret = OK; + } else { + //print all kind of data in just one row for readability reason + print_frame_u8("SS Init Data Ix2_fm = ", + array1dTo2d_u8(ssCompData.ix2_fm, + ssCompData.header.force_node, + ssCompData.header.force_node), + 1, + ssCompData.header.force_node); + print_frame_u8("SS Init Data Cx2_fm = ", + array1dTo2d_u8(ssCompData.cx2_fm, + ssCompData.header.force_node, + ssCompData.header.force_node), + 1, + ssCompData.header.force_node); + print_frame_u8("SS Init Data Ix2_sn = ", + array1dTo2d_u8(ssCompData.ix2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), + 1, + ssCompData.header.sense_node); + print_frame_u8("SS Init Data Cx2_sn = ", + array1dTo2d_u8(ssCompData.cx2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), + 1, + ssCompData.header.sense_node); + logError(0, "%s SS IX CX testes finished!.................", + tag); + logError(0, "FAILED fails_count = %d\n\n", count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (ix1_w != NULL) + kfree(ix1_w); + if (ix2_w != NULL) + kfree(ix2_w); + if (total_ix != NULL) + kfree(total_ix); + if (total_cx != NULL) + kfree(total_cx); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (ssCompData.ix2_fm != NULL) + kfree(ssCompData.ix2_fm); + if (ssCompData.ix2_sn != NULL) + kfree(ssCompData.ix2_sn); + if (ssCompData.cx2_fm != NULL) + kfree(ssCompData.cx2_fm); + if (ssCompData.cx2_sn != NULL) + kfree(ssCompData.cx2_sn); + ret = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + } + return ret; +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (ix1_w != NULL) + kfree(ix1_w); + if (ix2_w != NULL) + kfree(ix2_w); + if (total_ix != NULL) + kfree(total_ix); + if (total_cx != NULL) + kfree(total_cx); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (ssCompData.ix2_fm != NULL) + kfree(ssCompData.ix2_fm); + if (ssCompData.ix2_sn != NULL) + kfree(ssCompData.ix2_sn); + if (ssCompData.cx2_fm != NULL) + kfree(ssCompData.cx2_fm); + if (ssCompData.cx2_sn != NULL) + kfree(ssCompData.cx2_sn); + return ret; } -int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo) +int production_test_data(char *path_limits, int stop_on_fail, + struct TestToDo *todo) { int res = OK, ret; if (todo == NULL) { - logError(1, "%s production_test_data: No TestToDo specified!! ERROR = %02X\n", tag, (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA)); + logError(1, "%s %s: ", tag, __func__); + logError(1, "No TestToDo specified!! "); + logError(1, "ERROR = %02X\n", + (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA)); return (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA); } logError(0, "%s DATA Production test is starting...\n", tag); - ret = production_test_ms_raw(path_limits, stop_on_fail, todo); res |= ret; if (ret < 0) { - logError(1, "%s production_test_data: production_test_ms_raw failed... ERROR = %02X\n", tag, ret); + logError(1, "%s %s: ", tag, __func__); + logError(1, "production_test_ms_raw failed... "); + logError(1, "ERROR = %02X\n", ret); if (stop_on_fail == 1) goto END; } ret = production_test_ms_cx(path_limits, stop_on_fail, todo); res |= ret; - if (res < 0) { - logError(1, "%s production_test_data: production_test_ms_cx failed... ERROR = %02X\n", tag, ret); + if (ret < 0) { + logError(1, "%s %s: ", tag, __func__); + logError(1, "production_test_ms_cx failed... "); + logError(1, "ERROR = %02X\n", ret); if (stop_on_fail == 1) goto END; } @@ -2115,7 +3659,9 @@ int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo) ret = production_test_ss_raw(path_limits, stop_on_fail, todo); res |= ret; if (ret < 0) { - logError(1, "%s production_test_data: production_test_ss_raw failed... ERROR = %02X\n", tag, ret); + logError(1, "%s %s: ", tag, __func__); + logError(1, "production_test_ss_raw failed... "); + logError(1, "ERROR = %02X\n", ret); if (stop_on_fail == 1) goto END; } @@ -2123,7 +3669,9 @@ int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo) ret = production_test_ss_ix_cx(path_limits, stop_on_fail, todo); res |= ret; if (ret < 0) { - logError(1, "%s production_test_data: production_test_ss_ix_cx failed... ERROR = %02X\n", tag, ret); + logError(1, "%s %s: ", tag, __func__); + logError(1, "production_test_ss_ix_cx failed... "); + logError(1, "ERROR = %02X\n", ret); if (stop_on_fail == 1) goto END; } @@ -2136,52 +3684,50 @@ int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo) return res; } + int save_mp_flag(u32 signature) { - int res = -1, ret; + int res = -1; int i; u8 cmd[6] = {FTS_CMD_WRITE_MP_FLAG, 0x00, 0x00, 0x00, 0x00, 0x00}; u32ToU8(signature, &cmd[2]); - logError(0, "%s Starting Saving Flag with signature = %08X ...\n", tag, signature); + logError(0, "%s Starting Saving Flag with signature = %08X ...\n", + tag, signature); for (i = 0; i < SAVE_FLAG_RETRY && res < OK; i++) { - logError(0, "%s Attempt number %d to save mp flag !\n", tag, i+1); + logError(0, "%s Attempt number %d to save mp flag !\n", + tag, i+1); logError(0, "%s Command write flag sent...\n", tag); res = fts_writeFwCmd(cmd, 6); if (res >= OK) res = save_cx_tuning(); - } - ret = readChipInfo(1); /* need to update the MP Flag */ - if (ret < OK) { - logError(1, "%s save_mp_flag: read chip info ERROR %08X\n", tag, ret); - } - - res |= ret; if (res < OK) { - logError(1, "%s save_mp_flag: ERROR %08X ...\n", tag, res); - return res; + logError(1, "%s %s: ERROR %08X ...\n", tag, __func__, res); + } else { + logError(1, "%s Saving Flag DONE!\n", tag); + res = OK; } - logError(1, "%s Saving Flag DONE!\n", tag); - return OK; + return res; } -int parseProductionTestLimits(char *path, char *label, int **data, int *row, int *column) +int parseProductionTestLimits(char *path, char *label, + int **data, int *row, int *column) { - int find = 0; char *token = NULL; int i = 0; int j = 0; int z = 0; - char *line = NULL; + char *line2 = NULL; + char line[800]; int fd = -1; char *buf = NULL; - int n, size, pointer = 0, ret; + int n, size, pointer = 0, ret = OK; char *data_file = NULL; #ifndef LIMITS_H_FILE const struct firmware *fw = NULL; @@ -2194,131 +3740,174 @@ int parseProductionTestLimits(char *path, char *label, int **data, int *row, int fd = 0; #endif - line = (char *)kmalloc(1800*sizeof(char), GFP_KERNEL); - buf = line; + if (fd != 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } - if (fd == 0) { #ifndef LIMITS_H_FILE - size = fw->size; - data_file = (char *)fw->data; - logError(0, "%s Start to reading %s...\n", tag, path); + size = fw->size; + data_file = (char *)fw->data; + logError(0, "%s Start to reading %s...\n", tag, path); #else - size = LIMITS_SIZE_NAME; - data_file = (char *)(LIMITS_ARRAY_NAME); + size = LIMITS_SIZE_NAME; + data_file = (char *)(LIMITS_ARRAY_NAME); #endif + logError(0, "%s The size of the limits file is %d bytes\n", tag, size); - logError(0, "%s The size of the limits file is %d bytes...\n", tag, size); - - while (find == 0) { - /* start to look for the wanted label */ - if (readLine(&data_file[pointer], &line, size-pointer, &n) < 0) { - find = -1; - break; + while (find == 0) { + //start to look for the wanted label + if (readLine(&data_file[pointer], line, size-pointer, &n) < 0) { + find = -1; + break; + } + pointer += n; + //each header row start with + // *ex. *label, n_row, n_colum + if (line[0] != '*') + continue; + + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, "%s %s:kstrdup ERR %02X\n", + tag, __func__, ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + buf = line2; + line2 += 1; + token = strsep(&line2, ","); + //if the row is the wanted one i + //retrieve rows and columns info + if (strcmp(token, label) == 0) { + find = 1; + token = strsep(&line2, ","); + if (token != NULL) { + ret = kstrtoint(token, 10, row); + if (ret != 0) + return -EINVAL; + logError(0, "%s Row = %d\n", tag, *row); + } else { + logError(1, "%s %s 1:ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; + } + token = strsep(&line2, ","); + if (token != NULL) { + ret = kstrtoint(token, 10, column); + if (ret != 0) + return -EINVAL; + logError(0, "%s Column = %d\n", tag, *column); + } else { + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; } - pointer += n; - if (line[0] == '*') { /* each header row start with * ex. *label,n_row,n_colum */ - buf = line; - line += 1; - token = strsep(&line, ","); - if (strcmp(token, label) == 0) { - /* if the row is the wanted one i retrieve rows and columns info */ - find = 1; - token = strsep(&line, ","); - if (token != NULL) { - sscanf(token, "%d", row); - logError(0, "%s Row = %d\n", tag, *row); - } else { - logError(1, "%s parseProductionTestLimits 1: ERROR %02X\n", tag, ERROR_FILE_PARSE); - /* release_firmware(fw); */ - /* return ERROR_FILE_PARSE; */ - ret = ERROR_FILE_PARSE; - goto END; - } - token = strsep(&line, ","); - if (token != NULL) { - sscanf(token, "%d", column); - logError(0, "%s Column = %d\n", tag, *column); - } else { - logError(1, "%s parseProductionTestLimits 2: ERROR %02X\n", tag, ERROR_FILE_PARSE); - /* release_firmware(fw); */ - /* return ERROR_FILE_PARSE; */ - ret = ERROR_FILE_PARSE; - goto END; - } - - *data = (int *)kmalloc(((*row)*(*column))*sizeof(int), GFP_KERNEL); - /* allocate the memory for containing the data */ - j = 0; - if (*data == NULL) { - logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_ALLOC); - /* release_firmware(fw); */ - /* return ERROR_ALLOC; */ - ret = ERROR_ALLOC; - goto END; - } - - /* start to read the data */ - for (i = 0; i < *row; i++) { - line = buf; - if (readLine(&data_file[pointer], &line, size-pointer, &n) < 0) { - logError(1, "%s parseProductionTestLimits : ERROR %02X\n", tag, ERROR_FILE_READ); - /* release_firmware(fw); */ - /* return ERROR_FILE_READ; */ - ret = ERROR_FILE_READ; - goto END; - } - pointer += n; - token = strsep(&line, ","); - for (z = 0; (z < *column) && (token != NULL); z++) { - sscanf(token, "%d", ((*data) + j)); - j++; - token = strsep(&line, ","); - } - } - if (j == ((*row)*(*column))) { /* check that all the data are read */ - logError(0, "%s READ DONE!\n", tag); - /* release_firmware(fw); */ - /* return OK; */ - ret = OK; - goto END; - } - logError(1, "%s parseProductionTestLimits 3: ERROR %02X\n", tag, ERROR_FILE_PARSE); - /* release_firmware(fw); */ - /* return ERROR_FILE_PARSE; */ - ret = ERROR_FILE_PARSE; + kfree(buf); + buf = NULL; + //allocate memory for containing data + *data = (int *)kmalloc_array(((*row) * (*column)), + sizeof(int), GFP_KERNEL); + j = 0; + if (*data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + //release_firmware(fw); + //return ERROR_ALLOC; + ret = ERROR_ALLOC; + goto END; + } + //start to read the data + for (i = 0; i < *row; i++) { + //line = buf; + if (readLine(&data_file[pointer], line, + size-pointer, &n) < 0) { + logError(1, "%s %s : ERROR %02X\n", + tag, __func__, ERROR_FILE_READ); + //release_firmware(fw); + //return ERROR_FILE_READ + ret = ERROR_FILE_READ; + goto END; + } + pointer += n; + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, "%s %s: kstrdup ", + tag, __func__); + logError(1, "ERROR %02X\n", + ERROR_ALLOC); + ret = ERROR_ALLOC; goto END; } + buf = line2; + token = strsep(&line2, ","); + for (z = 0; + (z < *column) && (token != NULL); z++) { + ret = kstrtoint(token, + 10, + ((*data) + j)); + if (ret != 0) + return -EINVAL; + j++; + token = strsep(&line2, ","); + } + kfree(buf); + buf = NULL; } - + //check that all the data are read + if (j == ((*row) * (*column))) { + logError(0, "%s READ DONE!\n", tag); + //release_firmware(fw); + //return OK; + ret = OK; + goto END; + } + logError(1, "%s %s 3:ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; } - logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_LABEL_NOT_FOUND); - ret = ERROR_LABEL_NOT_FOUND; + kfree(buf); + buf = NULL; + + } + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_LABEL_NOT_FOUND); + ret = ERROR_LABEL_NOT_FOUND; END: + if (buf != NULL) + kfree(buf); #ifndef LIMITS_H_FILE - release_firmware(fw); + release_firmware(fw); #endif - return ret; - - } else { - logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); - return ERROR_FILE_NOT_FOUND; - } + return ret; } -int readLine(char *data, char **line, int size, int *n) +int readLine(char *data, char *line, int size, int *n) { int i = 0; + if (size < 1) - return 1; + return -EINVAL; - while (data[i] != '\n' && i < size) { - *(*line + i) = data[i]; - i++; - } - *n = i+1; - *(*line + i) = '\0'; + while (data[i] != '\n' && i < size) { + line[i] = data[i]; + i++; + } + *n = i + 1; + line[i] = '\0'; return OK; - } + + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.h b/drivers/input/touchscreen/st/fts_lib/ftsTest.h index 93da901c9f4292ff59a285e62765bbb403fbc38a..a2ee28285572308de97c989440e0f32f526acb0a 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTest.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.h @@ -1,91 +1,116 @@ /* - -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS API for MP test * -* * -************************************************************************** -************************************************************************** - -*/ + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_TEST_H +#define __FTS_TEST_H #include "ftsSoftware.h" -#define LIMITS_FILE "stm_fts_production_limits.csv" - -#define WAIT_FOR_FRESH_FRAMES 100 /* ms */ -#define WAIT_AFTER_SENSEOFF 50 /* ms */ -#define TIMEOUT_ITO_TEST_RESULT 200 /* ms */ -#define TIMEOUT_INITIALIZATION_TEST_RESULT 5000 /* ms */ - -/* LABELS PRODUCTION TEST LIMITS FILE */ -#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX" -#define MS_RAW_GAP "MS_RAW_DATA_GAP" -#define MS_CX1_MIN_MAX "MS_TOUCH_ACTIVE_CX1_MIN_MAX" -#define MS_CX2_MAP_MIN "MS_TOUCH_ACTIVE_CX2_MIN" -#define MS_CX2_MAP_MAX "MS_TOUCH_ACTIVE_CX2_MAX" -#define MS_CX2_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" -#define MS_CX2_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" -#define MS_TOTAL_CX_MAP_MIN "MS_TOUCH_ACTIVE_TOTAL_CX_MIN" -#define MS_TOTAL_CX_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_MAX" -#define MS_TOTAL_CX_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" -#define MS_TOTAL_CX_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" -#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX" -#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX" -#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP" -#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP" -#define SS_IX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_FORCE_MIN_MAX" -#define SS_IX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_SENSE_MIN_MAX" -#define SS_CX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_FORCE_MIN_MAX" -#define SS_CX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_SENSE_MIN_MAX" -#define SS_IX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_FORCE_MIN" -#define SS_IX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_FORCE_MAX" -#define SS_IX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_SENSE_MIN" -#define SS_IX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_SENSE_MAX" -#define SS_IX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_VERTICAL" -#define SS_IX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_HORIZONTAL" -#define SS_CX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_FORCE_MIN" -#define SS_CX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_FORCE_MAX" -#define SS_CX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_SENSE_MIN" -#define SS_CX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_SENSE_MAX" -#define SS_CX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" -#define SS_CX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" - -/* TOTAL SS */ -#define SS_TOTAL_IX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MIN" -#define SS_TOTAL_IX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MAX" -#define SS_TOTAL_IX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MIN" -#define SS_TOTAL_IX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MAX" -#define SS_TOTAL_IX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_VERTICAL" -#define SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_HORIZONTAL" -#define SS_TOTAL_CX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MIN" -#define SS_TOTAL_CX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MAX" -#define SS_TOTAL_CX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MIN" -#define SS_TOTAL_CX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MAX" -#define SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" -#define SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" - -/* KEYS */ -#define MS_KEY_RAW_MIN_MAX "MS_KEY_RAW_DATA_MIN_MAX" -#define MS_KEY_CX1_MIN_MAX "MS_KEY_CX1_MIN_MAX" -#define MS_KEY_CX2_MAP_MIN "MS_KEY_CX2_MIN" -#define MS_KEY_CX2_MAP_MAX "MS_KEY_CX2_MAX" -#define MS_KEY_TOTAL_CX_MAP_MIN "MS_KEY_TOTAL_CX_MIN" -#define MS_KEY_TOTAL_CX_MAP_MAX "MS_KEY_TOTAL_CX_MAX" - -/* CONSTANT TOTAL IX */ -#define SS_IX1_FORCE_W "SS_IX1_FORCE_W" -#define SS_IX2_FORCE_W "SS_IX2_FORCE_W" -#define SS_IX1_SENSE_W "SS_IX1_SENSE_W" -#define SS_IX2_SENSE_W "SS_IX2_SENSE_W" - -#define SAVE_FLAG_RETRY 3 - -typedef struct { +#define LIMITS_FILE "stm_fts_production_limits.csv" + +#define WAIT_FOR_FRESH_FRAMES 100 //ms +#define WAIT_AFTER_SENSEOFF 50 //ms + +#define TIMEOUT_ITO_TEST_RESULT 200 //ms +#define TIMEOUT_INITIALIZATION_TEST_RESULT 5000 //ms + +//LABELS PRODUCTION TEST LIMITS FILE +#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX" +#define MS_RAW_GAP "MS_RAW_DATA_GAP" +#define MS_CX1_MIN_MAX "MS_TOUCH_ACTIVE_CX1_MIN_MAX" +#define MS_CX2_MAP_MIN "MS_TOUCH_ACTIVE_CX2_MIN" +#define MS_CX2_MAP_MAX "MS_TOUCH_ACTIVE_CX2_MAX" +#define MS_CX2_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" +#define MS_CX2_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define MS_TOTAL_CX_MAP_MIN "MS_TOUCH_ACTIVE_TOTAL_CX_MIN" +#define MS_TOTAL_CX_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_MAX" +#define MS_TOTAL_CX_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" +#define MS_TOTAL_CX_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX" +#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX" +#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP" +#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP" +#define SS_IX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_FORCE_MIN_MAX" +#define SS_IX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_SENSE_MIN_MAX" +#define SS_CX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_FORCE_MIN_MAX" +#define SS_CX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_SENSE_MIN_MAX" +#define SS_IX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_FORCE_MIN" +#define SS_IX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_FORCE_MAX" +#define SS_IX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_SENSE_MIN" +#define SS_IX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_SENSE_MAX" +#define SS_IX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_VERTICAL" +#define SS_IX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_HORIZONTAL" +#define SS_CX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_FORCE_MIN" +#define SS_CX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_FORCE_MAX" +#define SS_CX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_SENSE_MIN" +#define SS_CX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_SENSE_MAX" +#define SS_CX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define SS_CX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" + +// TOTAL SS +#define SS_TOTAL_IX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MIN" +#define SS_TOTAL_IX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MAX" +#define SS_TOTAL_IX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MIN" +#define SS_TOTAL_IX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MAX" +#define SS_TOTAL_IX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_VERTICAL" +#define SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_HORIZONTAL" +#define SS_TOTAL_CX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MIN" +#define SS_TOTAL_CX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MAX" +#define SS_TOTAL_CX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MIN" +#define SS_TOTAL_CX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MAX" +#define SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" + +//KEYS +#define MS_KEY_RAW_MIN_MAX "MS_KEY_RAW_DATA_MIN_MAX" +#define MS_KEY_CX1_MIN_MAX "MS_KEY_CX1_MIN_MAX" +#define MS_KEY_CX2_MAP_MIN "MS_KEY_CX2_MIN" +#define MS_KEY_CX2_MAP_MAX "MS_KEY_CX2_MAX" +#define MS_KEY_TOTAL_CX_MAP_MIN "MS_KEY_TOTAL_CX_MIN" +#define MS_KEY_TOTAL_CX_MAP_MAX "MS_KEY_TOTAL_CX_MAX" + +//CONSTANT TOTAL IX +#define SS_IX1_FORCE_W "IX1_FORCE_W" +#define SS_IX2_FORCE_W "IX2_FORCE_W" +#define SS_IX1_SENSE_W "IX1_SENSE_W" +#define SS_IX2_SENSE_W "IX2_SENSE_W" + + +#define SAVE_FLAG_RETRY 3 + + +struct TestToDo { int MutualRaw; int MutualRawGap; int MutualCx1; @@ -125,13 +150,14 @@ typedef struct { int SelfSenseCxTotal; int SelfSenseCxTotalAdj; -} TestToDo; +}; int computeAdjHoriz(u8 *data, int row, int column, u8 **result); int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result); int computeAdjVert(u8 *data, int row, int column, u8 **result); int computeAdjVertTotal(u16 *data, int row, int column, u16 **result); -int computeTotal(u8 *data, u8 main, int row, int column, int m, int n, u16 **result); +int computeTotal(u8 *data, u8 main, int row, int column, int m, + int n, u16 **result); int checkLimitsMinMax(short *data, int row, int column, int min, int max); int checkLimitsMap(u8 *data, int row, int column, int *min, int *max); int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max); @@ -143,16 +169,24 @@ int ms_compensation_tuning(void); int ss_compensation_tuning(void); int lp_timer_calibration(void); int save_cx_tuning(void); -int production_test_splited_initialization(int saveToFlash); -int production_test_main(char *pathThresholds, int stop_on_fail, - int saveInit, TestToDo *todo, u32 signature); -int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo); -int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo); -int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo); -int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo); -int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo); -int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_split_initialization(int saveToFlash); +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + struct TestToDo *todo, u32 signature); +int production_test_ms_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ss_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_data(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); int production_test_ms_key_raw(char *path_limits); int save_mp_flag(u32 signature); -int parseProductionTestLimits(char *path, char *label, int **data, int *row, int *column); -int readLine(char *data, char **line, int size, int *n); +int parseProductionTestLimits(char *path, char *label, int **data, + int *row, int *column); +int readLine(char *data, char *line, int size, int *n); +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.c b/drivers/input/touchscreen/st/fts_lib/ftsTime.c index 03a2a39fe10b758298200fbc768c98829b879040..a2ec03a17c92b30c233f0bad95e08e2507b2b7d5 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTime.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.c @@ -1,20 +1,36 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS Utility for mesuring/handling the time * -* * -************************************************************************** -************************************************************************** - -*/ - -#include "ftsCrossCompile.h" -#include "ftsTime.h" +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for mesuring/handling the time * + * * + ************************************************************************** + *************************************************************************** + */ #include #include @@ -40,38 +56,45 @@ #include #include #include -/* #include */ +//#include -void startStopWatch(StopWatch *w) +#include "ftsCrossCompile.h" +#include "ftsTime.h" + + +void startStopWatch(struct StopWatch *w) { w->start = current_kernel_time(); } -void stopStopWatch(StopWatch *w) +void stopStopWatch(struct StopWatch *w) { w->end = current_kernel_time(); } -int elapsedMillisecond(StopWatch *w) +int elapsedMillisecond(struct StopWatch *w) { int result; - result = ((w->end.tv_sec - w->start.tv_sec)*1000) + (w->end.tv_nsec - w->start.tv_nsec) / 1000000; + result = ((w->end.tv_sec - w->start.tv_sec) * 1000) + + (w->end.tv_nsec - w->start.tv_nsec) / 1000000; return result; } -int elapsedNanosecond(StopWatch *w) +int elapsedNanosecond(struct StopWatch *w) { int result; - result = ((w->end.tv_sec - w->start.tv_sec)*1000000000) + (w->end.tv_nsec - w->start.tv_nsec); + result = ((w->end.tv_sec - w->start.tv_sec) * 1000000000) + + (w->end.tv_nsec - w->start.tv_nsec); return result; } -char *timestamp(void) +char *timestamp() { char *result = NULL; - result = (char *)kmalloc((1)*sizeof(char), GFP_KERNEL); + + result = (char *)kmalloc_array(1, sizeof(char), GFP_KERNEL); if (result == NULL) return NULL; result[0] = ' '; @@ -80,5 +103,5 @@ char *timestamp(void) void stdelay(unsigned long ms) { - msleep(ms); + msleep(ms); } diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.h b/drivers/input/touchscreen/st/fts_lib/ftsTime.h index 5eeca6eace9752d10e2370e5d9baf8fefcf1c9b3..2432cf7b266ae667238dced9729f038a99c4e553 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTime.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.h @@ -1,29 +1,53 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS Utility for mesuring/handling the time * -* * -************************************************************************** -************************************************************************** +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for mesuring/handling the time * + * * + ************************************************************************** + ************************************************************************** + * + */ -*/ - -#include "ftsCrossCompile.h" +#ifndef __FTS_TIME_H +#define __FTS_TIME_H #include -typedef struct { +#include "ftsCrossCompile.h" + +struct StopWatch { struct timespec start, end; -} StopWatch; +}; -void startStopWatch(StopWatch *w); -void stopStopWatch(StopWatch *w); -int elapsedMillisecond(StopWatch *w); -int elapsedNanosecond(StopWatch *w); +void startStopWatch(struct StopWatch *w); +void stopStopWatch(struct StopWatch *w); +int elapsedMillisecond(struct StopWatch *w); +int elapsedNanosecond(struct StopWatch *w); char *timestamp(void); void stdelay(unsigned long ms); +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.c b/drivers/input/touchscreen/st/fts_lib/ftsTool.c index 4c5d54f17ea79718e57c64361e1be04e5ae2fa99..a5b4c01fc3122eae0b5df93aba952b700f18c680 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTool.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.c @@ -1,27 +1,37 @@ /* - -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS Utility Functions * -* * -************************************************************************** -************************************************************************** - -*/ - -#include "ftsCompensation.h" -#include "ftsCrossCompile.h" -#include "ftsError.h" -#include "ftsHardware.h" -#include "ftsIO.h" -#include "ftsSoftware.h" -#include "ftsTime.h" -#include "ftsTool.h" -#include "../fts.h" /* needed for the PHONE_KEY define */ + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ #include #include @@ -44,11 +54,21 @@ #include #include -/* static char tag[8]="[ FTS ]\0"; */ +#include "ftsCompensation.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTime.h" +#include "ftsFlash.h" +#include "ftsTool.h" +#include "../fts.h" + +static char tag[8] = "[ FTS ]\0"; static int reset_gpio = GPIO_NOT_DEFINED; static int system_resetted_up; static int system_resetted_down; -extern chipInfo ftsInfo; int readB2(u16 address, u8 *outBuf, int len) { @@ -57,73 +77,81 @@ int readB2(u16 address, u8 *outBuf, int len) int retry = 0; int ret; int event_to_search[3]; - u8 *readEvent = (u8 *)kmalloc(FIFO_EVENT_SIZE*sizeof(u8), GFP_KERNEL); - u8 cmd[4] = { FTS_CMD_REQU_FW_CONF, 0x00, 0x00, (u8)len }; + char *temp = NULL; + u8 *init_outBuf = outBuf; + u16 init_addr = address; + u8 readEvent[FIFO_EVENT_SIZE] = {0}; + u8 cmd[4] = { FTS_CMD_REQU_FW_CONF, 0x00, 0x00, (u8)len }; if (readEvent == NULL) { - logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } - - u16ToU8_be(address, &cmd[1]); - - logError(0, "%s %s", tag, printHex("Command B2 = ", cmd, 4)); - do { - remaining = len; - ret = fts_writeFwCmd(cmd, 4); - if (ret < 0) { - logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_I2C_W); - return ret; - } /* ask to the FW the data */ - logError(0, "%s Command to FW sent!\n", tag); - event_to_search[0] = (int)EVENTID_FW_CONFIGURATION; - - while (remaining > OK) { - event_to_search[1] = (int)((address & 0xFF00)>>8); - event_to_search[2] = (int) (address & 0x00FF); - if (remaining > B2_DATA_BYTES) { - toRead = B2_DATA_BYTES; - remaining -= B2_DATA_BYTES; - } else { - toRead = remaining; - remaining = 0; - } - - ret = pollForEvent(event_to_search, 3, readEvent, GENERAL_TIMEOUT); - if (ret >= OK) { /* start the polling for reading the reply */ - memcpy(outBuf, &readEvent[3], toRead); - retry = 0; - outBuf += toRead; - - } else { - retry += 1; - break; - } - address += B2_DATA_BYTES; + u16ToU8_be(address, &cmd[1]); + temp = printHex("Command B2 = ", cmd, 4); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + do { + remaining = len; + ret = fts_writeFwCmd(cmd, 4); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ret; + } + //ask to the FW the data + logError(0, "%s Command to FW sent!\n", tag); + event_to_search[0] = (int)EVENTID_FW_CONFIGURATION; + while (remaining > OK) { + event_to_search[1] = (int)((address & 0xFF00) >> 8); + event_to_search[2] = (int)(address & 0x00FF); + if (remaining > B2_DATA_BYTES) { + toRead = B2_DATA_BYTES; + remaining -= B2_DATA_BYTES; + } else { + toRead = remaining; + remaining = 0; } - } while (retry < B2_RETRY && retry != 0); - - kfree(readEvent); - if (retry == B2_RETRY) { - logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_TIMEOUT); - return ERROR_TIMEOUT; + ret = pollForEvent(event_to_search, 3, + readEvent, GENERAL_TIMEOUT); + if (ret >= OK) { + //start the polling for reading the reply + memcpy(outBuf, &readEvent[3], toRead); + retry = 0; + outBuf += toRead; + } else { + retry += 1; + break; + } + address += B2_DATA_BYTES; } - logError(0, "%s B2 read %d bytes\n", tag, len); + logError(0, "%s %s:B2 failed...attempt = %d\n", + tag, __func__, retry); + outBuf = init_outBuf; + address = init_addr; + } while (retry < B2_RETRY && retry != 0); + + if (retry == B2_RETRY) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + logError(0, "%s B2 read %d bytes\n", tag, len); - return OK; + return OK; } int readB2U16(u16 address, u8 *outBuf, int byteToRead) { - int remaining = byteToRead; int toRead = 0; int ret; - u8 *buff = (u8 *)kmalloc((B2_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + u8 *buff = (u8 *)kmalloc_array((B2_CHUNK + 1), sizeof(u8), GFP_KERNEL); + if (buff == NULL) { - logError(1, "%s readB2U16: ERROR %02X\n", tag, ERROR_ALLOC); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); return ERROR_ALLOC; } @@ -137,20 +165,19 @@ int readB2U16(u16 address, u8 *outBuf, int byteToRead) } ret = readB2(address, buff, toRead); - if (ret < 0) + if (ret < 0) { + kfree(buff); return ret; + } memcpy(outBuf, buff, toRead); - address += toRead; - outBuf += toRead; - } - kfree(buff); return OK; } + int releaseInformation(void) { int ret; @@ -160,24 +187,475 @@ int releaseInformation(void) event_to_search[0] = (int)EVENTID_RELEASE_INFO; - logError(0, "%s releaseInformation started... Chip INFO:\n", tag); + logError(0, "%s %s: started... Chip INFO:\n", tag, __func__); ret = fts_writeFwCmd(cmd, 1); if (ret < OK) { - logError(1, "%s releaseInformation: ERROR %02X\n", tag, ret); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); return ret; } - ret = pollForEvent(event_to_search, 1, &readEvent[0], RELEASE_INFO_TIMEOUT); - /* start the polling for reading the reply */ + ret = pollForEvent(event_to_search, 1, &readEvent[0], + RELEASE_INFO_TIMEOUT); + //start the polling for reading the reply if (ret < OK) { - logError(1, "%s releaseInformation: ERROR %02X\n", tag, ret); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); return ret; } - logError(0, "%s releaseInformation: Finished!\n", tag, ret); + logError(0, "%s %s: Finished! %d\n", tag, __func__, ret); + return OK; +} + +int lockDownInfo(u8 *data) +{ + int ret; + int i = 0, num_event; + u8 cmd[1] = { FTS_CMD_LOCKDOWN_CMD }; + int event_to_search[3] = {EVENTID_LOCKDOWN_INFO, + EVENT_TYPE_LOCKDOWN, 0x00}; + u8 readEvent[FIFO_EVENT_SIZE]; + + + logError(0, "%s %s:started...\n", tag, __func__); + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ret); + return ret; + } + + + if (LOCKDOWN_CODE_SIZE <= 4) + num_event = 1; + else if (LOCKDOWN_CODE_SIZE % 4 == 0) + num_event = LOCKDOWN_CODE_SIZE / 4; + else + num_event = (LOCKDOWN_CODE_SIZE) / 4 + 1; + + logError(0, "%s %s:num_event = %d\n", tag, __func__, num_event); + for (i = 0; i < num_event; i++) { + ret = pollForEvent(event_to_search, 3, + &readEvent[0], GENERAL_TIMEOUT); + //start the polling for reading the reply + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ret); + return ret; + } + data[i * 4] = readEvent[3]; + data[i * 4 + 1] = readEvent[4]; + data[i * 4 + 2] = readEvent[5]; + data[i * 4 + 3] = readEvent[6]; + event_to_search[2] += 4; + //logError(0, "%02X %02X %02X %02X ", readEvent[3], + //readEvent[4], readEvent[5], readEvent[6]); + } + + logError(0, "%s %s:Finished! %d\n", tag, __func__, ret); return OK; +} + + +int calculateCRC8(u8 *u8_srcBuff, int size, u8 *crc) +{ + u8 u8_remainder; + u8 bit; + int i = 0; + + u8_remainder = 0x00; + logError(0, "%s %s: Start CRC computing...\n", tag, __func__); + + if (size == 0 || u8_srcBuff == NULL) { + logError(1, "Arguments passed not valid!"); + logError(1, "%s %s:Data pointer = NULL ", tag, __func__); + logError(1, "or size = 0 (%d) ERROR %08X\n", + size, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + // Perform modulo-2 division, a byte at a time. + //Bring the next byte into the remainder. + for (i = 0; i < size; i++) { + //Perform modulo-2 division, a bit at a time. + u8_remainder ^= u8_srcBuff[i]; + //Try to divide the current data bit. + for (bit = 8; bit > 0; --bit) { + if (u8_remainder & (0x1 << 7)) + u8_remainder = (u8_remainder << 1) ^ 0x9B; + else + u8_remainder = (u8_remainder << 1); + } + } //The final remainder is the CRC result. + *crc = u8_remainder; + logError(0, "%s %s: CRC value = %02X\n", tag, __func__, *crc); + return OK; +} + +int writeLockDownInfo(u8 *data, int size) +{ + int ret, i, toWrite, retry = 0, offset = size; + u8 cmd[2 + LOCKDOWN_CODE_WRITE_CHUNK] = {FTS_CMD_LOCKDOWN_FILL, 0x00}; + u8 crc = 0; + int event_to_search[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LOCKDOWN_WRITE}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + logError(0, "%s %s: Writing Lockdown code into the IC...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + if (size > LOCKDOWN_CODE_MAX_SIZE) { + logError(1, "%s %s: Lockdown data to write too big! ", + tag, __func__); + logError(1, "%d>%d ERROR %08X\n", + size, LOCKDOWN_CODE_MAX_SIZE, ret); + ret = (ERROR_OP_NOT_ALLOW | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + + temp = printHex("Lockdown Code = ", data, size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + logError(0, "%s %s: Filling FW buffer...\n", tag, __func__); + i = 0; + offset = size; + cmd[0] = FTS_CMD_LOCKDOWN_FILL; + while (offset > 0) { + if (offset > LOCKDOWN_CODE_WRITE_CHUNK) + toWrite = LOCKDOWN_CODE_WRITE_CHUNK; + else + toWrite = offset; + memcpy(&cmd[2], &data[i], toWrite); + cmd[1] = i; + + temp = printHex("Commmand = ", cmd, 2 + toWrite); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 2 + toWrite); + if (ret < OK) { + logError(1, "Unable to write Lockdown data "); + logError(1, "%s %s:Lockdown data at %d ", + tag, __func__, i); + logError(1, "iteration.%08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + i += toWrite;//update the offset + offset -= toWrite; + } + logError(0, "%s %s: Compute 8bit CRC...\n", tag, __func__); + ret = calculateCRC8(data, size, &crc); + if (ret < OK) { + logError(1, "%s %s:Unable to compute CRC..ERROR %08X\n", + tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + cmd[0] = FTS_CMD_LOCKDOWN_WRITE; + cmd[1] = 0x00; + cmd[2] = (u8)size; + cmd[3] = crc; + logError(0, "%s %s: Write Lockdown data...\n", + tag, __func__); + temp = printHex("Commmand = ", cmd, 4); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 4); + if (ret < OK) { + logError(1, "%s%s:Unable to send Lockdown data ", + tag, __func__); + logError(1, "write command%08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + ret = pollForEvent(event_to_search, + 2, + &readEvent[0], + GENERAL_TIMEOUT); + //start the polling for reading the reply + + if (ret < OK) { + logError(1, "%s%s:Cann't find lockdown code ", + tag, __func__); + logError(1, "%write reply %08X\n", ret); + continue; + } + + if (readEvent[2] != 0x00) { + logError(1, "%s %s:Event check FAIL!%02X != 0x00 ", + tag, __func__, readEvent[2]); + logError(1, "%ERR%08X\n", ERROR_LOCKDOWN_CODE); + ret = ERROR_LOCKDOWN_CODE; + continue; + } else { + logError(0, "%s %s:Lockdown Code write DONE!\n", + tag, __func__); + ret = OK; + break; + } + } + +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s: Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +int rewriteLockDownInfo(u8 *data, int size) +{ + int ret, i, toWrite, retry = 0, offset = size; + u8 cmd[2 + LOCKDOWN_CODE_WRITE_CHUNK] = {FTS_CMD_LOCKDOWN_FILL, 0x00}; + u8 crc = 0; + int event_to_search[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LOCKDOWN_WRITE}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + logError(0, "%s %s: ReWriting Lockdown code into the IC start ...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + if (size > LOCKDOWN_CODE_MAX_SIZE) { + logError(1, "%s %s: Lockdown data to write too big! ", + tag, __func__); + logError(1, "%d>%d ERROR %08X\n", + size, LOCKDOWN_CODE_MAX_SIZE, ret); + ret = (ERROR_OP_NOT_ALLOW | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + + temp = printHex("Lockdown Code = ", data, size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + logError(0, "%s %s: Filling FW buffer ...\n", tag, __func__); + i = 0; + offset = size; + cmd[0] = FTS_CMD_LOCKDOWN_FILL; + while (offset > 0) { + if (offset > LOCKDOWN_CODE_WRITE_CHUNK) + toWrite = LOCKDOWN_CODE_WRITE_CHUNK; + else + toWrite = offset; + memcpy(&cmd[2], &data[i], toWrite); + cmd[1] = i; + temp = printHex("Commmand = ", cmd, 2 + toWrite); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 2 + toWrite); + if (ret < OK) { + logError(1, "Unable to rewrite Lockdown data"); + logError(1, "%s %s: at %d iteration ", + tag, __func__, i); + logError(1, "ERROR %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + i += toWrite;//update the offset + offset -= toWrite; + } + logError(0, "%s %s: Compute 8bit CRC...\n", tag, __func__); + ret = calculateCRC8(data, size, &crc); + if (ret < OK) { + logError(1, "%s %s:Unable to compute CRC.. ", + tag, __func__); + logError(1, "ERROR %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + cmd[0] = FTS_CMD_LOCKDOWN_WRITE; + cmd[1] = 0x01; + cmd[2] = (u8)size; + cmd[3] = crc; + + temp = printHex("Commmand = ", cmd, 4); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + logError(0, "%s %s: ReWrite Lockdown data...\n", tag, __func__); + ret = fts_writeFwCmd(cmd, 4); + if (ret < OK) { + logError(1, "Unable to send Lockdown data"); + logError(1, "%s %s:rewrite command... ERROR %08X\n", + tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + //start the polling for reading the reply + ret = pollForEvent(event_to_search, 2, + &readEvent[0], GENERAL_TIMEOUT); + if (ret >= OK) { + if (readEvent[2] < 0x00) { + logError(1, "%s %s:Event check FAIL! ", + tag, __func__); + logError(1, "%02X != 0x00 %08X\n", + readEvent[2], ERROR_LOCKDOWN_CODE); + ret = ERROR_LOCKDOWN_CODE; + continue; + } else { + logError(0, "%s %s: Lockdown Code ", + tag, __func__); + logError(0, "rewrite DONE!\n"); + ret = OK; + break; + } + } else { + logError(1, "Can not find lockdown code write "); + logError(1, "reply event!%s %s: ERROR %08X\n", + tag, __func__, ret); + } + } +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s: Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +int readLockDownInfo(u8 *lockData, int *size) +{ + int ret, retry = 0, toRead = 0, byteToRead; + u8 cmd = FTS_CMD_LOCKDOWN_READ; + int event_to_search[3] = {EVENTID_LOCKDOWN_INFO_READ, -1, 0x00}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + lockData = NULL; + logError(0, "%s %s: Reading Lockdown code from the IC...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + event_to_search[2] = 0x00; + logError(0, "%s %s: Read Lockdown data.(%d attempt)\n", + tag, __func__, retry + 1); + ret = fts_writeFwCmd(&cmd, 1); + + if (ret < OK) { + logError(1, "%s%s:Unable to send Lockdown data ", + tag, __func__); + logError(1, "write CMD %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + //start the polling for reading the reply + ret = pollForEvent(event_to_search, 3, + &readEvent[0], GENERAL_TIMEOUT); + + if (ret < OK) { + logError(1, "Cann't find first lockdown code read"); + logError(1, "%s %s:reply event! ERROR %08X\n", + tag, __func__, ret); + continue; + } + + byteToRead = readEvent[1]; + *size = byteToRead; + logError(0, "%s %s:Lockdown Code size = %d\n", + tag, __func__, *size); + lockData = (u8 *)kmalloc_array((byteToRead), + sizeof(u8), GFP_KERNEL); + if (lockData == NULL) { + logError(1, "%s %s:Unable to allocate lockData %08X\n", + tag, __func__, ERROR_ALLOC); + ret = (ERROR_ALLOC | ERROR_LOCKDOWN_CODE); + continue; + } + while (byteToRead > 0) { + if ((readEvent[1] - readEvent[2]) + > LOCKDOWN_CODE_READ_CHUNK) { + toRead = LOCKDOWN_CODE_READ_CHUNK; + } else { + toRead = readEvent[1] - readEvent[2]; + } + byteToRead -= toRead; + memcpy(&lockData[readEvent[2]], + &readEvent[3], toRead); + event_to_search[2] += toRead; + if (byteToRead <= 0) + continue; + + ret = pollForEvent(event_to_search, + 3, + &readEvent[0], + GENERAL_TIMEOUT); + + //start polling for reading reply + if (ret < OK) { + logError(1, "Can not find lockdow"); + logError(1, "code read reply event "); + logError(1, "%s%s:offset%02X%08X\n", + tag, __func__, event_to_search[2], ret); + ret = (ERROR_ALLOC | ERROR_LOCKDOWN_CODE); + break; + } + } + if (byteToRead != 0) { + logError(1, "%s %s:Read Lockdown code FAIL! ", + tag, __func__); + logError(1, "ERROR %08X\n", ret); + continue; + } else { + logError(0, "%s %s: Lockdown Code read DONE!\n", + tag, __func__); + ret = OK; + temp = printHex("Lockdown Code = ", lockData, *size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + break; + } + } +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always + //renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s:Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; } char *printHex(char *label, u8 *buff, int count) @@ -186,24 +664,24 @@ char *printHex(char *label, u8 *buff, int count) char *result = NULL; offset = strlen(label); - result = (char *)kmalloc(((offset + 3 * count) + 1)*sizeof(char), GFP_KERNEL); + result = (char *)kmalloc_array(((offset + 3 * count) + 1), + sizeof(char), GFP_KERNEL); if (result != NULL) { strlcpy(result, label, sizeof(result)); - - for (i = 0; i < count; i++) { + for (i = 0; i < count; i++) snprintf(&result[offset + i * 3], 4, "%02X ", buff[i]); - } strlcat(result, "\n", sizeof(result)); } return result; } -int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_to_wait) +int pollForEvent(int *event_to_search, int event_bytes, + u8 *readData, int time_to_wait) { int i, find, retry, count_err; int time_to_count; int err_handling = OK; - StopWatch clock; + struct StopWatch clock; u8 cmd[1] = { FIFO_CMD_READONE }; char *temp = NULL; @@ -214,32 +692,46 @@ int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_t time_to_count = time_to_wait / TIMEOUT_RESOLUTION; startStopWatch(&clock); - while (find != 1 && retry < time_to_count && fts_readCmd(cmd, 1, readData, FIFO_EVENT_SIZE) >= 0) { - /* Log of errors */ + while (find != 1 && retry < time_to_count + && fts_readCmd(cmd, 1, readData, FIFO_EVENT_SIZE) >= 0) { + if (readData[0] == EVENTID_ERROR_EVENT) { - logError(1, "%s %s", tag, printHex("ERROR EVENT = ", readData, FIFO_EVENT_SIZE)); + temp = printHex("ERROR EVENT = ", + readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(1, "%s %s", tag, temp); + kfree(temp); count_err++; err_handling = errorHandler(readData, FIFO_EVENT_SIZE); - if ((err_handling&0xF0FF0000) == ERROR_HANDLER_STOP_PROC) { - logError(1, "%s pollForEvent: forced to be stopped! ERROR %08X\n", tag, err_handling); + if ((err_handling & 0xF0FF0000) + == ERROR_HANDLER_STOP_PROC) { + logError(1, "%s %s: forced to be stopped! ", + tag, __func__); + logError(1, "ERROR %08X\n", err_handling); return err_handling; } } else { if (readData[0] != EVENTID_NO_EVENT) { - logError(1, "%s %s", tag, printHex("READ EVENT = ", readData, FIFO_EVENT_SIZE)); + temp = printHex("READ EVENT = ", + readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(1, "%s %s", tag, temp); + kfree(temp); } - if (readData[0] == EVENTID_CONTROL_READY && event_to_search[0] != EVENTID_CONTROL_READY) { - logError(1, "%s pollForEvent: Unmanned Controller Ready Event! Setting reset flags...\n", tag); + if (readData[0] == EVENTID_CONTROL_READY && + event_to_search[0] != EVENTID_CONTROL_READY) { + logError(1, "Unmanned Controller Ready Event!"); + logError(1, "%s %s:Setting reset flags...\n", + tag, __func__); setSystemResettedUp(1); - setSystemResettedDown(1); + setSystemResettedDown(1); } } - find = 1; for (i = 0; i < event_bytes; i++) { - - if (event_to_search[i] != -1 && (int)readData[i] != event_to_search[i]) { + if (event_to_search[i] != -1 + && (int)readData[i] != event_to_search[i]) { find = 0; break; } @@ -250,53 +742,60 @@ int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_t } stopStopWatch(&clock); if ((retry >= time_to_count) && find != 1) { - logError(1, "%s pollForEvent: ERROR %02X\n", tag, ERROR_TIMEOUT); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); return ERROR_TIMEOUT; - } else if (find == 1) { + } + if (find == 1) { temp = printHex("FOUND EVENT = ", readData, FIFO_EVENT_SIZE); if (temp != NULL) logError(0, "%s %s", tag, temp); kfree(temp); - logError(0, "%s Event found in %d ms (%d iterations)! Number of errors found = %d\n", tag, elapsedMillisecond(&clock), retry, count_err); + logError(0, "%s Event found in %d ms (%d iterations)!\n", + tag, elapsedMillisecond(&clock), retry); + logError(0, "Number of errors found = %d\n", count_err); return count_err; } - logError(1, "%s pollForEvent: ERROR %02X\n", tag, ERROR_I2C_R); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); return ERROR_I2C_R; } int flushFIFO(void) { + u8 cmd = FIFO_CMD_FLUSH; - u8 cmd = FIFO_CMD_FLUSH; /* flush the FIFO */ if (fts_writeCmd(&cmd, 1) < 0) { - logError(1, "%s flushFIFO: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } logError(0, "%s FIFO flushed!\n", tag); return OK; - } int fts_disableInterrupt(void) { - u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_DISABLE }; /* disable interrupt */ + //disable interrupt + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_DISABLE }; + u16ToU8_be(IER_ADDR, &cmd[1]); if (fts_writeCmd(cmd, 4) < OK) { - logError(1, "%s fts_disableInterrupt: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } logError(0, "%s Interrupt Disabled!\n", tag); return OK; } + int fts_enableInterrupt(void) { - u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_ENABLE }; /* enable interrupt */ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_ENABLE }; + u16ToU8_be(IER_ADDR, &cmd[1]); - if (fts_writeCmd(cmd, 4) < 0) { - logError(1, "%s fts_enableInterrupt: ERROR %02X\n", tag, ERROR_I2C_W); + if (fts_writeCmd(cmd, 4) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); return ERROR_I2C_W; } logError(0, "%s Interrupt Enabled!\n", tag); @@ -307,11 +806,12 @@ int u8ToU16n(u8 *src, int src_length, u16 *dst) { int i, j; - if (src_length % 2 != 0) { - return 0; - } + if (src_length % 2 != 0) + return -EINVAL; + j = 0; - dst = (u16 *)kmalloc((src_length / 2)*sizeof(u16), GFP_KERNEL); + dst = (u16 *)kmalloc_array((src_length / 2), sizeof(u16), GFP_KERNEL); + for (i = 0; i < src_length; i += 2) { dst[j] = ((src[i+1] & 0x00FF) << 8) + (src[i] & 0x00FF); j++; @@ -335,16 +835,16 @@ int u8ToU16_le(u8 *src, u16 *dst) int u16ToU8n(u16 *src, int src_length, u8 *dst) { int i, j; - dst = (u8 *)kmalloc((2 * src_length)*sizeof(u8), GFP_KERNEL); + + dst = (u8 *)kmalloc_array(2 * src_length, sizeof(u8), GFP_KERNEL); j = 0; for (i = 0; i < src_length; i++) { - dst[j] = (u8) (src[i] & 0xFF00)>>8; + dst[j] = (u8) (src[i] & 0xFF00) >> 8; dst[j+1] = (u8) (src[i] & 0x00FF); j += 2; } return src_length * 2; - } int u16ToU8(u16 src, u8 *dst) @@ -370,7 +870,8 @@ int u16ToU8_le(u16 src, u8 *dst) int u8ToU32(u8 *src, u32 *dst) { - *dst = (u32)(((src[3] & 0x000000FF) << 24) + ((src[2] & 0x000000FF) << 16) + ((src[1] & 0x000000FF) << 8) + (src[0] & 0x000000FF)); + *dst = (u32)(((src[3] & 0xFF) << 24) + ((src[2] & 0xFF) << 16) + + ((src[1] & 0xFF) << 8) + (src[0] & 0xFF)); return 0; } @@ -383,7 +884,8 @@ int u32ToU8(u32 src, u8 *dst) return 0; } -int attempt_function(int(*code)(void), unsigned long wait_before_retry, int retry_count) +int attempt_function(int(*code)(void), unsigned long wait_before_retry, + int retry_count) { int result; int count = 0; @@ -395,16 +897,15 @@ int attempt_function(int(*code)(void), unsigned long wait_before_retry, int retr } while (count < retry_count && result < 0); if (count == retry_count) - return (result | ERROR_TIMEOUT); - else - return result; + result |= ERROR_TIMEOUT; + return result; } void setResetGpio(int gpio) { reset_gpio = gpio; - logError(1, "%s setResetGpio: reset_gpio = %d\n", tag, reset_gpio); + logError(1, "%s %s: reset_gpio = %d\n", tag, __func__, reset_gpio); } int fts_system_reset(void) @@ -414,38 +915,46 @@ int fts_system_reset(void) int res = -1; int i; u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, SYSTEM_RESET_VALUE }; + event_to_search = (int)EVENTID_CONTROL_READY; u16ToU8_be(SYSTEM_RESET_ADDRESS, &cmd[1]); logError(0, "%s System resetting...\n", tag); for (i = 0; i < SYSTEM_RESET_RETRY && res < 0; i++) { - if (reset_gpio == GPIO_NOT_DEFINED) { +#ifndef FTM3_CHIP + res |= fts_warm_boot(); +#endif res = fts_writeCmd(cmd, 4); } else { gpio_set_value(reset_gpio, 0); - msleep(10); + msleep(20); gpio_set_value(reset_gpio, 1); res = OK; } if (res < OK) { - logError(1, "%s fts_system_reset: ERROR %02X\n", tag, ERROR_I2C_W); + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_I2C_W); } else { - res = pollForEvent(&event_to_search, 1, readData, GENERAL_TIMEOUT); + res = pollForEvent(&event_to_search, 1, + readData, GENERAL_TIMEOUT); if (res < OK) { - logError(1, "%s fts_system_reset: ERROR %02X\n", tag, res); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, res); } } } if (res < OK) { - logError(1, "%s fts_system_reset...failed after 3 attempts: ERROR %02X\n", tag, (res | ERROR_SYSTEM_RESET_FAIL)); - return (res | ERROR_SYSTEM_RESET_FAIL); + logError(1, "%s %s:failed after 3 attempts: ERROR %02X\n", + tag, __func__, (res | ERROR_SYSTEM_RESET_FAIL)); + res = (res | ERROR_SYSTEM_RESET_FAIL); + } else { + logError(0, "%s System reset DONE!\n", tag); + system_resetted_down = 1; + system_resetted_up = 1; + res = OK; } - logError(0, "%s System reset DONE!\n", tag); - system_resetted_down = 1; - system_resetted_up = 1; - return OK; - + return res; } int isSystemResettedDown(void) @@ -475,11 +984,11 @@ int senseOn(void) ret = fts_writeFwCmd(cmd, 1); if (ret < OK) { - logError(1, "%s senseOn: ERROR %02X\n", tag, ERROR_SENSE_ON_FAIL); + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_ON_FAIL); return (ret|ERROR_SENSE_ON_FAIL); } - - logError(0, "%s senseOn: SENSE ON\n", tag); + logError(0, "%s %s: SENSE ON\n", tag, __func__); return OK; } @@ -490,13 +999,12 @@ int senseOff(void) ret = fts_writeFwCmd(cmd, 1); if (ret < OK) { - logError(1, "%s senseOff: ERROR %02X\n", tag, ERROR_SENSE_OFF_FAIL); + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_OFF_FAIL); return (ret | ERROR_SENSE_OFF_FAIL); } - - logError(0, "%s senseOff: SENSE OFF\n", tag); + logError(0, "%s %s: SENSE OFF\n", tag, __func__); return OK; - } int keyOn(void) @@ -506,13 +1014,13 @@ int keyOn(void) ret = fts_writeFwCmd(cmd, 1); if (ret < OK) { - logError(1, "%s keyOn: ERROR %02X\n", tag, ERROR_SENSE_ON_FAIL); + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_ON_FAIL); return (ret | ERROR_SENSE_ON_FAIL); } - logError(0, "%s keyOn: KEY ON\n", tag); + logError(0, "%s %s: KEY ON\n", tag, __func__); return OK; - } int keyOff(void) @@ -522,25 +1030,26 @@ int keyOff(void) ret = fts_writeFwCmd(cmd, 1); if (ret < OK) { - logError(1, "%s keyOff: ERROR %02X\n", tag, ERROR_SENSE_OFF_FAIL); + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_OFF_FAIL); return (ret | ERROR_SENSE_OFF_FAIL); } - logError(0, "%s keyOff: KEY OFF\n", tag); + logError(0, "%s %s: KEY OFF\n", tag, __func__); return OK; - } int cleanUp(int enableTouch) { int res; - logError(0, "%s cleanUp: system reset...\n", tag); + logError(0, "%s %s: system reset...\n", tag, __func__); res = fts_system_reset(); if (res < OK) return res; + if (enableTouch) { - logError(0, "%s cleanUp: enabling touches...\n", tag); + logError(0, "%s %s:enabling touches...\n", tag, __func__); res = senseOn(); if (res < OK) return res; @@ -549,19 +1058,18 @@ int cleanUp(int enableTouch) if (res < OK) return res; #endif - logError(0, "%s cleanUp: enabling interrupts...\n", tag); + logError(0, "%s %s:enabling interrupts...\n", tag, __func__); res = fts_enableInterrupt(); if (res < OK) return res; } return OK; - } int checkEcho(u8 *cmd, int size) { int ret, i; - int event_to_search[size+1]; + int event_to_search[size + 1]; u8 readData[FIFO_EVENT_SIZE]; if ((ftsInfo.u32_echoEn & 0x00000001) != ECHO_ENABLED) { @@ -569,60 +1077,181 @@ int checkEcho(u8 *cmd, int size) return OK; } if (size < 1) { - logError(1, "%s checkEcho: Error Size = %d not valid! or ECHO not Enabled! ERROR %08X\n", tag, size, ERROR_OP_NOT_ALLOW); + logError(1, "%s:Error Size = %d not valid!", tag, size); + logError(1, " or ECHO not Enabled!%08X\n", ERROR_OP_NOT_ALLOW); return ERROR_OP_NOT_ALLOW; } - if ((size+2) > FIFO_EVENT_SIZE) - size = FIFO_EVENT_SIZE-2; - /* Echo event EC xx xx xx xx xx xx fifo_status therefore for command - *with more than 6 bytes will echo only the first 6 - */ + if ((size + 2) > FIFO_EVENT_SIZE) + size = FIFO_EVENT_SIZE - 2; + //Echo event EC xx xx xx xx xx xx + //fifo_status therefore for command + //with more than 6 bytes will echo only the first 6 event_to_search[0] = EVENTID_ECHO; - for (i = 1; i <= size; i++) { - event_to_search[i] = cmd[i-1]; - } - ret = pollForEvent(event_to_search, size+1, readData, GENERAL_TIMEOUT); + for (i = 1; i <= size; i++) + event_to_search[i] = cmd[i - 1]; + ret = pollForEvent(event_to_search, size + 1, + readData, GENERAL_TIMEOUT); if (ret < OK) { - logError(1, "%s checkEcho: Echo Event not found! ERROR %02X\n", tag, ret); - return (ret | ERROR_CHECK_ECHO_FAIL); + logError(1, "%s %s:Echo Event not found! ERROR %02X\n", + tag, __func__, ret); + return (ret | ERROR_CHECK_ECHO_FAIL); } logError(0, "%s ECHO OK!\n", tag); - return OK; + ret = OK; + return ret; } -int featureEnableDisable(int on_off, u8 feature) +int featureEnableDisable(int on_off, u32 feature) { int ret; - u8 cmd[2] = { 0x00, feature }; + u8 cmd[5]; if (on_off == FEAT_ENABLE) { cmd[0] = FTS_CMD_FEATURE_ENABLE; - logError(0, "%s featureEnableDisable: Enabling feature %02X ...\n", tag, feature); + logError(0, "%s %s: Enabling feature %08X ...\n", + tag, __func__, feature); } else { cmd[0] = FTS_CMD_FEATURE_DISABLE; - logError(0, "%s featureEnableDisable: Disabling feature %02X ...\n", tag, feature); + logError(0, "%s %s: Disabling feature %08X ...\n", + tag, __func__, feature); } + u32ToU8(feature, &cmd[1]); - ret = fts_writeCmd(cmd, 2); /* not use writeFwCmd because this function can be called also during interrupt enable and should be fast */ + //not use writeFwCmd because this function can be + //called also during interrupt enable and should be fast + ret = fts_writeCmd(cmd, 5); if (ret < OK) { - logError(1, "%s featureEnableDisable: ERROR %02X\n", tag, ret); + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); return (ret | ERROR_FEATURE_ENABLE_DISABLE); } - logError(0, "%s featureEnableDisable: DONE!\n", tag); + logError(0, "%s %s: DONE!\n", tag, __func__); return OK; +} + +int writeNoiseParameters(u8 *noise) +{ + int ret, i; + u8 cmd[2+NOISE_PARAMETERS_SIZE]; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[2] = {EVENTID_NOISE_WRITE, NOISE_PARAMETERS}; + + logError(0, "%s %s: Writing noise parameters to the IC ...\n", + tag, __func__); + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + cmd[0] = FTS_CMD_NOISE_WRITE; + cmd[1] = NOISE_PARAMETERS; + logError(0, "%s %s: Noise parameters = ", tag, __func__); + for (i = 0; i < NOISE_PARAMETERS_SIZE; i++) { + cmd[2 + i] = noise[i]; + logError(0, "%02X", cmd[2 + i]); + } + + logError(0, "\n"); + ret = fts_writeCmd(cmd, NOISE_PARAMETERS_SIZE + 2); + //not use writeFwCmd because this function should be fast + if (ret < OK) { + logError(1, "%s %s:impossible write command... ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + ret = pollForEvent(event_to_search, 2, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(1, "%s %s: polling FIFO ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + if (readData[2] != 0x00) { + logError(1, "%s %s:Event check FAIL! %02X != 0x00 ERROR%02X\n", + tag, __func__, readData[2], ERROR_NOISE_PARAMETERS); + ret = ERROR_NOISE_PARAMETERS; + goto ERROR; + } + + logError(0, "%s %s:DONE!\n", tag, __func__); + ret = OK; +ERROR: + ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_NOISE_PARAMETERS); + } + return ret; } -short **array1dTo2d_short(short *data, int size, int columns) +int readNoiseParameters(u8 *noise) { + int ret, i; + u8 cmd[2]; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[2] = {EVENTID_NOISE_READ, NOISE_PARAMETERS}; + + logError(0, "%s %s:Reading noise parameters from the IC ...\n", + tag, __func__); + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + cmd[0] = FTS_CMD_NOISE_READ; + cmd[1] = NOISE_PARAMETERS; + ret = fts_writeCmd(cmd, 2);//not use writeFwCmd should be fast + if (ret < OK) { + logError(1, "%s %s:impossible write command... ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + ret = pollForEvent(event_to_search, 2, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(1, "%s %s: polling FIFO ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + logError(0, "%s %s: Noise parameters = ", tag, __func__); + for (i = 0; i < NOISE_PARAMETERS_SIZE; i++) { + noise[i] = readData[2 + i]; + logError(0, "%02X ", noise[i]); + } + + logError(0, "\n"); + logError(0, "%s %s: DONE!\n", tag, __func__); + ret = OK; +ERROR: + ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_NOISE_PARAMETERS); + } + return ret; +} + +short **array1dTo2d_short(short *data, int size, int columns) +{ int i; - short **matrix = (short **)kmalloc(((int)(size / columns))*sizeof(short *), GFP_KERNEL); + int count = size / columns; + short **matrix = (short **)kmalloc_array(count, + sizeof(short *), GFP_KERNEL); if (matrix != NULL) { - for (i = 0; i < (int)(size / columns); i++) { - matrix[i] = (short *)kmalloc(columns*sizeof(short), GFP_KERNEL); + for (i = 0; i < count; i++) { + matrix[i] = (short *)kmalloc_array(columns, + sizeof(short), GFP_KERNEL); } for (i = 0; i < size; i++) @@ -634,12 +1263,14 @@ short **array1dTo2d_short(short *data, int size, int columns) u8 **array1dTo2d_u8(u8 *data, int size, int columns) { - int i; - u8 **matrix = (u8 **)kmalloc(((int)(size / columns))*sizeof(u8 *), GFP_KERNEL); + int count = size / columns; + u8 **matrix = (u8 **)kmalloc_array(count, + sizeof(u8 *), GFP_KERNEL); if (matrix != NULL) { - for (i = 0; i < (int)(size / columns); i++) { - matrix[i] = (u8 *)kmalloc(columns*sizeof(u8), GFP_KERNEL); + for (i = 0; i < count; i++) { + matrix[i] = (u8 *)kmalloc_array(columns, + sizeof(u8), GFP_KERNEL); } for (i = 0; i < size; i++) @@ -652,55 +1283,59 @@ u8 **array1dTo2d_u8(u8 *data, int size, int columns) void print_frame_short(char *label, short **matrix, int row, int column) { int i, j; + logError(0, "%s %s\n", tag, label); for (i = 0; i < row; i++) { logError(0, "%s ", tag); - for (j = 0; j < column; j++) { - printk("%d ", matrix[i][j]); - } + for (j = 0; j < column; j++) + pr_err("%d", matrix[i][j]); logError(0, "\n"); kfree(matrix[i]); } + kfree(matrix); } void print_frame_u8(char *label, u8 **matrix, int row, int column) { int i, j; + logError(0, "%s %s\n", tag, label); - for (i = 0; i < row; i++) { - logError(0, "%s ", tag); - for (j = 0; j < column; j++) { - printk("%d ", matrix[i][j]); - } - logError(0, "\n"); - kfree(matrix[i]); - } + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + pr_err("%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); } void print_frame_u32(char *label, u32 **matrix, int row, int column) { int i, j; + logError(0, "%s %s\n", tag, label); for (i = 0; i < row; i++) { logError(0, "%s ", tag); - for (j = 0; j < column; j++) { - printk("%d ", matrix[i][j]); - } + for (j = 0; j < column; j++) + pr_err("%d ", matrix[i][j]); logError(0, "\n"); kfree(matrix[i]); } + kfree(matrix); } void print_frame_int(char *label, int **matrix, int row, int column) { int i, j; + logError(0, "%s %s\n", tag, label); for (i = 0; i < row; i++) { logError(0, "%s ", tag); - for (j = 0; j < column; j++) { - printk("%d ", matrix[i][j]); - } + for (j = 0; j < column; j++) + pr_err("%d ", matrix[i][j]); logError(0, "\n"); kfree(matrix[i]); } + kfree(matrix); } diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.h b/drivers/input/touchscreen/st/fts_lib/ftsTool.h index a90e79fc5607c4fa29f9b65c6445c578338344f5..309785bc082754254654e5f708ab0743d6a8843a 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTool.h +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.h @@ -1,35 +1,72 @@ /* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ -************************************************************************** -** STMicroelectronics ** -************************************************************************** -** marco.cali@st.com ** -************************************************************************** -* * -* FTS Utility Functions * -* * -************************************************************************** -************************************************************************** +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ -*/ -#define GPIO_NOT_DEFINED -1 +#ifndef __FTS_TOOL_H +#define __FTS_TOOL_H -#define TIMEOUT_RESOLUTION 10 /* ms */ -#define GENERAL_TIMEOUT (50*TIMEOUT_RESOLUTION) /* ms */ -#define RELEASE_INFO_TIMEOUT (15*TIMEOUT_RESOLUTION) /* ms */ +#define GPIO_NOT_DEFINED -1 +#define TIMEOUT_RESOLUTION 10 //ms +#define GENERAL_TIMEOUT (50*TIMEOUT_RESOLUTION) //ms +#define RELEASE_INFO_TIMEOUT (15*TIMEOUT_RESOLUTION) //ms -#define FEAT_ENABLE 1 -#define FEAT_DISABLE 0 -#define SYSTEM_RESET_RETRY 3 +#define FEAT_ENABLE 1 +#define FEAT_DISABLE 0 -#define B2_RETRY 2 +#define SYSTEM_RESET_RETRY 3 + +#define B2_RETRY 2 +//for FTM4 can not be greater than 13 bytes +#define LOCKDOWN_CODE_SIZE 10 + +#define LOCKDOWN_CODE_MAX_SIZE 63 +#define LOCKDOWN_CODE_WRITE_CHUNK 12 +#define LOCKDOWN_CODE_READ_CHUNK 4 +#define LOCKDOWN_CODE_RETRY 2 int readB2(u16 address, u8 *outBuf, int len); int readB2U16(u16 address, u8 *outBuf, int byteToRead); int releaseInformation(void); +int lockDownInfo(u8 *data); +int calculateCRC8(u8 *u8_srcBuff, int size, u8 *crc); +int writeLockDownInfo(u8 *data, int size); +int rewriteLockDownInfo(u8 *data, int size); +int readLockDownInfo(u8 *lockData, int *size); char *printHex(char *label, u8 *buff, int count); -int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_to_wait); +int pollForEvent(int *event_to_search, int event_bytes, + u8 *readData, int time_to_wait); int fts_disableInterrupt(void); int fts_enableInterrupt(void); int u8ToU16(u8 *src, u16 *dst); @@ -41,7 +78,8 @@ int u16ToU8_be(u16 src, u8 *dst); int u16ToU8n(u16 *src, int src_length, u8 *dst); int u8ToU32(u8 *src, u32 *dst); int u32ToU8(u32 src, u8 *dst); -int attempt_function(int(*code)(void), unsigned long wait_before_retry, int retry_count); +int attempt_function(int(*code)(void), unsigned long wait_before_retry, + int retry_count); void setResetGpio(int gpio); int fts_system_reset(void); int isSystemResettedUp(void); @@ -52,7 +90,9 @@ int senseOn(void); int senseOff(void); int keyOn(void); int keyOff(void); -int featureEnableDisable(int on_off, u8 feature); +int featureEnableDisable(int on_off, u32 feature); +int writeNoiseParameters(u8 *noise); +int readNoiseParameters(u8 *noise); int checkEcho(u8 *cmd, int size); void print_frame_short(char *label, short **matrix, int row, int column); short **array1dTo2d_short(short *data, int size, int columns); @@ -62,3 +102,5 @@ void print_frame_u32(char *label, u32 **matrix, int row, int column); void print_frame_int(char *label, int **matrix, int row, int column); int cleanUp(int enableTouch); int flushFIFO(void); + +#endif diff --git a/drivers/input/touchscreen/st/fts_limits.h b/drivers/input/touchscreen/st/fts_limits.h index d3be1a2b1e1a0e6c22b8136d09a8376d3aef578a..478f17d11e9193f96c6e2a28cd829a87b8060302 100644 --- a/drivers/input/touchscreen/st/fts_limits.h +++ b/drivers/input/touchscreen/st/fts_limits.h @@ -1,10 +1,1425 @@ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + + #ifndef FTS_LIMITS_H #define FTS_LIMITS_H -/* This is an auto generated header file -* --->Remember to change the name of the two variables!<--- */ -const uint32_t myArray2_size; +//This is an auto generated header file +//--->Remember to change the name of the two variables!<--- +const uint32_t myArray2_size = 16725; const uint8_t myArray2[] = { + 0x2A, 0x53, 0x54, 0x4F, 0x50, 0x5F, 0x4F, 0x4E, 0x5F, 0x46, 0x41, 0x49, + 0x4C, 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x31, 0x0A, 0x2A, 0x53, 0x54, 0x4F, + 0x50, 0x5F, 0x4F, 0x4E, 0x5F, 0x45, 0x52, 0x52, 0x4F, 0x52, 0x2C, 0x31, + 0x2C, 0x31, 0x0A, 0x31, 0x0A, 0x2A, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, + 0x46, 0x57, 0x5F, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x2C, 0x31, 0x2C, + 0x31, 0x0A, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x49, 0x58, 0x31, 0x5F, + 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x0A, + 0x32, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x46, 0x4F, + 0x52, 0x43, 0x45, 0x5F, 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x31, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x49, 0x58, 0x31, 0x5F, 0x53, 0x45, 0x4E, 0x53, + 0x45, 0x5F, 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x32, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, + 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x31, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, + 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x4D, 0x49, 0x4E, + 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x31, 0x30, 0x30, + 0x30, 0x2C, 0x31, 0x35, 0x30, 0x30, 0x30, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, + 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x47, 0x41, 0x50, + 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x33, 0x30, 0x30, 0x30, 0x0A, 0x2A, 0x4D, + 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x43, 0x58, 0x31, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, + 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, 0x2C, 0x36, 0x33, 0x0A, + 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x4D, 0x49, 0x4E, + 0x2C, 0x32, 0x32, 0x2C, 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, + 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, + 0x43, 0x58, 0x32, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, 0x32, + 0x37, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, + 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x48, 0x4F, 0x52, + 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x32, 0x32, 0x2C, 0x32, + 0x36, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, + 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x41, + 0x44, 0x4A, 0x5F, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, + 0x32, 0x31, 0x2C, 0x32, 0x37, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, + 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, + 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x32, 0x32, 0x2C, + 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, + 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, + 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, + 0x32, 0x37, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x41, + 0x44, 0x4A, 0x5F, 0x48, 0x4F, 0x52, 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, + 0x4C, 0x2C, 0x32, 0x32, 0x2C, 0x32, 0x36, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, + 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x41, 0x44, + 0x4A, 0x5F, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x32, + 0x31, 0x2C, 0x32, 0x37, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x52, 0x41, + 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, + 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, 0x2C, 0x38, 0x30, 0x30, + 0x30, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x43, 0x58, + 0x31, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, + 0x32, 0x0A, 0x30, 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x4B, + 0x45, 0x59, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, + 0x2C, 0x33, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x4D, 0x53, + 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x4D, 0x41, 0x58, + 0x2C, 0x31, 0x2C, 0x33, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x54, 0x4F, + 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, + 0x2C, 0x33, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x4D, 0x53, + 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, + 0x58, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x33, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, + 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x46, 0x4F, 0x52, 0x43, + 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, + 0x32, 0x0A, 0x31, 0x30, 0x30, 0x30, 0x2C, 0x31, 0x35, 0x30, 0x30, 0x30, + 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, + 0x41, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x47, 0x41, 0x50, 0x2C, + 0x31, 0x2C, 0x31, 0x0A, 0x33, 0x30, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, + 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x49, 0x58, 0x31, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, + 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, + 0x2C, 0x36, 0x33, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x32, + 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x32, + 0x32, 0x2C, 0x31, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x46, 0x4F, 0x52, + 0x43, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, 0x31, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x32, + 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, + 0x4C, 0x2C, 0x32, 0x31, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, + 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, + 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46, 0x4F, 0x52, + 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x32, 0x32, 0x2C, 0x31, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, + 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46, 0x4F, + 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, 0x31, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, + 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, + 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x56, + 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x32, 0x31, 0x2C, 0x31, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, + 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, 0x31, 0x5F, 0x46, 0x4F, + 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, + 0x31, 0x2C, 0x32, 0x0A, 0x30, 0x2C, 0x36, 0x33, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, + 0x4D, 0x49, 0x4E, 0x2C, 0x32, 0x32, 0x2C, 0x31, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, + 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, + 0x32, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, + 0x32, 0x32, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x56, 0x45, + 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x32, 0x31, 0x2C, 0x31, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, + 0x58, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, + 0x32, 0x32, 0x2C, 0x31, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, + 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, + 0x43, 0x58, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x41, 0x58, + 0x2C, 0x32, 0x32, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, + 0x41, 0x44, 0x4A, 0x5F, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, + 0x2C, 0x32, 0x31, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41, + 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, + 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, + 0x0A, 0x31, 0x30, 0x30, 0x30, 0x2C, 0x31, 0x35, 0x30, 0x30, 0x30, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, + 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x47, 0x41, 0x50, 0x2C, 0x31, + 0x2C, 0x31, 0x0A, 0x33, 0x30, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, + 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, + 0x49, 0x58, 0x31, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, + 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, 0x2C, + 0x36, 0x33, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, + 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x32, 0x5F, + 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x2C, + 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, + 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x37, 0x0A, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x48, 0x4F, + 0x52, 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x2C, 0x32, + 0x36, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, + 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x2C, + 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x53, + 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, + 0x37, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, + 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, + 0x5F, 0x49, 0x58, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x48, 0x4F, 0x52, 0x49, + 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x2C, 0x32, 0x36, 0x0A, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, + 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x43, 0x58, 0x31, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, + 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, + 0x2C, 0x36, 0x33, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, 0x32, + 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, + 0x2C, 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, + 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x37, 0x0A, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x48, + 0x4F, 0x52, 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x2C, + 0x32, 0x36, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, + 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, + 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, + 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, + 0x2C, 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, + 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, + 0x32, 0x37, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, + 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, + 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x48, 0x4F, 0x52, + 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x2C, 0x32, 0x36, + 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, }; #endif diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 8c6c6178ec12fd26ed9832311419da243ed4c154..025bae3853cc974bfd0eb56077d548f40fb4bf2a 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -687,6 +687,14 @@ static int stmfts_probe(struct i2c_client *client, input_set_drvdata(sdata->input, sdata); + /* + * stmfts_power_on expects interrupt to be disabled, but + * at this point the device is still off and I do not trust + * the status of the irq line that can generate some spurious + * interrupts. To be on the safe side it's better to not enable + * the interrupts during their request. + */ + irq_set_status_flags(client->irq, IRQ_NOAUTOEN); err = devm_request_threaded_irq(&client->dev, client->irq, NULL, stmfts_irq_handler, IRQF_ONESHOT, @@ -694,9 +702,6 @@ static int stmfts_probe(struct i2c_client *client, if (err) return err; - /* stmfts_power_on expects interrupt to be disabled */ - disable_irq(client->irq); - dev_dbg(&client->dev, "initializing ST-Microelectronics FTS...\n"); err = stmfts_power_on(sdata); diff --git a/drivers/iommu/arm-smmu-regs.h b/drivers/iommu/arm-smmu-regs.h index afac72cd8fd70103bcfd0dbbfb47f16c5c6b1a80..59fe3f49affd030fa31bbc32f9bcdaaf1baf06b2 100644 --- a/drivers/iommu/arm-smmu-regs.h +++ b/drivers/iommu/arm-smmu-regs.h @@ -37,6 +37,9 @@ #define sCR0_VMID16EN (1 << 31) #define sCR0_BSU_SHIFT 14 #define sCR0_BSU_MASK 0x3 +#define sCR0_SHCFG_SHIFT 22 +#define sCR0_SHCFG_MASK 0x3 +#define sCR0_SHCFG_NSH 3 /* Auxiliary Configuration register */ #define ARM_SMMU_GR0_sACR 0x10 @@ -114,6 +117,9 @@ #define S2CR_EXIDVALID (1 << 10) #define S2CR_TYPE_SHIFT 16 #define S2CR_TYPE_MASK 0x3 +#define S2CR_SHCFG_SHIFT 8 +#define S2CR_SHCFG_MASK 0x3 +#define S2CR_SHCFG_NSH 0x3 enum arm_smmu_s2cr_type { S2CR_TYPE_TRANS, S2CR_TYPE_BYPASS, @@ -184,6 +190,9 @@ enum arm_smmu_s2cr_privcfg { #define ARM_SMMU_CB_ATS1PR 0x800 #define ARM_SMMU_CB_ATSR 0x8f0 +#define SCTLR_SHCFG_SHIFT 22 +#define SCTLR_SHCFG_MASK 0x3 +#define SCTLR_SHCFG_NSH 0x3 #define SCTLR_S1_ASIDPNE (1 << 12) #define SCTLR_CFCFG (1 << 7) #define SCTLR_HUPCF (1 << 8) diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 8f7a3c00b6cf3dc5be9868e19b6541a0a938c678..fa58f40607518a702f00e5d147b79629a4e5e0e1 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -2861,15 +2861,9 @@ static int arm_smmu_device_remove(struct platform_device *pdev) struct arm_smmu_device *smmu = platform_get_drvdata(pdev); arm_smmu_device_disable(smmu); - return 0; } -static void arm_smmu_device_shutdown(struct platform_device *pdev) -{ - arm_smmu_device_remove(pdev); -} - static const struct of_device_id arm_smmu_of_match[] = { { .compatible = "arm,smmu-v3", }, { }, @@ -2883,7 +2877,6 @@ static struct platform_driver arm_smmu_driver = { }, .probe = arm_smmu_device_probe, .remove = arm_smmu_device_remove, - .shutdown = arm_smmu_device_shutdown, }; module_platform_driver(arm_smmu_driver); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 0493852050bdd63acb241f5bc4a02def3509edc1..4c47600bbfffc9cce553ef07d1406de11a60c230 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -248,6 +248,7 @@ struct arm_smmu_device { #define ARM_SMMU_OPT_DYNAMIC (1 << 3) #define ARM_SMMU_OPT_3LVL_TABLES (1 << 4) #define ARM_SMMU_OPT_NO_ASID_RETENTION (1 << 5) +#define ARM_SMMU_OPT_STATIC_CB (1 << 6) u32 options; enum arm_smmu_arch_version version; enum arm_smmu_implementation model; @@ -379,6 +380,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = { { ARM_SMMU_OPT_DYNAMIC, "qcom,dynamic" }, { ARM_SMMU_OPT_3LVL_TABLES, "qcom,use-3-lvl-tables" }, { ARM_SMMU_OPT_NO_ASID_RETENTION, "qcom,no-asid-retention" }, + { ARM_SMMU_OPT_STATIC_CB, "qcom,enable-static-cb"}, { 0, NULL}, }; @@ -402,6 +404,8 @@ static int arm_smmu_alloc_cb(struct iommu_domain *domain, struct arm_smmu_device *smmu, struct device *dev); +static bool arm_smmu_is_static_cb(struct arm_smmu_device *smmu); + static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) { return container_of(dom, struct arm_smmu_domain, domain); @@ -439,6 +443,11 @@ static bool is_iommu_pt_coherent(struct arm_smmu_domain *smmu_domain) return false; } +static bool arm_smmu_is_static_cb(struct arm_smmu_device *smmu) +{ + return smmu->options & ARM_SMMU_OPT_STATIC_CB; +} + static bool arm_smmu_is_domain_secure(struct arm_smmu_domain *smmu_domain) { return (smmu_domain->secure_vmid != VMID_INVAL); @@ -1480,6 +1489,9 @@ static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx, /* SCTLR */ reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE; + /* Ensure bypass transactions are Non-shareable */ + reg |= SCTLR_SHCFG_NSH << SCTLR_SHCFG_SHIFT; + if (attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) { reg &= ~SCTLR_CFCFG; reg |= SCTLR_HUPCF; @@ -1676,6 +1688,12 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, quirks |= IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT; if (is_iommu_pt_coherent(smmu_domain)) quirks |= IO_PGTABLE_QUIRK_NO_DMA; + if (smmu_domain->attributes & (1 << DOMAIN_ATTR_USE_LLC_NWA)) + quirks |= IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA; + if (((quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT) || + (quirks & IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA)) && + (smmu->model == QCOM_SMMUV500)) + quirks |= IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE; ret = arm_smmu_alloc_cb(domain, smmu, dev); if (ret < 0) @@ -1891,7 +1909,8 @@ static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx) struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx; u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT | (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT | - (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT; + (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT | + S2CR_SHCFG_NSH << S2CR_SHCFG_SHIFT; if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs && smmu->smrs[idx].valid) @@ -2812,6 +2831,11 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain, (1 << DOMAIN_ATTR_USE_UPSTREAM_HINT)); ret = 0; break; + case DOMAIN_ATTR_USE_LLC_NWA: + *((int *)data) = !!(smmu_domain->attributes & + (1 << DOMAIN_ATTR_USE_LLC_NWA)); + ret = 0; + break; case DOMAIN_ATTR_EARLY_MAP: *((int *)data) = !!(smmu_domain->attributes & (1 << DOMAIN_ATTR_EARLY_MAP)); @@ -2994,6 +3018,17 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain, 1 << DOMAIN_ATTR_USE_UPSTREAM_HINT; ret = 0; break; + case DOMAIN_ATTR_USE_LLC_NWA: + /* can't be changed while attached */ + if (smmu_domain->smmu != NULL) { + ret = -EBUSY; + break; + } + if (*((int *)data)) + smmu_domain->attributes |= + 1 << DOMAIN_ATTR_USE_LLC_NWA; + ret = 0; + break; case DOMAIN_ATTR_EARLY_MAP: { int early_map = *((int *)data); @@ -3498,6 +3533,10 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) if (smmu->features & ARM_SMMU_FEAT_EXIDS) reg |= sCR0_EXIDENABLE; + /* Force bypass transaction to be Non-Shareable & not io-coherent */ + reg &= ~(sCR0_SHCFG_MASK << sCR0_SHCFG_SHIFT); + reg |= sCR0_SHCFG_NSH << sCR0_SHCFG_SHIFT; + /* Push the button */ arm_smmu_tlb_sync_global(smmu); writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); @@ -3562,7 +3601,7 @@ static int arm_smmu_alloc_cb(struct iommu_domain *domain, cb = smmu->s2crs[idx].cbndx; } - if (cb < 0) { + if (cb < 0 && !arm_smmu_is_static_cb(smmu)) { mutex_unlock(&smmu->stream_map_mutex); return __arm_smmu_alloc_bitmap(smmu->context_map, smmu->num_s2_context_banks, @@ -4346,11 +4385,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev) return 0; } -static void arm_smmu_device_shutdown(struct platform_device *pdev) -{ - arm_smmu_device_remove(pdev); -} - static int __maybe_unused arm_smmu_pm_resume(struct device *dev) { struct arm_smmu_device *smmu = dev_get_drvdata(dev); @@ -4369,7 +4403,6 @@ static struct platform_driver arm_smmu_driver = { }, .probe = arm_smmu_device_dt_probe, .remove = arm_smmu_device_remove, - .shutdown = arm_smmu_device_shutdown, }; static struct platform_driver qsmmuv500_tbu_driver; @@ -4441,7 +4474,7 @@ IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", arm_smmu_of_init); #define DEBUG_PAR_PA_SHIFT 12 #define DEBUG_PAR_FAULT_VAL 0x1 -#define TBU_DBG_TIMEOUT_US 30000 +#define TBU_DBG_TIMEOUT_US 100 #define QSMMUV500_ACTLR_DEEP_PREFETCH_MASK 0x3 #define QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT 0x8 @@ -4504,11 +4537,12 @@ static bool arm_smmu_fwspec_match_smr(struct iommu_fwspec *fwspec, return false; } -static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu) +static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu, + struct arm_smmu_domain *smmu_domain) { unsigned long flags; - u32 val; - void __iomem *base; + u32 halt, fsr, sctlr_orig, sctlr, status; + void __iomem *base, *cb_base; spin_lock_irqsave(&tbu->halt_lock, flags); if (tbu->halt_count) { @@ -4517,19 +4551,48 @@ static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu) return 0; } + cb_base = ARM_SMMU_CB(smmu_domain->smmu, smmu_domain->cfg.cbndx); base = tbu->base; - val = readl_relaxed(base + DEBUG_SID_HALT_REG); - val |= DEBUG_SID_HALT_VAL; - writel_relaxed(val, base + DEBUG_SID_HALT_REG); + halt = readl_relaxed(base + DEBUG_SID_HALT_REG); + halt |= DEBUG_SID_HALT_VAL; + writel_relaxed(halt, base + DEBUG_SID_HALT_REG); - if (readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG, - val, (val & DEBUG_SR_HALT_ACK_VAL), - 0, TBU_DBG_TIMEOUT_US)) { + if (!readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG, status, + (status & DEBUG_SR_HALT_ACK_VAL), + 0, TBU_DBG_TIMEOUT_US)) + goto out; + + fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR); + if (!(fsr & FSR_FAULT)) { dev_err(tbu->dev, "Couldn't halt TBU!\n"); spin_unlock_irqrestore(&tbu->halt_lock, flags); return -ETIMEDOUT; } + /* + * We are in a fault; Our request to halt the bus will not complete + * until transactions in front of us (such as the fault itself) have + * completed. Disable iommu faults and terminate any existing + * transactions. + */ + sctlr_orig = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR); + sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE); + writel_relaxed(sctlr, cb_base + ARM_SMMU_CB_SCTLR); + + writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR); + writel_relaxed(RESUME_TERMINATE, cb_base + ARM_SMMU_CB_RESUME); + + if (readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG, status, + (status & DEBUG_SR_HALT_ACK_VAL), + 0, TBU_DBG_TIMEOUT_US)) { + dev_err(tbu->dev, "Couldn't halt TBU from fault context!\n"); + writel_relaxed(sctlr_orig, cb_base + ARM_SMMU_CB_SCTLR); + spin_unlock_irqrestore(&tbu->halt_lock, flags); + return -ETIMEDOUT; + } + + writel_relaxed(sctlr_orig, cb_base + ARM_SMMU_CB_SCTLR); +out: tbu->halt_count = 1; spin_unlock_irqrestore(&tbu->halt_lock, flags); return 0; @@ -4630,6 +4693,14 @@ static phys_addr_t qsmmuv500_iova_to_phys( void __iomem *cb_base; u32 sctlr_orig, sctlr; int needs_redo = 0; + ktime_t timeout; + + /* only 36 bit iova is supported */ + if (iova >= (1ULL << 36)) { + dev_err_ratelimited(smmu->dev, "ECATS: address too large: %pad\n", + &iova); + return 0; + } cb_base = ARM_SMMU_CB(smmu, cfg->cbndx); tbu = qsmmuv500_find_tbu(smmu, sid); @@ -4640,35 +4711,23 @@ static phys_addr_t qsmmuv500_iova_to_phys( if (ret) return 0; - /* - * Disable client transactions & wait for existing operations to - * complete. - */ - ret = qsmmuv500_tbu_halt(tbu); + ret = qsmmuv500_tbu_halt(tbu, smmu_domain); if (ret) goto out_power_off; - /* Only one concurrent atos operation */ - ret = qsmmuv500_ecats_lock(smmu_domain, tbu, &flags); - if (ret) - goto out_resume; - /* - * We can be called from an interrupt handler with FSR already set - * so terminate the faulting transaction prior to starting ecats. - * No new racing faults can occur since we in the halted state. * ECATS can trigger the fault interrupt, so disable it temporarily * and check for an interrupt manually. */ - fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR); - if (fsr & FSR_FAULT) { - writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR); - writel_relaxed(RESUME_TERMINATE, cb_base + ARM_SMMU_CB_RESUME); - } sctlr_orig = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR); sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE); writel_relaxed(sctlr, cb_base + ARM_SMMU_CB_SCTLR); + /* Only one concurrent atos operation */ + ret = qsmmuv500_ecats_lock(smmu_domain, tbu, &flags); + if (ret) + goto out_resume; + redo: /* Set address and stream-id */ val = readq_relaxed(tbu->base + DEBUG_SID_HALT_REG); @@ -4687,32 +4746,40 @@ static phys_addr_t qsmmuv500_iova_to_phys( writeq_relaxed(val, tbu->base + DEBUG_TXN_TRIGG_REG); ret = 0; - if (readl_poll_timeout_atomic(tbu->base + DEBUG_SR_HALT_ACK_REG, - val, !(val & DEBUG_SR_ECATS_RUNNING_VAL), - 0, TBU_DBG_TIMEOUT_US)) { - dev_err(tbu->dev, "ECATS translation timed out!\n"); + timeout = ktime_add_us(ktime_get(), TBU_DBG_TIMEOUT_US); + for (;;) { + val = readl_relaxed(tbu->base + DEBUG_SR_HALT_ACK_REG); + if (!(val & DEBUG_SR_ECATS_RUNNING_VAL)) + break; + val = readl_relaxed(cb_base + ARM_SMMU_CB_FSR); + if (val & FSR_FAULT) + break; + if (ktime_compare(ktime_get(), timeout) > 0) { + dev_err(tbu->dev, "ECATS translation timed out!\n"); + ret = -ETIMEDOUT; + break; + } } + val = readq_relaxed(tbu->base + DEBUG_PAR_REG); fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR); if (fsr & FSR_FAULT) { dev_err(tbu->dev, "ECATS generated a fault interrupt! FSR = %llx\n", - fsr); - ret = -EINVAL; + fsr); - writel_relaxed(val, cb_base + ARM_SMMU_CB_FSR); + /* Clear pending interrupts */ + writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR); /* - * Clear pending interrupts * Barrier required to ensure that the FSR is cleared - * before resuming SMMU operation + * before resuming SMMU operation. */ wmb(); writel_relaxed(RESUME_TERMINATE, cb_base + ARM_SMMU_CB_RESUME); - } - val = readq_relaxed(tbu->base + DEBUG_PAR_REG); - if (val & DEBUG_PAR_FAULT_VAL) { - dev_err(tbu->dev, "ECATS translation failed! PAR = %llx\n", - val); + /* Check if ECATS translation failed */ + if (val & DEBUG_PAR_FAULT_VAL) + dev_err(tbu->dev, "ECATS translation failed! PAR = %llx\n", + val); ret = -EINVAL; } diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index bb5c7c711f326cbebc2e178e7dda2ace6b4919a3..7ea5788f8484788c3b5583c955ca5556a8126b52 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -381,6 +381,9 @@ int dma_info_to_prot(enum dma_data_direction dir, bool coherent, if (attrs & DMA_ATTR_IOMMU_USE_UPSTREAM_HINT) prot |= IOMMU_USE_UPSTREAM_HINT; + if (attrs & DMA_ATTR_IOMMU_USE_LLC_NWA) + prot |= IOMMU_USE_LLC_NWA; + switch (dir) { case DMA_BIDIRECTIONAL: return prot | IOMMU_READ | IOMMU_WRITE; diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 25c2c75f5332efe3e98d7e1c290db71d515364f5..13485a40dd46ccf722a2024d3b1f7bd86ec33bd3 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1344,8 +1344,15 @@ static const struct iommu_ops exynos_iommu_ops = { static int __init exynos_iommu_init(void) { + struct device_node *np; int ret; + np = of_find_matching_node(NULL, sysmmu_of_match); + if (!np) + return 0; + + of_node_put(np); + lv2table_kmem_cache = kmem_cache_create("exynos-iommu-lv2table", LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL); if (!lv2table_kmem_cache) { diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 83f3d4831f943899fd3c80fdd32a7c999dd8f8b9..e8414bcf83904422be5aa819d41d9dd834d6d290 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1603,8 +1603,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, * flush. However, device IOTLB doesn't need to be flushed in this case. */ if (!cap_caching_mode(iommu->cap) || !map) - iommu_flush_dev_iotlb(get_iommu_domain(iommu, did), - addr, mask); + iommu_flush_dev_iotlb(domain, addr, mask); } static void iommu_flush_iova(struct iova_domain *iovad) diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index 003b4a4d4b7851c9e61c99db20cb6061899cea8e..d7def26ccf79ca8cd84b344058fd597b13e5b4fe 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -382,6 +382,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ pasid_max - 1, GFP_KERNEL); if (ret < 0) { kfree(svm); + kfree(sdev); goto out; } svm->pasid = ret; diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 3dcc65d8ef28dfa8f0e3392b17cb87175e133c60..c26b8e9604aa781416848fc079b5c6644f85c8db 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -169,15 +169,18 @@ #define ARM_LPAE_TCR_PS_48_BIT 0x5ULL #define ARM_LPAE_MAIR_ATTR_SHIFT(n) ((n) << 3) +#define ARM_LPAE_MAIR1_ATTR_SHIFT(n) ((n-4) << 3) #define ARM_LPAE_MAIR_ATTR_MASK 0xff #define ARM_LPAE_MAIR_ATTR_DEVICE 0x04 #define ARM_LPAE_MAIR_ATTR_NC 0x44 #define ARM_LPAE_MAIR_ATTR_WBRWA 0xff #define ARM_LPAE_MAIR_ATTR_UPSTREAM 0xf4 +#define ARM_LPAE_MAIR_ATTR_LLC_NWA 0xe4 #define ARM_LPAE_MAIR_ATTR_IDX_NC 0 #define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 #define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 #define ARM_LPAE_MAIR_ATTR_IDX_UPSTREAM 3 +#define ARM_LPAE_MAIR_ATTR_IDX_LLC_NWA 0x4ULL /* IOPTE accessors */ #define iopte_deref(pte, d) \ @@ -583,6 +586,9 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, else if (prot & IOMMU_USE_UPSTREAM_HINT) pte |= (ARM_LPAE_MAIR_ATTR_IDX_UPSTREAM << ARM_LPAE_PTE_ATTRINDX_SHIFT); + else if (prot & IOMMU_USE_LLC_NWA) + pte |= (ARM_LPAE_MAIR_ATTR_IDX_LLC_NWA + << ARM_LPAE_PTE_ATTRINDX_SHIFT); } else { pte = ARM_LPAE_PTE_HAP_FAULT; if (prot & IOMMU_READ) @@ -1116,7 +1122,9 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS | IO_PGTABLE_QUIRK_NO_DMA - | IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT)) + | IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT + | IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE + | IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA)) return NULL; data = arm_lpae_alloc_pgtable(cfg); @@ -1128,10 +1136,24 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); + else if ((cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT) && + (cfg->quirks & IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE)) + reg = (ARM_LPAE_TCR_SH_NS << ARM_LPAE_TCR_SH0_SHIFT) | + (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) | + (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); else if (cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT) reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) | (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); + else if ((cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA) && + (cfg->quirks & IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE)) + reg = (ARM_LPAE_TCR_SH_NS << ARM_LPAE_TCR_SH0_SHIFT) | + (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) | + (ARM_LPAE_TCR_RGN_WB << ARM_LPAE_TCR_ORGN0_SHIFT); + else if (cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA) + reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) | + (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) | + (ARM_LPAE_TCR_RGN_WB << ARM_LPAE_TCR_ORGN0_SHIFT); else reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) | (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) | @@ -1189,7 +1211,11 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_UPSTREAM)); cfg->arm_lpae_s1_cfg.mair[0] = reg; - cfg->arm_lpae_s1_cfg.mair[1] = 0; + + reg = ARM_LPAE_MAIR_ATTR_LLC_NWA + << ARM_LPAE_MAIR1_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_LLC_NWA); + + cfg->arm_lpae_s1_cfg.mair[1] = reg; /* Looking good; allocate a pgd */ data->pgd = __arm_lpae_alloc_pages(data->pgd_size, GFP_KERNEL, diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index c8b112b8b280ec451033484d6f6891ec78a72b69..2b6c758c3c38b2d498cbfd801879bce2cb9bebd5 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h @@ -81,18 +81,31 @@ struct io_pgtable_cfg { * be accessed by a fully cache-coherent IOMMU or CPU (e.g. for a * software-emulated IOMMU), such that pagetable updates need not * be treated as explicit DMA data. + * + + * IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE: + * Having page tables which are non coherent, but cached in a + * system cache requires SH=Non-Shareable. This applies to the + * qsmmuv500 model. For data buffers SH=Non-Shareable is not + * required. * IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT: Override the attributes * set in TCR for the page table walker. Use attributes specified * by the upstream hw instead. * + * IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA: Override the attributes + * set in TCR for the page table walker with Write-Back, + * no Write-Allocate cacheable encoding. + * */ #define IO_PGTABLE_QUIRK_ARM_NS BIT(0) #define IO_PGTABLE_QUIRK_NO_PERMS BIT(1) #define IO_PGTABLE_QUIRK_TLBI_ON_MAP BIT(2) #define IO_PGTABLE_QUIRK_ARM_MTK_4GB BIT(3) #define IO_PGTABLE_QUIRK_NO_DMA BIT(4) + #define IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE BIT(5) #define IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT BIT(6) + #define IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA BIT(7) unsigned long quirks; unsigned long pgsize_bitmap; unsigned int ias; diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index 9801a6ef72133a28e4d345c7932e03894d3e2a58..65dd81e8650d03d73d6fbd43585a5f332f270726 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -161,6 +161,7 @@ static void *test_virt_addr; struct iommu_debug_device { struct device *dev; struct iommu_domain *domain; + struct dma_iommu_mapping *mapping; u64 iova; u64 phys; size_t len; @@ -1329,6 +1330,8 @@ static ssize_t __iommu_debug_dma_attach_write(struct file *file, if (arm_iommu_attach_device(dev, dma_mapping)) goto out_release_mapping; + + ddev->mapping = dma_mapping; pr_err("Attached\n"); } else { if (!dev->archdata.mapping) { @@ -1342,7 +1345,7 @@ static ssize_t __iommu_debug_dma_attach_write(struct file *file, goto out; } arm_iommu_detach_device(dev); - arm_iommu_release_mapping(dev->archdata.mapping); + arm_iommu_release_mapping(ddev->mapping); pr_err("Detached\n"); } retval = count; @@ -1862,7 +1865,7 @@ static ssize_t iommu_debug_dma_map_write(struct file *file, if (kstrtouint(comma2 + 1, 0, &attr)) goto invalid_format; - if (v_addr < test_virt_addr || v_addr > (test_virt_addr + SZ_1M - 1)) + if (v_addr < test_virt_addr || v_addr + size > test_virt_addr + SZ_1M) goto invalid_addr; if (attr == 0) diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 33edfa794ae9f5514c5113fa27597a12373f2a5c..b36d5aa92a006b942f3afdd32c80ae7f7d39beb0 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -182,14 +182,28 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova, rb_insert_color(&iova->node, root); } +#ifdef CONFIG_ARM64_DMA_IOMMU_ALIGNMENT +#define MAX_ALIGN(shift) (((1 << CONFIG_ARM64_DMA_IOMMU_ALIGNMENT) * PAGE_SIZE)\ + >> (shift)) +#else +#define MAX_ALIGN(shift) ULONG_MAX +#endif + /* * Computes the padding size required, to make the start address - * naturally aligned on the power-of-two order of its size + * naturally aligned on the minimum of the power-of-two order of its size and + * max_align */ static unsigned int -iova_get_pad_size(unsigned int size, unsigned int limit_pfn) +iova_get_pad_size(unsigned int size, unsigned int limit_pfn, + unsigned int max_align) { - return (limit_pfn - size) & (__roundup_pow_of_two(size) - 1); + unsigned int align = __roundup_pow_of_two(size); + + if (align > max_align) + align = max_align; + + return (limit_pfn - size) & (align - 1); } static int __alloc_and_insert_iova_range(struct iova_domain *iovad, @@ -200,12 +214,14 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, unsigned long flags; unsigned long saved_pfn; unsigned int pad_size = 0; + unsigned long shift = iova_shift(iovad); /* Walk the tree backwards */ spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); saved_pfn = limit_pfn; curr = __get_cached_rbnode(iovad, &limit_pfn); prev = curr; + while (curr) { struct iova *curr_iova = rb_entry(curr, struct iova, node); @@ -213,7 +229,8 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, goto move_left; } else if (limit_pfn > curr_iova->pfn_hi) { if (size_aligned) - pad_size = iova_get_pad_size(size, limit_pfn); + pad_size = iova_get_pad_size(size, limit_pfn, + MAX_ALIGN(shift)); if ((curr_iova->pfn_hi + size + pad_size) < limit_pfn) break; /* found a free slot */ } @@ -225,7 +242,8 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, if (!curr) { if (size_aligned) - pad_size = iova_get_pad_size(size, limit_pfn); + pad_size = iova_get_pad_size(size, limit_pfn, + MAX_ALIGN(shift)); if ((iovad->start_pfn + size + pad_size) > limit_pfn) { spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); return -ENOMEM; diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c index 58fa6019a81c612672b60aa4e97e5e6f04543009..bd31fedb26d69d0eebe9f463cbc98e76f687d09c 100644 --- a/drivers/iommu/msm_dma_iommu_mapping.c +++ b/drivers/iommu/msm_dma_iommu_mapping.c @@ -236,8 +236,20 @@ static inline int __msm_dma_map_sg(struct device *dev, struct scatterlist *sg, (attrs & ~DMA_ATTR_SKIP_CPU_SYNC) == (iommu_map->attrs & ~DMA_ATTR_SKIP_CPU_SYNC) && sg_phys(sg) == iommu_map->buf_start_addr) { - sg->dma_address = iommu_map->sgl->dma_address; - sg->dma_length = iommu_map->sgl->dma_length; + struct scatterlist *sg_tmp = sg; + struct scatterlist *map_sg; + int i; + + for_each_sg(iommu_map->sgl, map_sg, nents, i) { + sg_dma_address(sg_tmp) = sg_dma_address(map_sg); + sg_dma_len(sg_tmp) = sg_dma_len(map_sg); + if (sg_dma_len(map_sg) == 0) + break; + + sg_tmp = sg_next(sg_tmp); + if (sg_tmp == NULL) + break; + } kref_get(&iommu_map->ref); diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 9ae71804b5dd67bf27322f88b7066f9e646fbdb5..1c2ca8d51a708c9dee4213d671dfca073344069d 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -21,6 +21,8 @@ #include "irq-gic-common.h" +static DEFINE_RAW_SPINLOCK(irq_controller_lock); + static const struct gic_kvm_info *gic_kvm_info; const struct gic_kvm_info *gic_get_kvm_info(void) @@ -52,11 +54,13 @@ int gic_configure_irq(unsigned int irq, unsigned int type, u32 confoff = (irq / 16) * 4; u32 val, oldval; int ret = 0; + unsigned long flags; /* * Read current configuration register, and insert the config * for "irq", depending on "type". */ + raw_spin_lock_irqsave(&irq_controller_lock, flags); val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff); if (type & IRQ_TYPE_LEVEL_MASK) val &= ~confmask; @@ -64,8 +68,10 @@ int gic_configure_irq(unsigned int irq, unsigned int type, val |= confmask; /* If the current configuration is the same, then we are done */ - if (val == oldval) + if (val == oldval) { + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); return 0; + } /* * Write back the new configuration, and possibly re-enable @@ -83,6 +89,7 @@ int gic_configure_irq(unsigned int irq, unsigned int type, pr_warn("GIC: PPI%d is secure or misconfigured\n", irq - 16); } + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); if (sync_access) sync_access(); diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c index 14a8c0a7e095eb32c383bccbb163174ce1ccec28..25a98de5cfb2831fa61d33fd3be3ef30d3f4e534 100644 --- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c +++ b/drivers/irqchip/irq-gic-v3-its-pci-msi.c @@ -132,6 +132,8 @@ static int __init its_pci_of_msi_init(void) for (np = of_find_matching_node(NULL, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { + if (!of_device_is_available(np)) + continue; if (!of_property_read_bool(np, "msi-controller")) continue; diff --git a/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c index 833a90fe33aed839a81b781831027e677eef5581..8881a053c173edfdb11ad322b9b60f9b61ef57aa 100644 --- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c +++ b/drivers/irqchip/irq-gic-v3-its-platform-msi.c @@ -154,6 +154,8 @@ static void __init its_pmsi_of_init(void) for (np = of_find_matching_node(NULL, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { + if (!of_device_is_available(np)) + continue; if (!of_property_read_bool(np, "msi-controller")) continue; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index af57f8473a88aec2278fce8022351695e151ef4c..13f195c9743e16db76f8cde868d8c8c85450078a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -3083,6 +3083,8 @@ static int __init its_of_probe(struct device_node *node) for (np = of_find_matching_node(node, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { + if (!of_device_is_available(np)) + continue; if (!of_property_read_bool(np, "msi-controller")) { pr_warn("%pOF: no msi-controller property, ITS ignored\n", np); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index a8a7c5235c2803ec6fc1563d4cd7d18ec368ec78..65cb116ac0aa2dbab450c17c26c940ed44b49795 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -650,7 +650,7 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) MPIDR_TO_SGI_AFFINITY(cluster_id, 1) | tlist << ICC_SGI1R_TARGET_LIST_SHIFT); - pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val); + pr_devel("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val); gic_write_sgi1r(val); } @@ -1281,6 +1281,10 @@ gic_acpi_parse_madt_gicc(struct acpi_subtable_header *header, u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2; void __iomem *redist_base; + /* GICC entry which has !ACPI_MADT_ENABLED is not unusable so skip */ + if (!(gicc->flags & ACPI_MADT_ENABLED)) + return 0; + redist_base = ioremap(gicc->gicr_base_address, size); if (!redist_base) return -ENOMEM; @@ -1330,6 +1334,13 @@ static int __init gic_acpi_match_gicc(struct acpi_subtable_header *header, if ((gicc->flags & ACPI_MADT_ENABLED) && gicc->gicr_base_address) return 0; + /* + * It's perfectly valid firmware can pass disabled GICC entry, driver + * should not treat as errors, skip the entry instead of probe fail. + */ + if (!(gicc->flags & ACPI_MADT_ENABLED)) + return 0; + return -ENODEV; } diff --git a/drivers/irqchip/qcom/Kconfig b/drivers/irqchip/qcom/Kconfig index e8e1842ee6b3b9b7bc3d9431f278d40085a0bad3..654b081c2658020177695fbb1c806175b2c0230c 100644 --- a/drivers/irqchip/qcom/Kconfig +++ b/drivers/irqchip/qcom/Kconfig @@ -14,9 +14,9 @@ config QTI_PDC_SM8150 help QTI Power Domain Controller for SM8150 -config QTI_PDC_SDM640 - bool "QTI PDC SDM640" +config QTI_PDC_SM6150 + bool "QTI PDC SM6150" select QTI_PDC - default y if ARCH_SDM640 + default y if ARCH_SM6150 help - QTI Power Domain Controller for SDM640 + QTI Power Domain Controller for SM6150 diff --git a/drivers/irqchip/qcom/Makefile b/drivers/irqchip/qcom/Makefile index 2691b19c676997fd1eb1f82644d3a50772a5d401..92e98bb1287d3bfe1aa0e242ec3a58449b066ed4 100644 --- a/drivers/irqchip/qcom/Makefile +++ b/drivers/irqchip/qcom/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_QTI_PDC) += pdc.o obj-$(CONFIG_QTI_PDC_SM8150) += pdc-sm8150.o -obj-$(CONFIG_QTI_PDC_SDM640) += pdc-sdm640.o +obj-$(CONFIG_QTI_PDC_SM6150) += pdc-sm6150.o diff --git a/drivers/irqchip/qcom/pdc-sdm640.c b/drivers/irqchip/qcom/pdc-sm6150.c similarity index 97% rename from drivers/irqchip/qcom/pdc-sdm640.c rename to drivers/irqchip/qcom/pdc-sm6150.c index 02e1d7c2fe67d887523b84d4b0a72dfbbad0187f..4a471059e3d1b9749801b11c5847252f56e4edb9 100644 --- a/drivers/irqchip/qcom/pdc-sdm640.c +++ b/drivers/irqchip/qcom/pdc-sm6150.c @@ -14,7 +14,7 @@ #include #include "pdc.h" -static struct pdc_pin sdm640_data[] = { +static struct pdc_pin sm6150_data[] = { {0, 512},/*rpmh_wake*/ {1, 513},/*ee0_apps_hlos_spmi_periph_irq*/ {2, 514},/*ee1_apps_trustzone_spmi_periph_irq*/ @@ -146,7 +146,7 @@ static struct pdc_pin sdm640_data[] = { static int __init qcom_pdc_gic_init(struct device_node *node, struct device_node *parent) { - return qcom_pdc_init(node, parent, sdm640_data); + return qcom_pdc_init(node, parent, sm6150_data); } -IRQCHIP_DECLARE(pdc_sdm640, "qcom,pdc-sdm640", qcom_pdc_gic_init); +IRQCHIP_DECLARE(pdc_sm6150, "qcom,pdc-sm6150", qcom_pdc_gic_init); diff --git a/drivers/irqchip/qcom/pdc.c b/drivers/irqchip/qcom/pdc.c index 923552ffffdf501da7aa616f8d77fd648a742cf1..f7284bdaf87a7fa3bf766e224a795d50fb12f8ca 100644 --- a/drivers/irqchip/qcom/pdc.c +++ b/drivers/irqchip/qcom/pdc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 #include #include +#include #include #include #include @@ -95,6 +96,20 @@ static inline int pdc_enable_intr(struct irq_data *d, bool on) return 0; } +static int qcom_pdc_gic_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool *state) +{ + return d->parent_data->chip->irq_get_irqchip_state(d, + which, state); +} + +static int qcom_pdc_gic_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool value) +{ + return d->parent_data->chip->irq_set_irqchip_state(d, + which, value); +} + static void qcom_pdc_gic_mask(struct irq_data *d) { pdc_enable_intr(d, false); @@ -220,6 +235,8 @@ static struct irq_chip qcom_pdc_gic_chip = { #ifdef CONFIG_SMP .irq_set_affinity = irq_chip_set_affinity_parent, #endif + .irq_get_irqchip_state = qcom_pdc_gic_get_irqchip_state, + .irq_set_irqchip_state = qcom_pdc_gic_set_irqchip_state, }; static int qcom_pdc_translate(struct irq_domain *d, diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c index dce6632daae1c4b5a0f43be11d80b99d995c5011..ae2b2669af1bc44667e49e60148c9591b7fa4e85 100644 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -156,7 +156,7 @@ _set_debug(struct fritzcard *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct fritzcard *card; diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c index d5bdbaf93a1afe2d905a21ecad5cca09d637a731..1fc290659e945a5ffdf2efcda8cf687c4bedd42d 100644 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -244,7 +244,7 @@ _set_debug(struct inf_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct inf_hw *card; diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c index 6a6d848bd18eefc1d7400a49f64c02960a6b4592..89d9ba8ed535e2bf25ad807add639b0240942e3a 100644 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -111,7 +111,7 @@ _set_debug(struct tiger_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct tiger_hw *card; diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c index 9815bb4eec9c747a6f285377622ca004b2ea0373..1f1446ed8d5f328506e71bce312862a8473c15c9 100644 --- a/drivers/isdn/hardware/mISDN/speedfax.c +++ b/drivers/isdn/hardware/mISDN/speedfax.c @@ -94,7 +94,7 @@ _set_debug(struct sfax_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct sfax_hw *card; diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c index d80072fef43415f2812169b2111391b3a6b5281e..209036a4af3a28c23498c9fdbc6c333bac0e33a8 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -101,7 +101,7 @@ _set_debug(struct w6692_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct w6692_hw *card; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 8fa42fe33fa9794d1e86ccb2d96c31d3d7bb8675..da8b46f9ec7f248b27ff72159968f3b0571e1511 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -726,6 +726,14 @@ config LEDS_NIC78BX To compile this driver as a module, choose M here: the module will be called leds-nic78bx. +config LEDS_QPNP_VIBRATOR_LDO + tristate "Vibrator-LDO support for QPNP PMIC" + depends on LEDS_CLASS && MFD_SPMI_PMIC + help + This option enables device driver support for the vibrator-ldo + peripheral found on Qualcomm Technologies, Inc. QPNP PMICs. + The vibrator-ldo peripheral is capable of driving ERM vibrators. + comment "LED Triggers" source "drivers/leds/trigger/Kconfig" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index dc92a3f209b970178383d1eaf0cd6955458d9633..e8e7a2ec587cd1b9fd924c7f143821fe95488d4c 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o +obj-$(CONFIG_LEDS_QPNP_VIBRATOR_LDO) += leds-qpnp-vibrator-ldo.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 47788708db13d69a2f77e49c3a6c17a390edfe95..85904d66ec495814db6e0ef2eb899b5d77dd10d5 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -321,7 +321,7 @@ static inline int get_current_reg_code(int target_curr_ma, int ires_ua) if (!ires_ua || !target_curr_ma || (target_curr_ma < (ires_ua / 1000))) return 0; - return DIV_ROUND_UP(target_curr_ma * 1000, ires_ua) - 1; + return DIV_ROUND_CLOSEST(target_curr_ma * 1000, ires_ua) - 1; } static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data) diff --git a/drivers/leds/leds-qpnp-vibrator-ldo.c b/drivers/leds/leds-qpnp-vibrator-ldo.c new file mode 100644 index 0000000000000000000000000000000000000000..6a143247cd9bd510bc5759cb61d115c8899f07ae --- /dev/null +++ b/drivers/leds/leds-qpnp-vibrator-ldo.c @@ -0,0 +1,550 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Vibrator-LDO register definitions */ +#define QPNP_VIB_LDO_REG_STATUS1 0x08 +#define QPNP_VIB_LDO_VREG_READY BIT(7) + +#define QPNP_VIB_LDO_REG_VSET_LB 0x40 + +#define QPNP_VIB_LDO_REG_EN_CTL 0x46 +#define QPNP_VIB_LDO_EN BIT(7) + +/* Vibrator-LDO voltage settings */ +#define QPNP_VIB_LDO_VMIN_UV 1504000 +#define QPNP_VIB_LDO_VMAX_UV 3544000 +#define QPNP_VIB_LDO_VOLT_STEP_UV 8000 + +/* + * Define vibration periods: default(5sec), min(50ms), max(15sec) and + * overdrive(30ms). + */ +#define QPNP_VIB_MIN_PLAY_MS 50 +#define QPNP_VIB_PLAY_MS 5000 +#define QPNP_VIB_MAX_PLAY_MS 15000 +#define QPNP_VIB_OVERDRIVE_PLAY_MS 30 + +struct vib_ldo_chip { + struct led_classdev cdev; + struct regmap *regmap; + struct mutex lock; + struct hrtimer stop_timer; + struct hrtimer overdrive_timer; + struct work_struct vib_work; + struct work_struct overdrive_work; + + u16 base; + int vmax_uV; + int overdrive_volt_uV; + int ldo_uV; + int state; + u64 vib_play_ms; + bool vib_enabled; + bool disable_overdrive; +}; + +static int qpnp_vib_ldo_set_voltage(struct vib_ldo_chip *chip, int new_uV) +{ + unsigned int val; + u32 vlevel; + u8 reg[2]; + int ret; + + if (chip->ldo_uV == new_uV) + return 0; + + vlevel = roundup(new_uV, QPNP_VIB_LDO_VOLT_STEP_UV) / 1000; + reg[0] = vlevel & 0xff; + reg[1] = (vlevel & 0xff00) >> 8; + ret = regmap_bulk_write(chip->regmap, + chip->base + QPNP_VIB_LDO_REG_VSET_LB, reg, 2); + if (ret < 0) { + pr_err("regmap write failed, ret=%d\n", ret); + return ret; + } + + if (chip->vib_enabled) { + ret = regmap_read_poll_timeout(chip->regmap, + chip->base + QPNP_VIB_LDO_REG_STATUS1, + val, val & QPNP_VIB_LDO_VREG_READY, + 100, 1000); + if (ret < 0) { + pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n", + val, ret); + return ret; + } + } + + chip->ldo_uV = new_uV; + return ret; +} + +static inline int qpnp_vib_ldo_enable(struct vib_ldo_chip *chip, bool enable) +{ + unsigned int val; + int ret; + + if (chip->vib_enabled == enable) + return 0; + + ret = regmap_update_bits(chip->regmap, + chip->base + QPNP_VIB_LDO_REG_EN_CTL, + QPNP_VIB_LDO_EN, + enable ? QPNP_VIB_LDO_EN : 0); + if (ret < 0) { + pr_err("Program Vibrator LDO %s is failed, ret=%d\n", + enable ? "enable" : "disable", ret); + return ret; + } + + if (enable) { + ret = regmap_read_poll_timeout(chip->regmap, + chip->base + QPNP_VIB_LDO_REG_STATUS1, + val, val & QPNP_VIB_LDO_VREG_READY, + 100, 1000); + if (ret < 0) { + pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n", + val, ret); + return ret; + } + } + + chip->vib_enabled = enable; + + return ret; +} + +static int qpnp_vibrator_play_on(struct vib_ldo_chip *chip) +{ + int volt_uV; + int ret; + + volt_uV = chip->vmax_uV; + if (!chip->disable_overdrive) + volt_uV = chip->overdrive_volt_uV ? chip->overdrive_volt_uV + : min(chip->vmax_uV * 2, QPNP_VIB_LDO_VMAX_UV); + + ret = qpnp_vib_ldo_set_voltage(chip, volt_uV); + if (ret < 0) { + pr_err("set voltage = %duV failed, ret=%d\n", volt_uV, ret); + return ret; + } + pr_debug("voltage set to %d uV\n", volt_uV); + + ret = qpnp_vib_ldo_enable(chip, true); + if (ret < 0) { + pr_err("vibration enable failed, ret=%d\n", ret); + return ret; + } + + if (!chip->disable_overdrive) + hrtimer_start(&chip->overdrive_timer, + ms_to_ktime(QPNP_VIB_OVERDRIVE_PLAY_MS), + HRTIMER_MODE_REL); + + return ret; +} + +static void qpnp_vib_work(struct work_struct *work) +{ + struct vib_ldo_chip *chip = container_of(work, struct vib_ldo_chip, + vib_work); + int ret = 0; + + if (chip->state) { + if (!chip->vib_enabled) + ret = qpnp_vibrator_play_on(chip); + + if (ret == 0) + hrtimer_start(&chip->stop_timer, + ms_to_ktime(chip->vib_play_ms), + HRTIMER_MODE_REL); + } else { + if (!chip->disable_overdrive) { + hrtimer_cancel(&chip->overdrive_timer); + cancel_work_sync(&chip->overdrive_work); + } + qpnp_vib_ldo_enable(chip, false); + } +} + +static enum hrtimer_restart vib_stop_timer(struct hrtimer *timer) +{ + struct vib_ldo_chip *chip = container_of(timer, struct vib_ldo_chip, + stop_timer); + + chip->state = 0; + schedule_work(&chip->vib_work); + return HRTIMER_NORESTART; +} + +static void qpnp_vib_overdrive_work(struct work_struct *work) +{ + struct vib_ldo_chip *chip = container_of(work, struct vib_ldo_chip, + overdrive_work); + int ret; + + mutex_lock(&chip->lock); + + /* LDO voltage update not required if Vibration disabled */ + if (!chip->vib_enabled) + goto unlock; + + ret = qpnp_vib_ldo_set_voltage(chip, chip->vmax_uV); + if (ret < 0) { + pr_err("set vibration voltage = %duV failed, ret=%d\n", + chip->vmax_uV, ret); + qpnp_vib_ldo_enable(chip, false); + goto unlock; + } + pr_debug("voltage set to %d\n", chip->vmax_uV); + +unlock: + mutex_unlock(&chip->lock); +} + +static enum hrtimer_restart vib_overdrive_timer(struct hrtimer *timer) +{ + struct vib_ldo_chip *chip = container_of(timer, struct vib_ldo_chip, + overdrive_timer); + schedule_work(&chip->overdrive_work); + pr_debug("overdrive timer expired\n"); + return HRTIMER_NORESTART; +} + +static ssize_t qpnp_vib_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->vib_enabled); +} + +static ssize_t qpnp_vib_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /* At present, nothing to do with setting state */ + return count; +} + +static ssize_t qpnp_vib_show_duration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&chip->stop_timer)) { + time_rem = hrtimer_get_remaining(&chip->stop_timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t qpnp_vib_store_duration(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + if (val < QPNP_VIB_MIN_PLAY_MS) + val = QPNP_VIB_MIN_PLAY_MS; + + if (val > QPNP_VIB_MAX_PLAY_MS) + val = QPNP_VIB_MAX_PLAY_MS; + + mutex_lock(&chip->lock); + chip->vib_play_ms = val; + mutex_unlock(&chip->lock); + + return count; +} + +static ssize_t qpnp_vib_show_activate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", 0); +} + +static ssize_t qpnp_vib_store_activate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + if (val != 0 && val != 1) + return count; + + mutex_lock(&chip->lock); + hrtimer_cancel(&chip->stop_timer); + chip->state = val; + pr_debug("state = %d, time = %llums\n", chip->state, chip->vib_play_ms); + mutex_unlock(&chip->lock); + schedule_work(&chip->vib_work); + + return count; +} + +static ssize_t qpnp_vib_show_vmax(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->vmax_uV / 1000); +} + +static ssize_t qpnp_vib_store_vmax(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + int data, ret; + + ret = kstrtoint(buf, 10, &data); + if (ret < 0) + return ret; + + data = data * 1000; /* Convert to microvolts */ + + /* check against vibrator ldo min/max voltage limits */ + data = min(data, QPNP_VIB_LDO_VMAX_UV); + data = max(data, QPNP_VIB_LDO_VMIN_UV); + + mutex_lock(&chip->lock); + chip->vmax_uV = data; + mutex_unlock(&chip->lock); + return ret; +} + +static struct device_attribute qpnp_vib_attrs[] = { + __ATTR(state, 0664, qpnp_vib_show_state, qpnp_vib_store_state), + __ATTR(duration, 0664, qpnp_vib_show_duration, qpnp_vib_store_duration), + __ATTR(activate, 0664, qpnp_vib_show_activate, qpnp_vib_store_activate), + __ATTR(vmax_mv, 0664, qpnp_vib_show_vmax, qpnp_vib_store_vmax), +}; + +static int qpnp_vib_parse_dt(struct device *dev, struct vib_ldo_chip *chip) +{ + int ret; + + ret = of_property_read_u32(dev->of_node, "qcom,vib-ldo-volt-uv", + &chip->vmax_uV); + if (ret < 0) { + pr_err("qcom,vib-ldo-volt-uv property read failed, ret=%d\n", + ret); + return ret; + } + + chip->disable_overdrive = of_property_read_bool(dev->of_node, + "qcom,disable-overdrive"); + + if (of_find_property(dev->of_node, "qcom,vib-overdrive-volt-uv", + NULL)) { + ret = of_property_read_u32(dev->of_node, + "qcom,vib-overdrive-volt-uv", + &chip->overdrive_volt_uV); + if (ret < 0) { + pr_err("qcom,vib-overdrive-volt-uv property read failed, ret=%d\n", + ret); + return ret; + } + + /* check against vibrator ldo min/max voltage limits */ + chip->overdrive_volt_uV = min(chip->overdrive_volt_uV, + QPNP_VIB_LDO_VMAX_UV); + chip->overdrive_volt_uV = max(chip->overdrive_volt_uV, + QPNP_VIB_LDO_VMIN_UV); + } + + return ret; +} + +/* Dummy functions for brightness */ +static enum led_brightness qpnp_vib_brightness_get(struct led_classdev *cdev) +{ + return 0; +} + +static void qpnp_vib_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ +} + +static int qpnp_vibrator_ldo_suspend(struct device *dev) +{ + struct vib_ldo_chip *chip = dev_get_drvdata(dev); + + mutex_lock(&chip->lock); + if (!chip->disable_overdrive) { + hrtimer_cancel(&chip->overdrive_timer); + cancel_work_sync(&chip->overdrive_work); + } + hrtimer_cancel(&chip->stop_timer); + cancel_work_sync(&chip->vib_work); + mutex_unlock(&chip->lock); + + return 0; +} +static SIMPLE_DEV_PM_OPS(qpnp_vibrator_ldo_pm_ops, qpnp_vibrator_ldo_suspend, + NULL); + +static int qpnp_vibrator_ldo_probe(struct platform_device *pdev) +{ + struct device_node *of_node = pdev->dev.of_node; + struct vib_ldo_chip *chip; + int i, ret; + u32 base; + + ret = of_property_read_u32(of_node, "reg", &base); + if (ret < 0) { + pr_err("reg property reading failed, ret=%d\n", ret); + return ret; + } + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chip->regmap) { + pr_err("couldn't get parent's regmap\n"); + return -EINVAL; + } + + ret = qpnp_vib_parse_dt(&pdev->dev, chip); + if (ret < 0) { + pr_err("couldn't parse device tree, ret=%d\n", ret); + return ret; + } + + chip->base = (uint16_t)base; + chip->vib_play_ms = QPNP_VIB_PLAY_MS; + mutex_init(&chip->lock); + INIT_WORK(&chip->vib_work, qpnp_vib_work); + INIT_WORK(&chip->overdrive_work, qpnp_vib_overdrive_work); + + hrtimer_init(&chip->stop_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + chip->stop_timer.function = vib_stop_timer; + hrtimer_init(&chip->overdrive_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + chip->overdrive_timer.function = vib_overdrive_timer; + dev_set_drvdata(&pdev->dev, chip); + + chip->cdev.name = "vibrator"; + chip->cdev.brightness_get = qpnp_vib_brightness_get; + chip->cdev.brightness_set = qpnp_vib_brightness_set; + chip->cdev.max_brightness = 100; + ret = devm_led_classdev_register(&pdev->dev, &chip->cdev); + if (ret < 0) { + pr_err("Error in registering led class device, ret=%d\n", ret); + goto fail; + } + + for (i = 0; i < ARRAY_SIZE(qpnp_vib_attrs); i++) { + ret = sysfs_create_file(&chip->cdev.dev->kobj, + &qpnp_vib_attrs[i].attr); + if (ret < 0) { + dev_err(&pdev->dev, "Error in creating sysfs file, ret=%d\n", + ret); + goto sysfs_fail; + } + } + + pr_info("Vibrator LDO successfully registered: uV = %d, overdrive = %s\n", + chip->vmax_uV, + chip->disable_overdrive ? "disabled" : "enabled"); + return 0; + +sysfs_fail: + for (--i; i >= 0; i--) + sysfs_remove_file(&chip->cdev.dev->kobj, + &qpnp_vib_attrs[i].attr); +fail: + mutex_destroy(&chip->lock); + dev_set_drvdata(&pdev->dev, NULL); + return ret; +} + +static int qpnp_vibrator_ldo_remove(struct platform_device *pdev) +{ + struct vib_ldo_chip *chip = dev_get_drvdata(&pdev->dev); + + if (!chip->disable_overdrive) { + hrtimer_cancel(&chip->overdrive_timer); + cancel_work_sync(&chip->overdrive_work); + } + hrtimer_cancel(&chip->stop_timer); + cancel_work_sync(&chip->vib_work); + mutex_destroy(&chip->lock); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static const struct of_device_id vibrator_ldo_match_table[] = { + { .compatible = "qcom,qpnp-vibrator-ldo" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, vibrator_ldo_match_table); + +static struct platform_driver qpnp_vibrator_ldo_driver = { + .driver = { + .name = "qcom,qpnp-vibrator-ldo", + .of_match_table = vibrator_ldo_match_table, + .pm = &qpnp_vibrator_ldo_pm_ops, + }, + .probe = qpnp_vibrator_ldo_probe, + .remove = qpnp_vibrator_ldo_remove, +}; +module_platform_driver(qpnp_vibrator_ldo_driver); + +MODULE_DESCRIPTION("QCOM QPNP Vibrator-LDO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-qti-tri-led.c b/drivers/leds/leds-qti-tri-led.c index 09485d53fccb6d9b05898b11e6a7ac8d2cabc60d..4798689f73cbf19757aec1dc9575637e87d36f49 100644 --- a/drivers/leds/leds-qti-tri-led.c +++ b/drivers/leds/leds-qti-tri-led.c @@ -54,6 +54,7 @@ struct led_setting { u32 off_ms; enum led_brightness brightness; bool blink; + bool breath; }; struct qpnp_led_dev { @@ -67,6 +68,7 @@ struct qpnp_led_dev { const char *default_trigger; u8 id; bool blinking; + bool breathing; }; struct qpnp_tri_led_chip { @@ -122,6 +124,10 @@ static int __tri_led_config_pwm(struct qpnp_led_dev *led, pstate.enabled = !!(pwm->duty_ns != 0); pstate.period = pwm->period_ns; pstate.duty_cycle = pwm->duty_ns; + pstate.output_type = led->led_setting.breath ? + PWM_OUTPUT_MODULATED : PWM_OUTPUT_FIXED; + /* Use default pattern in PWM device */ + pstate.output_pattern = NULL; rc = pwm_apply_state(led->pwm_dev, &pstate); if (rc < 0) @@ -237,7 +243,9 @@ static int qpnp_tri_led_set(struct qpnp_led_dev *led) /* Use initial period if no blinking is required */ period_ns = led->pwm_setting.pre_period_ns; - if (period_ns > INT_MAX / brightness) + if (brightness == LED_OFF) + duty_ns = 0; + else if (period_ns > INT_MAX / brightness) duty_ns = (period_ns / LED_FULL) * brightness; else duty_ns = (period_ns * brightness) / LED_FULL; @@ -261,9 +269,15 @@ static int qpnp_tri_led_set(struct qpnp_led_dev *led) if (led->led_setting.blink) { led->cdev.brightness = LED_FULL; led->blinking = true; + led->breathing = false; + } else if (led->led_setting.breath) { + led->cdev.brightness = LED_FULL; + led->blinking = false; + led->breathing = true; } else { led->cdev.brightness = led->led_setting.brightness; led->blinking = false; + led->breathing = false; } return rc; @@ -281,7 +295,7 @@ static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev, brightness = LED_FULL; if (brightness == led->led_setting.brightness && - !led->blinking) { + !led->blinking && !led->breathing) { mutex_unlock(&led->lock); return 0; } @@ -292,6 +306,7 @@ static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev, else led->led_setting.on_ms = 0; led->led_setting.blink = false; + led->led_setting.breath = false; rc = qpnp_tri_led_set(led); if (rc) @@ -327,14 +342,17 @@ static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev, if (*on_ms == 0) { led->led_setting.blink = false; + led->led_setting.breath = false; led->led_setting.brightness = LED_OFF; } else if (*off_ms == 0) { led->led_setting.blink = false; + led->led_setting.breath = false; led->led_setting.brightness = led->cdev.brightness; } else { led->led_setting.on_ms = *on_ms; led->led_setting.off_ms = *off_ms; led->led_setting.blink = true; + led->led_setting.breath = false; } rc = qpnp_tri_led_set(led); @@ -346,6 +364,52 @@ static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev, return rc; } +static ssize_t breath_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", led->led_setting.breath); +} + +static ssize_t breath_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + bool breath; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + + rc = kstrtobool(buf, &breath); + if (rc < 0) + return rc; + + mutex_lock(&led->lock); + if (led->breathing == breath) + goto unlock; + + led->led_setting.blink = false; + led->led_setting.breath = breath; + led->led_setting.brightness = breath ? LED_FULL : LED_OFF; + rc = qpnp_tri_led_set(led); + if (rc < 0) + dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n", + led->label, rc); + +unlock: + mutex_unlock(&led->lock); + return (rc < 0) ? rc : count; +} + +static DEVICE_ATTR(breath, 0644, breath_show, breath_store); +static const struct attribute *breath_attrs[] = { + &dev_attr_breath.attr, + NULL +}; + static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip) { struct qpnp_led_dev *led; @@ -367,15 +431,30 @@ static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip) if (rc < 0) { dev_err(chip->dev, "%s led class device registering failed, rc=%d\n", led->label, rc); - goto destroy; + goto err_out; + } + + if (pwm_get_output_type_supported(led->pwm_dev) + & PWM_OUTPUT_MODULATED) { + rc = sysfs_create_files(&led->cdev.dev->kobj, + breath_attrs); + if (rc < 0) { + dev_err(chip->dev, "Create breath file for %s led failed, rc=%d\n", + led->label, rc); + goto err_out; + } } } return 0; -destroy: - for (j = 0; j <= i; j++) - mutex_destroy(&chip->leds[i].lock); +err_out: + for (j = 0; j <= i; j++) { + if (j < i) + sysfs_remove_files(&chip->leds[j].cdev.dev->kobj, + breath_attrs); + mutex_destroy(&chip->leds[j].lock); + } return rc; } @@ -551,8 +630,10 @@ static int qpnp_tri_led_remove(struct platform_device *pdev) struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev); mutex_destroy(&chip->bus_lock); - for (i = 0; i < chip->num_leds; i++) + for (i = 0; i < chip->num_leds; i++) { + sysfs_remove_files(&chip->leds[i].cdev.dev->kobj, breath_attrs); mutex_destroy(&chip->leds[i].lock); + } dev_set_drvdata(chip->dev, NULL); return 0; } diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c index 934b1fce4ce1f0660d8c761eb0ec9822147fb754..8c13a9036d07fc4e1ee75c1abf5380ebde26337e 100644 --- a/drivers/md/bcache/alloc.c +++ b/drivers/md/bcache/alloc.c @@ -287,8 +287,10 @@ do { \ break; \ \ mutex_unlock(&(ca)->set->bucket_lock); \ - if (kthread_should_stop()) \ + if (kthread_should_stop()) { \ + set_current_state(TASK_RUNNING); \ return 0; \ + } \ \ schedule(); \ mutex_lock(&(ca)->set->bucket_lock); \ @@ -515,15 +517,21 @@ struct open_bucket { /* * We keep multiple buckets open for writes, and try to segregate different - * write streams for better cache utilization: first we look for a bucket where - * the last write to it was sequential with the current write, and failing that - * we look for a bucket that was last used by the same task. + * write streams for better cache utilization: first we try to segregate flash + * only volume write streams from cached devices, secondly we look for a bucket + * where the last write to it was sequential with the current write, and + * failing that we look for a bucket that was last used by the same task. * * The ideas is if you've got multiple tasks pulling data into the cache at the * same time, you'll get better cache utilization if you try to segregate their * data and preserve locality. * - * For example, say you've starting Firefox at the same time you're copying a + * For example, dirty sectors of flash only volume is not reclaimable, if their + * dirty sectors mixed with dirty sectors of cached device, such buckets will + * be marked as dirty and won't be reclaimed, though the dirty data of cached + * device have been written back to backend device. + * + * And say you've starting Firefox at the same time you're copying a * bunch of files. Firefox will likely end up being fairly hot and stay in the * cache awhile, but the data you copied might not be; if you wrote all that * data to the same buckets it'd get invalidated at the same time. @@ -540,7 +548,10 @@ static struct open_bucket *pick_data_bucket(struct cache_set *c, struct open_bucket *ret, *ret_task = NULL; list_for_each_entry_reverse(ret, &c->data_buckets, list) - if (!bkey_cmp(&ret->key, search)) + if (UUID_FLASH_ONLY(&c->uuids[KEY_INODE(&ret->key)]) != + UUID_FLASH_ONLY(&c->uuids[KEY_INODE(search)])) + continue; + else if (!bkey_cmp(&ret->key, search)) goto found; else if (ret->last_write_point == write_point) ret_task = ret; diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index abd31e847f967203e9f702cb4ca6e1772f6a7d46..e4a3f692057b84a5eb94be03b031cf48a54b4d02 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -906,7 +906,7 @@ void bcache_write_super(struct cache_set *); int bch_flash_dev_create(struct cache_set *c, uint64_t size); -int bch_cached_dev_attach(struct cached_dev *, struct cache_set *); +int bch_cached_dev_attach(struct cached_dev *, struct cache_set *, uint8_t *); void bch_cached_dev_detach(struct cached_dev *); void bch_cached_dev_run(struct cached_dev *); void bcache_device_stop(struct bcache_device *); diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 1598d1e04989ac5be90f96c2168e9981835c16cf..89d088cf95d96817ac7e7808327fd94606446366 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1868,14 +1868,17 @@ void bch_initial_gc_finish(struct cache_set *c) */ for_each_cache(ca, c, i) { for_each_bucket(b, ca) { - if (fifo_full(&ca->free[RESERVE_PRIO])) + if (fifo_full(&ca->free[RESERVE_PRIO]) && + fifo_full(&ca->free[RESERVE_BTREE])) break; if (bch_can_invalidate_bucket(ca, b) && !GC_MARK(b)) { __bch_invalidate_one_bucket(ca, b); - fifo_push(&ca->free[RESERVE_PRIO], - b - ca->buckets); + if (!fifo_push(&ca->free[RESERVE_PRIO], + b - ca->buckets)) + fifo_push(&ca->free[RESERVE_BTREE], + b - ca->buckets); } } } diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index e9fbf2bcd122b8d47b9cbd012cc408c2a0db534c..f34ad8720756043f0078dd0721874d477eddfe71 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -568,6 +568,7 @@ static void cache_lookup(struct closure *cl) { struct search *s = container_of(cl, struct search, iop.cl); struct bio *bio = &s->bio.bio; + struct cached_dev *dc; int ret; bch_btree_op_init(&s->op, -1); @@ -580,6 +581,27 @@ static void cache_lookup(struct closure *cl) return; } + /* + * We might meet err when searching the btree, If that happens, we will + * get negative ret, in this scenario we should not recover data from + * backing device (when cache device is dirty) because we don't know + * whether bkeys the read request covered are all clean. + * + * And after that happened, s->iop.status is still its initial value + * before we submit s->bio.bio + */ + if (ret < 0) { + BUG_ON(ret == -EINTR); + if (s->d && s->d->c && + !UUID_FLASH_ONLY(&s->d->c->uuids[s->d->id])) { + dc = container_of(s->d, struct cached_dev, disk); + if (dc && atomic_read(&dc->has_dirty)) + s->recoverable = false; + } + if (!s->iop.status) + s->iop.status = BLK_STS_IOERR; + } + closure_return(cl); } diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 9417170f180a9ff09f3e48d0a99057bd2754e316..fe6e4c319b7cf2b34c5de57e8f3bac9903c0ad62 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -893,6 +893,12 @@ static void cached_dev_detach_finish(struct work_struct *w) mutex_lock(&bch_register_lock); + cancel_delayed_work_sync(&dc->writeback_rate_update); + if (!IS_ERR_OR_NULL(dc->writeback_thread)) { + kthread_stop(dc->writeback_thread); + dc->writeback_thread = NULL; + } + memset(&dc->sb.set_uuid, 0, 16); SET_BDEV_STATE(&dc->sb, BDEV_STATE_NONE); @@ -933,7 +939,8 @@ void bch_cached_dev_detach(struct cached_dev *dc) cached_dev_put(dc); } -int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) +int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c, + uint8_t *set_uuid) { uint32_t rtime = cpu_to_le32(get_seconds()); struct uuid_entry *u; @@ -942,7 +949,8 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) bdevname(dc->bdev, buf); - if (memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16)) + if ((set_uuid && memcmp(set_uuid, c->sb.set_uuid, 16)) || + (!set_uuid && memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16))) return -ENOENT; if (dc->disk.c) { @@ -1184,7 +1192,7 @@ static void register_bdev(struct cache_sb *sb, struct page *sb_page, list_add(&dc->list, &uncached_devices); list_for_each_entry(c, &bch_cache_sets, list) - bch_cached_dev_attach(dc, c); + bch_cached_dev_attach(dc, c, NULL); if (BDEV_STATE(&dc->sb) == BDEV_STATE_NONE || BDEV_STATE(&dc->sb) == BDEV_STATE_STALE) @@ -1706,7 +1714,7 @@ static void run_cache_set(struct cache_set *c) bcache_write_super(c); list_for_each_entry_safe(dc, t, &uncached_devices, list) - bch_cached_dev_attach(dc, c); + bch_cached_dev_attach(dc, c, NULL); flash_devs_run(c); @@ -1823,6 +1831,7 @@ void bch_cache_release(struct kobject *kobj) static int cache_alloc(struct cache *ca) { size_t free; + size_t btree_buckets; struct bucket *b; __module_get(THIS_MODULE); @@ -1830,9 +1839,19 @@ static int cache_alloc(struct cache *ca) bio_init(&ca->journal.bio, ca->journal.bio.bi_inline_vecs, 8); + /* + * when ca->sb.njournal_buckets is not zero, journal exists, + * and in bch_journal_replay(), tree node may split, + * so bucket of RESERVE_BTREE type is needed, + * the worst situation is all journal buckets are valid journal, + * and all the keys need to replay, + * so the number of RESERVE_BTREE type buckets should be as much + * as journal buckets + */ + btree_buckets = ca->sb.njournal_buckets ?: 8; free = roundup_pow_of_two(ca->sb.nbuckets) >> 10; - if (!init_fifo(&ca->free[RESERVE_BTREE], 8, GFP_KERNEL) || + if (!init_fifo(&ca->free[RESERVE_BTREE], btree_buckets, GFP_KERNEL) || !init_fifo_exact(&ca->free[RESERVE_PRIO], prio_buckets(ca), GFP_KERNEL) || !init_fifo(&ca->free[RESERVE_MOVINGGC], free, GFP_KERNEL) || !init_fifo(&ca->free[RESERVE_NONE], free, GFP_KERNEL) || diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 234b2f5b286df209984ed1223ae51cedfbb9e314..5d81cd06af00c816432e063a2154e8df2bc1b49f 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -193,7 +193,7 @@ STORE(__cached_dev) { struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); - ssize_t v = size; + ssize_t v; struct cache_set *c; struct kobj_uevent_env *env; @@ -265,17 +265,20 @@ STORE(__cached_dev) } if (attr == &sysfs_attach) { - if (bch_parse_uuid(buf, dc->sb.set_uuid) < 16) + uint8_t set_uuid[16]; + + if (bch_parse_uuid(buf, set_uuid) < 16) return -EINVAL; + v = -ENOENT; list_for_each_entry(c, &bch_cache_sets, list) { - v = bch_cached_dev_attach(dc, c); + v = bch_cached_dev_attach(dc, c, set_uuid); if (!v) return size; } pr_err("Can't attach %s: cache set not found", buf); - size = v; + return v; } if (attr == &sysfs_detach && dc->disk.c) diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 70454f2ad2faacde608f40638dd55955eed4e586..f046dedc59ab979f5200cd1509d9d7813e38e902 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -420,18 +420,21 @@ static int bch_writeback_thread(void *arg) while (!kthread_should_stop()) { down_write(&dc->writeback_lock); + set_current_state(TASK_INTERRUPTIBLE); if (!atomic_read(&dc->has_dirty) || (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) && !dc->writeback_running)) { up_write(&dc->writeback_lock); - set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); return 0; + } schedule(); continue; } + set_current_state(TASK_RUNNING); searched_full_index = refill_dirty(dc); diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 554d60394c0663980d89c3bb84a48007f365470b..f575110454b6a2dd7df499e07fe15141184a7d06 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -148,6 +148,8 @@ struct crypt_config { mempool_t *tag_pool; unsigned tag_pool_max_sectors; + struct percpu_counter n_allocated_pages; + struct bio_set *bs; struct mutex bio_alloc_lock; @@ -219,6 +221,12 @@ struct crypt_config { #define MAX_TAG_SIZE 480 #define POOL_ENTRY_SIZE 512 +static DEFINE_SPINLOCK(dm_crypt_clients_lock); +static unsigned dm_crypt_clients_n = 0; +static volatile unsigned long dm_crypt_pages_per_client; +#define DM_CRYPT_MEMORY_PERCENT 2 +#define DM_CRYPT_MIN_PAGES_PER_CLIENT (BIO_MAX_PAGES * 16) + static void clone_init(struct dm_crypt_io *, struct bio *); static void kcryptd_queue_crypt(struct dm_crypt_io *io); static struct scatterlist *crypt_get_sg_data(struct crypt_config *cc, @@ -2156,6 +2164,43 @@ static int crypt_wipe_key(struct crypt_config *cc) return r; } +static void crypt_calculate_pages_per_client(void) +{ + unsigned long pages = (totalram_pages - totalhigh_pages) * DM_CRYPT_MEMORY_PERCENT / 100; + + if (!dm_crypt_clients_n) + return; + + pages /= dm_crypt_clients_n; + if (pages < DM_CRYPT_MIN_PAGES_PER_CLIENT) + pages = DM_CRYPT_MIN_PAGES_PER_CLIENT; + dm_crypt_pages_per_client = pages; +} + +static void *crypt_page_alloc(gfp_t gfp_mask, void *pool_data) +{ + struct crypt_config *cc = pool_data; + struct page *page; + + if (unlikely(percpu_counter_compare(&cc->n_allocated_pages, dm_crypt_pages_per_client) >= 0) && + likely(gfp_mask & __GFP_NORETRY)) + return NULL; + + page = alloc_page(gfp_mask); + if (likely(page != NULL)) + percpu_counter_add(&cc->n_allocated_pages, 1); + + return page; +} + +static void crypt_page_free(void *page, void *pool_data) +{ + struct crypt_config *cc = pool_data; + + __free_page(page); + percpu_counter_sub(&cc->n_allocated_pages, 1); +} + static void crypt_dtr(struct dm_target *ti) { struct crypt_config *cc = ti->private; @@ -2182,6 +2227,10 @@ static void crypt_dtr(struct dm_target *ti) mempool_destroy(cc->req_pool); mempool_destroy(cc->tag_pool); + if (cc->page_pool) + WARN_ON(percpu_counter_sum(&cc->n_allocated_pages) != 0); + percpu_counter_destroy(&cc->n_allocated_pages); + if (cc->iv_gen_ops && cc->iv_gen_ops->dtr) cc->iv_gen_ops->dtr(cc); @@ -2196,6 +2245,12 @@ static void crypt_dtr(struct dm_target *ti) /* Must zero key material before freeing */ kzfree(cc); + + spin_lock(&dm_crypt_clients_lock); + WARN_ON(!dm_crypt_clients_n); + dm_crypt_clients_n--; + crypt_calculate_pages_per_client(); + spin_unlock(&dm_crypt_clients_lock); } static int crypt_ctr_ivmode(struct dm_target *ti, const char *ivmode) @@ -2643,6 +2698,15 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->private = cc; + spin_lock(&dm_crypt_clients_lock); + dm_crypt_clients_n++; + crypt_calculate_pages_per_client(); + spin_unlock(&dm_crypt_clients_lock); + + ret = percpu_counter_init(&cc->n_allocated_pages, 0, GFP_KERNEL); + if (ret < 0) + goto bad; + /* Optional parameters need to be read before cipher constructor */ if (argc > 5) { ret = crypt_ctr_optional(ti, argc - 5, &argv[5]); @@ -2697,7 +2761,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) ALIGN(sizeof(struct dm_crypt_io) + cc->dmreq_start + additional_req_size, ARCH_KMALLOC_MINALIGN); - cc->page_pool = mempool_create_page_pool(BIO_MAX_PAGES, 0); + cc->page_pool = mempool_create(BIO_MAX_PAGES, crypt_page_alloc, crypt_page_free, cc); if (!cc->page_pool) { ti->error = "Cannot allocate page mempool"; goto bad; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 2704a55f8b6e51fd7e1e3196ddfacad111ed1689..8b7328666eaafc89384afa198a41da94df429a25 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -502,8 +502,20 @@ static int multipath_clone_and_map(struct dm_target *ti, struct request *rq, if (queue_dying) { atomic_inc(&m->pg_init_in_progress); activate_or_offline_path(pgpath); + return DM_MAPIO_DELAY_REQUEUE; } - return DM_MAPIO_DELAY_REQUEUE; + + /* + * blk-mq's SCHED_RESTART can cover this requeue, so we + * needn't deal with it by DELAY_REQUEUE. More importantly, + * we have to return DM_MAPIO_REQUEUE so that blk-mq can + * get the queue busy feedback (via BLK_STS_RESOURCE), + * otherwise I/O merging can suffer. + */ + if (q->mq_ops) + return DM_MAPIO_REQUEUE; + else + return DM_MAPIO_DELAY_REQUEUE; } clone->bio = clone->biotail = NULL; clone->rq_disk = bdev->bd_disk; diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index e0503aa6afb294d6050fcbfae5c097b9ae4e44a7..78f66d9c92ed3cb90b93bcce36d9e0030564f14a 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -32,6 +32,7 @@ #define DM_VERITY_OPT_LOGGING "ignore_corruption" #define DM_VERITY_OPT_RESTART "restart_on_corruption" #define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks" +#define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once" #define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC) @@ -473,6 +474,18 @@ static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io, return 0; } +/* + * Moves the bio iter one data block forward. + */ +static inline void verity_bv_skip_block(struct dm_verity *v, + struct dm_verity_io *io, + struct bvec_iter *iter) +{ + struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size); + + bio_advance_iter(bio, iter, 1 << v->data_dev_block_bits); +} + /* * Verify one "dm_verity_io" structure. */ @@ -486,9 +499,16 @@ static int verity_verify_io(struct dm_verity_io *io) for (b = 0; b < io->n_blocks; b++) { int r; + sector_t cur_block = io->block + b; struct ahash_request *req = verity_io_hash_req(v, io); - r = verity_hash_for_block(v, io, io->block + b, + if (v->validated_blocks && + likely(test_bit(cur_block, v->validated_blocks))) { + verity_bv_skip_block(v, io, &io->iter); + continue; + } + + r = verity_hash_for_block(v, io, cur_block, verity_io_want_digest(v, io), &is_zero); if (unlikely(r < 0)) @@ -522,13 +542,16 @@ static int verity_verify_io(struct dm_verity_io *io) return r; if (likely(memcmp(verity_io_real_digest(v, io), - verity_io_want_digest(v, io), v->digest_size) == 0)) + verity_io_want_digest(v, io), v->digest_size) == 0)) { + if (v->validated_blocks) + set_bit(cur_block, v->validated_blocks); continue; + } else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA, - io->block + b, NULL, &start) == 0) + cur_block, NULL, &start) == 0) continue; else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA, - io->block + b)) + cur_block)) return -EIO; } @@ -722,6 +745,8 @@ void verity_status(struct dm_target *ti, status_type_t type, args += DM_VERITY_OPTS_FEC; if (v->zero_digest) args++; + if (v->validated_blocks) + args++; if (!args) return; DMEMIT(" %u", args); @@ -740,6 +765,8 @@ void verity_status(struct dm_target *ti, status_type_t type, } if (v->zero_digest) DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES); + if (v->validated_blocks) + DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE); sz = verity_fec_status_table(v, sz, result, maxlen); break; } @@ -793,6 +820,7 @@ void verity_dtr(struct dm_target *ti) if (v->bufio) dm_bufio_client_destroy(v->bufio); + kvfree(v->validated_blocks); kfree(v->salt); kfree(v->root_digest); kfree(v->zero_digest); @@ -814,6 +842,26 @@ void verity_dtr(struct dm_target *ti) } EXPORT_SYMBOL_GPL(verity_dtr); +static int verity_alloc_most_once(struct dm_verity *v) +{ + struct dm_target *ti = v->ti; + + /* the bitset can only handle INT_MAX blocks */ + if (v->data_blocks > INT_MAX) { + ti->error = "device too large to use check_at_most_once"; + return -E2BIG; + } + + v->validated_blocks = kvzalloc(BITS_TO_LONGS(v->data_blocks) * + sizeof(unsigned long), GFP_KERNEL); + if (!v->validated_blocks) { + ti->error = "failed to allocate bitset for check_at_most_once"; + return -ENOMEM; + } + + return 0; +} + static int verity_alloc_zero_digest(struct dm_verity *v) { int r = -ENOMEM; @@ -883,6 +931,12 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v) } continue; + } else if (!strcasecmp(arg_name, DM_VERITY_OPT_AT_MOST_ONCE)) { + r = verity_alloc_most_once(v); + if (r) + return r; + continue; + } else if (verity_is_fec_opt_arg(arg_name)) { r = verity_fec_parse_opt_args(as, v, &argc, arg_name); if (r) @@ -1151,7 +1205,7 @@ EXPORT_SYMBOL_GPL(verity_ctr); static struct target_type verity_target = { .name = "verity", - .version = {1, 3, 0}, + .version = {1, 4, 0}, .module = THIS_MODULE, .ctr = verity_ctr, .dtr = verity_dtr, diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 29831d66d9c200e8d155040eb2e3086f1da3bcf4..e80e06aa5ec6876f8dd2596c4a485157b97cd111 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -63,6 +63,7 @@ struct dm_verity { sector_t hash_level_block[DM_VERITY_MAX_LEVELS]; struct dm_verity_fec *fec; /* forward error correction */ + unsigned long *validated_blocks; /* bitset blocks validated */ }; struct dm_verity_io { diff --git a/drivers/md/md.c b/drivers/md/md.c index e058c209bbcfa7f8ee218a677cea694e16f66f3d..7671b14756075732b749792f83f5956fb9557446 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5357,7 +5357,7 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) return NULL; } -static int add_named_array(const char *val, struct kernel_param *kp) +static int add_named_array(const char *val, const struct kernel_param *kp) { /* * val must be "md_*" or "mdNNN". @@ -9278,11 +9278,11 @@ static __exit void md_exit(void) subsys_initcall(md_init); module_exit(md_exit) -static int get_ro(char *buffer, struct kernel_param *kp) +static int get_ro(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%d", start_readonly); } -static int set_ro(const char *val, struct kernel_param *kp) +static int set_ro(const char *val, const struct kernel_param *kp) { return kstrtouint(val, 10, (unsigned int *)&start_readonly); } diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index e79f72b8b858077f7b646caf7606afb42a830411..98de74862d472531ecea493e47a6241947f31418 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -54,9 +54,10 @@ static int media_device_close(struct file *filp) return 0; } -static int media_device_get_info(struct media_device *dev, - struct media_device_info *info) +static long media_device_get_info(struct media_device *dev, void *arg) { + struct media_device_info *info = (struct media_device_info *)arg; + memset(info, 0, sizeof(*info)); if (dev->driver_name[0]) @@ -93,9 +94,9 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) return NULL; } -static long media_device_enum_entities(struct media_device *mdev, - struct media_entity_desc *entd) +static long media_device_enum_entities(struct media_device *mdev, void *arg) { + struct media_entity_desc *entd = (struct media_entity_desc *)arg; struct media_entity *ent; ent = find_entity(mdev, entd->id); @@ -146,9 +147,9 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad, upad->flags = kpad->flags; } -static long media_device_enum_links(struct media_device *mdev, - struct media_links_enum *links) +static long media_device_enum_links(struct media_device *mdev, void *arg) { + struct media_links_enum *links = (struct media_links_enum *)arg; struct media_entity *entity; entity = find_entity(mdev, links->entity); @@ -194,9 +195,9 @@ static long media_device_enum_links(struct media_device *mdev, return 0; } -static long media_device_setup_link(struct media_device *mdev, - struct media_link_desc *linkd) +static long media_device_setup_link(struct media_device *mdev, void *arg) { + struct media_link_desc *linkd = (struct media_link_desc *)arg; struct media_link *link = NULL; struct media_entity *source; struct media_entity *sink; @@ -222,9 +223,9 @@ static long media_device_setup_link(struct media_device *mdev, return __media_entity_setup_link(link, linkd->flags); } -static long media_device_get_topology(struct media_device *mdev, - struct media_v2_topology *topo) +static long media_device_get_topology(struct media_device *mdev, void *arg) { + struct media_v2_topology *topo = (struct media_v2_topology *)arg; struct media_entity *entity; struct media_interface *intf; struct media_pad *pad; diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c index 336e2f9bc1b6363045f4890c662299d8009f5e30..b762e5f0ba1d55c5916b43c9f9e070689151f61b 100644 --- a/drivers/media/pci/tw686x/tw686x-core.c +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -72,12 +72,12 @@ static const char *dma_mode_name(unsigned int mode) } } -static int tw686x_dma_mode_get(char *buffer, struct kernel_param *kp) +static int tw686x_dma_mode_get(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%s", dma_mode_name(dma_mode)); } -static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp) +static int tw686x_dma_mode_set(const char *val, const struct kernel_param *kp) { if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY))) dma_mode = TW686X_DMA_MODE_MEMCPY; diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/Makefile index a97e2a0a68eb8ea2c1b52f4973d97ac47f12bdc5..65c23274e5ae171eb13743928453ece65fbd14b6 100644 --- a/drivers/media/platform/msm/camera/cam_sensor_module/Makefile +++ b/drivers/media/platform/msm/camera/cam_sensor_module/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_actuator/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor/ -#obj-$(CONFIG_SPECTRA_CAMERA) += cam_flash/ +obj-$(CONFIG_SPECTRA_CAMERA) += cam_flash/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_eeprom/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_ois/ diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c index 37e768324f08dae1bd839e439358f37960b4e92c..49e3847512580411cc296fc43f55544df684481a 100644 --- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c +++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c @@ -31,10 +31,10 @@ static int cam_eeprom_read_memory(struct cam_eeprom_ctrl_t *e_ctrl, { int rc = 0; int j; - struct cam_sensor_i2c_reg_setting i2c_reg_settings; - struct cam_sensor_i2c_reg_array i2c_reg_array; + struct cam_sensor_i2c_reg_setting i2c_reg_settings = {0}; + struct cam_sensor_i2c_reg_array i2c_reg_array = {0}; struct cam_eeprom_memory_map_t *emap = block->map; - struct cam_eeprom_soc_private *eb_info; + struct cam_eeprom_soc_private *eb_info = NULL; uint8_t *memptr = block->mapdata; if (!e_ctrl) { diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c index ec163a726832a339f2ed137e808899e15b6c2ce5..ea3a18edddfbd26de366c2f4956d8c4a8f068475 100644 --- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c +++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c @@ -184,7 +184,7 @@ static int cam_flash_ops(struct cam_flash_ctrl *flash_ctrl, if (flash_ctrl->switch_trigger) cam_res_mgr_led_trigger_event( flash_ctrl->switch_trigger, - LED_SWITCH_ON); + (enum led_brightness)LED_SWITCH_ON); return 0; } @@ -212,7 +212,7 @@ int cam_flash_off(struct cam_flash_ctrl *flash_ctrl) if (flash_ctrl->switch_trigger) cam_res_mgr_led_trigger_event(flash_ctrl->switch_trigger, - LED_SWITCH_OFF); + (enum led_brightness)LED_SWITCH_OFF); flash_ctrl->flash_state = CAM_FLASH_STATE_START; return 0; diff --git a/drivers/media/platform/msm/npu/Makefile b/drivers/media/platform/msm/npu/Makefile index f80219b4a3fb80bdc183b0cd75f68d73152a05f4..712f22e2fca9ec90de5b3a165adbece58f0d7577 100644 --- a/drivers/media/platform/msm/npu/Makefile +++ b/drivers/media/platform/msm/npu/Makefile @@ -1 +1,7 @@ -obj-$(CONFIG_MSM_NPU) += npu_dev.o +obj-$(CONFIG_MSM_NPU) := msm_npu.o +msm_npu-objs := npu_dbg.o \ + npu_dev.o \ + npu_debugfs.o \ + npu_host_ipc.o \ + npu_hw_access.o \ + npu_mgr.o diff --git a/drivers/media/platform/msm/npu/npu_common.h b/drivers/media/platform/msm/npu/npu_common.h index 5c4a6b89ae7b3a785479838f9446ff6617834452..fc72013c7bc73a8fd5d46704380df5ceca554aca 100644 --- a/drivers/media/platform/msm/npu/npu_common.h +++ b/drivers/media/platform/msm/npu/npu_common.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -12,35 +12,29 @@ #ifndef _NPU_COMMON_H #define _NPU_COMMON_H -#include -#include -#include -#include + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include +#include +#include +#include +#include "npu_mgr.h" + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ /* get npu info */ #define MSM_NPU_GET_INFO_32 \ _IOWR(MSM_NPU_IOCTL_MAGIC, 1, compat_caddr_t) @@ -65,27 +59,133 @@ #define MSM_NPU_EXEC_NETWORK_32 \ _IOWR(MSM_NPU_IOCTL_MAGIC, 6, compat_caddr_t) -#define NPU_MAX_CLK_NUM 8 -#define NPU_MAX_REGULATOR_NUM 4 -#define NPU_MAX_DT_NAME_LEN 16 +#define NPU_MAX_MBOX_NUM 2 +#define NPU_MBOX_LOW_PRI 0 +#define NPU_MBOX_HIGH_PRI 1 + +#define DEFAULT_REG_DUMP_NUM 64 +#define ROW_BYTES 16 +#define GROUP_BYTES 4 -#define NPU_FIRMWARE_VERSION 0x1000 +#define NUM_TOTAL_CLKS 19 +#define NPU_MAX_REGULATOR_NUM 2 +#define NPU_MAX_DT_NAME_LEN 21 +#define NPU_MAX_PWRLEVELS 7 + +/* ------------------------------------------------------------------------- + * Data Structures + * ------------------------------------------------------------------------- + */ +struct npu_smmu_ctx { + int domain; + struct dma_iommu_mapping *mmu_mapping; + struct reg_bus_client *reg_bus_clt; + int32_t attach_cnt; +}; + +struct npu_ion_buf { + int fd; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attachment; + struct sg_table *table; + dma_addr_t iova; + uint32_t size; + void *phys_addr; + void *buf; + struct list_head list; +}; -struct npu_clk_t { +struct npu_clk { struct clk *clk; char clk_name[NPU_MAX_DT_NAME_LEN]; }; -struct npu_regulator_t { +struct npu_regulator { struct regulator *regulator; char regulator_name[NPU_MAX_DT_NAME_LEN]; }; -#define DEFAULT_REG_DUMP_NUM 0x100 -#define ROW_BYTES 16 -#define GROUP_BYTES 4 +struct npu_debugfs_ctx { + struct dentry *root; + uint32_t reg_off; + uint32_t reg_cnt; + char *buf; + size_t buf_len; + uint8_t *log_buf; + struct mutex log_lock; + uint32_t log_num_bytes_buffered; + uint32_t log_read_index; + uint32_t log_write_index; + uint32_t log_buf_size; +}; -struct npu_device_t { +struct npu_mbox { + struct mbox_client client; + struct mbox_chan *chan; + struct npu_device *npu_dev; + uint32_t id; +}; + +/** + * struct npul_pwrlevel - Struct holding different pwrlevel info obtained from + * from dtsi file + * @freq[]: NPU frequency vote in Hz + */ +struct npu_pwrlevel { + long clk_freq[NUM_TOTAL_CLKS]; +}; + +/* + * struct npu_reg - Struct holding npu register information + * @ off - register offset + * @ val - register value + * @ valid - if register value is valid + */ +struct npu_reg { + uint32_t off; + uint32_t val; + bool valid; +}; + +/** + * struct npu_pwrctrl - Power control settings for a NPU device + * @pwr_vote_num - voting information for power enable + * @pwrlevels - List of supported power levels + * @active_pwrlevel - The currently active power level + * @default_pwrlevel - device wake up power level + * @max_pwrlevel - maximum allowable powerlevel per the user + * @min_pwrlevel - minimum allowable powerlevel per the user + * @num_pwrlevels - number of available power levels + * @devbw - bw device + */ +struct npu_pwrctrl { + int32_t pwr_vote_num; + + struct npu_pwrlevel pwrlevels[NPU_MAX_PWRLEVELS]; + uint32_t active_pwrlevel; + uint32_t default_pwrlevel; + uint32_t max_pwrlevel; + uint32_t min_pwrlevel; + uint32_t num_pwrlevels; + + struct device *devbw; + uint32_t bwmon_enabled; + uint32_t uc_pwrlevel; +}; + +/** + * struct npu_thermalctrl - Thermal control settings for a NPU device + * @max_state - maximum thermal mitigation state + * @current_state - current thermal mitigation state + * @pwr_level -power level that thermal control requested + */ +struct npu_thermalctrl { + unsigned long max_state; + unsigned long current_state; + uint32_t pwr_level; +}; + +struct npu_device { struct mutex ctx_lock; struct platform_device *pdev; @@ -97,14 +197,51 @@ struct npu_device_t { size_t reg_size; char __iomem *npu_base; - u32 npu_phys; + uint32_t npu_phys; uint32_t core_clk_num; - struct npu_clk_t core_clks[NPU_MAX_CLK_NUM]; + struct npu_clk core_clks[NUM_TOTAL_CLKS]; uint32_t regulator_num; - struct npu_regulator_t regulators[NPU_MAX_DT_NAME_LEN]; + struct npu_regulator regulators[NPU_MAX_DT_NAME_LEN]; + + uint32_t irq; + + struct npu_ion_buf mapped_buffers; - u32 irq; + struct device *cb_device; + + struct npu_host_ctx host_ctx; + struct npu_smmu_ctx smmu_ctx; + struct npu_debugfs_ctx debugfs_ctx; + + struct npu_mbox mbox[NPU_MAX_MBOX_NUM]; + + struct thermal_cooling_device *tcdev; + struct npu_pwrctrl pwrctrl; + struct npu_thermalctrl thermalctrl; + + struct llcc_slice_desc *sys_cache; }; + + +/* ------------------------------------------------------------------------- + * Function Prototypes + * ------------------------------------------------------------------------- + */ +int npu_debugfs_init(struct npu_device *npu_dev); +void npu_debugfs_deinit(struct npu_device *npu_dev); + +int npu_enable_core_power(struct npu_device *npu_dev); +void npu_disable_core_power(struct npu_device *npu_dev); +int npu_enable_post_pil_clocks(struct npu_device *npu_dev); + +irqreturn_t npu_intr_hdler(int irq, void *ptr); + +int npu_set_uc_power_level(struct npu_device *npu_dev, + uint32_t pwr_level); + +int fw_init(struct npu_device *npu_dev); +void fw_deinit(struct npu_device *npu_dev); + #endif /* _NPU_COMMON_H */ diff --git a/drivers/media/platform/msm/npu/npu_dbg.c b/drivers/media/platform/msm/npu/npu_dbg.c new file mode 100644 index 0000000000000000000000000000000000000000..38643391153064f782abab9c9cd1738b3f2b4328 --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_dbg.c @@ -0,0 +1,345 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include "npu_common.h" +#include "npu_firmware.h" +#include "npu_hw.h" +#include "npu_hw_access.h" +#include "npu_mgr.h" + +/* ------------------------------------------------------------------------- + * File Scope Variables + * ------------------------------------------------------------------------- + */ +static const uint32_t debug_cal_reg_list[] = { + CAL_DP_DMA_WR_RLD_CMD_0, + CAL_DP_DMA_RD_RLD_CMD_0, + CAL_DP_DMA_RD_RLD_CMD_1, + CAL_DP_DMA_RD_RLD_CMD_2, + CAL_DP_DMA_RD_RLD_CMD_3, + CAL_DP_DMA_LOC_RLD_CMD_0, + CAL_DP_DMA_BUS_CFG, + CAL_DP_DMA_BUS_OT_CFG, + CAL_DP_DMA_WR_CFG_0, + CAL_DP_DMA_WR_NUM_CMD_IT_0, + CAL_DP_DMA_WR_START_ADDR_0, + CAL_DP_DMA_WR_MAX_ADDR_0, + CAL_DP_DMA_WR_STRIDE_DIM0_0, + CAL_DP_DMA_WR_STRIDE_DIM1_0, + CAL_DP_DMA_WR_STRIDE_DIM2_0, + CAL_DP_DMA_WR_ROW_INCR_0, + CAL_DP_DMA_WR_DIM0_XSIZE_0, + CAL_DP_DMA_WR_DIM0_INCR_0, + CAL_DP_DMA_WR_DIM0_NUM_BLK_0, + CAL_DP_DMA_WR_DIM1_XSIZE_0, + CAL_DP_DMA_WR_DIM1_YSIZE_0, + CAL_DP_DMA_WR_DIM2_XSIZE_0, + CAL_DP_DMA_WR_DIM2_YSIZE_0, + CAL_DP_DMA_WR_CLIENT_BLK_SIZE_CFG_0, + CAL_DP_DMA_WR_CLIENT_ADDR_CFG_0, + CAL_DP_DMA_WR_CLIENT_BUFF_CFG_0, + CAL_DP_DMA_WR_MMU_CFG_0, + CAL_DP_DMA_RD_CFG_0, + CAL_DP_DMA_RD_NUM_CMD_IT_0, + CAL_DP_DMA_RD_START_ADDR_0, + CAL_DP_DMA_RD_START_ADDR1_0, + CAL_DP_DMA_RD_END_ADDR0_0, + CAL_DP_DMA_RD_MAX_ADDR_0, + CAL_DP_DMA_RD_MAX_ADDR1_0, + CAL_DP_DMA_RD_STRIDE_DIM0_0, + CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_0, + CAL_DP_DMA_RD_STRIDE_DIM1_0, + CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_0, + CAL_DP_DMA_RD_STRIDE_DIM2_0, + CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_0, + CAL_DP_DMA_RD_ROW_INCR_0, + CAL_DP_DMA_RD_DIM0_BLK_SIZE_0, + CAL_DP_DMA_RD_DIM0_INCR_0, + CAL_DP_DMA_RD_DIM0_NUM_BLK_0, + CAL_DP_DMA_RD_DIM1_BLK_SIZE_0, + CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_0, + CAL_DP_DMA_RD_DIM2_BLK_SIZE_0, + CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_0, + CAL_DP_DMA_RD_DIM0_PAD_L_0, + CAL_DP_DMA_RD_DIM0_PAD_R_0, + CAL_DP_DMA_RD_PAD_TB_0, + CAL_DP_DMA_RD_DIM0_CROP_0, + CAL_DP_DMA_RD_CLIENT_ADDR_CFG_0, + CAL_DP_DMA_RD_CLIENT_BUFF_CFG_0, + CAL_DP_DMA_RD_MMU_CFG_0, + CAL_DP_DMA_RD_PAD_VALUE_0, + CAL_DP_DMA_RD_CFG_1, + CAL_DP_DMA_RD_NUM_CMD_IT_1, + CAL_DP_DMA_RD_START_ADDR_1, + CAL_DP_DMA_RD_START_ADDR1_1, + CAL_DP_DMA_RD_END_ADDR0_1, + CAL_DP_DMA_RD_MAX_ADDR_1, + CAL_DP_DMA_RD_MAX_ADDR1_1, + CAL_DP_DMA_RD_STRIDE_DIM0_1, + CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_1, + CAL_DP_DMA_RD_STRIDE_DIM1_1, + CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_1, + CAL_DP_DMA_RD_STRIDE_DIM2_1, + CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_1, + CAL_DP_DMA_RD_ROW_INCR_1, + CAL_DP_DMA_RD_DIM0_BLK_SIZE_1, + CAL_DP_DMA_RD_DIM0_INCR_1, + CAL_DP_DMA_RD_DIM0_NUM_BLK_1, + CAL_DP_DMA_RD_DIM1_BLK_SIZE_1, + CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_1, + CAL_DP_DMA_RD_DIM2_BLK_SIZE_1, + CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_1, + CAL_DP_DMA_RD_DIM0_PAD_L_1, + CAL_DP_DMA_RD_DIM0_PAD_R_1, + CAL_DP_DMA_RD_PAD_TB_1, + CAL_DP_DMA_RD_DIM0_CROP_1, + CAL_DP_DMA_RD_CLIENT_ADDR_CFG_1, + CAL_DP_DMA_RD_CLIENT_BUFF_CFG_1, + CAL_DP_DMA_RD_MMU_CFG_1, + CAL_DP_DMA_RD_PAD_VALUE_1, + CAL_DP_DMA_RD_CFG_2, + CAL_DP_DMA_RD_NUM_CMD_IT_2, + CAL_DP_DMA_RD_START_ADDR_2, + CAL_DP_DMA_RD_START_ADDR1_2, + CAL_DP_DMA_RD_END_ADDR0_2, + CAL_DP_DMA_RD_MAX_ADDR_2, + CAL_DP_DMA_RD_MAX_ADDR1_2, + CAL_DP_DMA_RD_STRIDE_DIM0_2, + CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_2, + CAL_DP_DMA_RD_STRIDE_DIM1_2, + CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_2, + CAL_DP_DMA_RD_STRIDE_DIM2_2, + CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_2, + CAL_DP_DMA_RD_ROW_INCR_2, + CAL_DP_DMA_RD_DIM0_BLK_SIZE_2, + CAL_DP_DMA_RD_DIM0_INCR_2, + CAL_DP_DMA_RD_DIM0_NUM_BLK_2, + CAL_DP_DMA_RD_DIM1_BLK_SIZE_2, + CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_2, + CAL_DP_DMA_RD_DIM2_BLK_SIZE_2, + CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_2, + CAL_DP_DMA_RD_DIM0_PAD_L_2, + CAL_DP_DMA_RD_DIM0_PAD_R_2, + CAL_DP_DMA_RD_PAD_TB_2, + CAL_DP_DMA_RD_DIM0_CROP_2, + CAL_DP_DMA_RD_CLIENT_ADDR_CFG_2, + CAL_DP_DMA_RD_CLIENT_BUFF_CFG_2, + CAL_DP_DMA_RD_MMU_CFG_2, + CAL_DP_DMA_RD_PAD_VALUE_2, + CAL_DP_DMA_RD_CFG_3, + CAL_DP_DMA_RD_NUM_CMD_IT_3, + CAL_DP_DMA_RD_START_ADDR_3, + CAL_DP_DMA_RD_START_ADDR1_3, + CAL_DP_DMA_RD_END_ADDR0_3, + CAL_DP_DMA_RD_MAX_ADDR_3, + CAL_DP_DMA_RD_MAX_ADDR1_3, + CAL_DP_DMA_RD_STRIDE_DIM0_3, + CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_3, + CAL_DP_DMA_RD_STRIDE_DIM1_3, + CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_3, + CAL_DP_DMA_RD_STRIDE_DIM2_3, + CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_3, + CAL_DP_DMA_RD_ROW_INCR_3, + CAL_DP_DMA_RD_DIM0_BLK_SIZE_3, + CAL_DP_DMA_RD_DIM0_INCR_3, + CAL_DP_DMA_RD_DIM0_NUM_BLK_3, + CAL_DP_DMA_RD_DIM1_BLK_SIZE_3, + CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_3, + CAL_DP_DMA_RD_DIM2_BLK_SIZE_3, + CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_3, + CAL_DP_DMA_RD_DIM0_PAD_L_3, + CAL_DP_DMA_RD_DIM0_PAD_R_3, + CAL_DP_DMA_RD_PAD_TB_3, + CAL_DP_DMA_RD_DIM0_CROP_3, + CAL_DP_DMA_RD_CLIENT_ADDR_CFG_3, + CAL_DP_DMA_RD_CLIENT_BUFF_CFG_3, + CAL_DP_DMA_RD_MMU_CFG_3, + CAL_DP_DMA_RD_PAD_VALUE_3, + CAL_DP_DMA_LOC_SRC_ADDR_0, + CAL_DP_DMA_LOC_DEST_ADDR_0, + CAL_DP_DMA_LOC_DATA_SIZE_0, + CAL_DP_DMA_WR_ERR_STATUS_0, + CAL_DP_DMA_WR_MAX_P_CNT_0, + CAL_DP_DMA_WR_STATUS_0_0, + CAL_DP_DMA_WR_STATUS_0_1, + CAL_DP_DMA_WR_STATUS_0_2, + CAL_DP_DMA_WR_STATUS_0_3, + CAL_DP_DMA_RD_ERR_STATUS_0, + CAL_DP_DMA_RD_MAX_P_CNT_0, + CAL_DP_DMA_RD_STATUS_0_0, + CAL_DP_DMA_RD_STATUS_0_1, + CAL_DP_DMA_RD_STATUS_0_2, + CAL_DP_DMA_RD_STATUS_0_3, + CAL_DP_DMA_RD_STATUS_0_4, + CAL_DP_DMA_RD_ERR_STATUS_1, + CAL_DP_DMA_RD_MAX_P_CNT_1, + CAL_DP_DMA_RD_STATUS_1_0, + CAL_DP_DMA_RD_STATUS_1_1, + CAL_DP_DMA_RD_STATUS_1_2, + CAL_DP_DMA_RD_STATUS_1_3, + CAL_DP_DMA_RD_STATUS_1_4, + CAL_DP_DMA_RD_ERR_STATUS_2, + CAL_DP_DMA_RD_MAX_P_CNT_2, + CAL_DP_DMA_RD_STATUS_2_0, + CAL_DP_DMA_RD_STATUS_2_1, + CAL_DP_DMA_RD_STATUS_2_2, + CAL_DP_DMA_RD_STATUS_2_3, + CAL_DP_DMA_RD_STATUS_2_4, + CAL_DP_DMA_RD_ERR_STATUS_3, + CAL_DP_DMA_RD_MAX_P_CNT_3, + CAL_DP_DMA_RD_STATUS_3_0, + CAL_DP_DMA_RD_STATUS_3_1, + CAL_DP_DMA_RD_STATUS_3_2, + CAL_DP_DMA_RD_STATUS_3_3, + CAL_DP_DMA_LOC_ERR_STATUS_0, + CAL_DP_DMA_LOC_STATUS_0_0, + CAL_DP_DMA_LOC_STATUS_0_1, + CAL_DP_DMA_LOC_STATUS_0_2, + CAL_DP_DMA_LOC_STATUS_0_3, + CAL_DP_CALDMA_ADAPT_STATUS_0_0, + CAL_DP_CALDMA_ADAPT_STATUS_0_1, + CAL_DP_SDMA_WR_STATUS_0_0, + CAL_DP_SDMA_WR_STATUS_0_1, + CAL_DP_SDMA_WR_STATUS_0_2, + CAL_DP_SDMA_WR_STATUS_0_3, + CAL_DP_SDMA_WR_STATUS_0_4, + CAL_DP_SDMA_WR_STATUS_0_5, + CAL_DP_SDMA_WR_STATUS_0_6, + CAL_DP_SDMA_WR_STATUS_0_7, + CAL_DP_SDMA_WR_STATUS_0_8, + CAL_DP_SDMA_RD_STATUS_0_0, + CAL_DP_SDMA_RD_STATUS_0_1, + CAL_DP_SDMA_RD_STATUS_0_2, + CAL_DP_SDMA_RD_STATUS_0_3, + CAL_DP_SDMA_RD_STATUS_0_4, + CAL_DP_SDMA_RD_STATUS_0_5, + CAL_DP_SDMA_RD_STATUS_0_6, + CAL_DP_SDMA_RD_STATUS_0_7, + CAL_DP_SDMA_RD_STATUS_0_8, + CAL_DP_SDMA_RD_STATUS_0_9, + CAL_DP_SDMA_RD_STATUS_0_10, + CAL_DP_TCM_SET_CMD, + CAL_DP_TCM_RESET_CMD, + CAL_DP_DTSWC_INT_CLR, + CAL_DP_TCM_INT_CLR, + CAL_DP_DTSWC_INT_SET, + CAL_DP_TCM_INT_SET, + CAL_DP_WD_RSS_CMD, + CAL_DP_RESET_CMD, + CAL_DP_PERF_CNT_CMD, + CAL_DP_DBUF_TRANSFER_CMD, + CAL_DP_CAL_EN_CTRL, + CAL_DP_EN_INT_CTRL, + CAL_DP_EN_TCM_INT_CTRL, + CAL_DP_TCM_VAL_CTRL, + CAL_DP_WD_COUNT_LO, + CAL_DP_WD_COUNT_HI, + CAL_DP_RSS_SEL_CTRL, + CAL_DP_CAL_CFG_W0, + CAL_DP_CAL_CFG_W1, + CAL_DP_CAL_CFG_W2, + CAL_DP_CAL_CFG_W3, + CAL_DP_PERF_CNT_START_SEL, + CAL_DP_PERF_CNT_STOP_SEL, + CAL_DP_PERF_CNT_EVENT_SEL, + CAL_DP_DBUF_RD_SEL, + CAL_DP_LM_CTRL, + CAL_DP_LM_LOOK_AHEAD, + CAL_DP_LM_CUB_TIMER, + CAL_DP_SIGB_STATUS, + CAL_DP_CAL_EN_WD_RSS_STATUS, + CAL_DP_EN_TCM_FLAGS_STATUS, + CAL_DP_VERSION_STATUS, + CAL_DP_CFG_STATUS, + CAL_DP_CUB_SAT_DTCT_STATUS, + CAL_DP_AHB_ERR_ADDR_STATUS, + CAL_DP_WD_STATUS_LO, + CAL_DP_WD_STATUS_HI, + CAL_DP_DTSWC_INT_STATUS, + CAL_DP_DTSWC_TCM_INT_STATUS, + CAL_DP_DTSWC_UM_INT_STATUS, + CAL_DP_DTSWC_TCM_UM_INT_STATUS, + CAL_DP_RSS_STATUS, + CAL_DP_RSS_STATUS_1, + CAL_DP_RSS_STATUS_2, + CAL_DP_RSS_STATUS_3, + CAL_DP_RSS_STATUS_4, + CAL_DP_RSS_STATUS_5, + CAL_DP_RSS_STATUS_6, + CAL_DP_RSS_STATUS_7, + CAL_DP_PERF_CNT0, + CAL_DP_PERF_CNT1, + CAL_DP_PERF_CNT2, + CAL_DP_PERF_CNT3, + CAL_DP_FINAL_MIN, + CAL_DP_FINAL_MAX, + CAL_DP_LM_STATUS +}; + +/* ------------------------------------------------------------------------- + * Function Definitions - Debug + * ------------------------------------------------------------------------- + */ +void npu_dump_debug_timeout_stats(struct npu_device *npu_dev) +{ + uint32_t reg_val; + + reg_val = REGR(npu_dev, REG_FW_JOB_CNT_START); + pr_info("fw jobs execute started count = %d\n", reg_val); + reg_val = REGR(npu_dev, REG_FW_JOB_CNT_END); + pr_info("fw jobs execute finished count = %d\n", reg_val); + reg_val = REGR(npu_dev, REG_NPU_FW_DEBUG_DATA); + pr_info("fw jobs aco parser debug = %d\n", reg_val); + npu_dump_cal_state(npu_dev); +} + +void npu_dump_cal_state(struct npu_device *npu_dev) +{ + uint32_t reg_val; + uint32_t i; + + reg_val = REGR(npu_dev, CAL_DP_DMA_RD_START_ADDR_0); + pr_info("DMA RD 0 Addr: 0x%x\n", reg_val); + reg_val = REGR(npu_dev, CAL_DP_DMA_WR_START_ADDR_0); + pr_info("DMA WR Addr: 0x%x\n", reg_val); + reg_val = REGR(npu_dev, CAL_DP_DMA_RD_START_ADDR_1); + pr_info("DMA RD 1 Addr: 0x%x\n", reg_val); + reg_val = REGR(npu_dev, CAL_DP_DMA_RD_START_ADDR_2); + pr_info("DMA RD 2 Addr: 0x%x\n", reg_val); + + /* mask irq status reg */ + reg_val = REGR(npu_dev, CAL_DP_DTSWC_INT_STATUS); + pr_info("Masked ISR Status: 0x%x\n", reg_val); + + /* unmasked mask irq status reg */ + reg_val = REGR(npu_dev, CAL_DP_DTSWC_UM_INT_STATUS); + pr_info("UnMasked ISR Status: 0x%x\n", reg_val); + + reg_val = REGR(npu_dev, CAL_DP_DMA_RD_ERR_STATUS_0); + pr_info("rd err 0 Status: 0x%x\n", reg_val); + reg_val = REGR(npu_dev, CAL_DP_DMA_RD_ERR_STATUS_1); + pr_info("rd err 1 Status: 0x%x\n", reg_val); + reg_val = REGR(npu_dev, CAL_DP_DMA_RD_ERR_STATUS_2); + pr_info("rd err 2 Status: 0x%x\n", reg_val); + + for (i = 0; i < sizeof(debug_cal_reg_list) / sizeof(uint32_t); i++) { + reg_val = REGR(npu_dev, debug_cal_reg_list[i]); + pr_info("Reg = 0x%x Val = 0x%x\n", debug_cal_reg_list[i], + reg_val); + } +} diff --git a/drivers/media/platform/msm/npu/npu_debugfs.c b/drivers/media/platform/msm/npu/npu_debugfs.c new file mode 100644 index 0000000000000000000000000000000000000000..4c2aace68eed75974395487745852356d243da96 --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_debugfs.c @@ -0,0 +1,491 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include + +#include "npu_hw_access.h" +#include "npu_common.h" + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +#define NPU_LOG_BUF_SIZE 4096 + +/* ------------------------------------------------------------------------- + * Function Prototypes + * ------------------------------------------------------------------------- + */ +static int npu_debug_open(struct inode *inode, struct file *file); +static int npu_debug_release(struct inode *inode, struct file *file); +static ssize_t npu_debug_reg_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos); +static ssize_t npu_debug_reg_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos); +static ssize_t npu_debug_off_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos); +static ssize_t npu_debug_off_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos); +static ssize_t npu_debug_log_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos); +static ssize_t npu_debug_ctrl_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos); + +/* ------------------------------------------------------------------------- + * Variables + * ------------------------------------------------------------------------- + */ +struct npu_device *g_npu_dev; + +static const struct file_operations npu_reg_fops = { + .open = npu_debug_open, + .release = npu_debug_release, + .read = npu_debug_reg_read, + .write = npu_debug_reg_write, +}; + +static const struct file_operations npu_off_fops = { + .open = npu_debug_open, + .release = npu_debug_release, + .read = npu_debug_off_read, + .write = npu_debug_off_write, +}; + +static const struct file_operations npu_log_fops = { + .open = npu_debug_open, + .release = npu_debug_release, + .read = npu_debug_log_read, + .write = NULL, +}; + +static const struct file_operations npu_ctrl_fops = { + .open = npu_debug_open, + .release = npu_debug_release, + .read = NULL, + .write = npu_debug_ctrl_write, +}; + +/* ------------------------------------------------------------------------- + * Function Implementations + * ------------------------------------------------------------------------- + */ +static int npu_debug_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + file->private_data = inode->i_private; + return 0; +} + +static int npu_debug_release(struct inode *inode, struct file *file) +{ + struct npu_device *npu_dev = file->private_data; + struct npu_debugfs_ctx *debugfs; + + debugfs = &npu_dev->debugfs_ctx; + + kfree(debugfs->buf); + debugfs->buf_len = 0; + debugfs->buf = NULL; + return 0; +} + +/* ------------------------------------------------------------------------- + * Function Implementations - Reg Read/Write + * ------------------------------------------------------------------------- + */ +static ssize_t npu_debug_reg_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + size_t off; + uint32_t data, cnt; + struct npu_device *npu_dev = file->private_data; + char buf[24]; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + cnt = sscanf(buf, "%zx %x", &off, &data); + pr_debug("%s %s 0x%zx, 0x%08x\n", __func__, buf, off, data); + + return count; + if (cnt < 2) + return -EINVAL; + + if (npu_enable_core_power(npu_dev)) + return -EPERM; + + REGW(npu_dev, off, data); + + npu_disable_core_power(npu_dev); + + pr_debug("write: addr=%zx data=%x\n", off, data); + + return count; +} + +static ssize_t npu_debug_reg_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct npu_device *npu_dev = file->private_data; + struct npu_debugfs_ctx *debugfs; + size_t len; + + debugfs = &npu_dev->debugfs_ctx; + + if (debugfs->reg_cnt == 0) + return 0; + + if (!debugfs->buf) { + char dump_buf[64]; + char *ptr; + int cnt, tot, off; + + debugfs->buf_len = sizeof(dump_buf) * + DIV_ROUND_UP(debugfs->reg_cnt, ROW_BYTES); + debugfs->buf = kzalloc(debugfs->buf_len, GFP_KERNEL); + + if (!debugfs->buf) + return -ENOMEM; + + ptr = npu_dev->npu_base + debugfs->reg_off; + tot = 0; + off = (int)debugfs->reg_off; + + if (npu_enable_core_power(npu_dev)) + return -EPERM; + + for (cnt = debugfs->reg_cnt * 4; cnt > 0; cnt -= ROW_BYTES) { + hex_dump_to_buffer(ptr, min(cnt, ROW_BYTES), + ROW_BYTES, GROUP_BYTES, dump_buf, + sizeof(dump_buf), false); + len = scnprintf(debugfs->buf + tot, + debugfs->buf_len - tot, "0x%08x: %s\n", + ((int) (unsigned long) ptr) - + ((int) (unsigned long) npu_dev->npu_base), + dump_buf); + + ptr += ROW_BYTES; + tot += len; + if (tot >= debugfs->buf_len) + break; + } + npu_disable_core_power(npu_dev); + + debugfs->buf_len = tot; + } + + if (*ppos >= debugfs->buf_len) + return 0; /* done reading */ + + len = min(count, debugfs->buf_len - (size_t) *ppos); + pr_debug("read %zi %zi", count, debugfs->buf_len - (size_t) *ppos); + if (copy_to_user(user_buf, debugfs->buf + *ppos, len)) { + pr_err("failed to copy to user\n"); + return -EFAULT; + } + + *ppos += len; /* increase offset */ + return len; +} + +/* ------------------------------------------------------------------------- + * Function Implementations - Offset Read/Write + * ------------------------------------------------------------------------- + */ +static ssize_t npu_debug_off_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + size_t off = 0; + uint32_t cnt, reg_cnt; + char buf[24]; + struct npu_device *npu_dev = file->private_data; + struct npu_debugfs_ctx *debugfs; + + pr_debug("npu_dev %pK %pK\n", npu_dev, g_npu_dev); + npu_dev = g_npu_dev; + debugfs = &npu_dev->debugfs_ctx; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + cnt = sscanf(buf, "%zx %x", &off, ®_cnt); + if (cnt == 1) + reg_cnt = DEFAULT_REG_DUMP_NUM; + pr_debug("reg off = %zx, %d cnt=%d\n", off, reg_cnt, cnt); + if (cnt >= 1) { + debugfs->reg_off = off; + debugfs->reg_cnt = reg_cnt; + } + + return count; +} + +static ssize_t npu_debug_off_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + size_t len; + char buf[64]; + struct npu_device *npu_dev = file->private_data; + struct npu_debugfs_ctx *debugfs; + + pr_debug("npu_dev %pK %pK\n", npu_dev, g_npu_dev); + npu_dev = g_npu_dev; + debugfs = &npu_dev->debugfs_ctx; + + if (*ppos) + return 0; /* the end */ + + len = scnprintf(buf, sizeof(buf), "offset=0x%08x cnt=%d\n", + debugfs->reg_off, debugfs->reg_cnt); + + if (copy_to_user(user_buf, buf, len)) { + pr_err("failed to copy to user\n"); + return -EFAULT; + } + + *ppos += len; /* increase offset */ + return len; +} + +/* ------------------------------------------------------------------------- + * Function Implementations - DebugFS Log + * ------------------------------------------------------------------------- + */ +static ssize_t npu_debug_log_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + size_t len = 0; + struct npu_device *npu_dev = file->private_data; + struct npu_debugfs_ctx *debugfs; + + pr_debug("npu_dev %pK %pK\n", npu_dev, g_npu_dev); + npu_dev = g_npu_dev; + debugfs = &npu_dev->debugfs_ctx; + + /* mutex log lock */ + mutex_lock(&debugfs->log_lock); + + if (debugfs->log_num_bytes_buffered != 0) { + if ((debugfs->log_read_index + + debugfs->log_num_bytes_buffered) > + debugfs->log_buf_size) { + /* Wrap around case */ + uint32_t remaining_to_end = debugfs->log_buf_size - + debugfs->log_read_index; + uint8_t *src_addr = debugfs->log_buf + + debugfs->log_read_index; + uint8_t *dst_addr = user_buf; + + if (copy_to_user(dst_addr, src_addr, + remaining_to_end)) { + pr_err("%s failed to copy to user", __func__); + return -EFAULT; + } + src_addr = debugfs->log_buf; + dst_addr = user_buf + remaining_to_end; + if (copy_to_user(dst_addr, src_addr, + debugfs->log_num_bytes_buffered - + remaining_to_end)) { + pr_err("%s failed to copy to user", __func__); + return -EFAULT; + } + debugfs->log_read_index = + debugfs->log_num_bytes_buffered - + remaining_to_end; + } else { + if (copy_to_user(user_buf, (debugfs->log_buf + + debugfs->log_read_index), + debugfs->log_num_bytes_buffered)) { + pr_err("%s failed to copy to user", __func__); + return -EFAULT; + } + debugfs->log_read_index += + debugfs->log_num_bytes_buffered; + if (debugfs->log_read_index == debugfs->log_buf_size) + debugfs->log_read_index = 0; + } + len = debugfs->log_num_bytes_buffered; + debugfs->log_num_bytes_buffered = 0; + } + + /* mutex log unlock */ + mutex_unlock(&debugfs->log_lock); + + return len; +} + +/* ------------------------------------------------------------------------- + * Function Implementations - DebugFS Control + * ------------------------------------------------------------------------- + */ +static ssize_t npu_debug_ctrl_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[24]; + struct npu_device *npu_dev = file->private_data; + struct npu_debugfs_ctx *debugfs; + + pr_debug("npu_dev %pK %pK\n", npu_dev, g_npu_dev); + npu_dev = g_npu_dev; + debugfs = &npu_dev->debugfs_ctx; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + if (count >= 2) + buf[count-1] = 0;/* remove line feed */ + + if (strcmp(buf, "on") == 0) { + pr_info("triggering fw_init\n"); + if (fw_init(npu_dev) != 0) + pr_info("error in fw_init\n"); + } else if (strcmp(buf, "off") == 0) { + pr_info("triggering fw_deinit\n"); + fw_deinit(npu_dev); + } else if (strcmp(buf, "0") == 0) { + pr_info("setting power state to 0\n"); + npu_dev->pwrctrl.active_pwrlevel = 0; + } else if (strcmp(buf, "1") == 0) { + pr_info("setting power state to 1\n"); + npu_dev->pwrctrl.active_pwrlevel = 1; + } else if (strcmp(buf, "2") == 0) { + pr_info("setting power state to 2\n"); + npu_dev->pwrctrl.active_pwrlevel = 2; + } else if (strcmp(buf, "3") == 0) { + pr_info("setting power state to 3\n"); + npu_dev->pwrctrl.active_pwrlevel = 3; + } else if (strcmp(buf, "4") == 0) { + pr_info("setting power state to 4\n"); + npu_dev->pwrctrl.active_pwrlevel = 4; + } else if (strcmp(buf, "5") == 0) { + pr_info("setting power state to 5\n"); + npu_dev->pwrctrl.active_pwrlevel = 5; + } else { + pr_info("ctrl invalid value\n"); + } + + return count; +} +/* ------------------------------------------------------------------------- + * Function Implementations - DebugFS + * ------------------------------------------------------------------------- + */ +int npu_debugfs_init(struct npu_device *npu_dev) +{ + struct npu_debugfs_ctx *debugfs = &npu_dev->debugfs_ctx; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + + g_npu_dev = npu_dev; + + debugfs->root = debugfs_create_dir("npu", NULL); + if (IS_ERR_OR_NULL(debugfs->root)) { + pr_err("debugfs_create_dir for npu failed, error %ld\n", + PTR_ERR(debugfs->root)); + return -ENODEV; + } + + if (!debugfs_create_file("reg", 0644, debugfs->root, + npu_dev, &npu_reg_fops)) { + pr_err("debugfs_create_file reg fail\n"); + goto err; + } + + if (!debugfs_create_file("off", 0644, debugfs->root, + npu_dev, &npu_off_fops)) { + pr_err("debugfs_create_file off fail\n"); + goto err; + } + + if (!debugfs_create_file("log", 0644, debugfs->root, + npu_dev, &npu_log_fops)) { + pr_err("debugfs_create_file log fail\n"); + goto err; + } + + if (!debugfs_create_file("ctrl", 0644, debugfs->root, + npu_dev, &npu_ctrl_fops)) { + pr_err("debugfs_create_file ctrl fail\n"); + goto err; + } + + if (!debugfs_create_bool("sys_cache_disable", 0644, + debugfs->root, &(host_ctx->sys_cache_disable))) { + pr_err("debugfs_creat_bool fail for sys cache\n"); + goto err; + } + + if (!debugfs_create_bool("fw_state", 0444, + debugfs->root, &(host_ctx->fw_enabled))) { + pr_err("debugfs_creat_bool fail for fw_state\n"); + goto err; + } + + if (!debugfs_create_u32("pwr_level", 0444, + debugfs->root, &(pwr->active_pwrlevel))) { + pr_err("debugfs_creat_bool fail for power level\n"); + goto err; + } + + debugfs->log_num_bytes_buffered = 0; + debugfs->log_read_index = 0; + debugfs->log_write_index = 0; + debugfs->log_buf_size = NPU_LOG_BUF_SIZE; + debugfs->log_buf = kzalloc(debugfs->log_buf_size, GFP_KERNEL); + if (!debugfs->log_buf) + goto err; + mutex_init(&debugfs->log_lock); + + return 0; + +err: + npu_debugfs_deinit(npu_dev); + return -ENODEV; +} + +void npu_debugfs_deinit(struct npu_device *npu_dev) +{ + struct npu_debugfs_ctx *debugfs = &npu_dev->debugfs_ctx; + + debugfs->log_num_bytes_buffered = 0; + debugfs->log_read_index = 0; + debugfs->log_write_index = 0; + debugfs->log_buf_size = 0; + kfree(debugfs->log_buf); + + if (!IS_ERR_OR_NULL(debugfs->root)) { + debugfs_remove_recursive(debugfs->root); + debugfs->root = NULL; + } +} diff --git a/drivers/media/platform/msm/npu/npu_dev.c b/drivers/media/platform/msm/npu/npu_dev.c index 03afbb1ab1d8ed4b9dc67425a8913d0b127947ed..bcdbf070bc2dbe741ebf451477dcf7716c3fb6d6 100644 --- a/drivers/media/platform/msm/npu/npu_dev.c +++ b/drivers/media/platform/msm/npu/npu_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -6,165 +6,1022 @@ * * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#include +#include +#include + #include "npu_common.h" +#include "npu_hw.h" + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +#define CLASS_NAME "npu" +#define DRIVER_NAME "msm_npu" +#define DDR_MAPPED_START_ADDR 0x80000000 +#define DDR_MAPPED_SIZE 0x60000000 + +#define PERF_MODE_DEFAULT 0 + +#define POWER_LEVEL_MIN_SVS 0 +#define POWER_LEVEL_LOW_SVS 1 +#define POWER_LEVEL_NOMINAL 4 + +/* ------------------------------------------------------------------------- + * File Scope Prototypes + * ------------------------------------------------------------------------- + */ +static int npu_enable_regulators(struct npu_device *npu_dev); +static void npu_disable_regulators(struct npu_device *npu_dev); +static int npu_enable_core_clocks(struct npu_device *npu_dev, bool post_pil); +static void npu_disable_core_clocks(struct npu_device *npu_dev); +static uint32_t npu_calc_power_level(struct npu_device *npu_dev); +static ssize_t npu_show_capabilities(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t npu_show_pwr_state(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t npu_store_pwr_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static void npu_suspend_devbw(struct npu_device *npu_dev); +static void npu_resume_devbw(struct npu_device *npu_dev); +static bool npu_is_post_clock(const char *clk_name); +static bool npu_is_exclude_clock(const char *clk_name); +static bool npu_is_exclude_rate_clock(const char *clk_name); +static int npu_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state); +static int npu_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state); +static int npu_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state); +static int npu_open(struct inode *inode, struct file *file); +static int npu_close(struct inode *inode, struct file *file); +static int npu_get_info(struct npu_device *npu_dev, unsigned long arg); +static int npu_map_buf(struct npu_device *npu_dev, unsigned long arg); +static int npu_unmap_buf(struct npu_device *npu_dev, unsigned long arg); +static int npu_load_network(struct npu_device *npu_dev, unsigned long arg); +static int npu_unload_network(struct npu_device *npu_dev, unsigned long arg); +static int npu_exec_network(struct npu_device *npu_dev, unsigned long arg); +static long npu_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +static int npu_parse_dt_clock(struct npu_device *npu_dev); +static int npu_parse_dt_regulator(struct npu_device *npu_dev); +static int npu_of_parse_pwrlevels(struct npu_device *npu_dev, + struct device_node *node); +static int npu_pwrctrl_init(struct npu_device *npu_dev); +static int npu_probe(struct platform_device *pdev); +static int npu_remove(struct platform_device *pdev); +static int npu_suspend(struct platform_device *dev, pm_message_t state); +static int npu_resume(struct platform_device *dev); +static int __init npu_init(void); +static void __exit npu_exit(void); + +/* ------------------------------------------------------------------------- + * File Scope Variables + * ------------------------------------------------------------------------- + */ +static const char * const npu_clock_order[] = { + "armwic_core_clk", + "cal_dp_clk_src", + "cal_dp_clk", + "cal_dp_cdc_clk", + "conf_noc_ahb_clk", + "comp_noc_axi_clk", + "npu_core_clk_src", + "npu_core_clk", + "npu_core_cti_clk", + "npu_core_apb_clk", + "npu_core_atb_clk", + "npu_cpc_clk", + "npu_cpc_timer_clk", + "qtimer_core_clk", + "sleep_clk", + "bwmon_clk", + "perf_cnt_clk", + "bto_core_clk", + "xo_clk" +}; + +static const char * const npu_post_clocks[] = { + "npu_cpc_clk", + "npu_cpc_timer_clk" +}; + +static const char * const npu_exclude_clocks[] = { + "npu_core_clk_src", + "cal_dp_clk_src", + "perf_cnt_clk", + "npu_core_cti_clk", + "npu_core_apb_clk", + "npu_core_atb_clk" +}; + +static const char * const npu_exclude_rate_clocks[] = { + "sleep_clk", + "xo_clk", + "conf_noc_ahb_clk", + "npu_core_cti_clk", + "npu_core_apb_clk", + "npu_core_atb_clk", + "npu_cpc_timer_clk", + "qtimer_core_clk", + "bwmon_clk", + "bto_core_clk" +}; + +static struct npu_reg npu_saved_bw_registers[] = { + { BWMON2_SAMPLING_WINDOW, 0, false }, + { BWMON2_BYTE_COUNT_THRESHOLD_HIGH, 0, false }, + { BWMON2_BYTE_COUNT_THRESHOLD_MEDIUM, 0, false }, + { BWMON2_BYTE_COUNT_THRESHOLD_LOW, 0, false }, + { BWMON2_ZONE_ACTIONS, 0, false }, + { BWMON2_ZONE_COUNT_THRESHOLD, 0, false }, +}; + +/* ------------------------------------------------------------------------- + * Entry Points for Probe + * ------------------------------------------------------------------------- + */ +/* Sys FS */ +static DEVICE_ATTR(caps, 0444, npu_show_capabilities, NULL); +static DEVICE_ATTR(pwr, 0644, npu_show_pwr_state, npu_store_pwr_state); + +static struct attribute *npu_fs_attrs[] = { + &dev_attr_caps.attr, + &dev_attr_pwr.attr, + NULL +}; + +static struct attribute_group npu_fs_attr_group = { + .attrs = npu_fs_attrs +}; + +static const struct of_device_id npu_dt_match[] = { + { .compatible = "qcom,msm-npu",}, + {} +}; + +static struct platform_driver npu_driver = { + .probe = npu_probe, + .remove = npu_remove, +#if defined(CONFIG_PM) + .suspend = npu_suspend, + .resume = npu_resume, +#endif + .driver = { + .name = "msm_npu", + .owner = THIS_MODULE, + .of_match_table = npu_dt_match, + .pm = NULL, + }, +}; + +static const struct file_operations npu_fops = { + .owner = THIS_MODULE, + .open = npu_open, + .release = npu_close, + .unlocked_ioctl = npu_ioctl, +}; + +static const struct thermal_cooling_device_ops npu_cooling_ops = { + .get_max_state = npu_get_max_state, + .get_cur_state = npu_get_cur_state, + .set_cur_state = npu_set_cur_state, +}; + +/* ------------------------------------------------------------------------- + * SysFS - Capabilities + * ------------------------------------------------------------------------- + */ +static ssize_t npu_show_capabilities(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t ret = 0; + struct npu_device *npu_dev = dev_get_drvdata(dev); + + if (!npu_enable_core_power(npu_dev)) { + if (snprintf(buf, PAGE_SIZE, "hw_version :0x%X", + REGR(npu_dev, NPU_HW_VERSION)) < 0) + ret = -EINVAL; + npu_disable_core_power(npu_dev); + } else + ret = -EPERM; + + return ret; +} + +/* ------------------------------------------------------------------------- + * SysFS - Power State + * ------------------------------------------------------------------------- + */ +static ssize_t npu_show_pwr_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct npu_device *npu_dev = dev_get_drvdata(dev); + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + + return snprintf(buf, PAGE_SIZE, "%s\n", + (pwr->pwr_vote_num > 0) ? "on" : "off"); +} + +static ssize_t npu_store_pwr_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct npu_device *npu_dev = dev_get_drvdata(dev); + bool pwr_on = false; + + if (strtobool(buf, &pwr_on) < 0) + return -EINVAL; + + if (pwr_on) { + if (npu_enable_core_power(npu_dev)) + return -EPERM; + } else { + npu_disable_core_power(npu_dev); + } + + return count; +} + +/* ------------------------------------------------------------------------- + * Power Related + * ------------------------------------------------------------------------- + */ +int npu_enable_core_power(struct npu_device *npu_dev) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + int ret = 0; + + if (!pwr->pwr_vote_num) { + ret = npu_enable_regulators(npu_dev); + if (ret) + return ret; + + ret = npu_enable_core_clocks(npu_dev, false); + if (ret) { + npu_disable_regulators(npu_dev); + pwr->pwr_vote_num = 0; + return ret; + } + npu_resume_devbw(npu_dev); + } + pwr->pwr_vote_num++; + + return ret; +} + +void npu_disable_core_power(struct npu_device *npu_dev) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + + if (!pwr->pwr_vote_num) + return; + pwr->pwr_vote_num--; + if (!pwr->pwr_vote_num) { + if (!npu_dev->host_ctx.fw_enabled) + npu_suspend_devbw(npu_dev); + npu_disable_core_clocks(npu_dev); + npu_disable_regulators(npu_dev); + } + /* init the power levels back to default */ + pwr->active_pwrlevel = pwr->default_pwrlevel; + pwr->uc_pwrlevel = pwr->default_pwrlevel; + pr_debug("setting back to default power level=%d\n", + pwr->default_pwrlevel); +} + +int npu_enable_post_pil_clocks(struct npu_device *npu_dev) +{ + return npu_enable_core_clocks(npu_dev, true); +} + + +static uint32_t npu_calc_power_level(struct npu_device *npu_dev) +{ + uint32_t ret_level; + uint32_t therm_pwr_level = npu_dev->thermalctrl.pwr_level; + uint32_t active_pwr_level = npu_dev->pwrctrl.active_pwrlevel; + uint32_t uc_pwr_level = npu_dev->pwrctrl.uc_pwrlevel; + + /* if thermal power is higher than usecase power + * leave level at use case. Otherwise go down to + * thermal power level + */ + if (therm_pwr_level > uc_pwr_level) + ret_level = uc_pwr_level; + else + ret_level = therm_pwr_level; + + /* adjust the power level */ + /* force to lowsvs, minsvs not supported */ + if (ret_level == POWER_LEVEL_MIN_SVS) + ret_level = POWER_LEVEL_LOW_SVS; + + pr_debug("%s therm=%d active=%d uc=%d set level=%d\n", __func__, + therm_pwr_level, active_pwr_level, uc_pwr_level, ret_level); + + return ret_level; +} + +static int npu_set_power_level(struct npu_device *npu_dev) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + struct npu_pwrlevel *pwrlevel; + int i, ret = 0; + long clk_rate = 0; + uint32_t pwr_level_to_set; + + if (!pwr->pwr_vote_num) { + pr_err("power is not enabled during set request\n"); + return -EINVAL; + } + + /* get power level to set */ + pwr_level_to_set = npu_calc_power_level(npu_dev); + + /* if the same as current, dont do anything */ + if (pwr_level_to_set == pwr->active_pwrlevel) + return 0; + + pr_debug("setting power level to [%d]\n", pwr_level_to_set); + + pwr->active_pwrlevel = pwr_level_to_set; + pwrlevel = &npu_dev->pwrctrl.pwrlevels[pwr->active_pwrlevel]; + + for (i = 0; i < npu_dev->core_clk_num; i++) { + if (npu_is_exclude_rate_clock( + npu_dev->core_clks[i].clk_name)) + continue; + + if (!npu_dev->host_ctx.fw_enabled) { + if (npu_is_post_clock( + npu_dev->core_clks[i].clk_name)) + continue; + } + + pr_debug("requested rate of clock [%s] to [%ld]\n", + npu_dev->core_clks[i].clk_name, pwrlevel->clk_freq[i]); + + clk_rate = clk_round_rate(npu_dev->core_clks[i].clk, + pwrlevel->clk_freq[i]); + + pr_debug("actual round clk rate [%ld]\n", clk_rate); + + ret = clk_set_rate(npu_dev->core_clks[i].clk, clk_rate); + if (ret) { + pr_err("clk_set_rate %s to %ld failed with %d\n", + npu_dev->core_clks[i].clk_name, + clk_rate, ret); + break; + } + } + + return ret; +} + +int npu_set_uc_power_level(struct npu_device *npu_dev, + uint32_t perf_mode) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + + if (perf_mode == PERF_MODE_DEFAULT) + pwr->uc_pwrlevel = POWER_LEVEL_NOMINAL; + else + pwr->uc_pwrlevel = perf_mode - 1; + + return npu_set_power_level(npu_dev); +} + +/* ------------------------------------------------------------------------- + * Bandwidth Related + * ------------------------------------------------------------------------- + */ +static void npu_save_bw_registers(struct npu_device *npu_dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(npu_saved_bw_registers); i++) { + npu_saved_bw_registers[i].val = REGR(npu_dev, + npu_saved_bw_registers[i].off); + npu_saved_bw_registers[i].valid = true; + } +} + +static void npu_restore_bw_registers(struct npu_device *npu_dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(npu_saved_bw_registers); i++) { + if (npu_saved_bw_registers[i].valid) { + REGW(npu_dev, npu_saved_bw_registers[i].off, + npu_saved_bw_registers[i].val); + npu_saved_bw_registers[i].valid = false; + } + } +} + +static void npu_suspend_devbw(struct npu_device *npu_dev) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + int ret; + + if (pwr->bwmon_enabled) { + pwr->bwmon_enabled = 0; + ret = devfreq_suspend_devbw(pwr->devbw); + if (ret) + pr_err("devfreq_suspend_devbw failed rc:%d\n", + ret); + npu_save_bw_registers(npu_dev); + } +} + +static void npu_resume_devbw(struct npu_device *npu_dev) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + int ret; + + if (!pwr->bwmon_enabled) { + pwr->bwmon_enabled = 1; + npu_restore_bw_registers(npu_dev); + ret = devfreq_resume_devbw(pwr->devbw); + + if (ret) + pr_err("devfreq_resume_devbw failed rc:%d\n", ret); + } +} + +/* ------------------------------------------------------------------------- + * Clocks Related + * ------------------------------------------------------------------------- + */ +static bool npu_is_post_clock(const char *clk_name) +{ + int ret = false; + int i; + + for (i = 0; i < ARRAY_SIZE(npu_post_clocks); i++) { + if (!strcmp(clk_name, npu_post_clocks[i])) { + ret = true; + break; + } + } + return ret; +} + +static bool npu_is_exclude_clock(const char *clk_name) +{ + int ret = false; + int i; + + for (i = 0; i < ARRAY_SIZE(npu_exclude_clocks); i++) { + if (!strcmp(clk_name, npu_exclude_clocks[i])) { + ret = true; + break; + } + } + return ret; +} + +static bool npu_is_exclude_rate_clock(const char *clk_name) +{ + int ret = false; + int i; + + for (i = 0; i < ARRAY_SIZE(npu_exclude_rate_clocks); i++) { + if (!strcmp(clk_name, npu_exclude_rate_clocks[i])) { + ret = true; + break; + } + } + return ret; +} + +static int npu_enable_core_clocks(struct npu_device *npu_dev, bool post_pil) +{ + int i, rc = 0; + struct npu_clk *core_clks = npu_dev->core_clks; + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + struct npu_pwrlevel *pwrlevel = + &npu_dev->pwrctrl.pwrlevels[pwr->active_pwrlevel]; + + for (i = 0; i < npu_dev->core_clk_num; i++) { + if (post_pil) { + if (!npu_is_post_clock(core_clks[i].clk_name)) + continue; + } else { + if (npu_is_post_clock(core_clks[i].clk_name)) + continue; + } + + if (npu_is_exclude_clock(core_clks[i].clk_name)) + continue; + + pr_debug("enabling clock [%s]\n", core_clks[i].clk_name); + + rc = clk_prepare_enable(core_clks[i].clk); + if (rc) { + pr_err("%s enable failed\n", + core_clks[i].clk_name); + break; + } + + if (npu_is_exclude_rate_clock(core_clks[i].clk_name)) + continue; + + pr_debug("setting rate of clock [%s] to [%ld]\n", + core_clks[i].clk_name, pwrlevel->clk_freq[i]); + + rc = clk_set_rate(core_clks[i].clk, + pwrlevel->clk_freq[i]); + if (rc) { + pr_debug("clk_set_rate %s to %ld failed\n", + core_clks[i].clk_name, + pwrlevel->clk_freq[i]); + break; + } + } + + return rc; +} + +static void npu_disable_core_clocks(struct npu_device *npu_dev) +{ + int i = 0; + struct npu_clk *core_clks = npu_dev->core_clks; + + for (i = (npu_dev->core_clk_num)-1; i >= 0 ; i--) { + if (npu_is_exclude_clock(core_clks[i].clk_name)) + continue; + if (!npu_dev->host_ctx.fw_enabled) { + if (npu_is_post_clock(npu_dev->core_clks[i].clk_name)) + continue; + } + + pr_debug("disabling clock [%s]\n", core_clks[i].clk_name); + clk_disable_unprepare(core_clks[i].clk); + } +} + +/* ------------------------------------------------------------------------- + * Thermal Functions + * ------------------------------------------------------------------------- + */ +static int npu_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct npu_device *npu_dev = cdev->devdata; + struct npu_thermalctrl *thermalctrl = &npu_dev->thermalctrl; + + pr_debug("enter %s thermal max state=%lu\n", __func__, + thermalctrl->max_state); + + *state = thermalctrl->max_state; + + return 0; +} + +static int npu_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct npu_device *npu_dev = cdev->devdata; + struct npu_thermalctrl *thermal = &npu_dev->thermalctrl; + + pr_debug("enter %s thermal current state=%lu\n", __func__, + thermal->current_state); + + *state = thermal->current_state; + + return 0; +} + +static int +npu_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct npu_device *npu_dev = cdev->devdata; + struct npu_thermalctrl *thermal = &npu_dev->thermalctrl; + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + + pr_debug("enter %s request state=%lu\n", __func__, state); + if (state > thermal->max_state) + return -EINVAL; + + thermal->current_state = state; + thermal->pwr_level = pwr->max_pwrlevel - state; + + return npu_set_power_level(npu_dev); +} + +/* ------------------------------------------------------------------------- + * Regulator Related + * ------------------------------------------------------------------------- + */ +static int npu_enable_regulators(struct npu_device *npu_dev) +{ + int i = 0; + int rc = 0; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + struct npu_regulator *regulators = npu_dev->regulators; + + if (!host_ctx->power_vote_num) { + for (i = 0; i < npu_dev->regulator_num; i++) { + rc = regulator_enable(regulators[i].regulator); + if (rc < 0) { + pr_err("%s enable failed\n", + regulators[i].regulator_name); + break; + } + pr_debug("regulator %s enabled\n", + regulators[i].regulator_name); + } + } + host_ctx->power_vote_num++; + return rc; +} + +static void npu_disable_regulators(struct npu_device *npu_dev) +{ + int i = 0; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + struct npu_regulator *regulators = npu_dev->regulators; + + if (host_ctx->power_vote_num > 0) { + for (i = 0; i < npu_dev->regulator_num; i++) { + regulator_disable(regulators[i].regulator); + pr_debug("regulator %s disabled\n", + regulators[i].regulator_name); + } + host_ctx->power_vote_num--; + } +} + +/* ------------------------------------------------------------------------- + * Interrupt Related + * ------------------------------------------------------------------------- + */ +int npu_enable_irq(struct npu_device *npu_dev) +{ + /* clear pending irq state */ + REGW(npu_dev, NPU_MASTERn_IPC_IRQ_OUT(0), 0x0); + enable_irq(npu_dev->irq); + + return 0; +} + +void npu_disable_irq(struct npu_device *npu_dev) +{ + disable_irq(npu_dev->irq); + /* clear pending irq state */ + REGW(npu_dev, NPU_MASTERn_IPC_IRQ_OUT(0), 0x0); +} + +/* ------------------------------------------------------------------------- + * System Cache + * ------------------------------------------------------------------------- + */ +int npu_enable_sys_cache(struct npu_device *npu_dev) +{ + int rc = 0; + uint32_t reg_val = 0; + + if (!npu_dev->host_ctx.sys_cache_disable) { + npu_dev->sys_cache = llcc_slice_getd(&(npu_dev->pdev->dev), + "npu"); + if (IS_ERR_OR_NULL(npu_dev->sys_cache)) { + pr_debug("unable to init sys cache\n"); + npu_dev->sys_cache = NULL; + return -ENODEV; + } + + /* set npu side regs - program SCID */ + reg_val = NPU_CACHE_ATTR_IDn___POR | SYS_CACHE_SCID; + + REGW(npu_dev, NPU_CACHE_ATTR_IDn(0), reg_val); + REGW(npu_dev, NPU_CACHE_ATTR_IDn(1), reg_val); + REGW(npu_dev, NPU_CACHE_ATTR_IDn(2), reg_val); + REGW(npu_dev, NPU_CACHE_ATTR_IDn(3), reg_val); + REGW(npu_dev, NPU_CACHE_ATTR_IDn(4), reg_val); + + pr_debug("prior to activate sys cache\n"); + rc = llcc_slice_activate(npu_dev->sys_cache); + if (rc) + pr_err("failed to activate sys cache\n"); + else + pr_debug("sys cache activated\n"); + } + + return rc; +} + +void npu_disable_sys_cache(struct npu_device *npu_dev) +{ + int rc = 0; + + if (!npu_dev->host_ctx.sys_cache_disable) { + if (npu_dev->sys_cache) { + rc = llcc_slice_deactivate(npu_dev->sys_cache); + if (rc) { + pr_err("failed to deactivate sys cache\n"); + return; + } + pr_debug("sys cache deactivated\n"); + llcc_slice_putd(npu_dev->sys_cache); + npu_dev->sys_cache = NULL; + } + } +} + +/* ------------------------------------------------------------------------- + * Open/Close + * ------------------------------------------------------------------------- + */ +static int npu_open(struct inode *inode, struct file *file) +{ + struct npu_device *npu_dev = container_of(inode->i_cdev, + struct npu_device, cdev); + + file->private_data = npu_dev; + + return 0; +} + +static int npu_close(struct inode *inode, struct file *file) +{ + return 0; +} + +/* ------------------------------------------------------------------------- + * IOCTL Implementations + * ------------------------------------------------------------------------- + */ +static int npu_get_info(struct npu_device *npu_dev, unsigned long arg) +{ + struct msm_npu_get_info_ioctl req; + void __user *argp = (void __user *)arg; + int ret = 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + + if (ret) { + pr_err("fail to copy from user\n"); + return -EFAULT; + } -#define CLASS_NAME "npu" -#define DRIVER_NAME "msm_npu" + ret = npu_host_get_info(npu_dev, &req); -static ssize_t npu_show_capabilities(struct device *dev, - struct device_attribute *attr, char *buf) -{ - size_t ret = 0; + if (ret) { + pr_err("npu_host_get_info failed\n"); + return ret; + } - ret = snprintf(buf, PAGE_SIZE, "hw_version :0x%X", - NPU_FIRMWARE_VERSION); - return ret; + ret = copy_to_user(argp, &req, sizeof(req)); + + if (ret) { + pr_err("fail to copy to user\n"); + return -EFAULT; + } + return 0; } -static DEVICE_ATTR(caps, 0444, npu_show_capabilities, NULL); +static int npu_map_buf(struct npu_device *npu_dev, unsigned long arg) +{ + struct msm_npu_map_buf_ioctl req; + void __user *argp = (void __user *)arg; + int ret = 0; -static struct attribute *npu_fs_attrs[] = { - &dev_attr_caps.attr, - NULL -}; + ret = copy_from_user(&req, argp, sizeof(req)); -static struct attribute_group npu_fs_attr_group = { - .attrs = npu_fs_attrs -}; + if (ret) { + pr_err("fail to copy from user\n"); + return -EFAULT; + } + + ret = npu_host_map_buf(npu_dev, &req); + + if (ret) { + pr_err("npu_host_map_buf failed\n"); + return ret; + } + + ret = copy_to_user(argp, &req, sizeof(req)); -static int npu_get_info(struct file *file, unsigned int cmd, - unsigned long arg) + if (ret) { + pr_err("fail to copy to user\n"); + return -EFAULT; + } + return 0; +} + +static int npu_unmap_buf(struct npu_device *npu_dev, unsigned long arg) { - struct msm_npu_get_info_ioctl_t req; + struct msm_npu_unmap_buf_ioctl req; void __user *argp = (void __user *)arg; - int ret; + int ret = 0; ret = copy_from_user(&req, argp, sizeof(req)); if (ret) { pr_err("fail to copy from user\n"); + return -EFAULT; + } + + ret = npu_host_unmap_buf(npu_dev, &req); + + if (ret) { + pr_err("npu_host_unmap_buf failed\n"); return ret; } - req.firmware_version = NPU_FIRMWARE_VERSION; + ret = copy_to_user(argp, &req, sizeof(req)); if (ret) { pr_err("fail to copy to user\n"); - return ret; + return -EFAULT; } return 0; } -static int npu_open(struct inode *inode, struct file *file) +static int npu_load_network(struct npu_device *npu_dev, unsigned long arg) { - struct npu_device_t *npu_dev = container_of(inode->i_cdev, - struct npu_device_t, cdev); + struct msm_npu_load_network_ioctl req; + void __user *argp = (void __user *)arg; + int ret = 0; - file->private_data = npu_dev; + ret = copy_from_user(&req, argp, sizeof(req)); + + if (ret) { + pr_err("fail to copy from user\n"); + return -EFAULT; + } + + pr_debug("network load with perf request %d\n", req.perf_mode); + + ret = npu_host_load_network(npu_dev, &req); + if (ret) { + pr_err("network load failed: %d\n", ret); + return -EFAULT; + } + + ret = copy_to_user(argp, &req, sizeof(req)); + if (ret) { + pr_err("fail to copy to user\n"); + return -EFAULT; + } return 0; } -static int npu_close(struct inode *inode, struct file *file) +static int npu_unload_network(struct npu_device *npu_dev, unsigned long arg) +{ + struct msm_npu_unload_network_ioctl req; + void __user *argp = (void __user *)arg; + int ret = 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + + if (ret) { + pr_err("fail to copy from user\n"); + return -EFAULT; + } + + ret = npu_host_unload_network(npu_dev, &req); + + if (ret) { + pr_err("npu_host_unload_network failed\n"); + return ret; + } + + ret = copy_to_user(argp, &req, sizeof(req)); + + if (ret) { + pr_err("fail to copy to user\n"); + return -EFAULT; + } + return 0; +} + +static int npu_exec_network(struct npu_device *npu_dev, unsigned long arg) { + struct msm_npu_exec_network_ioctl req; + void __user *argp = (void __user *)arg; + int ret = 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + + if (ret) { + pr_err("fail to copy from user\n"); + return -EFAULT; + } + + ret = npu_host_exec_network(npu_dev, &req); + + if (ret) { + pr_err("npu_host_exec_network failed\n"); + return ret; + } + + ret = copy_to_user(argp, &req, sizeof(req)); + + if (ret) { + pr_err("fail to copy to user\n"); + return -EFAULT; + } return 0; } static long npu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int ret = -EPERM; + int ret = -ENOIOCTLCMD; + struct npu_device *npu_dev = file->private_data; switch (cmd) { case MSM_NPU_GET_INFO: - ret = npu_get_info(file, cmd, arg); + ret = npu_get_info(npu_dev, arg); break; case MSM_NPU_MAP_BUF: + ret = npu_map_buf(npu_dev, arg); break; case MSM_NPU_UNMAP_BUF: + ret = npu_unmap_buf(npu_dev, arg); break; case MSM_NPU_LOAD_NETWORK: + ret = npu_load_network(npu_dev, arg); break; case MSM_NPU_UNLOAD_NETWORK: + ret = npu_unload_network(npu_dev, arg); break; case MSM_NPU_EXEC_NETWORK: + ret = npu_exec_network(npu_dev, arg); break; default: - pr_err("unexpected IOCTL %d\n", cmd); - } - - return ret; -} - -/* 32 bit */ -#ifdef CONFIG_COMPAT -static long npu_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -EPERM; - - switch (cmd) { - case MSM_NPU_GET_INFO_32: - ret = npu_get_info(file, cmd, arg); - break; - case MSM_NPU_MAP_BUF_32: - break; - case MSM_NPU_UNMAP_BUF_32: - break; - case MSM_NPU_LOAD_NETWORK_32: - break; - case MSM_NPU_UNLOAD_NETWORK_32: - break; - case MSM_NPU_EXEC_NETWORK_32: - break; - default: - pr_err("unexpected IOCTL %d\n", cmd); + pr_err("unexpected IOCTL %x\n", cmd); } return ret; } -#endif - -static const struct file_operations npu_fops = { - .owner = THIS_MODULE, - .open = npu_open, - .release = npu_close, - .unlocked_ioctl = npu_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = npu_compat_ioctl, -#endif -}; -static int npu_parse_dt_clock(struct npu_device_t *npu_dev) +/* ------------------------------------------------------------------------- + * Device Tree Parsing + * ------------------------------------------------------------------------- + */ +static int npu_parse_dt_clock(struct npu_device *npu_dev) { - u32 i = 0, rc = 0; + int rc = 0; + uint32_t i, j; const char *clock_name; int num_clk; - struct npu_clk_t *core_clks = npu_dev->core_clks; + struct npu_clk *core_clks = npu_dev->core_clks; struct platform_device *pdev = npu_dev->pdev; num_clk = of_property_count_strings(pdev->dev.of_node, "clock-names"); if (num_clk <= 0) { pr_err("clocks are not defined\n"); + rc = -EINVAL; goto clk_err; } - if (num_clk > NPU_MAX_CLK_NUM) { - pr_err("clock number is over the limit %d\n", num_clk); - num_clk = NPU_MAX_CLK_NUM; + if (num_clk != NUM_TOTAL_CLKS) { + pr_err("number of clocks is invalid [%d] should be [%d]\n", + num_clk, NUM_TOTAL_CLKS); + rc = -EINVAL; + goto clk_err; } npu_dev->core_clk_num = num_clk; for (i = 0; i < num_clk; i++) { of_property_read_string_index(pdev->dev.of_node, "clock-names", i, &clock_name); - strlcpy(core_clks[i].clk_name, clock_name, - sizeof(core_clks[i].clk_name)); - core_clks[i].clk = devm_clk_get(&pdev->dev, clock_name); - if (IS_ERR(core_clks[i].clk)) { + for (j = 0; j < num_clk; j++) { + if (!strcmp(npu_clock_order[j], clock_name)) + break; + } + if (j == num_clk) { + pr_err("clock is not in ordered list\n"); + rc = -EINVAL; + goto clk_err; + } + strlcpy(core_clks[j].clk_name, clock_name, + sizeof(core_clks[j].clk_name)); + core_clks[j].clk = devm_clk_get(&pdev->dev, clock_name); + if (IS_ERR(core_clks[j].clk)) { pr_err("unable to get clk: %s\n", clock_name); rc = -EINVAL; break; @@ -175,22 +1032,26 @@ static int npu_parse_dt_clock(struct npu_device_t *npu_dev) return rc; } -static int npu_parse_dt_regulator(struct npu_device_t *npu_dev) +static int npu_parse_dt_regulator(struct npu_device *npu_dev) { - u32 i = 0, rc = 0; + int rc = 0; + uint32_t i; const char *name; int num; - struct npu_regulator_t *regulators = npu_dev->regulators; + struct npu_regulator *regulators = npu_dev->regulators; struct platform_device *pdev = npu_dev->pdev; num = of_property_count_strings(pdev->dev.of_node, "qcom,proxy-reg-names"); if (num <= 0) { + rc = -EINVAL; pr_err("regulator not defined\n"); goto regulator_err; } if (num > NPU_MAX_REGULATOR_NUM) { - pr_err("regulator number is over the limit %d", num); + rc = -EINVAL; + pr_err("regulator number %d is over the limit %d\n", num, + NPU_MAX_REGULATOR_NUM); num = NPU_MAX_REGULATOR_NUM; } @@ -212,20 +1073,153 @@ static int npu_parse_dt_regulator(struct npu_device_t *npu_dev) return rc; } +static int npu_of_parse_pwrlevels(struct npu_device *npu_dev, + struct device_node *node) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + struct device_node *child; + uint32_t init_level = 0; + const char *clock_name; + struct platform_device *pdev = npu_dev->pdev; + + pwr->num_pwrlevels = 0; + + for_each_available_child_of_node(node, child) { + uint32_t i = 0; + uint32_t j = 0; + uint32_t index; + uint32_t clk_array_values[NUM_TOTAL_CLKS]; + struct npu_pwrlevel *level; + + if (of_property_read_u32(child, "reg", &index)) + return -EINVAL; + + if (index >= NPU_MAX_PWRLEVELS) { + pr_err("pwrlevel index %d is out of range\n", + index); + continue; + } + + if (index >= pwr->num_pwrlevels) + pwr->num_pwrlevels = index + 1; + + if (of_property_read_u32_array(child, "clk-freq", + clk_array_values, npu_dev->core_clk_num)) { + pr_err("pwrlevel index %d read clk-freq failed %d\n", + index, npu_dev->core_clk_num); + return -EINVAL; + } + + /* sort */ + level = &pwr->pwrlevels[index]; + for (i = 0; i < npu_dev->core_clk_num; i++) { + of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clock_name); + + for (j = 0; j < npu_dev->core_clk_num; j++) { + if (!strcmp(npu_clock_order[j], + clock_name)) { + level->clk_freq[j] = + clk_array_values[i]; + break; + } + } + if (j == npu_dev->core_clk_num) { + pr_err("pwrlevel clock is not in ordered list\n"); + return -EINVAL; + } + } + } + + of_property_read_u32(node, "initial-pwrlevel", &init_level); + + if (init_level >= pwr->num_pwrlevels) + init_level = 0; + + pr_debug("init power level %d\n", init_level); + pwr->active_pwrlevel = init_level; + pwr->default_pwrlevel = init_level; + pwr->uc_pwrlevel = init_level; + pwr->min_pwrlevel = 0; + pwr->max_pwrlevel = pwr->num_pwrlevels - 1; + + return 0; +} + +static int npu_pwrctrl_init(struct npu_device *npu_dev) +{ + struct platform_device *pdev = npu_dev->pdev; + struct device_node *node; + int ret = 0; + struct platform_device *p2dev; + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + + /* Power levels */ + node = of_find_node_by_name(pdev->dev.of_node, "qcom,npu-pwrlevels"); + + if (!node) { + pr_err("unable to find 'qcom,npu-pwrlevels'\n"); + return -EINVAL; + } + + ret = npu_of_parse_pwrlevels(npu_dev, node); + if (ret) + return ret; + + /* Parse Bandwidth */ + node = of_parse_phandle(pdev->dev.of_node, + "qcom,npubw-dev", 0); + + if (node) { + /* Set to 1 initially - we assume bwmon is on */ + pwr->bwmon_enabled = 1; + p2dev = of_find_device_by_node(node); + if (p2dev) { + pwr->devbw = &p2dev->dev; + } else { + pr_err("parser power level failed\n"); + ret = -EINVAL; + return ret; + } + } else { + pr_err("bwdev is not defined in dts\n"); + pwr->devbw = NULL; + ret = -EINVAL; + } + + return ret; +} + +static int npu_thermalctrl_init(struct npu_device *npu_dev) +{ + struct npu_pwrctrl *pwr = &npu_dev->pwrctrl; + struct npu_thermalctrl *thermalctrl = &npu_dev->thermalctrl; + int ret = 0; + + thermalctrl->max_state = pwr->max_pwrlevel; + thermalctrl->current_state = 0; + return ret; +} + +/* ------------------------------------------------------------------------- + * Probe/Remove + * ------------------------------------------------------------------------- + */ static int npu_probe(struct platform_device *pdev) { - int rc; - struct resource *res; - struct npu_device_t *npu_dev; + int rc = 0; + struct resource *res = 0; + struct npu_device *npu_dev = 0; + struct thermal_cooling_device *tcdev = 0; npu_dev = devm_kzalloc(&pdev->dev, - sizeof(struct npu_device_t), GFP_KERNEL); + sizeof(struct npu_device), GFP_KERNEL); if (!npu_dev) return -EFAULT; npu_dev->pdev = pdev; - platform_set_drvdata(pdev, npu_dev); + platform_set_drvdata(pdev, npu_dev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "npu_base"); if (!res) { @@ -233,7 +1227,6 @@ static int npu_probe(struct platform_device *pdev) rc = -ENOMEM; goto error_get_dev_num; } - npu_dev->reg_size = resource_size(res); rc = npu_parse_dt_regulator(npu_dev); @@ -244,6 +1237,14 @@ static int npu_probe(struct platform_device *pdev) if (rc) goto error_get_dev_num; + rc = npu_pwrctrl_init(npu_dev); + if (rc) + goto error_get_dev_num; + + rc = npu_thermalctrl_init(npu_dev); + if (rc) + goto error_get_dev_num; + npu_dev->npu_base = devm_ioremap(&pdev->dev, res->start, npu_dev->reg_size); if (unlikely(!npu_dev->npu_base)) { @@ -251,18 +1252,26 @@ static int npu_probe(struct platform_device *pdev) rc = -ENOMEM; goto error_get_dev_num; } + npu_dev->npu_phys = res->start; - pr_info("NPU HW Base phy_Address=0x%x virt=%pK\n", + pr_debug("hw base phy address=0x%x virt=%pK\n", npu_dev->npu_phys, npu_dev->npu_base); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { - pr_err("unable to get NPU irq\n"); + pr_err("unable to get irq\n"); rc = -ENOMEM; goto error_get_dev_num; } + rc = devm_request_irq(&pdev->dev, res->start, + npu_intr_hdler, 0x0, "npu", npu_dev); + if (rc) { + pr_err("devm_request_irq() failed\n"); + goto error_get_dev_num; + } + disable_irq(res->start); npu_dev->irq = res->start; - + pr_debug("irq %d\n", npu_dev->irq); /* character device might be optional */ rc = alloc_chrdev_region(&npu_dev->dev_num, 0, 1, DRIVER_NAME); if (rc < 0) { @@ -292,13 +1301,65 @@ static int npu_probe(struct platform_device *pdev) pr_err("cdev_add failed %d\n", rc); goto error_cdev_add; } - + dev_set_drvdata(npu_dev->device, npu_dev); + pr_debug("drvdata %pK %pK\n", dev_get_drvdata(&pdev->dev), + dev_get_drvdata(npu_dev->device)); rc = sysfs_create_group(&npu_dev->device->kobj, &npu_fs_attr_group); if (rc) { pr_err("unable to register npu sysfs nodes\n"); goto error_res_init; } + + if (IS_ENABLED(CONFIG_THERMAL)) { + tcdev = thermal_of_cooling_device_register(pdev->dev.of_node, + "npu", npu_dev, + &npu_cooling_ops); + if (IS_ERR(tcdev)) { + dev_err(&pdev->dev, + "npu: failed to register npu as cooling device"); + rc = PTR_ERR(tcdev); + goto error_driver_init; + } + npu_dev->tcdev = tcdev; + thermal_cdev_update(tcdev); + } + + rc = npu_debugfs_init(npu_dev); + if (rc) + goto error_driver_init; + + npu_dev->smmu_ctx.attach_cnt = 0; + npu_dev->smmu_ctx.mmu_mapping = arm_iommu_create_mapping( + pdev->dev.bus, DDR_MAPPED_START_ADDR, DDR_MAPPED_SIZE); + if (IS_ERR(npu_dev->smmu_ctx.mmu_mapping)) { + pr_err("iommu create mapping failed\n"); + rc = -ENOMEM; + npu_dev->smmu_ctx.mmu_mapping = NULL; + goto error_driver_init; + } + + rc = arm_iommu_attach_device(&(npu_dev->pdev->dev), + npu_dev->smmu_ctx.mmu_mapping); + if (rc) { + pr_err("arm_iommu_attach_device failed\n"); + goto error_driver_init; + } + + INIT_LIST_HEAD(&(npu_dev->mapped_buffers.list)); + + rc = npu_host_init(npu_dev); + if (rc) { + pr_err("unable to init host\n"); + goto error_driver_init; + } + return rc; +error_driver_init: + sysfs_remove_group(&npu_dev->device->kobj, &npu_fs_attr_group); + arm_iommu_detach_device(&(npu_dev->pdev->dev)); + if (!npu_dev->smmu_ctx.mmu_mapping) + arm_iommu_release_mapping(npu_dev->smmu_ctx.mmu_mapping); + sysfs_remove_group(&npu_dev->device->kobj, &npu_fs_attr_group); error_res_init: cdev_del(&npu_dev->cdev); error_cdev_add: @@ -313,44 +1374,50 @@ static int npu_probe(struct platform_device *pdev) static int npu_remove(struct platform_device *pdev) { - struct npu_device_t *npu_dev; + struct npu_device *npu_dev; npu_dev = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); + thermal_cooling_device_unregister(npu_dev->tcdev); + npu_debugfs_deinit(npu_dev); + npu_host_deinit(npu_dev); + arm_iommu_release_mapping(npu_dev->smmu_ctx.mmu_mapping); + arm_iommu_detach_device(&(npu_dev->pdev->dev)); sysfs_remove_group(&npu_dev->device->kobj, &npu_fs_attr_group); cdev_del(&npu_dev->cdev); device_destroy(npu_dev->class, npu_dev->dev_num); class_destroy(npu_dev->class); unregister_chrdev_region(npu_dev->dev_num, 1); + platform_set_drvdata(pdev, NULL); return 0; } -static const struct of_device_id npu_dt_match[] = { - { .compatible = "qcom,msm-npu",}, - {} -}; - -MODULE_DEVICE_TABLE(of, npu_dt_match); +/* ------------------------------------------------------------------------- + * Suspend/Resume + * ------------------------------------------------------------------------- + */ +#if defined(CONFIG_PM) +static int npu_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} -static struct platform_driver npu_driver = { - .probe = npu_probe, - .remove = npu_remove, - .driver = { - .name = "msm_npu", - .owner = THIS_MODULE, - .of_match_table = npu_dt_match, - .pm = NULL, - }, -}; +static int npu_resume(struct platform_device *dev) +{ + return 0; +} +#endif +/* ------------------------------------------------------------------------- + * Module Entry Points + * ------------------------------------------------------------------------- + */ static int __init npu_init(void) { int rc; rc = platform_driver_register(&npu_driver); if (rc) - pr_err("npu register failed %d", rc); + pr_err("register failed %d\n", rc); return rc; } @@ -362,5 +1429,7 @@ static void __exit npu_exit(void) module_init(npu_init); module_exit(npu_exit); +MODULE_DEVICE_TABLE(of, npu_dt_match); MODULE_DESCRIPTION("MSM NPU driver"); MODULE_LICENSE("GPL v2"); +MODULE_INFO(intree, "Y"); diff --git a/drivers/media/platform/msm/npu/npu_firmware.h b/drivers/media/platform/msm/npu/npu_firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..1d1cc6df23ec61d680c026d24ae4f3670cae9c09 --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_firmware.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2018, 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 _NPU_FIRMWARE_H +#define _NPU_FIRMWARE_H + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +/* NPU Firmware Control/Status Register, written by FW and read HOST */ +#define REG_NPU_FW_CTRL_STATUS NPU_GPR0 +/* written by HOST and read by FW for control */ +#define REG_NPU_HOST_CTRL_STATUS NPU_GPR1 +/* Data value for control */ +#define REG_NPU_HOST_CTRL_VALUE NPU_GPR2 +/* Simulates an interrupt for FW->HOST, used for pre-silicon */ +#define REG_FW_TO_HOST_EVENT NPU_GPR3 +/* Data value for debug */ +#define REG_NPU_FW_DEBUG_DATA NPU_GPR4 + +/* Started job count */ +#define REG_FW_JOB_CNT_START NPU_GPR14 +/* Finished job count */ +#define REG_FW_JOB_CNT_END NPU_GPR15 + +/* NPU FW Control/Status Register */ +/* bit fields definitions in CTRL STATUS REG */ +#define FW_CTRL_STATUS_IPC_READY_BIT 0 +#define FW_CTRL_STATUS_LOG_READY_BIT 1 +#define FW_CTRL_STATUS_EXECUTE_THREAD_READY_BIT 2 +#define FW_CTRL_STATUS_MAIN_THREAD_READY_BIT 3 +#define FW_CTRL_STATUS_EXECUTING_ACO 4 + +/* 32 bit values of the bit fields above */ +#define FW_CTRL_STATUS_IPC_READY_VAL (1 << FW_CTRL_STATUS_IPC_READY_BIT) +#define FW_CTRL_STATUS_LOG_READY_VAL (1 << FW_CTRL_STATUS_LOG_READY_BIT) +#define FW_CTRL_STATUS_EXECUTE_THREAD_READY_VAL \ + (1 << FW_CTRL_STATUS_EXECUTE_THREAD_READY_BIT) +#define FW_CTRL_STATUS_MAIN_THREAD_READY_VAL \ + (1 << FW_CTRL_STATUS_MAIN_THREAD_READY_BIT) +#define FW_CTRL_STATUS_EXECUTING_ACO_VAL \ + (1 << FW_CTRL_STATUS_EXECUTING_ACO) + +/* NPU HOST Control/Status Register */ +/* bit fields definitions in CTRL STATUS REG */ +/* Host has programmed IPC address into the REG_NPU_HOST_CTRL_VALUE register */ +#define HOST_CTRL_STATUS_IPC_ADDRESS_READY_BIT 0 +/* Host has enabled logging during boot */ +#define HOST_CTRL_STATUS_BOOT_ENABLE_LOGGING_BIT 1 +/* Host has enabled the clk gating of CAL during boot */ +#define HOST_CTRL_STATUS_BOOT_ENABLE_CLK_GATE_BIT 2 + +/* 32 bit values of the bit fields above */ +#define HOST_CTRL_STATUS_IPC_ADDRESS_READY_VAL \ + (1 << HOST_CTRL_STATUS_IPC_ADDRESS_READY_BIT) +#define HOST_CTRL_STATUS_BOOT_ENABLE_LOGGING_VAL \ + (1 << HOST_CTRL_STATUS_BOOT_ENABLE_LOGGING_BIT) +#define HOST_CTRL_STATUS_BOOT_ENABLE_CLK_GATE_VAL \ + (1 << HOST_CTRL_STATUS_BOOT_ENABLE_CLK_GATE_BIT) + +/* Queue table header definition */ +struct hfi_queue_tbl_header { + uint32_t qtbl_version; /* queue table version number */ + uint32_t qtbl_size; /* total tables+queues size in bytes */ + uint32_t qtbl_qhdr0_offset; /* offset of the 1st queue header entry */ + uint32_t qtbl_qhdr_size; /* queue header size */ + uint32_t qtbl_num_q; /* total number of queues */ + uint32_t qtbl_num_active_q; /* number of active queues */ +}; + +/* Queue header definition */ +struct hfi_queue_header { + uint32_t qhdr_status; /* 0 == inactive, 1 == active */ + /* 4 byte-aligned start offset from start of q table */ + uint32_t qhdr_start_offset; + /* queue type */ + uint32_t qhdr_type; + /* in bytes, value of 0 means packets are variable size.*/ + uint32_t qhdr_q_size; + /* size of the Queue packet entries, in bytes, 0 means variable size */ + uint32_t qhdr_pkt_size; + + uint32_t qhdr_pkt_drop_cnt; + /* receiver watermark in # of queue packets */ + uint32_t qhdr_rx_wm; + /* transmitter watermark in # of queue packets */ + uint32_t qhdr_tx_wm; + /* + * set to request an interrupt from transmitter + * if qhdr_tx_wm is reached + */ + uint32_t qhdr_rx_req; + /* + * set to request an interrupt from receiver + * if qhdr_rx_wm is reached + */ + uint32_t qhdr_tx_req; + uint32_t qhdr_rx_irq_status; /* Not used */ + uint32_t qhdr_tx_irq_status; /* Not used */ + uint32_t qhdr_read_idx; /* read index in bytes */ + uint32_t qhdr_write_idx; /* write index in bytes */ +}; + +/* in bytes */ +#define HFI_QUEUE_TABLE_HEADER_SIZE (sizeof(struct hfi_queue_tbl_header)) +#define HFI_QUEUE_HEADER_SIZE (sizeof(struct hfi_queue_header)) +#define HFI_QUEUE_TABLE_SIZE (HFI_QUEUE_TABLE_HEADER_SIZE + \ + (NPU_HFI_NUMBER_OF_QS * HFI_QUEUE_HEADER_SIZE)) + +/* Queue Indexes */ +#define IPC_QUEUE_CMD_HIGH_PRIORITY 0 /* High priority Queue APPS->M0 */ +#define IPC_QUEUE_APPS_EXEC 1 /* APPS Execute Queue APPS->M0 */ +#define IPC_QUEUE_DSP_EXEC 2 /* DSP Execute Queue DSP->M0 */ +#define IPC_QUEUE_APPS_RSP 3 /* APPS Message Queue M0->APPS */ +#define IPC_QUEUE_DSP_RSP 4 /* DSP Message Queue DSP->APPS */ +#define IPC_QUEUE_LOG 5 /* Log Message Queue M0->APPS */ + +#define NPU_HFI_NUMBER_OF_QS 6 +#define NPU_HFI_NUMBER_OF_ACTIVE_QS 6 + +#define NPU_HFI_QUEUES_PER_CHANNEL 2 + +#endif /* _NPU_FIRMWARE_H */ diff --git a/drivers/media/platform/msm/npu/npu_host_ipc.c b/drivers/media/platform/msm/npu/npu_host_ipc.c new file mode 100644 index 0000000000000000000000000000000000000000..0730e2019edca11eeabbee2ccc86165d70a99e44 --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_host_ipc.c @@ -0,0 +1,414 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include "npu_hw_access.h" +#include "npu_mgr.h" +#include "npu_firmware.h" +#include "npu_hw.h" +#include "npu_host_ipc.h" + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +/* HFI IPC interface */ +#define TX_HDR_TYPE 0x01000000 +#define RX_HDR_TYPE 0x00010000 +#define HFI_QTBL_STATUS_ENABLED 0x00000001 + +#define QUEUE_TBL_VERSION 0x87654321 + +/* ------------------------------------------------------------------------- + * Data Structures + * ------------------------------------------------------------------------- + */ +struct npu_queue_tuple { + uint32_t size; + uint32_t hdr; +}; + +static const struct npu_queue_tuple npu_q_setup[6] = { + { 1024, IPC_QUEUE_CMD_HIGH_PRIORITY | TX_HDR_TYPE | RX_HDR_TYPE }, + { 4096, IPC_QUEUE_APPS_EXEC | TX_HDR_TYPE | RX_HDR_TYPE }, + { 4096, IPC_QUEUE_DSP_EXEC | TX_HDR_TYPE | RX_HDR_TYPE }, + { 4096, IPC_QUEUE_APPS_RSP | TX_HDR_TYPE | RX_HDR_TYPE }, + { 1024, IPC_QUEUE_DSP_RSP | TX_HDR_TYPE | RX_HDR_TYPE }, + { 1024, IPC_QUEUE_LOG | TX_HDR_TYPE | RX_HDR_TYPE }, +}; + +/* ------------------------------------------------------------------------- + * File Scope Function Prototypes + * ------------------------------------------------------------------------- + */ +static int npu_host_ipc_init_hfi(struct npu_device *npu_dev); +static int npu_host_ipc_send_cmd_hfi(struct npu_device *npu_dev, + uint32_t q_idx, void *cmd_ptr); +static int npu_host_ipc_read_msg_hfi(struct npu_device *npu_dev, + uint32_t q_idx, uint32_t *msg_ptr); +static int ipc_queue_read(struct npu_device *npu_dev, uint32_t target_que, + uint8_t *packet, uint8_t *is_tx_req_set); +static int ipc_queue_write(struct npu_device *npu_dev, uint32_t target_que, + uint8_t *packet, uint8_t *is_rx_req_set); + +/* ------------------------------------------------------------------------- + * Function Definitions + * ------------------------------------------------------------------------- + */ +static int npu_host_ipc_init_hfi(struct npu_device *npu_dev) +{ + int status = 0; + struct hfi_queue_tbl_header *q_tbl_hdr = NULL; + struct hfi_queue_header *q_hdr_arr = NULL; + struct hfi_queue_header *q_hdr = NULL; + void *q_tbl_addr = 0; + uint32_t reg_val = 0; + uint32_t q_idx = 0; + uint32_t q_tbl_size = sizeof(struct hfi_queue_tbl_header) + + (NPU_HFI_NUMBER_OF_QS * sizeof(struct hfi_queue_header)); + uint32_t q_size = 0; + uint32_t cur_start_offset = 0; + + reg_val = REGR(npu_dev, REG_NPU_FW_CTRL_STATUS); + + /* + * If the firmware is already running and we're just attaching, + * we do not need to do this + */ + if ((reg_val & FW_CTRL_STATUS_LOG_READY_VAL) != 0) + return status; + + /* check for valid interface queue table start address */ + q_tbl_addr = kzalloc(q_tbl_size, GFP_KERNEL); + if (q_tbl_addr == NULL) + return -ENOMEM; + + /* retrieve interface queue table start address */ + q_tbl_hdr = q_tbl_addr; + q_hdr_arr = (struct hfi_queue_header *)((uint8_t *)q_tbl_addr + + sizeof(struct hfi_queue_tbl_header)); + + /* initialize the interface queue table header */ + q_tbl_hdr->qtbl_version = QUEUE_TBL_VERSION; + q_tbl_hdr->qtbl_size = q_tbl_size; + q_tbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_queue_tbl_header); + q_tbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_queue_header); + q_tbl_hdr->qtbl_num_q = NPU_HFI_NUMBER_OF_QS; + q_tbl_hdr->qtbl_num_active_q = NPU_HFI_NUMBER_OF_ACTIVE_QS; + + cur_start_offset = q_tbl_size; + + for (q_idx = IPC_QUEUE_CMD_HIGH_PRIORITY; + q_idx <= IPC_QUEUE_LOG; q_idx++) { + q_hdr = &q_hdr_arr[q_idx]; + /* queue is active */ + q_hdr->qhdr_status = 0x01; + q_hdr->qhdr_start_offset = cur_start_offset; + q_size = npu_q_setup[q_idx].size; + q_hdr->qhdr_type = npu_q_setup[q_idx].hdr; + /* in bytes */ + q_hdr->qhdr_q_size = q_size; + /* variable size packets */ + q_hdr->qhdr_pkt_size = 0; + q_hdr->qhdr_pkt_drop_cnt = 0; + q_hdr->qhdr_rx_wm = 0x1; + q_hdr->qhdr_tx_wm = 0x1; + /* since queue is initially empty */ + q_hdr->qhdr_rx_req = 0x1; + q_hdr->qhdr_tx_req = 0x0; + /* not used */ + q_hdr->qhdr_rx_irq_status = 0; + /* not used */ + q_hdr->qhdr_tx_irq_status = 0; + q_hdr->qhdr_read_idx = 0; + q_hdr->qhdr_write_idx = 0; + cur_start_offset += q_size; + } + + MEMW(npu_dev, IPC_ADDR, (uint8_t *)q_tbl_hdr, q_tbl_size); + kfree(q_tbl_addr); + /* Write in the NPU's address for where IPC starts */ + REGW(npu_dev, (uint32_t)REG_NPU_HOST_CTRL_VALUE, + (uint32_t)IPC_MEM_OFFSET_FROM_SSTCM); + /* Set value bit */ + reg_val = REGR(npu_dev, (uint32_t)REG_NPU_HOST_CTRL_STATUS); + REGW(npu_dev, (uint32_t)REG_NPU_HOST_CTRL_STATUS, reg_val | + HOST_CTRL_STATUS_IPC_ADDRESS_READY_VAL); + return status; +} + +static int npu_host_ipc_send_cmd_hfi(struct npu_device *npu_dev, + uint32_t q_idx, void *cmd_ptr) +{ + int status = 0; + uint8_t is_rx_req_set = 0; + + status = ipc_queue_write(npu_dev, q_idx, (uint8_t *)cmd_ptr, + &is_rx_req_set); + + if (status == -ENOSPC) { + while (status == -ENOSPC) { + status = ipc_queue_write(npu_dev, q_idx, + (uint8_t *)cmd_ptr, &is_rx_req_set); + } + } + + if (status == 0) { + if (is_rx_req_set == 1) + status = INTERRUPT_RAISE_NPU(npu_dev); + } + + if (status == 0) + pr_debug("Cmd Msg put on Command Queue - SUCCESSS"); + else + pr_err("Cmd Msg put on Command Queue - FAILURE"); + + return status; +} + +static int npu_host_ipc_read_msg_hfi(struct npu_device *npu_dev, + uint32_t q_idx, uint32_t *msg_ptr) +{ + int status = 0; + uint8_t is_tx_req_set; + + status = ipc_queue_read(npu_dev, q_idx, (uint8_t *)msg_ptr, + &is_tx_req_set); + + if (status == 0) { + /* raise interrupt if qhdr_tx_req is set */ + if (is_tx_req_set == 1) + status = INTERRUPT_RAISE_NPU(npu_dev); + } + + return status; +} + +static int ipc_queue_read(struct npu_device *npu_dev, + uint32_t target_que, uint8_t *packet, + uint8_t *is_tx_req_set) +{ + int status = 0; + struct hfi_queue_header queue; + uint32_t packet_size, new_read_idx; + size_t read_ptr; + size_t offset = 0; + + offset = (size_t)IPC_ADDR + sizeof(struct hfi_queue_tbl_header) + + target_que * sizeof(struct hfi_queue_header); + + if ((packet == NULL) || (is_tx_req_set == NULL)) + return -EINVAL; + + /* Read the queue */ + MEMR(npu_dev, (void *)((size_t)offset), (uint8_t *)&queue, + HFI_QUEUE_HEADER_SIZE); + /* check if queue is empty */ + if (queue.qhdr_read_idx == queue.qhdr_write_idx) { + /* + * set qhdr_rx_req, to inform the sender that the Interrupt + * needs to be raised with the next packet queued + */ + queue.qhdr_rx_req = 1; + *is_tx_req_set = 0; + status = -EPERM; + goto exit; + } + + read_ptr = ((size_t)(size_t)IPC_ADDR + + queue.qhdr_start_offset + queue.qhdr_read_idx); + + /* Read packet size */ + MEMR(npu_dev, (void *)((size_t)read_ptr), packet, 4); + packet_size = *((uint32_t *)packet); + + pr_debug("target_que: %d, packet_size: %d ", + target_que, + packet_size); + + if (packet_size == 0) { + status = -EPERM; + goto exit; + } + new_read_idx = queue.qhdr_read_idx + packet_size; + + if (new_read_idx < (queue.qhdr_q_size)) { + MEMR(npu_dev, (void *)((size_t)read_ptr), packet, packet_size); + } else { + new_read_idx -= (queue.qhdr_q_size); + + MEMR(npu_dev, (void *)((size_t)read_ptr), packet, + packet_size - new_read_idx); + + MEMR(npu_dev, (void *)((size_t)IPC_ADDR + + queue.qhdr_start_offset), + (void *)((size_t)packet + (packet_size-new_read_idx)), + new_read_idx); + } + + queue.qhdr_read_idx = new_read_idx; + + if (queue.qhdr_read_idx == queue.qhdr_write_idx) + /* + * receiver wants an interrupt from transmitter + * (when next item queued) because queue is empty + */ + queue.qhdr_rx_req = 1; + else + /* clear qhdr_rx_req since the queue is not empty */ + queue.qhdr_rx_req = 0; + + if (queue.qhdr_tx_req == 1) + /* transmitter requested an interrupt */ + *is_tx_req_set = 1; + else + *is_tx_req_set = 0; +exit: + /* Update RX interrupt request -- queue.qhdr_rx_req */ + MEMW(npu_dev, (void *)((size_t)offset + + (uint32_t)((size_t)&(queue.qhdr_rx_req) - + (size_t)&queue)), (uint8_t *)&queue.qhdr_rx_req, + sizeof(queue.qhdr_rx_req)); + /* Update Read pointer -- queue.qhdr_read_idx */ + MEMW(npu_dev, (void *)((size_t)offset + (uint32_t)( + (size_t)&(queue.qhdr_read_idx) - (size_t)&queue)), + (uint8_t *)&queue.qhdr_read_idx, sizeof(queue.qhdr_read_idx)); + + return status; +} + +static int ipc_queue_write(struct npu_device *npu_dev, + uint32_t target_que, uint8_t *packet, + uint8_t *is_rx_req_set) +{ + int status = 0; + struct hfi_queue_header queue; + uint32_t packet_size, new_write_idx; + uint32_t empty_space; + void *write_ptr; + uint32_t read_idx; + + size_t offset = (size_t)IPC_ADDR + + sizeof(struct hfi_queue_tbl_header) + + target_que * sizeof(struct hfi_queue_header); + + if ((packet == NULL) || (is_rx_req_set == NULL)) + return -EINVAL; + + MEMR(npu_dev, (void *)((size_t)offset), (uint8_t *)&queue, + HFI_QUEUE_HEADER_SIZE); + packet_size = (*(uint32_t *)packet); + if (packet_size == 0) { + /* assign failed status and return */ + status = -EPERM; + goto exit; + } + + /* sample Read Idx */ + read_idx = queue.qhdr_read_idx; + + /* Calculate Empty Space(UWord32) in the Queue */ + empty_space = (queue.qhdr_write_idx >= read_idx) ? + ((queue.qhdr_q_size) - (queue.qhdr_write_idx - read_idx)) : + (read_idx - queue.qhdr_write_idx); + + if (empty_space <= packet_size) { + /* + * If Queue is FULL/ no space for message + * set qhdr_tx_req. + */ + queue.qhdr_tx_req = 1; + + /* + * Queue is FULL, force raise an interrupt to Receiver + */ + *is_rx_req_set = 1; + + status = -ENOSPC; + goto exit; + } + + /* + * clear qhdr_tx_req so that receiver does not raise an interrupt + * on reading packets from Queue, since there is space to write + * the next packet + */ + queue.qhdr_tx_req = 0; + + new_write_idx = (queue.qhdr_write_idx + packet_size); + + write_ptr = (void *)(size_t)((size_t)IPC_ADDR + + queue.qhdr_start_offset + queue.qhdr_write_idx); + + if (new_write_idx < queue.qhdr_q_size) { + MEMW(npu_dev, (void *)((size_t)write_ptr), (uint8_t *)packet, + packet_size); + } else { + /* wraparound case */ + new_write_idx -= (queue.qhdr_q_size); + + MEMW(npu_dev, (void *)((size_t)write_ptr), (uint8_t *)packet, + packet_size - new_write_idx); + + MEMW(npu_dev, (void *)((size_t)((size_t)IPC_ADDR + + queue.qhdr_start_offset)), (uint8_t *)(packet + + (packet_size - new_write_idx)), new_write_idx); + } + + /* Update qhdr_write_idx */ + queue.qhdr_write_idx = new_write_idx; + + *is_rx_req_set = (queue.qhdr_rx_req == 1) ? 1 : 0; + + /* Update Write pointer -- queue.qhdr_write_idx */ +exit: + /* Update TX request -- queue.qhdr_tx_req */ + MEMW(npu_dev, (void *)((size_t)(offset + (uint32_t)( + (size_t)&(queue.qhdr_tx_req) - (size_t)&queue))), + &queue.qhdr_tx_req, sizeof(queue.qhdr_tx_req)); + MEMW(npu_dev, (void *)((size_t)(offset + (uint32_t)( + (size_t)&(queue.qhdr_write_idx) - (size_t)&queue))), + &queue.qhdr_write_idx, sizeof(queue.qhdr_write_idx)); + + return status; +} + +/* ------------------------------------------------------------------------- + * IPC Interface functions + * ------------------------------------------------------------------------- + */ +int npu_host_ipc_send_cmd(struct npu_device *npu_dev, uint32_t q_idx, + void *cmd_ptr) +{ + return npu_host_ipc_send_cmd_hfi(npu_dev, q_idx, cmd_ptr); +} + +int npu_host_ipc_read_msg(struct npu_device *npu_dev, uint32_t q_idx, + uint32_t *msg_ptr) +{ + return npu_host_ipc_read_msg_hfi(npu_dev, q_idx, msg_ptr); +} + +int npu_host_ipc_pre_init(struct npu_device *npu_dev) +{ + return npu_host_ipc_init_hfi(npu_dev); +} + +int npu_host_ipc_post_init(struct npu_device *npu_dev) +{ + return 0; +} diff --git a/drivers/media/platform/msm/npu/npu_host_ipc.h b/drivers/media/platform/msm/npu/npu_host_ipc.h new file mode 100644 index 0000000000000000000000000000000000000000..2acdced5550a058e9ffcec1a442e58e373c6b36f --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_host_ipc.h @@ -0,0 +1,323 @@ +/* Copyright (c) 2018, 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 NPU_HOST_IPC_H +#define NPU_HOST_IPC_H + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +/* Messages sent **to** NPU */ +/* IPC Message Commands -- uint32_t */ +/* IPC command start base */ +#define NPU_IPC_CMD_BASE 0x00000000 +/* ipc_cmd_load_pkt */ +#define NPU_IPC_CMD_LOAD 0x00000001 +/* ipc_cmd_unload_pkt */ +#define NPU_IPC_CMD_UNLOAD 0x00000002 +/* ipc_cmd_execute_pkt */ +#define NPU_IPC_CMD_EXECUTE 0x00000003 +/* ipc_cmd_set_logging_state */ +#define NPU_IPC_CMD_CONFIG_LOG 0x00000004 +#define NPU_IPC_CMD_CONFIG_PERFORMANCE 0x00000005 +#define NPU_IPC_CMD_CONFIG_DEBUG 0x00000006 +#define NPU_IPC_CMD_SHUTDOWN 0x00000007 + +/* Messages sent **from** NPU */ +/* IPC Message Response -- uint32_t */ +/* IPC response start base */ +#define NPU_IPC_MSG_BASE 0x00010000 +/* ipc_msg_load_pkt */ +#define NPU_IPC_MSG_LOAD_DONE 0x00010001 +/* ipc_msg_header_pkt */ +#define NPU_IPC_MSG_UNLOAD_DONE 0x00010002 +/* ipc_msg_header_pkt */ +#define NPU_IPC_MSG_EXECUTE_DONE 0x00010003 +/* ipc_msg_event_notify_pkt */ +#define NPU_IPC_MSG_EVENT_NOTIFY 0x00010004 + +/* Logging message size */ +/* Number 32-bit elements for the maximum log message size */ +#define NPU_LOG_MSG_MAX_SIZE 4 + +/* Performance */ +/* Performance counters for current network layer */ +/* Amount of data read from all the DMA read channels */ +#define NPU_PERFORMANCE_DMA_DATA_READ 0x01 +/* Amount of data written from all the DMA write channels */ +#define NPU_PERFORMANCE_DMA_DATA_WRITTEN 0x02 +/* Number of blocks read by DMA channels */ +#define NPU_PERFORMANCE_DMA_NUM_BLOCKS_READ 0x03 +/* Number of blocks written by DMA channels */ +#define NPU_PERFORMANCE_DMA_NUM_BLOCKS_WRITTEN 0x04 +/* Number of instructions executed by CAL */ +#define NPU_PERFORMANCE_INSTRUCTIONS_CAL 0x05 +/* Number of instructions executed by CUB */ +#define NPU_PERFORMANCE_INSTRUCTIONS_CUB 0x06 +/* Timestamp of start of network load */ +#define NPU_PERFORMANCE_TIMESTAMP_LOAD_START 0x07 +/* Timestamp of end of network load */ +#define NPU_PERFORMANCE_TIMESTAMP_LOAD_END 0x08 +/* Timestamp of start of network execute */ +#define NPU_PERFORMANCE_TIMESTAMP_EXECUTE_START 0x09 +/* Timestamp of end of network execute */ +#define NPU_PERFORMANCE_TIMESTAMP_EXECUTE_END 0x10 +/* Timestamp of CAL start */ +#define NPU_PERFORMANCE_TIMESTAMP_CAL_START 0x11 +/* Timestamp of CAL end */ +#define NPU_PERFORMANCE_TIMESTAMP_CAL_END 0x12 +/* Timestamp of CUB start */ +#define NPU_PERFORMANCE_TIMESTAMP_CUB_START 0x13 +/* Timestamp of CUB end */ +#define NPU_PERFORMANCE_TIMESTAMP_CUB_END 0x14 + +/* Performance enable */ +/* Select which counters you want back per layer */ + +/* Shutdown */ +/* Immediate shutdown, discard any state, etc */ +#define NPU_SHUTDOWN_IMMEDIATE 0x01 +/* Shutdown after current execution (if any) is completed */ +#define NPU_SHUTDOWN_WAIT_CURRENT_EXECUTION 0x02 + +/* Debug stats */ +#define NUM_LAYER_STATS_PER_EXE_MSG_MAX 110 + +/* ------------------------------------------------------------------------- + * Data Structures + * ------------------------------------------------------------------------- + */ +/* Command Header - Header for all Messages **TO** NPU */ +/* + * command header packet definition for + * messages sent from host->NPU + */ +struct ipc_cmd_header_pkt { + uint32_t size; + uint32_t cmd_type; + uint32_t trans_id; + uint32_t flags; /* TDO what flags and why */ +}; + +/* Message Header - Header for all messages **FROM** NPU */ +/* + * message header packet definition for + * mesasges sent from NPU->host + */ +struct ipc_msg_header_pkt { + uint32_t size; + uint32_t msg_type; + uint32_t status; + uint32_t trans_id; + uint32_t flags; +}; + +/* Execute */ +/* + * FIRMWARE + * keep lastNetworkIDRan = uint32 + * keep wasLastNetworkChunky = BOOLEAN + */ +/* + * ACO Buffer definition + */ +struct npu_aco_buffer { + /* + * used to track if previous network is the same and already loaded, + * we can save a dma + */ + uint32_t network_id; + /* + * size of header + first chunk ACO buffer - + * this saves a dma by dmaing both header and first chunk + */ + uint32_t buf_size; + /* + * SMMU 32-bit mapped address that the DMA engine can read - + * uses lower 32 bits + */ + uint64_t address; +}; + +/* + * ACO Patch Parameters + */ +struct npu_patch_tuple { + uint32_t value; + uint32_t chunk_id; + uint16_t instruction_size_in_bytes; + uint16_t variable_size_in_bits; + uint16_t shift_value_in_bits; + uint32_t loc_offset; +}; + +struct npu_patch_params { + uint32_t num_params; + struct npu_patch_tuple param[2]; +}; + +/* + * LOAD command packet definition + */ +struct ipc_cmd_load_pkt { + struct ipc_cmd_header_pkt header; + struct npu_aco_buffer buf_pkt; +}; + +/* + * UNLOAD command packet definition + */ +struct ipc_cmd_unload_pkt { + struct ipc_cmd_header_pkt header; + uint32_t aco_hdl; +}; + +/* + * Execute packet definition + */ +struct ipc_cmd_execute_pkt { + struct ipc_cmd_header_pkt header; + struct npu_patch_params patch_params; + uint32_t aco_hdl; +}; + +/* + * LOAD response packet definition + */ +struct ipc_msg_load_pkt { + struct ipc_msg_header_pkt header; + uint32_t aco_hdl; +}; + +/* + * UNLOAD response packet definition -- ipc_msg_header_pkt + */ + +/* + * Layer Stats information returned back during EXECUTE_DONE response + */ +struct ipc_layer_stats { + /* + * layer id + * 8 bits enough for the current networks supporting + */ + uint8_t layer_id; + /* + * hardware tick count per layer + */ + uint32_t tick_count; +}; + +struct ipc_execute_layer_stats { + /* + * total number of layers associated with the execution + */ + uint8_t total_num_layers; + /* + * pointer to each layer stats + */ + struct ipc_layer_stats + layer_stats_list[NUM_LAYER_STATS_PER_EXE_MSG_MAX]; +}; + +struct ipc_execute_stats { + /* + * total e2e IPC tick count during EXECUTE cmd + */ + uint32_t e2e_ipc_tick_count; + /* + * tick count on ACO loading + */ + uint32_t aco_load_tick_count; + /* + * tick count on ACO execution + */ + uint32_t aco_execution_tick_count; + /* + * individual layer stats + */ + struct ipc_execute_layer_stats exe_stats; +}; + +/* + * EXECUTE response packet definition + */ +struct ipc_msg_execute_pkt { + struct ipc_msg_header_pkt header; + struct ipc_execute_stats stats; +}; + +/* Logging Related */ + +/* + * ipc_log_state_t - Logging state + */ +struct ipc_log_state { + uint32_t module_msk; + uint32_t level_msk; +}; + +struct ipc_cmd_log_state_pkt { + struct ipc_cmd_header_pkt header; + struct ipc_log_state log_state; +}; + +struct ipc_msg_log_state_pkt { + struct ipc_msg_header_pkt header; + struct ipc_log_state log_state; +}; + +/* + * Logging message + * This is a message from the NPU that contains the + * logging message. The values of part1-4 are not exposed + * the receiver has to refer to the logging implementation to + * intrepret what these mean and how to parse + */ +struct ipc_msg_log_pkt { + struct ipc_msg_header_pkt header; + uint32_t log_msg[NPU_LOG_MSG_MAX_SIZE]; +}; + +/* Performance Related */ + +/* + * Set counter mask of which counters we want + * This is a message from HOST->NPU Firmware + */ +struct ipc_cmd_set_performance_query { + struct ipc_cmd_header_pkt header; + uint32_t cnt_msk; +}; + +/* + * Set counter mask of which counters we want + * This is a message from HOST->NPU Firmware + */ +struct ipc_msg_performance_counters { + struct ipc_cmd_header_pkt header; + uint32_t layer_id; + uint32_t num_tulpes; + /* Array of tuples [HEADER,value] */ + uint32_t cnt_tulpes[1]; +}; + +/* + * ipc_cmd_shutdown - Shutdown command + */ +struct ipc_cmd_shutdown_pkt { + struct ipc_cmd_header_pkt header; + uint32_t shutdown_flags; +}; + +#endif /* NPU_HOST_IPC_H */ diff --git a/drivers/media/platform/msm/npu/npu_hw.h b/drivers/media/platform/msm/npu/npu_hw.h new file mode 100644 index 0000000000000000000000000000000000000000..8ab802e41ee6e05018d91cd70081d43575c2063a --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_hw.h @@ -0,0 +1,304 @@ +/* Copyright (c) 2018, 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 NPU_HW_H +#define NPU_HW_H + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +#define NPU_HW_VERSION (0x00100000) +#define NPU_MASTERn_IPC_IRQ_OUT(n) (0x00101004+0x1000*(n)) +#define NPU_CACHE_ATTR_IDn___POR 0x00011100 +#define NPU_CACHE_ATTR_IDn(n) (0x00100800+0x4*(n)) +#define NPU_MASTERn_IPC_IRQ_IN_CTRL(n) (0x00101008+0x1000*(n)) +#define NPU_MASTER0_IPC_IRQ_IN_CTRL__IRQ_SOURCE_SELECT___S 4 +#define NPU_GPR0 (0x00100100) +#define NPU_GPR1 (0x00100104) +#define NPU_GPR2 (0x00100108) +#define NPU_GPR3 (0x0010010C) +#define NPU_GPR4 (0x00100110) +#define NPU_GPR14 (0x00100138) +#define NPU_GPR15 (0x0010013C) + +#define BWMON2_SAMPLING_WINDOW (0x001605A8) +#define BWMON2_BYTE_COUNT_THRESHOLD_HIGH (0x001605AC) +#define BWMON2_BYTE_COUNT_THRESHOLD_MEDIUM (0x001605B0) +#define BWMON2_BYTE_COUNT_THRESHOLD_LOW (0x001605B4) +#define BWMON2_ZONE_ACTIONS (0x001605B8) +#define BWMON2_ZONE_COUNT_THRESHOLD (0x001605BC) + +#define CAL_DP_DMA_WR_RLD_CMD_0 (0x00580000) +#define CAL_DP_DMA_RD_RLD_CMD_0 (0x00580004) +#define CAL_DP_DMA_RD_RLD_CMD_1 (0x00580008) +#define CAL_DP_DMA_RD_RLD_CMD_2 (0x0058000C) +#define CAL_DP_DMA_RD_RLD_CMD_3 (0x00580010) +#define CAL_DP_DMA_LOC_RLD_CMD_0 (0x00580014) +#define CAL_DP_DMA_BUS_CFG (0x00580200) +#define CAL_DP_DMA_BUS_OT_CFG (0x00580204) +#define CAL_DP_DMA_WR_CFG_0 (0x00580400) +#define CAL_DP_DMA_WR_NUM_CMD_IT_0 (0x00580404) +#define CAL_DP_DMA_WR_START_ADDR_0 (0x00580408) +#define CAL_DP_DMA_WR_MAX_ADDR_0 (0x0058040C) +#define CAL_DP_DMA_WR_STRIDE_DIM0_0 (0x00580410) +#define CAL_DP_DMA_WR_STRIDE_DIM1_0 (0x00580414) +#define CAL_DP_DMA_WR_STRIDE_DIM2_0 (0x00580418) +#define CAL_DP_DMA_WR_ROW_INCR_0 (0x0058041C) +#define CAL_DP_DMA_WR_DIM0_XSIZE_0 (0x00580420) +#define CAL_DP_DMA_WR_DIM0_INCR_0 (0x00580424) +#define CAL_DP_DMA_WR_DIM0_NUM_BLK_0 (0x00580428) +#define CAL_DP_DMA_WR_DIM1_XSIZE_0 (0x0058042C) +#define CAL_DP_DMA_WR_DIM1_YSIZE_0 (0x00580430) +#define CAL_DP_DMA_WR_DIM2_XSIZE_0 (0x00580434) +#define CAL_DP_DMA_WR_DIM2_YSIZE_0 (0x00580438) +#define CAL_DP_DMA_WR_CLIENT_BLK_SIZE_CFG_0 (0x0058043C) +#define CAL_DP_DMA_WR_CLIENT_ADDR_CFG_0 (0x00580440) +#define CAL_DP_DMA_WR_CLIENT_BUFF_CFG_0 (0x00580444) +#define CAL_DP_DMA_WR_MMU_CFG_0 (0x00580448) +#define CAL_DP_DMA_RD_CFG_0 (0x00580600) +#define CAL_DP_DMA_RD_NUM_CMD_IT_0 (0x00580604) +#define CAL_DP_DMA_RD_START_ADDR_0 (0x00580608) +#define CAL_DP_DMA_RD_START_ADDR1_0 (0x0058060C) +#define CAL_DP_DMA_RD_END_ADDR0_0 (0x00580610) +#define CAL_DP_DMA_RD_MAX_ADDR_0 (0x00580614) +#define CAL_DP_DMA_RD_MAX_ADDR1_0 (0x00580618) +#define CAL_DP_DMA_RD_STRIDE_DIM0_0 (0x0058061C) +#define CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_0 (0x00580620) +#define CAL_DP_DMA_RD_STRIDE_DIM1_0 (0x00580624) +#define CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_0 (0x00580628) +#define CAL_DP_DMA_RD_STRIDE_DIM2_0 (0x0058062C) +#define CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_0 (0x00580630) +#define CAL_DP_DMA_RD_ROW_INCR_0 (0x00580634) +#define CAL_DP_DMA_RD_DIM0_BLK_SIZE_0 (0x00580638) +#define CAL_DP_DMA_RD_DIM0_INCR_0 (0x0058063C) +#define CAL_DP_DMA_RD_DIM0_NUM_BLK_0 (0x00580640) +#define CAL_DP_DMA_RD_DIM1_BLK_SIZE_0 (0x00580644) +#define CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_0 (0x00580648) +#define CAL_DP_DMA_RD_DIM2_BLK_SIZE_0 (0x0058064C) +#define CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_0 (0x00580650) +#define CAL_DP_DMA_RD_DIM0_PAD_L_0 (0x00580654) +#define CAL_DP_DMA_RD_DIM0_PAD_R_0 (0x00580658) +#define CAL_DP_DMA_RD_PAD_TB_0 (0x0058065C) +#define CAL_DP_DMA_RD_DIM0_CROP_0 (0x00580660) +#define CAL_DP_DMA_RD_CLIENT_ADDR_CFG_0 (0x00580664) +#define CAL_DP_DMA_RD_CLIENT_BUFF_CFG_0 (0x00580668) +#define CAL_DP_DMA_RD_MMU_CFG_0 (0x0058066C) +#define CAL_DP_DMA_RD_PAD_VALUE_0 (0x00580670) +#define CAL_DP_DMA_RD_CFG_1 (0x00580800) +#define CAL_DP_DMA_RD_NUM_CMD_IT_1 (0x00580804) +#define CAL_DP_DMA_RD_START_ADDR_1 (0x00580808) +#define CAL_DP_DMA_RD_START_ADDR1_1 (0x0058080C) +#define CAL_DP_DMA_RD_END_ADDR0_1 (0x00580810) +#define CAL_DP_DMA_RD_MAX_ADDR_1 (0x00580814) +#define CAL_DP_DMA_RD_MAX_ADDR1_1 (0x00580818) +#define CAL_DP_DMA_RD_STRIDE_DIM0_1 (0x0058081C) +#define CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_1 (0x00580820) +#define CAL_DP_DMA_RD_STRIDE_DIM1_1 (0x00580824) +#define CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_1 (0x00580828) +#define CAL_DP_DMA_RD_STRIDE_DIM2_1 (0x0058082C) +#define CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_1 (0x00580830) +#define CAL_DP_DMA_RD_ROW_INCR_1 (0x00580834) +#define CAL_DP_DMA_RD_DIM0_BLK_SIZE_1 (0x00580838) +#define CAL_DP_DMA_RD_DIM0_INCR_1 (0x0058083C) +#define CAL_DP_DMA_RD_DIM0_NUM_BLK_1 (0x00580840) +#define CAL_DP_DMA_RD_DIM1_BLK_SIZE_1 (0x00580844) +#define CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_1 (0x00580848) +#define CAL_DP_DMA_RD_DIM2_BLK_SIZE_1 (0x0058084C) +#define CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_1 (0x00580850) +#define CAL_DP_DMA_RD_DIM0_PAD_L_1 (0x00580854) +#define CAL_DP_DMA_RD_DIM0_PAD_R_1 (0x00580858) +#define CAL_DP_DMA_RD_PAD_TB_1 (0x0058085C) +#define CAL_DP_DMA_RD_DIM0_CROP_1 (0x00580860) +#define CAL_DP_DMA_RD_CLIENT_ADDR_CFG_1 (0x00580864) +#define CAL_DP_DMA_RD_CLIENT_BUFF_CFG_1 (0x00580868) +#define CAL_DP_DMA_RD_MMU_CFG_1 (0x0058086C) +#define CAL_DP_DMA_RD_PAD_VALUE_1 (0x00580870) +#define CAL_DP_DMA_RD_CFG_2 (0x00580A00) +#define CAL_DP_DMA_RD_NUM_CMD_IT_2 (0x00580A04) +#define CAL_DP_DMA_RD_START_ADDR_2 (0x00580A08) +#define CAL_DP_DMA_RD_START_ADDR1_2 (0x00580A0C) +#define CAL_DP_DMA_RD_END_ADDR0_2 (0x00580A10) +#define CAL_DP_DMA_RD_MAX_ADDR_2 (0x00580A14) +#define CAL_DP_DMA_RD_MAX_ADDR1_2 (0x00580A18) +#define CAL_DP_DMA_RD_STRIDE_DIM0_2 (0x00580A1C) +#define CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_2 (0x00580A20) +#define CAL_DP_DMA_RD_STRIDE_DIM1_2 (0x00580A24) +#define CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_2 (0x00580A28) +#define CAL_DP_DMA_RD_STRIDE_DIM2_2 (0x00580A2C) +#define CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_2 (0x00580A30) +#define CAL_DP_DMA_RD_ROW_INCR_2 (0x00580A34) +#define CAL_DP_DMA_RD_DIM0_BLK_SIZE_2 (0x00580A38) +#define CAL_DP_DMA_RD_DIM0_INCR_2 (0x00580A3C) +#define CAL_DP_DMA_RD_DIM0_NUM_BLK_2 (0x00580A40) +#define CAL_DP_DMA_RD_DIM1_BLK_SIZE_2 (0x00580A44) +#define CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_2 (0x00580A48) +#define CAL_DP_DMA_RD_DIM2_BLK_SIZE_2 (0x00580A4C) +#define CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_2 (0x00580A50) +#define CAL_DP_DMA_RD_DIM0_PAD_L_2 (0x00580A54) +#define CAL_DP_DMA_RD_DIM0_PAD_R_2 (0x00580A58) +#define CAL_DP_DMA_RD_PAD_TB_2 (0x00580A5C) +#define CAL_DP_DMA_RD_DIM0_CROP_2 (0x00580A60) +#define CAL_DP_DMA_RD_CLIENT_ADDR_CFG_2 (0x00580A64) +#define CAL_DP_DMA_RD_CLIENT_BUFF_CFG_2 (0x00580A68) +#define CAL_DP_DMA_RD_MMU_CFG_2 (0x00580A6C) +#define CAL_DP_DMA_RD_PAD_VALUE_2 (0x00580A70) +#define CAL_DP_DMA_RD_CFG_3 (0x00580C00) +#define CAL_DP_DMA_RD_NUM_CMD_IT_3 (0x00580C04) +#define CAL_DP_DMA_RD_START_ADDR_3 (0x00580C08) +#define CAL_DP_DMA_RD_START_ADDR1_3 (0x00580C0C) +#define CAL_DP_DMA_RD_END_ADDR0_3 (0x00580C10) +#define CAL_DP_DMA_RD_MAX_ADDR_3 (0x00580C14) +#define CAL_DP_DMA_RD_MAX_ADDR1_3 (0x00580C18) +#define CAL_DP_DMA_RD_STRIDE_DIM0_3 (0x00580C1C) +#define CAL_DP_DMA_RD_STRIDE_DIM0_FIRST_3 (0x00580C20) +#define CAL_DP_DMA_RD_STRIDE_DIM1_3 (0x00580C24) +#define CAL_DP_DMA_RD_STRIDE_DIM1_FIRST_3 (0x00580C28) +#define CAL_DP_DMA_RD_STRIDE_DIM2_3 (0x00580C2C) +#define CAL_DP_DMA_RD_STRIDE_DIM2_FIRST_3 (0x00580C30) +#define CAL_DP_DMA_RD_ROW_INCR_3 (0x00580C34) +#define CAL_DP_DMA_RD_DIM0_BLK_SIZE_3 (0x00580C38) +#define CAL_DP_DMA_RD_DIM0_INCR_3 (0x00580C3C) +#define CAL_DP_DMA_RD_DIM0_NUM_BLK_3 (0x00580C40) +#define CAL_DP_DMA_RD_DIM1_BLK_SIZE_3 (0x00580C44) +#define CAL_DP_DMA_RD_DIM1_STRIPE_SIZE_3 (0x00580C48) +#define CAL_DP_DMA_RD_DIM2_BLK_SIZE_3 (0x00580C4C) +#define CAL_DP_DMA_RD_DIM2_STRIPE_SIZE_3 (0x00580C50) +#define CAL_DP_DMA_RD_DIM0_PAD_L_3 (0x00580C54) +#define CAL_DP_DMA_RD_DIM0_PAD_R_3 (0x00580C58) +#define CAL_DP_DMA_RD_PAD_TB_3 (0x00580C5C) +#define CAL_DP_DMA_RD_DIM0_CROP_3 (0x00580C60) +#define CAL_DP_DMA_RD_CLIENT_ADDR_CFG_3 (0x00580C64) +#define CAL_DP_DMA_RD_CLIENT_BUFF_CFG_3 (0x00580C68) +#define CAL_DP_DMA_RD_MMU_CFG_3 (0x00580C6C) +#define CAL_DP_DMA_RD_PAD_VALUE_3 (0x00580C70) +#define CAL_DP_DMA_LOC_SRC_ADDR_0 (0x00580E00) +#define CAL_DP_DMA_LOC_DEST_ADDR_0 (0x00580E04) +#define CAL_DP_DMA_LOC_DATA_SIZE_0 (0x00580E08) +#define CAL_DP_DMA_WR_ERR_STATUS_0 (0x00581000) +#define CAL_DP_DMA_WR_MAX_P_CNT_0 (0x00581004) +#define CAL_DP_DMA_WR_STATUS_0_0 (0x00581008) +#define CAL_DP_DMA_WR_STATUS_0_1 (0x0058100C) +#define CAL_DP_DMA_WR_STATUS_0_2 (0x00581010) +#define CAL_DP_DMA_WR_STATUS_0_3 (0x00581014) +#define CAL_DP_DMA_RD_ERR_STATUS_0 (0x00581200) +#define CAL_DP_DMA_RD_MAX_P_CNT_0 (0x00581204) +#define CAL_DP_DMA_RD_STATUS_0_0 (0x00581208) +#define CAL_DP_DMA_RD_STATUS_0_1 (0x0058120C) +#define CAL_DP_DMA_RD_STATUS_0_2 (0x00581210) +#define CAL_DP_DMA_RD_STATUS_0_3 (0x00581214) +#define CAL_DP_DMA_RD_STATUS_0_4 (0x00581218) +#define CAL_DP_DMA_RD_ERR_STATUS_1 (0x00581400) +#define CAL_DP_DMA_RD_MAX_P_CNT_1 (0x00581404) +#define CAL_DP_DMA_RD_STATUS_1_0 (0x00581408) +#define CAL_DP_DMA_RD_STATUS_1_1 (0x0058140C) +#define CAL_DP_DMA_RD_STATUS_1_2 (0x00581410) +#define CAL_DP_DMA_RD_STATUS_1_3 (0x00581414) +#define CAL_DP_DMA_RD_STATUS_1_4 (0x00581418) +#define CAL_DP_DMA_RD_ERR_STATUS_2 (0x00581600) +#define CAL_DP_DMA_RD_MAX_P_CNT_2 (0x00581604) +#define CAL_DP_DMA_RD_STATUS_2_0 (0x00581608) +#define CAL_DP_DMA_RD_STATUS_2_1 (0x0058160C) +#define CAL_DP_DMA_RD_STATUS_2_2 (0x00581610) +#define CAL_DP_DMA_RD_STATUS_2_3 (0x00581614) +#define CAL_DP_DMA_RD_STATUS_2_4 (0x00581618) +#define CAL_DP_DMA_RD_ERR_STATUS_3 (0x00581800) +#define CAL_DP_DMA_RD_MAX_P_CNT_3 (0x00581804) +#define CAL_DP_DMA_RD_STATUS_3_0 (0x00581808) +#define CAL_DP_DMA_RD_STATUS_3_1 (0x0058180C) +#define CAL_DP_DMA_RD_STATUS_3_2 (0x00581810) +#define CAL_DP_DMA_RD_STATUS_3_3 (0x00581814) +#define CAL_DP_DMA_LOC_ERR_STATUS_0 (0x00581A00) +#define CAL_DP_DMA_LOC_STATUS_0_0 (0x00581A04) +#define CAL_DP_DMA_LOC_STATUS_0_1 (0x00581A08) +#define CAL_DP_DMA_LOC_STATUS_0_2 (0x00581A0C) +#define CAL_DP_DMA_LOC_STATUS_0_3 (0x00581A10) +#define CAL_DP_CALDMA_ADAPT_STATUS_0_0 (0x00581C00) +#define CAL_DP_CALDMA_ADAPT_STATUS_0_1 (0x00581C04) +#define CAL_DP_SDMA_WR_STATUS_0_0 (0x00581E00) +#define CAL_DP_SDMA_WR_STATUS_0_1 (0x00581E04) +#define CAL_DP_SDMA_WR_STATUS_0_2 (0x00581E08) +#define CAL_DP_SDMA_WR_STATUS_0_3 (0x00581E0C) +#define CAL_DP_SDMA_WR_STATUS_0_4 (0x00581E10) +#define CAL_DP_SDMA_WR_STATUS_0_5 (0x00581E14) +#define CAL_DP_SDMA_WR_STATUS_0_6 (0x00581E18) +#define CAL_DP_SDMA_WR_STATUS_0_7 (0x00581E1C) +#define CAL_DP_SDMA_WR_STATUS_0_8 (0x00581E20) +#define CAL_DP_SDMA_RD_STATUS_0_0 (0x00581E24) +#define CAL_DP_SDMA_RD_STATUS_0_1 (0x00581E28) +#define CAL_DP_SDMA_RD_STATUS_0_2 (0x00581E2C) +#define CAL_DP_SDMA_RD_STATUS_0_3 (0x00581E30) +#define CAL_DP_SDMA_RD_STATUS_0_4 (0x00581E34) +#define CAL_DP_SDMA_RD_STATUS_0_5 (0x00581E38) +#define CAL_DP_SDMA_RD_STATUS_0_6 (0x00581E3C) +#define CAL_DP_SDMA_RD_STATUS_0_7 (0x00581E40) +#define CAL_DP_SDMA_RD_STATUS_0_8 (0x00581E44) +#define CAL_DP_SDMA_RD_STATUS_0_9 (0x00581E48) +#define CAL_DP_SDMA_RD_STATUS_0_10 (0x00581E4C) +#define CAL_DP_TCM_SET_CMD (0x005A0000) +#define CAL_DP_TCM_RESET_CMD (0x005A0004) +#define CAL_DP_DTSWC_INT_CLR (0x005A0008) +#define CAL_DP_TCM_INT_CLR (0x005A000C) +#define CAL_DP_DTSWC_INT_SET (0x005A0010) +#define CAL_DP_TCM_INT_SET (0x005A0014) +#define CAL_DP_WD_RSS_CMD (0x005A0018) +#define CAL_DP_RESET_CMD (0x005A001C) +#define CAL_DP_PERF_CNT_CMD (0x005A0020) +#define CAL_DP_DBUF_TRANSFER_CMD (0x005A0024) +#define CAL_DP_CAL_EN_CTRL (0x005B0000) +#define CAL_DP_EN_INT_CTRL (0x005B0004) +#define CAL_DP_EN_TCM_INT_CTRL (0x005B0008) +#define CAL_DP_TCM_VAL_CTRL (0x005B000C) +#define CAL_DP_WD_COUNT_LO (0x005B0010) +#define CAL_DP_WD_COUNT_HI (0x005B0014) +#define CAL_DP_RSS_SEL_CTRL (0x005B0018) +#define CAL_DP_CAL_CFG_W0 (0x005B001C) +#define CAL_DP_CAL_CFG_W1 (0x005B0020) +#define CAL_DP_CAL_CFG_W2 (0x005B0024) +#define CAL_DP_CAL_CFG_W3 (0x005B0028) +#define CAL_DP_PERF_CNT_START_SEL (0x005B002C) +#define CAL_DP_PERF_CNT_STOP_SEL (0x005B0030) +#define CAL_DP_PERF_CNT_EVENT_SEL (0x005B0034) +#define CAL_DP_DBUF_RD_SEL (0x005B0038) +#define CAL_DP_LM_CTRL (0x005B003C) +#define CAL_DP_LM_LOOK_AHEAD (0x005B0040) +#define CAL_DP_LM_CUB_TIMER (0x005B0044) +#define CAL_DP_SIGB_STATUS (0x005C0000) +#define CAL_DP_CAL_EN_WD_RSS_STATUS (0x005C0004) +#define CAL_DP_EN_TCM_FLAGS_STATUS (0x005C0008) +#define CAL_DP_VERSION_STATUS (0x005C000C) +#define CAL_DP_CFG_STATUS (0x005C0010) +#define CAL_DP_CUB_SAT_DTCT_STATUS (0x005C0014) +#define CAL_DP_AHB_ERR_ADDR_STATUS (0x005C0018) +#define CAL_DP_WD_STATUS_LO (0x005C001C) +#define CAL_DP_WD_STATUS_HI (0x005C0020) +#define CAL_DP_DTSWC_INT_STATUS (0x005C0024) +#define CAL_DP_DTSWC_TCM_INT_STATUS (0x005C0028) +#define CAL_DP_DTSWC_UM_INT_STATUS (0x005C002C) +#define CAL_DP_DTSWC_TCM_UM_INT_STATUS (0x005C0030) +#define CAL_DP_RSS_STATUS (0x005C0034) +#define CAL_DP_RSS_STATUS_1 (0x005C0038) +#define CAL_DP_RSS_STATUS_2 (0x005C003C) +#define CAL_DP_RSS_STATUS_3 (0x005C0040) +#define CAL_DP_RSS_STATUS_4 (0x005C0044) +#define CAL_DP_RSS_STATUS_5 (0x005C0048) +#define CAL_DP_RSS_STATUS_6 (0x005C004C) +#define CAL_DP_RSS_STATUS_7 (0x005C0050) +#define CAL_DP_PERF_CNT0 (0x005C0054) +#define CAL_DP_PERF_CNT1 (0x005C0058) +#define CAL_DP_PERF_CNT2 (0x005C005C) +#define CAL_DP_PERF_CNT3 (0x005C0060) +#define CAL_DP_FINAL_MIN (0x005C0064) +#define CAL_DP_FINAL_MAX (0x005C0068) +#define CAL_DP_LM_STATUS (0x005C006C) + +#endif /* NPU_HW_H */ diff --git a/drivers/media/platform/msm/npu/npu_hw_access.c b/drivers/media/platform/msm/npu/npu_hw_access.c new file mode 100644 index 0000000000000000000000000000000000000000..37b152f9c558dd48ea4394f8c35884f6aed00ce1 --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_hw_access.c @@ -0,0 +1,369 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include +#include +#include +#include + +#include "npu_hw_access.h" +#include "npu_common.h" +#include "npu_hw.h" + +/* ------------------------------------------------------------------------- + * Functions - Register + * ------------------------------------------------------------------------- + */ +uint32_t npu_reg_read(struct npu_device *npu_dev, uint32_t off) +{ + uint32_t ret = 0; + + ret = readl_relaxed(npu_dev->npu_base + off); + __iormb(); + return ret; +} + +void npu_reg_write(struct npu_device *npu_dev, uint32_t off, uint32_t val) +{ + writel_relaxed(val, npu_dev->npu_base + off); + __iowmb(); +} + +/* ------------------------------------------------------------------------- + * Functions - Memory + * ------------------------------------------------------------------------- + */ +void npu_mem_write(struct npu_device *npu_dev, void *dst, void *src, + uint32_t size) +{ + size_t dst_off = (size_t)dst; + uint32_t *src_ptr32 = (uint32_t *)src; + uint8_t *src_ptr8 = 0; + uint32_t i = 0; + uint32_t num = 0; + + num = size/4; + for (i = 0; i < num; i++) { + writel_relaxed(src_ptr32[i], npu_dev->npu_base + dst_off); + dst_off += 4; + } + + if (size%4 != 0) { + src_ptr8 = (uint8_t *)((size_t)src + (num*4)); + num = size%4; + for (i = 0; i < num; i++) { + writeb_relaxed(src_ptr8[i], npu_dev->npu_base + + dst_off); + dst_off += 1; + } + } +} + +int32_t npu_mem_read(struct npu_device *npu_dev, void *src, void *dst, + uint32_t size) +{ + size_t src_off = (size_t)src; + uint32_t *out32 = (uint32_t *)dst; + uint8_t *out8 = 0; + uint32_t i = 0; + uint32_t num = 0; + + num = size/4; + for (i = 0; i < num; i++) { + out32[i] = readl_relaxed(npu_dev->npu_base + src_off); + src_off += 4; + } + + if (size%4 != 0) { + out8 = (uint8_t *)((size_t)dst + (num*4)); + num = size%4; + for (i = 0; i < num; i++) { + out8[i] = readb_relaxed(npu_dev->npu_base + src_off); + src_off += 1; + } + } + return 0; +} + +void *npu_ipc_addr(void) +{ + return (void *)(IPC_MEM_OFFSET_FROM_SSTCM); +} + +/* ------------------------------------------------------------------------- + * Functions - Interrupt + * ------------------------------------------------------------------------- + */ +void npu_interrupt_ack(struct npu_device *npu_dev, uint32_t intr_num) +{ + /* Clear irq state */ + REGW(npu_dev, NPU_MASTERn_IPC_IRQ_OUT(0), 0x0); +} + +int32_t npu_interrupt_raise_m0(struct npu_device *npu_dev) +{ + int ret = 0; + + /* Bit 4 is setting IRQ_SOURCE_SELECT to local + * and we're triggering a pulse to NPU_MASTER0_IPC_IN_IRQ0 + */ + npu_reg_write(npu_dev, NPU_MASTERn_IPC_IRQ_IN_CTRL(0), 0x1 + << NPU_MASTER0_IPC_IRQ_IN_CTRL__IRQ_SOURCE_SELECT___S | 0x1); + + return ret; +} + +/* ------------------------------------------------------------------------- + * Functions - ION Memory + * ------------------------------------------------------------------------- + */ +static struct npu_ion_buf *npu_get_npu_ion_buffer(struct npu_device + *npu_dev) +{ + struct npu_ion_buf *ret_val = 0; + + ret_val = kmalloc(sizeof(struct npu_ion_buf), GFP_KERNEL); + if (ret_val) + list_add(&(ret_val->list), &(npu_dev->mapped_buffers.list)); + + return ret_val; +} + +static struct npu_ion_buf *npu_get_existing_ion_buffer(struct npu_device + *npu_dev, int buf_hdl) +{ + struct list_head *pos = 0; + struct npu_ion_buf *npu_ion_buf = 0; + + list_for_each(pos, &(npu_dev->mapped_buffers.list)) { + npu_ion_buf = list_entry(pos, struct npu_ion_buf, list); + if (npu_ion_buf->fd == buf_hdl) + return npu_ion_buf; + } + + return NULL; +} + +static struct npu_ion_buf *npu_clear_npu_ion_buffer(struct npu_device + *npu_dev, int buf_hdl, uint64_t addr) +{ + struct list_head *pos = 0; + struct npu_ion_buf *npu_ion_buf = 0; + + list_for_each(pos, &(npu_dev->mapped_buffers.list)) { + npu_ion_buf = list_entry(pos, struct npu_ion_buf, list); + if (npu_ion_buf->fd == buf_hdl && + npu_ion_buf->iova == addr) { + list_del(&npu_ion_buf->list); + return npu_ion_buf; + } + } + + return NULL; +} + +int npu_mem_map(struct npu_device *npu_dev, int buf_hdl, uint32_t size, + uint64_t *addr) +{ + int ret = 0; + + struct npu_ion_buf *ion_buf = npu_get_npu_ion_buffer(npu_dev); + struct npu_smmu_ctx *smmu_ctx = &npu_dev->smmu_ctx; + + if (!ion_buf) { + pr_err("%s no more table space\n", __func__); + ret = -ENOMEM; + return ret; + } + ion_buf->fd = buf_hdl; + ion_buf->size = size; + + if (ion_buf->fd == 0) + return -EINVAL; + + smmu_ctx->attach_cnt++; + + ion_buf->dma_buf = dma_buf_get(ion_buf->fd); + if (IS_ERR_OR_NULL(ion_buf->dma_buf)) { + pr_err("dma_buf_get failed %d\n", ion_buf->fd); + ret = -ENOMEM; + ion_buf->dma_buf = NULL; + goto map_end; + } + + ion_buf->attachment = dma_buf_attach(ion_buf->dma_buf, + &(npu_dev->pdev->dev)); + if (IS_ERR(ion_buf->attachment)) { + ret = -ENOMEM; + ion_buf->attachment = NULL; + goto map_end; + } + + ion_buf->attachment->dma_map_attrs = DMA_ATTR_IOMMU_USE_UPSTREAM_HINT; + + ion_buf->table = dma_buf_map_attachment(ion_buf->attachment, + DMA_BIDIRECTIONAL); + if (IS_ERR(ion_buf->table)) { + pr_err("npu dma_buf_map_attachment failed\n"); + ret = -ENOMEM; + ion_buf->table = NULL; + goto map_end; + } + + dma_sync_sg_for_device(&(npu_dev->pdev->dev), ion_buf->table->sgl, + ion_buf->table->nents, DMA_BIDIRECTIONAL); + ion_buf->iova = ion_buf->table->sgl->dma_address; + ion_buf->size = ion_buf->table->sgl->dma_length; +map_end: + if (ret) + npu_mem_unmap(npu_dev, buf_hdl, 0); + + *addr = ion_buf->iova; + return ret; +} + +void npu_mem_invalidate(struct npu_device *npu_dev, int buf_hdl) +{ + struct npu_ion_buf *ion_buf = npu_get_existing_ion_buffer(npu_dev, + buf_hdl); + + if (!ion_buf) + pr_err("%s cant find ion buf\n", __func__); + else + dma_sync_sg_for_cpu(&(npu_dev->pdev->dev), ion_buf->table->sgl, + ion_buf->table->nents, DMA_BIDIRECTIONAL); +} + +void npu_mem_unmap(struct npu_device *npu_dev, int buf_hdl, uint64_t addr) +{ + struct npu_ion_buf *ion_buf = 0; + + /* clear entry and retrieve the corresponding buffer */ + ion_buf = npu_clear_npu_ion_buffer(npu_dev, buf_hdl, addr); + + if (!ion_buf) { + pr_err("%s could not find buffer\n", __func__); + return; + } + if (ion_buf->table) + dma_buf_unmap_attachment(ion_buf->attachment, ion_buf->table, + DMA_BIDIRECTIONAL); + ion_buf->table = 0; + if (ion_buf->dma_buf && ion_buf->attachment) + dma_buf_detach(ion_buf->dma_buf, ion_buf->attachment); + ion_buf->attachment = 0; + if (ion_buf->dma_buf) + dma_buf_put(ion_buf->dma_buf); + ion_buf->dma_buf = 0; + npu_dev->smmu_ctx.attach_cnt--; + kfree(ion_buf); +} + +/* ------------------------------------------------------------------------- + * Functions - Work Queue + * ------------------------------------------------------------------------- + */ +void npu_destroy_wq(struct workqueue_struct *wq) +{ + destroy_workqueue(wq); +} + +struct workqueue_struct *npu_create_wq(struct npu_host_ctx *host_ctx, + const char *name, wq_hdlr_fn hdlr, struct work_struct *irq_work) +{ + struct workqueue_struct *wq = create_workqueue(name); + + INIT_WORK(irq_work, hdlr); + + return wq; +} + +/* ------------------------------------------------------------------------- + * Functions - Features + * ------------------------------------------------------------------------- + */ +uint8_t npu_hw_clk_gating_enabled(void) +{ + return 1; +} + +uint8_t npu_hw_log_enabled(void) +{ + return 1; +} + +/* ------------------------------------------------------------------------- + * Functions - Subsystem/PIL + * ------------------------------------------------------------------------- + */ +void *subsystem_get_local(char *sub_system) +{ + return subsystem_get(sub_system); +} + +void subsystem_put_local(void *sub_system_handle) +{ + return subsystem_put(sub_system_handle); +} + +/* ------------------------------------------------------------------------- + * Functions - Log + * ------------------------------------------------------------------------- + */ +void npu_process_log_message(struct npu_device *npu_dev, uint32_t *message, + uint32_t size) +{ + struct npu_debugfs_ctx *debugfs = &npu_dev->debugfs_ctx; + + /* mutex log lock */ + mutex_lock(&debugfs->log_lock); + + if ((debugfs->log_num_bytes_buffered + size) > + debugfs->log_buf_size) { + /* No more space, invalidate it all and start over */ + debugfs->log_read_index = 0; + debugfs->log_write_index = size; + debugfs->log_num_bytes_buffered = size; + memcpy(debugfs->log_buf, message, size); + } else { + if ((debugfs->log_write_index + size) > + debugfs->log_buf_size) { + /* Wrap around case */ + uint8_t *src_addr = (uint8_t *)message; + uint8_t *dst_addr = 0; + uint32_t remaining_to_end = debugfs->log_buf_size - + debugfs->log_write_index + 1; + dst_addr = debugfs->log_buf + debugfs->log_write_index; + memcpy(dst_addr, src_addr, remaining_to_end); + src_addr = &(src_addr[remaining_to_end]); + dst_addr = debugfs->log_buf; + memcpy(dst_addr, src_addr, size-remaining_to_end); + debugfs->log_write_index = size-remaining_to_end; + } else { + memcpy((debugfs->log_buf + debugfs->log_write_index), + message, size); + debugfs->log_write_index += size; + if (debugfs->log_write_index == debugfs->log_buf_size) + debugfs->log_write_index = 0; + } + debugfs->log_num_bytes_buffered += size; + } + + /* mutex log unlock */ + mutex_unlock(&debugfs->log_lock); +} diff --git a/drivers/media/platform/msm/npu/npu_hw_access.h b/drivers/media/platform/msm/npu/npu_hw_access.h new file mode 100644 index 0000000000000000000000000000000000000000..8f3eb332139ad1eae9ab0648338ac9cab5c608b9 --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_hw_access.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2018, 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 _NPU_HW_ACCESS_H +#define _NPU_HW_ACCESS_H + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include "npu_common.h" + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +#define IPC_MEM_OFFSET_FROM_SSTCM 0x00010000 +#define SYS_CACHE_SCID 23 + +#define REGW(npu_dev, off, val) npu_reg_write(npu_dev, off, val) +#define REGR(npu_dev, off) npu_reg_read(npu_dev, off) +#define MEMW(npu_dev, dst, src, size) npu_mem_write(npu_dev, (void *)(dst),\ + (void *)(src), size) +#define MEMR(npu_dev, src, dst, size) npu_mem_read(npu_dev, (void *)(src),\ + (void *)(dst), size) +#define IPC_ADDR npu_ipc_addr() +#define INTERRUPT_ACK(npu_dev, num) npu_interrupt_ack(npu_dev, num) +#define INTERRUPT_RAISE_NPU(npu_dev) npu_interrupt_raise_m0(npu_dev) + +/* ------------------------------------------------------------------------- + * Data Structures + * ------------------------------------------------------------------------- + */ +struct npu_device; +struct npu_ion_buf_t; +struct npu_host_ctx; +typedef irqreturn_t (*intr_hdlr_fn)(int32_t irq, void *ptr); +typedef void (*wq_hdlr_fn) (struct work_struct *work); + +/* ------------------------------------------------------------------------- + * Function Prototypes + * ------------------------------------------------------------------------- + */ +uint32_t npu_reg_read(struct npu_device *npu_dev, uint32_t off); +void npu_reg_write(struct npu_device *npu_dev, uint32_t off, uint32_t val); +void npu_mem_write(struct npu_device *npu_dev, void *dst, void *src, + uint32_t size); +int32_t npu_mem_read(struct npu_device *npu_dev, void *src, void *dst, + uint32_t size); + +int npu_mem_map(struct npu_device *npu_dev, int buf_hdl, uint32_t size, + uint64_t *addr); +void npu_mem_unmap(struct npu_device *npu_dev, int buf_hdl, uint64_t addr); +void npu_mem_invalidate(struct npu_device *npu_dev, int buf_hdl); + +void *npu_ipc_addr(void); +void npu_interrupt_ack(struct npu_device *npu_dev, uint32_t intr_num); +int32_t npu_interrupt_raise_m0(struct npu_device *npu_dev); + +struct workqueue_struct *npu_create_wq(struct npu_host_ctx *host_ctx, + const char *name, wq_hdlr_fn hdlr, struct work_struct *irq_work); +void npu_destroy_wq(struct workqueue_struct *wq); + +uint8_t npu_hw_clk_gating_enabled(void); +uint8_t npu_hw_log_enabled(void); + +int npu_enable_irq(struct npu_device *npu_dev); +void npu_disable_irq(struct npu_device *npu_dev); + +int npu_enable_sys_cache(struct npu_device *npu_dev); +void npu_disable_sys_cache(struct npu_device *npu_dev); + +void *subsystem_get_local(char *sub_system); +void subsystem_put_local(void *sub_system_handle); + +void npu_process_log_message(struct npu_device *npu_dev, uint32_t *msg, + uint32_t size); + +#endif /* _NPU_HW_ACCESS_H*/ diff --git a/drivers/media/platform/msm/npu/npu_mgr.c b/drivers/media/platform/msm/npu/npu_mgr.c new file mode 100644 index 0000000000000000000000000000000000000000..640f60f23be12e6baf166b4db5adb8692c7b934f --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_mgr.c @@ -0,0 +1,665 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include "npu_hw_access.h" +#include "npu_mgr.h" +#include "npu_firmware.h" +#include "npu_hw.h" +#include "npu_host_ipc.h" +#include "npu_common.h" + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +#define LOG_MSG_HEADER_SIZE 20 +#define LOG_MSG_START_MSG_INDEX 5 +#define LOG_MSG_TOTAL_SIZE_INDEX 0 +#define LOG_MSG_MSG_ID_INDEX 1 + +#define NPU_FW_TIMEOUT_POLL_INTERVAL_MS 20 +#define NPU_FW_TIMEOUT_MS 1000 + +/* ------------------------------------------------------------------------- + * File Scope Function Prototypes + * ------------------------------------------------------------------------- + */ +static void host_irq_wq(struct work_struct *work); +static void turn_off_fw_logging(struct npu_device *npu_dev); +static int wait_for_fw_ready(struct npu_device *npu_dev); +static struct npu_network *alloc_network(struct npu_host_ctx *ctx); +static struct npu_network *get_network(struct npu_host_ctx *ctx, int64_t id); +static void free_network(struct npu_host_ctx *ctx, int64_t id); +static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg); +static void log_msg_proc(struct npu_device *npu_dev, uint32_t *msg); +static void host_session_msg_hdlr(struct npu_device *npu_dev); +static void host_session_log_hdlr(struct npu_device *npu_dev); + +/* ------------------------------------------------------------------------- + * Function Definitions - Init / Deinit + * ------------------------------------------------------------------------- + */ +int fw_init(struct npu_device *npu_dev) +{ + uint32_t reg_val = 0; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + + if (host_ctx->fw_enabled) + return 0; + + if (npu_enable_core_power(npu_dev)) + return -EPERM; + + if (npu_enable_sys_cache(npu_dev)) + return -EPERM; + + /* Boot the NPU subsystem */ + host_ctx->subsystem_handle = subsystem_get_local("npu"); + + /* Clear control/status registers */ + REGW(npu_dev, REG_NPU_FW_CTRL_STATUS, 0x0); + REGW(npu_dev, REG_NPU_HOST_CTRL_STATUS, 0x0); + REGW(npu_dev, REG_NPU_HOST_CTRL_VALUE, 0x0); + REGW(npu_dev, REG_FW_TO_HOST_EVENT, 0x0); + + /* Post PIL clocks */ + if (npu_enable_post_pil_clocks(npu_dev)) + return -EPERM; + + /* + * Set logging state and clock gating state + * during FW bootup initialization + */ + reg_val = REGR(npu_dev, REG_NPU_HOST_CTRL_STATUS); + + /* Enable clock gating only if the HW access platform allows it */ + if (npu_hw_clk_gating_enabled()) + reg_val |= HOST_CTRL_STATUS_BOOT_ENABLE_CLK_GATE_VAL; + + REGW(npu_dev, REG_NPU_HOST_CTRL_STATUS, reg_val); + + /* Initialize the host side IPC */ + npu_host_ipc_pre_init(npu_dev); + + /* Keep reading ctrl status until NPU is ready */ + pr_debug("waiting for status ready from fw\n"); + + if (wait_for_fw_ready(npu_dev)) + return -EPERM; + + host_ctx->fw_enabled = 1; + + npu_host_ipc_post_init(npu_dev); + + if (npu_enable_irq(npu_dev)) + return -EPERM; + + /* Set logging state */ + if (!npu_hw_log_enabled()) { + pr_debug("fw logging disabled\n"); + turn_off_fw_logging(npu_dev); + } + + pr_debug("firmware init complete\n"); + return 0; +} + +void fw_deinit(struct npu_device *npu_dev) +{ + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + struct ipc_cmd_shutdown_pkt cmd_shutdown_pkt; + int ret = 0; + + if (!host_ctx->fw_enabled) + return; + + /* Command header */ + cmd_shutdown_pkt.header.cmd_type = NPU_IPC_CMD_SHUTDOWN; + cmd_shutdown_pkt.header.size = sizeof(struct ipc_cmd_shutdown_pkt); + cmd_shutdown_pkt.header.trans_id = 1; + cmd_shutdown_pkt.header.flags = 0xF; + ret = npu_host_ipc_send_cmd(npu_dev, IPC_QUEUE_CMD_HIGH_PRIORITY, + &cmd_shutdown_pkt); + + pr_debug("NPU_IPC_CMD_SHUTDOWN sent status: %d\n", ret); + + if (ret) + pr_err("npu_host_ipc_send_cmd failed\n"); + + npu_disable_irq(npu_dev); + npu_disable_sys_cache(npu_dev); + subsystem_put_local(host_ctx->subsystem_handle); + host_ctx->fw_enabled = 0; + npu_disable_core_power(npu_dev); + + pr_debug("firmware deinit complete\n"); +} + +int npu_host_init(struct npu_device *npu_dev) +{ + int sts = 0; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + + init_completion(&host_ctx->exec_done); + init_completion(&host_ctx->load_done); + init_completion(&host_ctx->unload_done); + + host_ctx->sys_cache_disable = 0; + + host_ctx->wq = npu_create_wq(host_ctx, "irq_hdl", host_irq_wq, + &host_ctx->irq_work); + if (!host_ctx->wq) + sts = -EPERM; + + return sts; +} + +void npu_host_deinit(struct npu_device *npu_dev) +{ + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + + npu_destroy_wq(host_ctx->wq); +} + +/* ------------------------------------------------------------------------- + * Function Definitions - Interrupt Handler + * ------------------------------------------------------------------------- + */ +irqreturn_t npu_intr_hdler(int irq, void *ptr) +{ + /* Check the interrupt we received */ + /* Currently this is the IPC interrupt */ + struct npu_device *npu_dev = (struct npu_device *)ptr; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + + INTERRUPT_ACK(npu_dev, irq); + + /* Check that the event thread currently is running */ + if (host_ctx->wq != 0) + queue_work(host_ctx->wq, &host_ctx->irq_work); + + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------------------- + * Function Definitions - Control + * ------------------------------------------------------------------------- + */ +static void host_irq_wq(struct work_struct *work) +{ + struct npu_host_ctx *host_ctx; + struct npu_device *npu_dev; + + host_ctx = container_of(work, struct npu_host_ctx, irq_work); + npu_dev = container_of(host_ctx, struct npu_device, host_ctx); + host_session_log_hdlr(npu_dev); + host_session_msg_hdlr(npu_dev); +} + +static void turn_off_fw_logging(struct npu_device *npu_dev) +{ + struct ipc_cmd_log_state_pkt log_packet; + int ret = 0; + + log_packet.header.cmd_type = NPU_IPC_CMD_CONFIG_LOG; + log_packet.header.size = sizeof(struct ipc_cmd_log_state_pkt); + log_packet.header.trans_id = 1; + log_packet.header.flags = 0xF; + log_packet.log_state.module_msk = 0; + log_packet.log_state.level_msk = 0; + ret = npu_host_ipc_send_cmd(npu_dev, IPC_QUEUE_CMD_HIGH_PRIORITY, + &log_packet); + + pr_debug("NPU_IPC_CMD_CONFIG_LOG sent status: %d\n", ret); + + if (ret) + pr_err("npu_host_ipc_send_cmd failed\n"); +} + +static int wait_for_fw_ready(struct npu_device *npu_dev) +{ + uint32_t ctrl_sts = 0; + uint32_t wait_cnt = 0; + + /* keep reading ctrl status until NPU is ready */ + while (!(ctrl_sts & FW_CTRL_STATUS_MAIN_THREAD_READY_VAL)) { + ctrl_sts = REGR(npu_dev, REG_NPU_FW_CTRL_STATUS); + msleep(NPU_FW_TIMEOUT_POLL_INTERVAL_MS); + wait_cnt += NPU_FW_TIMEOUT_POLL_INTERVAL_MS; + if (wait_cnt >= NPU_FW_TIMEOUT_MS) { + pr_err("timeout in %s\n", __func__); + return -EPERM; + } + } + pr_debug("status ready from fw received\n"); + return 0; +} + +/* ------------------------------------------------------------------------- + * Function Definitions - Network Management + * ------------------------------------------------------------------------- + */ +static struct npu_network *alloc_network(struct npu_host_ctx *ctx) +{ + int32_t i; + struct npu_network *network = ctx->networks; + + for (i = 0; i < MAX_LOADED_NETWORK; i++) { + if (network->id == 0) { + network->id = i + 1; + /* + * -IPC trans ID to start at 1 and increment + * by 1 for the next IPC cmd on the same network + */ + network->ipc_trans_id = 1; + break; + } + network++; + } + if (i >= MAX_LOADED_NETWORK) + return NULL; + ctx->network_num++; + return network; +} + +static struct npu_network *get_network(struct npu_host_ctx *ctx, int64_t id) +{ + if (id >= 1 && id <= MAX_LOADED_NETWORK) + return &ctx->networks[id - 1]; + pr_err("network id invalid %d\n", (int32_t)id); + return NULL; +} + +static void free_network(struct npu_host_ctx *ctx, int64_t id) +{ + struct npu_network *network = get_network(ctx, id); + + if (network) { + memset(network, 0, sizeof(struct npu_network)); + ctx->network_num--; + } +} + +/* ------------------------------------------------------------------------- + * Function Definitions - IPC + * ------------------------------------------------------------------------- + */ +static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) +{ + uint32_t msg_id; + struct ipc_msg_header_pkt *resp_pkt; + struct ipc_msg_load_pkt *load_rsp_pkt; + struct ipc_msg_execute_pkt *exe_rsp_pkt; + + msg_id = msg[1]; + switch (msg_id) { + case NPU_IPC_MSG_EXECUTE_DONE: + exe_rsp_pkt = (struct ipc_msg_execute_pkt *)msg; + + pr_debug("NPU_IPC_MSG_EXECUTE_DONE status: %d\n", + exe_rsp_pkt->header.status); + pr_debug("trans_id : %d", exe_rsp_pkt->header.trans_id); + pr_debug("e2e_IPC_time: %d (in tick count)\n", + exe_rsp_pkt->stats.e2e_ipc_tick_count); + pr_debug("aco_load_time: %d (in tick count)\n", + exe_rsp_pkt->stats.aco_load_tick_count); + pr_debug("aco_execute_time: %d (in tick count)\n", + exe_rsp_pkt->stats.aco_execution_tick_count); + pr_debug("total_num_layers: %d\n", + exe_rsp_pkt->stats.exe_stats.total_num_layers); + complete_all(&host_ctx->exec_done); + break; + case NPU_IPC_MSG_LOAD_DONE: + { + struct npu_network *network = 0; + uint32_t network_id = 0; + + load_rsp_pkt = (struct ipc_msg_load_pkt *)msg; + pr_debug("NPU_IPC_MSG_LOAD_DONE status: %d, trans_id: %d\n", + load_rsp_pkt->header.status, + load_rsp_pkt->header.trans_id); + + /* + * store the returned aco_hdl generated by the firmware + * response header flags filed was TEMP used to store + * the network ID on the way back + */ + network_id = load_rsp_pkt->header.flags; + network = get_network(host_ctx, network_id); + if (!network) { + pr_err("can't find network %d\n", network_id); + break; + } + network->network_hdl = load_rsp_pkt->aco_hdl; + complete_all(&host_ctx->load_done); + break; + } + + case NPU_IPC_MSG_UNLOAD_DONE: + resp_pkt = (struct ipc_msg_header_pkt *)msg; + pr_debug("NPU_IPC_MSG_UNLOAD_DONE status: %d, trans_id: %d\n", + resp_pkt->status, resp_pkt->trans_id); + complete_all(&host_ctx->unload_done); + break; + default: + pr_err("Not supported apps response received %d\n", + msg_id); + break; + } +} + +static void host_session_msg_hdlr(struct npu_device *npu_dev) +{ + uint32_t *msg; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + + msg = kzalloc(sizeof(uint32_t) * NPU_IPC_BUF_LENGTH, GFP_KERNEL); + if (!msg) + return; + while (npu_host_ipc_read_msg(npu_dev, IPC_QUEUE_APPS_RSP, msg) == 0) { + pr_debug("received from msg queue\n"); + app_msg_proc(host_ctx, msg); + } + kfree(msg); +} + +static void log_msg_proc(struct npu_device *npu_dev, uint32_t *msg) +{ + uint32_t msg_id; + uint32_t *log_msg; + uint32_t size; + + msg_id = msg[LOG_MSG_MSG_ID_INDEX]; + size = msg[LOG_MSG_TOTAL_SIZE_INDEX] - LOG_MSG_HEADER_SIZE; + + switch (msg_id) { + case NPU_IPC_MSG_EVENT_NOTIFY: + /* Process the message */ + log_msg = &(msg[LOG_MSG_START_MSG_INDEX]); + npu_process_log_message(npu_dev, log_msg, size); + break; + default: + pr_err("unsupported log response received %d\n", msg_id); + break; + } +} + +static void host_session_log_hdlr(struct npu_device *npu_dev) +{ + uint32_t *msg; + + msg = kzalloc(sizeof(uint32_t) * NPU_IPC_BUF_LENGTH, GFP_KERNEL); + + if (!msg) + return; + while (npu_host_ipc_read_msg(npu_dev, IPC_QUEUE_LOG, msg) == 0) { + pr_debug("received from log queue\n"); + log_msg_proc(npu_dev, msg); + } + kfree(msg); +} + +/* ------------------------------------------------------------------------- + * Function Definitions - Functionality + * ------------------------------------------------------------------------- + */ +int32_t npu_host_get_info(struct npu_device *npu_dev, + struct msm_npu_get_info_ioctl *get_info_ioctl) +{ + get_info_ioctl->firmware_version = FIRMWARE_VERSION; + return 0; +} + +int32_t npu_host_map_buf(struct npu_device *npu_dev, + struct msm_npu_map_buf_ioctl *map_ioctl) +{ + npu_mem_map(npu_dev, map_ioctl->buf_ion_hdl, map_ioctl->size, + &map_ioctl->npu_phys_addr); + return 0; +} + +int32_t npu_host_unmap_buf(struct npu_device *npu_dev, + struct msm_npu_unmap_buf_ioctl *unmap_ioctl) +{ + npu_mem_unmap(npu_dev, unmap_ioctl->buf_ion_hdl, + unmap_ioctl->npu_phys_addr); + return 0; +} + +void host_copy_patch_data(struct npu_patch_tuple *param, uint32_t value, + struct msm_npu_layer *layer_info) +{ + param->value = value; + param->chunk_id = layer_info->patch_info.chunk_id; + param->loc_offset = layer_info->patch_info.loc_offset; + param->instruction_size_in_bytes = + layer_info->patch_info.instruction_size_in_bytes; + param->shift_value_in_bits = + layer_info->patch_info.shift_value_in_bits; + param->variable_size_in_bits = + layer_info->patch_info.variable_size_in_bits; +} + +static uint32_t find_networks_perf_mode(struct npu_host_ctx *host_ctx) +{ + struct npu_network *network; + uint32_t max_perf_mode = 0; + int i = 0; + + network = host_ctx->networks; + + /* find the max level among all the networks */ + for (i = 0; i < host_ctx->network_num; i++) { + if ((network->perf_mode != 0) && + (network->perf_mode > max_perf_mode)) + max_perf_mode = network->perf_mode; + network++; + } + pr_debug("max perf mode for networks: %d\n", max_perf_mode); + + return max_perf_mode; +} + +int32_t npu_host_load_network(struct npu_device *npu_dev, + struct msm_npu_load_network_ioctl *load_ioctl) +{ + int ret = 0; + struct npu_network *network; + struct ipc_cmd_load_pkt load_packet; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + uint32_t networks_perf_mode = 0; + + ret = fw_init(npu_dev); + if (ret) + return ret; + + network = alloc_network(host_ctx); + if (!network) + return -ENOMEM; + + network->buf_hdl = load_ioctl->buf_ion_hdl; + network->size = load_ioctl->buf_size; + network->phy_add = load_ioctl->buf_phys_addr; + network->first_block_size = load_ioctl->first_block_size; + network->priority = load_ioctl->priority; + network->perf_mode = load_ioctl->perf_mode; + load_ioctl->network_hdl = network->id; + + networks_perf_mode = find_networks_perf_mode(host_ctx); + + ret = npu_set_uc_power_level(npu_dev, networks_perf_mode); + if (ret) { + pr_err("network load failed due to power level set\n"); + goto error_free_network; + } + + load_packet.header.cmd_type = NPU_IPC_CMD_LOAD; + load_packet.header.size = sizeof(struct ipc_cmd_load_pkt); + load_packet.header.trans_id = network->ipc_trans_id++; + load_packet.header.flags = 0; + + /* ACO Buffer. Use the npu mapped aco address */ + load_packet.buf_pkt.address = (uint64_t)network->phy_add; + load_packet.buf_pkt.buf_size = network->first_block_size; + load_packet.buf_pkt.network_id = network->id; + + /* NPU_IPC_CMD_LOAD will go onto IPC_QUEUE_APPS_EXEC */ + reinit_completion(&host_ctx->load_done); + ret = npu_host_ipc_send_cmd(npu_dev, + IPC_QUEUE_APPS_EXEC, &load_packet); + + pr_debug("NPU_IPC_CMD_LOAD sent status: %d\n", ret); + + if (ret) + return -EIO; + + if (!wait_for_completion_interruptible_timeout( + &host_ctx->load_done, NW_LOAD_TIMEOUT)) { + pr_err_ratelimited("npu: NPU_IPC_CMD_LOAD time out\n"); + ret = -ETIMEDOUT; + goto error_free_network; + } + + return ret; + +error_free_network: + free_network(host_ctx, network->id); + return ret; +} + +int32_t npu_host_unload_network(struct npu_device *npu_dev, + struct msm_npu_unload_network_ioctl *unload) +{ + int ret = 0; + struct ipc_cmd_unload_pkt unload_packet; + struct npu_network *network; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + + /* get the corresponding network for ipc trans id purpose */ + network = get_network(host_ctx, (int64_t)unload->network_hdl); + if (!network) + return -EINVAL; + + /* prepare IPC packet for UNLOAD */ + unload_packet.header.cmd_type = NPU_IPC_CMD_UNLOAD; + unload_packet.header.size = sizeof(struct ipc_cmd_unload_pkt); + unload_packet.header.trans_id = network->ipc_trans_id++; + unload_packet.header.flags = 0; + unload_packet.aco_hdl = (uint32_t)network->network_hdl; + + /* NPU_IPC_CMD_UNLOAD will go onto IPC_QUEUE_APPS_EXEC */ + reinit_completion(&host_ctx->unload_done); + ret = npu_host_ipc_send_cmd(npu_dev, IPC_QUEUE_APPS_EXEC, + &unload_packet); + + pr_debug("NPU_IPC_CMD_UNLOAD sent status: %d\n", ret); + + if (ret) + return -EIO; + + if (!wait_for_completion_interruptible_timeout(&host_ctx->unload_done, + NW_UNLOAD_TIMEOUT)) { + pr_err_ratelimited("npu: NPU_IPC_CMD_UNLOAD time out\n"); + ret = -ETIMEDOUT; + } else { + /* + * free the network on the kernel if the corresponding ACO + * handle is unloaded on the firmware side + */ + free_network(host_ctx, (int64_t)unload->network_hdl); + if (host_ctx->network_num <= 0) { + fw_deinit(npu_dev); + host_ctx->network_num = 0; + } + } + + return ret; +} + +int32_t npu_host_exec_network(struct npu_device *npu_dev, + struct msm_npu_exec_network_ioctl *exec_ioctl) +{ + struct ipc_cmd_execute_pkt exec_packet; + /* npu mapped addr */ + uint64_t input_addr = 0, output_addr = 0; + uint64_t input_off, output_off; + int32_t ret; + struct npu_network *network; + uint32_t timeout = NW_SMALL_EXEC_TIMEOUT; + struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; + int i = 0; + + network = get_network(host_ctx, (int64_t)exec_ioctl->network_hdl); + + if (!network) + return -EINVAL; + + memset(&exec_packet, 0, sizeof(exec_packet)); + if (exec_ioctl->patching_required) { + if (exec_ioctl->input_layer_num == 1) + input_addr = exec_ioctl->input_layers[0].buf_phys_addr; + if (exec_ioctl->output_layer_num == 1) + output_addr = + exec_ioctl->output_layers[0].buf_phys_addr; + exec_packet.patch_params.num_params = 2; + input_off = (uint64_t)input_addr; + output_off = (uint64_t)output_addr; + host_copy_patch_data(&exec_packet.patch_params.param[0], + (uint32_t)input_off, &exec_ioctl->input_layers[0]); + host_copy_patch_data(&exec_packet.patch_params.param[1], + (uint32_t)output_off, &exec_ioctl->output_layers[0]); + } else { + exec_packet.patch_params.num_params = 0; + } + + exec_packet.header.cmd_type = NPU_IPC_CMD_EXECUTE; + exec_packet.header.size = sizeof(struct ipc_cmd_execute_pkt); + exec_packet.header.trans_id = network->ipc_trans_id++; + exec_packet.header.flags = 0xF; + exec_packet.aco_hdl = network->network_hdl; + + /* Send it on the high priority queue */ + reinit_completion(&host_ctx->exec_done); + ret = npu_host_ipc_send_cmd(npu_dev, IPC_QUEUE_APPS_EXEC, &exec_packet); + + pr_debug("NPU_IPC_CMD_EXECUTE sent status: %d\n", ret); + + /* + * If this is a large network set the excecution timeout accordingly + */ + if (network->size > LARGE_NETWORK_SIZE_THRESHOLD) + timeout = NW_LARGE_EXEC_TIMEOUT; + + if (!wait_for_completion_interruptible_timeout( + &host_ctx->exec_done, timeout)) { + pr_err_ratelimited("npu: NPU_IPC_CMD_EXECUTE time out\n"); + /* dump debug stats */ + npu_dump_debug_timeout_stats(npu_dev); + ret = -ETIMEDOUT; + } + + /* Invalidate output buffers */ + for (i = 0; i < exec_ioctl->output_layer_num; i++) { + if (exec_ioctl->output_layer_num == 1) { + npu_mem_invalidate(npu_dev, + exec_ioctl->output_layers[i].buf_hdl); + } + } + + return ret; +} diff --git a/drivers/media/platform/msm/npu/npu_mgr.h b/drivers/media/platform/msm/npu/npu_mgr.h new file mode 100644 index 0000000000000000000000000000000000000000..b5103a6635f4130073f668d7209b34a5f9aea26e --- /dev/null +++ b/drivers/media/platform/msm/npu/npu_mgr.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2018, 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 _NPU_MGR_H +#define _NPU_MGR_H + +/* ------------------------------------------------------------------------- + * Includes + * ------------------------------------------------------------------------- + */ +#include "npu_hw_access.h" + +/* ------------------------------------------------------------------------- + * Defines + * ------------------------------------------------------------------------- + */ +#define NW_SMALL_EXEC_TIMEOUT_MS (1000*300) /* set for 5 min */ +#define NW_SMALL_EXEC_TIMEOUT msecs_to_jiffies(NW_SMALL_EXEC_TIMEOUT_MS) +#define NW_LARGE_EXEC_TIMEOUT_MS (1000*300*12) /* set for 60 min */ +#define NW_LARGE_EXEC_TIMEOUT msecs_to_jiffies(NW_LARGE_EXEC_TIMEOUT_MS) +#define NW_LOAD_TIMEOUT_MS (1000*300) /* set for 5 min */ +#define NW_LOAD_TIMEOUT msecs_to_jiffies(NW_LOAD_TIMEOUT_MS) +#define NW_UNLOAD_TIMEOUT_MS (1000*300) /* set for 5 min */ +#define NW_UNLOAD_TIMEOUT msecs_to_jiffies(NW_UNLOAD_TIMEOUT_MS) +#define FIRMWARE_VERSION 0x00001000 +#define MAX_LOADED_NETWORK 32 +#define LARGE_NETWORK_SIZE_THRESHOLD (5*1024) /* 5 KB */ +#define NPU_IPC_BUF_LENGTH 512 + +/* ------------------------------------------------------------------------- + * Data Structures + * ------------------------------------------------------------------------- + */ +struct npu_network { + uint64_t id; + int buf_hdl; + uint64_t phy_add; + uint32_t size; + uint32_t first_block_size; + uint32_t network_hdl; + uint32_t ipc_trans_id; + uint32_t priority; + uint32_t perf_mode; +}; + +struct npu_host_ctx { + void *subsystem_handle; + bool fw_enabled; + bool power_enabled; + int32_t power_vote_num; + struct work_struct irq_work; + struct workqueue_struct *wq; + struct completion exec_done; + struct completion load_done; + struct completion unload_done; + int32_t network_num; + struct npu_network networks[MAX_LOADED_NETWORK]; + bool sys_cache_disable; +}; + +struct npu_device; + +/* ------------------------------------------------------------------------- + * Function Prototypes + * ------------------------------------------------------------------------- + */ +int npu_host_init(struct npu_device *npu_dev); +void npu_host_deinit(struct npu_device *npu_dev); + +/* Host Driver IPC Interface */ +int npu_host_ipc_pre_init(struct npu_device *npu_dev); +int npu_host_ipc_post_init(struct npu_device *npu_dev); +void npu_host_ipc_deinit(struct npu_device *npu_dev); +int npu_host_ipc_send_cmd(struct npu_device *npu_dev, uint32_t queueIndex, + void *pCmd); +int npu_host_ipc_read_msg(struct npu_device *npu_dev, uint32_t queueIndex, + uint32_t *pMsg); + +int32_t npu_host_get_info(struct npu_device *npu_dev, + struct msm_npu_get_info_ioctl *get_info_ioctl); +int32_t npu_host_map_buf(struct npu_device *npu_dev, + struct msm_npu_map_buf_ioctl *map_ioctl); +int32_t npu_host_unmap_buf(struct npu_device *npu_dev, + struct msm_npu_unmap_buf_ioctl *unmap_ioctl); +int32_t npu_host_load_network(struct npu_device *npu_dev, + struct msm_npu_load_network_ioctl *load_ioctl); +int32_t npu_host_unload_network(struct npu_device *npu_dev, + struct msm_npu_unload_network_ioctl *unload); +int32_t npu_host_exec_network(struct npu_device *npu_dev, + struct msm_npu_exec_network_ioctl *exec_ioctl); + +void npu_dump_debug_timeout_stats(struct npu_device *npu_dev); +void npu_dump_cal_state(struct npu_device *npu_dev); + +#endif /* _NPU_MGR_H */ diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c index 0d1b82e24dfa41643a72260c8f976408655c2545..5fc3fc04e5a7d47bdef1cdb5d1f419c4f2f2e9bc 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c @@ -217,12 +217,18 @@ u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd) SDEROT_DBG("w:%d h:%d fps:%d pixfmt:%8.8x yuv:%d res:%llu rd:%d\n", width, height, fps, pixfmt, is_yuv, res, is_rd); + if (!is_yuv) + goto exit; + + /* + * If (total_source_pixels <= 62208000 && YUV) -> RD/WROT=2 //1080p30 + * If (total_source_pixels <= 124416000 && YUV) -> RD/WROT=4 //1080p60 + * If (total_source_pixels <= 2160p && YUV && FPS <= 30) -> RD/WROT = 32 + */ if (res <= (RES_1080p * 30)) ot_lim = 2; else if (res <= (RES_1080p * 60)) ot_lim = 4; - else if (res <= (RES_UHD * 30)) - ot_lim = 8; exit: SDEROT_DBG("ot_lim=%d\n", ot_lim); @@ -252,6 +258,8 @@ static u32 get_ot_limit(u32 reg_off, u32 bit_off, val &= (0xFF << bit_off); val = val >> bit_off; + SDEROT_EVTLOG(val, ot_lim); + if (val == ot_lim) ot_lim = 0; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c index 5d9b267375bd50140f4acaa5d57547e6f728d6ce..585582deeee961bb258c9d478b6ebef3c8c94c87 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c @@ -3400,7 +3400,7 @@ int sde_rotator_resume(struct platform_device *dev) */ int sde_rotator_session_open(struct sde_rot_mgr *mgr, struct sde_rot_file_private **pprivate, int session_id, - struct sde_rot_queue *queue) + struct sde_rot_queue_v1 *queue) { int ret; struct sde_rot_file_private *private; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h index 8421873a929743cf74abb84a782571d4580eae88..f6fdb9072503c835b03cc933c9d867fd6344e44c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -242,6 +242,12 @@ struct sde_rot_queue { struct sde_rot_hw_resource *hw; }; +struct sde_rot_queue_v1 { + struct kthread_worker *rot_kw; + struct task_struct *rot_thread; + struct sde_rot_timeline *timeline; + struct sde_rot_hw_resource *hw; +}; /* * struct sde_rot_entry_container - rotation request * @list: list of active requests managed by rotator manager @@ -294,7 +300,7 @@ struct sde_rot_entry { struct kthread_work commit_work; struct kthread_work done_work; struct sde_rot_queue *commitq; - struct sde_rot_queue *fenceq; + struct sde_rot_queue_v1 *fenceq; struct sde_rot_queue *doneq; struct sde_rot_entry_container *request; @@ -351,7 +357,7 @@ struct sde_rot_file_private { struct list_head req_list; struct list_head perf_list; struct sde_rot_mgr *mgr; - struct sde_rot_queue *fenceq; + struct sde_rot_queue_v1 *fenceq; }; /* @@ -586,7 +592,7 @@ void sde_rotator_core_dump(struct sde_rot_mgr *mgr); */ int sde_rotator_session_open(struct sde_rot_mgr *mgr, struct sde_rot_file_private **pprivate, int session_id, - struct sde_rot_queue *queue); + struct sde_rot_queue_v1 *queue); /* * sde_rotator_session_close - close the given rotator per file session diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index 916f978ffdd14298bb87d676df7783caa9612f1f..f902135e1fdfabb2dd84e43c4ff2d78be64c5c11 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -568,6 +568,11 @@ static ssize_t sde_rot_evtlog_dump_read(struct file *file, char __user *buff, if (__sde_rot_evtlog_dump_calc_range()) { len = sde_rot_evtlog_dump_entry(evtlog_buf, SDE_ROT_EVTLOG_BUF_MAX); + if (len < 0 || len > count) { + pr_err("len is more than the user buffer size\n"); + return 0; + } + if (copy_to_user(buff, evtlog_buf, len)) return -EFAULT; *ppos += len; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c index 9606c5c562ab1f6611c7677a550df12ec097db38..7d8f5fc9caeb1c4c90ded1ccd63c39c794e41db8 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -925,6 +925,7 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( ctx->rotate = 0; ctx->secure = 0; ctx->abort_pending = 0; + ctx->kthread_id = -1; ctx->format_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ctx->format_cap.fmt.pix.pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2; ctx->format_cap.fmt.pix.width = 640; @@ -963,6 +964,7 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( ctx, sde_rotator_queue_init); if (IS_ERR_OR_NULL(ctx->fh.m2m_ctx)) { ret = PTR_ERR(ctx->fh.m2m_ctx); + ctx->fh.m2m_ctx = NULL; goto error_m2m_init; } } @@ -982,18 +984,22 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( goto error_create_sysfs; } - snprintf(name, sizeof(name), "rot_fenceq_%d_%d", rot_dev->dev->id, - ctx->session_id); - kthread_init_worker(&ctx->work_queue.rot_kw); - ctx->work_queue.rot_thread = kthread_run(kthread_worker_fn, - &ctx->work_queue.rot_kw, name); - if (IS_ERR(ctx->work_queue.rot_thread)) { - SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate kthread\n"); - ret = -EPERM; - ctx->work_queue.rot_thread = NULL; - goto error_alloc_workqueue; + for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) { + if (rot_dev->kthread_free[i]) { + rot_dev->kthread_free[i] = false; + ctx->kthread_id = i; + ctx->work_queue.rot_kw = &rot_dev->rot_kw[i]; + ctx->work_queue.rot_thread = rot_dev->rot_thread[i]; + break; + } + } + + if (ctx->kthread_id < 0) { + SDEDEV_ERR(ctx->rot_dev->dev, + "fail to acquire the kthread\n"); + ret = -EINVAL; + goto error_alloc_kthread; } - SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name); snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id, ctx->session_id); @@ -1052,13 +1058,17 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( error_open_session: sde_rot_mgr_unlock(rot_dev->mgr); sde_rotator_destroy_timeline(ctx->work_queue.timeline); - kthread_flush_worker(&ctx->work_queue.rot_kw); - kthread_stop(ctx->work_queue.rot_thread); -error_alloc_workqueue: + rot_dev->kthread_free[ctx->kthread_id] = true; + ctx->kthread_id = -1; +error_alloc_kthread: sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group); error_create_sysfs: kobject_put(&ctx->kobj); error_kobj_init: + if (ctx->file) { + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + ctx->fh.m2m_ctx = NULL; + } error_m2m_init: if (ctx->file) { v4l2_fh_del(&ctx->fh); @@ -1096,10 +1106,12 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx, if (ctx->file) { v4l2_ctrl_handler_free(&ctx->ctrl_handler); SDEDEV_DBG(rot_dev->dev, "release streams s:%d\n", session_id); - v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, + if (ctx->fh.m2m_ctx) { + v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, + v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + } } mutex_unlock(&rot_dev->lock); SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n", session_id); @@ -1129,14 +1141,19 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx, mutex_lock(&rot_dev->lock); SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id); sde_rotator_destroy_timeline(ctx->work_queue.timeline); - kthread_flush_worker(&ctx->work_queue.rot_kw); - kthread_stop(ctx->work_queue.rot_thread); + if (ctx->kthread_id >= 0 && ctx->work_queue.rot_kw) { + kthread_flush_worker(ctx->work_queue.rot_kw); + rot_dev->kthread_free[ctx->kthread_id] = true; + } sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group); kobject_put(&ctx->kobj); if (ctx->file) { - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); + if (ctx->fh.m2m_ctx) + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + if (ctx->fh.vdev) { + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + } } kfree(ctx->vbinfo_out); kfree(ctx->vbinfo_cap); @@ -1817,7 +1834,7 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd, } else { SDEROT_ERR("invalid stats timestamp\n"); } - req->retire_kw = &ctx->work_queue.rot_kw; + req->retire_kw = ctx->work_queue.rot_kw; req->retire_work = &request->retire_work; trace_rot_entry_fence( @@ -3170,7 +3187,7 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx, goto error_init_request; } - req->retire_kw = &ctx->work_queue.rot_kw; + req->retire_kw = ctx->work_queue.rot_kw; req->retire_work = &request->retire_work; ret = sde_rotator_handle_request_common( @@ -3468,7 +3485,7 @@ static int sde_rotator_job_ready(void *priv) list_del_init(&request->list); list_add_tail(&request->list, &ctx->pending_list); spin_unlock(&ctx->list_lock); - kthread_queue_work(&ctx->work_queue.rot_kw, + kthread_queue_work(ctx->work_queue.rot_kw, &request->submit_work); } } else if (request && !atomic_read(&request->req->pending_count)) { @@ -3522,7 +3539,8 @@ static int sde_rotator_probe(struct platform_device *pdev) { struct sde_rotator_device *rot_dev; struct video_device *vdev; - int ret; + int ret, i; + char name[32]; SDEDEV_DBG(&pdev->dev, "SDE v4l2 rotator probed\n"); @@ -3605,9 +3623,29 @@ static int sde_rotator_probe(struct platform_device *pdev) rot_dev->debugfs_root = sde_rotator_create_debugfs(rot_dev); + for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) { + snprintf(name, sizeof(name), "rot_fenceq_%d_%d", + rot_dev->dev->id, i); + kthread_init_worker(&rot_dev->rot_kw[i]); + rot_dev->rot_thread[i] = kthread_run(kthread_worker_fn, + &rot_dev->rot_kw[i], name); + if (IS_ERR(rot_dev->rot_thread[i])) { + SDEDEV_ERR(rot_dev->dev, + "fail allocate kthread i:%d\n", i); + ret = -EPERM; + goto error_kthread_create; + } + rot_dev->kthread_free[i] = true; + } + SDEDEV_INFO(&pdev->dev, "SDE v4l2 rotator probe success\n"); return 0; +error_kthread_create: + for (i--; i >= 0; i--) + kthread_stop(rot_dev->rot_thread[i]); + sde_rotator_destroy_debugfs(rot_dev->debugfs_root); + video_unregister_device(rot_dev->vdev); error_video_register: video_device_release(vdev); error_alloc_video_device: @@ -3630,6 +3668,7 @@ static int sde_rotator_probe(struct platform_device *pdev) static int sde_rotator_remove(struct platform_device *pdev) { struct sde_rotator_device *rot_dev; + int i; rot_dev = platform_get_drvdata(pdev); if (rot_dev == NULL) { @@ -3638,6 +3677,8 @@ static int sde_rotator_remove(struct platform_device *pdev) } sde_rotator_pm_qos_remove(rot_dev->mdata); + for (i = MAX_ROT_OPEN_SESSION - 1; i >= 0; i--) + kthread_stop(rot_dev->rot_thread[i]); sde_rotator_destroy_debugfs(rot_dev->debugfs_root); video_unregister_device(rot_dev->vdev); video_device_release(rot_dev->vdev); diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h index 2bf439a04f51ad4201a723268d4d070d3ed7897c..ed3b7af0dee063c4022bb5825872a0fcf2fafa45 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -41,6 +41,8 @@ /* maximum number of outstanding requests per ctx session */ #define SDE_ROTATOR_REQUEST_MAX 2 +#define MAX_ROT_OPEN_SESSION 16 + struct sde_rotator_device; struct sde_rotator_ctx; @@ -137,6 +139,7 @@ struct sde_rotator_request { * @retired_list: list of retired/free request * @requests: static allocation of free requests * @rotcfg: current core rotation configuration + * @kthread_id: thread_id used for fence management */ struct sde_rotator_ctx { struct kobject kobj; @@ -161,7 +164,7 @@ struct sde_rotator_ctx { struct sde_rotator_vbinfo *vbinfo_cap; struct sde_rotator_vbinfo *vbinfo_out; wait_queue_head_t wait_queue; - struct sde_rot_queue work_queue; + struct sde_rot_queue_v1 work_queue; struct sde_rot_file_private *private; struct llcc_slice_desc *slice; u32 commit_sequence_id; @@ -171,6 +174,8 @@ struct sde_rotator_ctx { struct list_head retired_list; struct sde_rotator_request requests[SDE_ROTATOR_REQUEST_MAX]; struct sde_rotation_config rotcfg; + + int kthread_id; }; /* @@ -209,6 +214,9 @@ struct sde_rotator_statistics { * @open_timeout: maximum wait time for ctx open in msec * @open_wq: wait queue for ctx open * @excl_ctx: Pointer to exclusive ctx + * @rot_kw: rotator thread work + * @rot_thread: rotator threads + * @kthread_free: check if thread is available or not */ struct sde_rotator_device { struct mutex lock; @@ -234,6 +242,10 @@ struct sde_rotator_device { u32 open_timeout; wait_queue_head_t open_wq; struct sde_rotator_ctx *excl_ctx; + + struct kthread_worker rot_kw[MAX_ROT_OPEN_SESSION]; + struct task_struct *rot_thread[MAX_ROT_OPEN_SESSION]; + bool kthread_free[MAX_ROT_OPEN_SESSION]; }; static inline diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index 2ff3432033ae3585a48d679a435dc8988985ff88..d9f3fdc012cd850208565c74ff2f9bb9ec5b4061 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -2297,8 +2297,10 @@ void sde_hw_rotator_pre_pmevent(struct sde_rot_mgr *mgr, bool pmon) */ if (!pmon && mgr && mgr->hw_data) { rot = mgr->hw_data; - h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]); - l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]); + h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]) & + SDE_REGDMA_SWTS_MASK; + l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]) & + SDE_REGDMA_SWTS_MASK; /* Need to turn on clock to access rotator register */ sde_rotator_clk_ctrl(mgr, true); @@ -2373,8 +2375,10 @@ void sde_hw_rotator_post_pmevent(struct sde_rot_mgr *mgr, bool pmon) SDEROT_DBG("h_ts:0x%x, l_ts;0x%x\n", h_ts, l_ts); SDEROT_EVTLOG(h_ts, l_ts); rot->reset_hw_ts = true; - rot->last_hwts[ROT_QUEUE_LOW_PRIORITY] = l_ts; - rot->last_hwts[ROT_QUEUE_HIGH_PRIORITY] = h_ts; + rot->last_hwts[ROT_QUEUE_LOW_PRIORITY] = + l_ts & SDE_REGDMA_SWTS_MASK; + rot->last_hwts[ROT_QUEUE_HIGH_PRIORITY] = + h_ts & SDE_REGDMA_SWTS_MASK; } } @@ -2678,10 +2682,13 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw, l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]); SDEROT_EVTLOG(0xbad0, rststs, l_hwts, h_hwts, l_ts, h_ts); - if (ctx->q_id == ROT_QUEUE_HIGH_PRIORITY) + if (ctx->q_id == ROT_QUEUE_HIGH_PRIORITY) { h_ts = (h_ts - 1) & SDE_REGDMA_SWTS_MASK; - else + l_ts &= SDE_REGDMA_SWTS_MASK; + } else { l_ts = (l_ts - 1) & SDE_REGDMA_SWTS_MASK; + h_ts &= SDE_REGDMA_SWTS_MASK; + } SDEROT_DBG("h_ts:0x%x, l_ts;0x%x\n", h_ts, l_ts); SDEROT_EVTLOG(0x900d, h_ts, l_ts); diff --git a/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c index cdc0adce714fc86db183f0267cdb23535c6842bd..5dbed452206c91b3bf2f23fc4c4329d4be5a8d87 100644 --- a/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c +++ b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c @@ -351,34 +351,35 @@ static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d, * measured heuristics and hardcoded numbers taken from the firmware. */ /* Decoder parameters */ - int width, height, lcu_size, dpb_bpp, opb_bpp, fps, opb_factor; - bool unified_dpb_opb, dpb_compression_enabled, opb_compression_enabled, + int width, height, lcu_size, fps, dpb_bpp; + bool unified_dpb_opb, dpb_compression_enabled, + opb_compression_enabled = false, llc_ref_read_l2_cache_enabled = false, - llc_vpss_ds_line_buf_enabled = false; - fp_t dpb_opb_scaling_ratio, dpb_read_compression_factor, - dpb_write_compression_factor, opb_compression_factor, - qsmmu_bw_overhead_factor, height_ratio; + llc_top_line_buf_enabled = false; + fp_t dpb_read_compression_factor, dpb_opb_scaling_ratio, + dpb_write_compression_factor, opb_write_compression_factor, + qsmmu_bw_overhead_factor; /* Derived parameters */ - int lcu_per_frame, tnbr_per_lcu, colocated_bytes_per_lcu; + int lcu_per_frame, collocated_bytes_per_lcu, tnbr_per_lcu; unsigned long bitrate; - fp_t bins_to_bit_factor, dpb_write_factor, ten_bpc_packing_factor, - ten_bpc_bpp_factor, vsp_read_factor, vsp_write_factor, - bw_for_1x_8bpc, dpb_bw_for_1x, - motion_vector_complexity = 0, row_cache_penalty = 0, opb_bw = 0, - dpb_total = 0; + fp_t bins_to_bit_factor, vsp_read_factor, vsp_write_factor, + dpb_factor, dpb_write_factor, + y_bw_no_ubwc_8bpp, y_bw_no_ubwc_10bpp, + motion_vector_complexity = 0; + fp_t dpb_total = 0; /* Output parameters */ struct { fp_t vsp_read, vsp_write, collocated_read, collocated_write, - line_buffer_read, line_buffer_write, recon_read, - recon_write, opb_read, opb_write, dpb_read, dpb_write, + dpb_read, dpb_write, opb_read, opb_write, + line_buffer_read, line_buffer_write, total; } ddr = {0}; struct { - fp_t dpb_read, opb_read, total; + fp_t dpb_read, line_buffer_read, line_buffer_write, total; } llc = {0}; unsigned long ret = 0; @@ -387,31 +388,27 @@ static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d, width = max(d->input_width, BASELINE_DIMENSIONS.width); height = max(d->input_height, BASELINE_DIMENSIONS.height); + fps = d->fps; + lcu_size = d->lcu_size; dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX; - opb_bpp = d->num_formats >= 2 ? __bpp(d->color_formats[1]) : dpb_bpp; - - fps = d->fps; unified_dpb_opb = d->num_formats == 1; dpb_opb_scaling_ratio = fp_div(FP_INT(d->input_width * d->input_height), FP_INT(d->output_width * d->output_height)); - height_ratio = fp_div(d->input_height, d->output_height); - dpb_compression_enabled = d->num_formats >= 1 && - __ubwc(d->color_formats[0]); opb_compression_enabled = d->num_formats >= 2 && __ubwc(d->color_formats[1]); /* - * Convert Q16 number into Integer and Fractional part upto 2 places. - * Ex : 105752 / 65536 = 1.61; 1.61 in Q16 = 105752; - * Integer part = 105752 / 65536 = 1; - * Reminder = 105752 - 1 * 65536 = 40216; - * Fractional part = 40216 * 100 / 65536 = 61; - * Now converto to FP(1, 61, 100) for below code. + * convert q16 number into integer and fractional part upto 2 places. + * ex : 105752 / 65536 = 1.61; 1.61 in q16 = 105752; + * integer part = 105752 / 65536 = 1; + * reminder = 105752 - 1 * 65536 = 40216; + * fractional part = 40216 * 100 / 65536 = 61; + * now converto to fp(1, 61, 100) for below code. */ integer_part = d->compression_ratio >> 16; @@ -427,19 +424,18 @@ static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d, motion_vector_complexity = FP(integer_part, frac_part, 100); dpb_write_compression_factor = !dpb_compression_enabled ? FP_ONE : - __compression_ratio(__lut(width, height, fps), opb_bpp); + __compression_ratio(__lut(width, height, fps), dpb_bpp); dpb_write_compression_factor = d->use_dpb_read ? dpb_read_compression_factor : dpb_write_compression_factor; + opb_write_compression_factor = opb_compression_enabled ? + dpb_write_compression_factor : FP_ONE; - opb_compression_factor = !opb_compression_enabled ? FP_ONE : - __compression_ratio(__lut(width, height, fps), opb_bpp); - - llc_ref_read_l2_cache_enabled = llc_vpss_ds_line_buf_enabled = false; if (d->use_sys_cache) { llc_ref_read_l2_cache_enabled = true; - llc_vpss_ds_line_buf_enabled = true; + if (d->codec == HAL_VIDEO_CODEC_H264) + llc_top_line_buf_enabled = true; } /* Derived parameters setup */ @@ -450,142 +446,136 @@ static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d, bins_to_bit_factor = d->work_mode == VIDC_WORK_MODE_1 ? FP_INT(0) : FP_INT(4); - + vsp_write_factor = bins_to_bit_factor; vsp_read_factor = bins_to_bit_factor + FP_INT(2); - dpb_write_factor = FP(1, 5, 100); - - ten_bpc_packing_factor = FP(1, 67, 1000); - ten_bpc_bpp_factor = FP(1, 1, 4); + collocated_bytes_per_lcu = lcu_size == 16 ? 16 : + lcu_size == 32 ? 64 : 256; - vsp_write_factor = bins_to_bit_factor; + dpb_factor = FP(1, 50, 100); + dpb_write_factor = FP(1, 5, 100); tnbr_per_lcu = lcu_size == 16 ? 128 : lcu_size == 32 ? 64 : 128; - colocated_bytes_per_lcu = lcu_size == 16 ? 16 : - lcu_size == 32 ? 64 : 256; - - /* ........................................ for DDR */ + /* .... For DDR & LLC ...... */ ddr.vsp_read = fp_div(fp_mult(FP_INT(bitrate), vsp_read_factor), FP_INT(8)); ddr.vsp_write = fp_div(fp_mult(FP_INT(bitrate), vsp_write_factor), FP_INT(8)); ddr.collocated_read = FP_INT(lcu_per_frame * - colocated_bytes_per_lcu * fps / bps(1)); - ddr.collocated_write = FP_INT(lcu_per_frame * - colocated_bytes_per_lcu * fps / bps(1)); - - ddr.line_buffer_read = FP_INT(tnbr_per_lcu * - lcu_per_frame * fps / bps(1)); - ddr.line_buffer_write = ddr.line_buffer_read; - - bw_for_1x_8bpc = fp_div(FP_INT(width * height), FP_INT(32 * 8)); - - bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc, - fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000))); + collocated_bytes_per_lcu * fps / bps(1)); + ddr.collocated_write = ddr.collocated_read; - dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc : - fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor, - ten_bpc_bpp_factor)); + y_bw_no_ubwc_8bpp = fp_div(fp_div(fp_mult( + FP_INT((int)(width * height)), FP_INT((int)(256 * fps))), + FP_INT(32 * 8)), FP_INT(1000 * 1000)); + y_bw_no_ubwc_10bpp = fp_div(fp_div(fp_mult( + FP_INT((int)(width * height)), FP_INT((int)(256 * fps))), + FP_INT(48 * 4)), FP_INT(1000 * 1000)); - ddr.dpb_read = fp_div(fp_mult(fp_mult(dpb_bw_for_1x, - motion_vector_complexity), dpb_write_factor), + ddr.dpb_read = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.dpb_read = fp_div(fp_mult(ddr.dpb_read, + fp_mult(dpb_factor, motion_vector_complexity)), dpb_read_compression_factor); - ddr.dpb_write = fp_div(fp_mult(dpb_bw_for_1x, dpb_write_factor), - dpb_write_compression_factor); + ddr.dpb_write = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.dpb_write = fp_div(fp_mult(ddr.dpb_write, + fp_mult(dpb_factor, dpb_write_factor)), + dpb_write_compression_factor); + dpb_total = ddr.dpb_read + ddr.dpb_write; + if (llc_ref_read_l2_cache_enabled) { - row_cache_penalty = FP(1, 30, 100); - ddr.dpb_read = fp_div(ddr.dpb_read, row_cache_penalty); + ddr.dpb_read = fp_div(ddr.dpb_read, + d->codec == HAL_VIDEO_CODEC_H264 ? FP(1, 15, 100) : + FP(1, 30, 100)); llc.dpb_read = dpb_total - ddr.dpb_read; } - opb_factor = dpb_bpp == 8 ? 8 : 4; - - ddr.opb_read = unified_dpb_opb ? 0 : opb_compression_enabled ? - fp_div(fp_mult(fp_div(dpb_bw_for_1x, dpb_opb_scaling_ratio), - FP_INT(opb_factor)), height_ratio) : 0; - ddr.opb_write = unified_dpb_opb ? 0 : opb_compression_enabled ? - ddr.dpb_read : fp_div(fp_div(fp_mult(dpb_bw_for_1x, - FP(1, 50, 100)), dpb_opb_scaling_ratio), - opb_compression_factor); + ddr.opb_read = FP_ZERO; + ddr.opb_write = unified_dpb_opb ? FP_ZERO : (dpb_bpp == 8 ? + y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp); + ddr.opb_write = fp_div(ddr.opb_write, + fp_mult(dpb_opb_scaling_ratio, opb_write_compression_factor)); - if (llc_vpss_ds_line_buf_enabled) { - llc.opb_read = ddr.opb_read; - ddr.opb_write -= ddr.opb_read; - ddr.opb_read = 0; + ddr.line_buffer_read = FP_INT(tnbr_per_lcu * + lcu_per_frame * fps / bps(1)); + ddr.line_buffer_write = ddr.line_buffer_read; + if (llc_top_line_buf_enabled) { + llc.line_buffer_read = ddr.line_buffer_read; + llc.line_buffer_write = ddr.line_buffer_write; + ddr.line_buffer_write = ddr.line_buffer_read = FP_ZERO; } + ddr.total = ddr.vsp_read + ddr.vsp_write + ddr.collocated_read + ddr.collocated_write + + ddr.dpb_read + ddr.dpb_write + ddr.opb_read + ddr.opb_write + - ddr.dpb_read + ddr.dpb_write; + ddr.line_buffer_read + ddr.line_buffer_write; qsmmu_bw_overhead_factor = FP(1, 3, 100); ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); - llc.total = llc.dpb_read + llc.opb_read; + llc.total = llc.dpb_read + llc.line_buffer_read + llc.line_buffer_write; /* Dump all the variables for easier debugging */ if (debug) { struct dump dump[] = { {"DECODER PARAMETERS", "", DUMP_HEADER_MAGIC}, - {"LCU size", "%d", lcu_size}, - {"DPB bitdepth", "%d", dpb_bpp}, + {"lcu size", "%d", lcu_size}, + {"dpb bitdepth", "%d", dpb_bpp}, {"frame rate", "%d", fps}, - {"DPB/OPB unified", "%d", unified_dpb_opb}, - {"DPB/OPB downscaling ratio", DUMP_FP_FMT, + {"dpb/opb unified", "%d", unified_dpb_opb}, + {"dpb/opb downscaling ratio", DUMP_FP_FMT, dpb_opb_scaling_ratio}, - {"DPB compression", "%d", dpb_compression_enabled}, - {"OPB compression", "%d", opb_compression_enabled}, - {"DPB Read compression factor", DUMP_FP_FMT, + {"dpb compression", "%d", dpb_compression_enabled}, + {"opb compression", "%d", opb_compression_enabled}, + {"dpb read compression factor", DUMP_FP_FMT, dpb_read_compression_factor}, - {"DPB Write compression factor", DUMP_FP_FMT, + {"dpb write compression factor", DUMP_FP_FMT, dpb_write_compression_factor}, - {"OPB compression factor", DUMP_FP_FMT, - opb_compression_factor}, {"frame width", "%d", width}, {"frame height", "%d", height}, + {"llc ref read l2 cache enabled", "%d", + llc_ref_read_l2_cache_enabled}, + {"llc top line buf enabled", "%d", + llc_top_line_buf_enabled}, {"DERIVED PARAMETERS (1)", "", DUMP_HEADER_MAGIC}, - {"LCUs/frame", "%d", lcu_per_frame}, + {"lcus/frame", "%d", lcu_per_frame}, {"bitrate (Mbit/sec)", "%d", bitrate}, {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, - {"DPB write factor", DUMP_FP_FMT, dpb_write_factor}, - {"10bpc packing factor", DUMP_FP_FMT, - ten_bpc_packing_factor}, - {"10bpc,BPP factor", DUMP_FP_FMT, ten_bpc_bpp_factor}, - {"VSP read factor", DUMP_FP_FMT, vsp_read_factor}, - {"VSP write factor", DUMP_FP_FMT, vsp_write_factor}, - {"TNBR/LCU", "%d", tnbr_per_lcu}, - {"colocated bytes/LCU", "%d", colocated_bytes_per_lcu}, - {"B/W for 1x (NV12 8bpc)", DUMP_FP_FMT, bw_for_1x_8bpc}, - {"DPB B/W For 1x (NV12)", DUMP_FP_FMT, dpb_bw_for_1x}, + {"dpb write factor", DUMP_FP_FMT, dpb_write_factor}, + {"vsp read factor", DUMP_FP_FMT, vsp_read_factor}, + {"vsp write factor", DUMP_FP_FMT, vsp_write_factor}, + {"tnbr/lcu", "%d", tnbr_per_lcu}, + {"collocated bytes/LCU", "%d", collocated_bytes_per_lcu}, + {"bw for NV12 8bpc)", DUMP_FP_FMT, y_bw_no_ubwc_8bpp}, + {"bw for NV12 10bpc)", DUMP_FP_FMT, y_bw_no_ubwc_10bpp}, {"DERIVED PARAMETERS (2)", "", DUMP_HEADER_MAGIC}, - {"MV complexity", DUMP_FP_FMT, motion_vector_complexity}, - {"row cache penalty", DUMP_FP_FMT, row_cache_penalty}, + {"mv complexity", DUMP_FP_FMT, motion_vector_complexity}, {"qsmmu_bw_overhead_factor", DUMP_FP_FMT, qsmmu_bw_overhead_factor}, - {"OPB B/W (single instance)", DUMP_FP_FMT, opb_bw}, {"INTERMEDIATE DDR B/W", "", DUMP_HEADER_MAGIC}, - {"VSP read", DUMP_FP_FMT, ddr.vsp_read}, - {"VSP write", DUMP_FP_FMT, ddr.vsp_write}, + {"vsp read", DUMP_FP_FMT, ddr.vsp_read}, + {"vsp write", DUMP_FP_FMT, ddr.vsp_write}, {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, {"collocated write", DUMP_FP_FMT, ddr.collocated_write}, {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, {"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write}, - {"recon read", DUMP_FP_FMT, ddr.recon_read}, - {"recon write", DUMP_FP_FMT, ddr.recon_write}, - {"OPB read", DUMP_FP_FMT, ddr.opb_read}, - {"OPB write", DUMP_FP_FMT, ddr.opb_write}, - {"DPB read", DUMP_FP_FMT, ddr.dpb_read}, - {"DPB write", DUMP_FP_FMT, ddr.dpb_write}, - {"LLC DPB read", DUMP_FP_FMT, llc.dpb_read}, - {"LLC OPB read", DUMP_FP_FMT, llc.opb_read}, + {"opb read", DUMP_FP_FMT, ddr.opb_read}, + {"opb write", DUMP_FP_FMT, ddr.opb_write}, + {"dpb read", DUMP_FP_FMT, ddr.dpb_read}, + {"dpb write", DUMP_FP_FMT, ddr.dpb_write}, + {"dpb total", DUMP_FP_FMT, dpb_total}, + {"INTERMEDIATE LLC B/W", "", DUMP_HEADER_MAGIC}, + {"llc dpb read", DUMP_FP_FMT, llc.dpb_read}, + {"llc line buffer read", DUMP_FP_FMT, llc.line_buffer_read}, + {"llc line buffer write", DUMP_FP_FMT, llc.line_buffer_write}, }; __dump(dump, ARRAY_SIZE(dump)); @@ -614,76 +604,88 @@ static unsigned long __calculate_encoder(struct vidc_bus_vote_data *d, * measured heuristics and hardcoded numbers taken from the firmware. */ /* Encoder Parameters */ - - int width, height, fps, dpb_bpp, lcu_per_frame, lcu_size, - vertical_tile_width, colocated_bytes_per_lcu, bitrate, - ref_overlap_bw_factor; - enum hal_uncompressed_format dpb_color_format, original_color_format; - bool dpb_compression_enabled, original_compression_enabled, - work_mode_1, low_power, rotation, cropping_or_scaling, + int width, height, fps, lcu_size, bitrate, lcu_per_frame, + collocated_bytes_per_lcu, tnbr_per_lcu, dpb_bpp, + original_color_format, dpb_ubwc_tile_width, + dpb_ubwc_tile_height, vertical_tile_width; + bool work_mode_1, original_compression_enabled, + low_power, rotation, cropping_or_scaling, b_frames_enabled = false, - llc_dual_core_ref_read_buf_enabled = false, + llc_ref_chroma_cache_enabled = false, llc_top_line_buf_enabled = false, - llc_ref_chroma_cache_enabled = false; - fp_t dpb_compression_factor, original_compression_factor, - input_compression_factor, qsmmu_bw_overhead_factor, - ref_y_bw_factor, ref_cb_cr_bw_factor, ten_bpc_bpp_factor, - bw_for_1x_8bpc, dpb_bw_for_1x, ref_cb_cr_read, - bins_to_bit_factor, ref_y_read, ten_bpc_packing_factor, - dpb_write_factor, ref_overlap_bw, llc_ref_y_read, - llc_ref_cb_cr_read; + llc_vpss_rot_line_buf_enabled = false; + + fp_t bins_to_bit_factor, dpb_compression_factor, + original_compression_factor, + y_bw_no_ubwc_8bpp, y_bw_no_ubwc_10bpp, + input_compression_factor, + ref_y_read_bw_factor, ref_cbcr_read_bw_factor, + recon_write_bw_factor, mese_read_factor, + total_ref_read_crcb, + qsmmu_bw_overhead_factor; fp_t integer_part, frac_part; unsigned long ret = 0; /* Output parameters */ struct { fp_t vsp_read, vsp_write, collocated_read, collocated_write, - line_buffer_read, line_buffer_write, original_read, - original_write, dpb_read, dpb_write, total; + ref_read_y, ref_read_crcb, ref_write, + ref_write_overlap, orig_read, + line_buffer_read, line_buffer_write, + mese_read, mese_write, + total; } ddr = {0}; struct { - fp_t dpb_read, line_buffer, total; + fp_t ref_read_crcb, line_buffer, total; } llc = {0}; /* Encoder Parameters setup */ - ten_bpc_packing_factor = FP(1, 67, 1000); - ten_bpc_bpp_factor = FP(1, 1, 4); rotation = false; cropping_or_scaling = false; vertical_tile_width = 960; - ref_y_bw_factor = FP(1, 30, 100); - ref_cb_cr_bw_factor = FP(1, 50, 100); - dpb_write_factor = FP(1, 8, 100); + recon_write_bw_factor = FP(1, 8, 100); + ref_y_read_bw_factor = FP(1, 30, 100); + ref_cbcr_read_bw_factor = FP(1, 50, 100); /* Derived Parameters */ - lcu_size = d->lcu_size; fps = d->fps; - b_frames_enabled = d->b_frames_enabled; width = max(d->input_width, BASELINE_DIMENSIONS.width); height = max(d->input_height, BASELINE_DIMENSIONS.height); bitrate = __lut(width, height, fps)->bitrate; + lcu_size = d->lcu_size; lcu_per_frame = DIV_ROUND_UP(width, lcu_size) * DIV_ROUND_UP(height, lcu_size); + tnbr_per_lcu = lcu_size == 16 ? 128 : + lcu_size == 32 ? 64 : 128; + + y_bw_no_ubwc_8bpp = fp_div(fp_div(fp_mult( + FP_INT((int)(width * height)), FP_INT((int)(256 * fps))), + FP_INT(32 * 8)), FP_INT(1000 * 1000)); + y_bw_no_ubwc_10bpp = fp_div(fp_div(fp_mult( + FP_INT((int)(width * height)), FP_INT((int)(256 * fps))), + FP_INT(48 * 4)), FP_INT(1000 * 1000)); - dpb_color_format = HAL_COLOR_FORMAT_NV12_UBWC; + b_frames_enabled = d->b_frames_enabled; original_color_format = d->num_formats >= 1 ? d->color_formats[0] : HAL_UNUSED_COLOR; dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX; - dpb_compression_enabled = __ubwc(dpb_color_format); original_compression_enabled = __ubwc(original_color_format); work_mode_1 = d->work_mode == VIDC_WORK_MODE_1; low_power = d->power_mode == VIDC_POWER_LOW; bins_to_bit_factor = work_mode_1 ? FP_INT(0) : FP_INT(4); + dpb_ubwc_tile_width = dpb_bpp == 8 ? 32 : 48; + dpb_ubwc_tile_height = dpb_bpp == 8 ? 8 : 4; if (d->use_sys_cache) { - llc_dual_core_ref_read_buf_enabled = true; llc_ref_chroma_cache_enabled = true; + llc_top_line_buf_enabled = true, + llc_vpss_rot_line_buf_enabled = true; } /* @@ -712,103 +714,89 @@ static unsigned long __calculate_encoder(struct vidc_bus_vote_data *d, dpb_compression_factor : input_compression_factor : FP_ONE; - ddr.vsp_read = fp_mult(fp_div(FP_INT(bitrate), FP_INT(8)), - bins_to_bit_factor); + mese_read_factor = fp_div(FP_INT((width * height * fps)/4), + original_compression_factor); + mese_read_factor = fp_div(fp_mult(mese_read_factor, FP(2, 53, 100)), + FP_INT(1000 * 1000)); + + ddr.vsp_read = fp_div(fp_mult(FP_INT(bitrate), bins_to_bit_factor), + FP_INT(8)); ddr.vsp_write = ddr.vsp_read + fp_div(FP_INT(bitrate), FP_INT(8)); - colocated_bytes_per_lcu = lcu_size == 16 ? 16 : + collocated_bytes_per_lcu = lcu_size == 16 ? 16 : lcu_size == 32 ? 64 : 256; ddr.collocated_read = FP_INT(lcu_per_frame * - colocated_bytes_per_lcu * fps / bps(1)); + collocated_bytes_per_lcu * fps / bps(1)); ddr.collocated_write = ddr.collocated_read; - ddr.line_buffer_read = FP_INT(16 * lcu_per_frame * fps / bps(1)); + ddr.ref_read_y = ddr.ref_read_crcb = dpb_bpp == 8 ? + y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; - ddr.line_buffer_write = ddr.line_buffer_read; + if (width != vertical_tile_width) { + ddr.ref_read_y = fp_mult(ddr.ref_read_y, + ref_y_read_bw_factor); + } - llc.line_buffer = ddr.line_buffer_read + ddr.line_buffer_write; - if (llc_top_line_buf_enabled) - ddr.line_buffer_read = ddr.line_buffer_write = FP_INT(0); + ddr.ref_read_y = fp_div(ddr.ref_read_y, dpb_compression_factor); + if (b_frames_enabled) + ddr.ref_read_y = fp_mult(ddr.ref_read_y, FP_INT(2)); - llc.line_buffer -= (ddr.line_buffer_read + ddr.line_buffer_write); + ddr.ref_read_crcb = fp_mult(ddr.ref_read_crcb, FP(0, 50, 100)); + ddr.ref_read_crcb = fp_div(ddr.ref_read_crcb, dpb_compression_factor); + if (b_frames_enabled) + ddr.ref_read_crcb = fp_mult(ddr.ref_read_crcb, FP_INT(2)); - bw_for_1x_8bpc = fp_div(FP_INT(width * height), FP_INT(32 * 8)); + if (llc_ref_chroma_cache_enabled) { + total_ref_read_crcb = ddr.ref_read_crcb; + ddr.ref_read_crcb = fp_div(ddr.ref_read_crcb, + ref_cbcr_read_bw_factor); + llc.ref_read_crcb = total_ref_read_crcb - ddr.ref_read_crcb; + } - bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc, - fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000))); + ddr.ref_write = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.ref_write = fp_mult(ddr.ref_write, + (fp_div(FP(1, 50, 100), dpb_compression_factor))); - dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc : - fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor, - ten_bpc_bpp_factor)); + ddr.ref_write_overlap = fp_div(fp_mult(ddr.ref_write, + (recon_write_bw_factor - FP_ONE)), + recon_write_bw_factor); - ddr.original_read = fp_div(fp_mult(FP(1, 50, 100), dpb_bw_for_1x), + ddr.orig_read = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.orig_read = fp_div(fp_mult(ddr.orig_read, FP(1, 50, 100)), input_compression_factor); - ddr.original_write = FP_ZERO; - - ref_y_bw_factor = - width == vertical_tile_width ? FP_INT(1) : ref_y_bw_factor; - - ref_y_read = fp_mult(ref_y_bw_factor, dpb_bw_for_1x); - - ref_y_read = fp_div(ref_y_read, dpb_compression_factor); - - ref_y_read = - b_frames_enabled ? fp_mult(ref_y_read, FP_INT(2)) : ref_y_read; - - llc_ref_y_read = ref_y_read; - if (llc_dual_core_ref_read_buf_enabled) - ref_y_read = fp_div(ref_y_read, FP_INT(2)); - - llc_ref_y_read -= ref_y_read; - - ref_cb_cr_read = fp_mult(ref_cb_cr_bw_factor, dpb_bw_for_1x); - - ref_cb_cr_read = fp_div(ref_cb_cr_read, dpb_compression_factor); - - ref_cb_cr_read = - b_frames_enabled ? fp_mult(ref_cb_cr_read, FP_INT(2)) : - ref_cb_cr_read; - - llc_ref_cb_cr_read = ref_cb_cr_read; - - if (llc_ref_chroma_cache_enabled) - ref_cb_cr_read = fp_div(ref_cb_cr_read, ref_cb_cr_bw_factor); - - if (llc_dual_core_ref_read_buf_enabled) - ref_cb_cr_read = fp_div(ref_cb_cr_read, FP_INT(2)); - - llc_ref_cb_cr_read -= ref_cb_cr_read; + ddr.line_buffer_read = FP_INT(tnbr_per_lcu * lcu_per_frame * + fps / bps(1)); - ddr.dpb_write = fp_mult(dpb_write_factor, dpb_bw_for_1x); - - ddr.dpb_write = fp_mult(ddr.dpb_write, FP(1, 50, 100)); - - ddr.dpb_write = fp_div(ddr.dpb_write, input_compression_factor); - - ref_overlap_bw_factor = - width <= vertical_tile_width ? FP_INT(0) : FP_INT(1); - - ref_overlap_bw = fp_mult(ddr.dpb_write, ref_overlap_bw_factor); - - ref_overlap_bw = fp_div(ref_overlap_bw, dpb_write_factor); - - ref_overlap_bw = fp_mult(ref_overlap_bw, - (dpb_write_factor - FP_INT(1))); + ddr.line_buffer_write = ddr.line_buffer_read; + if (llc_top_line_buf_enabled) { + llc.line_buffer = ddr.line_buffer_read + ddr.line_buffer_write; + ddr.line_buffer_read = ddr.line_buffer_write = FP_ZERO; + } - ddr.dpb_read = ref_y_read + ref_cb_cr_read + ref_overlap_bw; + ddr.mese_read = dpb_bpp == 8 ? y_bw_no_ubwc_8bpp : y_bw_no_ubwc_10bpp; + ddr.mese_read = fp_div(ddr.mese_read, + fp_mult(FP(1, 37, 100), original_compression_factor)) + + mese_read_factor; - llc.dpb_read = llc_ref_y_read + llc_ref_cb_cr_read; + ddr.mese_write = FP_INT((width * height)/512) + + fp_div(FP_INT((width * height)/4), + original_compression_factor) + + FP_INT((width * height)/128); + ddr.mese_write = fp_div(fp_mult(ddr.mese_write, FP_INT(fps)), + FP_INT(1000 * 1000)); ddr.total = ddr.vsp_read + ddr.vsp_write + ddr.collocated_read + ddr.collocated_write + + ddr.ref_read_y + ddr.ref_read_crcb + + ddr.ref_write + ddr.ref_write_overlap + + ddr.orig_read + ddr.line_buffer_read + ddr.line_buffer_write + - ddr.original_read + ddr.original_write + - ddr.dpb_read + ddr.dpb_write; + ddr.mese_read + ddr.mese_write; - llc.total = llc.dpb_read + llc.line_buffer; + llc.total = llc.ref_read_crcb + llc.line_buffer; qsmmu_bw_overhead_factor = FP(1, 3, 100); ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); @@ -818,44 +806,55 @@ static unsigned long __calculate_encoder(struct vidc_bus_vote_data *d, {"ENCODER PARAMETERS", "", DUMP_HEADER_MAGIC}, {"width", "%d", width}, {"height", "%d", height}, - {"DPB format", "%#x", dpb_color_format}, - {"original frame format", "%#x", original_color_format}, {"fps", "%d", fps}, - {"DPB compression enable", "%d", dpb_compression_enabled}, - {"original compression enable", "%d", - original_compression_enabled}, - {"low power mode", "%d", low_power}, - {"Work Mode", "%d", work_mode_1}, - {"DPB compression factor", DUMP_FP_FMT, - dpb_compression_factor}, - {"original compression factor", DUMP_FP_FMT, - original_compression_factor}, {"rotation", "%d", rotation}, {"cropping or scaling", "%d", cropping_or_scaling}, + {"low power mode", "%d", low_power}, + {"work Mode", "%d", work_mode_1}, + {"original frame format", "%#x", original_color_format}, + {"original compression enabled", "%d", + original_compression_enabled}, + {"dpb compression factor", DUMP_FP_FMT, + dpb_compression_factor}, + {"input compression factor", DUMP_FP_FMT, + input_compression_factor}, + {"llc ref chroma cache enabled", DUMP_FP_FMT, + llc_ref_chroma_cache_enabled}, + {"llc top line buf enabled", DUMP_FP_FMT, + llc_top_line_buf_enabled}, + {"llc vpss rot line buf enabled ", DUMP_FP_FMT, + llc_vpss_rot_line_buf_enabled}, {"DERIVED PARAMETERS", "", DUMP_HEADER_MAGIC}, - {"LCU size", "%d", lcu_size}, + {"lcu size", "%d", lcu_size}, {"bitrate (Mbit/sec)", "%lu", bitrate}, {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, + {"original compression factor", DUMP_FP_FMT, + original_compression_factor}, + {"mese read factor", DUMP_FP_FMT, + mese_read_factor}, {"qsmmu_bw_overhead_factor", DUMP_FP_FMT, qsmmu_bw_overhead_factor}, + {"bw for NV12 8bpc)", DUMP_FP_FMT, y_bw_no_ubwc_8bpp}, + {"bw for NV12 10bpc)", DUMP_FP_FMT, y_bw_no_ubwc_10bpp}, {"INTERMEDIATE B/W DDR", "", DUMP_HEADER_MAGIC}, - {"ref_y_read", DUMP_FP_FMT, ref_y_read}, - {"ref_cb_cr_read", DUMP_FP_FMT, ref_cb_cr_read}, - {"ref_overlap_bw", DUMP_FP_FMT, ref_overlap_bw}, - {"VSP read", DUMP_FP_FMT, ddr.vsp_read}, - {"VSP write", DUMP_FP_FMT, ddr.vsp_write}, + {"vsp read", DUMP_FP_FMT, ddr.vsp_read}, + {"vsp write", DUMP_FP_FMT, ddr.vsp_write}, {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, {"collocated write", DUMP_FP_FMT, ddr.collocated_write}, + {"ref read y", DUMP_FP_FMT, ddr.ref_read_y}, + {"ref read crcb", DUMP_FP_FMT, ddr.ref_read_crcb}, + {"ref write", DUMP_FP_FMT, ddr.ref_write}, + {"ref write overlap", DUMP_FP_FMT, ddr.ref_write_overlap}, + {"original read", DUMP_FP_FMT, ddr.orig_read}, {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, {"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write}, - {"original read", DUMP_FP_FMT, ddr.original_read}, - {"original write", DUMP_FP_FMT, ddr.original_write}, - {"DPB read", DUMP_FP_FMT, ddr.dpb_read}, - {"DPB write", DUMP_FP_FMT, ddr.dpb_write}, - {"LLC DPB read", DUMP_FP_FMT, llc.dpb_read}, - {"LLC Line buffer", DUMP_FP_FMT, llc.line_buffer}, + {"mese read", DUMP_FP_FMT, ddr.mese_read}, + {"mese write", DUMP_FP_FMT, ddr.mese_write}, + {"INTERMEDIATE LLC B/W", "", DUMP_HEADER_MAGIC}, + {"llc ref read crcb", DUMP_FP_FMT, llc.ref_read_crcb}, + {"llc line buffer", DUMP_FP_FMT, llc.line_buffer}, }; __dump(dump, ARRAY_SIZE(dump)); } diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index c461e6c1ae8b1679400fd5a4a8c611a477799c93..2e335fdf6552627bdd75ddd1b0f688c50b58fe68 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -1377,6 +1377,14 @@ int create_pkt_cmd_session_set_property( pkt->size += sizeof(u32); break; } + case HAL_PARAM_VENC_ADAPTIVE_B: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_ADAPTIVE_B, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(struct hfi_enable); + break; + } case HAL_PARAM_VDEC_CONCEAL_COLOR: { struct hfi_conceal_color *hfi; diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c index cc29a8f9704d9f44dc26cdb9b9ed8a2fa8118331..9d098d7408c02b5096a5e0965d1db4ebfab0767d 100644 --- a/drivers/media/platform/msm/vidc/hfi_response_handler.c +++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c @@ -93,7 +93,7 @@ static enum vidc_status hfi_map_err_status(u32 hfi_err) return vidc_err; } -static enum msm_vidc_pixel_depth get_hal_pixel_depth(u32 hfi_bit_depth) +static int get_hal_pixel_depth(u32 hfi_bit_depth) { switch (hfi_bit_depth) { case HFI_BITDEPTH_8: return MSM_VIDC_BIT_DEPTH_8; @@ -119,7 +119,7 @@ static int hfi_process_sess_evt_seq_changed(u32 device_id, u32 entropy_mode = 0; u8 *data_ptr; int prop_id; - enum msm_vidc_pixel_depth luma_bit_depth, chroma_bit_depth; + int luma_bit_depth, chroma_bit_depth; struct hfi_colour_space *colour_info; if (sizeof(struct hfi_msg_event_notify_packet) > pkt->size) { @@ -858,6 +858,37 @@ static int copy_caps_to_sessions(struct hfi_capability_supported *cap, return 0; } +static int copy_nal_stream_format_caps_to_sessions(u32 nal_stream_format_value, + struct msm_vidc_capability *capabilities, u32 num_sessions, + u32 codecs, u32 domain) +{ + u32 i = 0; + struct msm_vidc_capability *capability; + u32 sess_codec; + u32 sess_domain; + + for (i = 0; i < num_sessions; i++) { + sess_codec = 0; + sess_domain = 0; + capability = &capabilities[i]; + + if (capability->codec) + sess_codec = + vidc_get_hfi_codec(capability->codec); + if (capability->domain) + sess_domain = + vidc_get_hfi_domain(capability->domain); + + if (!(sess_codec & codecs && sess_domain & domain)) + continue; + + capability->nal_stream_format.nal_stream_format_supported = + nal_stream_format_value; + } + + return 0; +} + static enum vidc_status hfi_parse_init_done_properties( struct msm_vidc_capability *capabilities, u32 num_sessions, u8 *data_ptr, u32 num_properties, @@ -986,6 +1017,15 @@ static enum vidc_status hfi_parse_init_done_properties( } case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: { + struct hfi_nal_stream_format_supported *prop = + (struct hfi_nal_stream_format_supported *) + (data_ptr + next_offset); + + copy_nal_stream_format_caps_to_sessions( + prop->nal_stream_format_supported, + capabilities, num_sessions, + codecs, domain); + next_offset += sizeof(struct hfi_nal_stream_format_supported); num_properties--; diff --git a/drivers/media/platform/msm/vidc/msm_cvp.c b/drivers/media/platform/msm/vidc/msm_cvp.c index de723b0a637ef976503c16858accf90bab9408d2..d1705b0c1af43a128fd311786420a2b39e09d29a 100644 --- a/drivers/media/platform/msm/vidc/msm_cvp.c +++ b/drivers/media/platform/msm/vidc/msm_cvp.c @@ -311,8 +311,29 @@ static int msm_cvp_request_power(struct msm_vidc_inst *inst, dprintk(VIDC_ERR, "%s: failed to scale clocks and bus for inst %pK (%#x)\n", __func__, inst, hash32_ptr(inst->session)); + goto exit; + } + + if (!inst->clk_data.min_freq && !inst->clk_data.ddr_bw && + !inst->clk_data.sys_cache_bw) { + rc = msm_cvp_inst_pause(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to pause inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + goto exit; + } + } else { + rc = msm_cvp_inst_resume(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to resume inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + goto exit; + } } +exit: return rc; } @@ -515,65 +536,70 @@ int msm_cvp_ctrl_init(struct msm_vidc_inst *inst, ARRAY_SIZE(msm_cvp_ctrls), ctrl_ops); } +int msm_cvp_inst_pause(struct msm_vidc_inst *inst) +{ + int rc; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + rc = call_hfi_op(hdev, session_pause, (void *)inst->session); + if (rc) + dprintk(VIDC_ERR, "%s: failed to pause inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + + return rc; +} + +int msm_cvp_inst_resume(struct msm_vidc_inst *inst) +{ + int rc; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + rc = call_hfi_op(hdev, session_resume, (void *)inst->session); + if (rc) + dprintk(VIDC_ERR, "%s: failed to resume inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + + return rc; +} + int msm_cvp_inst_deinit(struct msm_vidc_inst *inst) { int rc = 0; - struct hfi_device *hdev; - struct msm_vidc_cvp_buffer *cbuf; - struct vidc_unregister_buffer vbuf; + struct msm_vidc_cvp_buffer *cbuf, *temp; if (!inst || !inst->core) { dprintk(VIDC_ERR, "%s: invalid params\n", __func__); return -EINVAL; } - hdev = inst->core->device; dprintk(VIDC_DBG, "%s: inst %pK (%#x)\n", __func__, inst, hash32_ptr(inst->session)); - do { - mutex_lock(&inst->cvpbufs.lock); - if (list_empty(&inst->cvpbufs.list)) { - mutex_unlock(&inst->cvpbufs.lock); - break; - } - cbuf = list_first_entry(&inst->cvpbufs.list, - struct msm_vidc_cvp_buffer, list); - mutex_unlock(&inst->cvpbufs.lock); + rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE); + if (rc) + dprintk(VIDC_ERR, "%s: close failed\n", __func__); + mutex_lock(&inst->cvpbufs.lock); + list_for_each_entry_safe(cbuf, temp, &inst->cvpbufs.list, list) { print_cvp_buffer(VIDC_ERR, "unregistered", inst, cbuf); - memset(&vbuf, 0, sizeof(struct vidc_unregister_buffer)); - vbuf.index = cbuf->buf.index; - vbuf.type = get_hal_buftype(__func__, cbuf->buf.type); - vbuf.size = cbuf->buf.size; - vbuf.device_addr = cbuf->smem.device_addr; - vbuf.client_data = cbuf->smem.device_addr; - vbuf.response_required = true; - rc = call_hfi_op(hdev, session_unregister_buffer, - (void *)inst->session, &vbuf); - if (rc) { - dprintk(VIDC_ERR, "%s: unregbuf failed\n", __func__); - } else { - rc = wait_for_completion_timeout( - &inst->completions[ - SESSION_MSG_INDEX( - HAL_SESSION_UNREGISTER_BUFFER_DONE)], - msecs_to_jiffies( - inst->core->resources.msm_vidc_hw_rsp_timeout)); - if (!rc) - dprintk(VIDC_ERR, - "%s: wait timedout for unregbuf done\n", - __func__); - } rc = msm_smem_unmap_dma_buf(inst, &cbuf->smem); if (rc) dprintk(VIDC_ERR, "%s: unmap failed\n", __func__); - - mutex_lock(&inst->cvpbufs.lock); list_del(&cbuf->list); - mutex_unlock(&inst->cvpbufs.lock); kfree(cbuf); - cbuf = NULL; - } while (1); + } + mutex_unlock(&inst->cvpbufs.lock); inst->clk_data.min_freq = 0; inst->clk_data.ddr_bw = 0; diff --git a/drivers/media/platform/msm/vidc/msm_cvp.h b/drivers/media/platform/msm/vidc/msm_cvp.h index ea6ae4196fb507c487c68ad0b165c04181b21691..f8dc75f443db9d4bfb12564928f29ddf4a3f3726 100644 --- a/drivers/media/platform/msm/vidc/msm_cvp.h +++ b/drivers/media/platform/msm/vidc/msm_cvp.h @@ -26,6 +26,8 @@ void handle_session_unregister_buffer_done(enum hal_command_response cmd, int msm_vidc_cvp(struct msm_vidc_inst *inst, struct msm_vidc_arg *arg); int msm_cvp_inst_init(struct msm_vidc_inst *inst); int msm_cvp_inst_deinit(struct msm_vidc_inst *inst); +int msm_cvp_inst_pause(struct msm_vidc_inst *inst); +int msm_cvp_inst_resume(struct msm_vidc_inst *inst); int msm_cvp_ctrl_init(struct msm_vidc_inst *inst, const struct v4l2_ctrl_ops *ctrl_ops); #endif diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 498ade998dc89d86efd26abc8677c240386e45bf..5044fbf7c2ce4841c53c29180eb988b101ca9670 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -717,12 +717,14 @@ int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f) int msm_vdec_inst_init(struct msm_vidc_inst *inst) { int rc = 0; + struct msm_vidc_core *core; struct msm_vidc_format *fmt = NULL; - if (!inst) { + if (!inst || !inst->core) { dprintk(VIDC_ERR, "Invalid input = %pK\n", inst); return -EINVAL; } + core = inst->core; inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT; inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH; inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT; @@ -740,6 +742,8 @@ int msm_vdec_inst_init(struct msm_vidc_inst *inst) inst->bufq[CAPTURE_PORT].num_planes = 1; inst->prop.fps = DEFAULT_FPS; inst->clk_data.operating_rate = 0; + if (core->resources.decode_batching) + inst->batch.size = MAX_DEC_BATCH_SIZE; /* By default, initialize CAPTURE port to UBWC YUV format */ fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats, @@ -1128,10 +1132,7 @@ int msm_vdec_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY: property_id = HAL_CONFIG_REALTIME; - /* firmware has inverted values for realtime and - * non-realtime priority - */ - hal_property.enable = !(ctrl->val); + hal_property.enable = ctrl->val; pdata = &hal_property; switch (ctrl->val) { case V4L2_MPEG_MSM_VIDC_DISABLE: diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 892dcbcb839aee9e671733fae0fc3cf066550597..15c91a67ba49c4bdc93d04dd4a1506e7671ce918 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -147,6 +147,15 @@ static const char *const iframe_sizes[] = { "Unlimited" }; +static const char *const mpeg_video_stream_format[] = { + "NAL Format Start Codes", + "NAL Format One NAL Per Buffer", + "NAL Format One Byte Length", + "NAL Format Two Byte Length", + "NAL Format Four Byte Length", + NULL +}; + static struct msm_vidc_ctrl msm_venc_ctrls[] = { { .id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD, @@ -291,6 +300,15 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .menu_skip_mask = 0, .qmenu = NULL, }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ADAPTIVE_B, + .name = "Adaptive B frames", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_MSM_VIDC_DISABLE, + .maximum = V4L2_MPEG_MSM_VIDC_ENABLE, + .default_value = V4L2_MPEG_MSM_VIDC_ENABLE, + .step = 1, + }, { .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, .name = "CAPTURE Count", @@ -1097,7 +1115,19 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .step = 1, .qmenu = NULL, }, - + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT, + .name = "NAL Format", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES, + .maximum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH, + .default_value = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES) | + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH) + ), + .qmenu = mpeg_video_stream_format, + }, }; #define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls) @@ -1226,6 +1256,7 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) struct hal_vui_timing_info vui_timing_info = {0}; enum hal_iframesize_type iframesize_type = HAL_IFRAMESIZE_TYPE_DEFAULT; u32 color_primaries, custom_matrix; + struct hal_nal_stream_format_select stream_format; if (!inst || !inst->core || !inst->core->device) { dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); @@ -1307,6 +1338,11 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &intra_period; break; } + case V4L2_CID_MPEG_VIDC_VIDEO_ADAPTIVE_B: + property_id = HAL_PARAM_VENC_ADAPTIVE_B; + enable.enable = ctrl->val; + pdata = &enable; + break; case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME: property_id = HAL_CONFIG_VENC_REQUEST_IFRAME; request_iframe.enable = true; @@ -1828,10 +1864,7 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) } case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY: property_id = HAL_CONFIG_REALTIME; - /* firmware has inverted values for realtime and - * non-realtime priority - */ - enable.enable = !(ctrl->val); + enable.enable = ctrl->val; pdata = &enable; switch (ctrl->val) { case V4L2_MPEG_MSM_VIDC_DISABLE: @@ -2027,6 +2060,13 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) vui_timing_info.time_scale = NSEC_PER_SEC; break; } + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT: + { + property_id = HAL_PARAM_NAL_STREAM_FORMAT_SELECT; + stream_format.nal_stream_format_select = BIT(ctrl->val); + pdata = &stream_format; + break; + } case V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT: case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH: case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT: @@ -2286,12 +2326,12 @@ int msm_venc_s_ext_ctrl(struct msm_vidc_inst *inst, control[i].value; break; case V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM: - mdisp_sei->nMaxDispMasteringLum = - control[i].value; + mdisp_sei->nMaxDisplayMasteringLuminance + = control[i].value; break; case V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM: - mdisp_sei->nMinDispMasteringLum = - control[i].value; + mdisp_sei->nMinDisplayMasteringLuminance + = control[i].value; break; case V4L2_CID_MPEG_VIDC_VENC_MAX_CLL: cll_sei->nMaxContentLight = diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index 4374878a8e6fa9d40556bb54da89eee55674622b..3e9fa354b042b1f5b08690d6870bf74f5c44dfe9 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -231,16 +231,25 @@ EXPORT_SYMBOL(msm_vidc_query_ctrl); int msm_vidc_s_fmt(void *instance, struct v4l2_format *f) { + int rc = 0; struct msm_vidc_inst *inst = instance; if (!inst || !f) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_s_fmt(instance, f); + rc = msm_vdec_s_fmt(instance, f); if (inst->session_type == MSM_VIDC_ENCODER) - return msm_venc_s_fmt(instance, f); - return -EINVAL; + rc = msm_venc_s_fmt(instance, f); + + dprintk(VIDC_DBG, + "s_fmt: %x : type %d wxh %dx%d pixelfmt %#x num_planes %d size[0] %d size[1] %d in_reconfig %d\n", + hash32_ptr(inst->session), f->type, + f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.num_planes, + f->fmt.pix_mp.plane_fmt[0].sizeimage, + f->fmt.pix_mp.plane_fmt[1].sizeimage, inst->in_reconfig); + return rc; } EXPORT_SYMBOL(msm_vidc_s_fmt); @@ -297,6 +306,13 @@ int msm_vidc_g_fmt(void *instance, struct v4l2_format *f) f->fmt.pix_mp.plane_fmt[0].reserved[0] = VENUS_Y_SCANLINES(color_format, inst->prop.height[port]); + dprintk(VIDC_DBG, + "g_fmt: %x : type %d wxh %dx%d pixelfmt %#x num_planes %d size[0] %d size[1] %d in_reconfig %d\n", + hash32_ptr(inst->session), f->type, + f->fmt.pix_mp.width, f->fmt.pix_mp.height, + f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.num_planes, + f->fmt.pix_mp.plane_fmt[0].sizeimage, + f->fmt.pix_mp.plane_fmt[1].sizeimage, inst->in_reconfig); exit: return rc; } @@ -837,6 +853,11 @@ static int msm_vidc_queue_setup(struct vb2_queue *q, rc = -EINVAL; break; } + + dprintk(VIDC_DBG, + "queue_setup: %x : type %d num_buffers %d num_planes %d sizes[0] %d sizes[1] %d\n", + hash32_ptr(inst->session), q->type, *num_buffers, + *num_planes, sizes[0], sizes[1]); return rc; } @@ -983,6 +1004,15 @@ static inline int start_streaming(struct msm_vidc_inst *inst) } } + if (is_batching_allowed(inst)) { + dprintk(VIDC_DBG, + "%s: batching enabled for inst %pK (%#x)\n", + __func__, inst, hash32_ptr(inst->session)); + inst->batch.enable = true; + /* this will disable dcvs as batching enabled */ + msm_dcvs_try_enable(inst); + } + /* * For seq_changed_insufficient, driver should set session_continue * to firmware after the following sequence @@ -1164,42 +1194,111 @@ static void msm_vidc_stop_streaming(struct vb2_queue *q) inst, q->type); } -static void msm_vidc_buf_queue(struct vb2_buffer *vb2) +static int msm_vidc_queue_buf(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) { int rc = 0; - struct msm_vidc_inst *inst = NULL; - struct msm_vidc_buffer *mbuf = NULL; + struct msm_vidc_buffer *mbuf; - inst = vb2_get_drv_priv(vb2->vb2_queue); - if (!inst) { - dprintk(VIDC_ERR, "%s: invalid inst\n", __func__); - return; + if (!inst || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; } mbuf = msm_comm_get_vidc_buffer(inst, vb2); if (IS_ERR_OR_NULL(mbuf)) { + /* + * if the buffer has RBR_PENDING flag (-EEXIST) then don't queue + * it now, it will be queued via msm_comm_qbuf_rbr() as part of + * RBR event processing. + */ if (PTR_ERR(mbuf) == -EEXIST) - return; - print_vb2_buffer(VIDC_ERR, "failed to get vidc-buf", - inst, vb2); - rc = -EINVAL; - goto error; + return 0; + dprintk(VIDC_ERR, "%s: failed to get vidc-buf\n", __func__); + return -EINVAL; } if (!kref_get_mbuf(inst, mbuf)) { dprintk(VIDC_ERR, "%s: mbuf not found\n", __func__); - rc = -EINVAL; - goto error; + return -EINVAL; } - rc = msm_comm_qbuf(inst, mbuf); if (rc) - print_vidc_buffer(VIDC_ERR, "failed qbuf", inst, mbuf); - + dprintk(VIDC_ERR, "%s: failed qbuf\n", __func__); kref_put_mbuf(mbuf); -error: + return rc; +} + +static int msm_vidc_queue_buf_decode_batch(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) +{ + int rc; + struct msm_vidc_buffer *mbuf; + + if (!inst || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + mbuf = msm_comm_get_vidc_buffer(inst, vb2); + if (IS_ERR_OR_NULL(mbuf)) { + dprintk(VIDC_ERR, "%s: failed to get vidc-buf\n", __func__); + return -EINVAL; + } + if (!kref_get_mbuf(inst, mbuf)) { + dprintk(VIDC_ERR, "%s: mbuf not found\n", __func__); + return -EINVAL; + } + /* + * If this buffer has RBR_EPNDING then it will not be queued + * but it may trigger full batch queuing in below function. + */ + rc = msm_comm_qbuf_decode_batch(inst, mbuf); if (rc) + dprintk(VIDC_ERR, "%s: failed qbuf\n", __func__); + kref_put_mbuf(mbuf); + + return rc; +} + +static int msm_vidc_queue_buf_batch(struct msm_vidc_inst *inst, + struct vb2_buffer *vb2) +{ + int rc; + + if (!inst || !vb2) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + + if (inst->session_type == MSM_VIDC_DECODER && + vb2->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + rc = msm_vidc_queue_buf_decode_batch(inst, vb2); + else + rc = msm_vidc_queue_buf(inst, vb2); + + return rc; +} + +static void msm_vidc_buf_queue(struct vb2_buffer *vb2) +{ + int rc = 0; + struct msm_vidc_inst *inst = NULL; + + inst = vb2_get_drv_priv(vb2->vb2_queue); + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid inst\n", __func__); + return; + } + + if (inst->batch.enable) + rc = msm_vidc_queue_buf_batch(inst, vb2); + else + rc = msm_vidc_queue_buf(inst, vb2); + if (rc) { + print_vb2_buffer(VIDC_ERR, "failed vb2-qbuf", inst, vb2); msm_comm_generate_session_error(inst); + } } static const struct vb2_ops msm_vidc_vb2q_ops = { @@ -1402,7 +1501,8 @@ static int msm_vidc_get_count(struct msm_vidc_inst *inst, ctrl->val = bufreq->buffer_count_min_host; return 0; } - if (ctrl->val > bufreq->buffer_count_min_host) { + if (ctrl->val > bufreq->buffer_count_min_host && + ctrl->val <= MAX_NUM_OUTPUT_BUFFERS) { dprintk(VIDC_DBG, "Buffer count Host changed from %d to %d\n", bufreq->buffer_count_min_host, @@ -1421,6 +1521,12 @@ static int msm_vidc_get_count(struct msm_vidc_inst *inst, msm_vidc_update_host_buff_counts(inst); ctrl->val = bufreq->buffer_count_min_host; + dprintk(VIDC_DBG, + "g_count: %x : OUTPUT: min %d min_host %d actual %d\n", + hash32_ptr(inst->session), + bufreq->buffer_count_min, + bufreq->buffer_count_min_host, + bufreq->buffer_count_actual); return rc; } else if (ctrl->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE) { @@ -1452,7 +1558,8 @@ static int msm_vidc_get_count(struct msm_vidc_inst *inst, bufreq->buffer_count_min_host = ctrl->val; } - if (ctrl->val > bufreq->buffer_count_min_host) { + if (ctrl->val > bufreq->buffer_count_min_host && + ctrl->val <= MAX_NUM_CAPTURE_BUFFERS) { dprintk(VIDC_DBG, "Buffer count Host changed from %d to %d\n", bufreq->buffer_count_min_host, @@ -1471,7 +1578,12 @@ static int msm_vidc_get_count(struct msm_vidc_inst *inst, msm_vidc_update_host_buff_counts(inst); ctrl->val = bufreq->buffer_count_min_host; - + dprintk(VIDC_DBG, + "g_count: %x : CAPTURE: min %d min_host %d actual %d\n", + hash32_ptr(inst->session), + bufreq->buffer_count_min, + bufreq->buffer_count_min_host, + bufreq->buffer_count_actual); return rc; } return -EINVAL; @@ -1553,6 +1665,10 @@ static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDC_VIDEO_TME_PAYLOAD_VERSION: ctrl->val = inst->capability.tme_version; break; + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT: + ctrl->val = + inst->capability.nal_stream_format.nal_stream_format_supported; + break; default: /* * Other controls aren't really volatile, shouldn't need to diff --git a/drivers/media/platform/msm/vidc/msm_vidc.h b/drivers/media/platform/msm/vidc/msm_vidc.h index 913b5f9f29d96fd91bd0742983de21ce4541632e..776c74357cd2d717ed8629d0788c9d05d29342df 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.h +++ b/drivers/media/platform/msm/vidc/msm_vidc.h @@ -19,6 +19,7 @@ #include #include #include +#include #define HAL_BUFFER_MAX 0xd @@ -101,380 +102,6 @@ union msm_v4l2_cmd { struct v4l2_encoder_cmd enc; }; -#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12 0x2 -#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC 0x8002 -#define MSM_VIDC_EXTRADATA_FRAME_QP_ADV 0x1 - -struct msm_vidc_extradata_header { - unsigned int size; - unsigned int:32; /** Keeping binary compatibility */ - unsigned int:32; /* with firmware and OpenMAX IL **/ - unsigned int type; /* msm_vidc_extradata_type */ - unsigned int data_size; - unsigned char data[1]; -}; - -struct msm_vidc_interlace_payload { - unsigned int format; - unsigned int color_format; -}; - -struct msm_vidc_framerate_payload { - unsigned int frame_rate; -}; - -struct msm_vidc_ts_payload { - unsigned int timestamp_lo; - unsigned int timestamp_hi; -}; - -struct msm_vidc_concealmb_payload { - unsigned int num_mbs; -}; - -struct msm_vidc_recoverysei_payload { - unsigned int flags; -}; - -struct msm_vidc_aspect_ratio_payload { - unsigned int size; - unsigned int version; - unsigned int port_index; - unsigned int aspect_width; - unsigned int aspect_height; -}; - -struct msm_vidc_mpeg2_seqdisp_payload { - unsigned int video_format; - unsigned int color_descp; - unsigned int color_primaries; - unsigned int transfer_char; - unsigned int matrix_coeffs; - unsigned int disp_width; - unsigned int disp_height; -}; - -struct msm_vidc_vc1_seqdisp_payload { - unsigned int prog_seg_format; - unsigned int uv_sampl_fmt; - unsigned int color_format; - unsigned int color_primaries; - unsigned int transfer_char; - unsigned int matrix_coeffs; - unsigned int aspect_ratio; - unsigned int aspect_horiz; - unsigned int aspect_vert; -}; - -struct msm_vidc_input_crop_payload { - unsigned int size; - unsigned int version; - unsigned int port_index; - unsigned int left; - unsigned int top; - unsigned int width; - unsigned int height; -}; - -struct msm_vidc_extradata_index { - unsigned int type; - union { - struct msm_vidc_input_crop_payload input_crop; - struct msm_vidc_aspect_ratio_payload aspect_ratio; - }; -}; - -struct msm_vidc_panscan_window { - unsigned int panscan_height_offset; - unsigned int panscan_width_offset; - unsigned int panscan_window_width; - unsigned int panscan_window_height; -}; - -struct msm_vidc_panscan_window_payload { - unsigned int num_panscan_windows; - struct msm_vidc_panscan_window wnd[1]; -}; - -struct msm_vidc_stream_userdata_payload { - unsigned int type; - unsigned int data[1]; -}; - -struct msm_vidc_frame_qp_payload { - unsigned int frame_qp; - unsigned int qp_sum; - unsigned int skip_qp_sum; - unsigned int skip_num_blocks; - unsigned int total_num_blocks; -}; - -struct msm_vidc_frame_bits_info_payload { - unsigned int frame_bits; - unsigned int header_bits; -}; - -struct msm_vidc_s3d_frame_packing_payload { - unsigned int fpa_id; - unsigned int cancel_flag; - unsigned int fpa_type; - unsigned int quin_cunx_flag; - unsigned int content_interprtation_type; - unsigned int spatial_flipping_flag; - unsigned int frame0_flipped_flag; - unsigned int field_views_flag; - unsigned int current_frame_is_frame0_flag; - unsigned int frame0_self_contained_flag; - unsigned int frame1_self_contained_flag; - unsigned int frame0_graid_pos_x; - unsigned int frame0_graid_pos_y; - unsigned int frame1_graid_pos_x; - unsigned int frame1_graid_pos_y; - unsigned int fpa_reserved_byte; - unsigned int fpa_repetition_period; - unsigned int fpa_extension_flag; -}; - -struct msm_vidc_ubwc_cr_stats_info { - unsigned int stats_tile_32; - unsigned int stats_tile_64; - unsigned int stats_tile_96; - unsigned int stats_tile_128; - unsigned int stats_tile_160; - unsigned int stats_tile_192; - unsigned int stats_tile_256; -}; - -struct msm_vidc_yuv_stats_payload { - unsigned int frame_qp; - unsigned int texture; - unsigned int luma_in_q16; - unsigned int frame_difference; -}; - -struct msm_vidc_vpx_colorspace_payload { - unsigned int color_space; - unsigned int yuv_range_flag; - unsigned int sumsampling_x; - unsigned int sumsampling_y; -}; - -struct msm_vidc_roi_qp_payload { - int upper_qp_offset; - int lower_qp_offset; - unsigned int b_roi_info; - int mbi_info_size; - unsigned int data[1]; -}; - -struct msm_vidc_mastering_display_colour_sei_payload { - unsigned int nDisplayPrimariesX[3]; - unsigned int nDisplayPrimariesY[3]; - unsigned int nWhitePointX; - unsigned int nWhitePointY; - unsigned int nMaxDispMasteringLum; - unsigned int nMinDispMasteringLum; -}; - -struct msm_vidc_content_light_level_sei_payload { - unsigned int nMaxContentLight; - unsigned int nMaxPicAverageLight; -}; - -struct msm_vidc_vui_display_info_payload { - unsigned int video_signal_present_flag; - unsigned int video_format; - unsigned int bit_depth_y; - unsigned int bit_depth_c; - unsigned int video_full_range_flag; - unsigned int color_description_present_flag; - unsigned int color_primaries; - unsigned int transfer_characteristics; - unsigned int matrix_coefficients; - unsigned int chroma_location_info_present_flag; - unsigned int chroma_format_idc; - unsigned int separate_color_plane_flag; - unsigned int chroma_sample_loc_type_top_field; - unsigned int chroma_sample_loc_type_bottom_field; -}; - -enum msm_vidc_extradata_type { - MSM_VIDC_EXTRADATA_NONE = 0x00000000, - MSM_VIDC_EXTRADATA_MB_QUANTIZATION = 0x00000001, - MSM_VIDC_EXTRADATA_INTERLACE_VIDEO = 0x00000002, - MSM_VIDC_EXTRADATA_TIMESTAMP = 0x00000005, - MSM_VIDC_EXTRADATA_S3D_FRAME_PACKING = 0x00000006, - MSM_VIDC_EXTRADATA_FRAME_RATE = 0x00000007, - MSM_VIDC_EXTRADATA_PANSCAN_WINDOW = 0x00000008, - MSM_VIDC_EXTRADATA_RECOVERY_POINT_SEI = 0x00000009, - MSM_VIDC_EXTRADATA_MPEG2_SEQDISP = 0x0000000D, - MSM_VIDC_EXTRADATA_STREAM_USERDATA = 0x0000000E, - MSM_VIDC_EXTRADATA_FRAME_QP = 0x0000000F, - MSM_VIDC_EXTRADATA_FRAME_BITS_INFO = 0x00000010, - MSM_VIDC_EXTRADATA_VQZIP_SEI = 0x00000011, - MSM_VIDC_EXTRADATA_ROI_QP = 0x00000013, -#define MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO \ - MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO - MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO = 0x00000014, -#define MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI \ - MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI - MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI = 0x00000015, -#define MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI \ - MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI - MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI = 0x00000016, -#define MSM_VIDC_EXTRADATA_PQ_INFO \ - MSM_VIDC_EXTRADATA_PQ_INFO - MSM_VIDC_EXTRADATA_PQ_INFO = 0x00000017, -#define MSM_VIDC_EXTRADATA_COLOUR_REMAPPING_INFO_SEI \ - MSM_VIDC_EXTRADATA_COLOUR_REMAPPING_INFO_SEI - MSM_VIDC_EXTRADATA_COLOUR_REMAPPING_INFO_SEI = 0x00000018, -#define MSM_VIDC_EXTRADATA_UBWC_CR_STAT_INFO \ - MSM_VIDC_EXTRADATA_UBWC_CR_STAT_INFO - MSM_VIDC_EXTRADATA_UBWC_CR_STAT_INFO = 0x00000019, - MSM_VIDC_EXTRADATA_INPUT_CROP = 0x0700000E, -#define MSM_VIDC_EXTRADATA_OUTPUT_CROP \ - MSM_VIDC_EXTRADATA_OUTPUT_CROP - MSM_VIDC_EXTRADATA_OUTPUT_CROP = 0x0700000F, - MSM_VIDC_EXTRADATA_DIGITAL_ZOOM = 0x07000010, - MSM_VIDC_EXTRADATA_MULTISLICE_INFO = 0x7F100000, - MSM_VIDC_EXTRADATA_NUM_CONCEALED_MB = 0x7F100001, - MSM_VIDC_EXTRADATA_INDEX = 0x7F100002, - MSM_VIDC_EXTRADATA_ASPECT_RATIO = 0x7F100003, - MSM_VIDC_EXTRADATA_METADATA_LTR = 0x7F100004, - MSM_VIDC_EXTRADATA_METADATA_FILLER = 0x7FE00002, - MSM_VIDC_EXTRADATA_METADATA_MBI = 0x7F100005, -#define MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO \ - MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO - MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO = 0x7F100006, - MSM_VIDC_EXTRADATA_YUVSTATS_INFO = 0x7F100007, -}; -enum msm_vidc_interlace_type { - MSM_VIDC_INTERLACE_FRAME_PROGRESSIVE = 0x01, - MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST = 0x02, - MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04, - MSM_VIDC_INTERLACE_FRAME_TOPFIELDFIRST = 0x08, - MSM_VIDC_INTERLACE_FRAME_BOTTOMFIELDFIRST = 0x10, -}; - -/* enum msm_vidc_framepack_type */ -#define MSM_VIDC_FRAMEPACK_CHECKERBOARD 0x00 -#define MSM_VIDC_FRAMEPACK_COLUMN_INTERLEAVE 0x01 -#define MSM_VIDC_FRAMEPACK_ROW_INTERLEAVE 0x02 -#define MSM_VIDC_FRAMEPACK_SIDE_BY_SIDE 0x03 -#define MSM_VIDC_FRAMEPACK_TOP_BOTTOM 0x04 -#define MSM_VIDC_FRAMEPACK_TEMPORAL_INTERLEAVE 0x05 - -enum msm_vidc_recovery_sei { - MSM_VIDC_FRAME_RECONSTRUCTION_INCORRECT = 0x0, - MSM_VIDC_FRAME_RECONSTRUCTION_CORRECT = 0x01, - MSM_VIDC_FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT = 0x02, -}; -enum msm_vidc_userdata_type { - MSM_VIDC_USERDATA_TYPE_FRAME = 0x1, - MSM_VIDC_USERDATA_TYPE_TOP_FIELD = 0x2, - MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD = 0x3, -}; - -/* See colour_primaries of ISO/IEC 14496 for significance */ -enum msm_vidc_h264_color_primaries_values { - MSM_VIDC_RESERVED_1 = 0, - MSM_VIDC_BT709_5 = 1, - MSM_VIDC_UNSPECIFIED = 2, - MSM_VIDC_RESERVED_2 = 3, - MSM_VIDC_BT470_6_M = 4, - MSM_VIDC_BT601_6_625 = 5, - MSM_VIDC_BT470_6_BG = MSM_VIDC_BT601_6_625, - MSM_VIDC_BT601_6_525 = 6, - MSM_VIDC_SMPTE_240M = 7, - MSM_VIDC_GENERIC_FILM = 8, - MSM_VIDC_BT2020 = 9, -}; - -enum msm_vidc_vp9_color_primaries_values { - MSM_VIDC_CS_UNKNOWN, - MSM_VIDC_CS_BT_601, - MSM_VIDC_CS_BT_709, - MSM_VIDC_CS_SMPTE_170, - MSM_VIDC_CS_SMPTE_240, - MSM_VIDC_CS_BT_2020, - MSM_VIDC_CS_RESERVED, - MSM_VIDC_CS_RGB, -}; - -enum msm_vidc_h264_matrix_coeff_values { - MSM_VIDC_MATRIX_RGB = 0, - MSM_VIDC_MATRIX_BT_709_5 = 1, - MSM_VIDC_MATRIX_UNSPECIFIED = 2, - MSM_VIDC_MATRIX_RESERVED = 3, - MSM_VIDC_MATRIX_FCC_47 = 4, - MSM_VIDC_MATRIX_601_6_625 = 5, - MSM_VIDC_MATRIX_BT470_BG = MSM_VIDC_MATRIX_601_6_625, - MSM_VIDC_MATRIX_601_6_525 = 6, - MSM_VIDC_MATRIX_SMPTE_170M = MSM_VIDC_MATRIX_601_6_525, - MSM_VIDC_MATRIX_SMPTE_240M = 7, - MSM_VIDC_MATRIX_Y_CG_CO = 8, - MSM_VIDC_MATRIX_BT_2020 = 9, - MSM_VIDC_MATRIX_BT_2020_CONST = 10, -}; - -enum msm_vidc_h264_transfer_chars_values { - MSM_VIDC_TRANSFER_RESERVED_1 = 0, - MSM_VIDC_TRANSFER_BT709_5 = 1, - MSM_VIDC_TRANSFER_UNSPECIFIED = 2, - MSM_VIDC_TRANSFER_RESERVED_2 = 3, - MSM_VIDC_TRANSFER_BT_470_6_M = 4, - MSM_VIDC_TRANSFER_BT_470_6_BG = 5, - MSM_VIDC_TRANSFER_601_6_625 = 6, - MSM_VIDC_TRANSFER_601_6_525 = MSM_VIDC_TRANSFER_601_6_625, - MSM_VIDC_TRANSFER_SMPTE_240M = 7, - MSM_VIDC_TRANSFER_LINEAR = 8, - MSM_VIDC_TRANSFER_LOG_100_1 = 9, - MSM_VIDC_TRANSFER_LOG_100_SQRT10_1 = 10, - MSM_VIDC_TRANSFER_IEC_61966 = 11, - MSM_VIDC_TRANSFER_BT_1361 = 12, - MSM_VIDC_TRANSFER_SRGB = 13, - MSM_VIDC_TRANSFER_BT_2020_10 = 14, - MSM_VIDC_TRANSFER_BT_2020_12 = 15, -#define MSM_VIDC_TRANSFER_SMPTE_ST2084 \ - MSM_VIDC_TRANSFER_SMPTE_ST2084 - MSM_VIDC_TRANSFER_SMPTE_ST2084 = 16, -#define MSM_VIDC_TRANSFER_SMPTE_ST428_1 \ - MSM_VIDC_TRANSFER_SMPTE_ST428_1 - MSM_VIDC_TRANSFER_SMPTE_ST428_1 = 17, -#define MSM_VIDC_TRANSFER_HLG \ - MSM_VIDC_TRANSFER_HLG - MSM_VIDC_TRANSFER_HLG = 18, -}; - -enum msm_vidc_pixel_depth { - MSM_VIDC_BIT_DEPTH_8, - MSM_VIDC_BIT_DEPTH_10, - MSM_VIDC_BIT_DEPTH_UNSUPPORTED = 0XFFFFFFFF, -}; - -enum msm_vidc_video_format { - MSM_VIDC_COMPONENT, - MSM_VIDC_PAL, - MSM_VIDC_NTSC, - MSM_VIDC_SECAM, - MSM_VIDC_MAC, - MSM_VIDC_UNSPECIFIED_FORMAT, - MSM_VIDC_RESERVED_1_FORMAT, - MSM_VIDC_RESERVED_2_FORMAT, -}; - -enum msm_vidc_color_desc_flag { - MSM_VIDC_COLOR_DESC_NOT_PRESENT, - MSM_VIDC_COLOR_DESC_PRESENT, -}; - -/*enum msm_vidc_pic_struct */ -#define MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED 0x0 -#define MSM_VIDC_PIC_STRUCT_PROGRESSIVE 0x1 - -/*default when layer ID isn't specified*/ -#define MSM_VIDC_ALL_LAYER_ID 0xFF - void *msm_vidc_open(int core_id, int session_type); int msm_vidc_close(void *instance); int msm_vidc_suspend(int core_id); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c index 3576437f2e1a965b963462ba18c784f97e8df06a..4b30929ea670119781363259319309dcd9d80914 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c @@ -65,7 +65,7 @@ static inline unsigned long int get_ubwc_compression_ratio( return compression_ratio; } -static inline int msm_vidc_get_mbs_per_frame(struct msm_vidc_inst *inst) +int msm_vidc_get_mbs_per_frame(struct msm_vidc_inst *inst) { int height, width; @@ -270,7 +270,7 @@ int msm_comm_vote_bus(struct msm_vidc_core *core) vote_data[i].fps = inst->prop.fps; vote_data[i].power_mode = 0; - if (!msm_vidc_clock_scaling || is_turbo || + if (msm_vidc_clock_voting || is_turbo || inst->clk_data.buffer_counter < DCVS_FTB_WINDOW) vote_data[i].power_mode = VIDC_POWER_TURBO; @@ -370,8 +370,9 @@ static int msm_dcvs_scale_clocks(struct msm_vidc_inst *inst) return -EINVAL; } - if (!inst->clk_data.dcvs_mode) { - dprintk(VIDC_DBG, "DCVS is not enabled\n"); + if (!inst->clk_data.dcvs_mode || inst->batch.enable) { + dprintk(VIDC_DBG, "Skip DCVS (dcvs %d, batching %d)\n", + inst->clk_data.dcvs_mode, inst->batch.enable); return 0; } @@ -686,6 +687,14 @@ int msm_vidc_set_clocks(struct msm_vidc_core *core) freq_core_max = max_t(unsigned long, freq_core_1, freq_core_2); + if (msm_vidc_clock_voting) { + dprintk(VIDC_PROF, + "msm_vidc_clock_voting %d\n", + msm_vidc_clock_voting); + freq_core_max = msm_vidc_clock_voting; + break; + } + if (temp->clk_data.turbo_mode) { dprintk(VIDC_PROF, "Found an instance with Turbo request\n"); @@ -758,7 +767,7 @@ int msm_vidc_validate_operating_rate(struct msm_vidc_inst *inst, operating_rate = operating_rate >> 16; if ((curr_operating_rate * (1 + ops_left)) >= operating_rate || - !msm_vidc_clock_scaling || + msm_vidc_clock_voting || inst->clk_data.buffer_counter < DCVS_FTB_WINDOW) { dprintk(VIDC_DBG, "Requestd operating rate is valid %u\n", @@ -820,7 +829,7 @@ int msm_comm_scale_clocks(struct msm_vidc_inst *inst) inst->clk_data.min_freq = freq; if (inst->clk_data.buffer_counter < DCVS_FTB_WINDOW || - !msm_vidc_clock_scaling) + msm_vidc_clock_voting) inst->clk_data.min_freq = msm_vidc_max_freq(inst->core); else inst->clk_data.min_freq = freq; @@ -861,18 +870,18 @@ int msm_dcvs_try_enable(struct msm_vidc_inst *inst) return -EINVAL; } - if (!msm_vidc_clock_scaling || + if (msm_vidc_clock_voting || inst->flags & VIDC_THUMBNAIL || - inst->clk_data.low_latency_mode) { - dprintk(VIDC_PROF, - "This session doesn't need DCVS : %pK\n", - inst); + inst->clk_data.low_latency_mode || + inst->batch.enable) { + dprintk(VIDC_PROF, "DCVS disabled: %pK\n", inst); inst->clk_data.extra_capture_buffer_count = 0; inst->clk_data.extra_output_buffer_count = 0; inst->clk_data.dcvs_mode = false; return false; } inst->clk_data.dcvs_mode = true; + dprintk(VIDC_PROF, "DCVS enabled: %pK\n", inst); inst->clk_data.extra_capture_buffer_count = DCVS_DEC_EXTRA_OUTPUT_BUFFERS; @@ -988,14 +997,35 @@ void msm_clock_data_reset(struct msm_vidc_inst *inst) int msm_vidc_get_extra_buff_count(struct msm_vidc_inst *inst, enum hal_buffer buffer_type) { - if (!inst) { + int count = 0; + + if (!inst || !inst->core) { dprintk(VIDC_ERR, "%s Invalid args\n", __func__); return 0; } + /* + * no extra buffers for thumbnail session because + * neither dcvs nor batching will be enabled + */ + if (is_thumbnail_session(inst)) + return 0; - return buffer_type == HAL_BUFFER_INPUT ? + count = buffer_type == HAL_BUFFER_INPUT ? inst->clk_data.extra_output_buffer_count : inst->clk_data.extra_capture_buffer_count; + + /* + * if platform supports decode batching ensure minimum + * batch size count of extra buffers added on output port + */ + if (buffer_type == HAL_BUFFER_OUTPUT) { + if (inst->core->resources.decode_batching && + is_decode_session(inst) && + count < inst->batch.size) + count = inst->batch.size; + } + + return count; } int msm_vidc_decide_work_route(struct msm_vidc_inst *inst) @@ -1308,11 +1338,11 @@ int msm_vidc_decide_core_and_power_mode(struct msm_vidc_inst *inst) min_lp_load = core0_lp_load; } - current_inst_load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS) * - inst->clk_data.entry->vpp_cycles; + current_inst_load = (msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS) * + inst->clk_data.entry->vpp_cycles)/inst->clk_data.work_route; - current_inst_lp_load = msm_comm_get_inst_load(inst, - LOAD_CALC_NO_QUIRKS) * lp_cycles; + current_inst_lp_load = (msm_comm_get_inst_load(inst, + LOAD_CALC_NO_QUIRKS) * lp_cycles)/inst->clk_data.work_route; dprintk(VIDC_DBG, "Core 0 RT Load = %d Core 1 RT Load = %d\n", core0_load, core1_load); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h index aef9021154b4d40d3fa296c008870519f1a4da4e..8020bc1c0065489f5e5a4fb338af3f82d9320ee8 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h @@ -29,6 +29,7 @@ int msm_vidc_get_extra_buff_count(struct msm_vidc_inst *inst, int msm_vidc_set_clocks(struct msm_vidc_core *core); int msm_comm_vote_bus(struct msm_vidc_core *core); int msm_dcvs_try_enable(struct msm_vidc_inst *inst); +int msm_vidc_get_mbs_per_frame(struct msm_vidc_inst *inst); int msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst); int msm_comm_init_clocks_and_bus_data(struct msm_vidc_inst *inst); void msm_comm_free_freq_table(struct msm_vidc_inst *inst); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 5ace08c854fec7b8ac4876a412f13acba286bc20..a9bf66952f61462093a62b3a6de2d64fba1f4bdc 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -74,36 +74,6 @@ const char *const mpeg_video_vidc_extradata[] = { static void handle_session_error(enum hal_command_response cmd, void *data); static void msm_vidc_print_running_insts(struct msm_vidc_core *core); -bool msm_comm_turbo_session(struct msm_vidc_inst *inst) -{ - return !!(inst->flags & VIDC_TURBO); -} - -static inline bool is_thumbnail_session(struct msm_vidc_inst *inst) -{ - return !!(inst->flags & VIDC_THUMBNAIL); -} - -static inline bool is_low_power_session(struct msm_vidc_inst *inst) -{ - return !!(inst->flags & VIDC_LOW_POWER); -} - -static inline bool is_realtime_session(struct msm_vidc_inst *inst) -{ - return !!(inst->flags & VIDC_REALTIME); -} - -int msm_comm_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) -{ - return v4l2_g_ctrl(&inst->ctrl_handler, ctrl); -} - -int msm_comm_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) -{ - return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl); -} - int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id) { int rc = 0; @@ -795,7 +765,7 @@ int msm_comm_get_inst_load(struct msm_vidc_inst *inst, load = 0; } - if (msm_comm_turbo_session(inst)) { + if (is_turbo_session(inst)) { if (!(quirks & LOAD_CALC_IGNORE_TURBO_LOAD)) load = inst->core->resources.max_load; } @@ -2773,6 +2743,35 @@ static bool is_thermal_permissible(struct msm_vidc_core *core) return true; } +bool is_batching_allowed(struct msm_vidc_inst *inst) +{ + bool allowed = false; + + if (!inst || !inst->core) + return false; + + /* + * Enable decode batching based on below conditions + * - decode session + * - platform supports batching + * - session resolution <= 1080p + * - low latency not enabled + * - not a thumbnail session + * - realtime session + * - UBWC color format + */ + if (is_decode_session(inst) && inst->core->resources.decode_batching && + (msm_vidc_get_mbs_per_frame(inst) <= + MAX_DEC_BATCH_WIDTH * MAX_DEC_BATCH_HEIGHT) && + !inst->clk_data.low_latency_mode && + !is_thumbnail_session(inst) && is_realtime_session(inst) && + (inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_NV12_UBWC || + inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_NV12_TP10_UBWC)) + allowed = true; + + return allowed; +} + static int msm_comm_session_abort(struct msm_vidc_inst *inst) { int rc = 0, abort_completion = 0; @@ -3107,7 +3106,7 @@ static void msm_vidc_print_running_insts(struct msm_vidc_core *core) if (is_thumbnail_session(temp)) strlcat(properties, "N", sizeof(properties)); - if (msm_comm_turbo_session(temp)) + if (is_turbo_session(temp)) strlcat(properties, "T", sizeof(properties)); dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%4s\n", @@ -3977,6 +3976,30 @@ enum hal_buffer get_hal_buffer_type(unsigned int type, } } +static int num_pending_qbufs(struct msm_vidc_inst *inst, u32 type) +{ + int count = 0; + struct msm_vidc_buffer *mbuf; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return 0; + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(mbuf, &inst->registeredbufs.list, list) { + if (mbuf->vvb.vb2_buf.type != type) + continue; + /* Count only deferred buffers */ + if (!(mbuf->flags & MSM_VIDC_FLAG_DEFERRED)) + continue; + count++; + } + mutex_unlock(&inst->registeredbufs.lock); + + return count; +} + static int msm_comm_qbuf_to_hfi(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf) { @@ -4114,10 +4137,87 @@ int msm_comm_qbufs(struct msm_vidc_inst *inst) return rc; } +/* + * msm_comm_qbuf_decode_batch - count the buffers which are not queued to + * firmware yet (count includes rbr pending buffers too) and + * queue the buffers at once if full batch count reached. + * Don't queue rbr pending buffers as they would be queued + * when rbr event arrived from firmware. + */ +int msm_comm_qbuf_decode_batch(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf) +{ + int rc = 0; + u32 count = 0; + struct msm_vidc_buffer *buf; + + if (!inst || !mbuf) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, "%s: inst is in bad state\n", __func__); + return -EINVAL; + } + + if (inst->state != MSM_VIDC_START_DONE) { + mbuf->flags |= MSM_VIDC_FLAG_DEFERRED; + print_vidc_buffer(VIDC_DBG, "qbuf deferred", inst, mbuf); + return 0; + } + + /* + * Don't batch for initial few buffers to avoid startup latency increase + * due to batching + */ + if (inst->count.fbd < 30) + return msm_comm_qbuf(inst, mbuf); + + count = num_pending_qbufs(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (count < inst->batch.size) { + mbuf->flags |= MSM_VIDC_FLAG_DEFERRED; + print_vidc_buffer(VIDC_DBG, "qbuf_batch deferred", inst, mbuf); + return 0; + } + + rc = msm_comm_scale_clocks_and_bus(inst); + if (rc) + dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__); + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(buf, &inst->registeredbufs.list, list) { + /* Don't queue if buffer is not CAPTURE_MPLANE */ + if (!(buf->vvb.vb2_buf.type & + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) + continue; + /* Don't queue if buffer is not a deferred buffer */ + if (!(buf->flags & MSM_VIDC_FLAG_DEFERRED)) + continue; + /* Don't queue if RBR event is pending on this buffer */ + if (buf->flags & MSM_VIDC_FLAG_RBR_PENDING) + continue; + print_vidc_buffer(VIDC_DBG, "qbuf", inst, buf); + rc = msm_comm_qbuf_to_hfi(inst, buf); + if (rc) { + dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n", + __func__, rc); + break; + } + /* Queue pending buffers till the current buffer only */ + if (buf == mbuf) + break; + } + mutex_unlock(&inst->registeredbufs.lock); + + return rc; +} + int msm_vidc_update_host_buff_counts(struct msm_vidc_inst *inst) { int extra_buffers; struct hal_buffer_requirements *bufreq; + struct hal_buffer_requirements *bufreq_extra; bufreq = get_buff_req_buffer(inst, HAL_BUFFER_INPUT); @@ -4130,11 +4230,24 @@ int msm_vidc_update_host_buff_counts(struct msm_vidc_inst *inst) extra_buffers = msm_vidc_get_extra_buff_count(inst, HAL_BUFFER_INPUT); bufreq->buffer_count_min_host = bufreq->buffer_count_min + extra_buffers; - bufreq = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_INPUT); - if (bufreq) { - if (bufreq->buffer_count_min) - bufreq->buffer_count_min_host = - bufreq->buffer_count_min + extra_buffers; + + /* decode batching needs minimum batch size count of input buffers */ + if (is_decode_session(inst) && !is_thumbnail_session(inst) && + inst->core->resources.decode_batching && + bufreq->buffer_count_min_host < inst->batch.size) + bufreq->buffer_count_min_host = inst->batch.size; + + /* adjust min_host count for VP9 decoder */ + if (is_decode_session(inst) && !is_thumbnail_session(inst) && + inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_VP9 && + bufreq->buffer_count_min_host < MIN_NUM_OUTPUT_BUFFERS_VP9) + bufreq->buffer_count_min_host = MIN_NUM_OUTPUT_BUFFERS_VP9; + + bufreq_extra = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_INPUT); + if (bufreq_extra) { + if (bufreq_extra->buffer_count_min) + bufreq_extra->buffer_count_min_host = + bufreq->buffer_count_min_host; } if (msm_comm_get_stream_output_mode(inst) == @@ -6065,15 +6178,9 @@ struct msm_vidc_buffer *msm_comm_get_vidc_buffer(struct msm_vidc_inst *inst, if (found_plane0) rc = -EEXIST; } - /* - * If RBR pending on this buffer then enable RBR_PENDING flag - * and clear the DEFERRED flag to avoid this buffer getting - * queued to video hardware in msm_comm_qbuf() which tries to - * queue all the DEFERRED buffers. - */ if (rc == -EEXIST) { + /* enable RBR pending */ mbuf->flags |= MSM_VIDC_FLAG_RBR_PENDING; - mbuf->flags &= ~MSM_VIDC_FLAG_DEFERRED; } } diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h index 1261fe68c627f1c5c13af66551069eefb6b574e2..423cae63259045ef3f2edefa8554d3e225448092 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h @@ -15,6 +15,10 @@ #define _MSM_VIDC_COMMON_H_ #include "msm_vidc_internal.h" +#define MAX_DEC_BATCH_SIZE 6 +#define MAX_DEC_BATCH_WIDTH 1920 +#define MAX_DEC_BATCH_HEIGHT 1088 + struct vb2_buf_entry { struct list_head list; struct vb2_buffer *vb; @@ -34,6 +38,48 @@ enum load_calc_quirks { LOAD_CALC_IGNORE_NON_REALTIME_LOAD = 1 << 2, }; +static inline bool is_turbo_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_TURBO); +} + +static inline bool is_thumbnail_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_THUMBNAIL); +} + +static inline bool is_low_power_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_LOW_POWER); +} + +static inline bool is_realtime_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_REALTIME); +} + +static inline bool is_decode_session(struct msm_vidc_inst *inst) +{ + return inst->session_type == MSM_VIDC_DECODER; +} + +static inline bool is_encode_session(struct msm_vidc_inst *inst) +{ + return inst->session_type == MSM_VIDC_ENCODER; +} + +static inline int msm_comm_g_ctrl(struct msm_vidc_inst *inst, + struct v4l2_control *ctrl) +{ + return v4l2_g_ctrl(&inst->ctrl_handler, ctrl); +} + +static inline int msm_comm_s_ctrl(struct msm_vidc_inst *inst, + struct v4l2_control *ctrl) +{ + return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl); +} +bool is_batching_allowed(struct msm_vidc_inst *inst); enum hal_buffer get_hal_buffer_type(unsigned int type, unsigned int plane_num); void put_inst(struct msm_vidc_inst *inst); @@ -172,5 +218,6 @@ void msm_comm_store_mark_data(struct msm_vidc_list *data_list, void msm_comm_fetch_mark_data(struct msm_vidc_list *data_list, u32 index, u32 *mark_data, u32 *mark_target); int msm_comm_release_mark_data(struct msm_vidc_inst *inst); - +int msm_comm_qbuf_decode_batch(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *mbuf); #endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c index 2b4930f95367e4c958c1630f3362655c7188c46a..03af199ebb7c02368b24b515e1c98fb4968cb878 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -27,7 +27,7 @@ int msm_vidc_fw_debug_mode = 1; int msm_vidc_fw_low_power_mode = 1; bool msm_vidc_fw_coverage = !true; bool msm_vidc_thermal_mitigation_disabled = !true; -bool msm_vidc_clock_scaling = true; +int msm_vidc_clock_voting = !1; bool msm_vidc_syscache_disable = !true; #define MAX_DBG_BUF_SIZE 4096 @@ -200,8 +200,8 @@ struct dentry *msm_vidc_debugfs_init_drv(void) __debugfs_create(u32, "debug_output", &msm_vidc_debug_out) && __debugfs_create(bool, "disable_thermal_mitigation", &msm_vidc_thermal_mitigation_disabled) && - __debugfs_create(bool, "clock_scaling", - &msm_vidc_clock_scaling) && + __debugfs_create(u32, "core_clock_voting", + &msm_vidc_clock_voting) && __debugfs_create(bool, "disable_video_syscache", &msm_vidc_syscache_disable); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h index d6cfda575b3c083f9b9f8455e75f2495a203b8d9..898621d5e688dc7a89e8f921ec1f31b2f4fa6831 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h @@ -58,7 +58,7 @@ extern int msm_vidc_fw_debug_mode; extern int msm_vidc_fw_low_power_mode; extern bool msm_vidc_fw_coverage; extern bool msm_vidc_thermal_mitigation_disabled; -extern bool msm_vidc_clock_scaling; +extern int msm_vidc_clock_voting; extern bool msm_vidc_syscache_disable; #define dprintk(__level, __fmt, arg...) \ @@ -73,8 +73,8 @@ extern bool msm_vidc_syscache_disable; } while (0) #define MSM_VIDC_ERROR(value) \ - do { \ - dprintk(VIDC_DBG, "BugOn"); \ + do { if (value) \ + dprintk(VIDC_DBG, "BugOn"); \ BUG_ON(value); \ } while (0) diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h index f63056384d74e1d1ec07b5bf210e16ac8fc9e661..29b4d821e52d91ed865a854b87d1f56e59efadea 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h @@ -287,6 +287,11 @@ struct buf_count { int ebd; }; +struct batch_mode { + bool enable; + u32 size; +}; + struct clock_data { int buffer_counter; int load; @@ -407,7 +412,7 @@ struct msm_vidc_inst { u32 buffer_size_limit; enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM]; struct v4l2_ctrl **ctrls; - enum msm_vidc_pixel_depth bit_depth; + int bit_depth; struct kref kref; bool in_flush; u32 pic_struct; @@ -417,6 +422,7 @@ struct msm_vidc_inst { u32 entropy_mode; struct msm_vidc_codec_data *codec_data; struct hal_hdr10_pq_sei hdr10_sei_params; + struct batch_mode batch; }; extern struct msm_vidc_drv *vidc_driver; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_platform.c b/drivers/media/platform/msm/vidc/msm_vidc_platform.c index 2705d3ea560d8a808f2b8737fc609f24d21e667e..1301580a89a7b1ef5018c97e269212c8cbe6f498 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_platform.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c @@ -133,7 +133,7 @@ static struct msm_vidc_common_data sm8150_common_data[] = { }, { .key = "qcom,max-hw-load", - .value = 4147200, /* 4096x2160@120 */ + .value = 4147200, /* 4096x2160/256 MBs@120fps */ }, { .key = "qcom,max-hq-mbs-per-frame", @@ -167,6 +167,10 @@ static struct msm_vidc_common_data sm8150_common_data[] = { .key = "qcom,domain-cvp", .value = 1, }, + { + .key = "qcom,decode-batching", + .value = 1, + }, }; static struct msm_vidc_common_data sdm845_common_data[] = { diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c index 4ebed83ee1c4aa9d5d2a737cf0be24efc7e270b7..a1bf73744cb3a18e78741cb2f935a6bfeb025199 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c @@ -790,6 +790,8 @@ int read_platform_resources_from_drv_data( "qcom,domain-attr-non-fatal-faults"); res->cache_pagetables = find_key_value(platform_data, "qcom,domain-attr-cache-pagetables"); + res->decode_batching = find_key_value(platform_data, + "qcom,decode-batching"); res->csc_coeff_data = &platform_data->csc_data; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h index b6bd509da1cc677b210800996931f2d973b5e694..a32a94ce2acdfc3e1eeb479964b021af991a6b36 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h @@ -192,6 +192,7 @@ struct msm_vidc_platform_resources { bool domain_cvp; bool non_fatal_pagefaults; bool cache_pagetables; + bool decode_batching; struct msm_vidc_codec_data *codec_data; int codec_data_count; struct msm_vidc_csc_coeff *csc_coeff_data; diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 862328c065d5f55f6bd81493e01dfd6bbd8b31e9..277ebf2df272b5e497807ba1612626cba571dd39 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "hfi_packetization.h" #include "msm_vidc_debug.h" #include "venus_hfi.h" @@ -104,6 +105,7 @@ static int __enable_subcaches(struct venus_hfi_device *device); static int __set_subcaches(struct venus_hfi_device *device); static int __release_subcaches(struct venus_hfi_device *device); static int __disable_subcaches(struct venus_hfi_device *device); +static int __power_collapse(struct venus_hfi_device *device, bool force); static int venus_hfi_noc_error_info(void *dev); /** @@ -252,6 +254,216 @@ static void __sim_modify_cmd_packet(u8 *packet, struct venus_hfi_device *device) } } +static int __dsp_send_hfi_queue(struct venus_hfi_device *device) +{ + int rc; + + if (!device->res->domain_cvp) + return 0; + + if (!device->dsp_iface_q_table.mem_data.dma_handle) { + dprintk(VIDC_ERR, "%s: invalid dsm_handle\n", __func__); + return -EINVAL; + } + + if (device->dsp_flags & DSP_INIT) { + dprintk(VIDC_DBG, "%s: dsp already inited\n"); + return 0; + } + + dprintk(VIDC_DBG, "%s: hfi queue %#x size %d\n", + __func__, device->dsp_iface_q_table.mem_data.dma_handle, + device->dsp_iface_q_table.mem_data.size); + rc = fastcvpd_video_send_cmd_hfi_queue( + (phys_addr_t *)device->dsp_iface_q_table.mem_data.dma_handle, + device->dsp_iface_q_table.mem_data.size); + if (rc) { + dprintk(VIDC_ERR, "%s: dsp init failed\n", __func__); + return rc; + } + + device->dsp_flags |= DSP_INIT; + dprintk(VIDC_DBG, "%s: dsp inited\n", __func__); + return rc; +} + +static int __dsp_suspend(struct venus_hfi_device *device, bool force, u32 flags) +{ + int rc; + struct hal_session *temp; + + if (!device->res->domain_cvp) + return 0; + + if (!(device->dsp_flags & DSP_INIT)) + return 0; + + if (device->dsp_flags & DSP_SUSPEND) + return 0; + + list_for_each_entry(temp, &device->sess_head, list) { + /* if forceful suspend, don't check session pause info */ + if (force) + continue; + if (temp->domain == HAL_VIDEO_DOMAIN_CVP) { + /* don't suspend if cvp session is not paused */ + if (!(temp->flags & SESSION_PAUSE)) { + dprintk(VIDC_DBG, + "%s: cvp session %x not paused\n", + __func__, hash32_ptr(temp)); + return -EBUSY; + } + } + } + + dprintk(VIDC_DBG, "%s: suspend dsp\n", __func__); + rc = fastcvpd_video_suspend(flags); + if (rc) { + dprintk(VIDC_ERR, "%s: dsp suspend failed with error %d\n", + __func__, rc); + return -EINVAL; + } + + device->dsp_flags |= DSP_SUSPEND; + dprintk(VIDC_DBG, "%s: dsp suspended\n", __func__); + return 0; +} + +static int __dsp_resume(struct venus_hfi_device *device, u32 flags) +{ + int rc; + + if (!device->res->domain_cvp) + return 0; + + if (!(device->dsp_flags & DSP_SUSPEND)) { + dprintk(VIDC_DBG, "%s: dsp not suspended\n", __func__); + return 0; + } + + dprintk(VIDC_DBG, "%s: resume dsp\n", __func__); + rc = fastcvpd_video_resume(flags); + if (rc) { + dprintk(VIDC_ERR, + "%s: dsp resume failed with error %d\n", + __func__, rc); + return rc; + } + + device->dsp_flags &= ~DSP_SUSPEND; + dprintk(VIDC_DBG, "%s: dsp resumed\n", __func__); + return rc; +} + +static int __dsp_shutdown(struct venus_hfi_device *device, u32 flags) +{ + int rc; + + if (!device->res->domain_cvp) + return 0; + + if (!(device->dsp_flags & DSP_INIT)) { + dprintk(VIDC_DBG, "%s: dsp not inited\n", __func__); + return 0; + } + + dprintk(VIDC_DBG, "%s: shutdown dsp\n", __func__); + rc = fastcvpd_video_shutdown(flags); + if (rc) { + dprintk(VIDC_ERR, + "%s: dsp shutdown failed with error %d\n", + __func__, rc); + WARN_ON(1); + } + + device->dsp_flags &= ~DSP_INIT; + dprintk(VIDC_DBG, "%s: dsp shutdown successful\n", __func__); + return rc; +} + +static int __session_pause(struct venus_hfi_device *device, + struct hal_session *session) +{ + int rc = 0; + + /* ignore if session paused already */ + if (session->flags & SESSION_PAUSE) + return 0; + + session->flags |= SESSION_PAUSE; + dprintk(VIDC_DBG, "%s: cvp session %x paused\n", __func__, + hash32_ptr(session)); + + return rc; +} + +static int __session_resume(struct venus_hfi_device *device, + struct hal_session *session) +{ + int rc = 0; + + /* ignore if session already resumed */ + if (!(session->flags & SESSION_PAUSE)) + return 0; + + session->flags &= ~SESSION_PAUSE; + dprintk(VIDC_DBG, "%s: cvp session %x resumed\n", __func__, + hash32_ptr(session)); + + rc = __resume(device); + if (rc) { + dprintk(VIDC_ERR, "%s: resume failed\n", __func__); + goto exit; + } + + if (device->dsp_flags & DSP_SUSPEND) { + dprintk(VIDC_ERR, "%s: dsp not resumed\n", __func__); + rc = -EINVAL; + goto exit; + } + +exit: + return rc; +} + +static int venus_hfi_session_pause(void *sess) +{ + int rc; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + device = session->device; + + mutex_lock(&device->lock); + rc = __session_pause(device, session); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_resume(void *sess) +{ + int rc; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } + device = session->device; + + mutex_lock(&device->lock); + rc = __session_resume(device, session); + mutex_unlock(&device->lock); + + return rc; +} + static int __acquire_regulator(struct regulator_info *rinfo, struct venus_hfi_device *device) { @@ -1098,14 +1310,18 @@ static int venus_hfi_suspend(void *dev) } dprintk(VIDC_DBG, "Suspending Venus\n"); - flush_delayed_work(&venus_hfi_pm_work); - mutex_lock(&device->lock); - if (device->power_enabled) { + rc = __power_collapse(device, true); + if (rc) { dprintk(VIDC_WARN, "%s: Venus is busy\n", __func__); rc = -EBUSY; } mutex_unlock(&device->lock); + + /* Cancel pending delayed works if any */ + if (!rc) + cancel_delayed_work(&venus_hfi_pm_work); + return rc; } @@ -1385,7 +1601,7 @@ static void __set_queue_hdr_defaults(struct hfi_queue_header *q_hdr) static void __interface_dsp_queues_release(struct venus_hfi_device *device) { int i; - struct msm_smem *mem_data = &device->iface_q_table.mem_data; + struct msm_smem *mem_data = &device->dsp_iface_q_table.mem_data; struct context_bank_info *cb = mem_data->mapping_info.cb_info; if (!device->dsp_iface_q_table.align_virtual_addr) { @@ -1393,7 +1609,7 @@ static void __interface_dsp_queues_release(struct venus_hfi_device *device) return; } - dma_unmap_single_attrs(cb->dev, mem_data->dma_handle, + dma_unmap_single_attrs(cb->dev, mem_data->device_addr, mem_data->size, DMA_BIDIRECTIONAL, 0); dma_free_coherent(device->res->mem_adsp.dev, mem_data->size, mem_data->kvaddr, mem_data->dma_handle); @@ -1957,6 +2173,7 @@ static int venus_hfi_core_init(void *device) __enable_subcaches(device); __set_subcaches(device); + __dsp_send_hfi_queue(device); if (dev->res->pm_qos_latency_us) { #ifdef CONFIG_SMP @@ -1998,6 +2215,8 @@ static int venus_hfi_core_release(void *dev) __resume(device); __set_state(device, VENUS_STATE_DEINIT); + __dsp_shutdown(device, 0); + __unload_fw(device); /* unlink all sessions from device */ @@ -2040,7 +2259,7 @@ static int __get_q_size(struct venus_hfi_device *dev, unsigned int q_index) static void __core_clear_interrupt(struct venus_hfi_device *device) { - u32 intr_status = 0; + u32 intr_status = 0, mask = 0; if (!device) { dprintk(VIDC_ERR, "%s: NULL device\n", __func__); @@ -2048,10 +2267,11 @@ static void __core_clear_interrupt(struct venus_hfi_device *device) } intr_status = __read_register(device, VIDC_WRAPPER_INTR_STATUS); + mask = (VIDC_WRAPPER_INTR_STATUS_A2H_BMSK | + VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK | + VIDC_CTRL_INIT_IDLE_MSG_BMSK); - if (intr_status & VIDC_WRAPPER_INTR_STATUS_A2H_BMSK || - intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK || - intr_status & VIDC_CTRL_INIT_IDLE_MSG_BMSK) { + if (intr_status & mask) { device->intr_status |= intr_status; device->reg_count++; dprintk(VIDC_DBG, @@ -2059,9 +2279,6 @@ static void __core_clear_interrupt(struct venus_hfi_device *device) device, device->reg_count, intr_status); } else { device->spur_count++; - dprintk(VIDC_INFO, - "SPURIOUS_INTR for device: %pK: times: %d interrupt_status: %d\n", - device, device->spur_count, intr_status); } __write_register(device, VIDC_CPU_CS_A2HSOFTINTCLR, 1); @@ -2988,9 +3205,6 @@ static int __prepare_pc(struct venus_hfi_device *device) static void venus_hfi_pm_handler(struct work_struct *work) { int rc = 0; - u32 wfi_status = 0, idle_status = 0, pc_ready = 0; - int count = 0; - const int max_tries = 10; struct venus_hfi_device *device = list_first_entry( &hal_ctxt.dev_head, struct venus_hfi_device, list); @@ -3012,7 +3226,51 @@ static void venus_hfi_pm_handler(struct work_struct *work) __process_fatal_error(device); return; } + mutex_lock(&device->lock); + rc = __power_collapse(device, false); + mutex_unlock(&device->lock); + switch (rc) { + case 0: + device->skip_pc_count = 0; + /* Cancel pending delayed works if any */ + cancel_delayed_work(&venus_hfi_pm_work); + dprintk(VIDC_PROF, "%s: power collapse successful!\n", + __func__); + break; + case -EBUSY: + device->skip_pc_count = 0; + dprintk(VIDC_DBG, "%s: retry PC as dsp is busy\n", __func__); + queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, msecs_to_jiffies( + device->res->msm_vidc_pwr_collapse_delay)); + break; + case -EAGAIN: + device->skip_pc_count++; + dprintk(VIDC_WARN, "%s: retry power collapse (count %d)\n", + __func__, device->skip_pc_count); + queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, msecs_to_jiffies( + device->res->msm_vidc_pwr_collapse_delay)); + break; + default: + dprintk(VIDC_ERR, "%s: power collapse failed\n", __func__); + break; + } +} + +static int __power_collapse(struct venus_hfi_device *device, bool force) +{ + int rc = 0; + u32 wfi_status = 0, idle_status = 0, pc_ready = 0; + u32 flags = 0; + int count = 0; + const int max_tries = 10; + + if (!device) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return -EINVAL; + } if (!device->power_enabled) { dprintk(VIDC_DBG, "%s: Power already disabled\n", __func__); @@ -3023,8 +3281,15 @@ static void venus_hfi_pm_handler(struct work_struct *work) if (!rc) { dprintk(VIDC_WARN, "Core is in bad state, Skipping power collapse\n"); - goto skip_power_off; + return -EINVAL; } + + rc = __dsp_suspend(device, force, flags); + if (rc == -EBUSY) + goto exit; + else if (rc) + goto skip_power_off; + pc_ready = __read_register(device, VIDC_CTRL_STATUS) & VIDC_CTRL_STATUS_PC_READY; if (!pc_ready) { @@ -3077,23 +3342,14 @@ static void venus_hfi_pm_handler(struct work_struct *work) if (rc) dprintk(VIDC_ERR, "Failed __suspend\n"); - /* Cancel pending delayed works if any */ - cancel_delayed_work(&venus_hfi_pm_work); - device->skip_pc_count = 0; - - mutex_unlock(&device->lock); - return; +exit: + return rc; skip_power_off: - device->skip_pc_count++; - dprintk(VIDC_WARN, "Skip PC(%d, %#x, %#x, %#x)\n", - device->skip_pc_count, wfi_status, idle_status, pc_ready); - queue_delayed_work(device->venus_pm_workq, - &venus_hfi_pm_work, - msecs_to_jiffies( - device->res->msm_vidc_pwr_collapse_delay)); -exit: - mutex_unlock(&device->lock); + dprintk(VIDC_WARN, "Skip PC(%#x, %#x, %#x)\n", + wfi_status, idle_status, pc_ready); + + return -EAGAIN; } static void __process_sys_error(struct venus_hfi_device *device) @@ -3162,7 +3418,14 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet) } else { struct hfi_msg_sys_debug_packet *pkt = (struct hfi_msg_sys_debug_packet *) packet; - dprintk(log_level, "%s", pkt->rg_msg_data); + /* + * All fw messages starts with new line character. This + * causes dprintk to print this message in two lines + * in the kernel log. Ignoring the first character + * from the message fixes this to print it in a single + * line. + */ + dprintk(log_level, "%s", &pkt->rg_msg_data[1]); } } @@ -3385,7 +3648,6 @@ static void venus_hfi_core_work_handler(struct work_struct *work) mutex_lock(&device->lock); - dprintk(VIDC_DBG, "Handling interrupt\n"); if (!__core_in_valid_state(device)) { dprintk(VIDC_DBG, "%s - Core not in init state\n", __func__); @@ -3430,7 +3692,6 @@ static void venus_hfi_core_work_handler(struct work_struct *work) if (!(intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) enable_irq(device->hal_data->irq); - dprintk(VIDC_DBG, "Handling interrupt done\n"); /* * XXX: Don't add any code beyond here. Reacquiring locks after release * it above doesn't guarantee the atomicity that we're aiming for. @@ -3443,7 +3704,6 @@ static irqreturn_t venus_hfi_isr(int irq, void *dev) { struct venus_hfi_device *device = dev; - dprintk(VIDC_INFO, "Received an interrupt %d\n", irq); disable_irq_nosync(irq); queue_work(device->vidc_workq, &venus_hfi_work); return IRQ_HANDLED; @@ -4413,6 +4673,7 @@ static inline int __suspend(struct venus_hfi_device *device) static inline int __resume(struct venus_hfi_device *device) { int rc = 0; + u32 flags = 0; if (!device) { dprintk(VIDC_ERR, "Invalid params: %pK\n", device); @@ -4465,6 +4726,7 @@ static inline int __resume(struct venus_hfi_device *device) __enable_subcaches(device); __set_subcaches(device); + __dsp_resume(device, flags); dprintk(VIDC_PROF, "Resumed from power collapse\n"); exit: @@ -4883,6 +5145,8 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev) hdev->session_flush = venus_hfi_session_flush; hdev->session_set_property = venus_hfi_session_set_property; hdev->session_get_property = venus_hfi_session_get_property; + hdev->session_pause = venus_hfi_session_pause; + hdev->session_resume = venus_hfi_session_resume; hdev->scale_clocks = venus_hfi_scale_clocks; hdev->vote_bus = venus_hfi_vote_buses; hdev->get_fw_info = venus_hfi_get_fw_info; diff --git a/drivers/media/platform/msm/vidc/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h index 6993a2aaf8bd49ab178d441451e3e96c7d35bd4d..ef9e2ec593205880110a5f3bdb57f67a5f2774d8 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.h +++ b/drivers/media/platform/msm/vidc/venus_hfi.h @@ -220,6 +220,11 @@ struct venus_resources { struct msm_vidc_fw fw; }; +enum dsp_flag { + DSP_INIT = BIT(0), + DSP_SUSPEND = BIT(1), +}; + enum venus_hfi_state { VENUS_STATE_DEINIT = 1, VENUS_STATE_INIT, @@ -245,6 +250,7 @@ struct venus_hfi_device { struct vidc_mem_addr mem_addr; struct vidc_iface_q_info iface_queues[VIDC_IFACEQ_NUMQ]; struct vidc_iface_q_info dsp_iface_queues[VIDC_IFACEQ_NUMQ]; + u32 dsp_flags; struct hal_data *hal_data; struct workqueue_struct *vidc_workq; struct workqueue_struct *venus_pm_workq; diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h index e953a09839952930e157ebd872fa2a024f30a760..b8e86b5ce07e0a772b1b8f04d987e3b1678c5394 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi.h @@ -829,12 +829,17 @@ struct hfi_cmd_session_continue_packet { u32 session_id; }; +enum session_flags { + SESSION_PAUSE = BIT(1), +}; + struct hal_session { struct list_head list; void *session_id; bool is_decoder; enum hal_video_codec codec; enum hal_domain domain; + u32 flags; void *device; }; diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 736ebdcdea1a14e96efb0527ecdf083f072fbf5b..5c690843748d7a4289893062e067d4bccc3f2692 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -145,6 +145,7 @@ enum hal_property { HAL_PARAM_VENC_SESSION_QP_RANGE, HAL_CONFIG_VENC_INTRA_PERIOD, HAL_CONFIG_VENC_IDR_PERIOD, + HAL_PARAM_VENC_ADAPTIVE_B, HAL_PARAM_VPE_ROTATION, HAL_PARAM_VENC_INTRA_REFRESH, HAL_PARAM_VENC_MULTI_SLICE_CONTROL, @@ -1284,7 +1285,7 @@ struct msm_vidc_cb_event { enum vidc_status status; u32 height; u32 width; - enum msm_vidc_pixel_depth bit_depth; + int bit_depth; u32 hal_event_type; u32 packet_buffer; u32 extra_data_buffer; @@ -1444,6 +1445,8 @@ struct hfi_device { int (*session_set_property)(void *sess, enum hal_property ptype, void *pdata); int (*session_get_property)(void *sess, enum hal_property ptype); + int (*session_pause)(void *sess); + int (*session_resume)(void *sess); int (*scale_clocks)(void *dev, u32 freq); int (*vote_bus)(void *dev, struct vidc_bus_vote_data *data, int num_data); diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index 467c56bb379588248a8f07c4a110b1baee73dbca..481c7bb61bce9d7820d4236ca62183aff0d156a8 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -327,6 +327,8 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x035) #define HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x036) +#define HFI_PROPERTY_PARAM_VENC_ADAPTIVE_B \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x037) #define HFI_PROPERTY_CONFIG_VENC_COMMON_START \ (HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000) diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index f0f423c7ca410bb64e034523e13adfd2b806e42f..6f6d4df1e8a884e8aca5a9427f3861d1ee9cd41d 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -858,7 +858,8 @@ int vidioc_g_edid(struct file *file, void *_fh, return -EINVAL; if (edid->start_block + edid->blocks > dev->edid_blocks) edid->blocks = dev->edid_blocks - edid->start_block; - cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); + if (adap) + cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128); return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 8b5cbb6b7a704651117960d2ff7bd95a24ca4e7d..e5d8d99e124c257716a6b00b502c97af8e980b60 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -508,7 +508,8 @@ static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm) return !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD); else - return !!(vsp1_read(vsp1, VI6_CMD(dlm->index) & VI6_CMD_UPDHDR)); + return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) + & VI6_CMD_UPDHDR); } static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index f7f3b4b2c2dea03f683bfa42b2f92a250de1f3f7..8bd6b2f1af1546e96bc067a7b410dac74fef2f3e 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -452,7 +452,7 @@ static void wpf_configure(struct vsp1_entity *entity, : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } - if (pipe->bru || pipe->num_inputs > 1) + if (pipe->bru) srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU ? VI6_WPF_SRCRPF_VIRACT_MST : VI6_WPF_SRCRPF_VIRACT2_MST; diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 71fb5734995b35c4ef2a15fb4fc1504c54916b38..68df16b3ce72030c0b566dc31f93a58d51a040b8 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -112,6 +112,8 @@ static int usbtv_probe(struct usb_interface *intf, return 0; usbtv_audio_fail: + /* we must not free at this point */ + usb_get_dev(usbtv->udev); usbtv_video_free(usbtv); usbtv_video_fail: diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 6d22b22cb35b64e4e1ee4993c8010372c307443b..28b91b7d756f5f42968da0e6322a740242eb0d54 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2230,7 +2230,7 @@ static int uvc_reset_resume(struct usb_interface *intf) * Module parameters */ -static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) +static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp) { if (uvc_clock_param == CLOCK_MONOTONIC) return sprintf(buffer, "CLOCK_MONOTONIC"); @@ -2238,7 +2238,7 @@ static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) return sprintf(buffer, "CLOCK_REALTIME"); } -static int uvc_clock_param_set(const char *val, struct kernel_param *kp) +static int uvc_clock_param_set(const char *val, const struct kernel_param *kp) { if (strncasecmp(val, "clock_", strlen("clock_")) == 0) val += strlen("clock_"); diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 4de9fc1e6de5e9ac29cc658b3a338ab49ec6c052..dae6273017ef4f0b147a810715897742dbb37045 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -101,7 +101,7 @@ static int get_v4l2_window32(struct v4l2_window __user *kp, static int put_v4l2_window32(struct v4l2_window __user *kp, struct v4l2_window32 __user *up) { - struct v4l2_clip __user *kclips = kp->clips; + struct v4l2_clip __user *kclips; struct v4l2_clip32 __user *uclips; compat_caddr_t p; u32 clipcount; @@ -116,6 +116,8 @@ static int put_v4l2_window32(struct v4l2_window __user *kp, if (!clipcount) return 0; + if (get_user(kclips, &kp->clips)) + return -EFAULT; if (get_user(p, &up->clips)) return -EFAULT; uclips = compat_ptr(p); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index c286fba04d3894f14f3bf2ba9b904afff92acdb7..d9d9c25256a40ef74ce8ae2789a6636f525612e3 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1367,6 +1367,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_SE401: descr = "GSPCA SE401"; break; case V4L2_PIX_FMT_S5C_UYVY_JPG: descr = "S5C73MX interleaved UYVY/JPEG"; break; case V4L2_PIX_FMT_MT21C: descr = "Mediatek Compressed Format"; break; + case V4L2_PIX_FMT_TME: + descr = "TME"; break; default: WARN(1, "Unknown pixelformat 0x%08x\n", fmt->pixelformat); if (fmt->description[0]) @@ -2563,11 +2565,8 @@ struct v4l2_ioctl_info { unsigned int ioctl; u32 flags; const char * const name; - union { - u32 offset; - int (*func)(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *p); - } u; + int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file, + void *fh, void *p); void (*debug)(const void *arg, bool write_only); }; @@ -2575,27 +2574,23 @@ struct v4l2_ioctl_info { #define INFO_FL_PRIO (1 << 0) /* This control can be valid if the filehandle passes a control handler. */ #define INFO_FL_CTRL (1 << 1) -/* This is a standard ioctl, no need for special code */ -#define INFO_FL_STD (1 << 2) /* This is ioctl has its own function */ -#define INFO_FL_FUNC (1 << 3) +#define INFO_FL_FUNC (1 << 2) /* Queuing ioctl */ -#define INFO_FL_QUEUE (1 << 4) +#define INFO_FL_QUEUE (1 << 3) /* Always copy back result, even on error */ -#define INFO_FL_ALWAYS_COPY (1 << 5) +#define INFO_FL_ALWAYS_COPY (1 << 4) /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ sizeof(((struct v4l2_struct *)0)->field)) << 16) #define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) -#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \ - [_IOC_NR(_ioctl)] = { \ - .ioctl = _ioctl, \ - .flags = _flags | INFO_FL_STD, \ - .name = #_ioctl, \ - .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \ - .debug = _debug, \ +#define DEFINE_IOCTL_STD_FNC(_vidioc) \ + static int __v4l_ ## _vidioc ## _fnc( \ + const struct v4l2_ioctl_ops *ops, \ + struct file *file, void *fh, void *p) { \ + return ops->_vidioc(file, fh, p); \ } #define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \ @@ -2603,10 +2598,42 @@ struct v4l2_ioctl_info { .ioctl = _ioctl, \ .flags = _flags | INFO_FL_FUNC, \ .name = #_ioctl, \ - .u.func = _func, \ + .func = _func, \ .debug = _debug, \ } +#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \ + IOCTL_INFO_FNC(_ioctl, __v4l_ ## _vidioc ## _fnc, _debug, _flags) + +DEFINE_IOCTL_STD_FNC(vidioc_g_fbuf) +DEFINE_IOCTL_STD_FNC(vidioc_s_fbuf) +DEFINE_IOCTL_STD_FNC(vidioc_expbuf) +DEFINE_IOCTL_STD_FNC(vidioc_g_std) +DEFINE_IOCTL_STD_FNC(vidioc_g_audio) +DEFINE_IOCTL_STD_FNC(vidioc_s_audio) +DEFINE_IOCTL_STD_FNC(vidioc_g_input) +DEFINE_IOCTL_STD_FNC(vidioc_g_edid) +DEFINE_IOCTL_STD_FNC(vidioc_s_edid) +DEFINE_IOCTL_STD_FNC(vidioc_g_output) +DEFINE_IOCTL_STD_FNC(vidioc_g_audout) +DEFINE_IOCTL_STD_FNC(vidioc_s_audout) +DEFINE_IOCTL_STD_FNC(vidioc_g_jpegcomp) +DEFINE_IOCTL_STD_FNC(vidioc_s_jpegcomp) +DEFINE_IOCTL_STD_FNC(vidioc_enumaudio) +DEFINE_IOCTL_STD_FNC(vidioc_enumaudout) +DEFINE_IOCTL_STD_FNC(vidioc_enum_framesizes) +DEFINE_IOCTL_STD_FNC(vidioc_enum_frameintervals) +DEFINE_IOCTL_STD_FNC(vidioc_g_enc_index) +DEFINE_IOCTL_STD_FNC(vidioc_encoder_cmd) +DEFINE_IOCTL_STD_FNC(vidioc_try_encoder_cmd) +DEFINE_IOCTL_STD_FNC(vidioc_decoder_cmd) +DEFINE_IOCTL_STD_FNC(vidioc_try_decoder_cmd) +DEFINE_IOCTL_STD_FNC(vidioc_s_dv_timings) +DEFINE_IOCTL_STD_FNC(vidioc_g_dv_timings) +DEFINE_IOCTL_STD_FNC(vidioc_enum_dv_timings) +DEFINE_IOCTL_STD_FNC(vidioc_query_dv_timings) +DEFINE_IOCTL_STD_FNC(vidioc_dv_timings_cap) + static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), @@ -2791,14 +2818,8 @@ static long __video_do_ioctl(struct file *file, } write_only = _IOC_DIR(cmd) == _IOC_WRITE; - if (info->flags & INFO_FL_STD) { - typedef int (*vidioc_op)(struct file *file, void *fh, void *p); - const void *p = vfd->ioctl_ops; - const vidioc_op *vidioc = p + info->u.offset; - - ret = (*vidioc)(file, fh, arg); - } else if (info->flags & INFO_FL_FUNC) { - ret = info->u.func(ops, file, fh, arg); + if (info->flags & INFO_FL_FUNC) { + ret = info->func(ops, file, fh, arg); } else if (!ops->vidioc_default) { ret = -ENOTTY; } else { diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index cb115ba6a1d280e339247f14759cfe0c0a6fab6a..6d9adcaa26bad0614037969075af6ff4bde3410f 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -332,6 +332,10 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, struct vb2_buffer *vb; int ret; + /* Ensure that q->num_buffers+num_buffers is below VB2_MAX_FRAME */ + num_buffers = min_t(unsigned int, num_buffers, + VB2_MAX_FRAME - q->num_buffers); + for (buffer = 0; buffer < num_buffers; ++buffer) { /* Allocate videobuf buffer structures */ vb = kzalloc(q->buf_struct_size, GFP_KERNEL); diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 84eab28665f37c148407a9bdeb99adccac5f788c..7a93400eea2a7c36dcd4f462f2cdff461151988c 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -99,7 +99,7 @@ module_param(mpt_channel_mapping, int, 0); MODULE_PARM_DESC(mpt_channel_mapping, " Mapping id's to channels (default=0)"); static int mpt_debug_level; -static int mpt_set_debug_level(const char *val, struct kernel_param *kp); +static int mpt_set_debug_level(const char *val, const struct kernel_param *kp); module_param_call(mpt_debug_level, mpt_set_debug_level, param_get_int, &mpt_debug_level, 0600); MODULE_PARM_DESC(mpt_debug_level, @@ -242,7 +242,7 @@ pci_enable_io_access(struct pci_dev *pdev) pci_write_config_word(pdev, PCI_COMMAND, command_reg); } -static int mpt_set_debug_level(const char *val, struct kernel_param *kp) +static int mpt_set_debug_level(const char *val, const struct kernel_param *kp) { int ret = param_set_int(val, kp); MPT_ADAPTER *ioc; diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 345f6035599eaf91e20032fd1f5d819b9549729e..f1d93676b0fc72dc1e7f9a5effeb7605b19db17f 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -1995,6 +1995,7 @@ static struct scsi_host_template mptsas_driver_template = { .cmd_per_lun = 7, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = mptscsih_host_attrs, + .no_write_same = 1, }; static int mptsas_get_linkerrors(struct sas_phy *phy) diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 326f51c05d306abdbef61c00b13f77bbf854d9e1..eca16c6aeecc272b816ff3dd6d5b18464c95096f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -73,6 +73,7 @@ lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o KCOV_INSTRUMENT_lkdtm_rodata.o := n +CFLAGS_lkdtm_rodata.o += $(DISABLE_LTO) OBJCOPYFLAGS := OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \ diff --git a/drivers/misc/cxl/cxllib.c b/drivers/misc/cxl/cxllib.c index dc9bc1807fdfa52aede0748308d16f5c36c6c95f..562a6803d6904afb7da008750193b4d02582d24c 100644 --- a/drivers/misc/cxl/cxllib.c +++ b/drivers/misc/cxl/cxllib.c @@ -207,49 +207,74 @@ int cxllib_get_PE_attributes(struct task_struct *task, } EXPORT_SYMBOL_GPL(cxllib_get_PE_attributes); -int cxllib_handle_fault(struct mm_struct *mm, u64 addr, u64 size, u64 flags) +static int get_vma_info(struct mm_struct *mm, u64 addr, + u64 *vma_start, u64 *vma_end, + unsigned long *page_size) { - int rc; - u64 dar; struct vm_area_struct *vma = NULL; - unsigned long page_size; - - if (mm == NULL) - return -EFAULT; + int rc = 0; down_read(&mm->mmap_sem); vma = find_vma(mm, addr); if (!vma) { - pr_err("Can't find vma for addr %016llx\n", addr); rc = -EFAULT; goto out; } - /* get the size of the pages allocated */ - page_size = vma_kernel_pagesize(vma); - - for (dar = (addr & ~(page_size - 1)); dar < (addr + size); dar += page_size) { - if (dar < vma->vm_start || dar >= vma->vm_end) { - vma = find_vma(mm, addr); - if (!vma) { - pr_err("Can't find vma for addr %016llx\n", addr); - rc = -EFAULT; - goto out; - } - /* get the size of the pages allocated */ - page_size = vma_kernel_pagesize(vma); + *page_size = vma_kernel_pagesize(vma); + *vma_start = vma->vm_start; + *vma_end = vma->vm_end; +out: + up_read(&mm->mmap_sem); + return rc; +} + +int cxllib_handle_fault(struct mm_struct *mm, u64 addr, u64 size, u64 flags) +{ + int rc; + u64 dar, vma_start, vma_end; + unsigned long page_size; + + if (mm == NULL) + return -EFAULT; + + /* + * The buffer we have to process can extend over several pages + * and may also cover several VMAs. + * We iterate over all the pages. The page size could vary + * between VMAs. + */ + rc = get_vma_info(mm, addr, &vma_start, &vma_end, &page_size); + if (rc) + return rc; + + for (dar = (addr & ~(page_size - 1)); dar < (addr + size); + dar += page_size) { + if (dar < vma_start || dar >= vma_end) { + /* + * We don't hold the mm->mmap_sem semaphore + * while iterating, since the semaphore is + * required by one of the lower-level page + * fault processing functions and it could + * create a deadlock. + * + * It means the VMAs can be altered between 2 + * loop iterations and we could theoretically + * miss a page (however unlikely). But that's + * not really a problem, as the driver will + * retry access, get another page fault on the + * missing page and call us again. + */ + rc = get_vma_info(mm, dar, &vma_start, &vma_end, + &page_size); + if (rc) + return rc; } rc = cxl_handle_mm_fault(mm, flags, dar); - if (rc) { - pr_err("cxl_handle_mm_fault failed %d", rc); - rc = -EFAULT; - goto out; - } + if (rc) + return -EFAULT; } - rc = 0; -out: - up_read(&mm->mmap_sem); - return rc; + return 0; } EXPORT_SYMBOL_GPL(cxllib_handle_fault); diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index fc7efedbc4be2513b2137bea45e48407308f0902..24108bfad88982c0688de8d4bcef17def3750587 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -1132,7 +1132,8 @@ static void kgdbts_put_char(u8 chr) ts.run_test(0, chr); } -static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp) +static int param_set_kgdbts_var(const char *kmessage, + const struct kernel_param *kp) { int len = strlen(kmessage); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index e825f013e54e42ff171f1c93e776770fbfb3b1d1..22efc039f3022237d1bc65b625cabb6ddb9fa3b1 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -507,7 +507,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; default: - dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd); rets = -ENOIOCTLCMD; } diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 48a68e97367ea3095485ddf49d7e17afe52e031f..25f6f9db9435a8364f31d85ce3677d2c7c88b780 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -1160,21 +1160,21 @@ static int qseecom_dmabuf_map(int ion_fd, struct sg_table **sgt, int ret = 0; new_dma_buf = dma_buf_get(ion_fd); - if (new_dma_buf == NULL) { + if (IS_ERR_OR_NULL(new_dma_buf)) { pr_err("dma_buf_get() for ion_fd %d failed\n", ion_fd); ret = -ENOMEM; goto err; } new_attach = dma_buf_attach(new_dma_buf, qseecom.dev); - if (IS_ERR(new_attach)) { + if (IS_ERR_OR_NULL(new_attach)) { pr_err("dma_buf_attach() for ion_fd %d failed\n", ion_fd); ret = -ENOMEM; goto err_put; } new_sgt = dma_buf_map_attachment(new_attach, DMA_BIDIRECTIONAL); - if (IS_ERR(new_sgt)) { + if (IS_ERR_OR_NULL(new_sgt)) { ret = PTR_ERR(new_sgt); pr_err("dma_buf_map_attachment for ion_fd %d failed ret = %d\n", ion_fd, ret); @@ -1227,7 +1227,7 @@ static int qseecom_vaddr_map(int ion_fd, dma_buf_begin_cpu_access(new_dma_buf, DMA_BIDIRECTIONAL); new_va = dma_buf_kmap(new_dma_buf, 0); - if (!new_va) { + if (IS_ERR_OR_NULL(new_va)) { pr_err("dma_buf_kmap failed\n"); ret = -ENOMEM; goto err_unmap; @@ -1309,7 +1309,9 @@ static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc, } return 0; err: - qseecom_vaddr_unmap(svc->sb_virt, svc->sgt, svc->attach, svc->dmabuf); + if (svc->dmabuf) + qseecom_vaddr_unmap(svc->sb_virt, svc->sgt, svc->attach, + svc->dmabuf); return ret; } @@ -1407,7 +1409,7 @@ static int qseecom_unregister_listener(struct qseecom_dev_handle *data) } } - if (ptr_svc->sb_virt) + if (ptr_svc->dmabuf) qseecom_vaddr_unmap(ptr_svc->sb_virt, ptr_svc->sgt, ptr_svc->attach, ptr_svc->dmabuf); @@ -1709,7 +1711,8 @@ static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data, return ret; exit: - qseecom_vaddr_unmap(data->client.sb_virt, data->client.sgt, + if (data->client.dmabuf) + qseecom_vaddr_unmap(data->client.sb_virt, data->client.sgt, data->client.attach, data->client.dmabuf); return ret; } @@ -2617,7 +2620,7 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp) loadapp_err: __qseecom_disable_clk_scale_down(data); - if (vaddr) + if (dmabuf) qseecom_vaddr_unmap(vaddr, sgt, attach, dmabuf); enable_clk_err: if (qseecom.support_bus_scaling) { @@ -2766,7 +2769,7 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data, flags1); } unload_exit: - if (data->client.sb_virt) + if (data->client.dmabuf) qseecom_vaddr_unmap(data->client.sb_virt, data->client.sgt, data->client.attach, data->client.dmabuf); data->released = true; @@ -3270,11 +3273,13 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, else *(uint32_t *)cmd_buf = QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; - ret = qseecom_dmabuf_cache_operations(data->client.dmabuf, + if (data->client.dmabuf) { + ret = qseecom_dmabuf_cache_operations(data->client.dmabuf, QSEECOM_CACHE_CLEAN); - if (ret) { - pr_err("cache operation failed %d\n", ret); - return ret; + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } } __qseecom_reentrancy_check_if_this_app_blocked(ptr_app); @@ -3287,11 +3292,13 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, ret, data->client.app_id); goto exit; } - ret = qseecom_dmabuf_cache_operations(data->client.dmabuf, + if (data->client.dmabuf) { + ret = qseecom_dmabuf_cache_operations(data->client.dmabuf, QSEECOM_CACHE_INVALIDATE); - if (ret) { - pr_err("cache operation failed %d\n", ret); - return ret; + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } } if (qseecom.qsee_reentrancy_support) { @@ -3597,7 +3604,7 @@ static int __qseecom_allocate_sg_list_buffer(struct qseecom_dev_handle *data, /* Allocate a contiguous kernel buffer */ size = sg_ptr->nents * SG_ENTRY_SZ_64BIT; size = (size + PAGE_SIZE) & PAGE_MASK; - buf = dma_alloc_coherent(qseecom.pdev, + buf = dma_alloc_coherent(qseecom.dev, size, &coh_pmem, GFP_KERNEL); if (buf == NULL) { pr_err("failed to alloc memory for sg buf\n"); @@ -4141,7 +4148,7 @@ static int __qseecom_alloc_coherent_buf( /* Allocate a contiguous kernel buffer */ size = (size + PAGE_SIZE) & PAGE_MASK; - buf = dma_alloc_coherent(qseecom.pdev, + buf = dma_alloc_coherent(qseecom.dev, size, &coh_pmem, GFP_KERNEL); if (buf == NULL) { pr_err("failed to alloc memory for size %d\n", size); @@ -4155,6 +4162,7 @@ static int __qseecom_alloc_coherent_buf( static void __qseecom_free_coherent_buf(uint32_t size, u8 *vaddr, phys_addr_t paddr) { + size = (size + PAGE_SIZE) & PAGE_MASK; dma_free_coherent(qseecom.pdev, size, vaddr, paddr); } @@ -4289,7 +4297,8 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname, } exit_free_img_data: - __qseecom_free_coherent_buf(fw_size, img_data, pa); + if (img_data) + __qseecom_free_coherent_buf(fw_size, img_data, pa); return ret; } @@ -4400,7 +4409,8 @@ static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data, } exit_free_img_data: - __qseecom_free_coherent_buf(fw_size, img_data, pa); + if (img_data) + __qseecom_free_coherent_buf(fw_size, img_data, pa); return ret; } @@ -4634,6 +4644,9 @@ int qseecom_shutdown_app(struct qseecom_handle **handle) mutex_unlock(&app_access_lock); if (ret == 0) { + if (data->client.sb_virt) + __qseecom_free_coherent_buf(data->client.sb_length, + data->client.sb_virt, data->client.sb_phys); kzfree(data); kzfree(*handle); kzfree(kclient); @@ -4702,7 +4715,12 @@ int qseecom_send_command(struct qseecom_handle *handle, void *send_buf, if (!strcmp(data->client.app_name, "securemm")) data->use_legacy_cmd = true; + dmac_flush_range(req.cmd_req_buf, req.cmd_req_buf + req.cmd_req_len); + ret = __qseecom_send_cmd(data, &req); + + dmac_flush_range(req.resp_buf, req.resp_buf + req.resp_len); + data->use_legacy_cmd = false; if (qseecom.support_bus_scaling) __qseecom_add_bw_scale_down_timer( @@ -5330,7 +5348,7 @@ static int qseecom_load_external_elf(struct qseecom_dev_handle *data, } exit_cpu_restore: - if (va) + if (dmabuf) qseecom_vaddr_unmap(va, sgt, attach, dmabuf); return ret; } @@ -6425,7 +6443,7 @@ static int __qseecom_qteec_handle_pre_alc_fd(struct qseecom_dev_handle *data, size = sizeof(uint32_t) + sizeof(struct qseecom_sg_entry) * sg_ptr->nents; size = (size + PAGE_SIZE) & PAGE_MASK; - buf = dma_alloc_coherent(qseecom.pdev, + buf = dma_alloc_coherent(qseecom.dev, size, &coh_pmem, GFP_KERNEL); if (buf == NULL) { pr_err("failed to alloc memory for sg buf\n"); @@ -7673,7 +7691,7 @@ static int qseecom_release(struct inode *inode, struct file *file) break; case QSEECOM_SECURE_SERVICE: case QSEECOM_GENERIC: - if (data->client.sb_virt) + if (data->client.dmabuf) qseecom_vaddr_unmap(data->client.sb_virt, data->client.sgt, data->client.attach, data->client.dmabuf); diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index ace629934821a864dcb48b29a5a511c474915a17..88dc1cd3a2046c0d61b9b2dde27d133d4d45bd8c 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -345,13 +346,13 @@ static int uid_cputime_show(struct seq_file *m, void *v) uid_entry->active_utime = 0; } - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); if (!uid_entry || uid_entry->uid != uid) uid_entry = find_or_register_uid(uid); if (!uid_entry) { - read_unlock(&tasklist_lock); + rcu_read_unlock(); rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", __func__, uid); @@ -361,7 +362,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) uid_entry->active_utime += utime; uid_entry->active_stime += stime; } while_each_thread(temp, task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { u64 total_utime = uid_entry->utime + @@ -419,6 +420,10 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } + + /* Also remove uids from /proc/uid_time_in_state */ + cpufreq_task_times_remove_uids(uid_start, uid_end); + rt_mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index acbe24be3e5c9840c0c987a867d934344e7ef301..09f6823909dfeefcfbf612a1a2c6e8d44931793d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -843,6 +843,19 @@ config MMC_SUNXI This selects support for the SD/MMC Host Controller on Allwinner sunxi SoCs. +config MMC_CQ_HCI + tristate "Command Queue Support" + depends on HAS_DMA + help + This selects the Command Queue Host Controller Interface (CQHCI) + support present in host controllers of Qualcomm Technologies, Inc + amongst others. + This controller supports eMMC devices with command queue support. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_TOSHIBA_PCI tristate "Toshiba Type A SD/MMC Card Interface Driver" depends on PCI diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 7db8c7a8d38df162879199be9a4941387d243f2f..48b67f552afe115da1a1b70704bf4be8deba9730 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -362,9 +362,9 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, host->irq_mask &= ~irq; else host->irq_mask |= irq; - spin_unlock_irqrestore(&host->lock, flags); writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); + spin_unlock_irqrestore(&host->lock, flags); } static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 070f5da06fd20dcfee3decd947f1cee83bc5d6af..44da037b13ba1cf5a275a7cd8a7f2acf8810d50f 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -806,6 +806,8 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) slot->host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; break; case INTEL_MRFLD_SDIO: + /* Advertise 2.0v for compatibility with the SDIO card's OCR */ + slot->host->ocr_mask = MMC_VDD_20_21 | MMC_VDD_165_195; slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD; break; @@ -1190,7 +1192,7 @@ static void amd_enable_manual_tuning(struct pci_dev *pdev) pci_write_config_dword(pdev, AMD_SD_MISC_CONTROL, val); } -static int amd_execute_tuning(struct sdhci_host *host, u32 opcode) +static int amd_execute_tuning_hs200(struct sdhci_host *host, u32 opcode) { struct sdhci_pci_slot *slot = sdhci_priv(host); struct pci_dev *pdev = slot->chip->pdev; @@ -1229,6 +1231,27 @@ static int amd_execute_tuning(struct sdhci_host *host, u32 opcode) return 0; } +static int amd_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct sdhci_host *host = mmc_priv(mmc); + + /* AMD requires custom HS200 tuning */ + if (host->timing == MMC_TIMING_MMC_HS200) + return amd_execute_tuning_hs200(host, opcode); + + /* Otherwise perform standard SDHCI tuning */ + return sdhci_execute_tuning(mmc, opcode); +} + +static int amd_probe_slot(struct sdhci_pci_slot *slot) +{ + struct mmc_host_ops *ops = &slot->host->mmc_host_ops; + + ops->execute_tuning = amd_execute_tuning; + + return 0; +} + static int amd_probe(struct sdhci_pci_chip *chip) { struct pci_dev *smbus_dev; @@ -1263,12 +1286,12 @@ static const struct sdhci_ops amd_sdhci_pci_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, - .platform_execute_tuning = amd_execute_tuning, }; static const struct sdhci_pci_fixes sdhci_amd = { .probe = amd_probe, .ops = &amd_sdhci_pci_ops, + .probe_slot = amd_probe_slot, }; static const struct pci_device_id pci_ids[] = { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 83355c36a989c6804fe3c6a3449fbf95b9c9c62c..77d0c7de6b2e54b5ac793fa2b6e4a0f312fff7ab 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1628,6 +1628,13 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, if (mode != MMC_POWER_OFF) { switch (1 << vdd) { case MMC_VDD_165_195: + /* + * Without a regulator, SDHCI does not support 2.0v + * so we only get here if the driver deliberately + * added the 2.0v range to ocr_avail. Map it to 1.8v + * for the purpose of turning on the power. + */ + case MMC_VDD_20_21: pwr = SDHCI_POWER_180; break; case MMC_VDD_29_30: diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 3a6d49f07e22d7273e85d49c8882400bb446b2dd..de1562f27fdb04e7d1e1cf3e34d8d976ca5d5828 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -911,7 +911,7 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host) host->check_scc_error(host); /* If SET_BLOCK_COUNT, continue with main command */ - if (host->mrq) { + if (host->mrq && !mrq->cmd->error) { tmio_process_mrq(host, mrq); return; } diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 5e1b68cbcd0acf736a9d28978b6b7ef37055c5cd..e1b603ca0170e2aa5daf4ef75fb408e3c147135d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -45,6 +45,7 @@ #define I82802AB 0x00ad #define I82802AC 0x00ac #define PF38F4476 0x881c +#define M28F00AP30 0x8963 /* STMicroelectronics chips */ #define M50LPW080 0x002F #define M50FLW080A 0x0080 @@ -375,6 +376,17 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi, extp->MinorVersion = '1'; } +static int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip) +{ + /* + * Micron(was Numonyx) 1Gbit bottom boot are buggy w.r.t + * Erase Supend for their small Erase Blocks(0x8000) + */ + if (cfi->mfr == CFI_MFR_INTEL && cfi->id == M28F00AP30) + return 1; + return 0; +} + static inline struct cfi_pri_intelext * read_pri_intelext(struct map_info *map, __u16 adr) { @@ -831,21 +843,30 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) goto sleep; + /* Do not allow suspend iff read/write to EB address */ + if ((adr & chip->in_progress_block_mask) == + chip->in_progress_block_addr) + goto sleep; + + /* do not suspend small EBs, buggy Micron Chips */ + if (cfi_is_micron_28F00AP30(cfi, chip) && + (chip->in_progress_block_mask == ~(0x8000-1))) + goto sleep; /* Erase suspend */ - map_write(map, CMD(0xB0), adr); + map_write(map, CMD(0xB0), chip->in_progress_block_addr); /* If the flash has finished erasing, then 'erase suspend' * appears to make some (28F320) flash devices switch to * 'read' mode. Make sure that we switch to 'read status' * mode so we get the right data. --rmk */ - map_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), chip->in_progress_block_addr); chip->oldstate = FL_ERASING; chip->state = FL_ERASE_SUSPENDING; chip->erase_suspended = 1; for (;;) { - status = map_read(map, adr); + status = map_read(map, chip->in_progress_block_addr); if (map_word_andequal(map, status, status_OK, status_OK)) break; @@ -1041,8 +1062,8 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad sending the 0x70 (Read Status) command to an erasing chip and expecting it to be ignored, that's what we do. */ - map_write(map, CMD(0xd0), adr); - map_write(map, CMD(0x70), adr); + map_write(map, CMD(0xd0), chip->in_progress_block_addr); + map_write(map, CMD(0x70), chip->in_progress_block_addr); chip->oldstate = FL_READY; chip->state = FL_ERASING; break; @@ -1933,6 +1954,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; chip->erase_suspended = 0; + chip->in_progress_block_addr = adr; + chip->in_progress_block_mask = ~(len - 1); ret = INVAL_CACHE_AND_WAIT(map, chip, adr, adr, len, diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 56aa6b75213d86d0442823e9b4163118231f9f30..d524a64ed7546436e9efa0423e6ca81bd62a04d6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -816,9 +816,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) goto sleep; - /* We could check to see if we're trying to access the sector - * that is currently being erased. However, no user will try - * anything like that so we just wait for the timeout. */ + /* Do not allow suspend iff read/write to EB address */ + if ((adr & chip->in_progress_block_mask) == + chip->in_progress_block_addr) + goto sleep; /* Erase suspend */ /* It's harmless to issue the Erase-Suspend and Erase-Resume @@ -2267,6 +2268,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) chip->state = FL_ERASING; chip->erase_suspended = 0; chip->in_progress_block_addr = adr; + chip->in_progress_block_mask = ~(map->size - 1); INVALIDATE_CACHE_UDELAY(map, chip, adr, map->size, @@ -2356,6 +2358,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->state = FL_ERASING; chip->erase_suspended = 0; chip->in_progress_block_addr = adr; + chip->in_progress_block_mask = ~(len - 1); INVALIDATE_CACHE_UDELAY(map, chip, adr, len, diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 7c0b27d132b1bca8aa546cedac726cf5e92c6613..b479bd81120b3432e015b76496c3f3ea1d32580c 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1889,6 +1889,8 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, do { uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), map, cfi); mask = (1 << (cfi->device_type * 8)) - 1; + if (ofs >= map->size) + return 0; result = map_read(map, base + ofs); bank++; } while ((result.x[0] & mask) == CFI_MFR_CONTINUATION); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 7c887f111a7d03eaeb41dd4bdb6bce67bf81609b..62fd6905c648c6eb5ef167e019831729bb294a85 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -431,7 +431,7 @@ static int block2mtd_setup2(const char *val) } -static int block2mtd_setup(const char *val, struct kernel_param *kp) +static int block2mtd_setup(const char *val, const struct kernel_param *kp) { #ifdef MODULE return block2mtd_setup2(val); diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 8b66e52ca3ccb811e818dce496ca6e0fcc7c9ecb..7287696a21f91df6a875dc32a112e6206c855de1 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -266,7 +266,7 @@ static int phram_setup(const char *val) return ret; } -static int phram_param_call(const char *val, struct kernel_param *kp) +static int phram_param_call(const char *val, const struct kernel_param *kp) { #ifdef MODULE return phram_setup(val); diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c index 8268636675efc8b3d81959f0f1911a145c42fcc1..4124bf91bee6b64f443cfc894ab22162a74745b9 100644 --- a/drivers/mtd/nand/atmel/pmecc.c +++ b/drivers/mtd/nand/atmel/pmecc.c @@ -426,7 +426,7 @@ static int get_strength(struct atmel_pmecc_user *user) static int get_sectorsize(struct atmel_pmecc_user *user) { - return user->cache.cfg & PMECC_LOOKUP_TABLE_SIZE_1024 ? 1024 : 512; + return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512; } static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector) diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c index 766906f0394303ea74c33d2f0052389df4f632b2..ce366816a7efb47fee4fb947ea3a2507718dffc2 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/tango_nand.c @@ -654,7 +654,7 @@ static int tango_nand_probe(struct platform_device *pdev) writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); - clk = clk_get(&pdev->dev, NULL); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) return PTR_ERR(clk); diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index 53c7d8e0327aa4376bdbb3cb00d78babf70349a4..8d89204b90d258820ee9441e02ab6345b1497b1f 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -495,7 +495,9 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, void __iomem *reg_base = cqspi->iobase; void __iomem *ahb_base = cqspi->ahb_base; unsigned int remaining = n_rx; + unsigned int mod_bytes = n_rx % 4; unsigned int bytes_to_read = 0; + u8 *rxbuf_end = rxbuf + n_rx; int ret = 0; writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES); @@ -523,11 +525,24 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, } while (bytes_to_read != 0) { + unsigned int word_remain = round_down(remaining, 4); + bytes_to_read *= cqspi->fifo_width; bytes_to_read = bytes_to_read > remaining ? remaining : bytes_to_read; - ioread32_rep(ahb_base, rxbuf, - DIV_ROUND_UP(bytes_to_read, 4)); + bytes_to_read = round_down(bytes_to_read, 4); + /* Read 4 byte word chunks then single bytes */ + if (bytes_to_read) { + ioread32_rep(ahb_base, rxbuf, + (bytes_to_read / 4)); + } else if (!word_remain && mod_bytes) { + unsigned int temp = ioread32(ahb_base); + + bytes_to_read = mod_bytes; + memcpy(rxbuf, &temp, min((unsigned int) + (rxbuf_end - rxbuf), + bytes_to_read)); + } rxbuf += bytes_to_read; remaining -= bytes_to_read; bytes_to_read = cqspi_get_rd_sram_level(cqspi); diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c index 1cb3f7758fb60d8b084c27cc74d2098d5b7a691f..766b2c38568240ceb8505c52ea049f4543a581fc 100644 --- a/drivers/mtd/tests/oobtest.c +++ b/drivers/mtd/tests/oobtest.c @@ -193,6 +193,9 @@ static int verify_eraseblock(int ebnum) ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd_read_oob(mtd, addr, &ops); + if (mtd_is_bitflip(err)) + err = 0; + if (err || ops.oobretlen != use_len) { pr_err("error: readoob failed at %#llx\n", (long long)addr); @@ -227,6 +230,9 @@ static int verify_eraseblock(int ebnum) ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd_read_oob(mtd, addr, &ops); + if (mtd_is_bitflip(err)) + err = 0; + if (err || ops.oobretlen != mtd->oobavail) { pr_err("error: readoob failed at %#llx\n", (long long)addr); @@ -286,6 +292,9 @@ static int verify_eraseblock_in_one_go(int ebnum) /* read entire block's OOB at one go */ err = mtd_read_oob(mtd, addr, &ops); + if (mtd_is_bitflip(err)) + err = 0; + if (err || ops.oobretlen != len) { pr_err("error: readoob failed at %#llx\n", (long long)addr); @@ -527,6 +536,9 @@ static int __init mtd_oobtest_init(void) pr_info("attempting to start read past end of OOB\n"); pr_info("an error is expected...\n"); err = mtd_read_oob(mtd, addr0, &ops); + if (mtd_is_bitflip(err)) + err = 0; + if (err) { pr_info("error occurred as expected\n"); err = 0; @@ -571,6 +583,9 @@ static int __init mtd_oobtest_init(void) pr_info("attempting to read past end of device\n"); pr_info("an error is expected...\n"); err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); + if (mtd_is_bitflip(err)) + err = 0; + if (err) { pr_info("error occurred as expected\n"); err = 0; @@ -615,6 +630,9 @@ static int __init mtd_oobtest_init(void) pr_info("attempting to read past end of device\n"); pr_info("an error is expected...\n"); err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); + if (mtd_is_bitflip(err)) + err = 0; + if (err) { pr_info("error occurred as expected\n"); err = 0; @@ -684,6 +702,9 @@ static int __init mtd_oobtest_init(void) ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd_read_oob(mtd, addr, &ops); + if (mtd_is_bitflip(err)) + err = 0; + if (err) goto out; if (memcmpshow(addr, readbuf, writebuf, diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index b1fc28f63882e75125230d05462b775cb18732f7..d0b63bbf46a792e3d168606b6dd102b7bb0c9079 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -244,7 +244,7 @@ static int ubiblock_open(struct block_device *bdev, fmode_t mode) * in any case. */ if (mode & FMODE_WRITE) { - ret = -EPERM; + ret = -EROFS; goto out_unlock; } diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 842550b5712ae48a18e24c2c4945095b0c596ccf..68d6f41588687d8a11c1f0524b898b56e7c7a809 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -845,6 +845,17 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, return -EINVAL; } + /* + * Both UBI and UBIFS have been designed for SLC NAND and NOR flashes. + * MLC NAND is different and needs special care, otherwise UBI or UBIFS + * will die soon and you will lose all your data. + */ + if (mtd->type == MTD_MLCNANDFLASH) { + pr_err("ubi: refuse attaching mtd%d - MLC NAND is not supported\n", + mtd->index); + return -EINVAL; + } + if (ubi_num == UBI_DEV_NUM_AUTO) { /* Search for an empty slot in the @ubi_devices array */ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) @@ -1334,7 +1345,7 @@ static int bytes_str_to_int(const char *str) * This function returns zero in case of success and a negative error code in * case of error. */ -static int ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp) { int i, len; struct mtd_dev_param *p; diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c index 4f0bd6b4422adc9bfa5c7bfd4f1afdd8abcdb4fc..69dd21679a30c21fcb58779f83098034747d4037 100644 --- a/drivers/mtd/ubi/fastmap-wl.c +++ b/drivers/mtd/ubi/fastmap-wl.c @@ -362,7 +362,6 @@ static void ubi_fastmap_close(struct ubi_device *ubi) { int i; - flush_work(&ubi->fm_work); return_unused_pool_pebs(ubi, &ubi->fm_pool); return_unused_pool_pebs(ubi, &ubi->fm_wl_pool); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b2db581131b2d49a843c1f548e7341a12e395127..bf3be2e6d4a801c06fe4d3e6075f5ec4439e4fe5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1524,39 +1524,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_close; } - /* If the mode uses primary, then the following is handled by - * bond_change_active_slave(). - */ - if (!bond_uses_primary(bond)) { - /* set promiscuity level to new slave */ - if (bond_dev->flags & IFF_PROMISC) { - res = dev_set_promiscuity(slave_dev, 1); - if (res) - goto err_close; - } - - /* set allmulti level to new slave */ - if (bond_dev->flags & IFF_ALLMULTI) { - res = dev_set_allmulti(slave_dev, 1); - if (res) - goto err_close; - } - - netif_addr_lock_bh(bond_dev); - - dev_mc_sync_multiple(slave_dev, bond_dev); - dev_uc_sync_multiple(slave_dev, bond_dev); - - netif_addr_unlock_bh(bond_dev); - } - - if (BOND_MODE(bond) == BOND_MODE_8023AD) { - /* add lacpdu mc addr to mc list */ - u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR; - - dev_mc_add(slave_dev, lacpdu_multicast); - } - res = vlan_vids_add_by_dev(slave_dev, bond_dev); if (res) { netdev_err(bond_dev, "Couldn't add bond vlan ids to %s\n", @@ -1689,8 +1656,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } /* switch(bond_mode) */ #ifdef CONFIG_NET_POLL_CONTROLLER - slave_dev->npinfo = bond->dev->npinfo; - if (slave_dev->npinfo) { + if (bond->dev->npinfo) { if (slave_enable_netpoll(new_slave)) { netdev_info(bond_dev, "master_dev is using netpoll, but new slave device does not support netpoll\n"); res = -EBUSY; @@ -1721,6 +1687,40 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_upper_unlink; } + /* If the mode uses primary, then the following is handled by + * bond_change_active_slave(). + */ + if (!bond_uses_primary(bond)) { + /* set promiscuity level to new slave */ + if (bond_dev->flags & IFF_PROMISC) { + res = dev_set_promiscuity(slave_dev, 1); + if (res) + goto err_sysfs_del; + } + + /* set allmulti level to new slave */ + if (bond_dev->flags & IFF_ALLMULTI) { + res = dev_set_allmulti(slave_dev, 1); + if (res) { + if (bond_dev->flags & IFF_PROMISC) + dev_set_promiscuity(slave_dev, -1); + goto err_sysfs_del; + } + } + + netif_addr_lock_bh(bond_dev); + dev_mc_sync_multiple(slave_dev, bond_dev); + dev_uc_sync_multiple(slave_dev, bond_dev); + netif_addr_unlock_bh(bond_dev); + + if (BOND_MODE(bond) == BOND_MODE_8023AD) { + /* add lacpdu mc addr to mc list */ + u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR; + + dev_mc_add(slave_dev, lacpdu_multicast); + } + } + bond->slave_cnt++; bond_compute_features(bond); bond_set_carrier(bond); @@ -1744,6 +1744,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) return 0; /* Undo stages on error */ +err_sysfs_del: + bond_sysfs_slave_del(new_slave); + err_upper_unlink: bond_upper_dev_unlink(bond, new_slave); @@ -1751,9 +1754,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) netdev_rx_handler_unregister(slave_dev); err_detach: - if (!bond_uses_primary(bond)) - bond_hw_addr_flush(bond_dev, slave_dev); - vlan_vids_del_by_dev(slave_dev, bond_dev); if (rcu_access_pointer(bond->primary_slave) == new_slave) RCU_INIT_POINTER(bond->primary_slave, NULL); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 7ea72ef11a55d5a4425959da1d12c69b108676b2..d272dc6984ac6ef3f61743a16912c13552c35b2f 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -1321,6 +1321,10 @@ #define MDIO_VEND2_AN_STAT 0x8002 #endif +#ifndef MDIO_VEND2_PMA_CDR_CONTROL +#define MDIO_VEND2_PMA_CDR_CONTROL 0x8056 +#endif + #ifndef MDIO_CTRL1_SPEED1G #define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100) #endif @@ -1369,6 +1373,10 @@ #define XGBE_AN_CL37_TX_CONFIG_MASK 0x08 #define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100 +#define XGBE_PMA_CDR_TRACK_EN_MASK 0x01 +#define XGBE_PMA_CDR_TRACK_EN_OFF 0x00 +#define XGBE_PMA_CDR_TRACK_EN_ON 0x01 + /* Bit setting and getting macros * The get macro will extract the current bit field value from within * the variable diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 7d128be613103a1088655c4b1dcc2e10e234a82f..b91143947ed271a6d29c83040aae6f4304e1549d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -519,6 +519,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata) "debugfs_create_file failed\n"); } + if (pdata->vdata->an_cdr_workaround) { + pfile = debugfs_create_bool("an_cdr_workaround", 0600, + pdata->xgbe_debugfs, + &pdata->debugfs_an_cdr_workaround); + if (!pfile) + netdev_err(pdata->netdev, + "debugfs_create_bool failed\n"); + + pfile = debugfs_create_bool("an_cdr_track_early", 0600, + pdata->xgbe_debugfs, + &pdata->debugfs_an_cdr_track_early); + if (!pfile) + netdev_err(pdata->netdev, + "debugfs_create_bool failed\n"); + } + kfree(buf); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index d91fa595be9835318ce15a0b6b8ef6ef58bdf56a..e31d9d1fb6a66c34501fb76eade3ea961db33f4d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -349,6 +349,7 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1); /* Call MDIO/PHY initialization routine */ + pdata->debugfs_an_cdr_workaround = pdata->vdata->an_cdr_workaround; ret = pdata->phy_if.phy_init(pdata); if (ret) return ret; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 072b9f6645978d0c53ce40271b9dbfe44a6baa0a..1b45cd73a258f05211bfc2ca6e69124c5347bda8 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -432,11 +432,16 @@ static void xgbe_an73_disable(struct xgbe_prv_data *pdata) xgbe_an73_set(pdata, false, false); xgbe_an73_disable_interrupts(pdata); + pdata->an_start = 0; + netif_dbg(pdata, link, pdata->netdev, "CL73 AN disabled\n"); } static void xgbe_an_restart(struct xgbe_prv_data *pdata) { + if (pdata->phy_if.phy_impl.an_pre) + pdata->phy_if.phy_impl.an_pre(pdata); + switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73_REDRV: @@ -453,6 +458,9 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata) static void xgbe_an_disable(struct xgbe_prv_data *pdata) { + if (pdata->phy_if.phy_impl.an_post) + pdata->phy_if.phy_impl.an_post(pdata); + switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: case XGBE_AN_MODE_CL73_REDRV: @@ -505,11 +513,11 @@ static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata, XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); - if (pdata->phy_if.phy_impl.kr_training_post) - pdata->phy_if.phy_impl.kr_training_post(pdata); - netif_dbg(pdata, link, pdata->netdev, "KR training initiated\n"); + + if (pdata->phy_if.phy_impl.kr_training_post) + pdata->phy_if.phy_impl.kr_training_post(pdata); } return XGBE_AN_PAGE_RECEIVED; @@ -637,11 +645,11 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) return XGBE_AN_NO_LINK; } - xgbe_an73_disable(pdata); + xgbe_an_disable(pdata); xgbe_switch_mode(pdata); - xgbe_an73_restart(pdata); + xgbe_an_restart(pdata); return XGBE_AN_INCOMPAT_LINK; } @@ -820,6 +828,9 @@ static void xgbe_an37_state_machine(struct xgbe_prv_data *pdata) pdata->an_result = pdata->an_state; pdata->an_state = XGBE_AN_READY; + if (pdata->phy_if.phy_impl.an_post) + pdata->phy_if.phy_impl.an_post(pdata); + netif_dbg(pdata, link, pdata->netdev, "CL37 AN result: %s\n", xgbe_state_as_string(pdata->an_result)); } @@ -903,6 +914,9 @@ static void xgbe_an73_state_machine(struct xgbe_prv_data *pdata) pdata->kx_state = XGBE_RX_BPA; pdata->an_start = 0; + if (pdata->phy_if.phy_impl.an_post) + pdata->phy_if.phy_impl.an_post(pdata); + netif_dbg(pdata, link, pdata->netdev, "CL73 AN result: %s\n", xgbe_state_as_string(pdata->an_result)); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index eb23f9ba1a9a10091f8d42bb71e35291e224c4f1..82d1f416ee2ac96c9aa2d7e5d70c22650e0407fe 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -456,6 +456,7 @@ static const struct xgbe_version_data xgbe_v2a = { .irq_reissue_support = 1, .tx_desc_prefetch = 5, .rx_desc_prefetch = 5, + .an_cdr_workaround = 1, }; static const struct xgbe_version_data xgbe_v2b = { @@ -470,6 +471,7 @@ static const struct xgbe_version_data xgbe_v2b = { .irq_reissue_support = 1, .tx_desc_prefetch = 5, .rx_desc_prefetch = 5, + .an_cdr_workaround = 1, }; static const struct pci_device_id xgbe_pci_table[] = { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 3304a291aa964c034f1ebcace56d8aaec32c8407..aac884314000c9114decb1a29e743a593a5bce89 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -147,6 +147,14 @@ /* Rate-change complete wait/retry count */ #define XGBE_RATECHANGE_COUNT 500 +/* CDR delay values for KR support (in usec) */ +#define XGBE_CDR_DELAY_INIT 10000 +#define XGBE_CDR_DELAY_INC 10000 +#define XGBE_CDR_DELAY_MAX 100000 + +/* RRC frequency during link status check */ +#define XGBE_RRC_FREQUENCY 10 + enum xgbe_port_mode { XGBE_PORT_MODE_RSVD = 0, XGBE_PORT_MODE_BACKPLANE, @@ -245,6 +253,10 @@ enum xgbe_sfp_speed { #define XGBE_SFP_BASE_VENDOR_SN 4 #define XGBE_SFP_BASE_VENDOR_SN_LEN 16 +#define XGBE_SFP_EXTD_OPT1 1 +#define XGBE_SFP_EXTD_OPT1_RX_LOS BIT(1) +#define XGBE_SFP_EXTD_OPT1_TX_FAULT BIT(3) + #define XGBE_SFP_EXTD_DIAG 28 #define XGBE_SFP_EXTD_DIAG_ADDR_CHANGE BIT(2) @@ -324,6 +336,7 @@ struct xgbe_phy_data { unsigned int sfp_gpio_address; unsigned int sfp_gpio_mask; + unsigned int sfp_gpio_inputs; unsigned int sfp_gpio_rx_los; unsigned int sfp_gpio_tx_fault; unsigned int sfp_gpio_mod_absent; @@ -355,6 +368,10 @@ struct xgbe_phy_data { unsigned int redrv_addr; unsigned int redrv_lane; unsigned int redrv_model; + + /* KR AN support */ + unsigned int phy_cdr_notrack; + unsigned int phy_cdr_delay; }; /* I2C, MDIO and GPIO lines are muxed, so only one device at a time */ @@ -974,6 +991,49 @@ static void xgbe_phy_sfp_external_phy(struct xgbe_prv_data *pdata) phy_data->sfp_phy_avail = 1; } +static bool xgbe_phy_check_sfp_rx_los(struct xgbe_phy_data *phy_data) +{ + u8 *sfp_extd = phy_data->sfp_eeprom.extd; + + if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_RX_LOS)) + return false; + + if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS) + return false; + + if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_rx_los)) + return true; + + return false; +} + +static bool xgbe_phy_check_sfp_tx_fault(struct xgbe_phy_data *phy_data) +{ + u8 *sfp_extd = phy_data->sfp_eeprom.extd; + + if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_TX_FAULT)) + return false; + + if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT) + return false; + + if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_tx_fault)) + return true; + + return false; +} + +static bool xgbe_phy_check_sfp_mod_absent(struct xgbe_phy_data *phy_data) +{ + if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT) + return false; + + if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_mod_absent)) + return true; + + return false; +} + static bool xgbe_phy_belfuse_parse_quirks(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -1019,6 +1079,10 @@ static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata) if (sfp_base[XGBE_SFP_BASE_EXT_ID] != XGBE_SFP_EXT_ID_SFP) return; + /* Update transceiver signals (eeprom extd/options) */ + phy_data->sfp_tx_fault = xgbe_phy_check_sfp_tx_fault(phy_data); + phy_data->sfp_rx_los = xgbe_phy_check_sfp_rx_los(phy_data); + if (xgbe_phy_sfp_parse_quirks(pdata)) return; @@ -1184,7 +1248,6 @@ static int xgbe_phy_sfp_read_eeprom(struct xgbe_prv_data *pdata) static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; - unsigned int gpio_input; u8 gpio_reg, gpio_ports[2]; int ret; @@ -1199,23 +1262,9 @@ static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata) return; } - gpio_input = (gpio_ports[1] << 8) | gpio_ports[0]; - - if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT) { - /* No GPIO, just assume the module is present for now */ - phy_data->sfp_mod_absent = 0; - } else { - if (!(gpio_input & (1 << phy_data->sfp_gpio_mod_absent))) - phy_data->sfp_mod_absent = 0; - } - - if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS) && - (gpio_input & (1 << phy_data->sfp_gpio_rx_los))) - phy_data->sfp_rx_los = 1; + phy_data->sfp_gpio_inputs = (gpio_ports[1] << 8) | gpio_ports[0]; - if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT) && - (gpio_input & (1 << phy_data->sfp_gpio_tx_fault))) - phy_data->sfp_tx_fault = 1; + phy_data->sfp_mod_absent = xgbe_phy_check_sfp_mod_absent(phy_data); } static void xgbe_phy_sfp_mod_absent(struct xgbe_prv_data *pdata) @@ -2361,7 +2410,7 @@ static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart) return 1; /* No link, attempt a receiver reset cycle */ - if (phy_data->rrc_count++) { + if (phy_data->rrc_count++ > XGBE_RRC_FREQUENCY) { phy_data->rrc_count = 0; xgbe_phy_rrc(pdata); } @@ -2669,6 +2718,103 @@ static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata) return true; } +static void xgbe_phy_cdr_track(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (!pdata->debugfs_an_cdr_workaround) + return; + + if (!phy_data->phy_cdr_notrack) + return; + + usleep_range(phy_data->phy_cdr_delay, + phy_data->phy_cdr_delay + 500); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL, + XGBE_PMA_CDR_TRACK_EN_MASK, + XGBE_PMA_CDR_TRACK_EN_ON); + + phy_data->phy_cdr_notrack = 0; +} + +static void xgbe_phy_cdr_notrack(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (!pdata->debugfs_an_cdr_workaround) + return; + + if (phy_data->phy_cdr_notrack) + return; + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL, + XGBE_PMA_CDR_TRACK_EN_MASK, + XGBE_PMA_CDR_TRACK_EN_OFF); + + xgbe_phy_rrc(pdata); + + phy_data->phy_cdr_notrack = 1; +} + +static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata) +{ + if (!pdata->debugfs_an_cdr_track_early) + xgbe_phy_cdr_track(pdata); +} + +static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata) +{ + if (pdata->debugfs_an_cdr_track_early) + xgbe_phy_cdr_track(pdata); +} + +static void xgbe_phy_an_post(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + if (phy_data->cur_mode != XGBE_MODE_KR) + break; + + xgbe_phy_cdr_track(pdata); + + switch (pdata->an_result) { + case XGBE_AN_READY: + case XGBE_AN_COMPLETE: + break; + default: + if (phy_data->phy_cdr_delay < XGBE_CDR_DELAY_MAX) + phy_data->phy_cdr_delay += XGBE_CDR_DELAY_INC; + else + phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT; + break; + } + break; + default: + break; + } +} + +static void xgbe_phy_an_pre(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + if (phy_data->cur_mode != XGBE_MODE_KR) + break; + + xgbe_phy_cdr_notrack(pdata); + break; + default: + break; + } +} + static void xgbe_phy_stop(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -2680,6 +2826,9 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) xgbe_phy_sfp_reset(phy_data); xgbe_phy_sfp_mod_absent(pdata); + /* Reset CDR support */ + xgbe_phy_cdr_track(pdata); + /* Power off the PHY */ xgbe_phy_power_off(pdata); @@ -2712,6 +2861,9 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) /* Start in highest supported mode */ xgbe_phy_set_mode(pdata, phy_data->start_mode); + /* Reset CDR support */ + xgbe_phy_cdr_track(pdata); + /* After starting the I2C controller, we can check for an SFP */ switch (phy_data->port_mode) { case XGBE_PORT_MODE_SFP: @@ -3019,6 +3171,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) } } + phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT; + /* Register for driving external PHYs */ mii = devm_mdiobus_alloc(pdata->dev); if (!mii) { @@ -3071,4 +3225,10 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if) phy_impl->an_advertising = xgbe_phy_an_advertising; phy_impl->an_outcome = xgbe_phy_an_outcome; + + phy_impl->an_pre = xgbe_phy_an_pre; + phy_impl->an_post = xgbe_phy_an_post; + + phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; + phy_impl->kr_training_post = xgbe_phy_kr_training_post; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index ad102c8bac7bf44d8fa92d0bc175333b12f415fc..95d4b56448c68058102cc4fb3da9e1aefc9fea59 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -833,6 +833,7 @@ struct xgbe_hw_if { /* This structure represents implementation specific routines for an * implementation of a PHY. All routines are required unless noted below. * Optional routines: + * an_pre, an_post * kr_training_pre, kr_training_post */ struct xgbe_phy_impl_if { @@ -875,6 +876,10 @@ struct xgbe_phy_impl_if { /* Process results of auto-negotiation */ enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); + /* Pre/Post auto-negotiation support */ + void (*an_pre)(struct xgbe_prv_data *); + void (*an_post)(struct xgbe_prv_data *); + /* Pre/Post KR training enablement support */ void (*kr_training_pre)(struct xgbe_prv_data *); void (*kr_training_post)(struct xgbe_prv_data *); @@ -989,6 +994,7 @@ struct xgbe_version_data { unsigned int irq_reissue_support; unsigned int tx_desc_prefetch; unsigned int rx_desc_prefetch; + unsigned int an_cdr_workaround; }; struct xgbe_vxlan_data { @@ -1257,6 +1263,9 @@ struct xgbe_prv_data { unsigned int debugfs_xprop_reg; unsigned int debugfs_xi2c_reg; + + bool debugfs_an_cdr_workaround; + bool debugfs_an_cdr_track_early; }; /* Function prototypes*/ diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 05498e7f284034d86a8f8531de95cf7f59ec4890..6246003f9922f1a6fce43fca209afc525183ef7a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -2619,8 +2619,8 @@ void t4vf_sge_stop(struct adapter *adapter) int t4vf_sge_init(struct adapter *adapter) { struct sge_params *sge_params = &adapter->params.sge; - u32 fl0 = sge_params->sge_fl_buffer_size[0]; - u32 fl1 = sge_params->sge_fl_buffer_size[1]; + u32 fl_small_pg = sge_params->sge_fl_buffer_size[0]; + u32 fl_large_pg = sge_params->sge_fl_buffer_size[1]; struct sge *s = &adapter->sge; /* @@ -2628,9 +2628,20 @@ int t4vf_sge_init(struct adapter *adapter) * the Physical Function Driver. Ideally we should be able to deal * with _any_ configuration. Practice is different ... */ - if (fl0 != PAGE_SIZE || (fl1 != 0 && fl1 <= fl0)) { + + /* We only bother using the Large Page logic if the Large Page Buffer + * is larger than our Page Size Buffer. + */ + if (fl_large_pg <= fl_small_pg) + fl_large_pg = 0; + + /* The Page Size Buffer must be exactly equal to our Page Size and the + * Large Page Size Buffer should be 0 (per above) or a power of 2. + */ + if (fl_small_pg != PAGE_SIZE || + (fl_large_pg & (fl_large_pg - 1)) != 0) { dev_err(adapter->pdev_dev, "bad SGE FL buffer sizes [%d, %d]\n", - fl0, fl1); + fl_small_pg, fl_large_pg); return -EINVAL; } if ((sge_params->sge_control & RXPKTCPLMODE_F) != @@ -2642,8 +2653,8 @@ int t4vf_sge_init(struct adapter *adapter) /* * Now translate the adapter parameters into our internal forms. */ - if (fl1) - s->fl_pg_order = ilog2(fl1) - PAGE_SHIFT; + if (fl_large_pg) + s->fl_pg_order = ilog2(fl_large_pg) - PAGE_SHIFT; s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64); s->pktshift = PKTSHIFT_G(sge_params->sge_control); diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 7f837006bb6adf04a844d08a2a88d0957f704443..3bdeb295514bde273404c516d07b6c0c024aa025 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -2932,7 +2932,7 @@ static irqreturn_t gfar_transmit(int irq, void *grp_id) static bool gfar_add_rx_frag(struct gfar_rx_buff *rxb, u32 lstatus, struct sk_buff *skb, bool first) { - unsigned int size = lstatus & BD_LENGTH_MASK; + int size = lstatus & BD_LENGTH_MASK; struct page *page = rxb->page; bool last = !!(lstatus & BD_LFLAG(RXBD_LAST)); @@ -2947,11 +2947,16 @@ static bool gfar_add_rx_frag(struct gfar_rx_buff *rxb, u32 lstatus, if (last) size -= skb->len; - /* in case the last fragment consisted only of the FCS */ + /* Add the last fragment if it contains something other than + * the FCS, otherwise drop it and trim off any part of the FCS + * that was already received. + */ if (size > 0) skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, rxb->page_offset + RXBUF_ALIGNMENT, size, GFAR_RXB_TRUESIZE); + else if (size < 0) + pskb_trim(skb, skb->len + size); } /* try reuse page */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c index 86944bc3b273fd97232a60e68f25046e68042882..74bd260ca02a887869a507f8746dfc928522d4be 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -666,7 +666,7 @@ static void hns_gmac_get_strings(u32 stringset, u8 *data) static int hns_gmac_get_sset_count(int stringset) { - if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS) + if (stringset == ETH_SS_STATS) return ARRAY_SIZE(g_gmac_stats_string); return 0; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index b62816c1574eb840f74a334b904f9fd993733116..93e71e27401b4da815e899753dc7be1a83ff3f14 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -422,7 +422,7 @@ void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb) int hns_ppe_get_sset_count(int stringset) { - if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS) + if (stringset == ETH_SS_STATS) return ETH_PPE_STATIC_NUM; return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index 6f3570cfb501604bea3f22d73374dd8dc28d756f..e2e28532e4dc2d03cf15330c621f8fb49469e382 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -876,7 +876,7 @@ void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data) */ int hns_rcb_get_ring_sset_count(int stringset) { - if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS) + if (stringset == ETH_SS_STATS) return HNS_RING_STATIC_REG_NUM; return 0; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 7ea7f8a4aa2a9456f2d71cceccae9eff2b83421a..2e14a3ae1d8be0f9841a5c53f456c4d2e4f4d270 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -993,8 +993,10 @@ int hns_get_sset_count(struct net_device *netdev, int stringset) cnt--; return cnt; - } else { + } else if (stringset == ETH_SS_STATS) { return (HNS_NET_STATS_CNT + ops->get_sset_count(h, stringset)); + } else { + return -EOPNOTSUPP; } } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index a0ef97e7f3c93597ef2d90e7665f8bd271452a0d..ff7a70ffafc65f22fd3ef51416b6487d74f37824 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2092,6 +2092,10 @@ static int hclge_get_autoneg(struct hnae3_handle *handle) { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; + struct phy_device *phydev = hdev->hw.mac.phydev; + + if (phydev) + return phydev->autoneg; hclge_query_autoneg_result(hdev); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index 186772493711e2e7a567079cf9d8c31ff0e605da..d1e4dcec5db27a68617796e4aa5ab2c39a6d5f91 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1060,6 +1060,8 @@ hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) u64 rx_bytes = 0; u64 tx_pkts = 0; u64 rx_pkts = 0; + u64 tx_drop = 0; + u64 rx_drop = 0; for (idx = 0; idx < queue_num; idx++) { /* fetch the tx stats */ @@ -1068,6 +1070,8 @@ hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) start = u64_stats_fetch_begin_irq(&ring->syncp); tx_bytes += ring->stats.tx_bytes; tx_pkts += ring->stats.tx_pkts; + tx_drop += ring->stats.tx_busy; + tx_drop += ring->stats.sw_err_cnt; } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); /* fetch the rx stats */ @@ -1076,6 +1080,9 @@ hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) start = u64_stats_fetch_begin_irq(&ring->syncp); rx_bytes += ring->stats.rx_bytes; rx_pkts += ring->stats.rx_pkts; + rx_drop += ring->stats.non_vld_descs; + rx_drop += ring->stats.err_pkt_len; + rx_drop += ring->stats.l2_err; } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); } @@ -1091,8 +1098,8 @@ hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) stats->rx_missed_errors = netdev->stats.rx_missed_errors; stats->tx_errors = netdev->stats.tx_errors; - stats->rx_dropped = netdev->stats.rx_dropped; - stats->tx_dropped = netdev->stats.tx_dropped; + stats->rx_dropped = rx_drop + netdev->stats.rx_dropped; + stats->tx_dropped = tx_drop + netdev->stats.tx_dropped; stats->collisions = netdev->stats.collisions; stats->rx_over_errors = netdev->stats.rx_over_errors; stats->rx_frame_errors = netdev->stats.rx_frame_errors; @@ -1306,6 +1313,8 @@ static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu) return ret; } + netdev->mtu = new_mtu; + /* if the netdev was running earlier, bring it up again */ if (if_running && hns3_nic_net_open(netdev)) ret = -EINVAL; @@ -2687,8 +2696,12 @@ static int hns3_uninit_all_ring(struct hns3_nic_priv *priv) h->ae_algo->ops->reset_queue(h, i); hns3_fini_ring(priv->ring_data[i].ring); + devm_kfree(priv->dev, priv->ring_data[i].ring); hns3_fini_ring(priv->ring_data[i + h->kinfo.num_tqps].ring); + devm_kfree(priv->dev, + priv->ring_data[i + h->kinfo.num_tqps].ring); } + devm_kfree(priv->dev, priv->ring_data); return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c index e590d96e434a197761f63135823cb350f59268e3..a64a5a413d4d025f4c4a638236f46645cb84cea4 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c @@ -22,7 +22,8 @@ struct hns3_stats { #define HNS3_TQP_STAT(_string, _member) { \ .stats_string = _string, \ .stats_size = FIELD_SIZEOF(struct ring_stats, _member), \ - .stats_offset = offsetof(struct hns3_enet_ring, stats), \ + .stats_offset = offsetof(struct hns3_enet_ring, stats) +\ + offsetof(struct ring_stats, _member), \ } \ static const struct hns3_stats hns3_txq_stats[] = { @@ -189,13 +190,13 @@ static u64 *hns3_get_stats_tqps(struct hnae3_handle *handle, u64 *data) struct hnae3_knic_private_info *kinfo = &handle->kinfo; struct hns3_enet_ring *ring; u8 *stat; - u32 i; + int i, j; /* get stats for Tx */ for (i = 0; i < kinfo->num_tqps; i++) { ring = nic_priv->ring_data[i].ring; - for (i = 0; i < HNS3_TXQ_STATS_COUNT; i++) { - stat = (u8 *)ring + hns3_txq_stats[i].stats_offset; + for (j = 0; j < HNS3_TXQ_STATS_COUNT; j++) { + stat = (u8 *)ring + hns3_txq_stats[j].stats_offset; *data++ = *(u64 *)stat; } } @@ -203,8 +204,8 @@ static u64 *hns3_get_stats_tqps(struct hnae3_handle *handle, u64 *data) /* get stats for Rx */ for (i = 0; i < kinfo->num_tqps; i++) { ring = nic_priv->ring_data[i + kinfo->num_tqps].ring; - for (i = 0; i < HNS3_RXQ_STATS_COUNT; i++) { - stat = (u8 *)ring + hns3_rxq_stats[i].stats_offset; + for (j = 0; j < HNS3_RXQ_STATS_COUNT; j++) { + stat = (u8 *)ring + hns3_rxq_stats[j].stats_offset; *data++ = *(u64 *)stat; } } diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 3b0db01ead1f428a0bb16f6461bebed369be9289..3ae02b0620bc9656131fd2ba17ba2322a2fe7e6a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -2209,6 +2209,12 @@ static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance) struct ibmvnic_sub_crq_queue *scrq = instance; struct ibmvnic_adapter *adapter = scrq->adapter; + /* When booting a kdump kernel we can hit pending interrupts + * prior to completing driver initialization. + */ + if (unlikely(adapter->state != VNIC_OPEN)) + return IRQ_NONE; + adapter->rx_stats_buffers[scrq->scrq_num].interrupts++; if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) { diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index e69d49d91d67d4bcba203a945681c49be9346cb5..914258310ddd8fb84c26bb413b6f25c9a7d2fd35 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -815,8 +815,12 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) if (vid >= VLAN_N_VID) return -EINVAL; - /* Verify we have permission to add VLANs */ - if (hw->mac.vlan_override) + /* Verify that we have permission to add VLANs. If this is a request + * to remove a VLAN, we still want to allow the user to remove the + * VLAN device. In that case, we need to clear the bit in the + * active_vlans bitmask. + */ + if (set && hw->mac.vlan_override) return -EACCES; /* update active_vlans bitmask */ @@ -835,6 +839,12 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) rx_ring->vid &= ~FM10K_VLAN_CLEAR; } + /* If our VLAN has been overridden, there is no reason to send VLAN + * removal requests as they will be silently ignored. + */ + if (hw->mac.vlan_override) + return 0; + /* Do not remove default VLAN ID related entries from VLAN and MAC * tables */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 05e89864f781c361289f0a3170078a661988389b..ef22793d6a032b95d2a9b310c3427cf7a8ac8db2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2588,16 +2588,16 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, no_input_set: if (input_set & I40E_L3_SRC_MASK) - fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFF); + fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFFFFFF); if (input_set & I40E_L3_DST_MASK) - fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFF); + fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFFFFFF); if (input_set & I40E_L4_SRC_MASK) - fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFFFFFF); + fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFF); if (input_set & I40E_L4_DST_MASK) - fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFFFFFF); + fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFF); if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET) fsp->ring_cookie = RX_CLS_FLOW_DISC; @@ -3648,6 +3648,16 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, i40e_write_fd_input_set(pf, index, new_mask); + /* IP_USER_FLOW filters match both IPv4/Other and IPv4/Fragmented + * frames. If we're programming the input set for IPv4/Other, we also + * need to program the IPv4/Fragmented input set. Since we don't have + * separate support, we'll always assume and enforce that the two flow + * types must have matching input sets. + */ + if (index == I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_FRAG_IPV4, + new_mask); + /* Add the new offset and update table, if necessary */ if (new_flex_offset) { err = i40e_add_flex_offset(&pf->l4_flex_pit_list, src_offset, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b1cde1b051a464c80c198a45c80ceffc8cdae0ff..d36b799116e4839ac62a3d2a4346510078773a2a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5828,6 +5828,9 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf) /* Reprogram the default input set for Other/IPv4 */ i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_OTHER, I40E_L3_SRC_MASK | I40E_L3_DST_MASK); + + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_FRAG_IPV4, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK); } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index e368b0237a1b64115a02c69974a760108b35bb8a..4a85a24ced1c81595b54ea98a533235d5713d147 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2781,6 +2781,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) int ret = 0; struct hlist_node *h; int bkt; + u8 i; /* validate the request */ if (vf_id >= pf->num_alloc_vfs) { @@ -2792,6 +2793,16 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_idx]; + + /* When the VF is resetting wait until it is done. + * It can take up to 200 milliseconds, + * but wait for up to 300 milliseconds to be safe. + */ + for (i = 0; i < 15; i++) { + if (test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) + break; + msleep(20); + } if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", vf_id); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 82f69031e5cdbc23827e8b743ff8d69b2072a211..2ef32ab1dfae89ab26803f74bed36a76cc29dc88 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -186,6 +186,7 @@ enum i40evf_state_t { enum i40evf_critical_section_t { __I40EVF_IN_CRITICAL_TASK, /* cannot be interrupted */ __I40EVF_IN_CLIENT_TASK, + __I40EVF_IN_REMOVE_TASK, /* device being removed */ }; /* board specific private data structure */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 1ccad6f30ebf485666338a849a262468620acb0c..1b5d204c57c141cc05efb6432148efb2d9983b57 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1775,7 +1775,11 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter) adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; - if (netif_running(adapter->netdev)) { + /* We don't use netif_running() because it may be true prior to + * ndo_open() returning, so we can't assume it means all our open + * tasks have finished, since we're not holding the rtnl_lock here. + */ + if (adapter->state == __I40EVF_RUNNING) { set_bit(__I40E_VSI_DOWN, adapter->vsi.state); netif_carrier_off(adapter->netdev); netif_tx_disable(adapter->netdev); @@ -1833,6 +1837,13 @@ static void i40evf_reset_task(struct work_struct *work) struct i40evf_mac_filter *f; u32 reg_val; int i = 0, err; + bool running; + + /* When device is being removed it doesn't make sense to run the reset + * task, just return in such a case. + */ + if (test_bit(__I40EVF_IN_REMOVE_TASK, &adapter->crit_section)) + return; while (test_and_set_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section)) @@ -1892,7 +1903,13 @@ static void i40evf_reset_task(struct work_struct *work) } continue_reset: - if (netif_running(netdev)) { + /* We don't use netif_running() because it may be true prior to + * ndo_open() returning, so we can't assume it means all our open + * tasks have finished, since we're not holding the rtnl_lock here. + */ + running = (adapter->state == __I40EVF_RUNNING); + + if (running) { netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); adapter->link_up = false; @@ -1936,7 +1953,10 @@ static void i40evf_reset_task(struct work_struct *work) mod_timer(&adapter->watchdog_timer, jiffies + 2); - if (netif_running(adapter->netdev)) { + /* We were running when the reset started, so we need to restore some + * state here. + */ + if (running) { /* allocate transmit descriptors */ err = i40evf_setup_all_tx_resources(adapter); if (err) @@ -3008,7 +3028,8 @@ static void i40evf_remove(struct pci_dev *pdev) struct i40evf_mac_filter *f, *ftmp; struct i40e_hw *hw = &adapter->hw; int err; - + /* Indicate we are in remove and not to run reset_task */ + set_bit(__I40EVF_IN_REMOVE_TASK, &adapter->crit_section); cancel_delayed_work_sync(&adapter->init_task); cancel_work_sync(&adapter->reset_task); cancel_delayed_work_sync(&adapter->client_task); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 85876f4fb1fb2987ee179a0763044e81704e4ef8..46bf11afba08511075a737e8eb9f09edde4c3ebb 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -937,23 +937,34 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, if (v_opcode == VIRTCHNL_OP_EVENT) { struct virtchnl_pf_event *vpe = (struct virtchnl_pf_event *)msg; + bool link_up = vpe->event_data.link_event.link_status; switch (vpe->event) { case VIRTCHNL_EVENT_LINK_CHANGE: adapter->link_speed = vpe->event_data.link_event.link_speed; - if (adapter->link_up != - vpe->event_data.link_event.link_status) { - adapter->link_up = - vpe->event_data.link_event.link_status; - if (adapter->link_up) { - netif_tx_start_all_queues(netdev); - netif_carrier_on(netdev); - } else { - netif_tx_stop_all_queues(netdev); - netif_carrier_off(netdev); - } - i40evf_print_link_message(adapter); + + /* we've already got the right link status, bail */ + if (adapter->link_up == link_up) + break; + + /* If we get link up message and start queues before + * our queues are configured it will trigger a TX hang. + * In that case, just ignore the link status message, + * we'll get another one after we enable queues and + * actually prepared to send traffic. + */ + if (link_up && adapter->state != __I40EVF_RUNNING) + break; + + adapter->link_up = link_up; + if (link_up) { + netif_tx_start_all_queues(netdev); + netif_carrier_on(netdev); + } else { + netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); } + i40evf_print_link_message(adapter); break; case VIRTCHNL_EVENT_RESET_IMPENDING: dev_info(&adapter->pdev->dev, "PF reset warning received\n"); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index d1a44a84c97e68cc94257fe0ddb73645023d2192..6ca580cdfd843e792783daedb085835838358180 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -8373,7 +8373,8 @@ static void igb_rar_set_index(struct igb_adapter *adapter, u32 index) /* Indicate to hardware the Address is Valid. */ if (adapter->mac_table[index].state & IGB_MAC_STATE_IN_USE) { - rar_high |= E1000_RAH_AV; + if (is_valid_ether_addr(addr)) + rar_high |= E1000_RAH_AV; if (hw->mac.type == e1000_82575) rar_high |= E1000_RAH_POOL_1 * @@ -8411,17 +8412,36 @@ static int igb_set_vf_mac(struct igb_adapter *adapter, static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) { struct igb_adapter *adapter = netdev_priv(netdev); - if (!is_valid_ether_addr(mac) || (vf >= adapter->vfs_allocated_count)) + + if (vf >= adapter->vfs_allocated_count) + return -EINVAL; + + /* Setting the VF MAC to 0 reverts the IGB_VF_FLAG_PF_SET_MAC + * flag and allows to overwrite the MAC via VF netdev. This + * is necessary to allow libvirt a way to restore the original + * MAC after unbinding vfio-pci and reloading igbvf after shutting + * down a VM. + */ + if (is_zero_ether_addr(mac)) { + adapter->vf_data[vf].flags &= ~IGB_VF_FLAG_PF_SET_MAC; + dev_info(&adapter->pdev->dev, + "remove administratively set MAC on VF %d\n", + vf); + } else if (is_valid_ether_addr(mac)) { + adapter->vf_data[vf].flags |= IGB_VF_FLAG_PF_SET_MAC; + dev_info(&adapter->pdev->dev, "setting MAC %pM on VF %d\n", + mac, vf); + dev_info(&adapter->pdev->dev, + "Reload the VF driver to make this change effective."); + /* Generate additional warning if PF is down */ + if (test_bit(__IGB_DOWN, &adapter->state)) { + dev_warn(&adapter->pdev->dev, + "The VF MAC address has been set, but the PF device is not up.\n"); + dev_warn(&adapter->pdev->dev, + "Bring the PF device up before attempting to use the VF device.\n"); + } + } else { return -EINVAL; - adapter->vf_data[vf].flags |= IGB_VF_FLAG_PF_SET_MAC; - dev_info(&adapter->pdev->dev, "setting MAC %pM on VF %d\n", mac, vf); - dev_info(&adapter->pdev->dev, - "Reload the VF driver to make this change effective."); - if (test_bit(__IGB_DOWN, &adapter->state)) { - dev_warn(&adapter->pdev->dev, - "The VF MAC address has been set, but the PF device is not up.\n"); - dev_warn(&adapter->pdev->dev, - "Bring the PF device up before attempting to use the VF device.\n"); } return igb_set_vf_mac(adapter, vf, mac); } diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 841c2a0833495430f49650ce416d4b992bccbd57..0746b19ec6d3765ac43b44c850c077050d9f02a0 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -643,6 +643,10 @@ static void igb_ptp_tx_work(struct work_struct *work) adapter->ptp_tx_skb = NULL; clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state); adapter->tx_hwtstamp_timeouts++; + /* Clear the tx valid bit in TSYNCTXCTL register to enable + * interrupt + */ + rd32(E1000_TXSTMPH); dev_warn(&adapter->pdev->dev, "clearing Tx timestamp hang\n"); return; } @@ -717,6 +721,7 @@ void igb_ptp_rx_hang(struct igb_adapter *adapter) */ void igb_ptp_tx_hang(struct igb_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; bool timeout = time_is_before_jiffies(adapter->ptp_tx_start + IGB_PTP_TX_TIMEOUT); @@ -736,6 +741,10 @@ void igb_ptp_tx_hang(struct igb_adapter *adapter) adapter->ptp_tx_skb = NULL; clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state); adapter->tx_hwtstamp_timeouts++; + /* Clear the tx valid bit in TSYNCTXCTL register to enable + * interrupt + */ + rd32(E1000_TXSTMPH); dev_warn(&adapter->pdev->dev, "clearing Tx timestamp hang\n"); } } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 29f600fd6977e507bb7d0afcab6ee14c66844809..9e30cfeac04b17f3a7306c615977dd6bb1951580 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3987,11 +3987,15 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, rxdctl &= ~0x3FFFFF; rxdctl |= 0x080420; #if (PAGE_SIZE < 8192) - } else { + /* RXDCTL.RLPML does not work on 82599 */ + } else if (hw->mac.type != ixgbe_mac_82599EB) { rxdctl &= ~(IXGBE_RXDCTL_RLPMLMASK | IXGBE_RXDCTL_RLPML_EN); - /* Limit the maximum frame size so we don't overrun the skb */ + /* Limit the maximum frame size so we don't overrun the skb. + * This can happen in SRIOV mode when the MTU of the VF is + * higher than the MTU of the PF. + */ if (ring_uses_build_skb(ring) && !test_bit(__IXGBE_RX_3K_BUFFER, &ring->state)) rxdctl |= IXGBE_MAX_2K_FRAME_BUILD_SKB | diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 1145cde2274a4cb778ba816716c3c55a5850d71e..b12e3a4f94397cc28c92cf85dffdb16966abdd12 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -5087,7 +5087,7 @@ static int sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_WORK(&hw->restart_work, sky2_restart); pci_set_drvdata(pdev, hw); - pdev->d3_delay = 150; + pdev->d3_delay = 200; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c index 5f41dc92aa6848aafd3e3a4b7a735ad33da984dd..752a72499b4f16ebb22c126c258ca35a0f19619d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c @@ -156,57 +156,63 @@ static int mlx4_en_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num) static u8 mlx4_en_dcbnl_set_all(struct net_device *netdev) { struct mlx4_en_priv *priv = netdev_priv(netdev); + struct mlx4_en_port_profile *prof = priv->prof; struct mlx4_en_dev *mdev = priv->mdev; + u8 tx_pause, tx_ppp, rx_pause, rx_ppp; if (!(priv->dcbx_cap & DCB_CAP_DCBX_VER_CEE)) return 1; if (priv->cee_config.pfc_state) { int tc; + rx_ppp = prof->rx_ppp; + tx_ppp = prof->tx_ppp; - priv->prof->rx_pause = 0; - priv->prof->tx_pause = 0; for (tc = 0; tc < CEE_DCBX_MAX_PRIO; tc++) { u8 tc_mask = 1 << tc; switch (priv->cee_config.dcb_pfc[tc]) { case pfc_disabled: - priv->prof->tx_ppp &= ~tc_mask; - priv->prof->rx_ppp &= ~tc_mask; + tx_ppp &= ~tc_mask; + rx_ppp &= ~tc_mask; break; case pfc_enabled_full: - priv->prof->tx_ppp |= tc_mask; - priv->prof->rx_ppp |= tc_mask; + tx_ppp |= tc_mask; + rx_ppp |= tc_mask; break; case pfc_enabled_tx: - priv->prof->tx_ppp |= tc_mask; - priv->prof->rx_ppp &= ~tc_mask; + tx_ppp |= tc_mask; + rx_ppp &= ~tc_mask; break; case pfc_enabled_rx: - priv->prof->tx_ppp &= ~tc_mask; - priv->prof->rx_ppp |= tc_mask; + tx_ppp &= ~tc_mask; + rx_ppp |= tc_mask; break; default: break; } } - en_dbg(DRV, priv, "Set pfc on\n"); + rx_pause = !!(rx_ppp || tx_ppp) ? 0 : prof->rx_pause; + tx_pause = !!(rx_ppp || tx_ppp) ? 0 : prof->tx_pause; } else { - priv->prof->rx_pause = 1; - priv->prof->tx_pause = 1; - en_dbg(DRV, priv, "Set pfc off\n"); + rx_ppp = 0; + tx_ppp = 0; + rx_pause = prof->rx_pause; + tx_pause = prof->tx_pause; } if (mlx4_SET_PORT_general(mdev->dev, priv->port, priv->rx_skb_size + ETH_FCS_LEN, - priv->prof->tx_pause, - priv->prof->tx_ppp, - priv->prof->rx_pause, - priv->prof->rx_ppp)) { + tx_pause, tx_ppp, rx_pause, rx_ppp)) { en_err(priv, "Failed setting pause params\n"); return 1; } + prof->tx_ppp = tx_ppp; + prof->rx_ppp = rx_ppp; + prof->tx_pause = tx_pause; + prof->rx_pause = rx_pause; + return 0; } @@ -310,6 +316,7 @@ static int mlx4_en_ets_validate(struct mlx4_en_priv *priv, struct ieee_ets *ets) } switch (ets->tc_tsa[i]) { + case IEEE_8021QAZ_TSA_VENDOR: case IEEE_8021QAZ_TSA_STRICT: break; case IEEE_8021QAZ_TSA_ETS: @@ -347,6 +354,10 @@ static int mlx4_en_config_port_scheduler(struct mlx4_en_priv *priv, /* higher TC means higher priority => lower pg */ for (i = IEEE_8021QAZ_MAX_TCS - 1; i >= 0; i--) { switch (ets->tc_tsa[i]) { + case IEEE_8021QAZ_TSA_VENDOR: + pg[i] = MLX4_EN_TC_VENDOR; + tc_tx_bw[i] = MLX4_EN_BW_MAX; + break; case IEEE_8021QAZ_TSA_STRICT: pg[i] = num_strict++; tc_tx_bw[i] = MLX4_EN_BW_MAX; @@ -403,6 +414,7 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev, struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_port_profile *prof = priv->prof; struct mlx4_en_dev *mdev = priv->mdev; + u32 tx_pause, tx_ppp, rx_pause, rx_ppp; int err; en_dbg(DRV, priv, "cap: 0x%x en: 0x%x mbc: 0x%x delay: %d\n", @@ -411,23 +423,26 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev, pfc->mbc, pfc->delay); - prof->rx_pause = !pfc->pfc_en; - prof->tx_pause = !pfc->pfc_en; - prof->rx_ppp = pfc->pfc_en; - prof->tx_ppp = pfc->pfc_en; + rx_pause = prof->rx_pause && !pfc->pfc_en; + tx_pause = prof->tx_pause && !pfc->pfc_en; + rx_ppp = pfc->pfc_en; + tx_ppp = pfc->pfc_en; err = mlx4_SET_PORT_general(mdev->dev, priv->port, priv->rx_skb_size + ETH_FCS_LEN, - prof->tx_pause, - prof->tx_ppp, - prof->rx_pause, - prof->rx_ppp); - if (err) + tx_pause, tx_ppp, rx_pause, rx_ppp); + if (err) { en_err(priv, "Failed setting pause params\n"); - else - mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap, - prof->rx_ppp, prof->rx_pause, - prof->tx_ppp, prof->tx_pause); + return err; + } + + mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap, + rx_ppp, rx_pause, tx_ppp, tx_pause); + + prof->tx_ppp = tx_ppp; + prof->rx_ppp = rx_ppp; + prof->rx_pause = rx_pause; + prof->tx_pause = tx_pause; return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 3d4e4a5d00d1c5f81267c4a4a9675bc667709211..67f74fcb265e77ea800d2e24aa27b0d3f8aa5322 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -1046,27 +1046,32 @@ static int mlx4_en_set_pauseparam(struct net_device *dev, { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; + u8 tx_pause, tx_ppp, rx_pause, rx_ppp; int err; if (pause->autoneg) return -EINVAL; - priv->prof->tx_pause = pause->tx_pause != 0; - priv->prof->rx_pause = pause->rx_pause != 0; + tx_pause = !!(pause->tx_pause); + rx_pause = !!(pause->rx_pause); + rx_ppp = priv->prof->rx_ppp && !(tx_pause || rx_pause); + tx_ppp = priv->prof->tx_ppp && !(tx_pause || rx_pause); + err = mlx4_SET_PORT_general(mdev->dev, priv->port, priv->rx_skb_size + ETH_FCS_LEN, - priv->prof->tx_pause, - priv->prof->tx_ppp, - priv->prof->rx_pause, - priv->prof->rx_ppp); - if (err) - en_err(priv, "Failed setting pause params\n"); - else - mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap, - priv->prof->rx_ppp, - priv->prof->rx_pause, - priv->prof->tx_ppp, - priv->prof->tx_pause); + tx_pause, tx_ppp, rx_pause, rx_ppp); + if (err) { + en_err(priv, "Failed setting pause params, err = %d\n", err); + return err; + } + + mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap, + rx_ppp, rx_pause, tx_ppp, tx_pause); + + priv->prof->tx_pause = tx_pause; + priv->prof->rx_pause = rx_pause; + priv->prof->tx_ppp = tx_ppp; + priv->prof->rx_ppp = rx_ppp; return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 686e18de9a97b3e2ef96d9daa405ba1177546228..6b2f7122b3abd403fadfaec7dcfbd7fd28839b95 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -163,9 +163,9 @@ static void mlx4_en_get_profile(struct mlx4_en_dev *mdev) params->udp_rss = 0; } for (i = 1; i <= MLX4_MAX_PORTS; i++) { - params->prof[i].rx_pause = 1; + params->prof[i].rx_pause = !(pfcrx || pfctx); params->prof[i].rx_ppp = pfcrx; - params->prof[i].tx_pause = 1; + params->prof[i].tx_pause = !(pfcrx || pfctx); params->prof[i].tx_ppp = pfctx; params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE; params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 9c218f1cfc6caf50aca61c853422f8c1767a75c8..c097eef41a9c82dd19a493848f2ee5f1061c2080 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -3335,6 +3335,13 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, priv->msg_enable = MLX4_EN_MSG_LEVEL; #ifdef CONFIG_MLX4_EN_DCB if (!mlx4_is_slave(priv->mdev->dev)) { + u8 prio; + + for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; ++prio) { + priv->ets.prio_tc[prio] = prio; + priv->ets.tc_tsa[prio] = IEEE_8021QAZ_TSA_VENDOR; + } + priv->dcbx_cap = DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; priv->flags |= MLX4_EN_DCB_ENABLED; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index fdb3ad0cbe5427c450ef4695d605402f3d8e7148..2c1a5ff6acfaf1c0f8b6b521c0c2da2220fdecb9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -476,6 +476,7 @@ struct mlx4_en_frag_info { #define MLX4_EN_BW_MIN 1 #define MLX4_EN_BW_MAX 100 /* Utilize 100% of the line */ +#define MLX4_EN_TC_VENDOR 0 #define MLX4_EN_TC_ETS 7 enum dcb_pfc_type { diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index fabb533797275f75965f037bf14f5a63d5c4b066..a069fcc823c30f765d65d5b638b4449b2e41acd1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -5089,6 +5089,7 @@ static void rem_slave_fs_rule(struct mlx4_dev *dev, int slave) &tracker->res_tree[RES_FS_RULE]); list_del(&fs_rule->com.list); spin_unlock_irq(mlx4_tlock(dev)); + kfree(fs_rule->mirr_mbox); kfree(fs_rule); state = 0; break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a863572882b2b2949b530b03a76e9246ab5bdd1b..225b2ad3e15f47822b51e3c36542649d3f1f3181 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2718,6 +2718,9 @@ int mlx5e_open(struct net_device *netdev) mlx5_set_port_admin_status(priv->mdev, MLX5_PORT_UP); mutex_unlock(&priv->state_lock); + if (mlx5e_vxlan_allowed(priv->mdev)) + udp_tunnel_get_rx_info(netdev); + return err; } @@ -4276,13 +4279,6 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) if (netdev->reg_state != NETREG_REGISTERED) return; - /* Device already registered: sync netdev system state */ - if (mlx5e_vxlan_allowed(mdev)) { - rtnl_lock(); - udp_tunnel_get_rx_info(netdev); - rtnl_unlock(); - } - queue_work(priv->wq, &priv->set_rx_mode_work); rtnl_lock(); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 45e03c427faf93970a0b35bb17e7f6a0d46fa537..5ffd1db4e797693b57aff20ca7c374f14d9a615b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -43,6 +43,11 @@ #include "en_tc.h" #include "fs_core.h" +#define MLX5E_REP_PARAMS_LOG_SQ_SIZE \ + max(0x6, MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE) +#define MLX5E_REP_PARAMS_LOG_RQ_SIZE \ + max(0x6, MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE) + static const char mlx5e_rep_driver_name[] = "mlx5e_rep"; static void mlx5e_rep_get_drvinfo(struct net_device *dev, @@ -230,7 +235,7 @@ void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) static void mlx5e_rep_neigh_update_init_interval(struct mlx5e_rep_priv *rpriv) { #if IS_ENABLED(CONFIG_IPV6) - unsigned long ipv6_interval = NEIGH_VAR(&ipv6_stub->nd_tbl->parms, + unsigned long ipv6_interval = NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME); #else unsigned long ipv6_interval = ~0UL; @@ -366,7 +371,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb, case NETEVENT_NEIGH_UPDATE: n = ptr; #if IS_ENABLED(CONFIG_IPV6) - if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl) + if (n->tbl != &nd_tbl && n->tbl != &arp_tbl) #else if (n->tbl != &arp_tbl) #endif @@ -414,7 +419,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb, * done per device delay prob time parameter. */ #if IS_ENABLED(CONFIG_IPV6) - if (!p->dev || (p->tbl != ipv6_stub->nd_tbl && p->tbl != &arp_tbl)) + if (!p->dev || (p->tbl != &nd_tbl && p->tbl != &arp_tbl)) #else if (!p->dev || p->tbl != &arp_tbl) #endif @@ -610,7 +615,6 @@ static int mlx5e_rep_open(struct net_device *dev) struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *rep = rpriv->rep; - struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; int err; mutex_lock(&priv->state_lock); @@ -618,8 +622,9 @@ static int mlx5e_rep_open(struct net_device *dev) if (err) goto unlock; - if (!mlx5_eswitch_set_vport_state(esw, rep->vport, - MLX5_ESW_VPORT_ADMIN_STATE_UP)) + if (!mlx5_modify_vport_admin_state(priv->mdev, + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, + rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_UP)) netif_carrier_on(dev); unlock: @@ -632,11 +637,12 @@ static int mlx5e_rep_close(struct net_device *dev) struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *rep = rpriv->rep; - struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; int ret; mutex_lock(&priv->state_lock); - (void)mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN); + mlx5_modify_vport_admin_state(priv->mdev, + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, + rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN); ret = mlx5e_close_locked(dev); mutex_unlock(&priv->state_lock); return ret; @@ -797,9 +803,9 @@ static void mlx5e_build_rep_params(struct mlx5_core_dev *mdev, MLX5_CQ_PERIOD_MODE_START_FROM_CQE : MLX5_CQ_PERIOD_MODE_START_FROM_EQE; - params->log_sq_size = MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE; + params->log_sq_size = MLX5E_REP_PARAMS_LOG_SQ_SIZE; params->rq_wq_type = MLX5_WQ_TYPE_LINKED_LIST; - params->log_rq_size = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE; + params->log_rq_size = MLX5E_REP_PARAMS_LOG_RQ_SIZE; params->rx_am_enabled = MLX5_CAP_GEN(mdev, cq_moderation); mlx5e_set_rx_cq_mode_params(params, cq_period_mode); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 9ba1f72060aae4c57a55c9d7a4c049e1a6f8a69b..42bab73a9f408b82b7d4bef1d6dde7c627180927 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -484,7 +484,7 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) tbl = &arp_tbl; #if IS_ENABLED(CONFIG_IPV6) else if (m_neigh->family == AF_INET6) - tbl = ipv6_stub->nd_tbl; + tbl = &nd_tbl; #endif else return; @@ -2091,19 +2091,19 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, if (err != -EAGAIN) flow->flags |= MLX5E_TC_FLOW_OFFLOADED; + if (!(flow->flags & MLX5E_TC_FLOW_ESWITCH) || + !(flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP)) + kvfree(parse_attr); + err = rhashtable_insert_fast(&tc->ht, &flow->node, tc->ht_params); - if (err) - goto err_del_rule; + if (err) { + mlx5e_tc_del_flow(priv, flow); + kfree(flow); + } - if (flow->flags & MLX5E_TC_FLOW_ESWITCH && - !(flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP)) - kvfree(parse_attr); return err; -err_del_rule: - mlx5e_tc_del_flow(priv, flow); - err_free: kvfree(parse_attr); kfree(flow); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index a1296a62497dab01e43d6293d902548af8b6195e..71153c0f16054a2e987972b067c0070a54863e88 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -36,6 +36,9 @@ #include #include "mlx5_core.h" +/* Mutex to hold while enabling or disabling RoCE */ +static DEFINE_MUTEX(mlx5_roce_en_lock); + static int _mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport, u32 *out, int outlen) { @@ -998,17 +1001,35 @@ static int mlx5_nic_vport_update_roce_state(struct mlx5_core_dev *mdev, int mlx5_nic_vport_enable_roce(struct mlx5_core_dev *mdev) { - if (atomic_inc_return(&mdev->roce.roce_en) != 1) - return 0; - return mlx5_nic_vport_update_roce_state(mdev, MLX5_VPORT_ROCE_ENABLED); + int err = 0; + + mutex_lock(&mlx5_roce_en_lock); + if (!mdev->roce.roce_en) + err = mlx5_nic_vport_update_roce_state(mdev, MLX5_VPORT_ROCE_ENABLED); + + if (!err) + mdev->roce.roce_en++; + mutex_unlock(&mlx5_roce_en_lock); + + return err; } EXPORT_SYMBOL_GPL(mlx5_nic_vport_enable_roce); int mlx5_nic_vport_disable_roce(struct mlx5_core_dev *mdev) { - if (atomic_dec_return(&mdev->roce.roce_en) != 0) - return 0; - return mlx5_nic_vport_update_roce_state(mdev, MLX5_VPORT_ROCE_DISABLED); + int err = 0; + + mutex_lock(&mlx5_roce_en_lock); + if (mdev->roce.roce_en) { + mdev->roce.roce_en--; + if (mdev->roce.roce_en == 0) + err = mlx5_nic_vport_update_roce_state(mdev, MLX5_VPORT_ROCE_DISABLED); + + if (err) + mdev->roce.roce_en++; + } + mutex_unlock(&mlx5_roce_en_lock); + return err; } EXPORT_SYMBOL_GPL(mlx5_nic_vport_disable_roce); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index f8fa63b66739dfcc081b623ff7de717bb5928445..a1a15e0c22452d99d453355db27e55c4b70bae34 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -492,6 +492,7 @@ static int nfp_pci_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "Error: %d VFs already enabled, but loaded FW can only support %d\n", pf->num_vfs, pf->limit_vfs); + err = -EINVAL; goto err_fw_unload; } diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 37364555c42b3fe60438713c4b7ffd1ca3d4a9a6..f88ff3f4b6612912d33b75b7570641b8e98e8f67 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -68,10 +68,11 @@ /* CPP address to retrieve the data from */ #define NSP_BUFFER 0x10 #define NSP_BUFFER_CPP GENMASK_ULL(63, 40) -#define NSP_BUFFER_PCIE GENMASK_ULL(39, 38) -#define NSP_BUFFER_ADDRESS GENMASK_ULL(37, 0) +#define NSP_BUFFER_ADDRESS GENMASK_ULL(39, 0) #define NSP_DFLT_BUFFER 0x18 +#define NSP_DFLT_BUFFER_CPP GENMASK_ULL(63, 40) +#define NSP_DFLT_BUFFER_ADDRESS GENMASK_ULL(39, 0) #define NSP_DFLT_BUFFER_CONFIG 0x20 #define NSP_DFLT_BUFFER_SIZE_MB GENMASK_ULL(7, 0) @@ -412,8 +413,8 @@ static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, if (err < 0) return err; - cpp_id = FIELD_GET(NSP_BUFFER_CPP, reg) << 8; - cpp_buf = FIELD_GET(NSP_BUFFER_ADDRESS, reg); + cpp_id = FIELD_GET(NSP_DFLT_BUFFER_CPP, reg) << 8; + cpp_buf = FIELD_GET(NSP_DFLT_BUFFER_ADDRESS, reg); if (in_buf && in_size) { err = nfp_cpp_write(cpp, cpp_id, cpp_buf, in_buf, in_size); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 7c120b0e5126f436f06ddd38223094a2e9628815..8416c9a27765a6bebc367f9d3732fa6d02282b81 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -21,6 +21,7 @@ #include "rmnet_handlers.h" #include "rmnet_vnd.h" #include "rmnet_private.h" +#include "rmnet_map.h" /* Locking scheme - * The shared resource which needs to be protected is realdev->rx_handler_data. @@ -66,6 +67,8 @@ static int rmnet_unregister_real_device(struct net_device *real_dev, if (port->nr_rmnet_devs) return -EINVAL; + rmnet_map_tx_aggregate_exit(port); + kfree(port); netdev_rx_handler_unregister(real_dev); @@ -104,6 +107,8 @@ static int rmnet_register_real_device(struct net_device *real_dev) for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++) INIT_HLIST_HEAD(&port->muxed_ep[entry]); + rmnet_map_tx_aggregate_init(port); + netdev_dbg(real_dev, "registered with rmnet\n"); return 0; } @@ -136,7 +141,8 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION; + u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION | + RMNET_EGRESS_FORMAT_AGGREGATION; struct net_device *real_dev; int mode = RMNET_EPMODE_VND; struct rmnet_endpoint *ep; @@ -312,6 +318,8 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[], if (data[IFLA_RMNET_MUX_ID]) { mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]); ep = rmnet_get_endpoint(port, priv->mux_id); + if (!ep) + return -ENODEV; hlist_del_init_rcu(&ep->hlnode); hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]); @@ -348,15 +356,16 @@ static int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev) real_dev = priv->real_dev; - if (!rmnet_is_real_dev_registered(real_dev)) - return -ENODEV; - if (nla_put_u16(skb, IFLA_RMNET_MUX_ID, priv->mux_id)) goto nla_put_failure; - port = rmnet_get_port_rtnl(real_dev); + if (rmnet_is_real_dev_registered(real_dev)) { + port = rmnet_get_port_rtnl(real_dev); + f.flags = port->data_format; + } else { + f.flags = 0; + } - f.flags = port->data_format; f.mask = ~0; if (nla_put(skb, IFLA_RMNET_FLAGS, sizeof(f), &f)) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index af2d2dea1162f01759a642473a45263f7c2202d4..fd2c94e337d888e5db6b445100289039d8b607a8 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -37,6 +37,19 @@ struct rmnet_port { u8 rmnet_mode; struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP]; struct net_device *bridge_ep; + + u16 egress_agg_size; + u16 egress_agg_count; + + /* Protect aggregation related elements */ + spinlock_t agg_lock; + + struct sk_buff *agg_skb; + int agg_state; + u8 agg_count; + struct timespec agg_time; + struct timespec agg_last; + struct hrtimer hrtimer; }; extern struct rtnl_link_ops rmnet_link_ops; @@ -54,11 +67,24 @@ struct rmnet_pcpu_stats { struct u64_stats_sync syncp; }; +struct rmnet_priv_stats { + u64 csum_ok; + u64 csum_valid_unset; + u64 csum_validation_failed; + u64 csum_err_bad_buffer; + u64 csum_err_invalid_ip_version; + u64 csum_err_invalid_transport; + u64 csum_fragmented_pkt; + u64 csum_skipped; + u64 csum_sw; +}; + struct rmnet_priv { u8 mux_id; struct net_device *real_dev; struct rmnet_pcpu_stats __percpu *pcpu_stats; struct gro_cells gro_cells; + struct rmnet_priv_stats stats; }; struct rmnet_port *rmnet_get_port(struct net_device *real_dev); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 253b0acc9c83b21cb3cf192e7cefe31c27d48331..7350852311be8e4d1bd0ac064d99a86c43322c96 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -179,7 +179,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, if (skb_headroom(skb) < required_headroom) { if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) - goto fail; + return -ENOMEM; } if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) @@ -187,17 +187,31 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); if (!map_header) - goto fail; + return -ENOMEM; map_header->mux_id = mux_id; - skb->protocol = htons(ETH_P_MAP); + if (port->data_format & RMNET_EGRESS_FORMAT_AGGREGATION) { + int non_linear_skb; - return 0; + if (rmnet_map_tx_agg_skip(skb, required_headroom)) + goto done; -fail: - kfree_skb(skb); - return -ENOMEM; + non_linear_skb = (orig_dev->features & NETIF_F_GSO) && + skb_is_nonlinear(skb); + + if (non_linear_skb) { + if (unlikely(__skb_linearize(skb))) + goto done; + } + + rmnet_map_tx_aggregate(skb, port); + return -EINPROGRESS; + } + +done: + skb->protocol = htons(ETH_P_MAP); + return 0; } static void @@ -250,6 +264,7 @@ void rmnet_egress_handler(struct sk_buff *skb) struct rmnet_port *port; struct rmnet_priv *priv; u8 mux_id; + int err; sk_pacing_shift_update(skb->sk, 8); @@ -259,15 +274,21 @@ void rmnet_egress_handler(struct sk_buff *skb) mux_id = priv->mux_id; port = rmnet_get_port(skb->dev); - if (!port) { - kfree_skb(skb); - return; - } + if (!port) + goto drop; - if (rmnet_map_egress_handler(skb, port, mux_id, orig_dev)) + err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev); + if (err == -ENOMEM) + goto drop; + else if (err == -EINPROGRESS) return; rmnet_vnd_tx_fixup(skb, orig_dev); dev_queue_xmit(skb); + return; + +drop: + this_cpu_inc(priv->pcpu_stats->stats.tx_drops); + kfree_skb(skb); } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index 884f1f52dcc25e88713a28978bb9bdaa4bc3a320..13d5e1af77427e0de5f6737550127ac4d968164e 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -91,5 +91,9 @@ void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port); int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len); void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, struct net_device *orig_dev); +int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset); +void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port); +void rmnet_map_tx_aggregate_init(struct rmnet_port *port); +void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c index 78fdad0c6f76b1358906f99f095cd9abb2a27562..56a93df962e6a66c83653e720b4c4571679fdb73 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c @@ -69,17 +69,9 @@ static void rmnet_map_send_ack(struct sk_buff *skb, struct rmnet_map_control_command *cmd; int xmit_status; - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { - if (skb->len < sizeof(struct rmnet_map_header) + - RMNET_MAP_GET_LENGTH(skb) + - sizeof(struct rmnet_map_dl_csum_trailer)) { - kfree_skb(skb); - return; - } - - skb_trim(skb, skb->len - - sizeof(struct rmnet_map_dl_csum_trailer)); - } + if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) + skb_trim(skb, + skb->len - sizeof(struct rmnet_map_dl_csum_trailer)); skb->protocol = htons(ETH_P_MAP); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index a6ea09416f8ddac418da84aea6dc3ca2ce2c4de5..623ddba1494586f5a63e03d3492fe5bd66e16d3e 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -48,7 +48,8 @@ static __sum16 *rmnet_map_get_csum_field(unsigned char protocol, static int rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb, - struct rmnet_map_dl_csum_trailer *csum_trailer) + struct rmnet_map_dl_csum_trailer *csum_trailer, + struct rmnet_priv *priv) { __sum16 *csum_field, csum_temp, pseudo_csum, hdr_csum, ip_payload_csum; u16 csum_value, csum_value_final; @@ -58,19 +59,25 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb, ip4h = (struct iphdr *)(skb->data); if ((ntohs(ip4h->frag_off) & IP_MF) || - ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) + ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) { + priv->stats.csum_fragmented_pkt++; return -EOPNOTSUPP; + } txporthdr = skb->data + ip4h->ihl * 4; csum_field = rmnet_map_get_csum_field(ip4h->protocol, txporthdr); - if (!csum_field) + if (!csum_field) { + priv->stats.csum_err_invalid_transport++; return -EPROTONOSUPPORT; + } /* RFC 768 - Skip IPv4 UDP packets where sender checksum field is 0 */ - if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) + if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) { + priv->stats.csum_skipped++; return 0; + } csum_value = ~ntohs(csum_trailer->csum_value); hdr_csum = ~ip_fast_csum(ip4h, (int)ip4h->ihl); @@ -102,16 +109,20 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb, } } - if (csum_value_final == ntohs((__force __be16)*csum_field)) + if (csum_value_final == ntohs((__force __be16)*csum_field)) { + priv->stats.csum_ok++; return 0; - else + } else { + priv->stats.csum_validation_failed++; return -EINVAL; + } } #if IS_ENABLED(CONFIG_IPV6) static int rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb, - struct rmnet_map_dl_csum_trailer *csum_trailer) + struct rmnet_map_dl_csum_trailer *csum_trailer, + struct rmnet_priv *priv) { __sum16 *csum_field, ip6_payload_csum, pseudo_csum, csum_temp; u16 csum_value, csum_value_final; @@ -125,8 +136,10 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb, txporthdr = skb->data + sizeof(struct ipv6hdr); csum_field = rmnet_map_get_csum_field(ip6h->nexthdr, txporthdr); - if (!csum_field) + if (!csum_field) { + priv->stats.csum_err_invalid_transport++; return -EPROTONOSUPPORT; + } csum_value = ~ntohs(csum_trailer->csum_value); ip6_hdr_csum = (__force __be16) @@ -164,10 +177,13 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb, } } - if (csum_value_final == ntohs((__force __be16)*csum_field)) + if (csum_value_final == ntohs((__force __be16)*csum_field)) { + priv->stats.csum_ok++; return 0; - else + } else { + priv->stats.csum_validation_failed++; return -EINVAL; + } } #endif @@ -339,24 +355,34 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, */ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len) { + struct rmnet_priv *priv = netdev_priv(skb->dev); struct rmnet_map_dl_csum_trailer *csum_trailer; - if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) + if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) { + priv->stats.csum_sw++; return -EOPNOTSUPP; + } csum_trailer = (struct rmnet_map_dl_csum_trailer *)(skb->data + len); - if (!csum_trailer->valid) + if (!csum_trailer->valid) { + priv->stats.csum_valid_unset++; return -EINVAL; + } - if (skb->protocol == htons(ETH_P_IP)) - return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer); - else if (skb->protocol == htons(ETH_P_IPV6)) + if (skb->protocol == htons(ETH_P_IP)) { + return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer, priv); + } else if (skb->protocol == htons(ETH_P_IPV6)) { #if IS_ENABLED(CONFIG_IPV6) - return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer); + return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer, priv); #else + priv->stats.csum_err_invalid_ip_version++; return -EPROTONOSUPPORT; #endif + } else { + priv->stats.csum_err_invalid_ip_version++; + return -EPROTONOSUPPORT; + } return 0; } @@ -367,6 +393,7 @@ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len) void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, struct net_device *orig_dev) { + struct rmnet_priv *priv = netdev_priv(orig_dev); struct rmnet_map_ul_csum_header *ul_header; void *iphdr; @@ -389,8 +416,11 @@ void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb); return; #else + priv->stats.csum_err_invalid_ip_version++; goto sw_csum; #endif + } else { + priv->stats.csum_err_invalid_ip_version++; } } @@ -399,4 +429,199 @@ void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, ul_header->csum_insert_offset = 0; ul_header->csum_enabled = 0; ul_header->udp_ip4_ind = 0; + + priv->stats.csum_sw++; +} + +struct rmnet_agg_work { + struct work_struct work; + struct rmnet_port *port; +}; + +long rmnet_agg_time_limit __read_mostly = 1000000L; +long rmnet_agg_bypass_time __read_mostly = 10000000L; + +int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset) +{ + u8 *packet_start = skb->data + offset; + int is_icmp = 0; + + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *ip4h = (struct iphdr *)(packet_start); + + if (ip4h->protocol == IPPROTO_ICMP) + is_icmp = 1; + } else if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start); + + if (ip6h->nexthdr == IPPROTO_ICMPV6) { + is_icmp = 1; + } else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) { + struct frag_hdr *frag; + + frag = (struct frag_hdr *)(packet_start + + sizeof(struct ipv6hdr)); + if (frag->nexthdr == IPPROTO_ICMPV6) + is_icmp = 1; + } + } + + return is_icmp; +} + +static void rmnet_map_flush_tx_packet_work(struct work_struct *work) +{ + struct rmnet_agg_work *real_work; + struct rmnet_port *port; + unsigned long flags; + struct sk_buff *skb; + int agg_count = 0; + + real_work = (struct rmnet_agg_work *)work; + port = real_work->port; + skb = NULL; + + spin_lock_irqsave(&port->agg_lock, flags); + if (likely(port->agg_state == -EINPROGRESS)) { + /* Buffer may have already been shipped out */ + if (likely(port->agg_skb)) { + skb = port->agg_skb; + agg_count = port->agg_count; + port->agg_skb = NULL; + port->agg_count = 0; + memset(&port->agg_time, 0, sizeof(struct timespec)); + } + port->agg_state = 0; + } + + spin_unlock_irqrestore(&port->agg_lock, flags); + if (skb) { + skb->protocol = htons(ETH_P_MAP); + dev_queue_xmit(skb); + } + + kfree(work); +} + +enum hrtimer_restart rmnet_map_flush_tx_packet_queue(struct hrtimer *t) +{ + struct rmnet_agg_work *work; + struct rmnet_port *port; + + port = container_of(t, struct rmnet_port, hrtimer); + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (!work) { + port->agg_state = 0; + + return HRTIMER_NORESTART; + } + + INIT_WORK(&work->work, rmnet_map_flush_tx_packet_work); + work->port = port; + schedule_work((struct work_struct *)work); + return HRTIMER_NORESTART; +} + +void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port) +{ + struct timespec diff, last; + int size, agg_count = 0; + struct sk_buff *agg_skb; + unsigned long flags; + u8 *dest_buff; + +new_packet: + spin_lock_irqsave(&port->agg_lock, flags); + memcpy(&last, &port->agg_last, sizeof(struct timespec)); + getnstimeofday(&port->agg_last); + + if (!port->agg_skb) { + /* Check to see if we should agg first. If the traffic is very + * sparse, don't aggregate. We will need to tune this later + */ + diff = timespec_sub(port->agg_last, last); + + if (diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_bypass_time) { + spin_unlock_irqrestore(&port->agg_lock, flags); + skb->protocol = htons(ETH_P_MAP); + dev_queue_xmit(skb); + return; + } + + size = port->egress_agg_size - skb->len; + port->agg_skb = skb_copy_expand(skb, 0, size, GFP_ATOMIC); + if (!port->agg_skb) { + port->agg_skb = 0; + port->agg_count = 0; + memset(&port->agg_time, 0, sizeof(struct timespec)); + spin_unlock_irqrestore(&port->agg_lock, flags); + skb->protocol = htons(ETH_P_MAP); + dev_queue_xmit(skb); + return; + } + port->agg_count = 1; + getnstimeofday(&port->agg_time); + dev_kfree_skb_any(skb); + goto schedule; + } + diff = timespec_sub(port->agg_last, port->agg_time); + + if (skb->len > (port->egress_agg_size - port->agg_skb->len) || + port->agg_count >= port->egress_agg_count || + diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_time_limit) { + agg_skb = port->agg_skb; + agg_count = port->agg_count; + port->agg_skb = 0; + port->agg_count = 0; + memset(&port->agg_time, 0, sizeof(struct timespec)); + port->agg_state = 0; + spin_unlock_irqrestore(&port->agg_lock, flags); + hrtimer_cancel(&port->hrtimer); + agg_skb->protocol = htons(ETH_P_MAP); + dev_queue_xmit(agg_skb); + goto new_packet; + } + + dest_buff = skb_put(port->agg_skb, skb->len); + memcpy(dest_buff, skb->data, skb->len); + port->agg_count++; + dev_kfree_skb_any(skb); + +schedule: + if (port->agg_state != -EINPROGRESS) { + port->agg_state = -EINPROGRESS; + hrtimer_start(&port->hrtimer, ns_to_ktime(3000000), + HRTIMER_MODE_REL); + } + spin_unlock_irqrestore(&port->agg_lock, flags); +} + +void rmnet_map_tx_aggregate_init(struct rmnet_port *port) +{ + hrtimer_init(&port->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + port->hrtimer.function = rmnet_map_flush_tx_packet_queue; + port->egress_agg_size = 8192; + port->egress_agg_count = 20; + spin_lock_init(&port->agg_lock); +} + +void rmnet_map_tx_aggregate_exit(struct rmnet_port *port) +{ + unsigned long flags; + + hrtimer_cancel(&port->hrtimer); + spin_lock_irqsave(&port->agg_lock, flags); + if (port->agg_state == -EINPROGRESS) { + if (port->agg_skb) { + kfree_skb(port->agg_skb); + port->agg_skb = NULL; + port->agg_count = 0; + memset(&port->agg_time, 0, sizeof(struct timespec)); + } + + port->agg_state = 0; + } + + spin_unlock_irqrestore(&port->agg_lock, flags); } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h index b9cc4f85f2299de7661c5ee337c96fa410c31b5e..c30100ced465e718e59a7d9a579ed1ba5a5918db 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h @@ -18,6 +18,9 @@ #define RMNET_NEEDED_HEADROOM 16 #define RMNET_TX_QUEUE_LEN 1000 +/* Constants */ +#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(31) + /* Replace skb->dev to a virtual rmnet device and pass up the stack */ #define RMNET_EPMODE_VND (1) /* Pass the frame directly to another device with dev_queue_xmit() */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index 2ea16a088de8731cc96a2db88c844fa1ee0810a0..cb02e1a015c1a207db2dda91518562844d343a1f 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -152,6 +152,56 @@ static const struct net_device_ops rmnet_vnd_ops = { .ndo_get_stats64 = rmnet_get_stats64, }; +static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = { + "Checksum ok", + "Checksum valid bit not set", + "Checksum validation failed", + "Checksum error bad buffer", + "Checksum error bad ip version", + "Checksum error bad transport", + "Checksum skipped on ip fragment", + "Checksum skipped", + "Checksum computed in software", +}; + +static void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, &rmnet_gstrings_stats, + sizeof(rmnet_gstrings_stats)); + break; + } +} + +static int rmnet_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(rmnet_gstrings_stats); + default: + return -EOPNOTSUPP; + } +} + +static void rmnet_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct rmnet_priv *priv = netdev_priv(dev); + struct rmnet_priv_stats *st = &priv->stats; + + if (!data) + return; + + memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64)); +} + +static const struct ethtool_ops rmnet_ethtool_ops = { + .get_ethtool_stats = rmnet_get_ethtool_stats, + .get_strings = rmnet_get_strings, + .get_sset_count = rmnet_get_sset_count, +}; + /* Called by kernel whenever a new rmnet device is created. Sets MTU, * flags, ARP type, needed headroom, etc... */ @@ -170,6 +220,7 @@ void rmnet_vnd_setup(struct net_device *rmnet_dev) rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); rmnet_dev->needs_free_netdev = true; + rmnet_dev->ethtool_ops = &rmnet_ethtool_ops; } /* Exposed API */ diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 619a1b7281a0b76058e745696b4128c1ffcd18ca..db553d4e8d2298ca84b9f43e37350e665cee4b81 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -8466,12 +8466,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_msi_5; } + pci_set_drvdata(pdev, dev); + rc = register_netdev(dev); if (rc < 0) goto err_out_cnt_6; - pci_set_drvdata(pdev, dev); - netif_info(tp, probe, dev, "%s at 0x%p, %pM, XID %08x IRQ %d\n", rtl_chip_infos[chipset].name, ioaddr, dev->dev_addr, (u32)(RTL_R32(TxConfig) & 0x9cf0f8ff), pdev->irq); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 4404650b32c5e8a65f55445572d82d9ada7fdbde..8be4b32544ef86cd85304f6903a285f2ff1efc0e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -116,7 +116,7 @@ static int meson8b_init_clk(struct meson8b_dwmac *dwmac) snprintf(clk_name, sizeof(clk_name), "%s#m250_sel", dev_name(dev)); init.name = clk_name; init.ops = &clk_mux_ops; - init.flags = 0; + init.flags = CLK_SET_RATE_PARENT; init.parent_names = mux_parent_names; init.num_parents = MUX_CLK_NUM_PARENTS; @@ -144,7 +144,9 @@ static int meson8b_init_clk(struct meson8b_dwmac *dwmac) dwmac->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT; dwmac->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH; dwmac->m250_div.hw.init = &init; - dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO; + dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO | + CLK_DIVIDER_ROUND_CLOSEST; dwmac->m250_div_clk = devm_clk_register(dev, &dwmac->m250_div.hw); if (WARN_ON(IS_ERR(dwmac->m250_div_clk))) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 2f7d7ec59962a7050a278d53e2241024566bb66d..e1d03489ae63f1a1d4f580f7f950589b80317f41 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -562,10 +562,12 @@ static int dwmac4_irq_status(struct mac_device_info *hw, struct stmmac_extra_stats *x) { void __iomem *ioaddr = hw->pcsr; - u32 intr_status; + u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); + u32 intr_enable = readl(ioaddr + GMAC_INT_EN); int ret = 0; - intr_status = readl(ioaddr + GMAC_INT_STATUS); + /* Discard disabled bits */ + intr_status &= intr_enable; /* Not used events (e.g. MMC interrupts) are not handled. */ if ((intr_status & mmc_tx_irq)) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a5bb7b19040e9642ac166a53b19a2f448b841232..992c43b1868f97a627a475d91f2c3293286f9d1d 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -124,7 +124,7 @@ do { \ #define RX_PRIORITY_MAPPING 0x76543210 #define TX_PRIORITY_MAPPING 0x33221100 -#define CPDMA_TX_PRIORITY_MAP 0x01234567 +#define CPDMA_TX_PRIORITY_MAP 0x76543210 #define CPSW_VLAN_AWARE BIT(1) #define CPSW_ALE_VLAN_AWARE 1 diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 5aa59f41bf8c3992b5ba58e24ac8763ab264eb5f..71e2aef6b7a1b5cdb052fed4aeec2a843a1a9a4d 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -620,6 +620,10 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, lock_sock(sk); error = -EINVAL; + + if (sockaddr_len != sizeof(struct sockaddr_pppox)) + goto end; + if (sp->sa_protocol != PX_PROTO_OE) goto end; diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 6dde9a0cfe76ca3b09fcb355b26b75c3dad7c622..9b70a3af678e072cb29e498ea87e109290ceab20 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -464,7 +464,6 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, po->chan.mtu = dst_mtu(&rt->dst); if (!po->chan.mtu) po->chan.mtu = PPP_MRU; - ip_rt_put(rt); po->chan.mtu -= PPTP_HEADER_OVERHEAD; po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header); diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c index 5782733959f0e0a7648d92ae1717065eb72b85fd..f4e93f5fc2043ebb29c5b36e94afe49ec0c7d7ba 100644 --- a/drivers/net/slip/slhc.c +++ b/drivers/net/slip/slhc.c @@ -509,6 +509,10 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) if(x < 0 || x > comp->rslot_limit) goto bad; + /* Check if the cstate is initialized */ + if (!comp->rstate[x].initialized) + goto bad; + comp->flags &=~ SLF_TOSS; comp->recv_current = x; } else { @@ -673,6 +677,7 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) if (cs->cs_tcp.doff > 5) memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4); cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2; + cs->initialized = true; /* Put headers back on packet * Neither header checksum is recalculated */ diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 23cd41c82210df286ffe3a0f0efe6d4d7aaec380..8a222ae5950e46dd9977f3384ad3e7a6cd98fad8 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -261,6 +261,17 @@ static void __team_option_inst_mark_removed_port(struct team *team, } } +static bool __team_option_inst_tmp_find(const struct list_head *opts, + const struct team_option_inst *needle) +{ + struct team_option_inst *opt_inst; + + list_for_each_entry(opt_inst, opts, tmp_list) + if (opt_inst == needle) + return true; + return false; +} + static int __team_options_register(struct team *team, const struct team_option *option, size_t option_count) @@ -1061,14 +1072,11 @@ static void team_port_leave(struct team *team, struct team_port *port) } #ifdef CONFIG_NET_POLL_CONTROLLER -static int team_port_enable_netpoll(struct team *team, struct team_port *port) +static int __team_port_enable_netpoll(struct team_port *port) { struct netpoll *np; int err; - if (!team->dev->npinfo) - return 0; - np = kzalloc(sizeof(*np), GFP_KERNEL); if (!np) return -ENOMEM; @@ -1082,6 +1090,14 @@ static int team_port_enable_netpoll(struct team *team, struct team_port *port) return err; } +static int team_port_enable_netpoll(struct team_port *port) +{ + if (!port->team->dev->npinfo) + return 0; + + return __team_port_enable_netpoll(port); +} + static void team_port_disable_netpoll(struct team_port *port) { struct netpoll *np = port->np; @@ -1096,7 +1112,7 @@ static void team_port_disable_netpoll(struct team_port *port) kfree(np); } #else -static int team_port_enable_netpoll(struct team *team, struct team_port *port) +static int team_port_enable_netpoll(struct team_port *port) { return 0; } @@ -1197,11 +1213,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_dev_open; } - netif_addr_lock_bh(dev); - dev_uc_sync_multiple(port_dev, dev); - dev_mc_sync_multiple(port_dev, dev); - netif_addr_unlock_bh(dev); - err = vlan_vids_add_by_dev(port_dev, dev); if (err) { netdev_err(dev, "Failed to add vlan ids to device %s\n", @@ -1209,7 +1220,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_vids_add; } - err = team_port_enable_netpoll(team, port); + err = team_port_enable_netpoll(port); if (err) { netdev_err(dev, "Failed to enable netpoll on device %s\n", portname); @@ -1241,6 +1252,11 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_option_port_add; } + netif_addr_lock_bh(dev); + dev_uc_sync_multiple(port_dev, dev); + dev_mc_sync_multiple(port_dev, dev); + netif_addr_unlock_bh(dev); + port->index = -1; list_add_tail_rcu(&port->list, &team->port_list); team_port_enable(team, port); @@ -1265,8 +1281,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev) vlan_vids_del_by_dev(port_dev, dev); err_vids_add: - dev_uc_unsync(port_dev, dev); - dev_mc_unsync(port_dev, dev); dev_close(port_dev); err_dev_open: @@ -1903,7 +1917,7 @@ static int team_netpoll_setup(struct net_device *dev, mutex_lock(&team->lock); list_for_each_entry(port, &team->port_list, list) { - err = team_port_enable_netpoll(team, port); + err = __team_port_enable_netpoll(port); if (err) { __team_netpoll_cleanup(team); break; @@ -2563,6 +2577,14 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) if (err) goto team_put; opt_inst->changed = true; + + /* dumb/evil user-space can send us duplicate opt, + * keep only the last one + */ + if (__team_option_inst_tmp_find(&opt_inst_list, + opt_inst)) + continue; + list_add(&opt_inst->tmp_list, &opt_inst_list); } if (!opt_found) { diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 0fd45ea34312f586e1786fc8883ea36d43916633..27897184b2f69a8f0386adb16d066174a7ffe4b4 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1511,6 +1511,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, return -EINVAL; } + if (!(tun->flags & IFF_NO_PI)) + if (pi.flags & htons(CHECKSUM_UNNECESSARY)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + switch (tun->flags & TUN_TYPE_MASK) { case IFF_TUN: if (tun->flags & IFF_NO_PI) { diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 05dca3e5c93d4baf7fb975a6fef30d3d322f1aaf..178b956501a765621a5360fc2ed6f896375169af 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -895,6 +895,12 @@ static const struct usb_device_id products[] = { USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&wwan_info, +}, { + /* Cinterion AHS3 modem by GEMALTO */ + USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0055, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, + USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&wwan_info, }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index a8dd1c7a08cbe8aafa51168288946b1a4641c33f..1fb464837b3e7cfce5e2c81157aead81a4c4195c 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -928,7 +928,8 @@ static int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset, offset += 0x100; else ret = -EINVAL; - ret = lan78xx_read_raw_otp(dev, offset, length, data); + if (!ret) + ret = lan78xx_read_raw_otp(dev, offset, length, data); } return ret; @@ -2863,8 +2864,7 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) if (ret < 0) { netdev_warn(dev->net, "lan78xx_setup_irq_domain() failed : %d", ret); - kfree(pdata); - return ret; + goto out1; } dev->net->hard_header_len += TX_OVERHEAD; @@ -2872,14 +2872,32 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) /* Init all registers */ ret = lan78xx_reset(dev); + if (ret) { + netdev_warn(dev->net, "Registers INIT FAILED...."); + goto out2; + } ret = lan78xx_mdio_init(dev); + if (ret) { + netdev_warn(dev->net, "MDIO INIT FAILED....."); + goto out2; + } dev->net->flags |= IFF_MULTICAST; pdata->wol = WAKE_MAGIC; return ret; + +out2: + lan78xx_remove_irq_domain(dev); + +out1: + netdev_warn(dev->net, "Bind routine FAILED"); + cancel_work_sync(&pdata->set_multicast); + cancel_work_sync(&pdata->set_vlan); + kfree(pdata); + return ret; } static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf) @@ -2891,6 +2909,8 @@ static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf) lan78xx_remove_mdio(dev); if (pdata) { + cancel_work_sync(&pdata->set_multicast); + cancel_work_sync(&pdata->set_vlan); netif_dbg(dev, ifdown, dev->net, "free pdata"); kfree(pdata); pdata = NULL; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index b0a038e6fda043432926153b0b1fdcc188afe362..bb15b3012aa5fe90e05a4214b622671446ef5fb0 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -116,6 +116,17 @@ struct receive_queue { char name[40]; }; +/* Control VQ buffers: protected by the rtnl lock */ +struct control_buf { + struct virtio_net_ctrl_hdr hdr; + virtio_net_ctrl_ack status; + struct virtio_net_ctrl_mq mq; + u8 promisc; + u8 allmulti; + __virtio16 vid; + u64 offloads; +}; + struct virtnet_info { struct virtio_device *vdev; struct virtqueue *cvq; @@ -164,14 +175,7 @@ struct virtnet_info { struct hlist_node node; struct hlist_node node_dead; - /* Control VQ buffers: protected by the rtnl lock */ - struct virtio_net_ctrl_hdr ctrl_hdr; - virtio_net_ctrl_ack ctrl_status; - struct virtio_net_ctrl_mq ctrl_mq; - u8 ctrl_promisc; - u8 ctrl_allmulti; - u16 ctrl_vid; - u64 ctrl_offloads; + struct control_buf *ctrl; /* Ethtool settings */ u8 duplex; @@ -1340,25 +1344,25 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, /* Caller should know better */ BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)); - vi->ctrl_status = ~0; - vi->ctrl_hdr.class = class; - vi->ctrl_hdr.cmd = cmd; + vi->ctrl->status = ~0; + vi->ctrl->hdr.class = class; + vi->ctrl->hdr.cmd = cmd; /* Add header */ - sg_init_one(&hdr, &vi->ctrl_hdr, sizeof(vi->ctrl_hdr)); + sg_init_one(&hdr, &vi->ctrl->hdr, sizeof(vi->ctrl->hdr)); sgs[out_num++] = &hdr; if (out) sgs[out_num++] = out; /* Add return status. */ - sg_init_one(&stat, &vi->ctrl_status, sizeof(vi->ctrl_status)); + sg_init_one(&stat, &vi->ctrl->status, sizeof(vi->ctrl->status)); sgs[out_num] = &stat; BUG_ON(out_num + 1 > ARRAY_SIZE(sgs)); virtqueue_add_sgs(vi->cvq, sgs, out_num, 1, vi, GFP_ATOMIC); if (unlikely(!virtqueue_kick(vi->cvq))) - return vi->ctrl_status == VIRTIO_NET_OK; + return vi->ctrl->status == VIRTIO_NET_OK; /* Spin for a response, the kick causes an ioport write, trapping * into the hypervisor, so the request should be handled immediately. @@ -1367,7 +1371,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, !virtqueue_is_broken(vi->cvq)) cpu_relax(); - return vi->ctrl_status == VIRTIO_NET_OK; + return vi->ctrl->status == VIRTIO_NET_OK; } static int virtnet_set_mac_address(struct net_device *dev, void *p) @@ -1478,8 +1482,8 @@ static int _virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ)) return 0; - vi->ctrl_mq.virtqueue_pairs = cpu_to_virtio16(vi->vdev, queue_pairs); - sg_init_one(&sg, &vi->ctrl_mq, sizeof(vi->ctrl_mq)); + vi->ctrl->mq.virtqueue_pairs = cpu_to_virtio16(vi->vdev, queue_pairs); + sg_init_one(&sg, &vi->ctrl->mq, sizeof(vi->ctrl->mq)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &sg)) { @@ -1537,22 +1541,22 @@ static void virtnet_set_rx_mode(struct net_device *dev) if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX)) return; - vi->ctrl_promisc = ((dev->flags & IFF_PROMISC) != 0); - vi->ctrl_allmulti = ((dev->flags & IFF_ALLMULTI) != 0); + vi->ctrl->promisc = ((dev->flags & IFF_PROMISC) != 0); + vi->ctrl->allmulti = ((dev->flags & IFF_ALLMULTI) != 0); - sg_init_one(sg, &vi->ctrl_promisc, sizeof(vi->ctrl_promisc)); + sg_init_one(sg, &vi->ctrl->promisc, sizeof(vi->ctrl->promisc)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, sg)) dev_warn(&dev->dev, "Failed to %sable promisc mode.\n", - vi->ctrl_promisc ? "en" : "dis"); + vi->ctrl->promisc ? "en" : "dis"); - sg_init_one(sg, &vi->ctrl_allmulti, sizeof(vi->ctrl_allmulti)); + sg_init_one(sg, &vi->ctrl->allmulti, sizeof(vi->ctrl->allmulti)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, sg)) dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n", - vi->ctrl_allmulti ? "en" : "dis"); + vi->ctrl->allmulti ? "en" : "dis"); uc_count = netdev_uc_count(dev); mc_count = netdev_mc_count(dev); @@ -1598,8 +1602,8 @@ static int virtnet_vlan_rx_add_vid(struct net_device *dev, struct virtnet_info *vi = netdev_priv(dev); struct scatterlist sg; - vi->ctrl_vid = vid; - sg_init_one(&sg, &vi->ctrl_vid, sizeof(vi->ctrl_vid)); + vi->ctrl->vid = cpu_to_virtio16(vi->vdev, vid); + sg_init_one(&sg, &vi->ctrl->vid, sizeof(vi->ctrl->vid)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, &sg)) @@ -1613,8 +1617,8 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev, struct virtnet_info *vi = netdev_priv(dev); struct scatterlist sg; - vi->ctrl_vid = vid; - sg_init_one(&sg, &vi->ctrl_vid, sizeof(vi->ctrl_vid)); + vi->ctrl->vid = cpu_to_virtio16(vi->vdev, vid); + sg_init_one(&sg, &vi->ctrl->vid, sizeof(vi->ctrl->vid)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_DEL, &sg)) @@ -1912,9 +1916,9 @@ static int virtnet_restore_up(struct virtio_device *vdev) static int virtnet_set_guest_offloads(struct virtnet_info *vi, u64 offloads) { struct scatterlist sg; - vi->ctrl_offloads = cpu_to_virtio64(vi->vdev, offloads); + vi->ctrl->offloads = cpu_to_virtio64(vi->vdev, offloads); - sg_init_one(&sg, &vi->ctrl_offloads, sizeof(vi->ctrl_offloads)); + sg_init_one(&sg, &vi->ctrl->offloads, sizeof(vi->ctrl->offloads)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, &sg)) { @@ -2134,6 +2138,7 @@ static void virtnet_free_queues(struct virtnet_info *vi) kfree(vi->rq); kfree(vi->sq); + kfree(vi->ctrl); } static void _free_receive_bufs(struct virtnet_info *vi) @@ -2326,6 +2331,9 @@ static int virtnet_alloc_queues(struct virtnet_info *vi) { int i; + vi->ctrl = kzalloc(sizeof(*vi->ctrl), GFP_KERNEL); + if (!vi->ctrl) + goto err_ctrl; vi->sq = kzalloc(sizeof(*vi->sq) * vi->max_queue_pairs, GFP_KERNEL); if (!vi->sq) goto err_sq; @@ -2351,6 +2359,8 @@ static int virtnet_alloc_queues(struct virtnet_info *vi) err_rq: kfree(vi->sq); err_sq: + kfree(vi->ctrl); +err_ctrl: return -ENOMEM; } diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 67ecf2425b889ca260c84f7eb18be9f79321e29f..5c6a8ef54aec55334c05608f2dc4b7c5939806b4 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -579,12 +579,13 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s if (!IS_ERR(neigh)) { sock_confirm_neigh(skb, neigh); ret = neigh_output(neigh, skb); + rcu_read_unlock_bh(); + return ret; } rcu_read_unlock_bh(); err: - if (unlikely(ret < 0)) - vrf_tx_error(skb->dev, skb); + vrf_tx_error(skb->dev, skb); return ret; } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 252c2206cbb5b20ab90d800be36dd786e8239f09..c1772215702a8c31ec60afdf8cda3690a54715c5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -5955,9 +5955,8 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk) sta->addr, smps, err); } - if (changed & IEEE80211_RC_SUPP_RATES_CHANGED || - changed & IEEE80211_RC_NSS_CHANGED) { - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n", + if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n", sta->addr); err = ath10k_station_assoc(ar, arvif->vif, sta, true); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 396bf05c6bf69da7c1f6b43ce20848d1dbdde230..d8b041f48ca8e17ccb59fe4b099c9814d8e76a7c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2892,6 +2892,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) struct ath_txq *txq; int tidno; + rcu_read_lock(); + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { tid = ath_node_to_tid(an, tidno); txq = tid->txq; @@ -2909,6 +2911,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) if (!an->sta) break; /* just one multicast ath_atx_tid */ } + + rcu_read_unlock(); } #ifdef CONFIG_ATH9K_TX99 diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 7193eddadbba98c5f84d07d5eadf22f4c33b9875..2bca87b06c653adf5386708ecf8f808c5f607b54 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -10,6 +10,7 @@ wil6210-y += sysfs.o wil6210-y += wmi.o wil6210-y += interrupt.o wil6210-y += txrx.o +wil6210-y += txrx_edma.o wil6210-y += debug.o wil6210-y += rx_reorder.o wil6210-y += ioctl.o diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 193de567cdd56df7a7ff70a389502718581898b8..4719dd5a1ef6a816758247be1433812d2ae8b4bf 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -30,6 +30,9 @@ static u32 mem_addr; static u32 dbg_txdesc_index; static u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */ +static u32 dbg_status_msg_index; +/* 0..wil->num_rx_status_rings-1 for Rx, wil->tx_sring_idx for Tx */ +static u32 dbg_sring_index; enum dbg_off_type { doff_u32 = 0, @@ -47,6 +50,36 @@ struct dbg_off { enum dbg_off_type type; }; +static void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil, + struct wil_ring *ring, + char _s, char _h, int idx) +{ + u8 num_of_descs; + bool has_skb = false; + + if (ring->is_rx) { + struct wil_rx_enhanced_desc *rx_d = + (struct wil_rx_enhanced_desc *) + &ring->va[idx].rx.enhanced; + u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); + + has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; + seq_printf(s, "%c", (has_skb) ? _h : _s); + } else { + struct wil_tx_enhanced_desc *d = + (struct wil_tx_enhanced_desc *) + &ring->va[idx].tx.enhanced; + + num_of_descs = (u8)d->mac.d[2]; + has_skb = ring->ctx[idx].skb; + if (num_of_descs >= 1) + seq_printf(s, "%c", ring->ctx[idx].skb ? _h : _s); + else + /* num_of_descs == 0, it's a frag in a list of descs */ + seq_printf(s, "%c", has_skb ? 'h' : _s); + } +} + static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, const char *name, struct wil_ring *ring, char _s, char _h) @@ -58,7 +91,10 @@ static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, seq_printf(s, " pa = %pad\n", &ring->pa); seq_printf(s, " va = 0x%p\n", ring->va); seq_printf(s, " size = %d\n", ring->size); - seq_printf(s, " swtail = %d\n", ring->swtail); + if (wil->use_enhanced_dma_hw && ring->is_rx) + seq_printf(s, " swtail = %u\n", *ring->edma_rx_swtail.va); + else + seq_printf(s, " swtail = %d\n", ring->swtail); seq_printf(s, " swhead = %d\n", ring->swhead); seq_printf(s, " hwtail = [0x%08x] -> ", ring->hwtail); if (x) { @@ -72,13 +108,16 @@ static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, uint i; for (i = 0; i < ring->size; i++) { - volatile struct vring_tx_desc *d = - &ring->va[i].tx.legacy; - - if ((i % 128) == 0 && (i != 0)) + if ((i % 128) == 0 && i != 0) seq_puts(s, "\n"); - seq_printf(s, "%c", (d->dma.status & BIT(0)) ? - _s : (ring->ctx[i].skb ? _h : 'h')); + if (wil->use_enhanced_dma_hw) { + wil_print_desc_edma(s, wil, ring, _s, _h, i); + } else { + volatile struct vring_tx_desc *d = + &ring->va[i].tx.legacy; + seq_printf(s, "%c", (d->dma.status & BIT(0)) ? + _s : (ring->ctx[i].skb ? _h : 'h')); + } } seq_puts(s, "\n"); } @@ -157,6 +196,74 @@ static const struct file_operations fops_ring = { .llseek = seq_lseek, }; +static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil, + struct wil_status_ring *sring) +{ + void __iomem *x = wmi_addr(wil, sring->hwtail); + int sring_idx = sring - wil->srings; + u32 v; + + seq_printf(s, "Status Ring %s [ %d ] = {\n", + sring->is_rx ? "RX" : "TX", sring_idx); + seq_printf(s, " pa = %pad\n", &sring->pa); + seq_printf(s, " va = 0x%pK\n", sring->va); + seq_printf(s, " size = %d\n", sring->size); + seq_printf(s, " elem_size = %zu\n", sring->elem_size); + seq_printf(s, " swhead = %d\n", sring->swhead); + seq_printf(s, " hwtail = [0x%08x] -> ", sring->hwtail); + if (x) { + v = readl_relaxed(x); + seq_printf(s, "0x%08x = %d\n", v, v); + } else { + seq_puts(s, "???\n"); + } + seq_printf(s, " desc_rdy_pol = %d\n", sring->desc_rdy_pol); + + if (sring->va && (sring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) { + uint i; + + for (i = 0; i < sring->size; i++) { + u32 *sdword_0 = + (u32 *)(sring->va + (sring->elem_size * i)); + + if ((i % 128) == 0 && i != 0) + seq_puts(s, "\n"); + if (i == sring->swhead) + seq_printf(s, "%c", (*sdword_0 & BIT(31)) ? + 'X' : 'x'); + else + seq_printf(s, "%c", (*sdword_0 & BIT(31)) ? + '1' : '0'); + } + seq_puts(s, "\n"); + } + seq_puts(s, "}\n"); +} + +static int wil_srings_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + int i = 0; + + for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++) + if (wil->srings[i].va) + wil_print_sring(s, wil, &wil->srings[i]); + + return 0; +} + +static int wil_srings_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_srings_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_srings = { + .open = wil_srings_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + static void wil_seq_hexdump(struct seq_file *s, void *p, int len, const char *prefix) { @@ -975,53 +1082,92 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; struct wil_ring *ring; - bool tx = (dbg_ring_index < WIL6210_MAX_TX_RINGS); + bool tx; + int ring_idx = dbg_ring_index; + int txdesc_idx = dbg_txdesc_index; + volatile struct vring_tx_desc *d; + volatile u32 *u; + struct sk_buff *skb; + + if (wil->use_enhanced_dma_hw) { + /* RX ring index == 0 */ + if (ring_idx >= WIL6210_MAX_TX_RINGS) { + seq_printf(s, "invalid ring index %d\n", ring_idx); + return 0; + } + tx = ring_idx > 0; /* desc ring 0 is reserved for RX */ + } else { + /* RX ring index == WIL6210_MAX_TX_RINGS */ + if (ring_idx > WIL6210_MAX_TX_RINGS) { + seq_printf(s, "invalid ring index %d\n", ring_idx); + return 0; + } + tx = (ring_idx < WIL6210_MAX_TX_RINGS); + } - ring = tx ? &wil->ring_tx[dbg_ring_index] : &wil->ring_rx; + ring = tx ? &wil->ring_tx[ring_idx] : &wil->ring_rx; if (!ring->va) { if (tx) - seq_printf(s, "No Tx[%2d] VRING\n", dbg_ring_index); + seq_printf(s, "No Tx[%2d] RING\n", ring_idx); else - seq_puts(s, "No Rx VRING\n"); + seq_puts(s, "No Rx RING\n"); return 0; } - if (dbg_txdesc_index < ring->size) { - /* use struct vring_tx_desc for Rx as well, - * only field used, .dma.length, is the same - */ - volatile struct vring_tx_desc *d = - &ring->va[dbg_txdesc_index].tx.legacy; - volatile u32 *u = (volatile u32 *)d; - struct sk_buff *skb = ring->ctx[dbg_txdesc_index].skb; - + if (txdesc_idx >= ring->size) { if (tx) - seq_printf(s, "Tx[%2d][%3d] = {\n", dbg_ring_index, - dbg_txdesc_index); + seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n", + ring_idx, txdesc_idx, ring->size); else - seq_printf(s, "Rx[%3d] = {\n", dbg_txdesc_index); - seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", - u[0], u[1], u[2], u[3]); - seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", - u[4], u[5], u[6], u[7]); - seq_printf(s, " SKB = 0x%p\n", skb); + seq_printf(s, "RxDesc index (%d) >= size (%d)\n", + txdesc_idx, ring->size); + return 0; + } + + /* use struct vring_tx_desc for Rx as well, + * only field used, .dma.length, is the same + */ + d = &ring->va[txdesc_idx].tx.legacy; + u = (volatile u32 *)d; + skb = NULL; - if (skb) { - skb_get(skb); - wil_seq_print_skb(s, skb); - kfree_skb(skb); + if (wil->use_enhanced_dma_hw) { + if (tx) { + skb = ring->ctx[txdesc_idx].skb; + } else { + struct wil_rx_enhanced_desc *rx_d = + (struct wil_rx_enhanced_desc *) + &ring->va[txdesc_idx].rx.enhanced; + u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); + + if (!wil_val_in_range(buff_id, 0, + wil->rx_buff_mgmt.size)) { + seq_printf(s, "invalid buff_id %d\n", buff_id); + return 0; + } + skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; } - seq_puts(s, "}\n"); } else { - if (tx) - seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n", - dbg_ring_index, dbg_txdesc_index, - ring->size); - else - seq_printf(s, "RxDesc index (%d) >= size (%d)\n", - dbg_txdesc_index, ring->size); + skb = ring->ctx[txdesc_idx].skb; + } + if (tx) + seq_printf(s, "Tx[%2d][%3d] = {\n", ring_idx, + txdesc_idx); + else + seq_printf(s, "Rx[%3d] = {\n", txdesc_idx); + seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[0], u[1], u[2], u[3]); + seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[4], u[5], u[6], u[7]); + seq_printf(s, " SKB = 0x%p\n", skb); + + if (skb) { + skb_get(skb); + wil_seq_print_skb(s, skb); + kfree_skb(skb); } + seq_puts(s, "}\n"); return 0; } @@ -1038,6 +1184,115 @@ static const struct file_operations fops_txdesc = { .llseek = seq_lseek, }; +/*---------Tx/Rx status message------------*/ +static int wil_status_msg_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + int sring_idx = dbg_sring_index; + struct wil_status_ring *sring; + bool tx = sring_idx == wil->tx_sring_idx ? 1 : 0; + u32 status_msg_idx = dbg_status_msg_index; + u32 *u; + + if (sring_idx >= WIL6210_MAX_STATUS_RINGS) { + seq_printf(s, "invalid status ring index %d\n", sring_idx); + return 0; + } + + sring = &wil->srings[sring_idx]; + + if (!sring->va) { + seq_printf(s, "No %cX status ring\n", tx ? 'T' : 'R'); + return 0; + } + + if (status_msg_idx >= sring->size) { + seq_printf(s, "%cxDesc index (%d) >= size (%d)\n", + tx ? 'T' : 'R', status_msg_idx, sring->size); + return 0; + } + + u = sring->va + (sring->elem_size * status_msg_idx); + + seq_printf(s, "%cx[%d][%3d] = {\n", + tx ? 'T' : 'R', sring_idx, status_msg_idx); + + seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[0], u[1], u[2], u[3]); + if (!tx && !use_compressed_rx_status) + seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[4], u[5], u[6], u[7]); + + seq_puts(s, "}\n"); + + return 0; +} + +static int wil_status_msg_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_status_msg_debugfs_show, + inode->i_private); +} + +static const struct file_operations fops_status_msg = { + .open = wil_status_msg_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_print_rx_buff(struct seq_file *s, struct list_head *lh) +{ + struct wil_rx_buff *it; + int i = 0; + + list_for_each_entry(it, lh, list) { + if ((i % 16) == 0 && i != 0) + seq_puts(s, "\n "); + seq_printf(s, "[%4d] ", it->id); + i++; + } + seq_printf(s, "\nNumber of buffers: %u\n", i); + + return i; +} + +static int wil_rx_buff_mgmt_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct wil_rx_buff_mgmt *rbm = &wil->rx_buff_mgmt; + int num_active; + int num_free; + + seq_printf(s, " size = %zu\n", rbm->size); + seq_printf(s, " free_list_empty_cnt = %lu\n", + rbm->free_list_empty_cnt); + + /* Print active list */ + seq_puts(s, " Active list:\n"); + num_active = wil_print_rx_buff(s, &rbm->active); + seq_puts(s, "\n Free list:\n"); + num_free = wil_print_rx_buff(s, &rbm->free); + + seq_printf(s, " Total number of buffers: %u\n", + num_active + num_free); + + return 0; +} + +static int wil_rx_buff_mgmt_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_rx_buff_mgmt_debugfs_show, + inode->i_private); +} + +static const struct file_operations fops_rx_buff_mgmt = { + .open = wil_rx_buff_mgmt_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + /*---------beamforming------------*/ static char *wil_bfstatus_str(u32 status) { @@ -1517,6 +1772,13 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) p->stats.rx_large_frame, p->stats.rx_replay); + if (wil->use_enhanced_dma_hw) + seq_printf(s, + "mic error %lu, key error %lu, amsdu error %lu\n", + p->stats.rx_mic_error, + p->stats.rx_key_error, + p->stats.rx_amsdu_error); + seq_puts(s, "Rx/MCS:"); for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs); mcs++) @@ -1853,6 +2115,9 @@ static const struct { {"fw_capabilities", 0444, &fops_fw_capabilities}, {"fw_version", 0444, &fops_fw_version}, {"suspend_stats", 0644, &fops_suspend_stats}, + {"srings", 0444, &fops_srings}, + {"status_msg", 0444, &fops_status_msg}, + {"rx_buff_mgmt", 0444, &fops_rx_buff_mgmt}, }; static void wil6210_debugfs_init_files(struct wil6210_priv *wil, @@ -1899,6 +2164,7 @@ static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(abft_len, 0644, doff_u8), WIL_FIELD(wakeup_trigger, 0644, doff_u8), WIL_FIELD(ring_idle_trsh, 0644, doff_u32), + WIL_FIELD(num_rx_status_rings, 0644, doff_u8), {}, }; @@ -1915,6 +2181,8 @@ static const struct dbg_off dbg_statics[] = { {"ring_index", 0644, (ulong)&dbg_ring_index, doff_u32}, {"mem_addr", 0644, (ulong)&mem_addr, doff_u32}, {"led_polarity", 0644, (ulong)&led_polarity, doff_u8}, + {"status_index", 0644, (ulong)&dbg_status_msg_index, doff_u32}, + {"sring_index", 0644, (ulong)&dbg_sring_index, doff_u32}, {}, }; diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c index e7ff41e623d2b1a2c14311e1150552e025519305..a04c87ffd37bc6b87c16d289c6417dcc1aaba8e3 100644 --- a/drivers/net/wireless/ath/wil6210/ethtool.c +++ b/drivers/net/wireless/ath/wil6210/ethtool.c @@ -101,7 +101,7 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev, if (ret < 0) return ret; - wil_configure_interrupt_moderation(wil); + wil->txrx_ops.configure_interrupt_moderation(wil); wil_pm_runtime_put(wil); diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 84e9840c175282f319c221ffa1aec585304a5aa9..d7e112da6a8d3ba520aa9ed232053cd9481f16a8 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -44,6 +44,8 @@ (~(BIT_DMA_EP_RX_ICR_RX_HTRSH))) #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) +#define WIL6210_IMC_TX_EDMA BIT_TX_STATUS_IRQ +#define WIL6210_IMC_RX_EDMA BIT_RX_STATUS_IRQ #define WIL6210_IMC_MISC_NO_HALP (ISR_MISC_FW_READY | \ ISR_MISC_MBOX_EVT | \ ISR_MISC_FW_ERROR) @@ -87,12 +89,24 @@ static void wil6210_mask_irq_tx(struct wil6210_priv *wil) WIL6210_IRQ_DISABLE); } +static void wil6210_mask_irq_tx_edma(struct wil6210_priv *wil) +{ + wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMS), + WIL6210_IRQ_DISABLE); +} + static void wil6210_mask_irq_rx(struct wil6210_priv *wil) { wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMS), WIL6210_IRQ_DISABLE); } +static void wil6210_mask_irq_rx_edma(struct wil6210_priv *wil) +{ + wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMS), + WIL6210_IRQ_DISABLE); +} + static void wil6210_mask_irq_misc(struct wil6210_priv *wil, bool mask_halp) { wil_dbg_irq(wil, "mask_irq_misc: mask_halp(%s)\n", @@ -125,6 +139,12 @@ void wil6210_unmask_irq_tx(struct wil6210_priv *wil) WIL6210_IMC_TX); } +void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil) +{ + wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMC), + WIL6210_IMC_TX_EDMA); +} + void wil6210_unmask_irq_rx(struct wil6210_priv *wil) { bool unmask_rx_htrsh = atomic_read(&wil->connected_vifs) > 0; @@ -133,6 +153,12 @@ void wil6210_unmask_irq_rx(struct wil6210_priv *wil) unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH); } +void wil6210_unmask_irq_rx_edma(struct wil6210_priv *wil) +{ + wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMC), + WIL6210_IMC_RX_EDMA); +} + static void wil6210_unmask_irq_misc(struct wil6210_priv *wil, bool unmask_halp) { wil_dbg_irq(wil, "unmask_irq_misc: unmask_halp(%s)\n", @@ -164,7 +190,9 @@ void wil_mask_irq(struct wil6210_priv *wil) wil_dbg_irq(wil, "mask_irq\n"); wil6210_mask_irq_tx(wil); + wil6210_mask_irq_tx_edma(wil); wil6210_mask_irq_rx(wil); + wil6210_mask_irq_rx_edma(wil); wil6210_mask_irq_misc(wil, true); wil6210_mask_irq_pseudo(wil); } @@ -179,13 +207,43 @@ void wil_unmask_irq(struct wil6210_priv *wil) WIL_ICR_ICC_VALUE); wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC), WIL_ICR_ICC_MISC_VALUE); + wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC), + WIL_ICR_ICC_VALUE); + wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, ICC), + WIL_ICR_ICC_VALUE); wil6210_unmask_irq_pseudo(wil); - wil6210_unmask_irq_tx(wil); - wil6210_unmask_irq_rx(wil); + if (wil->use_enhanced_dma_hw) { + wil6210_unmask_irq_tx_edma(wil); + wil6210_unmask_irq_rx_edma(wil); + } else { + wil6210_unmask_irq_tx(wil); + wil6210_unmask_irq_rx(wil); + } wil6210_unmask_irq_misc(wil, true); } +void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil) +{ + u32 moderation; + + wil_s(wil, RGF_INT_GEN_IDLE_TIME_LIMIT, WIL_EDMA_IDLE_TIME_LIMIT_USEC); + + wil_s(wil, RGF_INT_GEN_TIME_UNIT_LIMIT, WIL_EDMA_TIME_UNIT_CLK_CYCLES); + + /* Update RX and TX moderation */ + moderation = wil->rx_max_burst_duration | + (WIL_EDMA_AGG_WATERMARK << WIL_EDMA_AGG_WATERMARK_POS); + wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_0, moderation); + wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_1, moderation); + + /* Treat special events as regular + * (set bit 0 to 0x1 and clear bits 1-8) + */ + wil_c(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1FE); + wil_s(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1); +} + void wil_configure_interrupt_moderation(struct wil6210_priv *wil) { struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; @@ -294,6 +352,97 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) return IRQ_HANDLED; } +static irqreturn_t wil6210_irq_rx_edma(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + bool need_unmask = true; + + trace_wil6210_irq_rx(isr); + wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); + + if (unlikely(!isr)) { + wil_err(wil, "spurious IRQ: RX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_rx_edma(wil); + + if (likely(isr & BIT_RX_STATUS_IRQ)) { + wil_dbg_irq(wil, "RX status ring\n"); + isr &= ~BIT_RX_STATUS_IRQ; + if (likely(test_bit(wil_status_fwready, wil->status))) { + if (likely(test_bit(wil_status_napi_en, wil->status))) { + wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); + need_unmask = false; + napi_schedule(&wil->napi_rx); + } else { + wil_err(wil, + "Got Rx interrupt while stopping interface\n"); + } + } else { + wil_err(wil, "Got Rx interrupt while in reset\n"); + } + } + + if (unlikely(isr)) + wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); + + /* Rx IRQ will be enabled when NAPI processing finished */ + + atomic_inc(&wil->isr_count_rx); + + if (unlikely(need_unmask)) + wil6210_unmask_irq_rx_edma(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + bool need_unmask = true; + + trace_wil6210_irq_tx(isr); + wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); + + if (unlikely(!isr)) { + wil_err(wil, "spurious IRQ: TX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_tx_edma(wil); + + if (likely(isr & BIT_TX_STATUS_IRQ)) { + wil_dbg_irq(wil, "TX status ring\n"); + isr &= ~BIT_TX_STATUS_IRQ; + if (likely(test_bit(wil_status_fwready, wil->status))) { + wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); + need_unmask = false; + napi_schedule(&wil->napi_tx); + } else { + wil_err(wil, "Got Tx status ring IRQ while in reset\n"); + } + } + + if (unlikely(isr)) + wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + + /* Tx IRQ will be enabled when NAPI processing finished */ + + atomic_inc(&wil->isr_count_tx); + + if (unlikely(need_unmask)) + wil6210_unmask_irq_tx_edma(wil); + + return IRQ_HANDLED; +} + static irqreturn_t wil6210_irq_tx(int irq, void *cookie) { struct wil6210_priv *wil = cookie; @@ -510,30 +659,53 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie) */ static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) { + u32 icm_rx, icr_rx, imv_rx; + u32 icm_tx, icr_tx, imv_tx; + u32 icm_misc, icr_misc, imv_misc; + if (!test_bit(wil_status_irqen, wil->status)) { - u32 icm_rx = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_DMA_EP_RX_ICR) + - offsetof(struct RGF_ICR, ICM)); - u32 icr_rx = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_DMA_EP_RX_ICR) + - offsetof(struct RGF_ICR, ICR)); - u32 imv_rx = wil_r(wil, RGF_DMA_EP_RX_ICR + + if (wil->use_enhanced_dma_hw) { + icm_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICM)); + icr_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + imv_rx = wil_r(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMV)); - u32 icm_tx = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_DMA_EP_TX_ICR) + - offsetof(struct RGF_ICR, ICM)); - u32 icr_tx = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_DMA_EP_TX_ICR) + - offsetof(struct RGF_ICR, ICR)); - u32 imv_tx = wil_r(wil, RGF_DMA_EP_TX_ICR + + icm_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICM)); + icr_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + imv_tx = wil_r(wil, RGF_INT_GEN_TX_ICR + + offsetof(struct RGF_ICR, IMV)); + } else { + icm_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICM)); + icr_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + imv_rx = wil_r(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMV)); - u32 icm_misc = wil_ioread32_and_clear(wil->csr + + icm_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICM)); + icr_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + imv_tx = wil_r(wil, RGF_DMA_EP_TX_ICR + + offsetof(struct RGF_ICR, IMV)); + } + icm_misc = wil_ioread32_and_clear(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICM)); - u32 icr_misc = wil_ioread32_and_clear(wil->csr + + icr_misc = wil_ioread32_and_clear(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICR)); - u32 imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR + + imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMV)); /* HALP interrupt can be unmasked when misc interrupts are @@ -592,11 +764,11 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie) * voting for wake thread - need at least 1 vote */ if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && - (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) + (wil->txrx_ops.irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) rc = IRQ_WAKE_THREAD; if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && - (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) + (wil->txrx_ops.irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) rc = IRQ_WAKE_THREAD; if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && @@ -624,6 +796,10 @@ void wil6210_clear_irq(struct wil6210_priv *wil) offsetof(struct RGF_ICR, ICR)); wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + offsetof(struct RGF_ICR, ICR)); + wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICR)); wmb(); /* make sure write completed */ @@ -652,6 +828,13 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq, bool use_msi) wil_dbg_misc(wil, "init_irq: %s\n", use_msi ? "MSI" : "INTx"); + if (wil->use_enhanced_dma_hw) { + wil->txrx_ops.irq_tx = wil6210_irq_tx_edma; + wil->txrx_ops.irq_rx = wil6210_irq_rx_edma; + } else { + wil->txrx_ops.irq_tx = wil6210_irq_tx; + wil->txrx_ops.irq_rx = wil6210_irq_rx; + } rc = request_threaded_irq(irq, wil6210_hardirq, wil6210_thread_irq, use_msi ? 0 : IRQF_SHARED, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 2f397f3aede80d649c6095b89c896ed0b4030a08..5d90e99aeaed31676a401979c6f5684cd89c0182 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -21,11 +21,13 @@ #include "wil6210.h" #include "txrx.h" +#include "txrx_edma.h" #include "wmi.h" #include "boot_loader.h" #define WAIT_FOR_HALP_VOTE_MS 100 #define WAIT_FOR_SCAN_ABORT_MS 1000 +#define WIL_DEFAULT_NUM_RX_STATUS_RINGS 1 bool debug_fw; /* = false; */ module_param(debug_fw, bool, 0444); @@ -77,9 +79,9 @@ static const struct kernel_param_ops mtu_max_ops = { module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, 0444); MODULE_PARM_DESC(mtu_max, " Max MTU value."); -static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT; -static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT; -static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT; +static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT; +static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT; +static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT; static int ring_order_set(const char *val, const struct kernel_param *kp) { @@ -110,9 +112,29 @@ MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order"); module_param_cb(bcast_ring_order, &ring_order_ops, &bcast_ring_order, 0444); MODULE_PARM_DESC(bcast_ring_order, " Bcast ring order; size = 1 << order"); -#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */ +enum { + WIL_BOOT_ERR, + WIL_BOOT_VANILLA, + WIL_BOOT_PRODUCTION, + WIL_BOOT_DEVELOPMENT, +}; + +enum { + WIL_SIG_STATUS_VANILLA = 0x0, + WIL_SIG_STATUS_DEVELOPMENT = 0x1, + WIL_SIG_STATUS_PRODUCTION = 0x2, + WIL_SIG_STATUS_CORRUPTED_PRODUCTION = 0x3, +}; + +#define RST_DELAY (20) /* msec, for loop in @wil_wait_device_ready */ #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */ +#define PMU_READY_DELAY_MS (4) /* ms, for sleep in @wil_wait_device_ready */ + +#define OTP_HW_DELAY (200) /* usec, loop in @wil_wait_device_ready_talyn_mb */ +/* round up to be above 2 ms total */ +#define OTP_HW_COUNT (1 + 2000 / OTP_HW_DELAY) + /* * Due to a hardware issue, * one has to read/write to/from NIC in 32-bit chunks; @@ -160,6 +182,37 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, } } +static void wil_ring_fini_tx(struct wil6210_priv *wil, int id) +{ + struct wil_ring *ring = &wil->ring_tx[id]; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[id]; + + lockdep_assert_held(&wil->mutex); + + if (!ring->va) + return; + + wil_dbg_misc(wil, "vring_fini_tx: id=%d\n", id); + + spin_lock_bh(&txdata->lock); + txdata->dot1x_open = false; + txdata->mid = U8_MAX; + txdata->enabled = 0; /* no Tx can be in progress or start anew */ + spin_unlock_bh(&txdata->lock); + /* napi_synchronize waits for completion of the current NAPI but will + * not prevent the next NAPI run. + * Add a memory barrier to guarantee that txdata->enabled is zeroed + * before napi_synchronize so that the next scheduled NAPI will not + * handle this vring + */ + wmb(); + /* make sure NAPI won't touch this vring */ + if (test_bit(wil_status_napi_en, wil->status)) + napi_synchronize(&wil->napi_tx); + + wil->txrx_ops.ring_fini_tx(wil, ring); +} + static void wil_disconnect_cid(struct wil6210_vif *vif, int cid, u16 reason_code, bool from_event) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) @@ -456,15 +509,16 @@ static void wil_fw_error_worker(struct work_struct *work) static int wil_find_free_ring(struct wil6210_priv *wil) { int i; + int min_ring_id = wil_get_min_tx_ring_id(wil); - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { if (!wil->ring_tx[i].va) return i; } return -EINVAL; } -int wil_tx_init(struct wil6210_vif *vif, int cid) +int wil_ring_init_tx(struct wil6210_vif *vif, int cid) { struct wil6210_priv *wil = vif_to_wil(vif); int rc = -EINVAL, ringid; @@ -482,7 +536,8 @@ int wil_tx_init(struct wil6210_vif *vif, int cid) wil_dbg_wmi(wil, "Configure for connection CID %d MID %d ring %d\n", cid, vif->mid, ringid); - rc = wil_vring_init_tx(vif, ringid, 1 << tx_ring_order, cid, 0); + rc = wil->txrx_ops.ring_init_tx(vif, ringid, 1 << tx_ring_order, + cid, 0); if (rc) wil_err(wil, "init TX for CID %d MID %d vring %d failed\n", cid, vif->mid, ringid); @@ -504,7 +559,7 @@ int wil_bcast_init(struct wil6210_vif *vif) return ri; vif->bcast_ring = ri; - rc = wil_vring_init_bcast(vif, ri, 1 << bcast_ring_order); + rc = wil->txrx_ops.ring_init_bcast(vif, ri, 1 << bcast_ring_order); if (rc) vif->bcast_ring = -1; @@ -594,6 +649,9 @@ int wil_priv_init(struct wil6210_priv *wil) wil->reply_mid = U8_MAX; wil->max_vifs = 1; + /* num of rx srings can be updated via debugfs before allocation */ + wil->num_rx_status_rings = WIL_DEFAULT_NUM_RX_STATUS_RINGS; + return 0; out_wmi_wq: @@ -777,11 +835,146 @@ static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode) } } -static int wil_target_reset(struct wil6210_priv *wil, int no_flash) +static int wil_wait_device_ready(struct wil6210_priv *wil, int no_flash) { int delay = 0; u32 x, x1 = 0; + /* wait until device ready. */ + if (no_flash) { + msleep(PMU_READY_DELAY_MS); + + wil_dbg_misc(wil, "Reset completed\n"); + } else { + do { + msleep(RST_DELAY); + x = wil_r(wil, RGF_USER_BL + + offsetof(struct bl_dedicated_registers_v0, + boot_loader_ready)); + if (x1 != x) { + wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", + x1, x); + x1 = x; + } + if (delay++ > RST_COUNT) { + wil_err(wil, "Reset not completed, bl.ready 0x%08x\n", + x); + return -ETIME; + } + } while (x != BL_READY); + + wil_dbg_misc(wil, "Reset completed in %d ms\n", + delay * RST_DELAY); + } + + return 0; +} + +static int wil_wait_device_ready_talyn_mb(struct wil6210_priv *wil) +{ + u32 otp_hw; + u8 signature_status; + bool otp_signature_err; + bool hw_section_done; + u32 otp_qc_secured; + int delay = 0; + + /* Wait for OTP signature test to complete */ + usleep_range(2000, 2200); + + wil->boot_config = WIL_BOOT_ERR; + + /* Poll until OTP signature status is valid. + * In vanilla and development modes, when signature test is complete + * HW sets BIT_OTP_SIGNATURE_ERR_TALYN_MB. + * In production mode BIT_OTP_SIGNATURE_ERR_TALYN_MB remains 0, poll + * for signature status change to 2 or 3. + */ + do { + otp_hw = wil_r(wil, RGF_USER_OTP_HW_RD_MACHINE_1); + signature_status = WIL_GET_BITS(otp_hw, 8, 9); + otp_signature_err = otp_hw & BIT_OTP_SIGNATURE_ERR_TALYN_MB; + + if (otp_signature_err && + signature_status == WIL_SIG_STATUS_VANILLA) { + wil->boot_config = WIL_BOOT_VANILLA; + break; + } + if (otp_signature_err && + signature_status == WIL_SIG_STATUS_DEVELOPMENT) { + wil->boot_config = WIL_BOOT_DEVELOPMENT; + break; + } + if (!otp_signature_err && + signature_status == WIL_SIG_STATUS_PRODUCTION) { + wil->boot_config = WIL_BOOT_PRODUCTION; + break; + } + if (!otp_signature_err && + signature_status == + WIL_SIG_STATUS_CORRUPTED_PRODUCTION) { + /* Unrecognized OTP signature found. Possibly a + * corrupted production signature, access control + * is applied as in production mode, therefore + * do not fail + */ + wil->boot_config = WIL_BOOT_PRODUCTION; + break; + } + if (delay++ > OTP_HW_COUNT) + break; + + usleep_range(OTP_HW_DELAY, OTP_HW_DELAY + 10); + } while (!otp_signature_err && signature_status == 0); + + if (wil->boot_config == WIL_BOOT_ERR) { + wil_err(wil, + "invalid boot config, signature_status %d otp_signature_err %d\n", + signature_status, otp_signature_err); + return -ETIME; + } + + wil_dbg_misc(wil, + "signature test done in %d usec, otp_hw 0x%x, boot_config %d\n", + delay * OTP_HW_DELAY, otp_hw, wil->boot_config); + + if (wil->boot_config == WIL_BOOT_VANILLA) + /* Assuming not SPI boot (currently not supported) */ + goto out; + + hw_section_done = otp_hw & BIT_OTP_HW_SECTION_DONE_TALYN_MB; + delay = 0; + + while (!hw_section_done) { + msleep(RST_DELAY); + + otp_hw = wil_r(wil, RGF_USER_OTP_HW_RD_MACHINE_1); + hw_section_done = otp_hw & BIT_OTP_HW_SECTION_DONE_TALYN_MB; + + if (delay++ > RST_COUNT) { + wil_err(wil, "TO waiting for hw_section_done\n"); + return -ETIME; + } + } + + wil_dbg_misc(wil, "HW section done in %d ms\n", delay * RST_DELAY); + + otp_qc_secured = wil_r(wil, RGF_OTP_QC_SECURED); + wil->secured_boot = otp_qc_secured & BIT_BOOT_FROM_ROM ? 1 : 0; + wil_dbg_misc(wil, "secured boot is %sabled\n", + wil->secured_boot ? "en" : "dis"); + +out: + wil_dbg_misc(wil, "Reset completed\n"); + + return 0; +} + +static int wil_target_reset(struct wil6210_priv *wil, int no_flash) +{ + u32 x; + int rc; + wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name); /* Clear MAC link up */ @@ -847,34 +1040,12 @@ static int wil_target_reset(struct wil6210_priv *wil, int no_flash) wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); - /* wait until device ready. typical time is 20..80 msec */ - if (no_flash) - do { - msleep(RST_DELAY); - x = wil_r(wil, USER_EXT_USER_PMU_3); - if (delay++ > RST_COUNT) { - wil_err(wil, "Reset not completed, PMU_3 0x%08x\n", - x); - return -ETIME; - } - } while ((x & BIT_PMU_DEVICE_RDY) == 0); + if (wil->hw_version == HW_VER_TALYN_MB) + rc = wil_wait_device_ready_talyn_mb(wil); else - do { - msleep(RST_DELAY); - x = wil_r(wil, RGF_USER_BL + - offsetof(struct bl_dedicated_registers_v0, - boot_loader_ready)); - if (x1 != x) { - wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", - x1, x); - x1 = x; - } - if (delay++ > RST_COUNT) { - wil_err(wil, "Reset not completed, bl.ready 0x%08x\n", - x); - return -ETIME; - } - } while (x != BL_READY); + rc = wil_wait_device_ready(wil, no_flash); + if (rc) + return rc; wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); @@ -882,7 +1053,7 @@ static int wil_target_reset(struct wil6210_priv *wil, int no_flash) wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN | BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC); - if (no_flash) { + if (wil->hw_version < HW_VER_TALYN_MB && no_flash) { /* Reset OTP HW vectors to fit 40MHz */ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME1, 0x60001); wil_w(wil, RGF_USER_XPM_IFC_RD_TIME2, 0x20027); @@ -897,7 +1068,6 @@ static int wil_target_reset(struct wil6210_priv *wil, int no_flash) wil_w(wil, RGF_USER_XPM_RD_DOUT_SAMPLE_TIME, 0x57); } - wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); return 0; } @@ -1321,7 +1491,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) rc = wil_target_reset(wil, no_flash); wil6210_clear_irq(wil); wil_enable_irq(wil); - wil_rx_fini(wil); + wil->txrx_ops.rx_fini(wil); + wil->txrx_ops.tx_fini(wil); if (rc) { if (!no_flash) wil_bl_crash_info(wil, true); @@ -1344,6 +1515,11 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil_info(wil, "Use firmware <%s> + board <%s>\n", wil->wil_fw_name, WIL_BOARD_FILE_NAME); + if (wil->secured_boot) { + wil_err(wil, "secured boot is not supported\n"); + return -ENOTSUPP; + } + if (!no_flash) wil_bl_prepare_halt(wil); @@ -1374,7 +1550,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) clear_bit(wil_status_resetting, wil->status); if (load_fw) { - wil_configure_interrupt_moderation(wil); wil_unmask_irq(wil); /* we just started MAC, wait for FW ready */ @@ -1389,6 +1564,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + wil->txrx_ops.configure_interrupt_moderation(wil); + rc = wil_restore_vifs(wil); if (rc) { wil_err(wil, "failed to restore vifs, rc %d\n", rc); @@ -1450,8 +1627,12 @@ int __wil_up(struct wil6210_priv *wil) if (rc) return rc; - /* Rx VRING. After MAC and beacon */ - rc = wil_rx_init(wil, 1 << rx_ring_order); + /* Rx RING. After MAC and beacon */ + rc = wil->txrx_ops.rx_init(wil, 1 << rx_ring_order); + if (rc) + return rc; + + rc = wil->txrx_ops.tx_init(wil); if (rc) return rc; @@ -1613,3 +1794,11 @@ void wil_halp_unvote(struct wil6210_priv *wil) mutex_unlock(&wil->halp.lock); } + +void wil_init_txrx_ops(struct wil6210_priv *wil) +{ + if (wil->use_enhanced_dma_hw) + wil_init_txrx_ops_edma(wil); + else + wil_init_txrx_ops_legacy_dma(wil); +} diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index df575cd99a8fd7085ebeddd87cc6d63a95b2cc27..0e0b8064fe015a7f4ed8f8002a6b480b43376066 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -133,6 +133,27 @@ static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) return done; } +static int wil6210_netdev_poll_rx_edma(struct napi_struct *napi, int budget) +{ + struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, + napi_rx); + int quota = budget; + int done; + + wil_rx_handle_edma(wil, "a); + done = budget - quota; + + if (done < budget) { + napi_complete_done(napi, done); + wil6210_unmask_irq_rx_edma(wil); + wil_dbg_txrx(wil, "NAPI RX complete\n"); + } + + wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done); + + return done; +} + static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) { struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, @@ -170,6 +191,30 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) return min(tx_done, budget); } +static int wil6210_netdev_poll_tx_edma(struct napi_struct *napi, int budget) +{ + struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, + napi_tx); + int tx_done; + /* There is only one status TX ring */ + struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx]; + + if (!sring->va) + return 0; + + tx_done = wil_tx_sring_handler(wil, sring); + + if (tx_done < budget) { + napi_complete(napi); + wil6210_unmask_irq_tx_edma(wil); + wil_dbg_txrx(wil, "NAPI TX complete\n"); + } + + wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done); + + return min(tx_done, budget); +} + static void wil_dev_setup(struct net_device *dev) { ether_setup(dev); @@ -435,11 +480,21 @@ int wil_if_add(struct wil6210_priv *wil) } init_dummy_netdev(&wil->napi_ndev); - netif_napi_add(&wil->napi_ndev, &wil->napi_rx, wil6210_netdev_poll_rx, - WIL6210_NAPI_BUDGET); - netif_tx_napi_add(&wil->napi_ndev, - &wil->napi_tx, wil6210_netdev_poll_tx, - WIL6210_NAPI_BUDGET); + if (wil->use_enhanced_dma_hw) { + netif_napi_add(&wil->napi_ndev, &wil->napi_rx, + wil6210_netdev_poll_rx_edma, + WIL6210_NAPI_BUDGET); + netif_tx_napi_add(&wil->napi_ndev, + &wil->napi_tx, wil6210_netdev_poll_tx_edma, + WIL6210_NAPI_BUDGET); + } else { + netif_napi_add(&wil->napi_ndev, &wil->napi_rx, + wil6210_netdev_poll_rx, + WIL6210_NAPI_BUDGET); + netif_tx_napi_add(&wil->napi_ndev, + &wil->napi_tx, wil6210_netdev_poll_tx, + WIL6210_NAPI_BUDGET); + } wil_update_net_queues_bh(wil, vif, NULL, true); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 19cf79212a6a1def6883bdd5050064559c0b1133..f534e5e82d06a64e175c39f72862b368f9715cfd 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -32,6 +32,10 @@ static bool ftm_mode; module_param(ftm_mode, bool, 0444); MODULE_PARM_DESC(ftm_mode, " Set factory test mode, default - false"); +static bool use_enhanced_dma_hw = true; +module_param(use_enhanced_dma_hw, bool, 0444); +MODULE_PARM_DESC(use_enhanced_dma_hw, " Use enhanced or legacy DMA HW. Default: true when available"); + #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP static int wil6210_pm_notify(struct notifier_block *notify_block, @@ -97,6 +101,10 @@ int wil_set_capabilities(struct wil6210_priv *wil) if (wil_r(wil, RGF_USER_OTP_HW_RD_MACHINE_1) & BIT_NO_FLASH_INDICATION) set_bit(hw_capa_no_flash, wil->hw_capa); + wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_TALYN : + WIL_FW_NAME_TALYN; + if (wil_fw_verify_file_exists(wil, wil_fw_name)) + wil->wil_fw_name = wil_fw_name; break; case JTAG_DEV_ID_TALYN_MB: wil->hw_name = "Talyn-MB"; @@ -106,6 +114,11 @@ int wil_set_capabilities(struct wil6210_priv *wil) wil->rgf_fw_assert_code_addr = TALYN_RGF_FW_ASSERT_CODE; wil->rgf_ucode_assert_code_addr = TALYN_RGF_UCODE_ASSERT_CODE; set_bit(hw_capa_no_flash, wil->hw_capa); + wil->use_enhanced_dma_hw = use_enhanced_dma_hw; + wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_TALYN : + WIL_FW_NAME_TALYN; + if (wil_fw_verify_file_exists(wil, wil_fw_name)) + wil->wil_fw_name = wil_fw_name; break; default: wil_err(wil, "Unknown board hardware, chip_id 0x%08x, chip_revision 0x%08x\n", @@ -115,6 +128,8 @@ int wil_set_capabilities(struct wil6210_priv *wil) return -EINVAL; } + wil_init_txrx_ops(wil); + iccm_section = wil_find_fw_mapping("fw_code"); if (!iccm_section) { wil_err(wil, "fw_code section not found in fw_mapping\n"); @@ -270,8 +285,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) .fw_recovery = wil_platform_rop_fw_recovery, }; u32 bar_size = pci_resource_len(pdev, 0); - int dma_addr_size[] = {48, 40, 32}; /* keep descending order */ - int i; + int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */ + int i, start_idx; /* check HW */ dev_info(&pdev->dev, WIL_NAME @@ -306,24 +321,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto if_free; } /* rollback to err_plat */ - - /* device supports >32bit addresses */ - for (i = 0; i < ARRAY_SIZE(dma_addr_size); i++) { - rc = dma_set_mask_and_coherent(dev, - DMA_BIT_MASK(dma_addr_size[i])); - if (rc) { - dev_err(dev, "dma_set_mask_and_coherent(%d) failed: %d\n", - dma_addr_size[i], rc); - continue; - } - dev_info(dev, "using dma mask %d", dma_addr_size[i]); - wil->dma_addr_size = dma_addr_size[i]; - break; - } - - if (wil->dma_addr_size == 0) - goto err_plat; - rc = pci_enable_device(pdev); if (rc && pdev->msi_enabled == 0) { wil_err(wil, @@ -363,6 +360,28 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) wil_err(wil, "wil_set_capabilities failed, rc %d\n", rc); goto err_iounmap; } + + /* device supports >32bit addresses. + * for legacy DMA start from 48 bit. + */ + start_idx = wil->use_enhanced_dma_hw ? 0 : 1; + + for (i = start_idx; i < ARRAY_SIZE(dma_addr_size); i++) { + rc = dma_set_mask_and_coherent(dev, + DMA_BIT_MASK(dma_addr_size[i])); + if (rc) { + dev_err(dev, "dma_set_mask_and_coherent(%d) failed: %d\n", + dma_addr_size[i], rc); + continue; + } + dev_info(dev, "using dma mask %d", dma_addr_size[i]); + wil->dma_addr_size = dma_addr_size[i]; + break; + } + + if (wil->dma_addr_size == 0) + goto err_iounmap; + wil6210_clear_irq(wil); /* FW should raise IRQ when ready */ diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 445f5331e51cec89c9fc037eb03a9eb37eb74706..93fcc55bcd22b77514ed7a6e0a0b76b2b6002aca 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -207,7 +207,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) goto reject_suspend; } - if (!wil_is_rx_idle(wil)) { + if (!wil->txrx_ops.is_rx_idle(wil)) { wil_dbg_pm(wil, "Pending RX data, reject suspend\n"); wil->suspend_stats.rejected_by_host++; goto reject_suspend; @@ -231,9 +231,9 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) start = jiffies; data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS); if (test_bit(wil_status_napi_en, wil->status)) { - while (!wil_is_rx_idle(wil)) { + while (!wil->txrx_ops.is_rx_idle(wil)) { if (time_after(jiffies, data_comp_to)) { - if (wil_is_rx_idle(wil)) + if (wil->txrx_ops.is_rx_idle(wil)) break; wil_err(wil, "TO waiting for idle RX, suspend failed\n"); diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 3b393f344934dc0d06305ba0a56486c371315bf6..9321b5e91a9cc9a7f1b239a349cc0c2bba51066c 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -95,17 +95,16 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { struct wil6210_vif *vif; struct net_device *ndev; - struct vring_rx_desc *d = wil_skb_rxdesc(skb); - int tid = wil_rxdesc_tid(d); - int cid = wil_rxdesc_cid(d); - int mid = wil_rxdesc_mid(d); - u16 seq = wil_rxdesc_seq(d); - int mcast = wil_rxdesc_mcast(d); - struct wil_sta_info *sta = &wil->sta[cid]; + int tid, cid, mid, mcast; + u16 seq; + struct wil_sta_info *sta; struct wil_tid_ampdu_rx *r; u16 hseq; int index; + wil->txrx_ops.get_reorder_params(skb, &tid, &cid, &mid, &seq, &mcast); + sta = &wil->sta[cid]; + wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x mcast %01x\n", mid, cid, tid, seq, mcast); @@ -365,8 +364,9 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) } } - rc = wmi_addba_rx_resp(wil, mid, cid, tid, dialog_token, status, - agg_amsdu, agg_wsize, agg_timeout); + rc = wil->txrx_ops.wmi_addba_rx_resp(wil, mid, cid, tid, dialog_token, + status, agg_amsdu, agg_wsize, + agg_timeout); if (rc || (status != WLAN_STATUS_SUCCESS)) { wil_err(wil, "do not apply ba, rc(%d), status(%d)\n", rc, status); diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h index c4db2a9d9f7f6c8338e5f35d331069e93bccf89d..efd4c7205ef1ed105052c900c15c853ea4245408 100644 --- a/drivers/net/wireless/ath/wil6210/trace.h +++ b/drivers/net/wireless/ath/wil6210/trace.h @@ -187,6 +187,38 @@ TRACE_EVENT(wil6210_rx, __entry->seq, __entry->type, __entry->subtype) ); +TRACE_EVENT(wil6210_rx_status, + TP_PROTO(u8 use_compressed, u16 buff_id, void *msg), + TP_ARGS(use_compressed, buff_id, msg), + TP_STRUCT__entry(__field(u8, use_compressed) + __field(u16, buff_id) + __field(unsigned int, len) + __field(u8, mid) + __field(u8, cid) + __field(u8, tid) + __field(u8, type) + __field(u8, subtype) + __field(u16, seq) + __field(u8, mcs) + ), + TP_fast_assign(__entry->use_compressed = use_compressed; + __entry->buff_id = buff_id; + __entry->len = wil_rx_status_get_length(msg); + __entry->mid = wil_rx_status_get_mid(msg); + __entry->cid = wil_rx_status_get_cid(msg); + __entry->tid = wil_rx_status_get_tid(msg); + __entry->type = wil_rx_status_get_frame_type(msg); + __entry->subtype = wil_rx_status_get_fc1(msg); + __entry->seq = wil_rx_status_get_seq(msg); + __entry->mcs = wil_rx_status_get_mcs(msg); + ), + TP_printk( + "compressed %d buff_id %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x type 0x%1x subtype 0x%1x", + __entry->use_compressed, __entry->buff_id, __entry->len, + __entry->mid, __entry->cid, __entry->tid, __entry->mcs, + __entry->seq, __entry->type, __entry->subtype) +); + TRACE_EVENT(wil6210_tx, TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags), TP_ARGS(vring, index, len, frags), @@ -226,6 +258,31 @@ TRACE_EVENT(wil6210_tx_done, __entry->err) ); +TRACE_EVENT(wil6210_tx_status, + TP_PROTO(struct wil_ring_tx_status *msg, u16 index, + unsigned int len), + TP_ARGS(msg, index, len), + TP_STRUCT__entry(__field(u16, index) + __field(unsigned int, len) + __field(u8, num_descs) + __field(u8, ring_id) + __field(u8, status) + __field(u8, mcs) + + ), + TP_fast_assign(__entry->index = index; + __entry->len = len; + __entry->num_descs = msg->num_descriptors; + __entry->ring_id = msg->ring_id; + __entry->status = msg->status; + __entry->mcs = wil_tx_status_get_mcs(msg); + ), + TP_printk( + "ring_id %d swtail 0x%x len %d num_descs %d status 0x%x mcs %d", + __entry->ring_id, __entry->index, __entry->len, + __entry->num_descs, __entry->status, __entry->mcs) +); + #endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/ #if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 6ce9e44fffe2a42dfdb34d3adc283708792201d4..04c9a7261c36afc2a2a733c0585e6046394b6608 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -28,6 +28,7 @@ #include "wmi.h" #include "txrx.h" #include "trace.h" +#include "txrx_edma.h" static bool rtap_include_phy_info; module_param(rtap_include_phy_info, bool, 0444); @@ -117,12 +118,6 @@ bool wil_is_tx_idle(struct wil6210_priv *wil) return true; } -/* wil_val_in_range - check if value in [min,max) */ -static inline bool wil_val_in_range(int val, int min, int max) -{ - return val >= min && val < max; -} - static int wil_vring_alloc(struct wil6210_priv *wil, struct wil_ring *vring) { struct device *dev = wil_to_dev(wil); @@ -184,9 +179,10 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct wil_ring *vring) return 0; } -static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d, +static void wil_txdesc_unmap(struct device *dev, union wil_tx_desc *desc, struct wil_ctx *ctx) { + struct vring_tx_desc *d = &desc->legacy; dma_addr_t pa = wil_desc_addr(&d->dma.addr); u16 dmalen = le16_to_cpu(d->dma.length); @@ -202,14 +198,13 @@ static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d, } } -static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring, - int tx) +static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring) { struct device *dev = wil_to_dev(wil); size_t sz = vring->size * sizeof(vring->va[0]); lockdep_assert_held(&wil->mutex); - if (tx) { + if (!vring->is_rx) { int vring_index = vring - wil->ring_tx; wil_dbg_misc(wil, "free Tx vring %d [%d] 0x%p:%pad 0x%p\n", @@ -226,7 +221,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring, u16 dmalen; struct wil_ctx *ctx; - if (tx) { + if (!vring->is_rx) { struct vring_tx_desc dd, *d = ⅆ volatile struct vring_tx_desc *_d = &vring->va[vring->swtail].tx.legacy; @@ -240,7 +235,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring, continue; } *d = *_d; - wil_txdesc_unmap(dev, d, ctx); + wil_txdesc_unmap(dev, (union wil_tx_desc *)d, ctx); if (ctx->skb) dev_kfree_skb_any(ctx->skb); vring->swtail = wil_ring_next_tail(vring); @@ -413,14 +408,7 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, } } -/* similar to ieee80211_ version, but FC contain only 1-st byte */ -static inline int wil_is_back_req(u8 fc) -{ - return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == - (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); -} - -bool wil_is_rx_idle(struct wil6210_priv *wil) +static bool wil_is_rx_idle(struct wil6210_priv *wil) { struct vring_rx_desc *_d; struct wil_ring *ring = &wil->ring_rx; @@ -645,7 +633,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count) * Cut'n'paste from original memcmp (see lib/string.c) * with minimal modifications */ -static int reverse_memcmp(const void *cs, const void *ct, size_t count) +int reverse_memcmp(const void *cs, const void *ct, size_t count) { const unsigned char *su1, *su2; int res = 0; @@ -690,6 +678,15 @@ static int wil_rx_crypto_check(struct wil6210_priv *wil, struct sk_buff *skb) return 0; } +static void wil_get_netif_rx_params(struct sk_buff *skb, int *cid, + int *security) +{ + struct vring_rx_desc *d = wil_skb_rxdesc(skb); + + *cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */ + *security = wil_rxdesc_security(d); +} + /* * Pass Rx packet to the netif. Update statistics. * Called in softirq context (NAPI poll). @@ -701,15 +698,14 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) struct wil6210_priv *wil = ndev_to_wil(ndev); struct wireless_dev *wdev = vif_to_wdev(vif); unsigned int len = skb->len; - struct vring_rx_desc *d = wil_skb_rxdesc(skb); - int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */ - int security = wil_rxdesc_security(d); + int cid; + int security; struct ethhdr *eth = (void *)skb->data; /* here looking for DA, not A1, thus Rxdesc's 'mcast' indication * is not suitable, need to look at data */ int mcast = is_multicast_ether_addr(eth->h_dest); - struct wil_net_stats *stats = &wil->sta[cid].stats; + struct wil_net_stats *stats; struct sk_buff *xmit_skb = NULL; static const char * const gro_res_str[] = { [GRO_MERGED] = "GRO_MERGED", @@ -719,6 +715,10 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) [GRO_DROP] = "GRO_DROP", }; + wil->txrx_ops.get_netif_rx_params(skb, &cid, &security); + + stats = &wil->sta[cid].stats; + if (ndev->features & NETIF_F_RXHASH) /* fake L4 to ensure it won't be re-calculated later * set hash to any non-zero value to activate rps @@ -729,7 +729,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) skb_orphan(skb); - if (security && (wil_rx_crypto_check(wil, skb) != 0)) { + if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) { rc = GRO_DROP; dev_kfree_skb(skb); stats->rx_replay++; @@ -843,7 +843,7 @@ static void wil_rx_buf_len_init(struct wil6210_priv *wil) } } -int wil_rx_init(struct wil6210_priv *wil, u16 size) +static int wil_rx_init(struct wil6210_priv *wil, u16 size) { struct wil_ring *vring = &wil->ring_rx; int rc; @@ -858,6 +858,7 @@ int wil_rx_init(struct wil6210_priv *wil, u16 size) wil_rx_buf_len_init(wil); vring->size = size; + vring->is_rx = true; rc = wil_vring_alloc(wil, vring); if (rc) return rc; @@ -872,22 +873,46 @@ int wil_rx_init(struct wil6210_priv *wil, u16 size) return 0; err_free: - wil_vring_free(wil, vring, 0); + wil_vring_free(wil, vring); return rc; } -void wil_rx_fini(struct wil6210_priv *wil) +static void wil_rx_fini(struct wil6210_priv *wil) { struct wil_ring *vring = &wil->ring_rx; wil_dbg_misc(wil, "rx_fini\n"); if (vring->va) - wil_vring_free(wil, vring, 0); + wil_vring_free(wil, vring); +} + +static int wil_tx_desc_map(union wil_tx_desc *desc, dma_addr_t pa, + u32 len, int vring_index) +{ + struct vring_tx_desc *d = &desc->legacy; + + wil_desc_addr_set(&d->dma.addr, pa); + d->dma.ip_length = 0; + /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ + d->dma.b11 = 0/*14 | BIT(7)*/; + d->dma.error = 0; + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = cpu_to_le16((u16)len); + d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + d->mac.d[0] = 0; + d->mac.d[1] = 0; + d->mac.d[2] = 0; + d->mac.ucode_cmd = 0; + /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ + d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | + (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); + + return 0; } -static inline void wil_tx_data_init(struct wil_ring_tx_data *txdata) +void wil_tx_data_init(struct wil_ring_tx_data *txdata) { spin_lock_bh(&txdata->lock); txdata->dot1x_open = 0; @@ -903,8 +928,8 @@ static inline void wil_tx_data_init(struct wil_ring_tx_data *txdata) spin_unlock_bh(&txdata->lock); } -int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, - int cid, int tid) +static int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, + int cid, int tid) { struct wil6210_priv *wil = vif_to_wil(vif); int rc; @@ -946,6 +971,7 @@ int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, } wil_tx_data_init(txdata); + vring->is_rx = false; vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -985,7 +1011,7 @@ int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, txdata->dot1x_open = false; txdata->enabled = 0; spin_unlock_bh(&txdata->lock); - wil_vring_free(wil, vring, 1); + wil_vring_free(wil, vring); wil->ring2cid_tid[id][0] = WIL6210_MAX_CID; wil->ring2cid_tid[id][1] = 0; @@ -1028,6 +1054,7 @@ int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) } wil_tx_data_init(txdata); + vring->is_rx = false; vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -1065,43 +1092,12 @@ int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) txdata->enabled = 0; txdata->dot1x_open = false; spin_unlock_bh(&txdata->lock); - wil_vring_free(wil, vring, 1); + wil_vring_free(wil, vring); out: return rc; } -void wil_ring_fini_tx(struct wil6210_priv *wil, int id) -{ - struct wil_ring *vring = &wil->ring_tx[id]; - struct wil_ring_tx_data *txdata = &wil->ring_tx_data[id]; - - lockdep_assert_held(&wil->mutex); - - if (!vring->va) - return; - - wil_dbg_misc(wil, "vring_fini_tx: id=%d\n", id); - - spin_lock_bh(&txdata->lock); - txdata->dot1x_open = false; - txdata->mid = U8_MAX; - txdata->enabled = 0; /* no Tx can be in progress or start anew */ - spin_unlock_bh(&txdata->lock); - /* napi_synchronize waits for completion of the current NAPI but will - * not prevent the next NAPI run. - * Add a memory barrier to guarantee that txdata->enabled is zeroed - * before napi_synchronize so that the next scheduled NAPI will not - * handle this vring - */ - wmb(); - /* make sure NAPI won't touch this vring */ - if (test_bit(wil_status_napi_en, wil->status)) - napi_synchronize(&wil->napi_tx); - - wil_vring_free(wil, vring, 1); -} - static struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil, struct wil6210_vif *vif, struct sk_buff *skb) @@ -1109,12 +1105,13 @@ static struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil, int i; struct ethhdr *eth = (void *)skb->data; int cid = wil_find_cid(wil, vif->mid, eth->h_dest); + int min_ring_id = wil_get_min_tx_ring_id(wil); if (cid < 0) return NULL; /* TODO: fix for multiple TID */ - for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++) { + for (i = min_ring_id; i < ARRAY_SIZE(wil->ring2cid_tid); i++) { if (!wil->ring_tx_data[i].dot1x_open && skb->protocol != cpu_to_be16(ETH_P_PAE)) continue; @@ -1138,8 +1135,8 @@ static struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil, return NULL; } -static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, - struct wil_ring *vring, struct sk_buff *skb); +static int wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct wil_ring *ring, struct sk_buff *skb); static struct wil_ring *wil_find_tx_ring_sta(struct wil6210_priv *wil, struct wil6210_vif *vif, @@ -1149,12 +1146,13 @@ static struct wil_ring *wil_find_tx_ring_sta(struct wil6210_priv *wil, int i; u8 cid; struct wil_ring_tx_data *txdata; + int min_ring_id = wil_get_min_tx_ring_id(wil); /* In the STA mode, it is expected to have only 1 VRING * for the AP we connected to. * find 1-st vring eligible for this skb and use it. */ - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { ring = &wil->ring_tx[i]; txdata = &wil->ring_tx_data[i]; if (!ring->va || !txdata->enabled || txdata->mid != vif->mid) @@ -1230,9 +1228,10 @@ static struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil, struct ethhdr *eth = (void *)skb->data; char *src = eth->h_source; struct wil_ring_tx_data *txdata, *txdata2; + int min_ring_id = wil_get_min_tx_ring_id(wil); /* find 1-st vring eligible for data */ - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->ring_tx[i]; txdata = &wil->ring_tx_data[i]; if (!v->va || !txdata->enabled || txdata->mid != vif->mid) @@ -1280,7 +1279,7 @@ static struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil, if (skb2) { wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i); wil_set_da_for_vring(wil, skb2, i); - wil_tx_vring(wil, vif, v2, skb2); + wil_tx_ring(wil, vif, v2, skb2); } else { wil_err(wil, "skb_copy failed\n"); } @@ -1289,28 +1288,6 @@ static struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil, return v; } -static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len, - int vring_index) -{ - wil_desc_addr_set(&d->dma.addr, pa); - d->dma.ip_length = 0; - /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ - d->dma.b11 = 0/*14 | BIT(7)*/; - d->dma.error = 0; - d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ - d->dma.length = cpu_to_le16((u16)len); - d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS); - d->mac.d[0] = 0; - d->mac.d[1] = 0; - d->mac.d[2] = 0; - d->mac.ucode_cmd = 0; - /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ - d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | - (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); - - return 0; -} - static inline void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags) { @@ -1520,7 +1497,8 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif, goto err_exit; } - wil_tx_desc_map(hdr_desc, pa, hdrlen, vring_index); + wil->txrx_ops.tx_desc_map((union wil_tx_desc *)hdr_desc, pa, + hdrlen, vring_index); wil_tx_desc_offload_setup_tso(hdr_desc, skb, wil_tso_type_hdr, is_ipv4, tcp_hdr_len, skb_net_hdr_len); wil_tx_last_desc(hdr_desc); @@ -1587,7 +1565,8 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif, d = &desc_mem; } - wil_tx_desc_map(d, pa, lenmss, vring_index); + wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, + pa, lenmss, vring_index); wil_tx_desc_offload_setup_tso(d, skb, desc_tso_type, is_ipv4, tcp_hdr_len, skb_net_hdr_len); @@ -1702,7 +1681,7 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif, *d = *_desc; _desc->dma.status = TX_DMA_STATUS_DU; ctx = &vring->ctx[i]; - wil_txdesc_unmap(dev, d, ctx); + wil_txdesc_unmap(dev, (union wil_tx_desc *)d, ctx); memset(ctx, 0, sizeof(*ctx)); descs_used--; } @@ -1710,26 +1689,26 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif, return rc; } -static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, - struct wil_ring *vring, struct sk_buff *skb) +static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct wil_ring *ring, struct sk_buff *skb) { struct device *dev = wil_to_dev(wil); struct vring_tx_desc dd, *d = ⅆ volatile struct vring_tx_desc *_d; - u32 swhead = vring->swhead; - int avail = wil_ring_avail_tx(vring); + u32 swhead = ring->swhead; + int avail = wil_ring_avail_tx(ring); int nr_frags = skb_shinfo(skb)->nr_frags; uint f = 0; - int vring_index = vring - wil->ring_tx; - struct wil_ring_tx_data *txdata = &wil->ring_tx_data[vring_index]; + int ring_index = ring - wil->ring_tx; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index]; uint i = swhead; dma_addr_t pa; int used; - bool mcast = (vring_index == vif->bcast_ring); + bool mcast = (ring_index == vif->bcast_ring); uint len = skb_headlen(skb); wil_dbg_txrx(wil, "tx_ring: %d bytes to ring %d, nr_frags %d\n", - skb->len, vring_index, nr_frags); + skb->len, ring_index, nr_frags); if (unlikely(!txdata->enabled)) return -EINVAL; @@ -1737,23 +1716,24 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, if (unlikely(avail < 1 + nr_frags)) { wil_err_ratelimited(wil, "Tx ring[%2d] full. No space for %d fragments\n", - vring_index, 1 + nr_frags); + ring_index, 1 + nr_frags); return -ENOMEM; } - _d = &vring->va[i].tx.legacy; + _d = &ring->va[i].tx.legacy; pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", vring_index, + wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", ring_index, skb_headlen(skb), skb->data, &pa); wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); if (unlikely(dma_mapping_error(dev, pa))) return -EINVAL; - vring->ctx[i].mapped_as = wil_mapped_as_single; + ring->ctx[i].mapped_as = wil_mapped_as_single; /* 1-st segment */ - wil_tx_desc_map(d, pa, len, vring_index); + wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, pa, len, + ring_index); if (unlikely(mcast)) { d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */ if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) /* set MCS 1 */ @@ -1762,11 +1742,11 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, /* Process TCP/UDP checksum offloading */ if (unlikely(wil_tx_desc_offload_setup(d, skb))) { wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n", - vring_index); + ring_index); goto dma_error; } - vring->ctx[i].nr_frags = nr_frags; + ring->ctx[i].nr_frags = nr_frags; wil_tx_desc_set_nr_frags(d, nr_frags + 1); /* middle segments */ @@ -1776,20 +1756,21 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, int len = skb_frag_size(frag); *_d = *d; - wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i); + wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i); wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); - i = (swhead + f + 1) % vring->size; - _d = &vring->va[i].tx.legacy; + i = (swhead + f + 1) % ring->size; + _d = &ring->va[i].tx.legacy; pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dev, pa))) { wil_err(wil, "Tx[%2d] failed to map fragment\n", - vring_index); + ring_index); goto dma_error; } - vring->ctx[i].mapped_as = wil_mapped_as_page; - wil_tx_desc_map(d, pa, len, vring_index); + ring->ctx[i].mapped_as = wil_mapped_as_page; + wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, + pa, len, ring_index); /* no need to check return code - * if it succeeded for 1-st descriptor, * it will succeed here too @@ -1801,7 +1782,7 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS); d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); *_d = *d; - wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i); + wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i); wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); @@ -1809,15 +1790,15 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, * to prevent skb release before accounting * in case of immediate "tx done" */ - vring->ctx[i].skb = skb_get(skb); + ring->ctx[i].skb = skb_get(skb); /* performance monitoring */ - used = wil_ring_used_tx(vring); + used = wil_ring_used_tx(ring); if (wil_val_in_range(wil->ring_idle_trsh, used, used + nr_frags + 1)) { txdata->idle += get_cycles() - txdata->last_idle; wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", - vring_index, used, used + nr_frags + 1); + ring_index, used, used + nr_frags + 1); } /* Make sure to advance the head only after descriptor update is done. @@ -1828,17 +1809,17 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, wmb(); /* advance swhead */ - wil_ring_advance_head(vring, nr_frags + 1); - wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", vring_index, swhead, - vring->swhead); - trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags); + wil_ring_advance_head(ring, nr_frags + 1); + wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", ring_index, swhead, + ring->swhead); + trace_wil6210_tx(ring_index, swhead, skb->len, nr_frags); /* make sure all writes to descriptors (shared memory) are done before * committing them to HW */ wmb(); - wil_w(wil, vring->hwtail, vring->swhead); + wil_w(wil, ring->hwtail, ring->swhead); return 0; dma_error: @@ -1847,12 +1828,14 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, for (f = 0; f < nr_frags; f++) { struct wil_ctx *ctx; - i = (swhead + f) % vring->size; - ctx = &vring->ctx[i]; - _d = &vring->va[i].tx.legacy; + i = (swhead + f) % ring->size; + ctx = &ring->ctx[i]; + _d = &ring->va[i].tx.legacy; *d = *_d; _d->dma.status = TX_DMA_STATUS_DU; - wil_txdesc_unmap(dev, d, ctx); + wil->txrx_ops.tx_desc_unmap(dev, + (union wil_tx_desc *)d, + ctx); memset(ctx, 0, sizeof(*ctx)); } @@ -1860,10 +1843,10 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, return -EINVAL; } -static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, - struct wil_ring *vring, struct sk_buff *skb) +static int wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct wil_ring *ring, struct sk_buff *skb) { - int ring_index = vring - wil->ring_tx; + int ring_index = ring - wil->ring_tx; struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index]; int rc; @@ -1878,8 +1861,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif, return -EINVAL; } - rc = (skb_is_gso(skb) ? __wil_tx_vring_tso : __wil_tx_vring) - (wil, vif, vring, skb); + rc = (skb_is_gso(skb) ? wil->txrx_ops.tx_ring_tso : __wil_tx_ring) + (wil, vif, ring, skb); spin_unlock(&txdata->lock); @@ -1986,7 +1969,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct wil6210_priv *wil = vif_to_wil(vif); struct ethhdr *eth = (void *)skb->data; bool bcast = is_multicast_ether_addr(eth->h_dest); - struct wil_ring *vring; + struct wil_ring *ring; static bool pr_once_fw; int rc; @@ -2012,36 +1995,36 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* find vring */ if (vif->wdev.iftype == NL80211_IFTYPE_STATION && !vif->pbss) { /* in STA mode (ESS), all to same VRING (to AP) */ - vring = wil_find_tx_ring_sta(wil, vif, skb); + ring = wil_find_tx_ring_sta(wil, vif, skb); } else if (bcast) { if (vif->pbss) /* in pbss, no bcast VRING - duplicate skb in * all stations VRINGs */ - vring = wil_find_tx_bcast_2(wil, vif, skb); + ring = wil_find_tx_bcast_2(wil, vif, skb); else if (vif->wdev.iftype == NL80211_IFTYPE_AP) /* AP has a dedicated bcast VRING */ - vring = wil_find_tx_bcast_1(wil, vif, skb); + ring = wil_find_tx_bcast_1(wil, vif, skb); else /* unexpected combination, fallback to duplicating * the skb in all stations VRINGs */ - vring = wil_find_tx_bcast_2(wil, vif, skb); + ring = wil_find_tx_bcast_2(wil, vif, skb); } else { /* unicast, find specific VRING by dest. address */ - vring = wil_find_tx_ucast(wil, vif, skb); + ring = wil_find_tx_ucast(wil, vif, skb); } - if (unlikely(!vring)) { + if (unlikely(!ring)) { wil_dbg_txrx(wil, "No Tx RING found for %pM\n", eth->h_dest); goto drop; } /* set up vring entry */ - rc = wil_tx_vring(wil, vif, vring, skb); + rc = wil_tx_ring(wil, vif, ring, skb); switch (rc) { case 0: /* shall we stop net queues? */ - wil_update_net_queues_bh(wil, vif, vring, true); + wil_update_net_queues_bh(wil, vif, ring, true); /* statistics will be updated on the tx_complete */ dev_kfree_skb_any(skb); return NETDEV_TX_OK; @@ -2057,22 +2040,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) return NET_XMIT_DROP; } -static inline bool wil_need_txstat(struct sk_buff *skb) -{ - struct ethhdr *eth = (void *)skb->data; - - return is_unicast_ether_addr(eth->h_dest) && skb->sk && - (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS); -} - -static inline void wil_consume_skb(struct sk_buff *skb, bool acked) -{ - if (unlikely(wil_need_txstat(skb))) - skb_complete_wifi_ack(skb, acked); - else - acked ? dev_consume_skb_any(skb) : dev_kfree_skb_any(skb); -} - /** * Clean up transmitted skb's from the Tx VRING * @@ -2148,7 +2115,9 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid) wil_hex_dump_txrx("TxCD ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); - wil_txdesc_unmap(dev, d, ctx); + wil->txrx_ops.tx_desc_unmap(dev, + (union wil_tx_desc *)d, + ctx); if (skb) { if (likely(d->dma.error == 0)) { @@ -2197,3 +2166,46 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid) return done; } + +static inline int wil_tx_init(struct wil6210_priv *wil) +{ + return 0; +} + +static inline void wil_tx_fini(struct wil6210_priv *wil) {} + +static void wil_get_reorder_params(struct sk_buff *skb, int *tid, int *cid, + int *mid, u16 *seq, int *mcast) +{ + struct vring_rx_desc *d = wil_skb_rxdesc(skb); + + *tid = wil_rxdesc_tid(d); + *cid = wil_rxdesc_cid(d); + *mid = wil_rxdesc_mid(d); + *seq = wil_rxdesc_seq(d); + *mcast = wil_rxdesc_mcast(d); +} + +void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil) +{ + wil->txrx_ops.configure_interrupt_moderation = + wil_configure_interrupt_moderation; + /* TX ops */ + wil->txrx_ops.tx_desc_map = wil_tx_desc_map; + wil->txrx_ops.tx_desc_unmap = wil_txdesc_unmap; + wil->txrx_ops.tx_ring_tso = __wil_tx_vring_tso; + wil->txrx_ops.ring_init_tx = wil_vring_init_tx; + wil->txrx_ops.ring_fini_tx = wil_vring_free; + wil->txrx_ops.ring_init_bcast = wil_vring_init_bcast; + wil->txrx_ops.tx_init = wil_tx_init; + wil->txrx_ops.tx_fini = wil_tx_fini; + /* RX ops */ + wil->txrx_ops.rx_init = wil_rx_init; + wil->txrx_ops.wmi_addba_rx_resp = wmi_addba_rx_resp; + wil->txrx_ops.get_reorder_params = wil_get_reorder_params; + wil->txrx_ops.get_netif_rx_params = + wil_get_netif_rx_params; + wil->txrx_ops.rx_crypto_check = wil_rx_crypto_check; + wil->txrx_ops.is_rx_idle = wil_is_rx_idle; + wil->txrx_ops.rx_fini = wil_rx_fini; +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 66217f81572675c7cf7607db041d5fa249719c7b..f361423628f54352780aa682e88709c5043d394d 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -555,6 +555,22 @@ static inline int wil_ring_is_full(struct wil_ring *ring) return wil_ring_next_tail(ring) == ring->swhead; } +static inline bool wil_need_txstat(struct sk_buff *skb) +{ + struct ethhdr *eth = (void *)skb->data; + + return is_unicast_ether_addr(eth->h_dest) && skb->sk && + (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS); +} + +static inline void wil_consume_skb(struct sk_buff *skb, bool acked) +{ + if (unlikely(wil_need_txstat(skb))) + skb_complete_wifi_ack(skb, acked); + else + acked ? dev_consume_skb_any(skb) : dev_kfree_skb_any(skb); +} + /* Used space in Tx ring */ static inline int wil_ring_used_tx(struct wil_ring *ring) { @@ -570,6 +586,25 @@ static inline int wil_ring_avail_tx(struct wil_ring *ring) return ring->size - wil_ring_used_tx(ring) - 1; } +static inline int wil_get_min_tx_ring_id(struct wil6210_priv *wil) +{ + /* In Enhanced DMA ring 0 is reserved for RX */ + return wil->use_enhanced_dma_hw ? 1 : 0; +} + +/* similar to ieee80211_ version, but FC contain only 1-st byte */ +static inline int wil_is_back_req(u8 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); +} + +/* wil_val_in_range - check if value in [min,max) */ +static inline bool wil_val_in_range(int val, int min, int max) +{ + return val >= min && val < max; +} + void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb); void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif, @@ -578,5 +613,7 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, int size, u16 ssn); void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *r); +void wil_tx_data_init(struct wil_ring_tx_data *txdata); +void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil); #endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c new file mode 100644 index 0000000000000000000000000000000000000000..11d6c9277e4de50288395b14efd24f55c56fce38 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -0,0 +1,1637 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "wil6210.h" +#include "txrx_edma.h" +#include "txrx.h" +#include "trace.h" + +/* limit status ring size in range [ring size..max ring size] */ +#define WIL_SRING_SIZE_ORDER_MIN (WIL_RING_SIZE_ORDER_MIN) +#define WIL_SRING_SIZE_ORDER_MAX (WIL_RING_SIZE_ORDER_MAX) +/* RX sring order should be bigger than RX ring order */ +#define WIL_RX_SRING_SIZE_ORDER_DEFAULT (11) +#define WIL_TX_SRING_SIZE_ORDER_DEFAULT (12) +#define WIL_RX_BUFF_ARR_SIZE_DEFAULT (1536) +#define WIL_EDMA_MAX_DATA_OFFSET (2) + +bool use_compressed_rx_status = true; +module_param(use_compressed_rx_status, bool, 0444); +MODULE_PARM_DESC(use_compressed_rx_status, " Use compressed or extended Rx status message. Default: true"); + +bool use_rx_hw_reordering = true; +module_param(use_rx_hw_reordering, bool, 0444); +MODULE_PARM_DESC(use_rx_hw_reordering, " RX Packet reordering in HW. Default: true"); + +static int sring_order_set(const char *val, const struct kernel_param *kp) +{ + int ret; + uint x; + + ret = kstrtouint(val, 0, &x); + if (ret) + return ret; + + if (x < WIL_SRING_SIZE_ORDER_MIN || x > WIL_SRING_SIZE_ORDER_MAX) + return -EINVAL; + + *((uint *)kp->arg) = x; + + return 0; +} + +static const struct kernel_param_ops sring_order_ops = { + .set = sring_order_set, + .get = param_get_uint, +}; + +/* Status ring size should be bigger than the number of RX buffers in order + * to prevent backpressure on the status ring, which may cause HW freeze. + */ +static uint rx_status_ring_order = WIL_RX_SRING_SIZE_ORDER_DEFAULT; +module_param_cb(rx_status_ring_order, &sring_order_ops, &rx_status_ring_order, + 0444); +MODULE_PARM_DESC(rx_status_ring_order, " Rx status ring order; size = 1 << order. Default: 11"); + +static uint tx_status_ring_order = WIL_TX_SRING_SIZE_ORDER_DEFAULT; +module_param_cb(tx_status_ring_order, &sring_order_ops, &tx_status_ring_order, + 0444); +MODULE_PARM_DESC(tx_status_ring_order, " Tx status ring order; size = 1 << order. Default: 12"); + +/* Number of RX buffer IDs should be bigger than the RX descriptor ring size + * as in HW reorder flow, the HW can consume additional buffers before + * releasing the previous ones. + */ +static uint rx_buff_id_count = WIL_RX_BUFF_ARR_SIZE_DEFAULT; +module_param(rx_buff_id_count, uint, 0444); +MODULE_PARM_DESC(rx_buff_id_count, " Rx buffer IDs count: default: 1536"); + +static void wil_tx_desc_unmap_edma(struct device *dev, + union wil_tx_desc *desc, + struct wil_ctx *ctx) +{ + struct wil_tx_enhanced_desc *d = (struct wil_tx_enhanced_desc *)desc; + dma_addr_t pa = wil_tx_desc_get_addr_edma(&d->dma); + u16 dmalen = le16_to_cpu(d->dma.length); + + switch (ctx->mapped_as) { + case wil_mapped_as_single: + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); + break; + case wil_mapped_as_page: + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); + break; + default: + break; + } +} + +static int wil_find_free_sring(struct wil6210_priv *wil) +{ + int i; + + for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++) { + if (!wil->srings[i].va) + return i; + } + + return -EINVAL; +} + +static void wil_sring_free(struct wil6210_priv *wil, + struct wil_status_ring *sring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz; + + if (!sring || !sring->va) + return; + + sz = sring->elem_size * sring->size; + + wil_dbg_misc(wil, "status_ring_free, size(bytes)=%zu, 0x%p:%pad\n", + sz, sring->va, &sring->pa); + + dma_free_coherent(dev, sz, (void *)sring->va, sring->pa); + sring->pa = 0; + sring->va = NULL; +} + +static int wil_sring_alloc(struct wil6210_priv *wil, + struct wil_status_ring *sring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = sring->elem_size * sring->size; + + wil_dbg_misc(wil, "status_ring_alloc: size=%zu\n", sz); + + if (sz == 0) { + wil_err(wil, "Cannot allocate a zero size status ring\n"); + return -EINVAL; + } + + sring->swhead = 0; + + /* Status messages are allocated and initialized to 0. This is necessary + * since DR bit should be initialized to 0. + */ + sring->va = dma_zalloc_coherent(dev, sz, &sring->pa, GFP_KERNEL); + if (!sring->va) + return -ENOMEM; + + wil_dbg_misc(wil, "status_ring[%d] 0x%p:%pad\n", sring->size, sring->va, + &sring->pa); + + return 0; +} + +static int wil_tx_init_edma(struct wil6210_priv *wil) +{ + int ring_id = wil_find_free_sring(wil); + struct wil_status_ring *sring; + int rc; + u16 status_ring_size = 1 << tx_status_ring_order; + + wil_dbg_misc(wil, "init TX sring: size=%u, ring_id=%u\n", + status_ring_size, ring_id); + + if (ring_id < 0) + return ring_id; + + /* Allocate Tx status ring. Tx descriptor rings will be + * allocated on WMI connect event + */ + sring = &wil->srings[ring_id]; + + sring->is_rx = false; + sring->size = status_ring_size; + sring->elem_size = sizeof(struct wil_ring_tx_status); + rc = wil_sring_alloc(wil, sring); + if (rc) + return rc; + + rc = wil_wmi_tx_sring_cfg(wil, ring_id); + if (rc) + goto out_free; + + sring->desc_rdy_pol = 1; + wil->tx_sring_idx = ring_id; + + return 0; +out_free: + wil_sring_free(wil, sring); + return rc; +} + +/** + * Allocate one skb for Rx descriptor RING + */ +static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil, + struct wil_ring *ring, u32 i) +{ + struct device *dev = wil_to_dev(wil); + unsigned int sz = wil->rx_buf_len + ETH_HLEN + + WIL_EDMA_MAX_DATA_OFFSET; + dma_addr_t pa; + u16 buff_id; + struct list_head *active = &wil->rx_buff_mgmt.active; + struct list_head *free = &wil->rx_buff_mgmt.free; + struct wil_rx_buff *rx_buff; + struct wil_rx_buff *buff_arr = wil->rx_buff_mgmt.buff_arr; + struct sk_buff *skb; + struct wil_rx_enhanced_desc dd, *d = ⅆ + struct wil_rx_enhanced_desc *_d = (struct wil_rx_enhanced_desc *) + &ring->va[i].rx.enhanced; + + if (unlikely(list_empty(free))) { + wil->rx_buff_mgmt.free_list_empty_cnt++; + return -EAGAIN; + } + + skb = dev_alloc_skb(sz); + if (unlikely(!skb)) + return -ENOMEM; + + skb_put(skb, sz); + + pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) { + kfree_skb(skb); + return -ENOMEM; + } + + /* Get the buffer ID - the index of the rx buffer in the buff_arr */ + rx_buff = list_first_entry(free, struct wil_rx_buff, list); + buff_id = rx_buff->id; + + /* Move a buffer from the free list to the active list */ + list_move(&rx_buff->list, active); + + buff_arr[buff_id].skb = skb; + + wil_desc_set_addr_edma(&d->dma.addr, &d->dma.addr_high_high, pa); + d->dma.length = cpu_to_le16(sz); + d->mac.buff_id = cpu_to_le16(buff_id); + *_d = *d; + + /* Save the physical address in skb->cb for later use in dma_unmap */ + memcpy(skb->cb, &pa, sizeof(pa)); + + return 0; +} + +static inline +void wil_get_next_rx_status_msg(struct wil_status_ring *sring, void *msg) +{ + memcpy(msg, (void *)(sring->va + (sring->elem_size * sring->swhead)), + sring->elem_size); +} + +static inline void wil_sring_advance_swhead(struct wil_status_ring *sring) +{ + sring->swhead = (sring->swhead + 1) % sring->size; + if (sring->swhead == 0) + sring->desc_rdy_pol = 1 - sring->desc_rdy_pol; +} + +static int wil_rx_refill_edma(struct wil6210_priv *wil) +{ + struct wil_ring *ring = &wil->ring_rx; + u32 next_head; + int rc = 0; + u32 swtail = *ring->edma_rx_swtail.va; + + for (; next_head = wil_ring_next_head(ring), (next_head != swtail); + ring->swhead = next_head) { + rc = wil_ring_alloc_skb_edma(wil, ring, ring->swhead); + if (unlikely(rc)) { + if (rc == -EAGAIN) + wil_dbg_txrx(wil, "No free buffer ID found\n"); + else + wil_err_ratelimited(wil, + "Error %d in refill desc[%d]\n", + rc, ring->swhead); + break; + } + } + + /* make sure all writes to descriptors (shared memory) are done before + * committing them to HW + */ + wmb(); + + wil_w(wil, ring->hwtail, ring->swhead); + + return rc; +} + +static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil, + struct wil_ring *ring) +{ + struct device *dev = wil_to_dev(wil); + u32 next_tail; + u32 swhead = (ring->swhead + 1) % ring->size; + dma_addr_t pa; + u16 dmalen; + + for (; next_tail = wil_ring_next_tail(ring), (next_tail != swhead); + ring->swtail = next_tail) { + struct wil_rx_enhanced_desc dd, *d = ⅆ + struct wil_rx_enhanced_desc *_d = + (struct wil_rx_enhanced_desc *) + &ring->va[ring->swtail].rx.enhanced; + struct sk_buff *skb; + u16 buff_id; + + *d = *_d; + pa = wil_rx_desc_get_addr_edma(&d->dma); + dmalen = le16_to_cpu(d->dma.length); + dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); + + /* Extract the SKB from the rx_buff management array */ + buff_id = __le16_to_cpu(d->mac.buff_id); + if (buff_id >= wil->rx_buff_mgmt.size) { + wil_err(wil, "invalid buff_id %d\n", buff_id); + continue; + } + skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; + wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL; + if (unlikely(!skb)) + wil_err(wil, "No Rx skb at buff_id %d\n", buff_id); + else + kfree_skb(skb); + + /* Move the buffer from the active to the free list */ + list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list, + &wil->rx_buff_mgmt.free); + } +} + +static void wil_free_rx_buff_arr(struct wil6210_priv *wil) +{ + struct wil_ring *ring = &wil->ring_rx; + + if (!wil->rx_buff_mgmt.buff_arr) + return; + + /* Move all the buffers to the free list in case active list is + * not empty in order to release all SKBs before deleting the array + */ + wil_move_all_rx_buff_to_free_list(wil, ring); + + kfree(wil->rx_buff_mgmt.buff_arr); + wil->rx_buff_mgmt.buff_arr = NULL; +} + +static int wil_init_rx_buff_arr(struct wil6210_priv *wil, + size_t size) +{ + struct wil_rx_buff *buff_arr; + struct list_head *active = &wil->rx_buff_mgmt.active; + struct list_head *free = &wil->rx_buff_mgmt.free; + int i; + + wil->rx_buff_mgmt.buff_arr = kcalloc(size, sizeof(struct wil_rx_buff), + GFP_KERNEL); + if (!wil->rx_buff_mgmt.buff_arr) + return -ENOMEM; + + /* Set list heads */ + INIT_LIST_HEAD(active); + INIT_LIST_HEAD(free); + + /* Linkify the list */ + buff_arr = wil->rx_buff_mgmt.buff_arr; + for (i = 0; i < size; i++) { + list_add(&buff_arr[i].list, free); + buff_arr[i].id = i; + } + + wil->rx_buff_mgmt.size = size; + + return 0; +} + +static int wil_init_rx_sring(struct wil6210_priv *wil, + u16 status_ring_size, + size_t elem_size, + u16 ring_id) +{ + struct wil_status_ring *sring = &wil->srings[ring_id]; + int rc; + + wil_dbg_misc(wil, "init RX sring: size=%u, ring_id=%u\n", sring->size, + ring_id); + + memset(&sring->rx_data, 0, sizeof(sring->rx_data)); + + sring->is_rx = true; + sring->size = status_ring_size; + sring->elem_size = elem_size; + rc = wil_sring_alloc(wil, sring); + if (rc) + return rc; + + rc = wil_wmi_rx_sring_add(wil, ring_id); + if (rc) + goto out_free; + + sring->desc_rdy_pol = 1; + + return 0; +out_free: + wil_sring_free(wil, sring); + return rc; +} + +static int wil_ring_alloc_desc_ring(struct wil6210_priv *wil, + struct wil_ring *ring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = ring->size * sizeof(ring->va[0]); + + wil_dbg_misc(wil, "alloc_desc_ring:\n"); + + BUILD_BUG_ON(sizeof(ring->va[0]) != 32); + + ring->swhead = 0; + ring->swtail = 0; + ring->ctx = kcalloc(ring->size, sizeof(ring->ctx[0]), GFP_KERNEL); + if (!ring->ctx) + goto err; + + ring->va = dma_zalloc_coherent(dev, sz, &ring->pa, GFP_KERNEL); + if (!ring->va) + goto err_free_ctx; + + if (ring->is_rx) { + sz = sizeof(*ring->edma_rx_swtail.va); + ring->edma_rx_swtail.va = + dma_zalloc_coherent(dev, sz, &ring->edma_rx_swtail.pa, + GFP_KERNEL); + if (!ring->edma_rx_swtail.va) + goto err_free_va; + } + + wil_dbg_misc(wil, "%s ring[%d] 0x%p:%pad 0x%p\n", + ring->is_rx ? "RX" : "TX", + ring->size, ring->va, &ring->pa, ring->ctx); + + return 0; +err_free_va: + dma_free_coherent(dev, ring->size * sizeof(ring->va[0]), + (void *)ring->va, ring->pa); + ring->va = NULL; +err_free_ctx: + kfree(ring->ctx); + ring->ctx = NULL; +err: + return -ENOMEM; +} + +static void wil_ring_free_edma(struct wil6210_priv *wil, struct wil_ring *ring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz; + int ring_index = 0; + + if (!ring->va) + return; + + sz = ring->size * sizeof(ring->va[0]); + + lockdep_assert_held(&wil->mutex); + if (ring->is_rx) { + wil_dbg_misc(wil, "free Rx ring [%d] 0x%p:%pad 0x%p\n", + ring->size, ring->va, + &ring->pa, ring->ctx); + + wil_move_all_rx_buff_to_free_list(wil, ring); + goto out; + } + + /* TX ring */ + ring_index = ring - wil->ring_tx; + + wil_dbg_misc(wil, "free Tx ring %d [%d] 0x%p:%pad 0x%p\n", + ring_index, ring->size, ring->va, + &ring->pa, ring->ctx); + + while (!wil_ring_is_empty(ring)) { + struct wil_ctx *ctx; + + struct wil_tx_enhanced_desc dd, *d = ⅆ + struct wil_tx_enhanced_desc *_d = + (struct wil_tx_enhanced_desc *) + &ring->va[ring->swtail].tx.enhanced; + + ctx = &ring->ctx[ring->swtail]; + if (!ctx) { + wil_dbg_txrx(wil, + "ctx(%d) was already completed\n", + ring->swtail); + ring->swtail = wil_ring_next_tail(ring); + continue; + } + *d = *_d; + wil_tx_desc_unmap_edma(dev, (union wil_tx_desc *)d, ctx); + if (ctx->skb) + dev_kfree_skb_any(ctx->skb); + ring->swtail = wil_ring_next_tail(ring); + } + +out: + dma_free_coherent(dev, sz, (void *)ring->va, ring->pa); + kfree(ring->ctx); + ring->pa = 0; + ring->va = NULL; + ring->ctx = NULL; +} + +static int wil_init_rx_desc_ring(struct wil6210_priv *wil, u16 desc_ring_size, + int status_ring_id) +{ + struct wil_ring *ring = &wil->ring_rx; + int rc; + + wil_dbg_misc(wil, "init RX desc ring\n"); + + ring->size = desc_ring_size; + ring->is_rx = true; + rc = wil_ring_alloc_desc_ring(wil, ring); + if (rc) + return rc; + + rc = wil_wmi_rx_desc_ring_add(wil, status_ring_id); + if (rc) + goto out_free; + + return 0; +out_free: + wil_ring_free_edma(wil, ring); + return rc; +} + +static void wil_get_reorder_params_edma(struct sk_buff *skb, int *tid, + int *cid, int *mid, u16 *seq, + int *mcast) +{ + struct wil_rx_status_extended *s = wil_skb_rxstatus(skb); + + *tid = wil_rx_status_get_tid(s); + *cid = wil_rx_status_get_cid(s); + *mid = wil_rx_status_get_mid(s); + *seq = le16_to_cpu(wil_rx_status_get_seq(s)); + *mcast = wil_rx_status_get_mcast(s); +} + +static void wil_get_netif_rx_params_edma(struct sk_buff *skb, int *cid, + int *security) +{ + struct wil_rx_status_extended *s = wil_skb_rxstatus(skb); + + *cid = wil_rx_status_get_cid(s); + *security = wil_rx_status_get_security(s); +} + +static int wil_rx_crypto_check_edma(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct wil_rx_status_extended *st; + int cid, tid, key_id, mc; + struct wil_sta_info *s; + struct wil_tid_crypto_rx *c; + struct wil_tid_crypto_rx_single *cc; + const u8 *pn; + + /* In HW reorder, HW is responsible for crypto check */ + if (use_rx_hw_reordering) + return 0; + + st = wil_skb_rxstatus(skb); + + cid = wil_rx_status_get_cid(st); + tid = wil_rx_status_get_tid(st); + key_id = wil_rx_status_get_key_id(st); + mc = wil_rx_status_get_mcast(st); + s = &wil->sta[cid]; + c = mc ? &s->group_crypto_rx : &s->tid_crypto_rx[tid]; + cc = &c->key_id[key_id]; + pn = (u8 *)&st->ext.pn_15_0; + + if (!cc->key_set) { + wil_err_ratelimited(wil, + "Key missing. CID %d TID %d MCast %d KEY_ID %d\n", + cid, tid, mc, key_id); + return -EINVAL; + } + + if (reverse_memcmp(pn, cc->pn, IEEE80211_GCMP_PN_LEN) <= 0) { + wil_err_ratelimited(wil, + "Replay attack. CID %d TID %d MCast %d KEY_ID %d PN %6phN last %6phN\n", + cid, tid, mc, key_id, pn, cc->pn); + return -EINVAL; + } + memcpy(cc->pn, pn, IEEE80211_GCMP_PN_LEN); + + return 0; +} + +static bool wil_is_rx_idle_edma(struct wil6210_priv *wil) +{ + struct wil_status_ring *sring; + struct wil_rx_status_extended msg1; + void *msg = &msg1; + u8 dr_bit; + int i; + + for (i = 0; i < wil->num_rx_status_rings; i++) { + sring = &wil->srings[i]; + if (!sring->va) + continue; + + wil_get_next_rx_status_msg(sring, msg); + dr_bit = wil_rx_status_get_desc_rdy_bit(msg); + + /* Check if there are unhandled RX status messages */ + if (dr_bit == sring->desc_rdy_pol) + return false; + } + + return true; +} + +static void wil_rx_buf_len_init_edma(struct wil6210_priv *wil) +{ + wil->rx_buf_len = rx_large_buf ? + WIL_MAX_ETH_MTU : TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; +} + +static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size) +{ + u16 status_ring_size = 1 << rx_status_ring_order; + struct wil_ring *ring = &wil->ring_rx; + int rc; + size_t elem_size = use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended); + int i; + u16 max_rx_pl_per_desc; + + /* In SW reorder one must use extended status messages */ + if (use_compressed_rx_status && !use_rx_hw_reordering) { + wil_err(wil, + "compressed RX status cannot be used with SW reorder\n"); + return -EINVAL; + } + + wil_dbg_misc(wil, + "rx_init, desc_ring_size=%u, status_ring_size=%u, elem_size=%zu\n", + desc_ring_size, status_ring_size, elem_size); + + wil_rx_buf_len_init_edma(wil); + + max_rx_pl_per_desc = wil->rx_buf_len + ETH_HLEN + + WIL_EDMA_MAX_DATA_OFFSET; + + /* Use debugfs dbg_num_rx_srings if set, reserve one sring for TX */ + if (wil->num_rx_status_rings > WIL6210_MAX_STATUS_RINGS - 1) + wil->num_rx_status_rings = WIL6210_MAX_STATUS_RINGS - 1; + + wil_dbg_misc(wil, "rx_init: allocate %d status rings\n", + wil->num_rx_status_rings); + + rc = wil_wmi_cfg_def_rx_offload(wil, max_rx_pl_per_desc); + if (rc) + return rc; + + /* Allocate status ring */ + for (i = 0; i < wil->num_rx_status_rings; i++) { + int sring_id = wil_find_free_sring(wil); + + if (sring_id < 0) { + rc = -EFAULT; + goto err_free_status; + } + rc = wil_init_rx_sring(wil, status_ring_size, elem_size, + sring_id); + if (rc) + goto err_free_status; + } + + /* Allocate descriptor ring */ + rc = wil_init_rx_desc_ring(wil, desc_ring_size, + WIL_DEFAULT_RX_STATUS_RING_ID); + if (rc) + goto err_free_status; + + if (rx_buff_id_count >= status_ring_size) { + wil_info(wil, + "rx_buff_id_count %d exceeds sring_size %d. set it to %d\n", + rx_buff_id_count, status_ring_size, + status_ring_size - 1); + rx_buff_id_count = status_ring_size - 1; + } + + /* Allocate Rx buffer array */ + rc = wil_init_rx_buff_arr(wil, rx_buff_id_count); + if (rc) + goto err_free_desc; + + /* Fill descriptor ring with credits */ + rc = wil_rx_refill_edma(wil); + if (rc) + goto err_free_rx_buff_arr; + + return 0; +err_free_rx_buff_arr: + wil_free_rx_buff_arr(wil); +err_free_desc: + wil_ring_free_edma(wil, ring); +err_free_status: + for (i = 0; i < wil->num_rx_status_rings; i++) + wil_sring_free(wil, &wil->srings[i]); + + return rc; +} + +static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, + int size, int cid, int tid) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + int rc; + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; + + lockdep_assert_held(&wil->mutex); + + wil_dbg_misc(wil, + "init TX ring: ring_id=%u, cid=%u, tid=%u, sring_id=%u\n", + ring_id, cid, tid, wil->tx_sring_idx); + + wil_tx_data_init(txdata); + ring->size = size; + rc = wil_ring_alloc_desc_ring(wil, ring); + if (rc) + goto out; + + wil->ring2cid_tid[ring_id][0] = cid; + wil->ring2cid_tid[ring_id][1] = tid; + if (!vif->privacy) + txdata->dot1x_open = true; + + rc = wil_wmi_tx_desc_ring_add(vif, ring_id, cid, tid); + if (rc) { + wil_err(wil, "WMI_TX_DESC_RING_ADD_CMD failed\n"); + goto out_free; + } + + if (txdata->dot1x_open && agg_wsize >= 0) + wil_addba_tx_request(wil, ring_id, agg_wsize); + + return 0; + out_free: + spin_lock_bh(&txdata->lock); + txdata->dot1x_open = false; + txdata->enabled = 0; + spin_unlock_bh(&txdata->lock); + wil_ring_free_edma(wil, ring); + wil->ring2cid_tid[ring_id][0] = WIL6210_MAX_CID; + wil->ring2cid_tid[ring_id][1] = 0; + + out: + return rc; +} + +/* This function is used only for RX SW reorder */ +static int wil_check_bar(struct wil6210_priv *wil, void *msg, int cid, + struct sk_buff *skb, struct wil_net_stats *stats) +{ + u8 ftype; + u8 fc1; + int mid; + int tid; + u16 seq; + struct wil6210_vif *vif; + + ftype = wil_rx_status_get_frame_type(msg); + if (ftype == IEEE80211_FTYPE_DATA) + return 0; + + fc1 = wil_rx_status_get_fc1(msg); + mid = wil_rx_status_get_mid(msg); + tid = wil_rx_status_get_tid(msg); + seq = le16_to_cpu(wil_rx_status_get_seq(msg)); + vif = wil->vifs[mid]; + + if (unlikely(!vif)) { + wil_dbg_txrx(wil, "RX descriptor with invalid mid %d", mid); + return -EAGAIN; + } + + wil_dbg_txrx(wil, + "Non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", + fc1, mid, cid, tid, seq); + stats->rx_non_data_frame++; + if (wil_is_back_req(fc1)) { + wil_dbg_txrx(wil, + "BAR: MID %d CID %d TID %d Seq 0x%03x\n", + mid, cid, tid, seq); + wil_rx_bar(wil, vif, cid, tid, seq); + } else { + u32 sz = use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended); + + /* print again all info. One can enable only this + * without overhead for printing every Rx frame + */ + wil_dbg_txrx(wil, + "Unhandled non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", + fc1, mid, cid, tid, seq); + wil_hex_dump_txrx("RxS ", DUMP_PREFIX_NONE, 32, 4, + (const void *)msg, sz, false); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + } + + return -EAGAIN; +} + +static int wil_rx_edma_check_errors(struct wil6210_priv *wil, void *msg, + struct wil_net_stats *stats, + struct sk_buff *skb) +{ + int error; + int l2_rx_status; + int l3_rx_status; + int l4_rx_status; + + error = wil_rx_status_get_error(msg); + if (!error) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + return 0; + } + + l2_rx_status = wil_rx_status_get_l2_rx_status(msg); + if (l2_rx_status != 0) { + wil_dbg_txrx(wil, "L2 RX error, l2_rx_status=0x%x\n", + l2_rx_status); + /* Due to HW issue, KEY error will trigger a MIC error */ + if (l2_rx_status & WIL_RX_EDMA_ERROR_MIC) { + wil_dbg_txrx(wil, + "L2 MIC/KEY error, dropping packet\n"); + stats->rx_mic_error++; + } + if (l2_rx_status & WIL_RX_EDMA_ERROR_KEY) { + wil_dbg_txrx(wil, "L2 KEY error, dropping packet\n"); + stats->rx_key_error++; + } + if (l2_rx_status & WIL_RX_EDMA_ERROR_REPLAY) { + wil_dbg_txrx(wil, + "L2 REPLAY error, dropping packet\n"); + stats->rx_replay++; + } + if (l2_rx_status & WIL_RX_EDMA_ERROR_AMSDU) { + wil_dbg_txrx(wil, + "L2 AMSDU error, dropping packet\n"); + stats->rx_amsdu_error++; + } + return -EFAULT; + } + + l3_rx_status = wil_rx_status_get_l3_rx_status(msg); + l4_rx_status = wil_rx_status_get_l4_rx_status(msg); + if (!l3_rx_status && !l4_rx_status) + skb->ip_summed = CHECKSUM_UNNECESSARY; + /* If HW reports bad checksum, let IP stack re-check it + * For example, HW don't understand Microsoft IP stack that + * mis-calculates TCP checksum - if it should be 0x0, + * it writes 0xffff in violation of RFC 1624 + */ + + return 0; +} + +static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil, + struct wil_status_ring *sring) +{ + struct device *dev = wil_to_dev(wil); + struct wil_rx_status_extended msg1; + void *msg = &msg1; + u16 buff_id; + struct sk_buff *skb; + dma_addr_t pa; + struct wil_ring_rx_data *rxdata = &sring->rx_data; + unsigned int sz = wil->rx_buf_len + ETH_HLEN + + WIL_EDMA_MAX_DATA_OFFSET; + struct wil_net_stats *stats; + u16 dmalen; + int cid; + int rc; + bool eop, headstolen; + int delta; + u8 dr_bit; + u8 data_offset; + struct wil_rx_status_extended *s; + u16 sring_idx = sring - wil->srings; + + BUILD_BUG_ON(sizeof(struct wil_rx_status_extended) > sizeof(skb->cb)); + +again: + wil_get_next_rx_status_msg(sring, msg); + dr_bit = wil_rx_status_get_desc_rdy_bit(msg); + + /* Completed handling all the ready status messages */ + if (dr_bit != sring->desc_rdy_pol) + return NULL; + + /* Extract the buffer ID from the status message */ + buff_id = le16_to_cpu(wil_rx_status_get_buff_id(msg)); + if (unlikely(!wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size))) { + wil_err(wil, "Corrupt buff_id=%d, sring->swhead=%d\n", + buff_id, sring->swhead); + wil_sring_advance_swhead(sring); + goto again; + } + + wil_sring_advance_swhead(sring); + + /* Extract the SKB from the rx_buff management array */ + skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; + wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL; + if (!skb) { + wil_err(wil, "No Rx skb at buff_id %d\n", buff_id); + goto again; + } + + memcpy(&pa, skb->cb, sizeof(pa)); + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); + dmalen = le16_to_cpu(wil_rx_status_get_length(msg)); + + trace_wil6210_rx_status(use_compressed_rx_status, buff_id, msg); + wil_dbg_txrx(wil, "Rx, buff_id=%u, sring_idx=%u, dmalen=%u bytes\n", + buff_id, sring_idx, dmalen); + wil_hex_dump_txrx("RxS ", DUMP_PREFIX_NONE, 32, 4, + (const void *)msg, use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended), false); + + /* Move the buffer from the active list to the free list */ + list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list, + &wil->rx_buff_mgmt.free); + + eop = wil_rx_status_get_eop(msg); + + cid = wil_rx_status_get_cid(msg); + if (unlikely(!wil_val_in_range(cid, 0, WIL6210_MAX_CID))) { + wil_err(wil, "Corrupt cid=%d, sring->swhead=%d\n", + cid, sring->swhead); + rxdata->skipping = true; + goto skipping; + } + stats = &wil->sta[cid].stats; + + if (unlikely(skb->len < ETH_HLEN)) { + wil_dbg_txrx(wil, "Short frame, len = %d\n", skb->len); + stats->rx_short_frame++; + rxdata->skipping = true; + goto skipping; + } + + /* Check and treat errors reported by HW */ + rc = wil_rx_edma_check_errors(wil, msg, stats, skb); + if (rc) { + rxdata->skipping = true; + goto skipping; + } + + if (unlikely(dmalen > sz)) { + wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); + stats->rx_large_frame++; + rxdata->skipping = true; + } + +skipping: + /* skipping indicates if a certain SKB should be dropped. + * It is set in case there is an error on the current SKB or in case + * of RX chaining: as long as we manage to merge the SKBs it will + * be false. once we have a bad SKB or we don't manage to merge SKBs + * it will be set to the !EOP value of the current SKB. + * This guarantees that all the following SKBs until EOP will also + * get dropped. + */ + if (unlikely(rxdata->skipping)) { + kfree_skb(skb); + if (rxdata->skb) { + kfree_skb(rxdata->skb); + rxdata->skb = NULL; + } + rxdata->skipping = !eop; + goto again; + } + + skb_trim(skb, dmalen); + + prefetch(skb->data); + + if (!rxdata->skb) { + rxdata->skb = skb; + } else { + if (likely(skb_try_coalesce(rxdata->skb, skb, &headstolen, + &delta))) { + kfree_skb_partial(skb, headstolen); + } else { + wil_err(wil, "failed to merge skbs!\n"); + kfree_skb(skb); + kfree_skb(rxdata->skb); + rxdata->skb = NULL; + rxdata->skipping = !eop; + goto again; + } + } + + if (!eop) + goto again; + + /* reaching here rxdata->skb always contains a full packet */ + skb = rxdata->skb; + rxdata->skb = NULL; + rxdata->skipping = false; + + stats->last_mcs_rx = wil_rx_status_get_mcs(msg); + if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs)) + stats->rx_per_mcs[stats->last_mcs_rx]++; + + if (!use_rx_hw_reordering && !use_compressed_rx_status && + wil_check_bar(wil, msg, cid, skb, stats) == -EAGAIN) { + kfree_skb(skb); + goto again; + } + + /* Compensate for the HW data alignment according to the status + * message + */ + data_offset = wil_rx_status_get_data_offset(msg); + if (data_offset == 0xFF || + data_offset > WIL_EDMA_MAX_DATA_OFFSET) { + wil_err(wil, "Unexpected data offset %d\n", data_offset); + kfree_skb(skb); + goto again; + } + + skb_pull(skb, data_offset); + + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + /* Has to be done after dma_unmap_single as skb->cb is also + * used for holding the pa + */ + s = wil_skb_rxstatus(skb); + memcpy(s, msg, sring->elem_size); + + return skb; +} + +void wil_rx_handle_edma(struct wil6210_priv *wil, int *quota) +{ + struct net_device *ndev; + struct wil_ring *ring = &wil->ring_rx; + struct wil_status_ring *sring; + struct sk_buff *skb; + int i; + + if (unlikely(!ring->va)) { + wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); + return; + } + wil_dbg_txrx(wil, "rx_handle\n"); + + for (i = 0; i < wil->num_rx_status_rings; i++) { + sring = &wil->srings[i]; + if (unlikely(!sring->va)) { + wil_err(wil, + "Rx IRQ while Rx status ring %d not yet initialized\n", + i); + continue; + } + + while ((*quota > 0) && + (NULL != (skb = + wil_sring_reap_rx_edma(wil, sring)))) { + (*quota)--; + if (use_rx_hw_reordering) { + void *msg = wil_skb_rxstatus(skb); + int mid = wil_rx_status_get_mid(msg); + struct wil6210_vif *vif = wil->vifs[mid]; + + if (unlikely(!vif)) { + wil_dbg_txrx(wil, + "RX desc invalid mid %d", + mid); + kfree_skb(skb); + continue; + } + ndev = vif_to_ndev(vif); + wil_netif_rx_any(skb, ndev); + } else { + wil_rx_reorder(wil, skb); + } + } + + wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); + } + + wil_rx_refill_edma(wil); +} + +static int wil_tx_desc_map_edma(union wil_tx_desc *desc, + dma_addr_t pa, + u32 len, + int ring_index) +{ + struct wil_tx_enhanced_desc *d = + (struct wil_tx_enhanced_desc *)&desc->enhanced; + + memset(d, 0, sizeof(struct wil_tx_enhanced_desc)); + + wil_desc_set_addr_edma(&d->dma.addr, &d->dma.addr_high_high, pa); + + /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ + d->dma.length = cpu_to_le16((u16)len); + d->mac.d[0] = (ring_index << WIL_EDMA_DESC_TX_MAC_CFG_0_QID_POS); + /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ + d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | + (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); + + return 0; +} + +static inline void +wil_get_next_tx_status_msg(struct wil_status_ring *sring, + struct wil_ring_tx_status *msg) +{ + struct wil_ring_tx_status *_msg = (struct wil_ring_tx_status *) + (sring->va + (sring->elem_size * sring->swhead)); + + *msg = *_msg; +} + +/** + * Clean up transmitted skb's from the Tx descriptor RING. + * Return number of descriptors cleared. + */ +int wil_tx_sring_handler(struct wil6210_priv *wil, + struct wil_status_ring *sring) +{ + struct net_device *ndev; + struct device *dev = wil_to_dev(wil); + struct wil_ring *ring = NULL; + struct wil_ring_tx_data *txdata; + /* Total number of completed descriptors in all descriptor rings */ + int desc_cnt = 0; + int cid; + struct wil_net_stats *stats = NULL; + struct wil_tx_enhanced_desc *_d; + unsigned int ring_id; + unsigned int num_descs; + int i; + u8 dr_bit; /* Descriptor Ready bit */ + struct wil_ring_tx_status msg; + struct wil6210_vif *vif; + int used_before_complete; + int used_new; + + wil_get_next_tx_status_msg(sring, &msg); + dr_bit = msg.desc_ready >> TX_STATUS_DESC_READY_POS; + + /* Process completion messages while DR bit has the expected polarity */ + while (dr_bit == sring->desc_rdy_pol) { + num_descs = msg.num_descriptors; + if (!num_descs) { + wil_err(wil, "invalid num_descs 0\n"); + goto again; + } + + /* Find the corresponding descriptor ring */ + ring_id = msg.ring_id; + + if (unlikely(ring_id >= WIL6210_MAX_TX_RINGS)) { + wil_err(wil, "invalid ring id %d\n", ring_id); + goto again; + } + ring = &wil->ring_tx[ring_id]; + if (unlikely(!ring->va)) { + wil_err(wil, "Tx irq[%d]: ring not initialized\n", + ring_id); + goto again; + } + txdata = &wil->ring_tx_data[ring_id]; + if (unlikely(!txdata->enabled)) { + wil_info(wil, "Tx irq[%d]: ring disabled\n", ring_id); + goto again; + } + vif = wil->vifs[txdata->mid]; + if (unlikely(!vif)) { + wil_dbg_txrx(wil, "invalid MID %d for ring %d\n", + txdata->mid, ring_id); + goto again; + } + + ndev = vif_to_ndev(vif); + + cid = wil->ring2cid_tid[ring_id][0]; + if (cid < WIL6210_MAX_CID) + stats = &wil->sta[cid].stats; + + wil_dbg_txrx(wil, + "tx_status: completed desc_ring (%d), num_descs (%d)\n", + ring_id, num_descs); + + used_before_complete = wil_ring_used_tx(ring); + + for (i = 0 ; i < num_descs; ++i) { + struct wil_ctx *ctx = &ring->ctx[ring->swtail]; + struct wil_tx_enhanced_desc dd, *d = ⅆ + u16 dmalen; + struct sk_buff *skb = ctx->skb; + + _d = (struct wil_tx_enhanced_desc *) + &ring->va[ring->swtail].tx.enhanced; + *d = *_d; + + dmalen = le16_to_cpu(d->dma.length); + trace_wil6210_tx_status(&msg, ring->swtail, dmalen); + wil_dbg_txrx(wil, + "TxC[%2d][%3d] : %d bytes, status 0x%02x\n", + ring_id, ring->swtail, dmalen, + msg.status); + wil_hex_dump_txrx("TxS ", DUMP_PREFIX_NONE, 32, 4, + (const void *)&msg, sizeof(msg), + false); + + wil_tx_desc_unmap_edma(dev, + (union wil_tx_desc *)d, + ctx); + + if (skb) { + if (likely(msg.status == 0)) { + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + if (stats) { + stats->tx_packets++; + stats->tx_bytes += skb->len; + } + } else { + ndev->stats.tx_errors++; + if (stats) + stats->tx_errors++; + } + wil_consume_skb(skb, msg.status == 0); + } + memset(ctx, 0, sizeof(*ctx)); + /* Make sure the ctx is zeroed before updating the tail + * to prevent a case where wil_tx_ring will see + * this descriptor as used and handle it before ctx zero + * is completed. + */ + wmb(); + + ring->swtail = wil_ring_next_tail(ring); + + desc_cnt++; + } + + /* performance monitoring */ + used_new = wil_ring_used_tx(ring); + if (wil_val_in_range(wil->ring_idle_trsh, + used_new, used_before_complete)) { + wil_dbg_txrx(wil, "Ring[%2d] idle %d -> %d\n", + ring_id, used_before_complete, used_new); + txdata->last_idle = get_cycles(); + } + +again: + wil_sring_advance_swhead(sring); + + wil_get_next_tx_status_msg(sring, &msg); + dr_bit = msg.desc_ready >> TX_STATUS_DESC_READY_POS; + } + + /* shall we wake net queues? */ + if (desc_cnt) + wil_update_net_queues(wil, vif, NULL, false); + + /* Update the HW tail ptr (RD ptr) */ + wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); + + return desc_cnt; +} + +/** + * Sets the descriptor @d up for csum and/or TSO offloading. The corresponding + * @skb is used to obtain the protocol and headers length. + * @tso_desc_type is a descriptor type for TSO: 0 - a header, 1 - first data, + * 2 - middle, 3 - last descriptor. + */ +static void wil_tx_desc_offload_setup_tso_edma(struct wil_tx_enhanced_desc *d, + int tso_desc_type, bool is_ipv4, + int tcp_hdr_len, + int skb_net_hdr_len, + int mss) +{ + /* Number of descriptors */ + d->mac.d[2] |= 1; + /* Maximum Segment Size */ + d->mac.tso_mss |= cpu_to_le16(mss >> 2); + /* L4 header len: TCP header length */ + d->dma.l4_hdr_len |= tcp_hdr_len & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK; + /* EOP, TSO desc type, Segmentation enable, + * Insert IPv4 and TCP / UDP Checksum + */ + d->dma.cmd |= BIT(WIL_EDMA_DESC_TX_CFG_EOP_POS) | + tso_desc_type << WIL_EDMA_DESC_TX_CFG_TSO_DESC_TYPE_POS | + BIT(WIL_EDMA_DESC_TX_CFG_SEG_EN_POS) | + BIT(WIL_EDMA_DESC_TX_CFG_INSERT_IP_CHKSUM_POS) | + BIT(WIL_EDMA_DESC_TX_CFG_INSERT_TCP_CHKSUM_POS); + /* Calculate pseudo-header */ + d->dma.w1 |= BIT(WIL_EDMA_DESC_TX_CFG_PSEUDO_HEADER_CALC_EN_POS) | + BIT(WIL_EDMA_DESC_TX_CFG_L4_TYPE_POS); + /* IP Header Length */ + d->dma.ip_length |= skb_net_hdr_len; + /* MAC header length and IP address family*/ + d->dma.b11 |= ETH_HLEN | + is_ipv4 << DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS; +} + +static int wil_tx_tso_gen_desc(struct wil6210_priv *wil, void *buff_addr, + int len, uint i, int tso_desc_type, + skb_frag_t *frag, struct wil_ring *ring, + struct sk_buff *skb, bool is_ipv4, + int tcp_hdr_len, int skb_net_hdr_len, + int mss, int *descs_used) +{ + struct device *dev = wil_to_dev(wil); + struct wil_tx_enhanced_desc *_desc = (struct wil_tx_enhanced_desc *) + &ring->va[i].tx.enhanced; + struct wil_tx_enhanced_desc desc_mem, *d = &desc_mem; + int ring_index = ring - wil->ring_tx; + dma_addr_t pa; + + if (len == 0) + return 0; + + if (!frag) { + pa = dma_map_single(dev, buff_addr, len, DMA_TO_DEVICE); + ring->ctx[i].mapped_as = wil_mapped_as_single; + } else { + pa = skb_frag_dma_map(dev, frag, 0, len, DMA_TO_DEVICE); + ring->ctx[i].mapped_as = wil_mapped_as_page; + } + if (unlikely(dma_mapping_error(dev, pa))) { + wil_err(wil, "TSO: Skb DMA map error\n"); + return -EINVAL; + } + + wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, pa, + len, ring_index); + wil_tx_desc_offload_setup_tso_edma(d, tso_desc_type, is_ipv4, + tcp_hdr_len, + skb_net_hdr_len, mss); + + /* hold reference to skb + * to prevent skb release before accounting + * in case of immediate "tx done" + */ + if (tso_desc_type == wil_tso_type_lst) + ring->ctx[i].skb = skb_get(skb); + + wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + *_desc = *d; + (*descs_used)++; + + return 0; +} + +static int __wil_tx_ring_tso_edma(struct wil6210_priv *wil, + struct wil6210_vif *vif, + struct wil_ring *ring, + struct sk_buff *skb) +{ + int ring_index = ring - wil->ring_tx; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index]; + int nr_frags = skb_shinfo(skb)->nr_frags; + int min_desc_required = nr_frags + 2; /* Headers, Head, Fragments */ + int used, avail = wil_ring_avail_tx(ring); + int f, hdrlen, headlen; + int gso_type; + bool is_ipv4; + u32 swhead = ring->swhead; + int descs_used = 0; /* total number of used descriptors */ + int rc = -EINVAL; + int tcp_hdr_len; + int skb_net_hdr_len; + int mss = skb_shinfo(skb)->gso_size; + + wil_dbg_txrx(wil, "tx_ring_tso: %d bytes to ring %d\n", skb->len, + ring_index); + + if (unlikely(!txdata->enabled)) + return -EINVAL; + + if (unlikely(avail < min_desc_required)) { + wil_err_ratelimited(wil, + "TSO: Tx ring[%2d] full. No space for %d fragments\n", + ring_index, min_desc_required); + return -ENOMEM; + } + + gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV6 | SKB_GSO_TCPV4); + switch (gso_type) { + case SKB_GSO_TCPV4: + is_ipv4 = true; + break; + case SKB_GSO_TCPV6: + is_ipv4 = false; + break; + default: + return -EINVAL; + } + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return -EINVAL; + + /* tcp header length and skb network header length are fixed for all + * packet's descriptors - read them once here + */ + tcp_hdr_len = tcp_hdrlen(skb); + skb_net_hdr_len = skb_network_header_len(skb); + + /* First descriptor must contain the header only + * Header Length = MAC header len + IP header len + TCP header len + */ + hdrlen = ETH_HLEN + tcp_hdr_len + skb_net_hdr_len; + wil_dbg_txrx(wil, "TSO: process header descriptor, hdrlen %u\n", + hdrlen); + rc = wil_tx_tso_gen_desc(wil, skb->data, hdrlen, swhead, + wil_tso_type_hdr, NULL, ring, skb, + is_ipv4, tcp_hdr_len, skb_net_hdr_len, + mss, &descs_used); + if (rc) + return -EINVAL; + + /* Second descriptor contains the head */ + headlen = skb_headlen(skb) - hdrlen; + wil_dbg_txrx(wil, "TSO: process skb head, headlen %u\n", headlen); + rc = wil_tx_tso_gen_desc(wil, skb->data + hdrlen, headlen, + (swhead + descs_used) % ring->size, + (nr_frags != 0) ? wil_tso_type_first : + wil_tso_type_lst, NULL, ring, skb, + is_ipv4, tcp_hdr_len, skb_net_hdr_len, + mss, &descs_used); + if (rc) + goto mem_error; + + /* Rest of the descriptors are from the SKB fragments */ + for (f = 0; f < nr_frags; f++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + int len = frag->size; + + wil_dbg_txrx(wil, "TSO: frag[%d]: len %u, descs_used %d\n", f, + len, descs_used); + + rc = wil_tx_tso_gen_desc(wil, NULL, len, + (swhead + descs_used) % ring->size, + (f != nr_frags - 1) ? + wil_tso_type_mid : wil_tso_type_lst, + frag, ring, skb, is_ipv4, + tcp_hdr_len, skb_net_hdr_len, + mss, &descs_used); + if (rc) + goto mem_error; + } + + /* performance monitoring */ + used = wil_ring_used_tx(ring); + if (wil_val_in_range(wil->ring_idle_trsh, + used, used + descs_used)) { + txdata->idle += get_cycles() - txdata->last_idle; + wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", + ring_index, used, used + descs_used); + } + + /* advance swhead */ + wil_ring_advance_head(ring, descs_used); + wil_dbg_txrx(wil, "TSO: Tx swhead %d -> %d\n", swhead, ring->swhead); + + /* make sure all writes to descriptors (shared memory) are done before + * committing them to HW + */ + wmb(); + + wil_w(wil, ring->hwtail, ring->swhead); + + return 0; + +mem_error: + while (descs_used > 0) { + struct device *dev = wil_to_dev(wil); + struct wil_ctx *ctx; + int i = (swhead + descs_used - 1) % ring->size; + struct wil_tx_enhanced_desc dd, *d = ⅆ + struct wil_tx_enhanced_desc *_desc = + (struct wil_tx_enhanced_desc *) + &ring->va[i].tx.enhanced; + + *d = *_desc; + ctx = &ring->ctx[i]; + wil_tx_desc_unmap_edma(dev, (union wil_tx_desc *)d, ctx); + memset(ctx, 0, sizeof(*ctx)); + descs_used--; + } + return rc; +} + +static int wil_ring_init_bcast_edma(struct wil6210_vif *vif, int ring_id, + int size) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct wil_ring *ring = &wil->ring_tx[ring_id]; + int rc; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; + + wil_dbg_misc(wil, "init bcast: ring_id=%d, sring_id=%d\n", + ring_id, wil->tx_sring_idx); + + lockdep_assert_held(&wil->mutex); + + wil_tx_data_init(txdata); + ring->size = size; + ring->is_rx = false; + rc = wil_ring_alloc_desc_ring(wil, ring); + if (rc) + goto out; + + wil->ring2cid_tid[ring_id][0] = WIL6210_MAX_CID; /* CID */ + wil->ring2cid_tid[ring_id][1] = 0; /* TID */ + if (!vif->privacy) + txdata->dot1x_open = true; + + rc = wil_wmi_bcast_desc_ring_add(vif, ring_id); + if (rc) + goto out_free; + + return 0; + + out_free: + spin_lock_bh(&txdata->lock); + txdata->enabled = 0; + txdata->dot1x_open = false; + spin_unlock_bh(&txdata->lock); + wil_ring_free_edma(wil, ring); + +out: + return rc; +} + +static void wil_tx_fini_edma(struct wil6210_priv *wil) +{ + struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx]; + + wil_dbg_misc(wil, "free TX sring\n"); + + wil_sring_free(wil, sring); +} + +static void wil_rx_data_free(struct wil_status_ring *sring) +{ + if (!sring) + return; + + kfree_skb(sring->rx_data.skb); + sring->rx_data.skb = NULL; +} + +static void wil_rx_fini_edma(struct wil6210_priv *wil) +{ + struct wil_ring *ring = &wil->ring_rx; + int i; + + wil_dbg_misc(wil, "rx_fini_edma\n"); + + wil_ring_free_edma(wil, ring); + + for (i = 0; i < wil->num_rx_status_rings; i++) { + wil_rx_data_free(&wil->srings[i]); + wil_sring_free(wil, &wil->srings[i]); + } + + wil_free_rx_buff_arr(wil); +} + +void wil_init_txrx_ops_edma(struct wil6210_priv *wil) +{ + wil->txrx_ops.configure_interrupt_moderation = + wil_configure_interrupt_moderation_edma; + /* TX ops */ + wil->txrx_ops.ring_init_tx = wil_ring_init_tx_edma; + wil->txrx_ops.ring_fini_tx = wil_ring_free_edma; + wil->txrx_ops.ring_init_bcast = wil_ring_init_bcast_edma; + wil->txrx_ops.tx_init = wil_tx_init_edma; + wil->txrx_ops.tx_fini = wil_tx_fini_edma; + wil->txrx_ops.tx_desc_map = wil_tx_desc_map_edma; + wil->txrx_ops.tx_desc_unmap = wil_tx_desc_unmap_edma; + wil->txrx_ops.tx_ring_tso = __wil_tx_ring_tso_edma; + /* RX ops */ + wil->txrx_ops.rx_init = wil_rx_init_edma; + wil->txrx_ops.wmi_addba_rx_resp = wmi_addba_rx_resp_edma; + wil->txrx_ops.get_reorder_params = wil_get_reorder_params_edma; + wil->txrx_ops.get_netif_rx_params = wil_get_netif_rx_params_edma; + wil->txrx_ops.rx_crypto_check = wil_rx_crypto_check_edma; + wil->txrx_ops.is_rx_idle = wil_is_rx_idle_edma; + wil->txrx_ops.rx_fini = wil_rx_fini_edma; +} + diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index 14e0b6ce6dc4bd06e7676cac4dfe032aad57073b..4a6398eec6076756d3b906a3ca7707f26713e733 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -19,6 +19,62 @@ #include "wil6210.h" +#define WIL_DEFAULT_RX_STATUS_RING_ID 0 +#define WIL_RX_DESC_RING_ID 0 +#define WIL_RX_STATUS_IRQ_IDX 0 +#define WIL_TX_STATUS_IRQ_IDX 1 + +#define WIL_EDMA_AGG_WATERMARK (0xffff) +#define WIL_EDMA_AGG_WATERMARK_POS (16) + +#define WIL_EDMA_IDLE_TIME_LIMIT_USEC (50) +#define WIL_EDMA_TIME_UNIT_CLK_CYCLES (330) /* fits 1 usec */ + +/* Error field */ +#define WIL_RX_EDMA_ERROR_MIC (1) +#define WIL_RX_EDMA_ERROR_KEY (2) /* Key missing */ +#define WIL_RX_EDMA_ERROR_REPLAY (3) +#define WIL_RX_EDMA_ERROR_AMSDU (4) +#define WIL_RX_EDMA_ERROR_FCS (7) + +#define WIL_RX_EDMA_ERROR_L3_ERR (BIT(0) | BIT(1)) +#define WIL_RX_EDMA_ERROR_L4_ERR (BIT(0) | BIT(1)) + +#define WIL_RX_EDMA_DLPF_LU_MISS_BIT BIT(11) +#define WIL_RX_EDMA_DLPF_LU_MISS_CID_TID_MASK 0x7 +#define WIL_RX_EDMA_DLPF_LU_HIT_CID_TID_MASK 0xf + +#define WIL_RX_EDMA_DLPF_LU_MISS_CID_POS 2 +#define WIL_RX_EDMA_DLPF_LU_HIT_CID_POS 4 + +#define WIL_RX_EDMA_DLPF_LU_MISS_TID_POS 5 + +#define WIL_RX_EDMA_MID_VALID_BIT BIT(22) + +#define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_POS 16 +#define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_LEN 6 + +#define WIL_EDMA_DESC_TX_CFG_EOP_POS 0 +#define WIL_EDMA_DESC_TX_CFG_EOP_LEN 1 + +#define WIL_EDMA_DESC_TX_CFG_TSO_DESC_TYPE_POS 3 +#define WIL_EDMA_DESC_TX_CFG_TSO_DESC_TYPE_LEN 2 + +#define WIL_EDMA_DESC_TX_CFG_SEG_EN_POS 5 +#define WIL_EDMA_DESC_TX_CFG_SEG_EN_LEN 1 + +#define WIL_EDMA_DESC_TX_CFG_INSERT_IP_CHKSUM_POS 6 +#define WIL_EDMA_DESC_TX_CFG_INSERT_IP_CHKSUM_LEN 1 + +#define WIL_EDMA_DESC_TX_CFG_INSERT_TCP_CHKSUM_POS 7 +#define WIL_EDMA_DESC_TX_CFG_INSERT_TCP_CHKSUM_LEN 1 + +#define WIL_EDMA_DESC_TX_CFG_L4_TYPE_POS 15 +#define WIL_EDMA_DESC_TX_CFG_L4_TYPE_LEN 1 + +#define WIL_EDMA_DESC_TX_CFG_PSEUDO_HEADER_CALC_EN_POS 5 +#define WIL_EDMA_DESC_TX_CFG_PSEUDO_HEADER_CALC_EN_LEN 1 + /* Enhanced Rx descriptor - MAC part * [dword 0] : Reserved * [dword 1] : Reserved @@ -216,7 +272,7 @@ struct wil_ring_tx_status { * bit 22..23 : CB mode:2 - The CB Mode: 0-DMG, 1-EDMG, 2-Wide * bit 24..27 : Data Offset:4 - The data offset, a code that describe the * payload shift from the beginning of the buffer: - * 0 - 0 Bytes, 1 - 2 Bytes, 2 - 6 Bytes + * 0 - 0 Bytes, 3 - 2 Bytes * bit 28 : A-MSDU Present:1 - The QoS (b7) A-MSDU present field * bit 29 : A-MSDU Type:1 The QoS (b8) A-MSDU Type field * bit 30 : A-MPDU:1 - Packet is part of aggregated MPDU @@ -284,7 +340,214 @@ struct wil_rx_status_extension { struct wil_rx_status_extended { struct wil_rx_status_compressed comp; struct wil_rx_status_extension ext; -}; +} __packed; + +static inline void *wil_skb_rxstatus(struct sk_buff *skb) +{ + return (void *)skb->cb; +} + +static inline __le16 wil_rx_status_get_length(void *msg) +{ + return ((struct wil_rx_status_compressed *)msg)->length; +} + +static inline u8 wil_rx_status_get_mcs(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d1, + 16, 21); +} + +static inline u16 wil_rx_status_get_flow_id(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 8, 19); +} + +static inline u8 wil_rx_status_get_mcast(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 26, 26); +} + +/** + * In case of DLPF miss the parsing of flow Id should be as follows: + * dest_id:2 + * src_id :3 - cid + * tid:3 + * Otherwise: + * tid:4 + * cid:4 + */ + +static inline u8 wil_rx_status_get_cid(void *msg) +{ + u16 val = wil_rx_status_get_flow_id(msg); + + if (val & WIL_RX_EDMA_DLPF_LU_MISS_BIT) + /* CID is in bits 2..4 */ + return (val >> WIL_RX_EDMA_DLPF_LU_MISS_CID_POS) & + WIL_RX_EDMA_DLPF_LU_MISS_CID_TID_MASK; + else + /* CID is in bits 4..7 */ + return (val >> WIL_RX_EDMA_DLPF_LU_HIT_CID_POS) & + WIL_RX_EDMA_DLPF_LU_HIT_CID_TID_MASK; +} + +static inline u8 wil_rx_status_get_tid(void *msg) +{ + u16 val = wil_rx_status_get_flow_id(msg); + + if (val & WIL_RX_EDMA_DLPF_LU_MISS_BIT) + /* TID is in bits 5..7 */ + return (val >> WIL_RX_EDMA_DLPF_LU_MISS_TID_POS) & + WIL_RX_EDMA_DLPF_LU_MISS_CID_TID_MASK; + else + /* TID is in bits 0..3 */ + return val & WIL_RX_EDMA_DLPF_LU_MISS_CID_TID_MASK; +} + +static inline int wil_rx_status_get_desc_rdy_bit(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 31, 31); +} + +static inline int wil_rx_status_get_eop(void *msg) /* EoP = End of Packet */ +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 30, 30); +} + +static inline __le16 wil_rx_status_get_buff_id(void *msg) +{ + return ((struct wil_rx_status_compressed *)msg)->buff_id; +} + +static inline u8 wil_rx_status_get_data_offset(void *msg) +{ + u8 val = WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d1, + 24, 27); + + switch (val) { + case 0: return 0; + case 3: return 2; + default: return 0xFF; + } +} + +static inline int wil_rx_status_get_frame_type(void *msg) +{ + if (use_compressed_rx_status) + return IEEE80211_FTYPE_DATA; + + return WIL_GET_BITS(((struct wil_rx_status_extended *)msg)->ext.d1, + 0, 1) << 2; +} + +static inline int wil_rx_status_get_fc1(void *msg) +{ + if (use_compressed_rx_status) + return 0; + + return WIL_GET_BITS(((struct wil_rx_status_extended *)msg)->ext.d1, + 0, 5) << 2; +} + +static inline __le16 wil_rx_status_get_seq(void *msg) +{ + if (use_compressed_rx_status) + return 0; + + return ((struct wil_rx_status_extended *)msg)->ext.seq_num; +} + +static inline int wil_rx_status_get_mid(void *msg) +{ + if (!(((struct wil_rx_status_compressed *)msg)->d0 & + WIL_RX_EDMA_MID_VALID_BIT)) + return 0; /* use the default MID */ + + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 20, 21); +} + +static inline int wil_rx_status_get_error(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 29, 29); +} + +static inline int wil_rx_status_get_l2_rx_status(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 0, 2); +} + +static inline int wil_rx_status_get_l3_rx_status(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 3, 4); +} + +static inline int wil_rx_status_get_l4_rx_status(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 5, 6); +} + +static inline int wil_rx_status_get_security(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, + 28, 28); +} + +static inline u8 wil_rx_status_get_key_id(void *msg) +{ + return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d1, + 31, 31); +} + +static inline u8 wil_tx_status_get_mcs(struct wil_ring_tx_status *msg) +{ + return WIL_GET_BITS(msg->d2, 0, 4); +} + +static inline u32 wil_ring_next_head(struct wil_ring *ring) +{ + return (ring->swhead + 1) % ring->size; +} + +static inline void wil_desc_set_addr_edma(struct wil_ring_dma_addr *addr, + __le16 *addr_high_high, + dma_addr_t pa) +{ + addr->addr_low = cpu_to_le32(lower_32_bits(pa)); + addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa)); + *addr_high_high = cpu_to_le16((u16)(upper_32_bits(pa) >> 16)); +} + +static inline +dma_addr_t wil_tx_desc_get_addr_edma(struct wil_ring_tx_enhanced_dma *dma) +{ + return le32_to_cpu(dma->addr.addr_low) | + ((u64)le16_to_cpu(dma->addr.addr_high) << 32) | + ((u64)le16_to_cpu(dma->addr_high_high) << 48); +} + +static inline +dma_addr_t wil_rx_desc_get_addr_edma(struct wil_ring_rx_enhanced_dma *dma) +{ + return le32_to_cpu(dma->addr.addr_low) | + ((u64)le16_to_cpu(dma->addr.addr_high) << 32) | + ((u64)le16_to_cpu(dma->addr_high_high) << 48); +} + +void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil); +int wil_tx_sring_handler(struct wil6210_priv *wil, + struct wil_status_ring *sring); +void wil_rx_handle_edma(struct wil6210_priv *wil, int *quota); +void wil_init_txrx_ops_edma(struct wil6210_priv *wil); #endif /* WIL6210_TXRX_EDMA_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 0638d7eacdf9102d9655929cdcc7f6d51d80bf04..b9014d20b933f4c16c97a4b8b37301d152b0b58f 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "wmi.h" #include "wil_platform.h" #include "ftm.h" @@ -37,6 +38,12 @@ extern bool rx_align_2; extern bool rx_large_buf; extern bool debug_fw; extern bool disable_ap_sme; +extern bool use_rx_hw_reordering; +extern bool use_compressed_rx_status; + +struct wil6210_priv; +struct wil6210_vif; +union wil_tx_desc; #define WIL_NAME "wil6210" @@ -46,6 +53,9 @@ extern bool disable_ap_sme; #define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" #define WIL_FW_NAME_FTM_SPARROW_PLUS "wil6210_sparrow_plus_ftm.fw" +#define WIL_FW_NAME_TALYN "wil6436.fw" +#define WIL_FW_NAME_FTM_TALYN "wil6436_ftm.fw" + #define WIL_BOARD_FILE_NAME "wil6210.brd" /* board & radio parameters */ #define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */ @@ -206,7 +216,9 @@ struct RGF_ICR { #define RGF_USER_SPARROW_M_4 (0x880c50) /* Sparrow */ #define BIT_SPARROW_M_4_SEL_SLEEP_OR_REF BIT(2) #define RGF_USER_OTP_HW_RD_MACHINE_1 (0x880ce0) - #define BIT_NO_FLASH_INDICATION BIT(8) + #define BIT_OTP_SIGNATURE_ERR_TALYN_MB BIT(0) + #define BIT_OTP_HW_SECTION_DONE_TALYN_MB BIT(2) + #define BIT_NO_FLASH_INDICATION BIT(8) #define RGF_USER_XPM_IFC_RD_TIME1 (0x880cec) #define RGF_USER_XPM_IFC_RD_TIME2 (0x880cf0) #define RGF_USER_XPM_IFC_RD_TIME3 (0x880cf4) @@ -308,6 +320,29 @@ struct RGF_ICR { #define RGF_CAF_PLL_LOCK_STATUS (0x88afec) #define BIT_CAF_OSC_DIG_XTAL_STABLE BIT(0) +#define RGF_OTP_QC_SECURED (0x8a0038) + #define BIT_BOOT_FROM_ROM BIT(31) + +/* eDMA */ +#define RGF_INT_COUNT_ON_SPECIAL_EVT (0x8b62d8) + +#define RGF_INT_CTRL_INT_GEN_CFG_0 (0x8bc000) +#define RGF_INT_CTRL_INT_GEN_CFG_1 (0x8bc004) +#define RGF_INT_GEN_TIME_UNIT_LIMIT (0x8bc0c8) + +#define RGF_INT_GEN_CTRL (0x8bc0ec) + #define BIT_CONTROL_0 BIT(0) + +/* eDMA status interrupts */ +#define RGF_INT_GEN_RX_ICR (0x8bc0f4) + #define BIT_RX_STATUS_IRQ BIT(WIL_RX_STATUS_IRQ_IDX) +#define RGF_INT_GEN_TX_ICR (0x8bc110) + #define BIT_TX_STATUS_IRQ BIT(WIL_TX_STATUS_IRQ_IDX) +#define RGF_INT_CTRL_RX_INT_MASK (0x8bc12c) +#define RGF_INT_CTRL_TX_INT_MASK (0x8bc130) + +#define RGF_INT_GEN_IDLE_TIME_LIMIT (0x8bc134) + #define USER_EXT_USER_PMU_3 (0x88d00c) #define BIT_PMU_DEVICE_RDY BIT(0) @@ -486,6 +521,17 @@ struct wil_ring { bool is_rx; }; +/** + * Additional data for Rx ring. + * Used for enhanced DMA RX chaining. + */ +struct wil_ring_rx_data { + /* the skb being assembled */ + struct sk_buff *skb; + /* true if we are skipping a bad fragmented packet */ + bool skipping; +}; + /** * Status ring structure, used for enhanced DMA completions for RX and TX. */ @@ -498,6 +544,43 @@ struct wil_status_ring { u32 hwtail; /* write here to inform hw */ bool is_rx; u8 desc_rdy_pol; /* Expected descriptor ready bit polarity */ + struct wil_ring_rx_data rx_data; +}; + +/** + * struct tx_rx_ops - different TX/RX ops for legacy and enhanced + * DMA flow + */ +struct wil_txrx_ops { + void (*configure_interrupt_moderation)(struct wil6210_priv *wil); + /* TX ops */ + int (*ring_init_tx)(struct wil6210_vif *vif, int ring_id, + int size, int cid, int tid); + void (*ring_fini_tx)(struct wil6210_priv *wil, struct wil_ring *ring); + int (*ring_init_bcast)(struct wil6210_vif *vif, int id, int size); + int (*tx_init)(struct wil6210_priv *wil); + void (*tx_fini)(struct wil6210_priv *wil); + int (*tx_desc_map)(union wil_tx_desc *desc, dma_addr_t pa, + u32 len, int ring_index); + void (*tx_desc_unmap)(struct device *dev, + union wil_tx_desc *desc, + struct wil_ctx *ctx); + int (*tx_ring_tso)(struct wil6210_priv *wil, struct wil6210_vif *vif, + struct wil_ring *ring, struct sk_buff *skb); + irqreturn_t (*irq_tx)(int irq, void *cookie); + /* RX ops */ + int (*rx_init)(struct wil6210_priv *wil, u16 ring_size); + void (*rx_fini)(struct wil6210_priv *wil); + int (*wmi_addba_rx_resp)(struct wil6210_priv *wil, u8 mid, u8 cid, + u8 tid, u8 token, u16 status, bool amsdu, + u16 agg_wsize, u16 timeout); + void (*get_reorder_params)(struct sk_buff *skb, int *tid, int *cid, + int *mid, u16 *seq, int *mcast); + void (*get_netif_rx_params)(struct sk_buff *skb, + int *cid, int *security); + int (*rx_crypto_check)(struct wil6210_priv *wil, struct sk_buff *skb); + bool (*is_rx_idle)(struct wil6210_priv *wil); + irqreturn_t (*irq_rx)(int irq, void *cookie); }; /** @@ -515,18 +598,6 @@ struct wil_ring_tx_data { spinlock_t lock; }; -/** - * Additional data for Rx ring. - * Used for enhanced DMA RX chaining. - */ -struct wil_ring_rx_data { - /* the skb being assembled */ - struct sk_buff *skb; - /* true if we are skipping a bad fragmented packet */ - bool skipping; - u16 buff_size; -}; - enum { /* for wil6210_priv.status */ wil_status_fwready = 0, /* FW operational */ wil_status_dontscan, @@ -857,17 +928,19 @@ struct wil6210_priv { /* DMA related */ struct wil_ring ring_rx; - struct wil_ring_rx_data ring_rx_data; unsigned int rx_buf_len; struct wil_ring ring_tx[WIL6210_MAX_TX_RINGS]; struct wil_ring_tx_data ring_tx_data[WIL6210_MAX_TX_RINGS]; struct wil_status_ring srings[WIL6210_MAX_STATUS_RINGS]; - int num_rx_status_rings; + u8 num_rx_status_rings; + int tx_sring_idx; u8 ring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; u32 ring_idle_trsh; /* HW fetches up to 16 descriptors at once */ u32 dma_addr_size; /* indicates dma addr size */ struct wil_rx_buff_mgmt rx_buff_mgmt; + bool use_enhanced_dma_hw; + struct wil_txrx_ops txrx_ops; struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ /* statistics */ @@ -921,6 +994,9 @@ struct wil6210_priv { u32 rgf_fw_assert_code_addr; u32 rgf_ucode_assert_code_addr; u32 iccm_base; + + bool secured_boot; + u8 boot_config; }; #define wil_to_wiphy(i) (i->wiphy) @@ -1199,14 +1275,10 @@ void wil_probe_client_flush(struct wil6210_vif *vif); void wil_probe_client_worker(struct work_struct *work); void wil_disconnect_worker(struct work_struct *work); -int wil_rx_init(struct wil6210_priv *wil, u16 size); -void wil_rx_fini(struct wil6210_priv *wil); +void wil_init_txrx_ops(struct wil6210_priv *wil); /* TX API */ -int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, - int cid, int tid); -void wil_ring_fini_tx(struct wil6210_priv *wil, int id); -int wil_tx_init(struct wil6210_vif *vif, int cid); +int wil_ring_init_tx(struct wil6210_vif *vif, int cid); int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size); int wil_bcast_init(struct wil6210_vif *vif); void wil_bcast_fini(struct wil6210_vif *vif); @@ -1219,10 +1291,12 @@ void wil_update_net_queues_bh(struct wil6210_priv *wil, struct wil6210_vif *vif, netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); int wil_tx_complete(struct wil6210_vif *vif, int ringid); void wil6210_unmask_irq_tx(struct wil6210_priv *wil); +void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil); /* RX API */ void wil_rx_handle(struct wil6210_priv *wil, int *quota); void wil6210_unmask_irq_rx(struct wil6210_priv *wil); +void wil6210_unmask_irq_rx_edma(struct wil6210_priv *wil); int wil_iftype_nl2wmi(enum nl80211_iftype type); @@ -1244,7 +1318,6 @@ bool wil_is_wmi_idle(struct wil6210_priv *wil); int wmi_resume(struct wil6210_priv *wil); int wmi_suspend(struct wil6210_priv *wil); bool wil_is_tx_idle(struct wil6210_priv *wil); -bool wil_is_rx_idle(struct wil6210_priv *wil); int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size); void wil_fw_core_dump(struct wil6210_priv *wil); @@ -1278,4 +1351,19 @@ int wmi_start_sched_scan(struct wil6210_priv *wil, struct cfg80211_sched_scan_request *request); int wmi_stop_sched_scan(struct wil6210_priv *wil); +int reverse_memcmp(const void *cs, const void *ct, size_t count); + +/* WMI for enhanced DMA */ +int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id); +int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, + u16 max_rx_pl_per_desc); +int wil_wmi_rx_sring_add(struct wil6210_priv *wil, u16 ring_id); +int wil_wmi_rx_desc_ring_add(struct wil6210_priv *wil, int status_ring_id); +int wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, + int tid); +int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id); +int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, + u8 tid, u8 token, u16 status, bool amsdu, + u16 agg_wsize, u16 timeout); + #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h index 177026e5323be420d176aa3aa97895fdf9242b26..bca090611477dcd49ccb5b1144dafa5d0247eeed 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.h +++ b/drivers/net/wireless/ath/wil6210/wil_platform.h @@ -29,6 +29,7 @@ enum wil_platform_event { enum wil_platform_features { WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL = 0, + WIL_PLATFORM_FEATURE_TRIPLE_MSI = 1, WIL_PLATFORM_FEATURE_MAX, }; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 3b2eef059a44d92b5502a295b2fc58bde44d996b..6d87369589db7bb3cc73eff66c3b4ecc5bc4d606 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -39,6 +39,10 @@ module_param(led_id, byte, 0444); MODULE_PARM_DESC(led_id, " 60G device led enablement. Set the led ID (0-2) to enable"); +static bool amsdu_en; +module_param(amsdu_en, bool, 0444); +MODULE_PARM_DESC(amsdu_en, " enable A-MSDU, default: false"); + #define WIL_WAIT_FOR_SUSPEND_RESUME_COMP 200 #define WIL_WMI_CALL_GENERAL_TO_MS 100 @@ -421,14 +425,16 @@ static const char *cmdid2name(u16 cmdid) return "WMI_DEL_STA_CMD"; case WMI_DISCONNECT_STA_CMDID: return "WMI_DISCONNECT_STA_CMD"; - case WMI_VRING_BA_EN_CMDID: - return "WMI_VRING_BA_EN_CMD"; - case WMI_VRING_BA_DIS_CMDID: - return "WMI_VRING_BA_DIS_CMD"; + case WMI_RING_BA_EN_CMDID: + return "WMI_RING_BA_EN_CMD"; + case WMI_RING_BA_DIS_CMDID: + return "WMI_RING_BA_DIS_CMD"; case WMI_RCP_DELBA_CMDID: return "WMI_RCP_DELBA_CMD"; case WMI_RCP_ADDBA_RESP_CMDID: return "WMI_RCP_ADDBA_RESP_CMD"; + case WMI_RCP_ADDBA_RESP_EDMA_CMDID: + return "WMI_RCP_ADDBA_RESP_EDMA_CMD"; case WMI_PS_DEV_PROFILE_CFG_CMDID: return "WMI_PS_DEV_PROFILE_CFG_CMD"; case WMI_SET_MGMT_RETRY_LIMIT_CMDID: @@ -451,6 +457,18 @@ static const char *cmdid2name(u16 cmdid) return "WMI_START_SCHED_SCAN_CMD"; case WMI_STOP_SCHED_SCAN_CMDID: return "WMI_STOP_SCHED_SCAN_CMD"; + case WMI_TX_STATUS_RING_ADD_CMDID: + return "WMI_TX_STATUS_RING_ADD_CMD"; + case WMI_RX_STATUS_RING_ADD_CMDID: + return "WMI_RX_STATUS_RING_ADD_CMD"; + case WMI_TX_DESC_RING_ADD_CMDID: + return "WMI_TX_DESC_RING_ADD_CMD"; + case WMI_RX_DESC_RING_ADD_CMDID: + return "WMI_RX_DESC_RING_ADD_CMD"; + case WMI_BCAST_DESC_RING_ADD_CMDID: + return "WMI_BCAST_DESC_RING_ADD_CMD"; + case WMI_CFG_DEF_RX_OFFLOAD_CMDID: + return "WMI_CFG_DEF_RX_OFFLOAD_CMD"; default: return "Untracked CMD"; } @@ -505,8 +523,8 @@ static const char *eventid2name(u16 eventid) return "WMI_RCP_ADDBA_REQ_EVENT"; case WMI_DELBA_EVENTID: return "WMI_DELBA_EVENT"; - case WMI_VRING_EN_EVENTID: - return "WMI_VRING_EN_EVENT"; + case WMI_RING_EN_EVENTID: + return "WMI_RING_EN_EVENT"; case WMI_DATA_PORT_OPEN_EVENTID: return "WMI_DATA_PORT_OPEN_EVENT"; case WMI_AOA_MEAS_EVENTID: @@ -575,6 +593,16 @@ static const char *eventid2name(u16 eventid) return "WMI_STOP_SCHED_SCAN_EVENT"; case WMI_SCHED_SCAN_RESULT_EVENTID: return "WMI_SCHED_SCAN_RESULT_EVENT"; + case WMI_TX_STATUS_RING_CFG_DONE_EVENTID: + return "WMI_TX_STATUS_RING_CFG_DONE_EVENT"; + case WMI_RX_STATUS_RING_CFG_DONE_EVENTID: + return "WMI_RX_STATUS_RING_CFG_DONE_EVENT"; + case WMI_TX_DESC_RING_CFG_DONE_EVENTID: + return "WMI_TX_DESC_RING_CFG_DONE_EVENT"; + case WMI_RX_DESC_RING_CFG_DONE_EVENTID: + return "WMI_RX_DESC_RING_CFG_DONE_EVENT"; + case WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID: + return "WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENT"; default: return "Untracked EVENT"; } @@ -967,7 +995,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len) wil->sta[evt->cid].mid = vif->mid; wil->sta[evt->cid].status = wil_sta_conn_pending; - rc = wil_tx_init(vif, evt->cid); + rc = wil_ring_init_tx(vif, evt->cid); if (rc) { wil_err(wil, "config tx vring failed for CID %d, rc (%d)\n", evt->cid, rc); @@ -1117,11 +1145,11 @@ static void wmi_evt_eapol_rx(struct wil6210_vif *vif, int id, void *d, int len) } } -static void wmi_evt_vring_en(struct wil6210_vif *vif, int id, void *d, int len) +static void wmi_evt_ring_en(struct wil6210_vif *vif, int id, void *d, int len) { struct wil6210_priv *wil = vif_to_wil(vif); - struct wmi_vring_en_event *evt = d; - u8 vri = evt->vring_index; + struct wmi_ring_en_event *evt = d; + u8 vri = evt->ring_index; struct wireless_dev *wdev = vif_to_wdev(vif); wil_dbg_wmi(wil, "Enable vring %d MID %d\n", vri, vif->mid); @@ -1354,7 +1382,7 @@ static const struct { {WMI_BA_STATUS_EVENTID, wmi_evt_ba_status}, {WMI_RCP_ADDBA_REQ_EVENTID, wmi_evt_addba_rx_req}, {WMI_DELBA_EVENTID, wmi_evt_delba}, - {WMI_VRING_EN_EVENTID, wmi_evt_vring_en}, + {WMI_RING_EN_EVENTID, wmi_evt_ring_en}, {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_ignore}, {WMI_AOA_MEAS_EVENTID, wmi_evt_aoa_meas}, {WMI_TOF_SESSION_END_EVENTID, wmi_evt_ftm_session_ended}, @@ -2124,29 +2152,32 @@ int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, int wmi_addba(struct wil6210_priv *wil, u8 mid, u8 ringid, u8 size, u16 timeout) { - struct wmi_vring_ba_en_cmd cmd = { - .ringid = ringid, + u8 amsdu = wil->use_enhanced_dma_hw && use_rx_hw_reordering && + amsdu_en ? 1 : 0; + + struct wmi_ring_ba_en_cmd cmd = { + .ring_id = ringid, .agg_max_wsize = size, .ba_timeout = cpu_to_le16(timeout), - .amsdu = 0, + .amsdu = amsdu, }; - wil_dbg_wmi(wil, "addba: (ring %d size %d timeout %d)\n", ringid, size, - timeout); + wil_dbg_wmi(wil, "addba: (ring %d size %d timeout %d amsdu %d)\n", + ringid, size, timeout, amsdu); - return wmi_send(wil, WMI_VRING_BA_EN_CMDID, mid, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_RING_BA_EN_CMDID, mid, &cmd, sizeof(cmd)); } int wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason) { - struct wmi_vring_ba_dis_cmd cmd = { - .ringid = ringid, + struct wmi_ring_ba_dis_cmd cmd = { + .ring_id = ringid, .reason = cpu_to_le16(reason), }; wil_dbg_wmi(wil, "delba_tx: (ring %d reason %d)\n", ringid, reason); - return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, mid, &cmd, sizeof(cmd)); + return wmi_send(wil, WMI_RING_BA_DIS_CMDID, mid, &cmd, sizeof(cmd)); } int wmi_delba_rx(struct wil6210_priv *wil, u8 mid, u8 cidxtid, u16 reason) @@ -2205,6 +2236,54 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, return rc; } +int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, + u8 token, u16 status, bool amsdu, u16 agg_wsize, + u16 timeout) +{ + int rc; + struct wmi_rcp_addba_resp_edma_cmd cmd = { + .cid = cid, + .tid = tid, + .dialog_token = token, + .status_code = cpu_to_le16(status), + /* bit 0: A-MSDU supported + * bit 1: policy (should be 0 for us) + * bits 2..5: TID + * bits 6..15: buffer size + */ + .ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) | + (agg_wsize << 6)), + .ba_timeout = cpu_to_le16(timeout), + /* route all the connections to status ring 0 */ + .status_ring_id = WIL_DEFAULT_RX_STATUS_RING_ID, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_rcp_addba_resp_sent_event evt; + } __packed reply = { + .evt = {.status = cpu_to_le16(WMI_FW_STATUS_FAILURE)}, + }; + + wil_dbg_wmi(wil, + "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s, sring_id %d\n", + cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-", + WIL_DEFAULT_RX_STATUS_RING_ID); + + rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_EDMA_CMDID, mid, &cmd, + sizeof(cmd), WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, + sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) + return rc; + + if (reply.evt.status) { + wil_err(wil, "ADDBA response failed with status %d\n", + le16_to_cpu(reply.evt.status)); + rc = -EINVAL; + } + + return rc; +} + int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile) { @@ -3017,3 +3096,258 @@ int wmi_stop_sched_scan(struct wil6210_priv *wil) return 0; } + +int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id) +{ + int rc; + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); + struct wil_status_ring *sring = &wil->srings[ring_id]; + struct wmi_tx_status_ring_add_cmd cmd = { + .ring_cfg = { + .ring_size = cpu_to_le16(sring->size), + }, + .irq_index = WIL_TX_STATUS_IRQ_IDX + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_tx_status_ring_cfg_done_event evt; + } __packed reply; + + cmd.ring_cfg.ring_id = ring_id; + + cmd.ring_cfg.ring_mem_base = cpu_to_le64(sring->pa); + reply.evt.status = WMI_FW_STATUS_FAILURE; + rc = wmi_call(wil, WMI_TX_STATUS_RING_ADD_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_TX_STATUS_RING_CFG_DONE_EVENTID, + &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "TX_STATUS_RING_ADD_CMD failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "TX_STATUS_RING_ADD_CMD failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + sring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); + + return 0; +} + +int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, u16 max_rx_pl_per_desc) +{ + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); + int rc; + struct wmi_cfg_def_rx_offload_cmd cmd = { + .max_msdu_size = cpu_to_le16(wil_mtu2macbuf(WIL_MAX_ETH_MTU)), + .max_rx_pl_per_desc = cpu_to_le16(max_rx_pl_per_desc), + .decap_trans_type = WMI_DECAP_TYPE_802_3, + .l2_802_3_offload_ctrl = 0, + .l3_l4_ctrl = 1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS, + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_cfg_def_rx_offload_done_event evt; + } __packed reply = { + .evt = {.status = WMI_FW_STATUS_FAILURE}, + }; + + rc = wmi_call(wil, WMI_CFG_DEF_RX_OFFLOAD_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID, &reply, + sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "WMI_CFG_DEF_RX_OFFLOAD_CMD failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "WMI_CFG_DEF_RX_OFFLOAD_CMD failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + return 0; +} + +int wil_wmi_rx_sring_add(struct wil6210_priv *wil, u16 ring_id) +{ + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); + struct wil_status_ring *sring = &wil->srings[ring_id]; + int rc; + struct wmi_rx_status_ring_add_cmd cmd = { + .ring_cfg = { + .ring_size = cpu_to_le16(sring->size), + .ring_id = ring_id, + }, + .rx_msg_type = use_compressed_rx_status ? + WMI_RX_MSG_TYPE_COMPRESSED : + WMI_RX_MSG_TYPE_EXTENDED, + .irq_index = WIL_RX_STATUS_IRQ_IDX, + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_rx_status_ring_cfg_done_event evt; + } __packed reply; + + cmd.ring_cfg.ring_mem_base = cpu_to_le64(sring->pa); + reply.evt.status = WMI_FW_STATUS_FAILURE; + rc = wmi_call(wil, WMI_RX_STATUS_RING_ADD_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_RX_STATUS_RING_CFG_DONE_EVENTID, &reply, + sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "RX_STATUS_RING_ADD_CMD failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "RX_STATUS_RING_ADD_CMD failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + sring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); + + return 0; +} + +int wil_wmi_rx_desc_ring_add(struct wil6210_priv *wil, int status_ring_id) +{ + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); + struct wil_ring *ring = &wil->ring_rx; + int rc; + struct wmi_rx_desc_ring_add_cmd cmd = { + .ring_cfg = { + .ring_size = cpu_to_le16(ring->size), + .ring_id = WIL_RX_DESC_RING_ID, + }, + .status_ring_id = status_ring_id, + .irq_index = WIL_RX_STATUS_IRQ_IDX, + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_rx_desc_ring_cfg_done_event evt; + } __packed reply; + + cmd.ring_cfg.ring_mem_base = cpu_to_le64(ring->pa); + cmd.sw_tail_host_addr = cpu_to_le64(ring->edma_rx_swtail.pa); + reply.evt.status = WMI_FW_STATUS_FAILURE; + rc = wmi_call(wil, WMI_RX_DESC_RING_ADD_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_RX_DESC_RING_CFG_DONE_EVENTID, &reply, + sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "WMI_RX_DESC_RING_ADD_CMD failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "WMI_RX_DESC_RING_ADD_CMD failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + ring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); + + return 0; +} + +int wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, + int tid) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + int sring_id = wil->tx_sring_idx; /* there is only one TX sring */ + int rc; + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; + struct wmi_tx_desc_ring_add_cmd cmd = { + .ring_cfg = { + .ring_size = cpu_to_le16(ring->size), + .ring_id = ring_id, + }, + .status_ring_id = sring_id, + .cid = cid, + .tid = tid, + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + .max_msdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), + .schd_params = { + .priority = cpu_to_le16(0), + .timeslot_us = cpu_to_le16(0xfff), + } + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_tx_desc_ring_cfg_done_event evt; + } __packed reply; + + cmd.ring_cfg.ring_mem_base = cpu_to_le64(ring->pa); + reply.evt.status = WMI_FW_STATUS_FAILURE; + rc = wmi_call(wil, WMI_TX_DESC_RING_ADD_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_TX_DESC_RING_CFG_DONE_EVENTID, &reply, + sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "WMI_TX_DESC_RING_ADD_CMD failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "WMI_TX_DESC_RING_ADD_CMD failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + spin_lock_bh(&txdata->lock); + ring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); + txdata->mid = vif->mid; + txdata->enabled = 1; + spin_unlock_bh(&txdata->lock); + + return 0; +} + +int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct wil_ring *ring = &wil->ring_tx[ring_id]; + int rc; + struct wmi_bcast_desc_ring_add_cmd cmd = { + .ring_cfg = { + .ring_size = cpu_to_le16(ring->size), + .ring_id = ring_id, + }, + .status_ring_id = wil->tx_sring_idx, + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_rx_desc_ring_cfg_done_event evt; + } __packed reply; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; + + cmd.ring_cfg.ring_mem_base = cpu_to_le64(ring->pa); + reply.evt.status = WMI_FW_STATUS_FAILURE; + rc = wmi_call(wil, WMI_BCAST_DESC_RING_ADD_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_TX_DESC_RING_CFG_DONE_EVENTID, &reply, + sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "WMI_BCAST_DESC_RING_ADD_CMD failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "Broadcast Tx config failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + spin_lock_bh(&txdata->lock); + ring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); + txdata->mid = vif->mid; + txdata->enabled = 1; + spin_unlock_bh(&txdata->lock); + + return 0; +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 28568dc15d7ac030d902ce6af52afa8c6fc2801b..93f9e08b77106b198e1f270a83aacb40c13ad424 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -136,8 +136,8 @@ enum wmi_command_id { WMI_CFG_RX_CHAIN_CMDID = 0x820, WMI_VRING_CFG_CMDID = 0x821, WMI_BCAST_VRING_CFG_CMDID = 0x822, - WMI_VRING_BA_EN_CMDID = 0x823, - WMI_VRING_BA_DIS_CMDID = 0x824, + WMI_RING_BA_EN_CMDID = 0x823, + WMI_RING_BA_DIS_CMDID = 0x824, WMI_RCP_ADDBA_RESP_CMDID = 0x825, WMI_RCP_DELBA_CMDID = 0x826, WMI_SET_SSID_CMDID = 0x827, @@ -151,6 +151,7 @@ enum wmi_command_id { WMI_BF_SM_MGMT_CMDID = 0x838, WMI_BF_RXSS_MGMT_CMDID = 0x839, WMI_BF_TRIG_CMDID = 0x83A, + WMI_RCP_ADDBA_RESP_EDMA_CMDID = 0x83B, WMI_LINK_MAINTAIN_CFG_WRITE_CMDID = 0x842, WMI_LINK_MAINTAIN_CFG_READ_CMDID = 0x843, WMI_SET_SECTORS_CMDID = 0x849, @@ -219,6 +220,12 @@ enum wmi_command_id { WMI_PRIO_TX_SECTORS_ORDER_CMDID = 0x9A5, WMI_PRIO_TX_SECTORS_NUMBER_CMDID = 0x9A6, WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_CMDID = 0x9A7, + WMI_TX_STATUS_RING_ADD_CMDID = 0x9C0, + WMI_RX_STATUS_RING_ADD_CMDID = 0x9C1, + WMI_TX_DESC_RING_ADD_CMDID = 0x9C2, + WMI_RX_DESC_RING_ADD_CMDID = 0x9C3, + WMI_BCAST_DESC_RING_ADD_CMDID = 0x9C4, + WMI_CFG_DEF_RX_OFFLOAD_CMDID = 0x9C5, WMI_SCHEDULING_SCHEME_CMDID = 0xA01, WMI_FIXED_SCHEDULING_CONFIG_CMDID = 0xA02, WMI_ENABLE_FIXED_SCHEDULING_CMDID = 0xA03, @@ -716,18 +723,90 @@ struct wmi_lo_power_calib_from_otp_event { u8 reserved[3]; } __packed; -/* WMI_VRING_BA_EN_CMDID */ -struct wmi_vring_ba_en_cmd { - u8 ringid; +struct wmi_edma_ring_cfg { + __le64 ring_mem_base; + /* size in number of items */ + __le16 ring_size; + u8 ring_id; + u8 reserved; +} __packed; + +enum wmi_rx_msg_type { + WMI_RX_MSG_TYPE_COMPRESSED = 0x00, + WMI_RX_MSG_TYPE_EXTENDED = 0x01, +}; + +struct wmi_tx_status_ring_add_cmd { + struct wmi_edma_ring_cfg ring_cfg; + u8 irq_index; + u8 reserved[3]; +} __packed; + +struct wmi_rx_status_ring_add_cmd { + struct wmi_edma_ring_cfg ring_cfg; + u8 irq_index; + /* wmi_rx_msg_type */ + u8 rx_msg_type; + u8 reserved[2]; +} __packed; + +struct wmi_cfg_def_rx_offload_cmd { + __le16 max_msdu_size; + __le16 max_rx_pl_per_desc; + u8 decap_trans_type; + u8 l2_802_3_offload_ctrl; + u8 l2_nwifi_offload_ctrl; + u8 vlan_id; + u8 nwifi_ds_trans_type; + u8 l3_l4_ctrl; + u8 reserved[6]; +} __packed; + +struct wmi_tx_desc_ring_add_cmd { + struct wmi_edma_ring_cfg ring_cfg; + __le16 max_msdu_size; + /* Correlated status ring (0-63) */ + u8 status_ring_id; + u8 cid; + u8 tid; + u8 encap_trans_type; + u8 mac_ctrl; + u8 to_resolution; + u8 agg_max_wsize; + u8 reserved[3]; + struct wmi_vring_cfg_schd schd_params; +} __packed; + +struct wmi_rx_desc_ring_add_cmd { + struct wmi_edma_ring_cfg ring_cfg; + u8 irq_index; + /* 0-63 status rings */ + u8 status_ring_id; + u8 reserved[2]; + __le64 sw_tail_host_addr; +} __packed; + +struct wmi_bcast_desc_ring_add_cmd { + struct wmi_edma_ring_cfg ring_cfg; + __le16 max_msdu_size; + /* Correlated status ring (0-63) */ + u8 status_ring_id; + u8 encap_trans_type; + u8 reserved[4]; +} __packed; + +/* WMI_RING_BA_EN_CMDID */ +struct wmi_ring_ba_en_cmd { + u8 ring_id; u8 agg_max_wsize; __le16 ba_timeout; u8 amsdu; u8 reserved[3]; } __packed; -/* WMI_VRING_BA_DIS_CMDID */ -struct wmi_vring_ba_dis_cmd { - u8 ringid; +/* WMI_RING_BA_DIS_CMDID */ +struct wmi_ring_ba_dis_cmd { + u8 ring_id; u8 reserved; __le16 reason; } __packed; @@ -877,6 +956,21 @@ struct wmi_rcp_addba_resp_cmd { __le16 ba_timeout; } __packed; +/* WMI_RCP_ADDBA_RESP_EDMA_CMDID */ +struct wmi_rcp_addba_resp_edma_cmd { + u8 cid; + u8 tid; + u8 dialog_token; + u8 reserved; + __le16 status_code; + /* ieee80211_ba_parameterset field to send */ + __le16 ba_param_set; + __le16 ba_timeout; + u8 status_ring_id; + /* wmi_cfg_rx_chain_cmd_reorder_type */ + u8 reorder_type; +} __packed; + /* WMI_RCP_DELBA_CMDID */ struct wmi_rcp_delba_cmd { u8 cidxtid; @@ -1332,7 +1426,7 @@ enum wmi_event_id { WMI_BF_CTRL_DONE_EVENTID = 0x1862, WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, WMI_GET_STATUS_DONE_EVENTID = 0x1864, - WMI_VRING_EN_EVENTID = 0x1865, + WMI_RING_EN_EVENTID = 0x1865, WMI_GET_RF_STATUS_EVENTID = 0x1866, WMI_GET_BASEBAND_TYPE_EVENTID = 0x1867, WMI_UNIT_TEST_EVENTID = 0x1900, @@ -1381,6 +1475,11 @@ enum wmi_event_id { WMI_PRIO_TX_SECTORS_ORDER_EVENTID = 0x19A5, WMI_PRIO_TX_SECTORS_NUMBER_EVENTID = 0x19A6, WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_EVENTID = 0x19A7, + WMI_TX_STATUS_RING_CFG_DONE_EVENTID = 0x19C0, + WMI_RX_STATUS_RING_CFG_DONE_EVENTID = 0x19C1, + WMI_TX_DESC_RING_CFG_DONE_EVENTID = 0x19C2, + WMI_RX_DESC_RING_CFG_DONE_EVENTID = 0x19C3, + WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID = 0x19C5, WMI_SCHEDULING_SCHEME_EVENTID = 0x1A01, WMI_FIXED_SCHEDULING_CONFIG_COMPLETE_EVENTID = 0x1A02, WMI_ENABLE_FIXED_SCHEDULING_COMPLETE_EVENTID = 0x1A03, @@ -1760,6 +1859,49 @@ struct wmi_rcp_addba_resp_sent_event { __le16 status; } __packed; +/* WMI_TX_STATUS_RING_CFG_DONE_EVENTID */ +struct wmi_tx_status_ring_cfg_done_event { + u8 ring_id; + /* wmi_fw_status */ + u8 status; + u8 reserved[2]; + __le32 ring_tail_ptr; +} __packed; + +/* WMI_RX_STATUS_RING_CFG_DONE_EVENTID */ +struct wmi_rx_status_ring_cfg_done_event { + u8 ring_id; + /* wmi_fw_status */ + u8 status; + u8 reserved[2]; + __le32 ring_tail_ptr; +} __packed; + +/* WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID */ +struct wmi_cfg_def_rx_offload_done_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_TX_DESC_RING_CFG_DONE_EVENTID */ +struct wmi_tx_desc_ring_cfg_done_event { + u8 ring_id; + /* wmi_fw_status */ + u8 status; + u8 reserved[2]; + __le32 ring_tail_ptr; +} __packed; + +/* WMI_RX_DESC_RING_CFG_DONE_EVENTID */ +struct wmi_rx_desc_ring_cfg_done_event { + u8 ring_id; + /* wmi_fw_status */ + u8 status; + u8 reserved[2]; + __le32 ring_tail_ptr; +} __packed; + /* WMI_RCP_ADDBA_REQ_EVENTID */ struct wmi_rcp_addba_req_event { u8 cidxtid; @@ -1802,9 +1944,9 @@ struct wmi_data_port_open_event { u8 reserved[3]; } __packed; -/* WMI_VRING_EN_EVENTID */ -struct wmi_vring_en_event { - u8 vring_index; +/* WMI_RING_EN_EVENTID */ +struct wmi_ring_en_event { + u8 ring_index; u8 reserved[3]; } __packed; diff --git a/drivers/net/wireless/cnss_utils/cnss_utils.c b/drivers/net/wireless/cnss_utils/cnss_utils.c index 49551309c42c83e9903c516b7dd448c296b72f96..77a58c8cb6a40fb3ba23ea0af76d4a69aaf9c3df 100644 --- a/drivers/net/wireless/cnss_utils/cnss_utils.c +++ b/drivers/net/wireless/cnss_utils/cnss_utils.c @@ -13,8 +13,10 @@ #define pr_fmt(fmt) "cnss_utils: " fmt #include +#include #include #include +#include #include #define CNSS_MAX_CH_NUM 45 @@ -29,6 +31,7 @@ struct cnss_dfs_nol_info { }; #define MAX_NO_OF_MAC_ADDR 4 +#define MAC_PREFIX_LEN 2 struct cnss_wlan_mac_addr { u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN]; u32 no_of_mac_addr_set; @@ -50,6 +53,7 @@ static struct cnss_utils_priv { struct cnss_wlan_mac_addr wlan_mac_addr; struct cnss_wlan_mac_addr wlan_der_mac_addr; enum cnss_utils_cc_src cc_source; + struct dentry *root_dentry; } *cnss_utils_priv; int cnss_utils_set_wlan_unsafe_channel(struct device *dev, @@ -317,6 +321,137 @@ enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev) } EXPORT_SYMBOL(cnss_utils_get_cc_source); +static ssize_t cnss_utils_mac_write(struct file *fp, + const char __user *user_buf, + size_t count, loff_t *off) +{ + struct cnss_utils_priv *priv = + ((struct seq_file *)fp->private_data)->private; + char buf[128]; + char *input, *mac_type, *mac_address; + u8 *dest_mac; + u8 val; + const char *delim = " \n"; + size_t len = 0; + char temp[3] = ""; + + len = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EINVAL; + buf[len] = '\0'; + + input = buf; + + mac_type = strsep(&input, delim); + if (!mac_type) + return -EINVAL; + if (!input) + return -EINVAL; + + mac_address = strsep(&input, delim); + if (!mac_address) + return -EINVAL; + if (strncmp("0x", mac_address, MAC_PREFIX_LEN)) { + pr_err("Invalid MAC prefix\n"); + return -EINVAL; + } + + len = strlen(mac_address); + mac_address += MAC_PREFIX_LEN; + len -= MAC_PREFIX_LEN; + if (len < ETH_ALEN * 2 || len > ETH_ALEN * 2 * MAX_NO_OF_MAC_ADDR || + len % (ETH_ALEN * 2) != 0) { + pr_err("Invalid MAC address length %zu\n", len); + return -EINVAL; + } + + if (!strcmp("provisioned", mac_type)) { + dest_mac = &priv->wlan_mac_addr.mac_addr[0][0]; + priv->wlan_mac_addr.no_of_mac_addr_set = len / (ETH_ALEN * 2); + } else if (!strcmp("derived", mac_type)) { + dest_mac = &priv->wlan_der_mac_addr.mac_addr[0][0]; + priv->wlan_der_mac_addr.no_of_mac_addr_set = + len / (ETH_ALEN * 2); + } else { + pr_err("Invalid MAC address type %s\n", mac_type); + return -EINVAL; + } + + while (len--) { + temp[0] = *mac_address++; + temp[1] = *mac_address++; + if (kstrtou8(temp, 16, &val)) + return -EINVAL; + *dest_mac++ = val; + } + return count; +} + +static int cnss_utils_mac_show(struct seq_file *s, void *data) +{ + u8 mac[6]; + int i; + struct cnss_utils_priv *priv = s->private; + struct cnss_wlan_mac_addr *addr = NULL; + + addr = &priv->wlan_mac_addr; + if (addr->no_of_mac_addr_set) { + seq_puts(s, "\nProvisioned MAC addresseses\n"); + for (i = 0; i < addr->no_of_mac_addr_set; i++) { + ether_addr_copy(mac, addr->mac_addr[i]); + seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + } + } + + addr = &priv->wlan_der_mac_addr; + if (addr->no_of_mac_addr_set) { + seq_puts(s, "\nDerived MAC addresseses\n"); + for (i = 0; i < addr->no_of_mac_addr_set; i++) { + ether_addr_copy(mac, addr->mac_addr[i]); + seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + } + } + + return 0; +} + +static int cnss_utils_mac_open(struct inode *inode, struct file *file) +{ + return single_open(file, cnss_utils_mac_show, inode->i_private); +} + +static const struct file_operations cnss_utils_mac_fops = { + .read = seq_read, + .write = cnss_utils_mac_write, + .release = single_release, + .open = cnss_utils_mac_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + +static int cnss_utils_debugfs_create(struct cnss_utils_priv *priv) +{ + int ret = 0; + struct dentry *root_dentry; + + root_dentry = debugfs_create_dir("cnss_utils", NULL); + + if (IS_ERR(root_dentry)) { + ret = PTR_ERR(root_dentry); + pr_err("Unable to create debugfs %d\n", ret); + goto out; + } + priv->root_dentry = root_dentry; + debugfs_create_file("mac_address", 0600, root_dentry, priv, + &cnss_utils_mac_fops); +out: + return ret; +} + static int __init cnss_utils_init(void) { struct cnss_utils_priv *priv = NULL; @@ -329,7 +464,7 @@ static int __init cnss_utils_init(void) mutex_init(&priv->unsafe_channel_list_lock); spin_lock_init(&priv->dfs_nol_info_lock); - + cnss_utils_debugfs_create(priv); cnss_utils_priv = priv; return 0; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index c8e7b54a538ac9bb5be4b36dec5c42afb70ad8bc..73da5e63a609296816414b9069bb6154cc55efe0 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -53,6 +53,7 @@ #include #include "iwl-config.h" #include "iwl-agn-hw.h" +#include "fw/file.h" /* Highest firmware API version supported */ #define IWL9000_UCODE_API_MAX 34 @@ -264,6 +265,67 @@ const struct iwl_cfg iwl9560_2ac_cfg_soc = { .integrated = true, .soc_latency = 5000, }; + +const struct iwl_cfg iwl9460_2ac_cfg_shared_clk = { + .name = "Intel(R) Dual Band Wireless AC 9460", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, + .soc_latency = 5000, + .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK +}; + +const struct iwl_cfg iwl9461_2ac_cfg_shared_clk = { + .name = "Intel(R) Dual Band Wireless AC 9461", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, + .soc_latency = 5000, + .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK +}; + +const struct iwl_cfg iwl9462_2ac_cfg_shared_clk = { + .name = "Intel(R) Dual Band Wireless AC 9462", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, + .soc_latency = 5000, + .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK +}; + +const struct iwl_cfg iwl9560_2ac_cfg_shared_clk = { + .name = "Intel(R) Dual Band Wireless AC 9560", + .fw_name_pre = IWL9000A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE, + .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, + .soc_latency = 5000, + .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK +}; + MODULE_FIRMWARE(IWL9000A_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9000B_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9000RFB_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index e988e4c371c43f999fa961361d5f822d15fe409c..1b3ad8ef0c792fcec6e704e6e90d08598c83777b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -434,6 +434,7 @@ enum iwl_fw_phy_cfg { FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS, FW_PHY_CFG_RX_CHAIN_POS = 20, FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS, + FW_PHY_CFG_SHARED_CLK = BIT(31), }; #define IWL_UCODE_MAX_CS 1 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index e226179c32fa7e252c710de011472852045babbd..85fe1a928adc961b3ccfcf160ab209d4e2ad637c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -394,6 +394,7 @@ struct iwl_cfg { u8 max_vht_ampdu_exponent; u8 ucode_api_max; u8 ucode_api_min; + u32 extra_phy_cfg_flags; }; /* @@ -476,6 +477,10 @@ extern const struct iwl_cfg iwl9460_2ac_cfg_soc; extern const struct iwl_cfg iwl9461_2ac_cfg_soc; extern const struct iwl_cfg iwl9462_2ac_cfg_soc; extern const struct iwl_cfg iwl9560_2ac_cfg_soc; +extern const struct iwl_cfg iwl9460_2ac_cfg_shared_clk; +extern const struct iwl_cfg iwl9461_2ac_cfg_shared_clk; +extern const struct iwl_cfg iwl9462_2ac_cfg_shared_clk; +extern const struct iwl_cfg iwl9560_2ac_cfg_shared_clk; extern const struct iwl_cfg iwla000_2ac_cfg_hr; extern const struct iwl_cfg iwla000_2ac_cfg_hr_cdb; extern const struct iwl_cfg iwla000_2ac_cfg_jf; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 83485493a79aaea1a6c8d130cadb6be10371847c..b71a9d11a50f1e1b63bff4e7a3a307ec1a9a9cb0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -435,6 +435,10 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) /* Set parameters */ phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); + + /* set flags extra PHY configuration flags from the device's cfg */ + phy_cfg_cmd.phy_cfg |= cpu_to_le32(mvm->cfg->extra_phy_cfg_flags); + phy_cfg_cmd.calib_control.event_trigger = mvm->fw->default_calib[ucode_type].event_trigger; phy_cfg_cmd.calib_control.flow_trigger = diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 0f7bd37bf1728fc5edd28e08290f55a6d1a3982a..9a8605abb00a99da41ed36638e32f4a16a363bba 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -8,6 +8,7 @@ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016-2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -36,6 +37,7 @@ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * All rights reserved. * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -515,9 +517,9 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8265_2ac_cfg)}, /* 9000 Series */ - {IWL_PCI_DEVICE(0x2526, 0x0000, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0010, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0014, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0018, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0030, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0034, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0038, iwl9560_2ac_cfg)}, @@ -542,11 +544,15 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x2526, 0x1410, iwl9270_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x1420, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2526, 0x1610, iwl9270_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2526, 0x2034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2526, 0x4010, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x4030, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x4034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2526, 0x40A4, iwl9460_2ac_cfg)}, - {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x4234, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2526, 0x42A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0010, iwl9160_2ac_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0014, iwl9160_2ac_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0210, iwl9160_2ac_cfg)}, @@ -567,38 +573,146 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x2720, 0x0264, iwl9461_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2720, 0x02A0, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2720, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x2034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2720, 0x4030, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2720, 0x4034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2720, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2720, 0x42A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x003C, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x0060, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x0064, iwl9461_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x00A0, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x023C, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x0260, iwl9461_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x0264, iwl9461_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x02A0, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x30DC, 0x02A4, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0034, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0064, iwl9461_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x00A0, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x00A4, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0230, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0234, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0238, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x023C, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0260, iwl9461_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x0264, iwl9461_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x02A0, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x02A4, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x4030, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x4034, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x31DC, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x30DC, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x30DC, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x2034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x30DC, 0x42A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0034, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0064, iwl9461_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x00A0, iwl9462_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x00A4, iwl9462_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0230, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0234, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0238, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x023C, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0260, iwl9461_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x0264, iwl9461_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x02A0, iwl9462_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x02A4, iwl9462_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x31DC, 0x1030, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x31DC, 0x2030, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x2034, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x4030, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x4034, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x40A4, iwl9462_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x4234, iwl9560_2ac_cfg_shared_clk)}, + {IWL_PCI_DEVICE(0x31DC, 0x42A4, iwl9462_2ac_cfg_shared_clk)}, {IWL_PCI_DEVICE(0x34F0, 0x0030, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x34F0, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x003C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0060, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x023C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x02A0, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x34F0, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x34F0, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x34F0, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x2034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x34F0, 0x42A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x003C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0060, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x023C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3DF0, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3DF0, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x2034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x3DF0, 0x42A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x003C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0060, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x023C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x43F0, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x43F0, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x2034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x43F0, 0x42A4, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x0030, iwl9560_2ac_cfg_soc)}, @@ -624,11 +738,44 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x1210, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl9460_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x2034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl9460_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x4030, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x4034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x9DF0, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x9DF0, 0x42A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0038, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x003C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0060, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0064, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x00A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x00A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0230, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0238, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x023C, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0260, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x0264, iwl9461_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x02A0, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x1010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0xA0F0, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0xA0F0, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x2034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x4030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x4034, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA0F0, 0x42A4, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x0030, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x0034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x0038, iwl9560_2ac_cfg_soc)}, @@ -645,10 +792,16 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0xA370, 0x0264, iwl9461_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x02A0, iwl9462_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x02A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x1010, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0xA370, 0x1030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x1210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0xA370, 0x2030, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x2034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x4030, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x4034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0xA370, 0x40A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x4234, iwl9560_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0xA370, 0x42A4, iwl9462_2ac_cfg_soc)}, /* a000 Series */ {IWL_PCI_DEVICE(0x2720, 0x0A10, iwla000_2ac_cfg_hr_cdb)}, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index d148dbf3beeb95cb3161791af97844694943ffef..d686ba10fecc1804bb00db48dcfc1c96eda1165f 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -3153,8 +3153,10 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) { u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]); - if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) + if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) { + kfree(hwname); return -EINVAL; + } param.regd = hwsim_world_regdom_custom[idx]; } @@ -3425,8 +3427,11 @@ static void __net_exit hwsim_exit_net(struct net *net) continue; list_del(&data->list); - INIT_WORK(&data->destroy_work, destroy_radio); - schedule_work(&data->destroy_work); + spin_unlock_bh(&hwsim_radio_lock); + mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), + NULL); + spin_lock_bh(&hwsim_radio_lock); + } spin_unlock_bh(&hwsim_radio_lock); } diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c index ecc96312a370377b1d29bc6f4dd450062a6538b4..6fe0c6abe0d6eca885b9a2be028de258c90f01d8 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c @@ -142,15 +142,25 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, if (!rt2x00dev->ops->hw->set_rts_threshold && (tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS | IEEE80211_TX_RC_USE_CTS_PROTECT))) { - if (rt2x00queue_available(queue) <= 1) - goto exit_fail; + if (rt2x00queue_available(queue) <= 1) { + /* + * Recheck for full queue under lock to avoid race + * conditions with rt2x00lib_txdone(). + */ + spin_lock(&queue->tx_lock); + if (rt2x00queue_threshold(queue)) + rt2x00queue_pause_queue(queue); + spin_unlock(&queue->tx_lock); + + goto exit_free_skb; + } if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb)) - goto exit_fail; + goto exit_free_skb; } if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false))) - goto exit_fail; + goto exit_free_skb; /* * Pausing queue has to be serialized with rt2x00lib_txdone(). Note @@ -164,10 +174,6 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, return; - exit_fail: - spin_lock(&queue->tx_lock); - rt2x00queue_pause_queue(queue); - spin_unlock(&queue->tx_lock); exit_free_skb: ieee80211_free_txskb(hw, skb); } diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 121b94f09714858a49c04dca090192bf057730f0..9a1d15b3ce4535540184d34a5600be131ba329dd 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1450,6 +1450,7 @@ static int rtl8187_probe(struct usb_interface *intf, goto err_free_dev; } mutex_init(&priv->io_mutex); + mutex_init(&priv->conf_mutex); SET_IEEE80211_DEV(dev, &intf->dev); usb_set_intfdata(intf, dev); @@ -1625,7 +1626,6 @@ static int rtl8187_probe(struct usb_interface *intf, printk(KERN_ERR "rtl8187: Cannot register device\n"); goto err_free_dmabuf; } - mutex_init(&priv->conf_mutex); skb_queue_head_init(&priv->b_tx_status.queue); wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n", diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 6d02c660b4ab785db914889c9819691c84b9a372..037defd10b91800a5210c4f115584d30278d9e5f 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1200,8 +1200,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS); enable = bss_conf->arp_addr_cnt == 1 && bss_conf->assoc; - wl1251_acx_arp_ip_filter(wl, enable, addr); - + ret = wl1251_acx_arp_ip_filter(wl, enable, addr); if (ret < 0) goto out_sleep; } diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index c980cdbd6e53ffff7a568e4b09f5b85658283cf8..f07b9c9bb5ba8155ec959d441e2c4af9ec40095f 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -351,6 +351,9 @@ static int xennet_open(struct net_device *dev) unsigned int i = 0; struct netfront_queue *queue = NULL; + if (!np->queues) + return -ENODEV; + for (i = 0; i < num_queues; ++i) { queue = &np->queues[i]; napi_enable(&queue->napi); @@ -1358,18 +1361,8 @@ static int netfront_probe(struct xenbus_device *dev, #ifdef CONFIG_SYSFS info->netdev->sysfs_groups[0] = &xennet_dev_group; #endif - err = register_netdev(info->netdev); - if (err) { - pr_warn("%s: register_netdev err=%d\n", __func__, err); - goto fail; - } return 0; - - fail: - xennet_free_netdev(netdev); - dev_set_drvdata(&dev->dev, NULL); - return err; } static void xennet_end_access(int ref, void *page) @@ -1738,8 +1731,6 @@ static void xennet_destroy_queues(struct netfront_info *info) { unsigned int i; - rtnl_lock(); - for (i = 0; i < info->netdev->real_num_tx_queues; i++) { struct netfront_queue *queue = &info->queues[i]; @@ -1748,8 +1739,6 @@ static void xennet_destroy_queues(struct netfront_info *info) netif_napi_del(&queue->napi); } - rtnl_unlock(); - kfree(info->queues); info->queues = NULL; } @@ -1765,8 +1754,6 @@ static int xennet_create_queues(struct netfront_info *info, if (!info->queues) return -ENOMEM; - rtnl_lock(); - for (i = 0; i < *num_queues; i++) { struct netfront_queue *queue = &info->queues[i]; @@ -1775,7 +1762,7 @@ static int xennet_create_queues(struct netfront_info *info, ret = xennet_init_queue(queue); if (ret < 0) { - dev_warn(&info->netdev->dev, + dev_warn(&info->xbdev->dev, "only created %d queues\n", i); *num_queues = i; break; @@ -1789,10 +1776,8 @@ static int xennet_create_queues(struct netfront_info *info, netif_set_real_num_tx_queues(info->netdev, *num_queues); - rtnl_unlock(); - if (*num_queues == 0) { - dev_err(&info->netdev->dev, "no queues\n"); + dev_err(&info->xbdev->dev, "no queues\n"); return -EINVAL; } return 0; @@ -1829,6 +1814,7 @@ static int talk_to_netback(struct xenbus_device *dev, goto out; } + rtnl_lock(); if (info->queues) xennet_destroy_queues(info); @@ -1839,6 +1825,7 @@ static int talk_to_netback(struct xenbus_device *dev, info->queues = NULL; goto out; } + rtnl_unlock(); /* Create shared ring, alloc event channel -- for each queue */ for (i = 0; i < num_queues; ++i) { @@ -1935,8 +1922,10 @@ static int talk_to_netback(struct xenbus_device *dev, xenbus_transaction_end(xbt, 1); destroy_ring: xennet_disconnect_backend(info); + rtnl_lock(); xennet_destroy_queues(info); out: + rtnl_unlock(); device_unregister(&dev->dev); return err; } @@ -1966,6 +1955,15 @@ static int xennet_connect(struct net_device *dev) netdev_update_features(dev); rtnl_unlock(); + if (dev->reg_state == NETREG_UNINITIALIZED) { + err = register_netdev(dev); + if (err) { + pr_warn("%s: register_netdev err=%d\n", __func__, err); + device_unregister(&np->xbdev->dev); + return err; + } + } + /* * All public and private state should now be sane. Get * ready to start sending and receiving packets and give the driver @@ -2008,7 +2006,10 @@ static void netback_changed(struct xenbus_device *dev, case XenbusStateInitialised: case XenbusStateReconfiguring: case XenbusStateReconfigured: + break; + case XenbusStateUnknown: + wake_up_all(&module_unload_q); break; case XenbusStateInitWait: @@ -2139,7 +2140,9 @@ static int xennet_remove(struct xenbus_device *dev) xenbus_switch_state(dev, XenbusStateClosing); wait_event(module_unload_q, xenbus_read_driver_state(dev->otherend) == - XenbusStateClosing); + XenbusStateClosing || + xenbus_read_driver_state(dev->otherend) == + XenbusStateUnknown); xenbus_switch_state(dev, XenbusStateClosed); wait_event(module_unload_q, @@ -2151,10 +2154,14 @@ static int xennet_remove(struct xenbus_device *dev) xennet_disconnect_backend(info); - unregister_netdev(info->netdev); + if (info->netdev->reg_state == NETREG_REGISTERED) + unregister_netdev(info->netdev); - if (info->queues) + if (info->queues) { + rtnl_lock(); xennet_destroy_queues(info); + rtnl_unlock(); + } xennet_free_netdev(info->netdev); return 0; diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index f58d8e3053236ad4608e5552052214b0694a3a95..18339b7e88a46292a294313f40f991afc47cb8eb 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -998,6 +998,9 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, mw_base = nt->mw_vec[mw_num].phys_addr; mw_size = nt->mw_vec[mw_num].phys_size; + if (max_mw_size && mw_size > max_mw_size) + mw_size = max_mw_size; + tx_size = (unsigned int)mw_size / num_qps_mw; qp_offset = tx_size * (qp_num / mw_count); diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index b2feda35966b124652c662f65392231ca464875c..c625df951fa11749f587403a8136d209e1b9a603 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "btt.h" #include "nd.h" @@ -1535,6 +1536,8 @@ static int btt_blk_init(struct btt *btt) btt->btt_disk->private_data = btt; btt->btt_disk->queue = btt->btt_queue; btt->btt_disk->flags = GENHD_FL_EXT_DEVT; + btt->btt_disk->queue->backing_dev_info->capabilities |= + BDI_CAP_SYNCHRONOUS_IO; blk_queue_make_request(btt->btt_queue, btt_make_request); blk_queue_logical_block_size(btt->btt_queue, btt->sector_size); diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index 98466d762c8fac4f624b2fe4583a81251abad6c9..0939f064054d580c3088b790eff1cdf4054926c4 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c @@ -65,9 +65,11 @@ static int nvdimm_probe(struct device *dev) ndd->ns_next = nd_label_next_nsindex(ndd->ns_current); nd_label_copy(ndd, to_next_namespace_index(ndd), to_current_namespace_index(ndd)); - rc = nd_label_reserve_dpa(ndd); - if (ndd->ns_current >= 0) - nvdimm_set_aliasing(dev); + if (ndd->ns_current >= 0) { + rc = nd_label_reserve_dpa(ndd); + if (rc == 0) + nvdimm_set_aliasing(dev); + } nvdimm_clear_locked(dev); nvdimm_bus_unlock(dev); diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 0af988739a06c1195b1335ab29f54d9b9752c591..228bafa4d322422e608f7ba802046db8aaaff42f 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -1926,7 +1926,7 @@ struct device *create_namespace_pmem(struct nd_region *nd_region, } if (i < nd_region->ndr_mappings) { - struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); + struct nvdimm *nvdimm = nd_region->mapping[i].nvdimm; /* * Give up if we don't find an instance of a uuid at each @@ -1934,7 +1934,7 @@ struct device *create_namespace_pmem(struct nd_region *nd_region, * find a dimm with two instances of the same uuid. */ dev_err(&nd_region->dev, "%s missing label for %pUb\n", - dev_name(ndd->dev), nd_label->uuid); + nvdimm_name(nvdimm), nd_label->uuid); rc = -EINVAL; goto err; } diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 39dfd7affa319a3aa0599e6a1bfa6ade7ec3c5f1..7fbc5c5dc8e176b7fda861f9929ea879bad0406e 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "pmem.h" #include "pfn.h" #include "nd.h" @@ -394,6 +395,7 @@ static int pmem_attach_disk(struct device *dev, disk->fops = &pmem_fops; disk->queue = q; disk->flags = GENHD_FL_EXT_DEVT; + disk->queue->backing_dev_info->capabilities |= BDI_CAP_SYNCHRONOUS_IO; nvdimm_namespace_disk_name(ndns, disk->disk_name); set_capacity(disk, (pmem->size - pmem->pfn_pad - pmem->data_offset) / 512); diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c index 7b75d9de55ab0d33939bf314a81ee01e26a6d1b1..c0080f6ab2f5b209eb8aeae003df6659f9124f11 100644 --- a/drivers/nvme/target/fcloop.c +++ b/drivers/nvme/target/fcloop.c @@ -204,6 +204,10 @@ struct fcloop_lport { struct completion unreg_done; }; +struct fcloop_lport_priv { + struct fcloop_lport *lport; +}; + struct fcloop_rport { struct nvme_fc_remote_port *remoteport; struct nvmet_fc_target_port *targetport; @@ -370,6 +374,7 @@ fcloop_tgt_fcprqst_done_work(struct work_struct *work) spin_lock(&tfcp_req->reqlock); fcpreq = tfcp_req->fcpreq; + tfcp_req->fcpreq = NULL; spin_unlock(&tfcp_req->reqlock); if (tport->remoteport && fcpreq) { @@ -611,11 +616,7 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport, if (!tfcp_req) /* abort has already been called */ - return; - - if (rport->targetport) - nvmet_fc_rcv_fcp_abort(rport->targetport, - &tfcp_req->tgt_fcp_req); + goto finish; /* break initiator/target relationship for io */ spin_lock(&tfcp_req->reqlock); @@ -623,6 +624,11 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport, tfcp_req->fcpreq = NULL; spin_unlock(&tfcp_req->reqlock); + if (rport->targetport) + nvmet_fc_rcv_fcp_abort(rport->targetport, + &tfcp_req->tgt_fcp_req); + +finish: /* post the aborted io completion */ fcpreq->status = -ECANCELED; schedule_work(&inireq->iniwork); @@ -657,7 +663,8 @@ fcloop_nport_get(struct fcloop_nport *nport) static void fcloop_localport_delete(struct nvme_fc_local_port *localport) { - struct fcloop_lport *lport = localport->private; + struct fcloop_lport_priv *lport_priv = localport->private; + struct fcloop_lport *lport = lport_priv->lport; /* release any threads waiting for the unreg to complete */ complete(&lport->unreg_done); @@ -697,7 +704,7 @@ static struct nvme_fc_port_template fctemplate = { .max_dif_sgl_segments = FCLOOP_SGL_SEGS, .dma_boundary = FCLOOP_DMABOUND_4G, /* sizes of additional private data for data structures */ - .local_priv_sz = sizeof(struct fcloop_lport), + .local_priv_sz = sizeof(struct fcloop_lport_priv), .remote_priv_sz = sizeof(struct fcloop_rport), .lsrqst_priv_sz = sizeof(struct fcloop_lsreq), .fcprqst_priv_sz = sizeof(struct fcloop_ini_fcpreq), @@ -728,11 +735,17 @@ fcloop_create_local_port(struct device *dev, struct device_attribute *attr, struct fcloop_ctrl_options *opts; struct nvme_fc_local_port *localport; struct fcloop_lport *lport; - int ret; + struct fcloop_lport_priv *lport_priv; + unsigned long flags; + int ret = -ENOMEM; + + lport = kzalloc(sizeof(*lport), GFP_KERNEL); + if (!lport) + return -ENOMEM; opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) - return -ENOMEM; + goto out_free_lport; ret = fcloop_parse_options(opts, buf); if (ret) @@ -752,23 +765,25 @@ fcloop_create_local_port(struct device *dev, struct device_attribute *attr, ret = nvme_fc_register_localport(&pinfo, &fctemplate, NULL, &localport); if (!ret) { - unsigned long flags; - /* success */ - lport = localport->private; + lport_priv = localport->private; + lport_priv->lport = lport; + lport->localport = localport; INIT_LIST_HEAD(&lport->lport_list); spin_lock_irqsave(&fcloop_lock, flags); list_add_tail(&lport->lport_list, &fcloop_lports); spin_unlock_irqrestore(&fcloop_lock, flags); - - /* mark all of the input buffer consumed */ - ret = count; } out_free_opts: kfree(opts); +out_free_lport: + /* free only if we're going to fail */ + if (ret) + kfree(lport); + return ret ? ret : count; } @@ -790,6 +805,8 @@ __wait_localport_unreg(struct fcloop_lport *lport) wait_for_completion(&lport->unreg_done); + kfree(lport); + return ret; } diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 179cbc5929b8b9be96c9c3e2a653f05cf29e51dc..b1103e519ef615cc10f4cbb2c9d38ac22429e4a1 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -975,7 +975,7 @@ int __init early_init_dt_scan_chosen_stdout(void) int offset; const char *p, *q, *options = NULL; int l; - const struct earlycon_id *match; + const struct earlycon_id **p_match; const void *fdt = initial_boot_params; offset = fdt_path_offset(fdt, "/chosen"); @@ -1002,7 +1002,10 @@ int __init early_init_dt_scan_chosen_stdout(void) return 0; } - for (match = __earlycon_table; match < __earlycon_table_end; match++) { + for (p_match = __earlycon_table; p_match < __earlycon_table_end; + p_match++) { + const struct earlycon_id *match = *p_match; + if (!match->compatible[0]) continue; diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 489492b608cf325ee5e1b662b2902f5465c5eecf..380916bff9e05ce4a1cd12fbdbb6a4b787759dec 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2646,6 +2646,7 @@ enum parport_pc_pci_cards { netmos_9901, netmos_9865, quatech_sppxp100, + wch_ch382l, }; @@ -2708,6 +2709,7 @@ static struct parport_pc_pci { /* netmos_9901 */ { 1, { { 0, -1 }, } }, /* netmos_9865 */ { 1, { { 0, -1 }, } }, /* quatech_sppxp100 */ { 1, { { 0, 1 }, } }, + /* wch_ch382l */ { 1, { { 2, -1 }, } }, }; static const struct pci_device_id parport_pc_pci_tbl[] = { @@ -2797,6 +2799,8 @@ static const struct pci_device_id parport_pc_pci_tbl[] = { /* Quatech SPPXP-100 Parallel port PCI ExpressCard */ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 }, + /* WCH CH382L PCI-E single parallel port card */ + { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l }, { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl); diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c index 26ed0c08f20972e6d1d6ef09b4daf140dc17a193..9bfc22b5da4b718ffe7ed5e1d9805e0e9317fee7 100644 --- a/drivers/pci/host/pci-aardvark.c +++ b/drivers/pci/host/pci-aardvark.c @@ -32,6 +32,7 @@ #define PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT 5 #define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11) #define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT 12 +#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ 0x2 #define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0 #define PCIE_CORE_LINK_L0S_ENTRY BIT(0) #define PCIE_CORE_LINK_TRAINING BIT(5) @@ -103,7 +104,8 @@ #define PCIE_ISR1_MASK_REG (CONTROL_BASE_ADDR + 0x4C) #define PCIE_ISR1_POWER_STATE_CHANGE BIT(4) #define PCIE_ISR1_FLUSH BIT(5) -#define PCIE_ISR1_ALL_MASK GENMASK(5, 4) +#define PCIE_ISR1_INTX_ASSERT(val) BIT(8 + (val)) +#define PCIE_ISR1_ALL_MASK GENMASK(11, 4) #define PCIE_MSI_ADDR_LOW_REG (CONTROL_BASE_ADDR + 0x50) #define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54) #define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58) @@ -175,8 +177,6 @@ #define PCIE_CONFIG_WR_TYPE0 0xa #define PCIE_CONFIG_WR_TYPE1 0xb -/* PCI_BDF shifts 8bit, so we need extra 4bit shift */ -#define PCIE_BDF(dev) (dev << 4) #define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20) #define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15) #define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12) @@ -299,7 +299,8 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE | (7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) | PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE | - PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT; + (PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ << + PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT); advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG); /* Program PCIe Control 2 to disable strict ordering */ @@ -440,7 +441,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, u32 reg; int ret; - if (PCI_SLOT(devfn) != 0) { + if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) { *val = 0xffffffff; return PCIBIOS_DEVICE_NOT_FOUND; } @@ -459,7 +460,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, advk_writel(pcie, reg, PIO_CTRL); /* Program the address registers */ - reg = PCIE_BDF(devfn) | PCIE_CONF_REG(where); + reg = PCIE_CONF_ADDR(bus->number, devfn, where); advk_writel(pcie, reg, PIO_ADDR_LS); advk_writel(pcie, 0, PIO_ADDR_MS); @@ -494,7 +495,7 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn, int offset; int ret; - if (PCI_SLOT(devfn) != 0) + if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) return PCIBIOS_DEVICE_NOT_FOUND; if (where % size) @@ -612,9 +613,9 @@ static void advk_pcie_irq_mask(struct irq_data *d) irq_hw_number_t hwirq = irqd_to_hwirq(d); u32 mask; - mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); - mask |= PCIE_ISR0_INTX_ASSERT(hwirq); - advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); + mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); + mask |= PCIE_ISR1_INTX_ASSERT(hwirq); + advk_writel(pcie, mask, PCIE_ISR1_MASK_REG); } static void advk_pcie_irq_unmask(struct irq_data *d) @@ -623,9 +624,9 @@ static void advk_pcie_irq_unmask(struct irq_data *d) irq_hw_number_t hwirq = irqd_to_hwirq(d); u32 mask; - mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); - mask &= ~PCIE_ISR0_INTX_ASSERT(hwirq); - advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); + mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); + mask &= ~PCIE_ISR1_INTX_ASSERT(hwirq); + advk_writel(pcie, mask, PCIE_ISR1_MASK_REG); } static int advk_pcie_irq_map(struct irq_domain *h, @@ -768,29 +769,35 @@ static void advk_pcie_handle_msi(struct advk_pcie *pcie) static void advk_pcie_handle_int(struct advk_pcie *pcie) { - u32 val, mask, status; + u32 isr0_val, isr0_mask, isr0_status; + u32 isr1_val, isr1_mask, isr1_status; int i, virq; - val = advk_readl(pcie, PCIE_ISR0_REG); - mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); - status = val & ((~mask) & PCIE_ISR0_ALL_MASK); + isr0_val = advk_readl(pcie, PCIE_ISR0_REG); + isr0_mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + isr0_status = isr0_val & ((~isr0_mask) & PCIE_ISR0_ALL_MASK); + + isr1_val = advk_readl(pcie, PCIE_ISR1_REG); + isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); + isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK); - if (!status) { - advk_writel(pcie, val, PCIE_ISR0_REG); + if (!isr0_status && !isr1_status) { + advk_writel(pcie, isr0_val, PCIE_ISR0_REG); + advk_writel(pcie, isr1_val, PCIE_ISR1_REG); return; } /* Process MSI interrupts */ - if (status & PCIE_ISR0_MSI_INT_PENDING) + if (isr0_status & PCIE_ISR0_MSI_INT_PENDING) advk_pcie_handle_msi(pcie); /* Process legacy interrupts */ for (i = 0; i < PCI_NUM_INTX; i++) { - if (!(status & PCIE_ISR0_INTX_ASSERT(i))) + if (!(isr1_status & PCIE_ISR1_INTX_ASSERT(i))) continue; - advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i), - PCIE_ISR0_REG); + advk_writel(pcie, PCIE_ISR1_INTX_ASSERT(i), + PCIE_ISR1_REG); virq = irq_find_mapping(pcie->irq_domain, i); generic_handle_irq(virq); diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 04dac6a42c9f287519379757a0cefd3c08e14e11..73b724143be02a19ff2eb364a9316a288f1d44c1 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -457,7 +457,6 @@ struct hv_pcibus_device { spinlock_t device_list_lock; /* Protect lists below */ void __iomem *cfg_addr; - struct semaphore enum_sem; struct list_head resources_for_children; struct list_head children; @@ -471,6 +470,8 @@ struct hv_pcibus_device { struct retarget_msi_interrupt retarget_msi_interrupt_params; spinlock_t retarget_msi_interrupt_lock; + + struct workqueue_struct *wq; }; /* @@ -1604,12 +1605,8 @@ static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus, * It must also treat the omission of a previously observed device as * notification that the device no longer exists. * - * Note that this function is a work item, and it may not be - * invoked in the order that it was queued. Back to back - * updates of the list of present devices may involve queuing - * multiple work items, and this one may run before ones that - * were sent later. As such, this function only does something - * if is the last one in the queue. + * Note that this function is serialized with hv_eject_device_work(), + * because both are pushed to the ordered workqueue hbus->wq. */ static void pci_devices_present_work(struct work_struct *work) { @@ -1630,11 +1627,6 @@ static void pci_devices_present_work(struct work_struct *work) INIT_LIST_HEAD(&removed); - if (down_interruptible(&hbus->enum_sem)) { - put_hvpcibus(hbus); - return; - } - /* Pull this off the queue and process it if it was the last one. */ spin_lock_irqsave(&hbus->device_list_lock, flags); while (!list_empty(&hbus->dr_list)) { @@ -1651,7 +1643,6 @@ static void pci_devices_present_work(struct work_struct *work) spin_unlock_irqrestore(&hbus->device_list_lock, flags); if (!dr) { - up(&hbus->enum_sem); put_hvpcibus(hbus); return; } @@ -1738,7 +1729,6 @@ static void pci_devices_present_work(struct work_struct *work) break; } - up(&hbus->enum_sem); put_hvpcibus(hbus); kfree(dr); } @@ -1784,7 +1774,7 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, spin_unlock_irqrestore(&hbus->device_list_lock, flags); get_hvpcibus(hbus); - schedule_work(&dr_wrk->wrk); + queue_work(hbus->wq, &dr_wrk->wrk); } /** @@ -1862,7 +1852,7 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev) get_pcichild(hpdev, hv_pcidev_ref_pnp); INIT_WORK(&hpdev->wrk, hv_eject_device_work); get_hvpcibus(hpdev->hbus); - schedule_work(&hpdev->wrk); + queue_work(hpdev->hbus->wq, &hpdev->wrk); } /** @@ -2475,13 +2465,18 @@ static int hv_pci_probe(struct hv_device *hdev, spin_lock_init(&hbus->config_lock); spin_lock_init(&hbus->device_list_lock); spin_lock_init(&hbus->retarget_msi_interrupt_lock); - sema_init(&hbus->enum_sem, 1); init_completion(&hbus->remove_event); + hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0, + hbus->sysdata.domain); + if (!hbus->wq) { + ret = -ENOMEM; + goto free_bus; + } ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0, hv_pci_onchannelcallback, hbus); if (ret) - goto free_bus; + goto destroy_wq; hv_set_drvdata(hdev, hbus); @@ -2550,6 +2545,8 @@ static int hv_pci_probe(struct hv_device *hdev, hv_free_config_window(hbus); close: vmbus_close(hdev->channel); +destroy_wq: + destroy_workqueue(hbus->wq); free_bus: free_page((unsigned long)hbus); return ret; @@ -2629,6 +2626,7 @@ static int hv_pci_remove(struct hv_device *hdev) irq_domain_free_fwnode(hbus->sysdata.fwnode); put_hvpcibus(hbus); wait_for_completion(&hbus->remove_event); + destroy_workqueue(hbus->wq); free_page((unsigned long)hbus); return 0; } diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 86a179f0fe3bf590357f07096ef9880511de9553..85320e48b26b8a0855f7911e8d497c736b155715 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -56,13 +56,6 @@ #define PCIE20_PARF_DBI_BASE_ADDR 0x350 #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x358 -#define PCS_BASE 0x800 - -#define PCS_PORT(n) (PCS_BASE + n * 0x1000) - -#define PCIE_N_SW_RESET(n) (PCS_PORT(n) + 0x00) -#define PCIE_N_POWER_DOWN_CONTROL(n) (PCS_PORT(n) + 0x04) - #define PCIE_GEN3_SPCIE_CAP 0x0154 #define PCIE_GEN3_GEN2_CTRL 0x080c #define PCIE_GEN3_RELATED 0x0890 @@ -600,6 +593,7 @@ struct msm_pcie_dev_t { uint32_t wr_halt_size; uint32_t slv_addr_space_size; uint32_t phy_status_offset; + uint32_t phy_power_down_offset; uint32_t cpl_timeout; uint32_t current_bdf; uint32_t perst_delay_us_min; @@ -1251,6 +1245,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev) dev->slv_addr_space_size); PCIE_DBG_FS(dev, "phy_status_offset: 0x%x\n", dev->phy_status_offset); + PCIE_DBG_FS(dev, "phy_power_down_offset: 0x%x\n", + dev->phy_power_down_offset); PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n", dev->cpl_timeout); PCIE_DBG_FS(dev, "current_bdf: 0x%x\n", @@ -1455,6 +1451,10 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev, pci_walk_bus(bus, &msm_pcie_config_l1_enable, dev); + /* enable l1 mode, clear bit 5 (REQ_NOT_ENTR_L1) */ + msm_pcie_write_mask(dev->parf + + PCIE20_PARF_PM_CTRL, BIT(5), 0); + msm_pcie_config_l1_enable(dev->dev, dev); } break; @@ -1549,6 +1549,13 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev, break; } + if (((base_sel - 1) >= MSM_PCIE_MAX_RES) || + (!dev->res[base_sel - 1].resource)) { + PCIE_DBG_FS(dev, "PCIe: RC%d Resource does not exist\n", + dev->rc_idx); + break; + } + PCIE_DBG_FS(dev, "base: %s: 0x%pK\nwr_offset: 0x%x\nwr_mask: 0x%x\nwr_value: 0x%x\n", dev->res[base_sel - 1].name, @@ -1568,6 +1575,13 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev, break; case MSM_PCIE_DUMP_PCIE_REGISTER_SPACE: + if (((base_sel - 1) >= MSM_PCIE_MAX_RES) || + (!dev->res[base_sel - 1].resource)) { + PCIE_DBG_FS(dev, "PCIe: RC%d Resource does not exist\n", + dev->rc_idx); + break; + } + if (!base_sel) { PCIE_DBG_FS(dev, "Invalid base_sel: 0x%x\n", base_sel); break; @@ -3734,23 +3748,22 @@ static void msm_pcie_setup_gen3(struct msm_pcie_dev_t *dev) { PCIE_DBG(dev, "PCIe: RC%d: Setting up Gen3\n", dev->rc_idx); - msm_pcie_write_reg(dev->dm_core, - PCIE_GEN3_EQ_FB_MODE_DIR_CHANGE, - (0x05 << 14) | (0x05 << 10) | (0x0d << 5)); + msm_pcie_write_reg_field(dev->dm_core, + PCIE_GEN3_GEN2_CTRL, 0x1f00, 1); - msm_pcie_write_mask(dev->dm_core + - PCIE_GEN3_EQ_CONTROL, BIT(4), 0); + msm_pcie_write_mask(dev->dm_core, + PCIE_GEN3_EQ_CONTROL, 0x20); msm_pcie_write_mask(dev->dm_core + PCIE_GEN3_RELATED, BIT(0), 0); /* configure PCIe preset */ - msm_pcie_write_reg(dev->dm_core, - PCIE_GEN3_MISC_CONTROL, 1); + msm_pcie_write_reg_field(dev->dm_core, + PCIE_GEN3_MISC_CONTROL, BIT(0), 1); msm_pcie_write_reg(dev->dm_core, PCIE_GEN3_SPCIE_CAP, 0x77777777); - msm_pcie_write_reg(dev->dm_core, - PCIE_GEN3_MISC_CONTROL, 1); + msm_pcie_write_reg_field(dev->dm_core, + PCIE_GEN3_MISC_CONTROL, BIT(0), 0); } static int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) @@ -4025,12 +4038,8 @@ static int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) gpio_set_value(dev->gpio[MSM_PCIE_GPIO_EP].num, 1 - dev->gpio[MSM_PCIE_GPIO_EP].on); - if (dev->max_link_speed != GEN3_SPEED) { - msm_pcie_write_reg(dev->phy, - PCIE_N_SW_RESET(dev->rc_idx), 0x1); - msm_pcie_write_reg(dev->phy, - PCIE_N_POWER_DOWN_CONTROL(dev->rc_idx), 0); - } + if (dev->phy_power_down_offset) + msm_pcie_write_reg(dev->phy, dev->phy_power_down_offset, 0); msm_pcie_pipe_clk_deinit(dev); msm_pcie_clk_deinit(dev); @@ -4068,12 +4077,8 @@ static void msm_pcie_disable(struct msm_pcie_dev_t *dev, u32 options) gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num, dev->gpio[MSM_PCIE_GPIO_PERST].on); - if (dev->max_link_speed != GEN3_SPEED) { - msm_pcie_write_reg(dev->phy, - PCIE_N_SW_RESET(dev->rc_idx), 0x1); - msm_pcie_write_reg(dev->phy, - PCIE_N_POWER_DOWN_CONTROL(dev->rc_idx), 0); - } + if (dev->phy_power_down_offset) + msm_pcie_write_reg(dev->phy, dev->phy_power_down_offset, 0); if (options & PM_CLK) { msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, 0, @@ -5889,6 +5894,19 @@ static int msm_pcie_probe(struct platform_device *pdev) rc_idx, msm_pcie_dev[rc_idx].phy_status_offset); } + msm_pcie_dev[rc_idx].phy_power_down_offset = 0; + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,phy-power-down-offset", + &msm_pcie_dev[rc_idx].phy_power_down_offset); + if (ret) + PCIE_DBG(&msm_pcie_dev[rc_idx], + "RC%d: qcom,phy-power-down-offset not found.\n", + rc_idx); + else + PCIE_DBG(&msm_pcie_dev[rc_idx], + "RC%d: phy-power-down-offset: 0x%x.\n", + rc_idx, msm_pcie_dev[rc_idx].phy_power_down_offset); + msm_pcie_dev[rc_idx].cpl_timeout = 0; ret = of_property_read_u32((&pdev->dev)->of_node, "qcom,cpl-timeout", diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 5ed2dcaa8e27c0ebe0729c3f543a58261c86a651..711875afdd70ae40205375588a5b807e95320011 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -558,6 +558,7 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) { unsigned long long sta = 0; struct acpiphp_func *func; + u32 dvid; list_for_each_entry(func, &slot->funcs, sibling) { if (func->flags & FUNC_HAS_STA) { @@ -568,19 +569,27 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) if (ACPI_SUCCESS(status) && sta) break; } else { - u32 dvid; - - pci_bus_read_config_dword(slot->bus, - PCI_DEVFN(slot->device, - func->function), - PCI_VENDOR_ID, &dvid); - if (dvid != 0xffffffff) { + if (pci_bus_read_dev_vendor_id(slot->bus, + PCI_DEVFN(slot->device, func->function), + &dvid, 0)) { sta = ACPI_STA_ALL; break; } } } + if (!sta) { + /* + * Check for the slot itself since it may be that the + * ACPI slot is a device below PCIe upstream port so in + * that case it may not even be reachable yet. + */ + if (pci_bus_read_dev_vendor_id(slot->bus, + PCI_DEVFN(slot->device, 0), &dvid, 0)) { + sta = ACPI_STA_ALL; + } + } + return (unsigned int)sta; } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 633e55c57b136bb4c382c1bab3b5310e16a6314c..bcb96af284a110f52e91238c6a1350d4d5dfecde 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1065,7 +1065,8 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) } EXPORT_SYMBOL(pci_disable_link_state); -static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) +static int pcie_aspm_set_policy(const char *val, + const struct kernel_param *kp) { int i; struct pcie_link_state *link; @@ -1092,7 +1093,7 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) return 0; } -static int pcie_aspm_get_policy(char *buffer, struct kernel_param *kp) +static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp) { int i, cnt = 0; for (i = 0; i < ARRAY_SIZE(policy_str); i++) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 05fadcc4f9d2e314b7ade6bd6159d6f206e3af3c..116127a0accb7f255fac27d66c05b676ca0feebe 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3879,6 +3879,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9120, quirk_dma_func1_alias); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123, quirk_dma_func1_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9128, + quirk_dma_func1_alias); /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9130, quirk_dma_func1_alias); @@ -4806,9 +4808,13 @@ static void quirk_no_ext_tags(struct pci_dev *pdev) pci_walk_bus(bridge->bus, pci_configure_extended_tags, NULL); } +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0132, quirk_no_ext_tags); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0140, quirk_no_ext_tags); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0141, quirk_no_ext_tags); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0142, quirk_no_ext_tags); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0144, quirk_no_ext_tags); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0420, quirk_no_ext_tags); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0422, quirk_no_ext_tags); #ifdef CONFIG_PCI_ATS /* diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index e665ea6dcb22e44254474b4c018ef7fadac71099..2288889ebfe9822974198468e254cabd6d583e0f 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -915,6 +915,9 @@ struct arm_pmu *armpmu_alloc(void) events->percpu_pmu = pmu; } + pmu->pmu_state = ARM_PMU_STATE_OFF; + pmu->percpu_irq = -1; + return pmu; out_free_pmu: @@ -944,9 +947,6 @@ int armpmu_register(struct arm_pmu *pmu) if (!__oprofile_cpu_pmu) __oprofile_cpu_pmu = pmu; - pmu->pmu_state = ARM_PMU_STATE_OFF; - pmu->percpu_irq = -1; - pr_info("enabled with %s PMU driver, %d counters available\n", pmu->name, pmu->num_events); diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 1161e11fb3cfb811e380ee38afcca5ab1eec5e11..afedb8cd19906edce5629c9985fcde67d0b31bf0 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -410,11 +410,13 @@ static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) return true; /* - * The A31 companion pmic (axp221) does not generate vbus change - * interrupts when the board is driving vbus, so we must poll + * The A31/A23/A33 companion pmics (AXP221/AXP223) do not + * generate vbus change interrupts when the board is driving + * vbus using the N_VBUSEN pin on the pmic, so we must poll * when using the pmic for vbus-det _and_ we're driving vbus. */ - if (data->cfg->type == sun6i_a31_phy && + if ((data->cfg->type == sun6i_a31_phy || + data->cfg->type == sun8i_a33_phy) && data->vbus_power_supply && data->phys[0].regulator_on) return true; @@ -885,7 +887,7 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { .num_phys = 2, - .type = sun4i_a10_phy, + .type = sun6i_a31_phy, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.c index 220bc5699077a23b14c23fd4fc3d14ded32ffda0..c571c5bbf378be32a710e013b76386ab836a3899 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.c @@ -19,6 +19,9 @@ static int ufs_qcom_phy_qmp_v4_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B) { + writel_relaxed(0x01, ufs_qcom_phy->mmio + UFS_PHY_SW_RESET); + /* Ensure PHY is in reset before writing PHY calibration data */ + wmb(); /* * Writing PHY calibration in this order: * 1. Write Rate-A calibration first (1-lane mode). @@ -33,8 +36,10 @@ int ufs_qcom_phy_qmp_v4_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, if (is_rate_B) ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_rate_B, ARRAY_SIZE(phy_cal_table_rate_B)); + + writel_relaxed(0x00, ufs_qcom_phy->mmio + UFS_PHY_SW_RESET); /* flush buffered writes */ - mb(); + wmb(); return 0; } diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.h index bb1c669a7aebcf9e6d2579b865a73cd48a9335b2..5d45115ed5f4e552d193b9bba05e577cb656b6e0 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.h +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-v4.h @@ -55,6 +55,7 @@ #define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0xB8) #define QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE1 COM_OFF(0x1B4) #define QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE1 COM_OFF(0x1B8) +#define QSERDES_COM_CMN_IPTRIM COM_OFF(0x60) /* UFS PHY registers */ #define UFS_PHY_PHY_START PHY_OFF(0x00) @@ -125,6 +126,9 @@ #define QSERDES_RX0_RX_MODE_10_HIGH2 RX_OFF(0, 0x1A0) #define QSERDES_RX0_RX_MODE_10_HIGH3 RX_OFF(0, 0x1A4) #define QSERDES_RX0_RX_MODE_10_HIGH4 RX_OFF(0, 0x1A8) +#define QSERDES_RX0_AC_JTAG_ENABLE RX_OFF(0, 0x68) +#define QSERDES_RX0_UCDR_FO_GAIN RX_OFF(0, 0x08) +#define QSERDES_RX0_UCDR_SO_GAIN RX_OFF(0, 0x14) #define QSERDES_RX1_SIGDET_LVL RX_OFF(1, 0x120) #define QSERDES_RX1_SIGDET_CNTRL RX_OFF(1, 0x11C) @@ -158,7 +162,9 @@ #define QSERDES_RX1_RX_MODE_10_HIGH2 RX_OFF(1, 0x1A0) #define QSERDES_RX1_RX_MODE_10_HIGH3 RX_OFF(1, 0x1A4) #define QSERDES_RX1_RX_MODE_10_HIGH4 RX_OFF(1, 0x1A8) - +#define QSERDES_RX1_AC_JTAG_ENABLE RX_OFF(1, 0x68) +#define QSERDES_RX1_UCDR_FO_GAIN RX_OFF(1, 0x08) +#define QSERDES_RX1_UCDR_SO_GAIN RX_OFF(1, 0x14) #define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1) @@ -211,34 +217,49 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_DEGLITCH_CNTRL, 0x1E), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_BAND, 0x18), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN, 0x0A), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x7F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CTRL2, 0x80), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_TERM_BW, 0x1B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2, 0x06), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x4E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4, 0x1D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_OFFSET_ADAPTOR_CNTRL2, 0x27), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_IDAC_MEASURE_TIME, 0x10), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_IDAC_TSETTLE_LOW, 0xC0), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_IDAC_TSETTLE_HIGH, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_LOW, 0xE0), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH2, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH3, 0x09), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH4, 0xB1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_LOW, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH2, 0xF6), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH3, 0x3B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_00_HIGH4, 0x3D), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_LOW, 0xE0), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH, 0xC8), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH2, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH3, 0x09), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH3, 0x3B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_01_HIGH4, 0xB1), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_LOW, 0xE0), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH, 0xC8), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH2, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH3, 0x09), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH3, 0x3B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_MODE_10_HIGH4, 0xB1), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0x9A), /* 8 us */ + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_MID_TERM_CTRL1, 0x43), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_DEBUG_BUS_CLKSEL, 0x1F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PLL_CNTL, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0xD8), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_PWM_GEAR_BAND, 0xAA), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HS_GEAR_BAND, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HSGEAR_CAPABILITY, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HSGEAR_CAPABILITY, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_AC_JTAG_ENABLE, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FO_GAIN, 0x0C), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_GAIN, 0x04), }; static struct ufs_qcom_phy_calibration phy_cal_table_2nd_lane[] = { @@ -253,46 +274,37 @@ static struct ufs_qcom_phy_calibration phy_cal_table_2nd_lane[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_DEGLITCH_CNTRL, 0x1E), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_BAND, 0x18), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN, 0x0A), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x7F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0xF1), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CTRL2, 0x80), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_TERM_BW, 0x1B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2, 0x06), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3, 0x4E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4, 0x1D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_OFFSET_ADAPTOR_CNTRL2, 0x27), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_IDAC_MEASURE_TIME, 0x10), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_IDAC_TSETTLE_LOW, 0xC0), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_IDAC_TSETTLE_HIGH, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_LOW, 0xE0), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH2, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH3, 0x09), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH4, 0xB1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_LOW, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH2, 0xF6), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH3, 0x3B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_00_HIGH4, 0x3D), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_LOW, 0xE0), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH, 0xC8), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH2, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH3, 0x09), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH3, 0x3B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_01_HIGH4, 0xB1), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_LOW, 0xE0), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH, 0xC8), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH2, 0xC8), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH3, 0x09), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH3, 0x3B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_MODE_10_HIGH4, 0xB1), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6C), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_MID_TERM_CTRL1, 0x43), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_DEBUG_BUS_CLKSEL, 0x1F), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PLL_CNTL, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0xD8), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_PWM_GEAR_BAND, 0xAA), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HS_GEAR_BAND, 0x06), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_HSGEAR_CAPABILITY, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_HSGEAR_CAPABILITY, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_AC_JTAG_ENABLE, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FO_GAIN, 0x0C), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_GAIN, 0x04), }; static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c index b420c7028b6f7105e263a52fb241a7a58b393150..acf632a527ca444fd0fe507de588623e3ae1ec79 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -311,8 +311,8 @@ int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) if (err) goto out; - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, - "vddp-ref-clk"); + ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, + "vddp-ref-clk"); out: return err; @@ -808,3 +808,8 @@ void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy) ufs_qcom_phy->phy_spec_ops->dbg_register_dump(ufs_qcom_phy); } EXPORT_SYMBOL(ufs_qcom_phy_dbg_register_dump); + +MODULE_AUTHOR("Yaniv Gardi "); +MODULE_AUTHOR("Vivek Gautam "); +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 0f3a02495aeb66aae84c31bab5d40278825946f7..beeb7cbb50155821375761bf297ebf69276284ce 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -46,6 +46,9 @@ #define BYT_TRIG_POS BIT(25) #define BYT_TRIG_LVL BIT(24) #define BYT_DEBOUNCE_EN BIT(20) +#define BYT_GLITCH_FILTER_EN BIT(19) +#define BYT_GLITCH_F_SLOW_CLK BIT(17) +#define BYT_GLITCH_F_FAST_CLK BIT(16) #define BYT_PULL_STR_SHIFT 9 #define BYT_PULL_STR_MASK (3 << BYT_PULL_STR_SHIFT) #define BYT_PULL_STR_2K (0 << BYT_PULL_STR_SHIFT) @@ -1579,6 +1582,9 @@ static int byt_irq_type(struct irq_data *d, unsigned int type) */ value &= ~(BYT_DIRECT_IRQ_EN | BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + /* Enable glitch filtering */ + value |= BYT_GLITCH_FILTER_EN | BYT_GLITCH_F_SLOW_CLK | + BYT_GLITCH_F_FAST_CLK; writel(value, reg); diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 72b4527d690f43bae235936f766b7be2dca79a90..71df0f70b61f01c51484c87f35bdba9a1158cdbc 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -427,18 +427,6 @@ static void __intel_gpio_set_direction(void __iomem *padcfg0, bool input) writel(value, padcfg0); } -static void intel_gpio_set_gpio_mode(void __iomem *padcfg0) -{ - u32 value; - - /* Put the pad into GPIO mode */ - value = readl(padcfg0) & ~PADCFG0_PMODE_MASK; - /* Disable SCI/SMI/NMI generation */ - value &= ~(PADCFG0_GPIROUTIOXAPIC | PADCFG0_GPIROUTSCI); - value &= ~(PADCFG0_GPIROUTSMI | PADCFG0_GPIROUTNMI); - writel(value, padcfg0); -} - static int intel_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned pin) @@ -446,6 +434,7 @@ static int intel_gpio_request_enable(struct pinctrl_dev *pctldev, struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); void __iomem *padcfg0; unsigned long flags; + u32 value; raw_spin_lock_irqsave(&pctrl->lock, flags); @@ -455,7 +444,13 @@ static int intel_gpio_request_enable(struct pinctrl_dev *pctldev, } padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0); - intel_gpio_set_gpio_mode(padcfg0); + /* Put the pad into GPIO mode */ + value = readl(padcfg0) & ~PADCFG0_PMODE_MASK; + /* Disable SCI/SMI/NMI generation */ + value &= ~(PADCFG0_GPIROUTIOXAPIC | PADCFG0_GPIROUTSCI); + value &= ~(PADCFG0_GPIROUTSMI | PADCFG0_GPIROUTNMI); + writel(value, padcfg0); + /* Disable TX buffer and enable RX (this will be input) */ __intel_gpio_set_direction(padcfg0, true); @@ -940,8 +935,6 @@ static int intel_gpio_irq_type(struct irq_data *d, unsigned type) raw_spin_lock_irqsave(&pctrl->lock, flags); - intel_gpio_set_gpio_mode(reg); - value = readl(reg); value &= ~(PADCFG0_RXEVCFG_MASK | PADCFG0_RXINV); diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 2dc42cb40dba0c1d3acf1a8c3f220939fe69c484..062ee60ba17bacdc2247d29856d47440af93c4eb 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -166,12 +166,12 @@ config PINCTRL_SDMSHRIKE Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SDMSHRIKE platform. -config PINCTRL_SDM640 - tristate "Qualcomm Technologies Inc SDM640 pin controller driver" +config PINCTRL_SM6150 + tristate "Qualcomm Technologies Inc SM6150 pin controller driver" depends on GPIOLIB && OF select PINCTRL_MSM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm - Technologies Inc SDM640 platform. + Technologies Inc SM6150 platform. endif diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 0435aca11c33d3633a8b2ce5fd6a511abb4ef646..8cfd857a8ff6a3102fb3740c08fd6944cc843960 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -21,4 +21,4 @@ obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o obj-$(CONFIG_PINCTRL_SM8150) += pinctrl-sm8150.o obj-$(CONFIG_PINCTRL_SDMSHRIKE) += pinctrl-sdmshrike.o -obj-$(CONFIG_PINCTRL_SDM640) += pinctrl-sdm640.o +obj-$(CONFIG_PINCTRL_SM6150) += pinctrl-sm6150.o diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 53fab077ab56276d262e9c8934bed644a0ab069a..85660fc49a20518502963e0f8f08d90a545cf7fd 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013, Sony Mobile Communications AB. - * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018, 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 @@ -849,6 +849,38 @@ static void msm_dirconn_irq_mask(struct irq_data *d) parent_data->chip->irq_mask(parent_data); } +static void msm_dirconn_irq_enable(struct irq_data *d) +{ + struct irq_desc *desc = irq_data_to_desc(d); + struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq); + irq_hw_number_t dir_conn_irq = 0; + + if (!parent_data) + return; + + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + struct irq_data *dir_conn_data = + irq_get_irq_data(irq_find_mapping(parent_data->domain, + dir_conn_irq)); + + if (dir_conn_data && + dir_conn_data->chip->irq_set_irqchip_state) + dir_conn_data->chip->irq_set_irqchip_state( + dir_conn_data, + IRQCHIP_STATE_PENDING, 0); + + if (dir_conn_data && dir_conn_data->chip->irq_unmask) + dir_conn_data->chip->irq_unmask(dir_conn_data); + } + + if (parent_data->chip->irq_set_irqchip_state) + parent_data->chip->irq_set_irqchip_state(parent_data, + IRQCHIP_STATE_PENDING, 0); + + if (parent_data->chip->irq_unmask) + parent_data->chip->irq_unmask(parent_data); +} + static void msm_dirconn_irq_unmask(struct irq_data *d) { struct irq_desc *desc = irq_data_to_desc(d); @@ -1079,6 +1111,7 @@ static int msm_dirconn_irq_set_type(struct irq_data *d, unsigned int type) static struct irq_chip msm_dirconn_irq_chip = { .name = "msmgpio-dc", .irq_mask = msm_dirconn_irq_mask, + .irq_enable = msm_dirconn_irq_enable, .irq_unmask = msm_dirconn_irq_unmask, .irq_eoi = msm_dirconn_irq_eoi, .irq_ack = msm_dirconn_irq_ack, diff --git a/drivers/pinctrl/qcom/pinctrl-sdm640.c b/drivers/pinctrl/qcom/pinctrl-sm6150.c similarity index 97% rename from drivers/pinctrl/qcom/pinctrl-sdm640.c rename to drivers/pinctrl/qcom/pinctrl-sm6150.c index 0d692cc5eb3041743145128519f5f930c6ac2bb7..937ffde559638af6946b070b5acd51116a759cab 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm640.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6150.c @@ -121,7 +121,7 @@ .intr_detection_bit = -1, \ .intr_detection_width = -1, \ } -static const struct pinctrl_pin_desc sdm640_pins[] = { +static const struct pinctrl_pin_desc sm6150_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), PINCTRL_PIN(2, "GPIO_2"), @@ -390,7 +390,7 @@ static const unsigned int sdc2_cmd_pins[] = { 128 }; static const unsigned int sdc2_data_pins[] = { 129 }; static const unsigned int ufs_reset_pins[] = { 130 }; -enum sdm640_functions { +enum sm6150_functions { msm_mux_qup02, msm_mux_gpio, msm_mux_qdss_gpio6, @@ -1148,7 +1148,7 @@ static const char * const mclk2_groups[] = { "gpio122", }; -static const struct msm_function sdm640_functions[] = { +static const struct msm_function sm6150_functions[] = { FUNCTION(qup02), FUNCTION(gpio), FUNCTION(qdss_gpio6), @@ -1339,7 +1339,7 @@ static const struct msm_function sdm640_functions[] = { * pin descriptor registered with pinctrl core. * Clients would not be able to request these dummy pin groups. */ -static const struct msm_pingroup sdm640_groups[] = { +static const struct msm_pingroup sm6150_groups[] = { [0] = PINGROUP(0, WEST, qup02, NA, qdss_gpio6, NA, NA, NA, NA, NA, NA), [1] = PINGROUP(1, WEST, qup02, NA, qdss_gpio7, NA, NA, NA, NA, NA, NA), [2] = PINGROUP(2, WEST, qup02, NA, qdss_gpio8, NA, NA, NA, NA, NA, NA), @@ -1558,7 +1558,7 @@ static const struct msm_pingroup sdm640_groups[] = { [130] = UFS_RESET(ufs_reset, 0x9f000), }; -static struct msm_dir_conn sdm640_dir_conn[] = { +static struct msm_dir_conn sm6150_dir_conn[] = { {1, 525}, {3, 511}, {7, 535}, @@ -1628,51 +1628,51 @@ static struct msm_dir_conn sdm640_dir_conn[] = { {0, 209}, }; -static const struct msm_pinctrl_soc_data sdm640_pinctrl = { - .pins = sdm640_pins, - .npins = ARRAY_SIZE(sdm640_pins), - .functions = sdm640_functions, - .nfunctions = ARRAY_SIZE(sdm640_functions), - .groups = sdm640_groups, - .ngroups = ARRAY_SIZE(sdm640_groups), +static const struct msm_pinctrl_soc_data sm6150_pinctrl = { + .pins = sm6150_pins, + .npins = ARRAY_SIZE(sm6150_pins), + .functions = sm6150_functions, + .nfunctions = ARRAY_SIZE(sm6150_functions), + .groups = sm6150_groups, + .ngroups = ARRAY_SIZE(sm6150_groups), .ngpios = 123, - .dir_conn = sdm640_dir_conn, - .n_dir_conns = ARRAY_SIZE(sdm640_dir_conn), + .dir_conn = sm6150_dir_conn, + .n_dir_conns = ARRAY_SIZE(sm6150_dir_conn), .dir_conn_irq_base = 216, }; -static int sdm640_pinctrl_probe(struct platform_device *pdev) +static int sm6150_pinctrl_probe(struct platform_device *pdev) { - return msm_pinctrl_probe(pdev, &sdm640_pinctrl); + return msm_pinctrl_probe(pdev, &sm6150_pinctrl); } -static const struct of_device_id sdm640_pinctrl_of_match[] = { - { .compatible = "qcom,sdm640-pinctrl", }, +static const struct of_device_id sm6150_pinctrl_of_match[] = { + { .compatible = "qcom,sm6150-pinctrl", }, { }, }; -static struct platform_driver sdm640_pinctrl_driver = { +static struct platform_driver sm6150_pinctrl_driver = { .driver = { - .name = "sdm640-pinctrl", + .name = "sm6150-pinctrl", .owner = THIS_MODULE, - .of_match_table = sdm640_pinctrl_of_match, + .of_match_table = sm6150_pinctrl_of_match, }, - .probe = sdm640_pinctrl_probe, + .probe = sm6150_pinctrl_probe, .remove = msm_pinctrl_remove, }; -static int __init sdm640_pinctrl_init(void) +static int __init sm6150_pinctrl_init(void) { - return platform_driver_register(&sdm640_pinctrl_driver); + return platform_driver_register(&sm6150_pinctrl_driver); } -arch_initcall(sdm640_pinctrl_init); +arch_initcall(sm6150_pinctrl_init); -static void __exit sdm640_pinctrl_exit(void) +static void __exit sm6150_pinctrl_exit(void) { - platform_driver_unregister(&sdm640_pinctrl_driver); + platform_driver_unregister(&sm6150_pinctrl_driver); } -module_exit(sdm640_pinctrl_exit); +module_exit(sm6150_pinctrl_exit); -MODULE_DESCRIPTION("QTI sdm640 pinctrl driver"); +MODULE_DESCRIPTION("QTI sm6150 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sdm640_pinctrl_of_match); +MODULE_DEVICE_TABLE(of, sm6150_pinctrl_of_match); diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index dc01db3fa2b74e3b13c1704f0f2792636ec8a9ba..45350332238404b79df758832ee62ca31e40d3b0 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -147,6 +147,17 @@ config RNDIS_IPA This Network interface is aimed to allow data path go through IPA core while using RNDIS protocol. +config IPA3_MHI_PROXY + tristate "IPA3 MHI proxy driver" + depends on RMNET_IPA3 + help + This driver is used as a proxy between modem and MHI host driver. + Its main functionality is to setup MHI Satellite channels on behalf of + modem and provide the ability of modem to MHI device communication. + Once the configuration is done modem will communicate directly with + the MHI device without AP involvement, with the exception of + power management. + config IPA_UT tristate "IPA Unit-Test Framework and Test Suites" depends on IPA3 && DEBUG_FS @@ -169,4 +180,12 @@ config MSM_11AD If you choose to build it as a module, it will be called msm_11ad_proxy. +config SEEMP_CORE + tristate "SEEMP Core" + help + This option enables QTI Snapdragron Smart Protection to detect + anomalies in various activities. It records task activities in + a log and rates the actions according to whether a typical user would + use the tools. + endmenu diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index 6e36268a7502f287ea901c0e01bc713aba536090..12149fde88fe84605564115d2dc93f30521d7d84 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_GSI) += gsi/ obj-$(CONFIG_IPA) += ipa/ obj-$(CONFIG_IPA3) += ipa/ obj-$(CONFIG_MSM_11AD) += msm_11ad/ +obj-$(CONFIG_SEEMP_CORE) += seemp_core/ diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index e936e3e33297f1062b539eb425563b3eb7214b0d..1a020b8e47bd101a7a6dee3f3d5b37cfdd559fab 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -1635,6 +1635,10 @@ static void gsi_program_chan_ctx(struct gsi_chan_props *props, unsigned int ee, GSI_EE_n_GSI_CH_k_QOS_MAX_PREFETCH_BMSK) | ((props->use_db_eng << GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_SHFT) & GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_BMSK)); + if (gsi_ctx->per.ver >= GSI_VER_2_0) + val |= ((props->prefetch_mode << + GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_SHFT) + & GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_BMSK); gsi_writel(val, gsi_ctx->base + GSI_EE_n_GSI_CH_k_QOS_OFFS(props->ch_id, ee)); } diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h index 3c35b02486dac1adf14a7c29328617bc1fc12cdd..2067ac922a747836beb4284d5984e4771e6bd344 100644 --- a/drivers/platform/msm/gsi/gsi_reg.h +++ b/drivers/platform/msm/gsi/gsi_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -1124,6 +1124,8 @@ #define GSI_EE_n_GSI_CH_k_QOS_RMSK 0x303 #define GSI_EE_n_GSI_CH_k_QOS_MAXk 30 #define GSI_EE_n_GSI_CH_k_QOS_MAXn 3 +#define GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_BMSK 0x400 +#define GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_SHFT 0xa #define GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_BMSK 0x200 #define GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_SHFT 0x9 #define GSI_EE_n_GSI_CH_k_QOS_MAX_PREFETCH_BMSK 0x100 diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index 87c4e488e626bcb3b2bd6b0ee62b13ad65459594..6551d99a0fe2aa49e67fdf38823f52e9fa1e12be 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -2771,8 +2771,8 @@ static const struct of_device_id ipa_plat_drv_match[] = { { .compatible = "qcom,ipa-smmu-ap-cb", }, { .compatible = "qcom,ipa-smmu-wlan-cb", }, { .compatible = "qcom,ipa-smmu-uc-cb", }, - { .compatible = "qcom,smp2pgpio-map-ipa-1-in", }, - { .compatible = "qcom,smp2pgpio-map-ipa-1-out", }, + { .compatible = "qcom,smp2p-map-ipa-1-in", }, + { .compatible = "qcom,smp2p-map-ipa-1-out", }, {} }; diff --git a/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c b/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c index 95f78e45fa5e1017c9e8699f6d9521ea0a792243..f7c4dc43dacfbd8572641f04efb3b61e36009758 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c +++ b/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c @@ -1442,6 +1442,10 @@ static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl, usb_to_ipa_ep_cfg.route.rt_tbl_hdl = 0; usb_to_ipa_ep_cfg.mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS; usb_to_ipa_ep_cfg.mode.mode = IPA_BASIC; + + /* enable hdr_metadata_reg_valid */ + usb_to_ipa_ep_cfg.hdr.hdr_metadata_reg_valid = true; + result = ipa_cfg_ep(usb_to_ipa_hdl, &usb_to_ipa_ep_cfg); if (result) { ECM_IPA_ERROR("failed to configure USB to IPA point\n"); diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index 9f75c6c13ab089b429d419f4fb76c24adeb91c79..a6f713b271c853bec92ce9ae7a08b6fd7130fd03 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -1871,12 +1871,15 @@ static int ipa3_usb_xdci_connect_internal( } if (ipa_pm_is_used()) { - result = ipa_pm_set_perf_profile( - ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl, - params->max_supported_bandwidth_mbps); - if (result) { - IPA_USB_ERR("failed to set perf profile\n"); - return result; + /* perf profile is not set on USB DPL pipe */ + if (ttype != IPA_USB_TRANSPORT_DPL) { + result = ipa_pm_set_perf_profile( + ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl, + params->max_supported_bandwidth_mbps); + if (result) { + IPA_USB_ERR("failed to set perf profile\n"); + return result; + } } result = ipa_pm_activate_sync( diff --git a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c index ae42f54d9119cad4770db05a16a31fb2758c1398..d607f9f8cb210b17d61e9ad2a656624fa83684bf 100644 --- a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c +++ b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c @@ -359,7 +359,7 @@ static struct ipa_ep_cfg usb_to_ipa_ep_cfg_deaggr_dis = { sizeof(struct rndis_pkt_hdr), .hdr_a5_mux = false, .hdr_remove_additional = false, - .hdr_metadata_reg_valid = false, + .hdr_metadata_reg_valid = true, }, .hdr_ext = { .hdr_pad_to_alignment = 0, @@ -406,7 +406,7 @@ static struct ipa_ep_cfg usb_to_ipa_ep_cfg_deaggr_en = { .hdr_ofst_pkt_size = 3 * sizeof(u32), .hdr_a5_mux = false, .hdr_remove_additional = false, - .hdr_metadata_reg_valid = false, + .hdr_metadata_reg_valid = true, }, .hdr_ext = { .hdr_pad_to_alignment = 0, @@ -2166,6 +2166,9 @@ static int rndis_ipa_ep_registers_cfg( ipa_to_usb_ep_cfg.aggr.aggr_time_limit, ipa_to_usb_ep_cfg.aggr.aggr_pkt_limit); + /* enable hdr_metadata_reg_valid */ + usb_to_ipa_ep_cfg->hdr.hdr_metadata_reg_valid = true; + result = ipa_cfg_ep(ipa_to_usb_hdl, &ipa_to_usb_ep_cfg); if (result) { pr_err("failed to configure IPA to USB end-point\n"); diff --git a/drivers/platform/msm/ipa/ipa_v3/Makefile b/drivers/platform/msm/ipa/ipa_v3/Makefile index ae4dccfe75c3ad762212ea54bcafb94c6473d521..9901e642a6a56fc0922f92c5987146cb5de378bd 100644 --- a/drivers/platform/msm/ipa/ipa_v3/Makefile +++ b/drivers/platform/msm/ipa/ipa_v3/Makefile @@ -7,3 +7,5 @@ ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o ipa_hw_stats.o ipa_pm.o ipa_wdi3_i.o obj-$(CONFIG_RMNET_IPA3) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o + +obj-$(CONFIG_IPA3_MHI_PROXY) += ipa_mhi_proxy.o diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 9015bc9ab6cb723aede204a2277dc115f38ba723..0cbbb42e52618a151d03bed4b4123cf8d0f2f8ec 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #ifdef CONFIG_ARM64 @@ -61,6 +63,8 @@ #define IPA_GPIO_IN_QUERY_CLK_IDX 0 #define IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX 0 #define IPA_GPIO_OUT_CLK_VOTE_IDX 1 +#define IPA_SMP2P_SMEM_STATE_MASK 3 + #define IPA_SUMMING_THRESHOLD (0x10) #define IPA_PIPE_MEM_START_OFST (0x0) @@ -374,7 +378,7 @@ static int ipa3_active_clients_panic_notifier(struct notifier_block *this, { ipa3_active_clients_log_print_table(active_clients_table_buf, IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE); - IPAERR("%s", active_clients_table_buf); + IPAERR("%s\n", active_clients_table_buf); return NOTIFY_DONE; } @@ -682,6 +686,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) u32 pyld_sz; u8 header[128] = { 0 }; u8 *param = NULL; + bool is_vlan_mode; struct ipa_ioc_nat_alloc_mem nat_mem; struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc; struct ipa_ioc_v4_nat_init nat_init; @@ -691,6 +696,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct ipa_ioc_nat_pdn_entry mdfy_pdn; struct ipa_ioc_rm_dependency rm_depend; struct ipa_ioc_nat_dma_cmd *table_dma_cmd; + struct ipa_ioc_get_vlan_mode vlan_mode; size_t sz; int pre_entry; @@ -1785,6 +1791,28 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; + case IPA_IOC_GET_VLAN_MODE: + if (copy_from_user(&vlan_mode, (const void __user *)arg, + sizeof(struct ipa_ioc_get_vlan_mode))) { + retval = -EFAULT; + break; + } + retval = ipa3_is_vlan_mode( + vlan_mode.iface, + &is_vlan_mode); + if (retval) + break; + + vlan_mode.is_vlan_mode = is_vlan_mode; + + if (copy_to_user((void __user *)arg, + &vlan_mode, + sizeof(struct ipa_ioc_get_vlan_mode))) { + retval = -EFAULT; + break; + } + break; + case IPA_IOC_ADD_VLAN_IFACE: if (ipa3_send_vlan_l2tp_msg(arg, ADD_VLAN_IFACE)) { retval = -EFAULT; @@ -4221,8 +4249,9 @@ static void ipa3_freeze_clock_vote_and_notify_modem(void) if (ipa3_ctx->smp2p_info.res_sent) return; - if (ipa3_ctx->smp2p_info.out_base_id == 0) { - IPAERR("smp2p out gpio not assigned\n"); + if (IS_ERR(ipa3_ctx->smp2p_info.smem_state)) { + IPAERR("fail to get smp2p clk resp bit %d\n", + PTR_ERR(ipa3_ctx->smp2p_info.smem_state)); return; } @@ -4233,11 +4262,9 @@ static void ipa3_freeze_clock_vote_and_notify_modem(void) else ipa3_ctx->smp2p_info.ipa_clk_on = true; - gpio_set_value(ipa3_ctx->smp2p_info.out_base_id + - IPA_GPIO_OUT_CLK_VOTE_IDX, - ipa3_ctx->smp2p_info.ipa_clk_on); - gpio_set_value(ipa3_ctx->smp2p_info.out_base_id + - IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX, 1); + qcom_smem_state_update_bits(ipa3_ctx->smp2p_info.smem_state, + BIT(IPA_SMP2P_SMEM_STATE_MASK), + BIT(ipa3_ctx->smp2p_info.ipa_clk_on | (1<<1))); ipa3_ctx->smp2p_info.res_sent = true; IPADBG("IPA clocks are %s\n", @@ -4273,6 +4300,9 @@ static int ipa3_panic_notifier(struct notifier_block *this, if (res) IPAERR("uC panic handler failed %d\n", res); + if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt) != 0) + ipahal_print_all_regs(); + return NOTIFY_DONE; } @@ -4526,13 +4556,18 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, /* * IPAv3.5 and above requires to disable prefetch for USB in order - * to allow MBIM to work, currently MBIM is not needed in MHI mode. + * to allow MBIM to work. */ if ((ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5 && ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) && (!ipa3_ctx->ipa_config_is_mhi)) ipa3_disable_prefetch(IPA_CLIENT_USB_CONS); + if ((ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5 + && ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) && + (ipa3_ctx->ipa_config_is_mhi)) + ipa3_disable_prefetch(IPA_CLIENT_MHI_CONS); + memset(&gsi_props, 0, sizeof(gsi_props)); gsi_props.ver = ipa3_get_gsi_ver(resource_p->ipa_hw_type); gsi_props.ee = resource_p->ee; @@ -4578,8 +4613,6 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, IPADBG("teth_bridge initialized"); } - ipa3_debugfs_init(); - result = ipa3_uc_interface_init(); if (result) IPAERR(":ipa Uc interface init failed (%d)\n", -result); @@ -4613,6 +4646,8 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, ipa3_ctx->ipa_initialization_complete = true; mutex_unlock(&ipa3_ctx->lock); + ipa3_debugfs_init(); + ipa3_trigger_ipa_ready_cbs(); complete_all(&ipa3_ctx->init_completion_obj); pr_info("IPA driver initialization was successful.\n"); @@ -4975,6 +5010,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, ipa3_ctx->gsi_ch20_wa = resource_p->gsi_ch20_wa; ipa3_ctx->use_ipa_pm = resource_p->use_ipa_pm; ipa3_ctx->ipa3_active_clients_logging.log_rdy = false; + ipa3_ctx->ipa_config_is_mhi = resource_p->ipa_mhi_dynamic_config; ipa3_ctx->mhi_evid_limits[0] = resource_p->mhi_evid_limits[0]; ipa3_ctx->mhi_evid_limits[1] = resource_p->mhi_evid_limits[1]; @@ -5502,6 +5538,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->ipa3_hw_mode = 0; ipa_drv_res->modem_cfg_emb_pipe_flt = false; ipa_drv_res->ipa_wdi2 = false; + ipa_drv_res->ipa_mhi_dynamic_config = false; ipa_drv_res->use_64_bit_dma_mask = false; ipa_drv_res->use_bw_vote = false; ipa_drv_res->wan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ; @@ -5564,6 +5601,13 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->use_ipa_teth_bridge ? "True" : "False"); + ipa_drv_res->ipa_mhi_dynamic_config = + of_property_read_bool(pdev->dev.of_node, + "qcom,use-ipa-in-mhi-mode"); + IPADBG(": ipa_mhi_dynamic_config (%s)\n", + ipa_drv_res->ipa_mhi_dynamic_config + ? "True" : "False"); + ipa_drv_res->modem_cfg_emb_pipe_flt = of_property_read_bool(pdev->dev.of_node, "qcom,modem-cfg-emb-pipe-flt"); @@ -6209,56 +6253,42 @@ static int ipa3_smp2p_probe(struct device *dev) { struct device_node *node = dev->of_node; int res; + int irq = 0; if (ipa3_ctx == NULL) { IPAERR("ipa3_ctx was not initialized\n"); - return -ENXIO; + return -EPROBE_DEFER; } IPADBG("node->name=%s\n", node->name); - if (strcmp("qcom,smp2pgpio_map_ipa_1_out", node->name) == 0) { - res = of_get_gpio(node, 0); - if (res < 0) { - IPADBG("of_get_gpio returned %d\n", res); - return res; + if (strcmp("qcom,smp2p_map_ipa_1_out", node->name) == 0) { + if (of_find_property(node, "qcom,smem-states", NULL)) { + ipa3_ctx->smp2p_info.smem_state = + qcom_smem_state_get(dev, "ipa-smp2p-out", + &ipa3_ctx->smp2p_info.smem_bit); + if (IS_ERR(ipa3_ctx->smp2p_info.smem_state)) { + IPAERR("fail to get smp2p clk resp bit %d\n", + PTR_ERR(ipa3_ctx->smp2p_info.smem_state)); + return PTR_ERR(ipa3_ctx->smp2p_info.smem_state); + } + IPADBG("smem_bit=%d\n", ipa3_ctx->smp2p_info.smem_bit); } - - ipa3_ctx->smp2p_info.out_base_id = res; - IPADBG("smp2p out_base_id=%d\n", - ipa3_ctx->smp2p_info.out_base_id); - } else if (strcmp("qcom,smp2pgpio_map_ipa_1_in", node->name) == 0) { - int irq; - - res = of_get_gpio(node, 0); + } else if (strcmp("qcom,smp2p_map_ipa_1_in", node->name) == 0) { + res = irq = of_irq_get_byname(node, "ipa-smp2p-in"); if (res < 0) { - IPADBG("of_get_gpio returned %d\n", res); + IPADBG("of_irq_get_byname returned %d\n", irq); return res; } - ipa3_ctx->smp2p_info.in_base_id = res; - IPADBG("smp2p in_base_id=%d\n", - ipa3_ctx->smp2p_info.in_base_id); - - /* register for modem clk query */ - irq = gpio_to_irq(ipa3_ctx->smp2p_info.in_base_id + - IPA_GPIO_IN_QUERY_CLK_IDX); - if (irq < 0) { - IPAERR("gpio_to_irq failed %d\n", irq); - return -ENODEV; - } + ipa3_ctx->smp2p_info.in_base_id = irq; IPADBG("smp2p irq#=%d\n", irq); - res = request_irq(irq, + res = devm_request_threaded_irq(dev, irq, NULL, (irq_handler_t)ipa3_smp2p_modem_clk_query_isr, IRQF_TRIGGER_RISING, "ipa_smp2p_clk_vote", dev); if (res) { IPAERR("fail to register smp2p irq=%d\n", irq); return -ENODEV; } - res = enable_irq_wake(ipa3_ctx->smp2p_info.in_base_id + - IPA_GPIO_IN_QUERY_CLK_IDX); - if (res) - IPAERR("failed to enable irq wake\n"); } - return 0; } @@ -6298,11 +6328,10 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p, } if (of_device_is_compatible(dev->of_node, - "qcom,smp2pgpio-map-ipa-1-in")) + "qcom,smp2p-map-ipa-1-out")) return ipa3_smp2p_probe(dev); - if (of_device_is_compatible(dev->of_node, - "qcom,smp2pgpio-map-ipa-1-out")) + "qcom,smp2p-map-ipa-1-in")) return ipa3_smp2p_probe(dev); result = get_ipa_dts_configuration(pdev_p, &ipa3_res); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 29481263a1b629993e42052fd2714d0dae44ce9d..afc30bf7209cebd8ba6a3339c7538fd3e480bbcd 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -196,7 +196,7 @@ static int ipa3_reconfigure_channel_to_gpi(struct ipa3_ep_context *ep, chan_props.ring_len = 2 * GSI_CHAN_RE_SIZE_16B; chan_props.ring_base_vaddr = dma_alloc_coherent(ipa3_ctx->pdev, chan_props.ring_len, - &chan_dma_addr, 0); + &chan_dma_addr, GFP_ATOMIC); chan_props.ring_base_addr = chan_dma_addr; chan_dma->base = chan_props.ring_base_vaddr; chan_dma->phys_base = chan_props.ring_base_addr; @@ -299,7 +299,7 @@ static int ipa3_reset_with_open_aggr_frame_wa(u32 clnt_hdl, memset(&xfer_elem, 0, sizeof(struct gsi_xfer_elem)); buff = dma_alloc_coherent(ipa3_ctx->pdev, 1, &dma_addr, - GFP_KERNEL); + GFP_ATOMIC); xfer_elem.addr = dma_addr; xfer_elem.len = 1; xfer_elem.flags = GSI_XFER_FLAG_EOT; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index ba1af38cbbe5fa4b7ca433a4b18b0a42aacb27d1..00b812f6f2e0f791a8df0a0b57225d7032bb12c2 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -1889,6 +1889,16 @@ static ssize_t ipa3_pm_ex_read_stats(struct file *file, char __user *ubuf, return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt); } +static ssize_t ipa3_read_ipahal_regs(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + ipahal_print_all_regs(); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + + return 0; +} + static void ipa_dump_status(struct ipahal_pkt_status *status) { IPA_DUMP_STATUS_FIELD(status_opcode); @@ -2155,6 +2165,10 @@ static const struct ipa3_debugfs_file debugfs_files[] = { "enable_low_prio_print", IPA_WRITE_ONLY_MODE, NULL, { .write = ipa3_enable_ipc_low, } + }, { + "ipa_dump_regs", IPA_READ_ONLY_MODE, NULL, { + .read = ipa3_read_ipahal_regs, + } } }; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index c5544e9e74cdcdf8df62b4bbeb7fb8d736946da3..569ab9d5edd2e68cc9fff39e163f750cc4852d66 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -319,10 +319,12 @@ int ipa3_send(struct ipa3_sys_context *sys, for (i = 0; i < num_desc; i++) { tx_pkt = kmem_cache_zalloc(ipa3_ctx->tx_pkt_wrapper_cache, - mem_flag); - if (!tx_pkt) + GFP_ATOMIC); + if (!tx_pkt) { + IPAERR("failed to alloc tx wrapper\n"); + result = -ENOMEM; goto failure; - + } INIT_LIST_HEAD(&tx_pkt->link); if (i == 0) { @@ -336,6 +338,7 @@ int ipa3_send(struct ipa3_sys_context *sys, if (ipa_populate_tag_field(&desc[i], tx_pkt, &tag_pyld_ret)) { IPAERR("Failed to populate tag field\n"); + result = -EFAULT; goto failure_dma_map; } } @@ -375,6 +378,7 @@ int ipa3_send(struct ipa3_sys_context *sys, } if (dma_mapping_error(ipa3_ctx->pdev, tx_pkt->mem.phys_base)) { IPAERR("failed to do dma map.\n"); + result = -EFAULT; goto failure_dma_map; } @@ -421,6 +425,7 @@ int ipa3_send(struct ipa3_sys_context *sys, gsi_xfer, true); if (result != GSI_STATUS_SUCCESS) { IPAERR("GSI xfer failed.\n"); + result = -EFAULT; goto failure; } @@ -472,7 +477,7 @@ int ipa3_send(struct ipa3_sys_context *sys, } spin_unlock_bh(&sys->spinlock); - return -EFAULT; + return result; } /** @@ -645,7 +650,6 @@ int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout) atomic_set(&comp->cnt, 2); sys = ipa3_ctx->ep[ep_idx].sys; - IPA_ACTIVE_CLIENTS_INC_SIMPLE(); if (num_desc == 1) { if (descr->callback || descr->user1) @@ -684,7 +688,6 @@ int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout) kfree(comp); bail: - IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return result; } @@ -3626,6 +3629,11 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, dma_addr_t dma_addr; dma_addr_t evt_dma_addr; int result; + gfp_t mem_flag = GFP_KERNEL; + + if (in->client == IPA_CLIENT_APPS_WAN_CONS || + in->client == IPA_CLIENT_APPS_WAN_PROD) + mem_flag = GFP_ATOMIC; if (!ep) { IPAERR("EP context is empty\n"); @@ -3663,7 +3671,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, gsi_evt_ring_props.ring_base_vaddr = dma_alloc_coherent(ipa3_ctx->pdev, gsi_evt_ring_props.ring_len, - &evt_dma_addr, GFP_KERNEL); + &evt_dma_addr, mem_flag); if (!gsi_evt_ring_props.ring_base_vaddr) { IPAERR("fail to dma alloc %u bytes\n", gsi_evt_ring_props.ring_len); @@ -3738,7 +3746,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, gsi_channel_props.ring_len = 2 * in->desc_fifo_sz; gsi_channel_props.ring_base_vaddr = dma_alloc_coherent(ipa3_ctx->pdev, gsi_channel_props.ring_len, - &dma_addr, GFP_KERNEL); + &dma_addr, mem_flag); if (!gsi_channel_props.ring_base_vaddr) { IPAERR("fail to dma alloc %u bytes\n", gsi_channel_props.ring_len); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c index 65fa40b2681b85fdfe5bd4b1aa18f91db788fd02..79cec5ae5f4c928b213cb75cb8476b8299e0ca1e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -24,12 +24,63 @@ int ipa_hw_stats_init(void) { + int ret = 0, ep_index; + struct ipa_teth_stats_endpoints *teth_stats_init; + if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) return 0; /* initialize stats here */ ipa3_ctx->hw_stats.enabled = true; - return 0; + + teth_stats_init = kzalloc(sizeof(*teth_stats_init), GFP_KERNEL); + if (!teth_stats_init) { + IPAERR("mem allocated failed!\n"); + return -ENOMEM; + } + /* enable prod mask */ + teth_stats_init->prod_mask = ( + IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_PROD) | + IPA_CLIENT_BIT_32(IPA_CLIENT_USB_PROD) | + IPA_CLIENT_BIT_32(IPA_CLIENT_WLAN1_PROD)); + + if (IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_PROD)) { + ep_index = ipa3_get_ep_mapping(IPA_CLIENT_Q6_WAN_PROD); + if (ep_index == -1) { + IPAERR("Invalid client.\n"); + kfree(teth_stats_init); + return -EINVAL; + } + teth_stats_init->dst_ep_mask[ep_index] = + (IPA_CLIENT_BIT_32(IPA_CLIENT_WLAN1_CONS) | + IPA_CLIENT_BIT_32(IPA_CLIENT_USB_CONS)); + } + + if (IPA_CLIENT_BIT_32(IPA_CLIENT_USB_PROD)) { + ep_index = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD); + if (ep_index == -1) { + IPAERR("Invalid client.\n"); + kfree(teth_stats_init); + return -EINVAL; + } + teth_stats_init->dst_ep_mask[ep_index] = + IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_CONS); + } + + if (IPA_CLIENT_BIT_32(IPA_CLIENT_WLAN1_PROD)) { + ep_index = ipa3_get_ep_mapping(IPA_CLIENT_WLAN1_PROD); + if (ep_index == -1) { + IPAERR("Invalid client.\n"); + kfree(teth_stats_init); + return -EINVAL; + } + teth_stats_init->dst_ep_mask[ep_index] = + IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_CONS); + } + + ret = ipa_init_teth_stats(teth_stats_init); + kfree(teth_stats_init); + return ret; } int ipa_init_quota_stats(u32 pipe_bitmask) @@ -347,9 +398,12 @@ int ipa_init_teth_stats(struct ipa_teth_stats_endpoints *in) /* reset driver's cache */ memset(&ipa3_ctx->hw_stats.teth.init, 0, sizeof(ipa3_ctx->hw_stats.teth.init)); - for (i = 0; i < IPA_CLIENT_MAX; i++) + for (i = 0; i < IPA_CLIENT_MAX; i++) { + memset(&ipa3_ctx->hw_stats.teth.prod_stats_sum[i], 0, + sizeof(ipa3_ctx->hw_stats.teth.prod_stats_sum[i])); memset(&ipa3_ctx->hw_stats.teth.prod_stats[i], 0, sizeof(ipa3_ctx->hw_stats.teth.prod_stats[i])); + } ipa3_ctx->hw_stats.teth.init.prod_bitmask = in->prod_mask; memcpy(ipa3_ctx->hw_stats.teth.init.cons_bitmask, in->dst_ep_mask, sizeof(ipa3_ctx->hw_stats.teth.init.cons_bitmask)); @@ -457,8 +511,7 @@ int ipa_init_teth_stats(struct ipa_teth_stats_endpoints *in) return ret; } -int ipa_get_teth_stats(enum ipa_client_type prod, - struct ipa_quota_stats_all *out) +int ipa_get_teth_stats(void) { int i, j; int ret; @@ -468,16 +521,17 @@ int ipa_get_teth_stats(enum ipa_client_type prod, struct ipahal_imm_cmd_pyld *cmd_pyld; struct ipa_mem_buffer mem; struct ipa3_desc desc = { 0 }; - struct ipahal_stats_tethering_all *stats; + struct ipahal_stats_tethering_all *stats_all; + struct ipa_hw_stats_teth *sw_stats = &ipa3_ctx->hw_stats.teth; + struct ipahal_stats_tethering *stats; + struct ipa_quota_stats *quota_stats; + struct ipahal_stats_init_tethering *init = + (struct ipahal_stats_init_tethering *) + &ipa3_ctx->hw_stats.teth.init; if (!ipa3_ctx->hw_stats.enabled) return 0; - if (!IPA_CLIENT_IS_PROD(prod) || ipa3_get_ep_mapping(prod) == -1) { - IPAERR("invalid prod %d\n", prod); - return -EINVAL; - } - get_offset.init = ipa3_ctx->hw_stats.teth.init; ret = ipahal_stats_get_offset(IPAHAL_HW_STATS_TETHERING, &get_offset, &offset); @@ -524,20 +578,26 @@ int ipa_get_teth_stats(enum ipa_client_type prod, goto destroy_imm; } - stats = kzalloc(sizeof(*stats), GFP_KERNEL); - if (!stats) { + stats_all = kzalloc(sizeof(*stats_all), GFP_KERNEL); + if (!stats_all) { IPADBG("failed to alloc memory\n"); ret = -ENOMEM; goto destroy_imm; } ret = ipahal_parse_stats(IPAHAL_HW_STATS_TETHERING, - &ipa3_ctx->hw_stats.teth.init, mem.base, stats); + &ipa3_ctx->hw_stats.teth.init, mem.base, stats_all); if (ret) { - IPAERR("failed to parse stats (error %d)\n", ret); + IPAERR("failed to parse stats_all (error %d)\n", ret); goto free_stats; } + /* reset prod_stats cache */ + for (i = 0; i < IPA_CLIENT_MAX; i++) { + memset(&ipa3_ctx->hw_stats.teth.prod_stats[i], 0, + sizeof(ipa3_ctx->hw_stats.teth.prod_stats[i])); + } + /* * update driver cache. * the stats were read from hardware with clear_after_read meaning @@ -545,8 +605,6 @@ int ipa_get_teth_stats(enum ipa_client_type prod, */ for (i = 0; i < IPA_CLIENT_MAX; i++) { for (j = 0; j < IPA_CLIENT_MAX; j++) { - struct ipa_hw_stats_teth *sw_stats = - &ipa3_ctx->hw_stats.teth; int prod_idx = ipa3_get_ep_mapping(i); int cons_idx = ipa3_get_ep_mapping(j); @@ -556,32 +614,53 @@ int ipa_get_teth_stats(enum ipa_client_type prod, if (cons_idx == -1 || cons_idx >= IPA3_MAX_NUM_PIPES) continue; - if (ipa3_ctx->ep[prod_idx].client != i || - ipa3_ctx->ep[cons_idx].client != j) - continue; - - sw_stats->prod_stats[i].client[j].num_ipv4_bytes += - stats->stats[prod_idx][cons_idx].num_ipv4_bytes; - sw_stats->prod_stats[i].client[j].num_ipv4_pkts += - stats->stats[prod_idx][cons_idx].num_ipv4_pkts; - sw_stats->prod_stats[i].client[j].num_ipv6_bytes += - stats->stats[prod_idx][cons_idx].num_ipv6_bytes; - sw_stats->prod_stats[i].client[j].num_ipv6_pkts += - stats->stats[prod_idx][cons_idx].num_ipv6_pkts; + /* save hw-query result */ + if ((init->prod_bitmask & (1 << prod_idx)) && + (init->cons_bitmask[prod_idx] + & (1 << cons_idx))) { + IPADBG_LOW("prod %d cons %d\n", + prod_idx, cons_idx); + stats = &stats_all->stats[prod_idx][cons_idx]; + IPADBG_LOW("num_ipv4_bytes %lld\n", + stats->num_ipv4_bytes); + IPADBG_LOW("num_ipv4_pkts %lld\n", + stats->num_ipv4_pkts); + IPADBG_LOW("num_ipv6_pkts %lld\n", + stats->num_ipv6_pkts); + IPADBG_LOW("num_ipv6_bytes %lld\n", + stats->num_ipv6_bytes); + + /* update stats*/ + quota_stats = + &sw_stats->prod_stats[i].client[j]; + quota_stats->num_ipv4_bytes = + stats->num_ipv4_bytes; + quota_stats->num_ipv4_pkts = + stats->num_ipv4_pkts; + quota_stats->num_ipv6_bytes = + stats->num_ipv6_bytes; + quota_stats->num_ipv6_pkts = + stats->num_ipv6_pkts; + + /* Accumulated stats */ + quota_stats = + &sw_stats->prod_stats_sum[i].client[j]; + quota_stats->num_ipv4_bytes += + stats->num_ipv4_bytes; + quota_stats->num_ipv4_pkts += + stats->num_ipv4_pkts; + quota_stats->num_ipv6_bytes += + stats->num_ipv6_bytes; + quota_stats->num_ipv6_pkts += + stats->num_ipv6_pkts; + } } } - if (!out) { - ret = 0; - goto free_stats; - } - - /* copy results to out parameter */ - *out = ipa3_ctx->hw_stats.teth.prod_stats[prod]; - ret = 0; free_stats: - kfree(stats); + kfree(stats_all); + stats = NULL; destroy_imm: ipahal_destroy_imm_cmd(cmd_pyld); free_dma_mem: @@ -590,6 +669,22 @@ int ipa_get_teth_stats(enum ipa_client_type prod, } +int ipa_query_teth_stats(enum ipa_client_type prod, + struct ipa_quota_stats_all *out, bool reset) +{ + if (!IPA_CLIENT_IS_PROD(prod) || ipa3_get_ep_mapping(prod) == -1) { + IPAERR("invalid prod %d\n", prod); + return -EINVAL; + } + + /* copy results to out parameter */ + if (reset) + *out = ipa3_ctx->hw_stats.teth.prod_stats[prod]; + else + *out = ipa3_ctx->hw_stats.teth.prod_stats_sum[prod]; + return 0; +} + int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons) { int ret; @@ -604,14 +699,14 @@ int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons) } /* reading stats will reset them in hardware */ - ret = ipa_get_teth_stats(prod, NULL); + ret = ipa_get_teth_stats(); if (ret) { IPAERR("ipa_get_teth_stats failed %d\n", ret); return ret; } /* reset driver's cache */ - stats = &ipa3_ctx->hw_stats.teth.prod_stats[prod].client[cons]; + stats = &ipa3_ctx->hw_stats.teth.prod_stats_sum[prod].client[cons]; memset(stats, 0, sizeof(*stats)); return 0; } @@ -631,7 +726,7 @@ int ipa_reset_all_cons_teth_stats(enum ipa_client_type prod) } /* reading stats will reset them in hardware */ - ret = ipa_get_teth_stats(prod, NULL); + ret = ipa_get_teth_stats(); if (ret) { IPAERR("ipa_get_teth_stats failed %d\n", ret); return ret; @@ -639,7 +734,7 @@ int ipa_reset_all_cons_teth_stats(enum ipa_client_type prod) /* reset driver's cache */ for (i = 0; i < IPA_CLIENT_MAX; i++) { - stats = &ipa3_ctx->hw_stats.teth.prod_stats[prod].client[i]; + stats = &ipa3_ctx->hw_stats.teth.prod_stats_sum[prod].client[i]; memset(stats, 0, sizeof(*stats)); } @@ -658,7 +753,7 @@ int ipa_reset_all_teth_stats(void) /* reading stats will reset them in hardware */ for (i = 0; i < IPA_CLIENT_MAX; i++) { if (IPA_CLIENT_IS_PROD(i) && ipa3_get_ep_mapping(i) != -1) { - ret = ipa_get_teth_stats(i, NULL); + ret = ipa_get_teth_stats(); if (ret) { IPAERR("ipa_get_teth_stats failed %d\n", ret); return ret; @@ -670,7 +765,7 @@ int ipa_reset_all_teth_stats(void) /* reset driver's cache */ for (i = 0; i < IPA_CLIENT_MAX; i++) { - stats = &ipa3_ctx->hw_stats.teth.prod_stats[i]; + stats = &ipa3_ctx->hw_stats.teth.prod_stats_sum[i]; memset(stats, 0, sizeof(*stats)); } @@ -1564,7 +1659,7 @@ static ssize_t ipa_debugfs_print_tethering_stats(struct file *file, (1 << ep_idx))) continue; - res = ipa_get_teth_stats(i, out); + res = ipa_get_teth_stats(); if (res) { mutex_unlock(&ipa3_ctx->lock); kfree(out); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 615b701ac387f73f44c838ebd8a191cb2268591a..8a39857d0b675892929ac1b36fb83737dd459873 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1092,6 +1092,8 @@ struct ipa3_smp2p_info { u32 in_base_id; bool ipa_clk_on; bool res_sent; + unsigned int smem_bit; + struct qcom_smem_state *smem_state; }; /** @@ -1141,6 +1143,7 @@ struct ipa_hw_stats_quota { struct ipa_hw_stats_teth { struct ipahal_stats_init_tethering init; + struct ipa_quota_stats_all prod_stats_sum[IPA_CLIENT_MAX]; struct ipa_quota_stats_all prod_stats[IPA_CLIENT_MAX]; }; @@ -1436,6 +1439,7 @@ struct ipa3_plat_drv_res { bool gsi_ch20_wa; bool tethered_flow_control; u32 mhi_evid_limits[2]; /* start and end values */ + bool ipa_mhi_dynamic_config; u32 ipa_tz_unlock_reg_num; struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg; bool use_ipa_pm; @@ -2132,6 +2136,7 @@ int __ipa3_release_hdr(u32 hdr_hdl); int __ipa3_release_hdr_proc_ctx(u32 proc_ctx_hdl); int _ipa_read_ep_reg_v3_0(char *buf, int max_len, int pipe); int _ipa_read_ep_reg_v4_0(char *buf, int max_len, int pipe); +int _ipa_read_ipahal_regs(void); void _ipa_enable_clks_v3_0(void); void _ipa_disable_clks_v3_0(void); struct device *ipa3_get_dma_dev(void); @@ -2235,6 +2240,7 @@ int ipa3_uc_mhi_resume_channel(int channelHandle, bool LPTransitionRejected); int ipa3_uc_mhi_stop_event_update_channel(int channelHandle); int ipa3_uc_mhi_print_stats(char *dbg_buff, int size); int ipa3_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len); +int ipa3_uc_send_remote_ipa_info(u32 remote_addr, uint32_t mbox_n); void ipa3_tag_destroy_imm(void *user1, int user2); const struct ipa_gsi_ep_config *ipa3_get_gsi_ep_info (enum ipa_client_type client); @@ -2276,8 +2282,10 @@ int ipa_reset_all_drop_stats(void); int ipa_init_teth_stats(struct ipa_teth_stats_endpoints *in); -int ipa_get_teth_stats(enum ipa_client_type prod, - struct ipa_quota_stats_all *out); +int ipa_get_teth_stats(void); + +int ipa_query_teth_stats(enum ipa_client_type prod, + struct ipa_quota_stats_all *out, bool reset); int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons); @@ -2356,4 +2364,5 @@ void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys); int ipa3_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs); void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc, struct ipahal_imm_cmd_pyld *cmd_pyld); +int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res); #endif /* _IPA3_I_H_ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi_proxy.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi_proxy.c new file mode 100644 index 0000000000000000000000000000000000000000..e05c0232f00be05c34af50d387619463842afa65 --- /dev/null +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi_proxy.c @@ -0,0 +1,995 @@ +/* Copyright (c) 2018 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 +#include +#include +#include +#include +#include +#include "ipa_qmi_service.h" +#include "../ipa_common_i.h" +#include "ipa_i.h" + +#define IMP_DRV_NAME "ipa_mhi_proxy" + +#define IMP_DBG(fmt, args...) \ + do { \ + pr_debug(IMP_DRV_NAME " %s:%d " fmt, \ + __func__, __LINE__, ## args); \ + IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \ + IMP_DRV_NAME " %s:%d " fmt, ## args); \ + IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ + IMP_DRV_NAME " %s:%d " fmt, ## args); \ + } while (0) + +#define IMP_DBG_LOW(fmt, args...) \ + do { \ + pr_debug(IMP_DRV_NAME " %s:%d " fmt, \ + __func__, __LINE__, ## args); \ + IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ + IMP_DRV_NAME " %s:%d " fmt, ## args); \ + } while (0) + + +#define IMP_ERR(fmt, args...) \ + do { \ + pr_err(IMP_DRV_NAME " %s:%d " fmt, \ + __func__, __LINE__, ## args); \ + IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \ + IMP_DRV_NAME " %s:%d " fmt, ## args); \ + IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ + IMP_DRV_NAME " %s:%d " fmt, ## args); \ + } while (0) + + +#define IMP_FUNC_ENTRY() \ + IMP_DBG_LOW("ENTRY\n") +#define IMP_FUNC_EXIT() \ + IMP_DBG_LOW("EXIT\n") + +#define IMP_IPA_UC_UL_CH_n 0 +#define IMP_IPA_UC_UL_EV_n 1 +#define IMP_IPA_UC_DL_CH_n 2 +#define IMP_IPA_UC_DL_EV_n 3 +#define IMP_IPA_UC_m 1 + +/* each pair of UL/DL channels are defined below */ +static const struct mhi_device_id mhi_driver_match_table[] = { + { .chan = "IP_HW_OFFLOAD_0" }, + {}, +}; + +static int imp_mhi_probe_cb(struct mhi_device *, const struct mhi_device_id *); +static void imp_mhi_remove_cb(struct mhi_device *); +static void imp_mhi_status_cb(struct mhi_device *, enum MHI_CB); + +static struct mhi_driver mhi_driver = { + .id_table = mhi_driver_match_table, + .probe = imp_mhi_probe_cb, + .remove = imp_mhi_remove_cb, + .status_cb = imp_mhi_status_cb, + .driver = { + .name = IMP_DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +struct imp_channel_context_type { + u32 chstate:8; + u32 brsmode:2; + u32 pollcfg:6; + u32 reserved:16; + + u32 chtype; + + u32 erindex; + + u64 rbase; + + u64 rlen; + + u64 rpp; + + u64 wpp; +} __packed; + +struct imp_event_context_type { + u32 reserved:8; + u32 intmodc:8; + u32 intmodt:16; + + u32 ertype; + + u32 msivec; + + u64 rbase; + + u64 rlen; + + u64 rpp; + + u64 wpp; +} __packed; + +struct imp_iova_addr { + dma_addr_t base; + unsigned int size; +}; + +struct imp_dev_info { + struct platform_device *pdev; + bool smmu_enabled; + struct imp_iova_addr ctrl; + struct imp_iova_addr data; + u32 chdb_base; + u32 erdb_base; +}; + +struct imp_event_props { + u16 id; + phys_addr_t doorbell; + u16 uc_mbox_n; + struct imp_event_context_type ev_ctx; +}; + +struct imp_event { + struct imp_event_props props; +}; + +struct imp_channel_props { + enum dma_data_direction dir; + u16 id; + phys_addr_t doorbell; + u16 uc_mbox_n; + struct imp_channel_context_type ch_ctx; + +}; + +struct imp_channel { + struct imp_channel_props props; + struct imp_event event; +}; + +enum imp_state { + IMP_INVALID = 0, + IMP_PROBED, + IMP_READY, + IMP_STARTED +}; + +struct imp_qmi_cache { + struct ipa_mhi_ready_indication_msg_v01 ready_ind; + struct ipa_mhi_alloc_channel_req_msg_v01 alloc_ch_req; + struct ipa_mhi_alloc_channel_resp_msg_v01 alloc_ch_resp; +}; + +struct imp_mhi_driver { + struct mhi_device *mhi_dev; + struct imp_channel ul_chan; + struct imp_channel dl_chan; +}; + +struct imp_context { + struct imp_dev_info dev_info; + struct imp_mhi_driver md; + struct mutex mutex; + enum imp_state state; + bool in_lpm; + bool lpm_disabled; + struct imp_qmi_cache qmi; + +}; + +static struct imp_context *imp_ctx; + +static void _populate_smmu_info(struct ipa_mhi_ready_indication_msg_v01 *req) +{ + req->smmu_info_valid = true; + req->smmu_info.iova_ctl_base_addr = imp_ctx->dev_info.ctrl.base; + req->smmu_info.iova_ctl_size = imp_ctx->dev_info.ctrl.size; + req->smmu_info.iova_data_base_addr = imp_ctx->dev_info.data.base; + req->smmu_info.iova_data_size = imp_ctx->dev_info.data.size; +} + +static void imp_mhi_trigger_ready_ind(void) +{ + struct ipa_mhi_ready_indication_msg_v01 *req + = &imp_ctx->qmi.ready_ind; + int ret; + struct imp_channel *ch; + struct ipa_mhi_ch_init_info_type_v01 *ch_info; + + IMP_FUNC_ENTRY(); + if (imp_ctx->state != IMP_PROBED) { + IMP_ERR("invalid state %d\n", imp_ctx->state); + goto exit; + } + + if (imp_ctx->dev_info.smmu_enabled) + _populate_smmu_info(req); + + req->ch_info_arr_len = 0; + BUILD_BUG_ON(QMI_IPA_REMOTE_MHI_CHANNELS_NUM_MAX_V01 < 2); + + /* UL channel */ + ch = &imp_ctx->md.ul_chan; + ch_info = &req->ch_info_arr[req->ch_info_arr_len]; + + ch_info->ch_id = ch->props.id; + ch_info->direction_type = ch->props.dir; + ch_info->er_id = ch->event.props.id; + + /* uC is a doorbell proxy between local Q6 and remote Q6 */ + ch_info->ch_doorbell_addr = ipa3_ctx->ipa_wrapper_base + + ipahal_get_reg_base() + + ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n, + IMP_IPA_UC_m, + ch->props.uc_mbox_n); + + ch_info->er_doorbell_addr = ipa3_ctx->ipa_wrapper_base + + ipahal_get_reg_base() + + ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n, + IMP_IPA_UC_m, + ch->event.props.uc_mbox_n); + req->ch_info_arr_len++; + + /* DL channel */ + ch = &imp_ctx->md.dl_chan; + ch_info = &req->ch_info_arr[req->ch_info_arr_len]; + + ch_info->ch_id = ch->props.id; + ch_info->direction_type = ch->props.dir; + ch_info->er_id = ch->event.props.id; + + /* uC is a doorbell proxy between local Q6 and remote Q6 */ + ch_info->ch_doorbell_addr = ipa3_ctx->ipa_wrapper_base + + ipahal_get_reg_base() + + ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n, + IMP_IPA_UC_m, + ch->props.uc_mbox_n); + + ch_info->er_doorbell_addr = ipa3_ctx->ipa_wrapper_base + + ipahal_get_reg_base() + + ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n, + IMP_IPA_UC_m, + ch->event.props.uc_mbox_n); + req->ch_info_arr_len++; + + IMP_DBG("sending IND to modem\n"); + ret = ipa3_qmi_send_mhi_ready_indication(req); + if (ret) { + IMP_ERR("failed to send ready indication to modem %d\n", ret); + return; + } + + imp_ctx->state = IMP_READY; + +exit: + IMP_FUNC_EXIT(); +} + +static struct imp_channel *imp_get_ch_by_id(u16 id) +{ + if (imp_ctx->md.ul_chan.props.id == id) + return &imp_ctx->md.ul_chan; + + if (imp_ctx->md.dl_chan.props.id == id) + return &imp_ctx->md.dl_chan; + + return NULL; +} + +static struct ipa_mhi_er_info_type_v01 * + _find_ch_in_er_info_arr(struct ipa_mhi_alloc_channel_req_msg_v01 *req, + u16 id) +{ + int i; + + if (req->er_info_arr_len > QMI_IPA_REMOTE_MHI_CHANNELS_NUM_MAX_V01) + return NULL; + + for (i = 0; i < req->tr_info_arr_len; i++) + if (req->er_info_arr[i].er_id == id) + return &req->er_info_arr[i]; + return NULL; +} + +/* round addresses for closest page per SMMU requirements */ +static inline void imp_smmu_round_to_page(uint64_t iova, uint64_t pa, + uint64_t size, unsigned long *iova_p, phys_addr_t *pa_p, u32 *size_p) +{ + *iova_p = rounddown(iova, PAGE_SIZE); + *pa_p = rounddown(pa, PAGE_SIZE); + *size_p = roundup(size + pa - *pa_p, PAGE_SIZE); +} + +static void __map_smmu_info(struct device *dev, + struct imp_iova_addr *partition, int num_mapping, + struct ipa_mhi_mem_addr_info_type_v01 *map_info, + bool map) +{ + int i; + struct iommu_domain *domain; + unsigned long iova_p; + phys_addr_t pa_p; + u32 size_p; + + domain = iommu_get_domain_for_dev(dev); + if (!domain) { + IMP_ERR("domain is NULL for dev\n"); + return; + } + + for (i = 0; i < num_mapping; i++) { + imp_smmu_round_to_page(map_info[i].iova, map_info[i].pa, + map_info[i].size, &iova_p, &pa_p, &size_p); + + if (map) { + /* boundary check */ + WARN_ON(partition->base > iova_p || + (partition->base + partition->size) < + (iova_p + size_p)); + + IMP_DBG("mapping 0x%lx to 0x%pa size %d\n", + iova_p, &pa_p, size_p); + iommu_map(domain, + iova_p, pa_p, size_p, + IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO); + } else { + IMP_DBG("unmapping 0x%lx to 0x%pa size %d\n", + iova_p, &pa_p, size_p); + iommu_unmap(domain, iova_p, size_p); + } + } +} + +static int __imp_configure_mhi_device( + struct ipa_mhi_alloc_channel_req_msg_v01 *req, + struct ipa_mhi_alloc_channel_resp_msg_v01 *resp) +{ + struct mhi_buf ch_config[2]; + int i; + struct ipa_mhi_er_info_type_v01 *er_info; + struct imp_channel *ch; + int ridx = 0; + int ret; + + IMP_FUNC_ENTRY(); + + /* configure MHI */ + for (i = 0; i < req->tr_info_arr_len; i++) { + ch = imp_get_ch_by_id(req->tr_info_arr[i].ch_id); + if (!ch) { + IMP_ERR("unknown channel %d\n", + req->tr_info_arr[i].ch_id); + resp->alloc_resp_arr[ridx].ch_id = + req->tr_info_arr[i].ch_id; + resp->alloc_resp_arr[ridx].is_success = 0; + ridx++; + resp->alloc_resp_arr_len = ridx; + resp->resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp->resp.error = IPA_QMI_ERR_INVALID_ID_V01; + return -EINVAL; + } + + /* populate CCA */ + if (req->tr_info_arr[i].brst_mode_type == + QMI_IPA_BURST_MODE_ENABLED_V01) + ch->props.ch_ctx.brsmode = 3; + else if (req->tr_info_arr[i].brst_mode_type == + QMI_IPA_BURST_MODE_DISABLED_V01) + ch->props.ch_ctx.brsmode = 2; + else + ch->props.ch_ctx.brsmode = 0; + + ch->props.ch_ctx.pollcfg = req->tr_info_arr[i].poll_cfg; + ch->props.ch_ctx.chtype = ch->props.dir; + ch->props.ch_ctx.erindex = ch->event.props.id; + ch->props.ch_ctx.rbase = req->tr_info_arr[i].ring_iova; + ch->props.ch_ctx.rlen = req->tr_info_arr[i].ring_len; + ch->props.ch_ctx.rpp = req->tr_info_arr[i].rp; + ch->props.ch_ctx.wpp = req->tr_info_arr[i].wp; + + ch_config[0].buf = &ch->props.ch_ctx; + ch_config[0].len = sizeof(ch->props.ch_ctx); + ch_config[0].name = "CCA"; + + /* populate ECA */ + er_info = _find_ch_in_er_info_arr(req, ch->event.props.id); + if (!er_info) { + IMP_ERR("no event ring for ch %d\n", + req->tr_info_arr[i].ch_id); + resp->alloc_resp_arr[ridx].ch_id = + req->tr_info_arr[i].ch_id; + resp->alloc_resp_arr[ridx].is_success = 0; + ridx++; + resp->alloc_resp_arr_len = ridx; + resp->resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp->resp.error = IPA_QMI_ERR_INTERNAL_V01; + return -EINVAL; + } + + ch->event.props.ev_ctx.intmodc = er_info->intmod_count; + ch->event.props.ev_ctx.intmodt = er_info->intmod_cycles; + ch->event.props.ev_ctx.ertype = 1; + ch->event.props.ev_ctx.msivec = er_info->msi_addr; + ch->event.props.ev_ctx.rbase = er_info->ring_iova; + ch->event.props.ev_ctx.rlen = er_info->ring_len; + ch->event.props.ev_ctx.rpp = er_info->rp; + ch->event.props.ev_ctx.wpp = er_info->wp; + ch_config[1].buf = &ch->event.props.ev_ctx; + ch_config[1].len = sizeof(ch->event.props.ev_ctx); + ch_config[1].name = "ECA"; + + IMP_DBG("Configuring MHI device for ch %d\n", ch->props.id); + ret = mhi_device_configure(imp_ctx->md.mhi_dev, ch->props.dir, + ch_config, 2); + if (ret) { + IMP_ERR("mhi_device_configure failed for ch %d\n", + req->tr_info_arr[i].ch_id); + resp->alloc_resp_arr[ridx].ch_id = + req->tr_info_arr[i].ch_id; + resp->alloc_resp_arr[ridx].is_success = 0; + ridx++; + resp->alloc_resp_arr_len = ridx; + resp->resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp->resp.error = IPA_QMI_ERR_INTERNAL_V01; + return -EINVAL; + } + } + + IMP_FUNC_EXIT(); + + return 0; +} + +/** + * imp_handle_allocate_channel_req() - Allocate a new MHI channel + * + * Allocates MHI channel and start them. + * + * Return: QMI return codes + */ +struct ipa_mhi_alloc_channel_resp_msg_v01 *imp_handle_allocate_channel_req( + struct ipa_mhi_alloc_channel_req_msg_v01 *req) +{ + int ret; + struct ipa_mhi_alloc_channel_resp_msg_v01 *resp = + &imp_ctx->qmi.alloc_ch_resp; + + IMP_FUNC_ENTRY(); + + memset(resp, 0, sizeof(*resp)); + + if (imp_ctx->state != IMP_READY) { + IMP_ERR("invalid state %d\n", imp_ctx->state); + resp->resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp->resp.error = IPA_QMI_ERR_INCOMPATIBLE_STATE_V01; + return resp; + } + + /* cache the req */ + memcpy(&imp_ctx->qmi.alloc_ch_req, req, sizeof(*req)); + + if (req->tr_info_arr_len > QMI_IPA_REMOTE_MHI_CHANNELS_NUM_MAX_V01) { + IMP_ERR("invalid tr_info_arr_len %d\n", req->tr_info_arr_len); + resp->resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp->resp.error = IPA_QMI_ERR_NO_MEMORY_V01; + return resp; + } + + if ((req->ctrl_addr_map_info_len == 0 || + req->data_addr_map_info_len == 0) && + imp_ctx->dev_info.smmu_enabled) { + IMP_ERR("no mapping provided, but smmu is enabled\n"); + resp->resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp->resp.error = IPA_QMI_ERR_INTERNAL_V01; + return resp; + } + + mutex_lock(&imp_ctx->mutex); + + if (imp_ctx->dev_info.smmu_enabled) { + /* map CTRL */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.ctrl, + req->ctrl_addr_map_info_len, + req->ctrl_addr_map_info, + true); + + /* map DATA */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.data, + req->data_addr_map_info_len, + req->data_addr_map_info, + true); + } + + resp->alloc_resp_arr_valid = true; + ret = __imp_configure_mhi_device(req, resp); + if (ret) + goto fail_smmu; + + IMP_DBG("Starting MHI channels %d and %d\n", + imp_ctx->md.ul_chan.props.id, + imp_ctx->md.dl_chan.props.id); + ret = mhi_prepare_for_transfer(imp_ctx->md.mhi_dev); + if (ret) { + IMP_ERR("mhi_prepare_for_transfer failed %d\n", ret); + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .ch_id = imp_ctx->md.ul_chan.props.id; + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .is_success = 0; + resp->alloc_resp_arr_len++; + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .ch_id = imp_ctx->md.dl_chan.props.id; + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .is_success = 0; + resp->alloc_resp_arr_len++; + resp->resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp->resp.error = IPA_QMI_ERR_INTERNAL_V01; + goto fail_smmu; + } + + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .ch_id = imp_ctx->md.ul_chan.props.id; + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .is_success = 1; + resp->alloc_resp_arr_len++; + + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .ch_id = imp_ctx->md.dl_chan.props.id; + resp->alloc_resp_arr[resp->alloc_resp_arr_len] + .is_success = 1; + resp->alloc_resp_arr_len++; + + imp_ctx->state = IMP_STARTED; + mutex_unlock(&imp_ctx->mutex); + IMP_FUNC_EXIT(); + + resp->resp.result = IPA_QMI_RESULT_SUCCESS_V01; + return resp; + +fail_smmu: + if (imp_ctx->dev_info.smmu_enabled) { + /* unmap CTRL */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.ctrl, + req->ctrl_addr_map_info_len, + req->ctrl_addr_map_info, + false); + + /* unmap DATA */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.data, + req->data_addr_map_info_len, + req->data_addr_map_info, + false); + } + mutex_unlock(&imp_ctx->mutex); + return resp; +} + +/** + * imp_handle_vote_req() - Votes for MHI / PCIe clocks + * + * Hold a vote to prevent / allow low power mode on MHI. + * + * Return: 0 on success, negative otherwise + */ +int imp_handle_vote_req(bool vote) +{ + int ret; + + IMP_DBG_LOW("vote %d\n", vote); + + mutex_lock(&imp_ctx->mutex); + if (imp_ctx->state != IMP_READY) { + IMP_ERR("unexpected vote when in state %d\n", imp_ctx->state); + mutex_unlock(&imp_ctx->mutex); + return -EPERM; + } + + if (vote == imp_ctx->lpm_disabled) { + IMP_ERR("already voted/devoted %d\n", vote); + mutex_unlock(&imp_ctx->mutex); + return -EPERM; + } + + if (vote) { + ret = mhi_device_get_sync(imp_ctx->md.mhi_dev); + if (ret) { + IMP_ERR("mhi_sync_get failed %d\n", ret); + mutex_unlock(&imp_ctx->mutex); + return ret; + } + imp_ctx->lpm_disabled = true; + } else { + mhi_device_put(imp_ctx->md.mhi_dev); + imp_ctx->lpm_disabled = false; + } + + return 0; +} + +static int imp_read_iova_from_dtsi(const char *node, struct imp_iova_addr *out) +{ + u32 iova_mapping[2]; + struct device_node *of_node = imp_ctx->dev_info.pdev->dev.of_node; + + if (of_property_read_u32_array(of_node, node, iova_mapping, 2)) { + IMP_DBG("failed to read of_node %s\n", node); + return -EINVAL; + } + + out->base = iova_mapping[0]; + out->size = iova_mapping[1]; + IMP_DBG("%s: base: 0x%pad size: 0x%x\n", node, &out->base, out->size); + + return 0; +} + +static void imp_mhi_shutdown(void) +{ + struct ipa_mhi_cleanup_req_msg_v01 req = { 0 }; + + IMP_FUNC_ENTRY(); + + if (imp_ctx->state == IMP_STARTED) { + req.cleanup_valid = true; + req.cleanup = true; + ipa3_qmi_send_mhi_cleanup_request(&req); + if (imp_ctx->dev_info.smmu_enabled) { + struct ipa_mhi_alloc_channel_req_msg_v01 *creq + = &imp_ctx->qmi.alloc_ch_req; + + /* unmap CTRL */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.ctrl, + creq->ctrl_addr_map_info_len, + creq->ctrl_addr_map_info, + false); + + /* unmap DATA */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.data, + creq->data_addr_map_info_len, + creq->data_addr_map_info, + false); + } + if (imp_ctx->lpm_disabled) { + mhi_device_put(imp_ctx->md.mhi_dev); + imp_ctx->lpm_disabled = false; + } + + } + imp_ctx->state = IMP_PROBED; + + IMP_FUNC_EXIT(); +} + +static int imp_mhi_probe_cb(struct mhi_device *mhi_dev, + const struct mhi_device_id *id) +{ + struct imp_channel *ch; + struct imp_event *ev; + int ret; + + IMP_FUNC_ENTRY(); + + if (id != &mhi_driver_match_table[0]) { + IMP_ERR("only chan=%s is supported for now\n", + mhi_driver_match_table[0].chan); + return -EPERM; + } + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + imp_ctx->md.mhi_dev = mhi_dev; + + mutex_lock(&imp_ctx->mutex); + /* store UL channel properties */ + ch = &imp_ctx->md.ul_chan; + ev = &imp_ctx->md.ul_chan.event; + + ch->props.id = mhi_dev->ul_chan_id; + ch->props.dir = DMA_TO_DEVICE; + ch->props.doorbell = imp_ctx->dev_info.chdb_base + ch->props.id * 8; + ch->props.uc_mbox_n = IMP_IPA_UC_UL_CH_n; + IMP_DBG("ul ch id %d doorbell 0x%pa uc_mbox_n %d\n", + ch->props.id, &ch->props.doorbell, ch->props.uc_mbox_n); + + ret = ipa3_uc_send_remote_ipa_info(ch->props.doorbell, + ch->props.uc_mbox_n); + if (ret) + goto fail; + IMP_DBG("mapped ch db 0x%pad to mbox %d\n", &ch->props.doorbell, + ch->props.uc_mbox_n); + + ev->props.id = mhi_dev->ul_event_id; + ev->props.doorbell = imp_ctx->dev_info.erdb_base + ev->props.id * 8; + ev->props.uc_mbox_n = IMP_IPA_UC_UL_EV_n; + IMP_DBG("allocated ev %d\n", ev->props.id); + + ret = ipa3_uc_send_remote_ipa_info(ev->props.doorbell, + ev->props.uc_mbox_n); + if (ret) + goto fail; + IMP_DBG("mapped ch db 0x%pad to mbox %d\n", &ev->props.doorbell, + ev->props.uc_mbox_n); + + /* store DL channel properties */ + ch = &imp_ctx->md.dl_chan; + ev = &imp_ctx->md.dl_chan.event; + + ch->props.dir = DMA_FROM_DEVICE; + ch->props.id = mhi_dev->dl_chan_id; + ch->props.doorbell = imp_ctx->dev_info.chdb_base + ch->props.id * 8; + ch->props.uc_mbox_n = IMP_IPA_UC_DL_CH_n; + IMP_DBG("dl ch id %d doorbell 0x%pa uc_mbox_n %d\n", + ch->props.id, &ch->props.doorbell, ch->props.uc_mbox_n); + + ret = ipa3_uc_send_remote_ipa_info(ch->props.doorbell, + ch->props.uc_mbox_n); + if (ret) + goto fail; + IMP_DBG("mapped ch db 0x%pad to mbox %d\n", &ch->props.doorbell, + ch->props.uc_mbox_n); + + ev->props.id = mhi_dev->dl_event_id; + ev->props.doorbell = imp_ctx->dev_info.erdb_base + ev->props.id * 8; + ev->props.uc_mbox_n = IMP_IPA_UC_DL_EV_n; + IMP_DBG("allocated ev %d\n", ev->props.id); + + ret = ipa3_uc_send_remote_ipa_info(ev->props.doorbell, + ev->props.uc_mbox_n); + if (ret) + goto fail; + IMP_DBG("mapped ch db 0x%pad to mbox %d\n", &ev->props.doorbell, + ev->props.uc_mbox_n); + + imp_mhi_trigger_ready_ind(); + + mutex_unlock(&imp_ctx->mutex); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + mhi_device_get_sync(imp_ctx->md.mhi_dev); + + + IMP_FUNC_EXIT(); + return 0; + +fail: + mutex_unlock(&imp_ctx->mutex); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return ret; +} + +static void imp_mhi_remove_cb(struct mhi_device *mhi_dev) +{ + IMP_FUNC_ENTRY(); + + mutex_lock(&imp_ctx->mutex); + imp_mhi_shutdown(); + mutex_unlock(&imp_ctx->mutex); + IMP_FUNC_EXIT(); +} + +static void imp_mhi_status_cb(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb) +{ + IMP_DBG("%d\n", mhi_cb); + + mutex_lock(&imp_ctx->mutex); + if (mhi_dev != imp_ctx->md.mhi_dev) { + IMP_DBG("ignoring secondary callbacks\n"); + mutex_unlock(&imp_ctx->mutex); + return; + } + + switch (mhi_cb) { + case MHI_CB_IDLE: + break; + case MHI_CB_LPM_ENTER: + if (imp_ctx->state == IMP_STARTED) { + if (!imp_ctx->in_lpm) { + IPA_ACTIVE_CLIENTS_DEC_SPECIAL("IMP"); + imp_ctx->in_lpm = true; + } else { + IMP_ERR("already in LPM\n"); + } + } + break; + case MHI_CB_LPM_EXIT: + if (imp_ctx->state == IMP_STARTED) { + if (imp_ctx->in_lpm) { + IPA_ACTIVE_CLIENTS_INC_SPECIAL("IMP"); + imp_ctx->in_lpm = false; + } else { + IMP_ERR("not in LPM\n"); + } + } + break; + + case MHI_CB_EE_RDDM: + case MHI_CB_PENDING_DATA: + IMP_ERR("unexpected event %d\n", mhi_cb); + break; + } + mutex_unlock(&imp_ctx->mutex); +} + +static int imp_probe(struct platform_device *pdev) +{ + int ret; + + IMP_FUNC_ENTRY(); + + if (ipa3_uc_state_check()) { + IMP_DBG("uC not ready yet\n"); + return -EPROBE_DEFER; + } + + imp_ctx->dev_info.pdev = pdev; + imp_ctx->dev_info.smmu_enabled = true; + ret = imp_read_iova_from_dtsi("qcom,ctrl-iova", + &imp_ctx->dev_info.ctrl); + if (ret) + imp_ctx->dev_info.smmu_enabled = false; + + ret = imp_read_iova_from_dtsi("qcom,data-iova", + &imp_ctx->dev_info.data); + if (ret) + imp_ctx->dev_info.smmu_enabled = false; + + IMP_DBG("smmu_enabled=%d\n", imp_ctx->dev_info.smmu_enabled); + + if (of_property_read_u32(pdev->dev.of_node, "qcom,mhi-chdb-base", + &imp_ctx->dev_info.chdb_base)) { + IMP_ERR("failed to read of_node %s\n", "qcom,mhi-chdb-base"); + return -EINVAL; + } + IMP_DBG("chdb-base=0x%x\n", imp_ctx->dev_info.chdb_base); + + if (of_property_read_u32(pdev->dev.of_node, "qcom,mhi-erdb-base", + &imp_ctx->dev_info.erdb_base)) { + IMP_ERR("failed to read of_node %s\n", "qcom,mhi-erdb-base"); + return -EINVAL; + } + IMP_DBG("erdb-base=0x%x\n", imp_ctx->dev_info.erdb_base); + + imp_ctx->state = IMP_PROBED; + ret = mhi_driver_register(&mhi_driver); + if (ret) { + IMP_ERR("mhi_driver_register failed %d\n", ret); + mutex_unlock(&imp_ctx->mutex); + return ret; + } + + IMP_FUNC_EXIT(); + return 0; +} + +static int imp_remove(struct platform_device *pdev) +{ + IMP_FUNC_ENTRY(); + mutex_lock(&imp_ctx->mutex); + mhi_driver_unregister(&mhi_driver); + + if (!imp_ctx->in_lpm) + IPA_ACTIVE_CLIENTS_DEC_SPECIAL("IMP"); + imp_ctx->in_lpm = false; + imp_ctx->lpm_disabled = false; + + imp_ctx->state = IMP_INVALID; + mutex_unlock(&imp_ctx->mutex); + + return 0; +} + +static const struct of_device_id imp_dt_match[] = { + { .compatible = "qcom,ipa-mhi-proxy" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imp_dt_match); + +static struct platform_driver ipa_mhi_proxy_driver = { + .driver = { + .name = "ipa_mhi_proxy", + .owner = THIS_MODULE, + .of_match_table = imp_dt_match, + }, + .probe = imp_probe, + .remove = imp_remove, +}; + +/** + * imp_handle_modem_ready() - Registers IMP as a platform device + * + * This function is called after modem is loaded and QMI handshake is done. + * IMP will register itself as a platform device, and on support device the + * probe function will get called. + * + * Return: None + */ +void imp_handle_modem_ready(void) +{ + + if (!imp_ctx) { + imp_ctx = kzalloc(sizeof(*imp_ctx), GFP_KERNEL); + if (!imp_ctx) + return; + + mutex_init(&imp_ctx->mutex); + } + + if (imp_ctx->state != IMP_INVALID) { + IMP_ERR("unexpected state %d\n", imp_ctx->state); + return; + } + + IMP_DBG("register platform device\n"); + platform_driver_register(&ipa_mhi_proxy_driver); +} + +/** + * imp_handle_modem_shutdown() - Handles modem SSR + * + * Performs MHI cleanup when modem is going to SSR (Subsystem Restart). + * + * Return: None + */ +void imp_handle_modem_shutdown(void) +{ + IMP_FUNC_ENTRY(); + + mutex_lock(&imp_ctx->mutex); + + if (imp_ctx->state == IMP_INVALID) { + mutex_unlock(&imp_ctx->mutex); + return; + } + if (imp_ctx->state == IMP_STARTED) { + mhi_unprepare_from_transfer(imp_ctx->md.mhi_dev); + imp_ctx->state = IMP_READY; + } + + if (imp_ctx->state == IMP_READY) { + if (imp_ctx->dev_info.smmu_enabled) { + struct ipa_mhi_alloc_channel_req_msg_v01 *creq + = &imp_ctx->qmi.alloc_ch_req; + + /* unmap CTRL */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.ctrl, + creq->ctrl_addr_map_info_len, + creq->ctrl_addr_map_info, + false); + + /* unmap DATA */ + __map_smmu_info(imp_ctx->md.mhi_dev->dev.parent, + &imp_ctx->dev_info.data, + creq->data_addr_map_info_len, + creq->data_addr_map_info, + false); + } + } + + imp_ctx->state = IMP_PROBED; + mutex_unlock(&imp_ctx->mutex); + + IMP_FUNC_EXIT(); + + platform_driver_unregister(&ipa_mhi_proxy_driver); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("IPA MHI Proxy Driver"); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi_proxy.h b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..3a1d97d188c8964727f6d7dd4a17322f35051913 --- /dev/null +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi_proxy.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2018 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 __IMP_H_ +#define __IMP_H_ + +#ifdef CONFIG_IPA3_MHI_PROXY + +#include "ipa_qmi_service.h" + +void imp_handle_modem_ready(void); + +struct ipa_mhi_alloc_channel_resp_msg_v01 *imp_handle_allocate_channel_req( + struct ipa_mhi_alloc_channel_req_msg_v01 *req); + +int imp_handle_vote_req(bool vote); + +#else /* CONFIG_IPA3_MHI_PROXY */ + +static inline void imp_handle_modem_ready(void) +{ + +} + +static inline struct ipa_mhi_alloc_channel_resp_msg_v01 + *imp_handle_allocate_channel_req( + struct ipa_mhi_alloc_channel_req_msg_v01 *req) +{ + return NULL; +} + +static inline int imp_handle_vote_req(bool vote) +{ + return -EPERM; +} + +#endif /* CONFIG_IPA3_MHI_PROXY */ + +#endif /* __IMP_H_ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c index 7e6442df4b500653e784b6b93d9e2a671b02f471..8f92e04e3caaf47ab7b3728fe70ddf7e9fca5763 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c @@ -80,6 +80,7 @@ static int ipa3_nat_ipv6ct_mmap(struct file *filp, struct vm_area_struct *vma) unsigned long vsize = vma->vm_end - vma->vm_start; unsigned long phys_addr; int result = 0; + struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_AP); IPADBG("\n"); @@ -111,8 +112,14 @@ static int ipa3_nat_ipv6ct_mmap(struct file *filp, struct vm_area_struct *vma) goto bail; } } + /* check if smmu enable & dma_coherent mode */ + if (!cb->valid || + !is_device_dma_coherent(cb->dev)) { + vma->vm_page_prot = + pgprot_noncached(vma->vm_page_prot); + IPADBG("App smmu enable in DMA mode\n"); + } - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); if (dev->is_sys_mem) { IPADBG("Mapping system memory\n"); IPADBG("map sz=0x%zx\n", dev->size); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index b2063d25b0c7a2e326555246cb52b910bca2c505..dfb94a683c286ebb7ddccaa8fd09262166f9ad88 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -23,6 +23,7 @@ #include #include "ipa_qmi_service.h" +#include "ipa_mhi_proxy.h" #define IPA_Q6_SVC_VERS 1 #define IPA_A5_SVC_VERS 1 @@ -35,6 +36,7 @@ #define QMI_SEND_STATS_REQ_TIMEOUT_MS 5000 #define QMI_SEND_REQ_TIMEOUT_MS 60000 +#define QMI_MHI_SEND_REQ_TIMEOUT_MS 1000 #define QMI_IPA_FORCE_CLEAR_DATAPATH_TIMEOUT_MS 1000 @@ -69,6 +71,9 @@ static void ipa3_handle_indication_req(struct qmi_handle *qmi_handle, indication_req = (struct ipa_indication_reg_req_msg_v01 *)decoded_msg; IPAWANDBG("Received INDICATION Request\n"); + /* cache the client sq */ + memcpy(&ipa3_qmi_ctx->client_sq, sq, sizeof(*sq)); + memset(&resp, 0, sizeof(struct ipa_indication_reg_resp_msg_v01)); resp.resp.result = IPA_QMI_RESULT_SUCCESS_V01; @@ -95,7 +100,7 @@ static void ipa3_handle_indication_req(struct qmi_handle *qmi_handle, IPA_QMI_RESULT_SUCCESS_V01; rc = qmi_send_indication(qmi_handle, - &(ipa3_qmi_ctx->ipa_q6_client_params.sq), + &(ipa3_qmi_ctx->client_sq), QMI_IPA_MASTER_DRIVER_INIT_COMPLETE_IND_V01, QMI_IPA_MASTER_DRIVER_INIT_COMPLETE_IND_MAX_MSG_LEN_V01, ipa3_master_driver_init_complt_ind_msg_data_v01_ei, @@ -261,6 +266,64 @@ static void ipa3_handle_modem_init_cmplt_req(struct qmi_handle *qmi_handle, IPAWANDBG("Sent QMI_IPA_INIT_MODEM_DRIVER_CMPLT_RESP_V01\n"); } +static void ipa3_handle_mhi_alloc_channel_req(struct qmi_handle *qmi_handle, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded_msg) +{ + struct ipa_mhi_alloc_channel_req_msg_v01 *ch_alloc_req; + struct ipa_mhi_alloc_channel_resp_msg_v01 *resp; + int rc; + + IPAWANDBG("Received QMI_IPA_MHI_ALLOC_CHANNEL_REQ_V01\n"); + ch_alloc_req = (struct ipa_mhi_alloc_channel_req_msg_v01 *)decoded_msg; + + resp = imp_handle_allocate_channel_req(ch_alloc_req); + + rc = qmi_send_response(qmi_handle, sq, txn, + QMI_IPA_MHI_ALLOC_CHANNEL_RESP_V01, + IPA_MHI_ALLOC_CHANNEL_RESP_MSG_V01_MAX_MSG_LEN, + ipa_mhi_alloc_channel_resp_msg_v01_ei, + resp); + + if (rc < 0) + IPAWANERR("QMI_IPA_MHI_ALLOC_CHANNEL_RESP_V01 failed\n"); + else + IPAWANDBG("Sent QMI_IPA_MHI_ALLOC_CHANNEL_RESP_V01\n"); +} + +static void ipa3_handle_mhi_vote_req(struct qmi_handle *qmi_handle, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded_msg) +{ + struct ipa_mhi_clk_vote_req_msg_v01 *vote_req; + struct ipa_mhi_clk_vote_resp_msg_v01 resp; + int rc; + + IPAWANDBG_LOW("Received QMI_IPA_MHI_CLK_VOTE_REQ_V01\n"); + vote_req = (struct ipa_mhi_clk_vote_req_msg_v01 *)decoded_msg; + + rc = imp_handle_vote_req(vote_req->mhi_vote); + if (rc) { + resp.resp.result = IPA_QMI_RESULT_FAILURE_V01; + resp.resp.error = IPA_QMI_ERR_INTERNAL_V01; + } else { + resp.resp.result = IPA_QMI_RESULT_SUCCESS_V01; + } + + rc = qmi_send_response(qmi_handle, sq, txn, + QMI_IPA_MHI_CLK_VOTE_RESP_V01, + IPA_MHI_CLK_VOTE_RESP_MSG_V01_MAX_MSG_LEN, + ipa_mhi_clk_vote_resp_msg_v01_ei, + &resp); + + if (rc < 0) + IPAWANERR("QMI_IPA_MHI_CLK_VOTE_RESP_V01 failed\n"); + else + IPAWANDBG("Sent QMI_IPA_MHI_CLK_VOTE_RESP_V01\n"); +} + static void ipa3_a5_svc_disconnect_cb(struct qmi_handle *qmi, unsigned int node, unsigned int port) { @@ -325,7 +388,7 @@ static int ipa3_qmi_send_req_wait(struct qmi_handle *client_handle, } ret = qmi_send_request(client_handle, - &ipa3_qmi_ctx->ipa_q6_client_params.sq, + &ipa3_qmi_ctx->server_sq, &txn, req_desc->msg_id, req_desc->max_msg_len, @@ -425,6 +488,20 @@ static int ipa3_qmi_init_modem_send_sync_msg(void) req.v6_hash_filter_tbl_start_addr = IPA_MEM_PART(v6_flt_hash_ofst) + smem_restr_bytes; + req.hw_stats_quota_base_addr_valid = true; + req.hw_stats_quota_base_addr = + IPA_MEM_PART(stats_quota_ofst) + smem_restr_bytes; + + req.hw_stats_quota_size_valid = true; + req.hw_stats_quota_size = IPA_MEM_PART(stats_quota_size); + + req.hw_drop_stats_base_addr_valid = true; + req.hw_drop_stats_base_addr = + IPA_MEM_PART(stats_drop_ofst) + smem_restr_bytes; + + req.hw_drop_stats_table_size_valid = true; + req.hw_drop_stats_table_size = IPA_MEM_PART(stats_drop_size); + if (!ipa3_uc_loaded_check()) { /* First time boot */ req.is_ssr_bootup_valid = false; req.is_ssr_bootup = 0; @@ -974,8 +1051,8 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) struct ipa_master_driver_init_complt_ind_msg_v01 ind; rc = kernel_connect(ipa_q6_clnt->sock, - (struct sockaddr *) &ipa3_qmi_ctx->ipa_q6_client_params.sq, - sizeof(ipa3_qmi_ctx->ipa_q6_client_params.sq), + (struct sockaddr *) &ipa3_qmi_ctx->server_sq, + sizeof(ipa3_qmi_ctx->server_sq), 0); if (rc < 0) { @@ -1030,7 +1107,7 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) IPA_QMI_RESULT_SUCCESS_V01; rc = qmi_send_indication(ipa3_svc_handle, - &ipa3_qmi_ctx->ipa_q6_client_params.sq, + &ipa3_qmi_ctx->client_sq, QMI_IPA_MASTER_DRIVER_INIT_COMPLETE_IND_V01, QMI_IPA_MASTER_DRIVER_INIT_COMPLETE_IND_MAX_MSG_LEN_V01, ipa3_master_driver_init_complt_ind_msg_data_v01_ei, @@ -1045,17 +1122,21 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) static void ipa3_q6_clnt_svc_exit(struct work_struct *work) { - ipa3_qmi_ctx->ipa_q6_client_params.sq.sq_family = 0; - ipa3_qmi_ctx->ipa_q6_client_params.sq.sq_node = 0; - ipa3_qmi_ctx->ipa_q6_client_params.sq.sq_port = 0; + ipa3_qmi_ctx->server_sq.sq_family = 0; + ipa3_qmi_ctx->server_sq.sq_node = 0; + ipa3_qmi_ctx->server_sq.sq_port = 0; } static int ipa3_q6_clnt_svc_event_notify_svc_new(struct qmi_handle *qmi, struct qmi_service *service) { - ipa3_qmi_ctx->ipa_q6_client_params.sq.sq_family = AF_QIPCRTR; - ipa3_qmi_ctx->ipa_q6_client_params.sq.sq_node = service->node; - ipa3_qmi_ctx->ipa_q6_client_params.sq.sq_port = service->port; + IPAWANDBG("QMI svc:%d vers:%d ins:%d node:%d port:%d\n", + service->service, service->version, service->instance, + service->node, service->port); + + ipa3_qmi_ctx->server_sq.sq_family = AF_QIPCRTR; + ipa3_qmi_ctx->server_sq.sq_node = service->node; + ipa3_qmi_ctx->server_sq.sq_port = service->port; if (!workqueues_stopped) { queue_delayed_work(ipa_clnt_req_workqueue, @@ -1064,20 +1145,32 @@ static int ipa3_q6_clnt_svc_event_notify_svc_new(struct qmi_handle *qmi, return 0; } -static void ipa3_q6_clnt_svc_event_notify_svc_exit(struct qmi_handle *qmi) +static void ipa3_q6_clnt_svc_event_notify_net_reset(struct qmi_handle *qmi) { if (!workqueues_stopped) queue_delayed_work(ipa_clnt_req_workqueue, &ipa3_work_svc_exit, 0); } +static void ipa3_q6_clnt_svc_event_notify_svc_exit(struct qmi_handle *qmi, + struct qmi_service *svc) +{ + IPAWANDBG("QMI svc:%d vers:%d ins:%d node:%d port:%d\n", svc->service, + svc->version, svc->instance, svc->node, svc->port); + + if (!workqueues_stopped) + queue_delayed_work(ipa_clnt_req_workqueue, + &ipa3_work_svc_exit, 0); +} + static struct qmi_ops server_ops = { .del_client = ipa3_a5_svc_disconnect_cb, }; static struct qmi_ops client_ops = { .new_server = ipa3_q6_clnt_svc_event_notify_svc_new, - .net_reset = ipa3_q6_clnt_svc_event_notify_svc_exit, + .del_server = ipa3_q6_clnt_svc_event_notify_svc_exit, + .net_reset = ipa3_q6_clnt_svc_event_notify_net_reset, }; static struct qmi_msg_handler server_handlers[] = { @@ -1128,6 +1221,22 @@ static struct qmi_msg_handler server_handlers[] = { QMI_IPA_INIT_MODEM_DRIVER_CMPLT_REQ_MAX_MSG_LEN_V01, .fn = ipa3_handle_modem_init_cmplt_req, }, + { + .type = QMI_REQUEST, + .msg_id = QMI_IPA_MHI_ALLOC_CHANNEL_REQ_V01, + .ei = ipa_mhi_alloc_channel_req_msg_v01_ei, + .decoded_size = + IPA_MHI_ALLOC_CHANNEL_REQ_MSG_V01_MAX_MSG_LEN, + .fn = ipa3_handle_mhi_alloc_channel_req, + }, + { + .type = QMI_REQUEST, + .msg_id = QMI_IPA_MHI_CLK_VOTE_REQ_V01, + .ei = ipa_mhi_clk_vote_req_msg_v01_ei, + .decoded_size = + IPA_MHI_CLK_VOTE_REQ_MSG_V01_MAX_MSG_LEN, + .fn = ipa3_handle_mhi_vote_req, + }, }; @@ -1575,6 +1684,55 @@ int ipa3_qmi_get_per_client_packet_stats( "struct ipa_get_stats_per_client_req_msg_v01"); } +int ipa3_qmi_send_mhi_ready_indication( + struct ipa_mhi_ready_indication_msg_v01 *req) +{ + IPAWANDBG("Sending QMI_IPA_MHI_READY_IND_V01\n"); + + if (unlikely(!ipa3_svc_handle)) + return -ETIMEDOUT; + + return qmi_send_indication(ipa3_svc_handle, + &ipa3_qmi_ctx->client_sq, + QMI_IPA_MHI_READY_IND_V01, + IPA_MHI_READY_INDICATION_MSG_V01_MAX_MSG_LEN, + ipa_mhi_ready_indication_msg_v01_ei, + req); +} + +int ipa3_qmi_send_mhi_cleanup_request(struct ipa_mhi_cleanup_req_msg_v01 *req) +{ + + struct ipa_msg_desc req_desc, resp_desc; + struct ipa_mhi_cleanup_resp_msg_v01 resp; + int rc; + + memset(&resp, 0, sizeof(resp)); + + IPAWANDBG("Sending QMI_IPA_MHI_CLEANUP_REQ_V01\n"); + if (unlikely(!ipa_q6_clnt)) + return -ETIMEDOUT; + + req_desc.max_msg_len = IPA_MHI_CLK_VOTE_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_IPA_MHI_CLEANUP_REQ_V01; + req_desc.ei_array = ipa_mhi_cleanup_req_msg_v01_ei; + + resp_desc.max_msg_len = IPA_MHI_CLK_VOTE_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_IPA_MHI_CLEANUP_RESP_V01; + resp_desc.ei_array = ipa_mhi_cleanup_resp_msg_v01_ei; + + rc = ipa3_qmi_send_req_wait(ipa_q6_clnt, + &req_desc, req, + &resp_desc, &resp, + QMI_MHI_SEND_REQ_TIMEOUT_MS); + + IPAWANDBG("QMI_IPA_MHI_CLEANUP_RESP_V01 received\n"); + + return ipa3_check_qmi_response(rc, + QMI_IPA_MHI_CLEANUP_REQ_V01, resp.resp.result, + resp.resp.error, "ipa_mhi_cleanup_req_msg"); +} + void ipa3_qmi_init(void) { mutex_init(&ipa3_qmi_lock); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h index 9d47878fda989a6626896e72d5daa6c89f66c29f..ce2e34a683ba3aef13d0346d06382d322f2912ed 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h @@ -69,10 +69,6 @@ DEV_NAME " %s:%d " fmt, ## args); \ } while (0) -struct ipa_q6_all_client_params { - struct sockaddr_qrtr sq; -}; - extern struct ipa3_qmi_context *ipa3_qmi_ctx; struct ipa3_qmi_context { @@ -92,7 +88,8 @@ struct ipa3_qmi_context { ipa_configure_ul_firewall_rules_req_msg_cache [MAX_NUM_QMI_RULE_CACHE]; bool modem_cfg_emb_pipe_flt; - struct ipa_q6_all_client_params ipa_q6_client_params; + struct sockaddr_qrtr client_sq; + struct sockaddr_qrtr server_sq; }; struct ipa3_rmnet_mux_val { @@ -175,6 +172,18 @@ extern struct qmi_elem_info extern struct qmi_elem_info ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_ready_indication_msg_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_mem_addr_info_type_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_tr_info_type_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_er_info_type_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_alloc_channel_req_msg_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_ch_alloc_resp_type_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_alloc_channel_resp_msg_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_clk_vote_req_msg_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_clk_vote_resp_msg_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_cleanup_req_msg_v01_ei[]; +extern struct qmi_elem_info ipa_mhi_cleanup_resp_msg_v01_ei[]; + /** * struct ipa3_rmnet_context - IPA rmnet context * @ipa_rmnet_ssr: support modem SSR @@ -285,6 +294,11 @@ int ipa3_qmi_get_per_client_packet_stats( struct ipa_get_stats_per_client_req_msg_v01 *req, struct ipa_get_stats_per_client_resp_msg_v01 *resp); +int ipa3_qmi_send_mhi_ready_indication( + struct ipa_mhi_ready_indication_msg_v01 *req); + +int ipa3_qmi_send_mhi_cleanup_request(struct ipa_mhi_cleanup_req_msg_v01 *req); + void ipa3_qmi_init(void); void ipa3_qmi_cleanup(void); @@ -407,6 +421,18 @@ static inline int ipa3_qmi_stop_data_qouta(void) static inline void ipa3_q6_handshake_complete(bool ssr_bootup) { } +static int ipa3_qmi_send_mhi_ready_indication( + struct ipa_mhi_ready_indication_msg_v01 *req) +{ + return -EPERM; +} + +static int ipa3_qmi_send_mhi_cleanup_request( + struct ipa_mhi_cleanup_req_msg_v01 *req) +{ + return -EPERM; +} + static inline int ipa3_wwan_set_modem_perf_profile(int throughput); static inline int ipa3_qmi_enable_per_client_stats( struct ipa_enable_per_client_stats_req_msg_v01 *req, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c index 6878e0a23ca38a853ce6a1dba8a5db5489449c01..5a544becccb5a94e160f3fa7ec7e8a9e131100b5 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c @@ -1092,11 +1092,92 @@ struct qmi_elem_info ipa3_init_modem_driver_req_msg_data_v01_ei[] = { struct ipa_init_modem_driver_req_msg_v01, v6_hash_filter_tbl_start_addr), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x1F, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_stats_quota_base_addr_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x1F, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_stats_quota_base_addr), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x20, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_stats_quota_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x20, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_stats_quota_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x21, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_drop_stats_base_addr_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x21, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_drop_stats_base_addr), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x22, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_drop_stats_table_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x22, + .offset = offsetof( + struct ipa_init_modem_driver_req_msg_v01, + hw_drop_stats_table_size), + }, { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, }, + }; struct qmi_elem_info ipa3_init_modem_driver_resp_msg_data_v01_ei[] = { @@ -1256,6 +1337,26 @@ struct qmi_elem_info ipa3_indication_reg_req_msg_data_v01_ei[] = { struct ipa_indication_reg_req_msg_v01, data_usage_quota_reached), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof( + struct ipa_indication_reg_req_msg_v01, + ipa_mhi_ready_ind_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof( + struct ipa_indication_reg_req_msg_v01, + ipa_mhi_ready_ind), + }, { .data_type = QMI_EOTI, .is_array = NO_ARRAY, @@ -3384,3 +3485,568 @@ struct qmi_elem_info ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[] = { .tlv_type = QMI_COMMON_TLV_TYPE, }, }; + +static struct qmi_elem_info ipa_mhi_ch_init_info_type_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_ch_init_info_type_v01, + ch_id), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_ch_init_info_type_v01, + er_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_ch_init_info_type_v01, + ch_doorbell_addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_ch_init_info_type_v01, + er_doorbell_addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_ch_init_info_type_v01, + direction_type), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info ipa_mhi_smmu_info_type_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_smmu_info_type_v01, + iova_ctl_base_addr), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_smmu_info_type_v01, + iova_ctl_size), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_smmu_info_type_v01, + iova_data_base_addr), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_smmu_info_type_v01, + iova_data_size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + + +struct qmi_elem_info ipa_mhi_ready_indication_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ipa_mhi_ready_indication_msg_v01, + ch_info_arr_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_REMOTE_MHI_CHANNELS_NUM_MAX_V01, + .elem_size = sizeof(struct ipa_mhi_ch_init_info_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ipa_mhi_ready_indication_msg_v01, + ch_info_arr), + .ei_array = ipa_mhi_ch_init_info_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ipa_mhi_ready_indication_msg_v01, + smmu_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct ipa_mhi_smmu_info_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ipa_mhi_ready_indication_msg_v01, + smmu_info), + .ei_array = ipa_mhi_smmu_info_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_mem_addr_info_type_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_mem_addr_info_type_v01, + pa), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_mem_addr_info_type_v01, + iova), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_mem_addr_info_type_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_tr_info_type_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_tr_info_type_v01, + ch_id), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_tr_info_type_v01, + poll_cfg), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum ipa_mhi_brst_mode_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_tr_info_type_v01, + brst_mode_type), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_tr_info_type_v01, + ring_iova), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_tr_info_type_v01, + ring_len), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_tr_info_type_v01, + rp), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_tr_info_type_v01, + wp), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_er_info_type_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + er_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + intmod_cycles), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + intmod_count), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + msi_addr), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + ring_iova), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + ring_len), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + rp), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_er_info_type_v01, + wp), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_alloc_channel_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + tr_info_arr_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_REMOTE_MHI_CHANNELS_NUM_MAX_V01, + .elem_size = sizeof(struct ipa_mhi_tr_info_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + tr_info_arr), + .ei_array = ipa_mhi_tr_info_type_v01_ei, + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + er_info_arr_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_REMOTE_MHI_CHANNELS_NUM_MAX_V01, + .elem_size = sizeof(struct ipa_mhi_er_info_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + er_info_arr), + .ei_array = ipa_mhi_er_info_type_v01_ei, + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + ctrl_addr_map_info_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_REMOTE_MHI_MEMORY_MAPPING_NUM_MAX_V01, + .elem_size = sizeof(struct ipa_mhi_mem_addr_info_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + ctrl_addr_map_info), + .ei_array = ipa_mhi_mem_addr_info_type_v01_ei, + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x04, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + data_addr_map_info_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_REMOTE_MHI_MEMORY_MAPPING_NUM_MAX_V01, + .elem_size = sizeof(struct ipa_mhi_mem_addr_info_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x04, + .offset = offsetof(struct ipa_mhi_alloc_channel_req_msg_v01, + data_addr_map_info), + .ei_array = ipa_mhi_mem_addr_info_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_ch_alloc_resp_type_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_ch_alloc_resp_type_v01, + ch_id), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct ipa_mhi_ch_alloc_resp_type_v01, + is_success), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_alloc_channel_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ipa_mhi_alloc_channel_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ipa_mhi_alloc_channel_resp_msg_v01, + alloc_resp_arr_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ipa_mhi_alloc_channel_resp_msg_v01, + alloc_resp_arr_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_IPA_REMOTE_MHI_CHANNELS_NUM_MAX_V01, + .elem_size = sizeof(struct ipa_mhi_ch_alloc_resp_type_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ipa_mhi_alloc_channel_resp_msg_v01, + alloc_resp_arr), + .ei_array = ipa_mhi_ch_alloc_resp_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_clk_vote_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ipa_mhi_clk_vote_req_msg_v01, + mhi_vote), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_clk_vote_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ipa_mhi_clk_vote_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_cleanup_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ipa_mhi_cleanup_req_msg_v01, + cleanup_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ipa_mhi_cleanup_req_msg_v01, + cleanup), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info ipa_mhi_cleanup_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ipa_mhi_cleanup_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c index 01b66391ba0a96dbbefa67c2b17f676c0993fa52..f4be9a8c3ec5447dcedd1b99ba23b9fcb51edb23 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -41,6 +41,7 @@ * IPA_CPU_2_HW_CMD_MEMCPY : CPU instructs HW to do memcopy using QMB. * IPA_CPU_2_HW_CMD_RESET_PIPE : Command to reset a pipe - SW WA for a HW bug. * IPA_CPU_2_HW_CMD_GSI_CH_EMPTY : Command to check for GSI channel emptiness. + * IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO: Command to store remote IPA Info */ enum ipa3_cpu_2_hw_commands { IPA_CPU_2_HW_CMD_NO_OP = @@ -65,6 +66,8 @@ enum ipa3_cpu_2_hw_commands { FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 9), IPA_CPU_2_HW_CMD_GSI_CH_EMPTY = FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 10), + IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 11), }; /** @@ -161,6 +164,19 @@ union IpaHwChkChEmptyCmdData_t { u32 raw32b; } __packed; + +/** + * Structure holding the parameters for IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO + * command. + * @remoteIPAAddr: 5G IPA address : uC proxies Q6 doorbell to this address + * @mboxN: mbox on which Q6 will interrupt uC + */ +struct IpaHwDbAddrInfo_t { + u32 remoteIPAAddr; + uint32_t mboxN; +} __packed; + + /** * When resource group 10 limitation mitigation is enabled, uC send * cmd should be able to run in interrupt context, so using spin lock @@ -820,6 +836,11 @@ int ipa3_uc_notify_clk_state(bool enabled) { u32 opcode; + if (ipa3_ctx->ipa_hw_type > IPA_HW_v4_0) { + IPADBG_LOW("not supported past IPA v4.0\n"); + return 0; + } + /* * If the uC interface has not been initialized yet, * don't notify the uC on the enable/disable @@ -927,3 +948,36 @@ int ipa3_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len) dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base, mem.phys_base); return res; } + +int ipa3_uc_send_remote_ipa_info(u32 remote_addr, uint32_t mbox_n) +{ + int res; + struct ipa_mem_buffer cmd; + struct IpaHwDbAddrInfo_t *uc_info; + + cmd.size = sizeof(*uc_info); + cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size, + &cmd.phys_base, GFP_KERNEL); + if (cmd.base == NULL) + return -ENOMEM; + + uc_info = (struct IpaHwDbAddrInfo_t *) cmd.base; + uc_info->remoteIPAAddr = remote_addr; + uc_info->mboxN = mbox_n; + + res = ipa3_uc_send_cmd((u32)(cmd.phys_base), + IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO, 0, + false, 10 * HZ); + + if (res) { + IPAERR("fail to map 0x%x to mbox %d\n", + uc_info->remoteIPAAddr, + uc_info->mboxN); + goto free_coherent; + } + + res = 0; +free_coherent: + dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); + return res; +} diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index ab4ba8a1ca90a0460157f8ffa51ec3b0fa7b5d82..609be4fa595e8f1a7e8d6e4c85d135f2d851ee78 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -88,6 +88,8 @@ #define IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_DEC_UCP 0x00000013 /* 2 Packet Processing pass + no decipher + uCP */ #define IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP 0x00000004 +/* 2 Packet Processing pass + no decipher + uCP + HPS REP DMA Parser. */ +#define IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP 0x00000804 /* 2 Packet Processing pass + decipher + uCP */ #define IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_DEC_UCP 0x00000015 /* Packet Processing + no decipher + no uCP */ @@ -1147,13 +1149,13 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_4_0][IPA_CLIENT_WLAN1_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 6, 2, 8, 16, IPA_EE_UC } }, [IPA_4_0][IPA_CLIENT_USB_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 0, 8, 8, 16, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_APPS_LAN_PROD] = { @@ -1165,7 +1167,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_4_0][IPA_CLIENT_APPS_WAN_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 2, 3, 16, 32, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_APPS_CMD_PROD] = { @@ -1177,13 +1179,13 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_4_0][IPA_CLIENT_ODU_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 1, 0, 8, 16, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_ETHERNET_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 9, 0, 8, 16, IPA_EE_UC } }, [IPA_4_0][IPA_CLIENT_Q6_WAN_PROD] = { @@ -1253,7 +1255,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 19, 12, 9, 9, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_USB_DPL_CONS] = { true, IPA_v4_0_GROUP_UL_DL, @@ -1309,31 +1311,31 @@ static const struct ipa_ep_configuration ipa3_ep_mapping true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 11, 6, 9, 9, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_TEST1_CONS] = { true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 11, 6, 9, 9, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_TEST2_CONS] = { true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 12, 2, 5, 5, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_TEST3_CONS] = { true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 19, 12, 9, 9, IPA_EE_AP } }, [IPA_4_0][IPA_CLIENT_TEST4_CONS] = { true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 21, 14, 9, 9, IPA_EE_AP } }, /* Dummy consumer (pipe 31) is used in L2TP rt rule */ [IPA_4_0][IPA_CLIENT_DUMMY_CONS] = { @@ -1507,13 +1509,13 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_4_1][IPA_CLIENT_WLAN1_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 6, 2, 8, 16, IPA_EE_UC } }, [IPA_4_1][IPA_CLIENT_USB_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 0, 8, 8, 16, IPA_EE_AP } }, [IPA_4_1][IPA_CLIENT_APPS_LAN_PROD] = { @@ -1525,7 +1527,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_4_1][IPA_CLIENT_APPS_WAN_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 2, 3, 16, 32, IPA_EE_AP } }, [IPA_4_1][IPA_CLIENT_APPS_CMD_PROD] = { @@ -1537,13 +1539,13 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_4_1][IPA_CLIENT_ODU_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 1, 0, 8, 16, IPA_EE_AP } }, [IPA_4_1][IPA_CLIENT_ETHERNET_PROD] = { true, IPA_v4_0_GROUP_UL_DL, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, + IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP, QMB_MASTER_SELECT_DDR, { 9, 0, 8, 16, IPA_EE_UC } }, [IPA_4_1][IPA_CLIENT_Q6_WAN_PROD] = { @@ -1613,7 +1615,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 19, 12, 9, 9, IPA_EE_AP } }, [IPA_4_1][IPA_CLIENT_USB_DPL_CONS] = { true, IPA_v4_0_GROUP_UL_DL, @@ -1663,7 +1665,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 11, 6, 9, 9, IPA_EE_AP } }, [IPA_4_1][IPA_CLIENT_TEST1_CONS] = { true, IPA_v4_0_GROUP_UL_DL, @@ -1675,7 +1677,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 12, 2, 9, 9, IPA_EE_AP } }, [IPA_4_1][IPA_CLIENT_TEST3_CONS] = { true, IPA_v4_0_GROUP_UL_DL, @@ -1687,7 +1689,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 21, 14, 9, 9, IPA_EE_AP } }, /* Dummy consumer (pipe 31) is used in L2TP rt rule */ [IPA_4_1][IPA_CLIENT_DUMMY_CONS] = { @@ -4151,6 +4153,9 @@ static void ipa3_tag_free_skb(void *user1, int user2) } #define REQUIRED_TAG_PROCESS_DESCRIPTORS 4 +#define MAX_RETRY_ALLOC 10 +#define ALLOC_MIN_SLEEP_RX 100000 +#define ALLOC_MAX_SLEEP_RX 200000 /* ipa3_tag_process() - Initiates a tag process. Incorporates the input * descriptors @@ -4178,6 +4183,7 @@ int ipa3_tag_process(struct ipa3_desc desc[], int res; struct ipa3_tag_completion *comp; int ep_idx; + u32 retry_cnt = 0; /* Not enough room for the required descriptors for the tag process */ if (IPA_TAG_MAX_DESC - descs_num < REQUIRED_TAG_PROCESS_DESCRIPTORS) { @@ -4283,10 +4289,22 @@ int ipa3_tag_process(struct ipa3_desc desc[], tag_desc[desc_idx].callback = ipa3_tag_free_skb; tag_desc[desc_idx].user1 = dummy_skb; desc_idx++; - +retry_alloc: /* send all descriptors to IPA with single EOT */ res = ipa3_send(sys, desc_idx, tag_desc, true); if (res) { + if (res == -ENOMEM) { + if (retry_cnt < MAX_RETRY_ALLOC) { + IPADBG( + "failed to alloc memory retry cnt = %d\n", + retry_cnt); + retry_cnt++; + usleep_range(ALLOC_MIN_SLEEP_RX, + ALLOC_MAX_SLEEP_RX); + goto retry_alloc; + } + + } IPAERR("failed to send TAG packets %d\n", res); res = -ENOMEM; goto fail_free_skb; @@ -4627,7 +4645,7 @@ static void ipa3_set_tag_process_before_gating(bool val) * * Returns: 0 on success, negative on failure */ -static int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res) +int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res) { if (!res) { IPAERR("NULL out param\n"); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h index bf00ce7180a681f20423a547f23327577b522f7d..26b7f0fc005e7d42edb4786dfe39ac1e3190337f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h @@ -46,6 +46,15 @@ IPAHAL_DRV_NAME " %s:%d " fmt, ## args); \ } while (0) +#define IPAHAL_DBG_REG(fmt, args...) \ + do { \ + pr_err(fmt, ## args); \ + IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \ + " %s:%d " fmt, ## args); \ + IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \ + " %s:%d " fmt, ## args); \ + } while (0) + #define IPAHAL_ERR_RL(fmt, args...) \ do { \ pr_err_ratelimited_ipa(IPAHAL_DRV_NAME " %s:%d " fmt, \ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c index 1b3f5f15a46cd54d2f01a622695d2adac54b8f64..48f6e7db24c38f22e0574cfc0169b52fafbc4990 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c @@ -18,6 +18,8 @@ #include "ipahal_reg.h" #include "ipahal_reg_i.h" +#define IPA_MAX_MSG_LEN 4096 + static const char *ipareg_name_to_str[IPA_REG_MAX] = { __stringify(IPA_ROUTE), __stringify(IPA_IRQ_STTS_EE_n), @@ -26,6 +28,9 @@ static const char *ipareg_name_to_str[IPA_REG_MAX] = { __stringify(IPA_IRQ_SUSPEND_INFO_EE_n), __stringify(IPA_SUSPEND_IRQ_EN_EE_n), __stringify(IPA_SUSPEND_IRQ_CLR_EE_n), + __stringify(IPA_HOLB_DROP_IRQ_INFO_EE_n), + __stringify(IPA_HOLB_DROP_IRQ_EN_EE_n), + __stringify(IPA_HOLB_DROP_IRQ_CLR_EE_n), __stringify(IPA_BCR), __stringify(IPA_ENABLED_PIPES), __stringify(IPA_COMP_SW_RESET), @@ -35,8 +40,21 @@ static const char *ipareg_name_to_str[IPA_REG_MAX] = { __stringify(IPA_SPARE_REG_1), __stringify(IPA_SPARE_REG_2), __stringify(IPA_COMP_CFG), + __stringify(IPA_STATE_TX_WRAPPER), + __stringify(IPA_STATE_TX1), + __stringify(IPA_STATE_FETCHER), + __stringify(IPA_STATE_FETCHER_MASK), + __stringify(IPA_STATE_DFETCHER), + __stringify(IPA_STATE_ACL), + __stringify(IPA_STATE), + __stringify(IPA_STATE_RX_ACTIVE), + __stringify(IPA_STATE_TX0), __stringify(IPA_STATE_AGGR_ACTIVE), __stringify(IPA_COUNTER_CFG), + __stringify(IPA_STATE_GSI_TLV), + __stringify(IPA_STATE_GSI_AOS), + __stringify(IPA_STATE_GSI_IF), + __stringify(IPA_STATE_GSI_SKIP), __stringify(IPA_ENDP_INIT_HDR_n), __stringify(IPA_ENDP_INIT_HDR_EXT_n), __stringify(IPA_ENDP_INIT_AGGR_n), @@ -47,6 +65,7 @@ static const char *ipareg_name_to_str[IPA_REG_MAX] = { __stringify(IPA_ENDP_INIT_CONN_TRACK_n), __stringify(IPA_ENDP_INIT_CTRL_n), __stringify(IPA_ENDP_INIT_CTRL_SCND_n), + __stringify(IPA_ENDP_INIT_CTRL_STATUS_n), __stringify(IPA_ENDP_INIT_HOL_BLOCK_EN_n), __stringify(IPA_ENDP_INIT_HOL_BLOCK_TIMER_n), __stringify(IPA_ENDP_INIT_DEAGGR_n), @@ -56,6 +75,7 @@ static const char *ipareg_name_to_str[IPA_REG_MAX] = { __stringify(IPA_IRQ_EE_UC_n), __stringify(IPA_ENDP_INIT_HDR_METADATA_MASK_n), __stringify(IPA_ENDP_INIT_HDR_METADATA_n), + __stringify(IPA_ENDP_INIT_PROD_CFG_n), __stringify(IPA_ENDP_INIT_RSRC_GRP_n), __stringify(IPA_SHARED_MEM_SIZE), __stringify(IPA_SRAM_DIRECT_ACCESS_n), @@ -67,6 +87,8 @@ static const char *ipareg_name_to_str[IPA_REG_MAX] = { __stringify(IPA_SYS_PKT_PROC_CNTXT_BASE), __stringify(IPA_LOCAL_PKT_PROC_CNTXT_BASE), __stringify(IPA_ENDP_STATUS_n), + __stringify(IPA_ENDP_WEIGHTS_n), + __stringify(IPA_ENDP_YELLOW_RED_MARKER), __stringify(IPA_ENDP_FILTER_ROUTER_HSH_CFG_n), __stringify(IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n), __stringify(IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n), @@ -106,6 +128,12 @@ static const char *ipareg_name_to_str[IPA_REG_MAX] = { __stringify(IPA_STAT_ROUTER_IPV6_END_ID), __stringify(IPA_STAT_DROP_CNT_BASE_n), __stringify(IPA_STAT_DROP_CNT_MASK_n), + __stringify(IPA_SNOC_FEC_EE_n), + __stringify(IPA_FEC_ADDR_EE_n), + __stringify(IPA_FEC_ADDR_MSB_EE_n), + __stringify(IPA_FEC_ATTR_EE_n), + __stringify(IPA_MBIM_DEAGGR_FEC_ATTR_EE_n), + __stringify(IPA_GEN_DEAGGR_FEC_ATTR_EE_n), }; static void ipareg_construct_dummy(enum ipahal_reg_name reg, @@ -1689,6 +1717,9 @@ static void ipareg_parse_counter_cfg( * @parse - CB to parse register value to abstracted structure * @offset - register offset relative to base address * @n_ofst - N parameterized register sub-offset + * @n_start - starting n for n_registers + * @n_end - ending n for n_registers + * @en_print - enable this register to be printed when the device crashes */ struct ipahal_reg_obj { void (*construct)(enum ipahal_reg_name reg, const void *fields, @@ -1697,6 +1728,9 @@ struct ipahal_reg_obj { u32 val); u32 offset; u32 n_ofst; + int n_start; + int n_end; + bool en_print; }; /* @@ -1714,368 +1748,546 @@ static struct ipahal_reg_obj ipahal_reg_objs[IPA_HW_MAX][IPA_REG_MAX] = { /* IPAv3 */ [IPA_HW_v3_0][IPA_ROUTE] = { ipareg_construct_route, ipareg_parse_dummy, - 0x00000048, 0}, + 0x00000048, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_IRQ_STTS_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00003008, 0x1000}, + 0x00003008, 0x1000, 0, 0, 0}, [IPA_HW_v3_0][IPA_IRQ_EN_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000300c, 0x1000}, + 0x0000300c, 0x1000, 0, 0, 0}, [IPA_HW_v3_0][IPA_IRQ_CLR_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00003010, 0x1000}, + 0x00003010, 0x1000, 0, 0, 0}, [IPA_HW_v3_0][IPA_IRQ_SUSPEND_INFO_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00003098, 0x1000}, + 0x00003098, 0x1000, 0, 0, 0}, [IPA_HW_v3_0][IPA_BCR] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x000001D0, 0}, + 0x000001D0, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENABLED_PIPES] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000038, 0}, + 0x00000038, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_COMP_SW_RESET] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000040, 0}, + 0x00000040, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_VERSION] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000034, 0}, + 0x00000034, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_TAG_TIMER] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000060, 0 }, + 0x00000060, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_COMP_HW_VERSION] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000030, 0}, + 0x00000030, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_SPARE_REG_1] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00005090, 0}, + 0x00005090, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_SPARE_REG_2] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00005094, 0}, + 0x00005094, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_COMP_CFG] = { ipareg_construct_comp_cfg, ipareg_parse_comp_cfg, - 0x0000003C, 0}, + 0x0000003C, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_STATE_AGGR_ACTIVE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000010C, 0}, + 0x0000010C, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_HDR_n] = { ipareg_construct_endp_init_hdr_n, ipareg_parse_dummy, - 0x00000810, 0x70}, + 0x00000810, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_HDR_EXT_n] = { ipareg_construct_endp_init_hdr_ext_n, ipareg_parse_dummy, - 0x00000814, 0x70}, + 0x00000814, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_AGGR_n] = { ipareg_construct_endp_init_aggr_n, ipareg_parse_endp_init_aggr_n, - 0x00000824, 0x70}, + 0x00000824, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_AGGR_FORCE_CLOSE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x000001EC, 0}, + 0x000001EC, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_ROUTE_n] = { ipareg_construct_endp_init_route_n, ipareg_parse_dummy, - 0x00000828, 0x70}, + 0x00000828, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_MODE_n] = { ipareg_construct_endp_init_mode_n, ipareg_parse_dummy, - 0x00000820, 0x70}, + 0x00000820, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_NAT_n] = { ipareg_construct_endp_init_nat_n, ipareg_parse_dummy, - 0x0000080C, 0x70}, + 0x0000080C, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_n] = { ipareg_construct_endp_init_ctrl_n, ipareg_parse_endp_init_ctrl_n, - 0x00000800, 0x70}, + 0x00000800, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_SCND_n] = { ipareg_construct_endp_init_ctrl_scnd_n, ipareg_parse_dummy, - 0x00000804, 0x70 }, + 0x00000804, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_HOL_BLOCK_EN_n] = { ipareg_construct_endp_init_hol_block_en_n, ipareg_parse_dummy, - 0x0000082c, 0x70}, + 0x0000082c, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_HOL_BLOCK_TIMER_n] = { ipareg_construct_endp_init_hol_block_timer_n, ipareg_parse_dummy, - 0x00000830, 0x70}, + 0x00000830, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_DEAGGR_n] = { ipareg_construct_endp_init_deaggr_n, ipareg_parse_dummy, - 0x00000834, 0x70}, + 0x00000834, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_SEQ_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000083C, 0x70}, + 0x0000083C, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_DEBUG_CNT_REG_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000600, 0x4}, + 0x00000600, 0x4, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_CFG_n] = { ipareg_construct_endp_init_cfg_n, ipareg_parse_dummy, - 0x00000808, 0x70}, + 0x00000808, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_IRQ_EE_UC_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000301c, 0x1000}, + 0x0000301c, 0x1000, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_HDR_METADATA_MASK_n] = { ipareg_construct_endp_init_hdr_metadata_mask_n, ipareg_parse_dummy, - 0x00000818, 0x70}, + 0x00000818, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_HDR_METADATA_n] = { ipareg_construct_endp_init_hdr_metadata_n, ipareg_parse_dummy, - 0x0000081c, 0x70}, + 0x0000081c, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_INIT_RSRC_GRP_n] = { ipareg_construct_endp_init_rsrc_grp_n, ipareg_parse_dummy, - 0x00000838, 0x70}, + 0x00000838, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_SHARED_MEM_SIZE] = { ipareg_construct_dummy, ipareg_parse_shared_mem_size, - 0x00000054, 0}, + 0x00000054, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_SRAM_DIRECT_ACCESS_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00007000, 0x4}, + 0x00007000, 0x4, 0, 0, 0}, [IPA_HW_v3_0][IPA_DEBUG_CNT_CTRL_n] = { ipareg_construct_debug_cnt_ctrl_n, ipareg_parse_dummy, - 0x00000640, 0x4}, + 0x00000640, 0x4, 0, 0, 0}, [IPA_HW_v3_0][IPA_UC_MAILBOX_m_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00032000, 0x4}, + 0x00032000, 0x4, 0, 0, 0}, [IPA_HW_v3_0][IPA_FILT_ROUT_HASH_FLUSH] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000090, 0}, + 0x00000090, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_SINGLE_NDP_MODE] = { ipareg_construct_single_ndp_mode, ipareg_parse_single_ndp_mode, - 0x00000068, 0}, + 0x00000068, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_QCNCM] = { ipareg_construct_qcncm, ipareg_parse_qcncm, - 0x00000064, 0}, + 0x00000064, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_SYS_PKT_PROC_CNTXT_BASE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x000001e0, 0}, + 0x000001e0, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_LOCAL_PKT_PROC_CNTXT_BASE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x000001e8, 0}, + 0x000001e8, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_STATUS_n] = { ipareg_construct_endp_status_n, ipareg_parse_dummy, - 0x00000840, 0x70}, + 0x00000840, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_ENDP_FILTER_ROUTER_HSH_CFG_n] = { ipareg_construct_hash_cfg_n, ipareg_parse_hash_cfg_n, - 0x0000085C, 0x70}, + 0x0000085C, 0x70, 0, 0, 0}, [IPA_HW_v3_0][IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x00000400, 0x20}, + 0x00000400, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x00000404, 0x20}, + 0x00000404, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_SRC_RSRC_GRP_45_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x00000408, 0x20}, + 0x00000408, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_SRC_RSRC_GRP_67_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x0000040C, 0x20}, + 0x0000040C, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_DST_RSRC_GRP_01_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x00000500, 0x20}, + 0x00000500, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_DST_RSRC_GRP_23_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x00000504, 0x20}, + 0x00000504, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_DST_RSRC_GRP_45_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x00000508, 0x20}, + 0x00000508, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_DST_RSRC_GRP_67_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy, - 0x0000050c, 0x20}, + 0x0000050c, 0x20, 0, 0, 0}, [IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MIN_DEPTH_0] = { ipareg_construct_rx_hps_clients_depth0, ipareg_parse_dummy, - 0x000023C4, 0}, + 0x000023C4, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MIN_DEPTH_1] = { ipareg_construct_rx_hps_clients_depth1, ipareg_parse_dummy, - 0x000023C8, 0}, + 0x000023C8, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MAX_DEPTH_0] = { ipareg_construct_rx_hps_clients_depth0, ipareg_parse_dummy, - 0x000023CC, 0}, + 0x000023CC, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MAX_DEPTH_1] = { ipareg_construct_rx_hps_clients_depth1, ipareg_parse_dummy, - 0x000023D0, 0}, + 0x000023D0, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_QSB_MAX_WRITES] = { ipareg_construct_qsb_max_writes, ipareg_parse_dummy, - 0x00000074, 0}, + 0x00000074, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_QSB_MAX_READS] = { ipareg_construct_qsb_max_reads, ipareg_parse_dummy, - 0x00000078, 0}, + 0x00000078, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_DPS_SEQUENCER_FIRST] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0001e000, 0}, + 0x0001e000, 0, 0, 0, 0}, [IPA_HW_v3_0][IPA_HPS_SEQUENCER_FIRST] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0001e080, 0}, + 0x0001e080, 0, 0, 0, 0}, /* IPAv3.1 */ [IPA_HW_v3_1][IPA_IRQ_SUSPEND_INFO_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00003030, 0x1000}, + 0x00003030, 0x1000, 0, 0, 0}, [IPA_HW_v3_1][IPA_SUSPEND_IRQ_EN_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00003034, 0x1000}, + 0x00003034, 0x1000, 0, 0, 0}, [IPA_HW_v3_1][IPA_SUSPEND_IRQ_CLR_EE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00003038, 0x1000}, + 0x00003038, 0x1000, 0, 0, 0}, /* IPAv3.5 */ [IPA_HW_v3_5][IPA_TX_CFG] = { ipareg_construct_tx_cfg, ipareg_parse_tx_cfg, - 0x000001FC, 0}, + 0x000001FC, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy, - 0x00000400, 0x20}, + 0x00000400, 0x20, 0, 0, 0}, [IPA_HW_v3_5][IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy, - 0x00000404, 0x20}, + 0x00000404, 0x20, 0, 0, 0}, [IPA_HW_v3_5][IPA_SRC_RSRC_GRP_45_RSRC_TYPE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_SRC_RSRC_GRP_67_RSRC_TYPE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_DST_RSRC_GRP_01_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy, - 0x00000500, 0x20}, + 0x00000500, 0x20, 0, 0, 0}, [IPA_HW_v3_5][IPA_DST_RSRC_GRP_23_RSRC_TYPE_n] = { ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy, - 0x00000504, 0x20}, + 0x00000504, 0x20, 0, 0, 0}, [IPA_HW_v3_5][IPA_DST_RSRC_GRP_45_RSRC_TYPE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_DST_RSRC_GRP_67_RSRC_TYPE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_ENDP_INIT_RSRC_GRP_n] = { ipareg_construct_endp_init_rsrc_grp_n_v3_5, ipareg_parse_dummy, - 0x00000838, 0x70}, + 0x00000838, 0x70, 0, 0, 0}, [IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MIN_DEPTH_0] = { ipareg_construct_rx_hps_clients_depth0_v3_5, ipareg_parse_dummy, - 0x000023C4, 0}, + 0x000023C4, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MIN_DEPTH_1] = { ipareg_construct_dummy, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MAX_DEPTH_0] = { ipareg_construct_rx_hps_clients_depth0_v3_5, ipareg_parse_dummy, - 0x000023CC, 0}, + 0x000023CC, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MAX_DEPTH_1] = { ipareg_construct_dummy, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_SPARE_REG_1] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00002780, 0}, + 0x00002780, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_SPARE_REG_2] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00002784, 0}, + 0x00002784, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_IDLE_INDICATION_CFG] = { ipareg_construct_idle_indication_cfg, ipareg_parse_dummy, - 0x00000220, 0}, + 0x00000220, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_HPS_FTCH_ARB_QUEUE_WEIGHT] = { ipareg_construct_hps_queue_weights, - ipareg_parse_hps_queue_weights, 0x000005a4, 0}, + ipareg_parse_hps_queue_weights, 0x000005a4, 0, 0, 0, 0}, [IPA_HW_v3_5][IPA_COUNTER_CFG] = { ipareg_construct_counter_cfg, ipareg_parse_counter_cfg, - 0x000001F0, 0 }, + 0x000001F0, 0, 0, 0, 0}, /* IPAv4.0 */ + [IPA_HW_v4_0][IPA_IRQ_SUSPEND_INFO_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003030, 0x1000, 0, 1, 1}, + [IPA_HW_v4_0][IPA_SUSPEND_IRQ_EN_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003034, 0x1000, 0, 1, 1}, + [IPA_HW_v4_0][IPA_SUSPEND_IRQ_CLR_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003038, 0x1000, 0, 1, 1}, + [IPA_HW_v4_0][IPA_IRQ_EN_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x0000300c, 0x1000, 0, 1, 1}, + [IPA_HW_v4_0][IPA_TAG_TIMER] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000060, 0, 0, 0, 1}, [IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_n] = { ipareg_construct_endp_init_ctrl_n_v4_0, ipareg_parse_dummy, - 0x00000800, 0x70 }, + 0x00000800, 0x70, 0, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_EXT_n] = { + ipareg_construct_endp_init_hdr_ext_n, ipareg_parse_dummy, + 0x00000814, 0x70, 0, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_AGGR_n] = { + ipareg_construct_endp_init_aggr_n, + ipareg_parse_endp_init_aggr_n, + 0x00000824, 0x70, 0, 23, 1}, [IPA_HW_v4_0][IPA_TX_CFG] = { ipareg_construct_tx_cfg_v4_0, ipareg_parse_tx_cfg_v4_0, - 0x000001FC, 0}, + 0x000001FC, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_DEBUG_CNT_REG_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_DEBUG_CNT_CTRL_n] = { ipareg_construct_debug_cnt_ctrl_n, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_QCNCM] = { ipareg_construct_qcncm, ipareg_parse_qcncm, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_SINGLE_NDP_MODE] = { ipareg_construct_single_ndp_mode, ipareg_parse_single_ndp_mode, - -1, 0}, + -1, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_QSB_MAX_READS] = { ipareg_construct_qsb_max_reads_v4_0, ipareg_parse_dummy, - 0x00000078, 0}, + 0x00000078, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_FILT_ROUT_HASH_FLUSH] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000014c, 0}, - [IPA_HW_v4_0][IPA_STATE_AGGR_ACTIVE] = { - ipareg_construct_dummy, ipareg_parse_dummy, - 0x000000b4, 0}, + 0x0000014c, 0, 0, 0, 0}, + [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_n] = { + ipareg_construct_endp_init_hdr_n, ipareg_parse_dummy, + 0x00000810, 0x70, 0, 23, 1}, [IPA_HW_v4_0][IPA_ENDP_INIT_ROUTE_n] = { ipareg_construct_endp_init_route_n, ipareg_parse_dummy, - -1, 0}, + -1, 0, 0, 0, 0}, + [IPA_HW_v4_0][IPA_ENDP_INIT_MODE_n] = { + ipareg_construct_endp_init_mode_n, ipareg_parse_dummy, + 0x00000820, 0x70, 0, 10, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_NAT_n] = { + ipareg_construct_endp_init_nat_n, ipareg_parse_dummy, + 0x0000080C, 0x70, 0, 10, 1}, [IPA_HW_v4_0][IPA_ENDP_STATUS_n] = { ipareg_construct_endp_status_n_v4_0, ipareg_parse_dummy, - 0x00000840, 0x70}, - [IPA_HW_v4_0][IPA_CLKON_CFG] = { - ipareg_construct_clkon_cfg, ipareg_parse_clkon_cfg, - 0x00000044, 0}, + 0x00000840, 0x70, 0, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_FILTER_ROUTER_HSH_CFG_n] = { + ipareg_construct_hash_cfg_n, ipareg_parse_hash_cfg_n, + 0x0000085C, 0x70, 0, 32, 1}, [IPA_HW_v4_0][IPA_ENDP_INIT_CONN_TRACK_n] = { ipareg_construct_endp_init_conn_track_n, ipareg_parse_dummy, - 0x00000850, 0x70}, + 0x00000850, 0x70, 0, 10, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_SCND_n] = { + ipareg_construct_endp_init_ctrl_scnd_n, ipareg_parse_dummy, + 0x00000804, 0x70, 0, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_HOL_BLOCK_EN_n] = { + ipareg_construct_endp_init_hol_block_en_n, + ipareg_parse_dummy, + 0x0000082c, 0x70, 10, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_HOL_BLOCK_TIMER_n] = { + ipareg_construct_endp_init_hol_block_timer_n, + ipareg_parse_dummy, + 0x00000830, 0x70, 10, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_DEAGGR_n] = { + ipareg_construct_endp_init_deaggr_n, + ipareg_parse_dummy, + 0x00000834, 0x70, 0, 10, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_SEQ_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x0000083C, 0x70, 0, 10, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_CFG_n] = { + ipareg_construct_endp_init_cfg_n, ipareg_parse_dummy, + 0x00000808, 0x70, 0, 23, 1}, + [IPA_HW_v4_0][IPA_IRQ_EE_UC_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x0000301c, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_METADATA_MASK_n] = { + ipareg_construct_endp_init_hdr_metadata_mask_n, + ipareg_parse_dummy, + 0x00000818, 0x70, 10, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_METADATA_n] = { + ipareg_construct_endp_init_hdr_metadata_n, + ipareg_parse_dummy, + 0x0000081c, 0x70, 0, 10, 1}, + [IPA_HW_v4_0][IPA_CLKON_CFG] = { + ipareg_construct_clkon_cfg, ipareg_parse_clkon_cfg, + 0x00000044, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_QUOTA_BASE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000700, 0x4 }, + 0x00000700, 0x4, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_QUOTA_MASK_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000708, 0x4 }, + 0x00000708, 0x4, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_TETHERING_BASE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000710, 0x4 }, + 0x00000710, 0x4, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_TETHERING_MASK_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000718, 0x4 }, + 0x00000718, 0x4, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_BASE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000720, 0x0 }, + 0x00000720, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_BASE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000724, 0x0 }, + 0x00000724, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_BASE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000728, 0x0 }, + 0x00000728, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_BASE] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000072C, 0x0 }, + 0x0000072C, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_START_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000730, 0x0 }, + 0x00000730, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_START_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000734, 0x0 }, + 0x00000734, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_START_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000738, 0x0 }, + 0x00000738, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_START_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000073C, 0x0 }, + 0x0000073C, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_END_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000740, 0x0 }, + 0x00000740, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_END_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000744, 0x0 }, + 0x00000744, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_END_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000748, 0x0 }, + 0x00000748, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_END_ID] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x0000074C, 0x0 }, + 0x0000074C, 0, 0, 0, 0}, [IPA_HW_v4_0][IPA_STAT_DROP_CNT_BASE_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000750, 0x4 }, + 0x00000750, 0x4, 3, 1}, [IPA_HW_v4_0][IPA_STAT_DROP_CNT_MASK_n] = { ipareg_construct_dummy, ipareg_parse_dummy, - 0x00000758, 0x4 }, + 0x00000758, 0x4, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_TX_WRAPPER] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000090, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_TX1] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000094, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_FETCHER] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000098, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_FETCHER_MASK] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x0000009C, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_DFETCHER] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000A0, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_ACL] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000A4, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000A8, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_RX_ACTIVE] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000AC, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_TX0] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000B0, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_AGGR_ACTIVE] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000B4, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_GSI_TLV] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000B8, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_GSI_AOS] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000B8, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_GSI_IF] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000C0, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_STATE_GSI_SKIP] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x000000C4, 0, 0, 0, 1}, + [IPA_HW_v4_0][IPA_SNOC_FEC_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003018, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_FEC_ADDR_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003020, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_FEC_ADDR_MSB_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003024, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_FEC_ATTR_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003028, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_MBIM_DEAGGR_FEC_ATTR_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003028, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_GEN_DEAGGR_FEC_ATTR_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003028, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_INFO_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x0000303C, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_EN_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003040, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_CLR_EE_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00003044, 0x1000, 0, 0, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_STATUS_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000864, 0x70, 0, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_PROD_CFG_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000CC8, 0x70, 10, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_INIT_RSRC_GRP_n] = { + ipareg_construct_endp_init_rsrc_grp_n_v3_5, + ipareg_parse_dummy, + 0x00000838, 0x70, 0, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_WEIGHTS_n] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000CA4, 0x70, 10, 23, 1}, + [IPA_HW_v4_0][IPA_ENDP_YELLOW_RED_MARKER] = { + ipareg_construct_dummy, ipareg_parse_dummy, + 0x00000CC0, 0x70, 10, 23, 1}, }; +int ipahal_print_all_regs(void) +{ + int i, j; + + IPAHAL_DBG("Printing all registers for ipa_hw_type %d\n", + ipahal_ctx->hw_type); + + if ((ipahal_ctx->hw_type < IPA_HW_v4_0) || + (ipahal_ctx->hw_type >= IPA_HW_MAX)) { + IPAHAL_ERR("invalid IPA HW type (%d)\n", ipahal_ctx->hw_type); + return -EINVAL; + } + + for (i = 0; i < IPA_REG_MAX ; i++) { + if (!ipahal_reg_objs[IPA_HW_v4_0][i].en_print) + continue; + + j = ipahal_reg_objs[ipahal_ctx->hw_type][i].n_start; + + if (j == ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end) + IPAHAL_DBG_REG("%s=0x%x\n", ipahal_reg_name_str(i), + ipahal_read_reg_n(i, j)); + + for (; j < ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end; j++) + IPAHAL_DBG_REG("%s_%u=0x%x\n", ipahal_reg_name_str(i), + j, ipahal_read_reg_n(i, j)); + } + return 0; +} + /* * ipahal_reg_init() - Build the registers information table * See ipahal_reg_objs[][] comments diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h index f0aa207a9a3a85f4643782724d287da44f368b7c..0bece888a50c3d7cfcab16a0633bf1dd7f8a8e37 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h @@ -29,6 +29,9 @@ enum ipahal_reg_name { IPA_IRQ_SUSPEND_INFO_EE_n, IPA_SUSPEND_IRQ_EN_EE_n, IPA_SUSPEND_IRQ_CLR_EE_n, + IPA_HOLB_DROP_IRQ_INFO_EE_n, + IPA_HOLB_DROP_IRQ_EN_EE_n, + IPA_HOLB_DROP_IRQ_CLR_EE_n, IPA_BCR, IPA_ENABLED_PIPES, IPA_COMP_SW_RESET, @@ -38,8 +41,21 @@ enum ipahal_reg_name { IPA_SPARE_REG_1, IPA_SPARE_REG_2, IPA_COMP_CFG, + IPA_STATE_TX_WRAPPER, + IPA_STATE_TX1, + IPA_STATE_FETCHER, + IPA_STATE_FETCHER_MASK, + IPA_STATE_DFETCHER, + IPA_STATE_ACL, + IPA_STATE, + IPA_STATE_RX_ACTIVE, + IPA_STATE_TX0, IPA_STATE_AGGR_ACTIVE, IPA_COUNTER_CFG, + IPA_STATE_GSI_TLV, + IPA_STATE_GSI_AOS, + IPA_STATE_GSI_IF, + IPA_STATE_GSI_SKIP, IPA_ENDP_INIT_HDR_n, IPA_ENDP_INIT_HDR_EXT_n, IPA_ENDP_INIT_AGGR_n, @@ -50,6 +66,7 @@ enum ipahal_reg_name { IPA_ENDP_INIT_CONN_TRACK_n, IPA_ENDP_INIT_CTRL_n, IPA_ENDP_INIT_CTRL_SCND_n, + IPA_ENDP_INIT_CTRL_STATUS_n, IPA_ENDP_INIT_HOL_BLOCK_EN_n, IPA_ENDP_INIT_HOL_BLOCK_TIMER_n, IPA_ENDP_INIT_DEAGGR_n, @@ -59,6 +76,7 @@ enum ipahal_reg_name { IPA_IRQ_EE_UC_n, IPA_ENDP_INIT_HDR_METADATA_MASK_n, IPA_ENDP_INIT_HDR_METADATA_n, + IPA_ENDP_INIT_PROD_CFG_n, IPA_ENDP_INIT_RSRC_GRP_n, IPA_SHARED_MEM_SIZE, IPA_SRAM_DIRECT_ACCESS_n, @@ -70,6 +88,8 @@ enum ipahal_reg_name { IPA_SYS_PKT_PROC_CNTXT_BASE, IPA_LOCAL_PKT_PROC_CNTXT_BASE, IPA_ENDP_STATUS_n, + IPA_ENDP_WEIGHTS_n, + IPA_ENDP_YELLOW_RED_MARKER, IPA_ENDP_FILTER_ROUTER_HSH_CFG_n, IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n, IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n, @@ -109,6 +129,12 @@ enum ipahal_reg_name { IPA_STAT_ROUTER_IPV6_END_ID, IPA_STAT_DROP_CNT_BASE_n, IPA_STAT_DROP_CNT_MASK_n, + IPA_SNOC_FEC_EE_n, + IPA_FEC_ADDR_EE_n, + IPA_FEC_ADDR_MSB_EE_n, + IPA_FEC_ATTR_EE_n, + IPA_MBIM_DEAGGR_FEC_ATTR_EE_n, + IPA_GEN_DEAGGR_FEC_ATTR_EE_n, IPA_REG_MAX, }; @@ -508,6 +534,9 @@ struct ipahal_ep_cfg_ctrl_scnd { bool endp_delay; }; + +int ipahal_print_all_regs(void); + /* * ipahal_reg_name_str() - returns string that represent the register * @reg_name: [in] register name diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 5f7cdedcd1552d05e47c3826cbb4ae15919577e5..f1672ddbed614fdad125b158906295e5b22c3e54 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -36,6 +36,7 @@ #include #include #include +#include "ipa_mhi_proxy.h" #include "ipa_trace.h" @@ -1531,6 +1532,8 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Extended IOCTLs */ case RMNET_IOCTL_EXTENDED: + if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN)) + return -EPERM; IPAWANDBG("get ioctl: RMNET_IOCTL_EXTENDED\n"); if (copy_from_user(&ext_ioctl_data, (u8 *)ifr->ifr_ifru.ifru_data, @@ -3135,9 +3138,6 @@ static int rmnet_ipa3_query_tethering_stats_modem( req->reset_stats_valid = true; req->reset_stats = true; IPAWANDBG("reset the pipe stats\n"); - } else { - /* print tethered-client enum */ - IPAWANDBG("Tethered-client enum(%d)\n", data->ipa_client); } rc = ipa3_qmi_get_data_stats(req, resp); @@ -3208,8 +3208,8 @@ static int rmnet_ipa3_query_tethering_stats_modem( (unsigned long int) stat_ptr->num_ipv6_bytes); if (ipa_get_client_uplink( stat_ptr->pipe_index) == true) { - if (data->ipa_client == - stat_ptr->pipe_index) { + if (data->ipa_client == ipa_get_client( + stat_ptr->pipe_index)) { /* update the DL stats */ data->ipv4_tx_packets += stat_ptr->num_ipv4_packets; @@ -3233,6 +3233,133 @@ static int rmnet_ipa3_query_tethering_stats_modem( return 0; } +static int rmnet_ipa3_query_tethering_stats_hw( + struct wan_ioctl_query_tether_stats *data, bool reset) +{ + int rc = 0; + struct ipa_quota_stats_all *con_stats; + + if (reset) { + IPAWANERR("only reset the pipe stats without returning stats"); + rc = ipa_get_teth_stats(); + if (rc) { + IPAWANERR("ipa_get_teth_stats failed %d,\n", rc); + return rc; + } + return 0; + } + /* qet HW-stats */ + rc = ipa_get_teth_stats(); + if (rc) { + IPAWANDBG("ipa_get_teth_stats failed %d,\n", rc); + return rc; + } + + /* query DL stats */ + IPAWANDBG("reset the pipe stats? (%d)\n", reset); + con_stats = kzalloc(sizeof(*con_stats), GFP_KERNEL); + if (!con_stats) { + IPAWANERR("no memory\n"); + return -ENOMEM; + } + rc = ipa_query_teth_stats(IPA_CLIENT_Q6_WAN_PROD, con_stats, reset); + if (rc) { + IPAERR("IPA_CLIENT_Q6_WAN_PROD query failed %d,\n", rc); + kfree(con_stats); + return rc; + } + IPAWANDBG("wlan: v4_rx_p(%d) b(%lld) v6_rx_p(%d) b(%lld)\n", + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_pkts, + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_bytes, + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_pkts, + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_bytes); + + IPAWANDBG("usb: v4_rx_p(%d) b(%lld) v6_rx_p(%d) b(%lld)\n", + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_pkts, + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_bytes, + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_pkts, + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_bytes); + + /* update the DL stats */ + data->ipv4_rx_packets = + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_pkts + + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_pkts; + data->ipv6_rx_packets = + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_pkts + + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_pkts; + data->ipv4_rx_bytes = + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_bytes + + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_bytes; + data->ipv6_rx_bytes = + con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_bytes + + con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_bytes; + + IPAWANDBG("v4_rx_p(%lu) v6_rx_p(%lu) v4_rx_b(%lu) v6_rx_b(%lu)\n", + (unsigned long int) data->ipv4_rx_packets, + (unsigned long int) data->ipv6_rx_packets, + (unsigned long int) data->ipv4_rx_bytes, + (unsigned long int) data->ipv6_rx_bytes); + + /* query USB UL stats */ + memset(con_stats, 0, sizeof(struct ipa_quota_stats_all)); + rc = ipa_query_teth_stats(IPA_CLIENT_USB_PROD, con_stats, reset); + if (rc) { + IPAERR("IPA_CLIENT_USB_PROD query failed %d\n", rc); + kfree(con_stats); + return rc; + } + + IPAWANDBG("usb: v4_tx_p(%d) b(%lld) v6_tx_p(%d) b(%lld)\n", + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts, + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes, + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts, + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes); + + /* update the USB UL stats */ + data->ipv4_tx_packets = + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts; + data->ipv6_tx_packets = + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts; + data->ipv4_tx_bytes = + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes; + data->ipv6_tx_bytes = + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes; + + /* query WLAN UL stats */ + memset(con_stats, 0, sizeof(struct ipa_quota_stats_all)); + rc = ipa_query_teth_stats(IPA_CLIENT_WLAN1_PROD, con_stats, reset); + if (rc) { + IPAERR("IPA_CLIENT_WLAN1_PROD query failed %d\n", rc); + kfree(con_stats); + return rc; + } + + IPAWANDBG("wlan: v4_tx_p(%d) b(%lld) v6_tx_p(%d) b(%lld)\n", + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts, + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes, + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts, + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes); + + /* update the wlan UL stats */ + data->ipv4_tx_packets += + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts; + data->ipv6_tx_packets += + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts; + data->ipv4_tx_bytes += + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes; + data->ipv6_tx_bytes += + con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes; + + IPAWANDBG("v4_tx_p(%lu) v6_tx_p(%lu) v4_tx_b(%lu) v6_tx_b(%lu)\n", + (unsigned long int) data->ipv4_tx_packets, + (unsigned long int) data->ipv6_tx_packets, + (unsigned long int) data->ipv4_tx_bytes, + (unsigned long int) data->ipv6_tx_bytes); + kfree(con_stats); + return rc; +} + + int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, bool reset) { @@ -3302,11 +3429,26 @@ int rmnet_ipa3_query_tethering_stats_all( } else { IPAWANDBG_LOW(" query modem-backhaul stats\n"); tether_stats.ipa_client = data->ipa_client; - rc = rmnet_ipa3_query_tethering_stats_modem( - &tether_stats, data->reset_stats); - if (rc) { - IPAWANERR("modem WAN_IOC_QUERY_TETHER_STATS failed\n"); - return rc; + if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0 || + !ipa3_ctx->hw_stats.enabled) { + IPAWANDBG("hw version %d,hw_stats.enabled %d\n", + ipa3_ctx->ipa_hw_type, + ipa3_ctx->hw_stats.enabled); + /* get modem stats from QMI */ + rc = rmnet_ipa3_query_tethering_stats_modem( + &tether_stats, data->reset_stats); + if (rc) { + IPAWANERR("modem QUERY_TETHER_STATS failed\n"); + return rc; + } + } else { + /* get modem stats from IPA-HW counters */ + rc = rmnet_ipa3_query_tethering_stats_hw( + &tether_stats, data->reset_stats); + if (rc) { + IPAWANERR("modem QUERY_TETHER_STATS failed\n"); + return rc; + } } data->tx_bytes = tether_stats.ipv4_tx_bytes + tether_stats.ipv6_tx_bytes; @@ -3460,6 +3602,8 @@ void ipa3_q6_handshake_complete(bool ssr_bootup) */ rmnet_ipa_get_network_stats_and_update(); } + + imp_handle_modem_ready(); } static inline bool rmnet_ipa3_check_any_client_inited diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c index fb8f91643f624fef1b49574c688e5f01e42d74f2..3eb263722887627d11cc008dfffb44ce8cff079c 100644 --- a/drivers/platform/msm/msm_11ad/msm_11ad.c +++ b/drivers/platform/msm/msm_11ad/msm_11ad.c @@ -131,7 +131,8 @@ struct msm11ad_ctx { /* cpu boost support */ bool use_cpu_boost; bool is_cpu_boosted; - struct cpumask boost_cpu; + struct cpumask boost_cpu_0; + struct cpumask boost_cpu_1; bool keep_radio_on_during_sleep; int features; @@ -964,13 +965,22 @@ static void msm_11ad_init_cpu_boost(struct msm11ad_ctx *ctx) if (minfreq != maxfreq) { /* - * use first big core for boost, to be compatible with WLAN + * use first 2 big cores for boost, to be compatible with WLAN * which assigns big cores from the last index */ ctx->use_cpu_boost = true; - cpumask_clear(&ctx->boost_cpu); - cpumask_set_cpu(boost_cpu, &ctx->boost_cpu); - dev_info(ctx->dev, "CPU boost: will use core %d\n", boost_cpu); + cpumask_clear(&ctx->boost_cpu_0); + cpumask_clear(&ctx->boost_cpu_1); + cpumask_set_cpu(boost_cpu, &ctx->boost_cpu_0); + if (boost_cpu < (nr_cpu_ids - 1)) { + cpumask_set_cpu(boost_cpu + 1, &ctx->boost_cpu_1); + dev_info(ctx->dev, "CPU boost: will use cores %d - %d\n", + boost_cpu, boost_cpu + 1); + } else { + cpumask_set_cpu(boost_cpu, &ctx->boost_cpu_1); + dev_info(ctx->dev, "CPU boost: will use core %d\n", + boost_cpu); + } } else { ctx->use_cpu_boost = false; dev_info(ctx->dev, "CPU boost disabled, uniform topology\n"); @@ -1253,7 +1263,8 @@ static struct platform_driver msm_11ad_driver = { }; module_platform_driver(msm_11ad_driver); -static void msm_11ad_set_boost_affinity(struct msm11ad_ctx *ctx) +static void msm_11ad_set_affinity_hint(struct msm11ad_ctx *ctx, uint irq, + struct cpumask *boost_cpu) { /* * There is a very small window where user space can change the @@ -1264,15 +1275,14 @@ static void msm_11ad_set_boost_affinity(struct msm11ad_ctx *ctx) struct irq_desc *desc; while (retries > 0) { - irq_modify_status(ctx->pcidev->irq, IRQ_NO_BALANCING, 0); - rc = irq_set_affinity_hint(ctx->pcidev->irq, &ctx->boost_cpu); + irq_modify_status(irq, IRQ_NO_BALANCING, 0); + rc = irq_set_affinity_hint(irq, boost_cpu); if (rc) dev_warn(ctx->dev, "Failed set affinity, rc=%d\n", rc); - irq_modify_status(ctx->pcidev->irq, 0, IRQ_NO_BALANCING); - desc = irq_to_desc(ctx->pcidev->irq); - if (cpumask_equal(desc->irq_common_data.affinity, - &ctx->boost_cpu)) + irq_modify_status(irq, 0, IRQ_NO_BALANCING); + desc = irq_to_desc(irq); + if (cpumask_equal(desc->irq_common_data.affinity, boost_cpu)) break; retries--; } @@ -1281,15 +1291,30 @@ static void msm_11ad_set_boost_affinity(struct msm11ad_ctx *ctx) dev_warn(ctx->dev, "failed to set CPU boost affinity\n"); } -static void msm_11ad_clear_boost_affinity(struct msm11ad_ctx *ctx) +static void msm_11ad_set_boost_affinity(struct msm11ad_ctx *ctx) +{ + msm_11ad_set_affinity_hint(ctx, ctx->pcidev->irq, &ctx->boost_cpu_0); + /* boost rx and tx interrupts */ + if (ctx->features & BIT(WIL_PLATFORM_FEATURE_TRIPLE_MSI)) + msm_11ad_set_affinity_hint(ctx, ctx->pcidev->irq + 1, + &ctx->boost_cpu_1); +} + +static void msm_11ad_clear_affinity_hint(struct msm11ad_ctx *ctx, uint irq) { int rc; - irq_modify_status(ctx->pcidev->irq, IRQ_NO_BALANCING, 0); - rc = irq_set_affinity_hint(ctx->pcidev->irq, NULL); + irq_modify_status(irq, IRQ_NO_BALANCING, 0); + rc = irq_set_affinity_hint(irq, NULL); if (rc) - dev_warn(ctx->dev, - "Failed clear affinity, rc=%d\n", rc); + dev_warn(ctx->dev, "Failed clear affinity, rc=%d\n", rc); +} + +static void msm_11ad_clear_boost_affinity(struct msm11ad_ctx *ctx) +{ + msm_11ad_clear_affinity_hint(ctx, ctx->pcidev->irq); + if (ctx->features & BIT(WIL_PLATFORM_FEATURE_TRIPLE_MSI)) + msm_11ad_clear_affinity_hint(ctx, ctx->pcidev->irq + 1); } /* hooks for the wil6210 driver */ diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c index 077e3a65cda3945ba7454b1300671a2b5c6f38be..618aa60a4aecd1a0a9d60f8ea56132b45dcdeb66 100644 --- a/drivers/platform/msm/msm_ext_display.c +++ b/drivers/platform/msm/msm_ext_display.c @@ -404,6 +404,7 @@ int msm_ext_disp_register_audio_codec(struct platform_device *pdev, return ret; } +EXPORT_SYMBOL(msm_ext_disp_register_audio_codec); int msm_ext_disp_select_audio_codec(struct platform_device *pdev, struct msm_ext_disp_codec_id *codec) @@ -441,6 +442,7 @@ int msm_ext_disp_select_audio_codec(struct platform_device *pdev, return ret; } +EXPORT_SYMBOL(msm_ext_disp_select_audio_codec); static int msm_ext_disp_validate_intf(struct msm_ext_disp_init_data *init_data) { diff --git a/drivers/platform/msm/seemp_core/Makefile b/drivers/platform/msm/seemp_core/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a26db430718d6812fb0ac266d8cc9b9b1364764f --- /dev/null +++ b/drivers/platform/msm/seemp_core/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -Iinclude/linux +obj-$(CONFIG_SEEMP_CORE) += seemp_core.o +seemp_core-objs:= seemp_logk.o seemp_ringbuf.o seemp_event_encoder.o \ No newline at end of file diff --git a/drivers/platform/msm/seemp_core/seemp_event_encoder.c b/drivers/platform/msm/seemp_core/seemp_event_encoder.c new file mode 100644 index 0000000000000000000000000000000000000000..e73b69c11e3c9256013de9d617ef0c70daf95e16 --- /dev/null +++ b/drivers/platform/msm/seemp_core/seemp_event_encoder.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2015, 2017-2018, 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 +#include "seemp_logk.h" +#include "seemp_event_encoder.h" + +static char *scan_id(char *s); +static void encode_seemp_section(char *section_start, char *section_eq, + char *section_end, bool param, bool numeric, + int id, __s32 numeric_value); + +static void check_param_range(char *section_eq, bool param, + bool *numeric, int val_len, __s32 *numeric_value) +{ + long long_value = 0; + + if (param && *numeric) { + /*check if 2 bytes & in[-99999,999999]*/ + *numeric = (val_len >= 2) && (val_len <= 6); + if (*numeric) { + if (kstrtol(section_eq + 1, 10, &long_value) + != 0) { + *numeric = false; + } else { + *numeric_value = (__s32)long_value; + /* We are checking whether the value + * lies within 16bits + */ + *numeric = (long_value >= -32768) && + (long_value <= 32767); + } + } + } +} + +void encode_seemp_params(struct seemp_logk_blk *blk) +{ + struct seemp_logk_blk tmp; + char *s = 0; + char *msg_section_start = 0; + char *msg_section_eq = 0; + char *msg_s = 0; + + memcpy(tmp.payload.msg, blk->payload.msg, BLK_MAX_MSG_SZ); + s = tmp.payload.msg + 1; + tmp.payload.msg[BLK_MAX_MSG_SZ - 1] = 0; /* zero-terminate */ + + while (true) { + char *section_start = s; + char *section_eq = scan_id(s); + bool param = (section_eq - section_start >= 2) && + (*section_eq == '=') && (section_eq[1] != ' '); + bool numeric = false; + int id = -1; + __s32 numeric_value = 0; + int id_len; + int val_len; + char ch; + + if (param) { + id = param_id_index(section_start, section_eq); + + if (id < 0) + param = false; + } + + if (!param) { + s = section_eq; + while ((*s != 0) && (*s != ',')) + s++; + } else { + s = section_eq + 1; /* equal sign */ + numeric = (*s == '-') || ((*s >= '0') && (*s <= '9')); + + if (numeric) + s++; /* first char of number */ + + while ((*s != 0) && (*s != ',')) { + if (*s == '=') + param = false; + else if (!((*s >= '0') && (*s <= '9'))) + numeric = false; + + s++; + } + + if (param) { + id_len = section_eq - section_start; + val_len = s - (section_eq + 1); + param = (id_len >= 2) && (id_len <= 31) + && (val_len <= 31); + ch = *s; + *s = 0; + + check_param_range(section_eq, param, + &numeric, val_len, &numeric_value); + *s = ch; + } + } + + msg_section_start = blk->payload.msg + (section_start - + tmp.payload.msg); + msg_section_eq = blk->payload.msg + (section_eq - + tmp.payload.msg); + msg_s = blk->payload.msg + (s - tmp.payload.msg); + encode_seemp_section(msg_section_start, msg_section_eq, + msg_s, param, numeric, id, numeric_value); + + if (*s == 0) + break; + + s++; + } + + blk->len = s - blk->payload.msg; +} + +static char *scan_id(char *s) +{ + while ((*s == '_') || + ((*s >= 'A') && (*s <= 'Z')) || + ((*s >= 'a') && (*s <= 'z'))) { + s++; + } + + return s; +} + +static void encode_seemp_section(char *section_start, char *section_eq, + char *section_end, bool param, bool numeric, + int id, __s32 numeric_value) +{ + param = param && (section_eq + 1 < section_end); + + if (!param) { + /* Encode skip section */ + int skip_len = section_end - section_start; + char skip_len_hi = skip_len & 0xE0; + char skip_len_lo = skip_len & 0x1F; + + if (skip_len < 32) { + section_start[-1] = 0xC0 | skip_len_lo; + /* [1:1:0:0 0000] */ + } else { + section_start[-1] = 0xE0 | skip_len_lo; + /* [1:1:1:0 0000] */ + + if (skip_len_hi & 0x20) + section_start[0] |= 0x80; + + if (skip_len_hi & 0x40) + section_start[1] |= 0x80; + + if (skip_len_hi & 0x80) + section_start[2] |= 0x80; + } + } else { + /* Encode ID=VALUE section */ + char id_len = section_eq - section_start; + char value_len = section_end - (section_eq + 1); + + section_start[-1] = 0x00 | id_len; + *(__s16 *)section_start = id; + section_eq[0] = (!numeric ? 0x80 : 0x00) | value_len; + + if (numeric) + *(__s16 *)(section_eq + 1) = numeric_value; + } +} diff --git a/drivers/platform/msm/seemp_core/seemp_event_encoder.h b/drivers/platform/msm/seemp_core/seemp_event_encoder.h new file mode 100644 index 0000000000000000000000000000000000000000..a64b18b806cc8bde7a023a6949837ca502ad5821 --- /dev/null +++ b/drivers/platform/msm/seemp_core/seemp_event_encoder.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2015, 2017-2018, 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 __SEEMP_EVENT_ENCODER_H__ +#define __SEEMP_EVENT_ENCODER_H__ + +#include "seemp_logk.h" + +void encode_seemp_params(struct seemp_logk_blk *blk); + +#endif /* __SEEMP_EVENT_ENCODER_H__ */ diff --git a/drivers/platform/msm/seemp_core/seemp_logk.c b/drivers/platform/msm/seemp_core/seemp_logk.c new file mode 100644 index 0000000000000000000000000000000000000000..ca77f925c06b8e2619c3e1f70b6140a6cc0a5ad6 --- /dev/null +++ b/drivers/platform/msm/seemp_core/seemp_logk.c @@ -0,0 +1,687 @@ +/* + * Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "seemp: %s: " fmt, __func__ + +#include "seemp_logk.h" +#include "seemp_ringbuf.h" + +#ifndef VM_RESERVED +#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) +#endif + +#define MASK_BUFFER_SIZE 256 +#define FOUR_MB 4 +#define YEAR_BASE 1900 + +static struct seemp_logk_dev *slogk_dev; + +static unsigned int ring_sz = FOUR_MB; + +/* + * default is besteffort, apps do not get blocked + */ +static unsigned int block_apps; + + +/* + * When this flag is turned on, + * kmalloc should be used for ring buf allocation + * otherwise it is vmalloc. + * default is to use vmalloc + * kmalloc has a limit of 4MB + */ +unsigned int kmalloc_flag; + +static struct class *cl; + +static rwlock_t filter_lock; +static struct seemp_source_mask *pmask; +static unsigned int num_sources; + +static long seemp_logk_reserve_rdblks( + struct seemp_logk_dev *sdev, unsigned long arg); +static long seemp_logk_set_mask(unsigned long arg); +static long seemp_logk_set_mapping(unsigned long arg); +static long seemp_logk_check_filter(unsigned long arg); + +void* (*seemp_logk_kernel_begin)(char **buf); + +void (*seemp_logk_kernel_end)(void *blck); + +/* + * the last param is the permission bits * + * kernel logging is done in three steps: + * (1) fetch a block, fill everything except payload. + * (2) return payload pointer to the caller. + * (3) caller fills its data directly into the payload area. + * (4) caller invoked finish_record(), to finish writing. + */ +void *seemp_logk_kernel_start_record(char **buf) +{ + struct seemp_logk_blk *blk; + struct timespec now; + struct tm ts; + int idx; + int ret; + + DEFINE_WAIT(write_wait); + + ret = 0; + idx = 0; + now = current_kernel_time(); + blk = ringbuf_fetch_wr_block(slogk_dev); + if (!blk) { + /* + * there is no blk to write + * if block_apps == 0; quietly return + */ + if (!block_apps) { + *buf = NULL; + return NULL; + } + /*else wait for the blks to be available*/ + while (1) { + mutex_lock(&slogk_dev->lock); + prepare_to_wait(&slogk_dev->writers_wq, + &write_wait, TASK_INTERRUPTIBLE); + ret = (slogk_dev->num_write_avail_blks <= 0); + if (!ret) { + /* don't have to wait*/ + break; + } + mutex_unlock(&slogk_dev->lock); + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + + finish_wait(&slogk_dev->writers_wq, &write_wait); + if (ret) + return NULL; + + idx = slogk_dev->write_idx; + slogk_dev->write_idx = + (slogk_dev->write_idx + 1) % slogk_dev->num_tot_blks; + slogk_dev->num_write_avail_blks--; + slogk_dev->num_write_in_prog_blks++; + slogk_dev->num_writers++; + + blk = &slogk_dev->ring[idx]; + /*mark block invalid*/ + blk->status = 0x0; + mutex_unlock(&slogk_dev->lock); + } + + blk->version = OBSERVER_VERSION; + blk->pid = current->tgid; + blk->tid = current->pid; + blk->uid = (current_uid()).val; + blk->sec = now.tv_sec; + blk->nsec = now.tv_nsec; + strlcpy(blk->appname, current->comm, TASK_COMM_LEN); + time_to_tm(now.tv_sec, 0, &ts); + ts.tm_year += YEAR_BASE; + ts.tm_mon += 1; + + snprintf(blk->ts, TS_SIZE, "%04ld-%02d-%02d %02d:%02d:%02d", + ts.tm_year, ts.tm_mon, ts.tm_mday, + ts.tm_hour, ts.tm_min, ts.tm_sec); + + *buf = blk->payload.msg; + + return blk; +} + +void seemp_logk_kernel_end_record(void *blck) +{ + struct seemp_logk_blk *blk = (struct seemp_logk_blk *)blck; + + if (blk) { + /*update status at the very end*/ + blk->status |= 0x1; + blk->uid = (current_uid()).val; + + ringbuf_finish_writer(slogk_dev, blk); + } +} + +static int seemp_logk_usr_record(const char __user *buf, size_t count) +{ + struct seemp_logk_blk *blk; + struct seemp_logk_blk usr_blk; + struct seemp_logk_blk *local_blk; + struct timespec now; + struct tm ts; + int idx, ret; + + DEFINE_WAIT(write_wait); + + if (buf) { + local_blk = (struct seemp_logk_blk *)buf; + if (copy_from_user(&usr_blk.pid, &local_blk->pid, + sizeof(usr_blk.pid)) != 0) + return -EFAULT; + if (copy_from_user(&usr_blk.tid, &local_blk->tid, + sizeof(usr_blk.tid)) != 0) + return -EFAULT; + if (copy_from_user(&usr_blk.uid, &local_blk->uid, + sizeof(usr_blk.uid)) != 0) + return -EFAULT; + if (copy_from_user(&usr_blk.len, &local_blk->len, + sizeof(usr_blk.len)) != 0) + return -EFAULT; + if (copy_from_user(&usr_blk.payload, &local_blk->payload, + sizeof(struct blk_payload)) != 0) + return -EFAULT; + } else { + return -EFAULT; + } + idx = ret = 0; + now = current_kernel_time(); + blk = ringbuf_fetch_wr_block(slogk_dev); + if (!blk) { + if (!block_apps) + return 0; + while (1) { + mutex_lock(&slogk_dev->lock); + prepare_to_wait(&slogk_dev->writers_wq, + &write_wait, + TASK_INTERRUPTIBLE); + ret = (slogk_dev->num_write_avail_blks <= 0); + if (!ret) + break; + mutex_unlock(&slogk_dev->lock); + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + finish_wait(&slogk_dev->writers_wq, &write_wait); + if (ret) + return -EINTR; + + idx = slogk_dev->write_idx; + slogk_dev->write_idx = + (slogk_dev->write_idx + 1) % slogk_dev->num_tot_blks; + slogk_dev->num_write_avail_blks--; + slogk_dev->num_write_in_prog_blks++; + slogk_dev->num_writers++; + blk = &slogk_dev->ring[idx]; + /*mark block invalid*/ + blk->status = 0x0; + mutex_unlock(&slogk_dev->lock); + } + if (usr_blk.len > sizeof(struct blk_payload)-1) + usr_blk.len = sizeof(struct blk_payload)-1; + + memcpy(&blk->payload, &usr_blk.payload, sizeof(struct blk_payload)); + blk->pid = usr_blk.pid; + blk->uid = usr_blk.uid; + blk->tid = usr_blk.tid; + blk->sec = now.tv_sec; + blk->nsec = now.tv_nsec; + time_to_tm(now.tv_sec, 0, &ts); + ts.tm_year += YEAR_BASE; + ts.tm_mon += 1; + snprintf(blk->ts, TS_SIZE, "%02ld-%02d-%02d %02d:%02d:%02d", + ts.tm_year, ts.tm_mon, ts.tm_mday, + ts.tm_hour, ts.tm_min, ts.tm_sec); + strlcpy(blk->appname, current->comm, TASK_COMM_LEN); + blk->status |= 0x1; + ringbuf_finish_writer(slogk_dev, blk); + return ret; +} + +static void seemp_logk_attach(void) +{ + seemp_logk_kernel_end = seemp_logk_kernel_end_record; + seemp_logk_kernel_begin = seemp_logk_kernel_start_record; +} + +static void seemp_logk_detach(void) +{ + seemp_logk_kernel_begin = NULL; + seemp_logk_kernel_end = NULL; +} + +static ssize_t +seemp_logk_write(struct file *file, const char __user *buf, size_t count, + loff_t *ppos) +{ + return seemp_logk_usr_record(buf, count); +} + +static int +seemp_logk_open(struct inode *inode, struct file *filp) +{ + int ret; + + /*disallow seeks on this file*/ + ret = nonseekable_open(inode, filp); + if (ret) { + pr_err("ret= %d\n", ret); + return ret; + } + + slogk_dev->minor = iminor(inode); + filp->private_data = slogk_dev; + + return 0; +} + +static bool seemp_logk_get_bit_from_vector(__u8 *pVec, __u32 index) +{ + unsigned int byte_num = index/8; + unsigned int bit_num = index%8; + unsigned char byte; + + if (DIV_ROUND_UP(index, 8) > MASK_BUFFER_SIZE) + return false; + + byte = pVec[byte_num]; + + return !(byte & (1 << bit_num)); +} + +static long seemp_logk_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct seemp_logk_dev *sdev; + int ret; + + sdev = (struct seemp_logk_dev *) filp->private_data; + + if (cmd == SEEMP_CMD_RESERVE_RDBLKS) { + return seemp_logk_reserve_rdblks(sdev, arg); + } else if (cmd == SEEMP_CMD_RELEASE_RDBLKS) { + mutex_lock(&sdev->lock); + sdev->read_idx = (sdev->read_idx + sdev->num_read_in_prog_blks) + % sdev->num_tot_blks; + sdev->num_write_avail_blks += sdev->num_read_in_prog_blks; + ret = sdev->num_read_in_prog_blks; + sdev->num_read_in_prog_blks = 0; + /*wake up any waiting writers*/ + mutex_unlock(&sdev->lock); + if (ret && block_apps) + wake_up_interruptible(&sdev->writers_wq); + } else if (cmd == SEEMP_CMD_GET_RINGSZ) { + if (copy_to_user((unsigned int *)arg, &sdev->ring_sz, + sizeof(unsigned int))) + return -EFAULT; + } else if (cmd == SEEMP_CMD_GET_BLKSZ) { + if (copy_to_user((unsigned int *)arg, &sdev->blk_sz, + sizeof(unsigned int))) + return -EFAULT; + } else if (cmd == SEEMP_CMD_SET_MASK) { + return seemp_logk_set_mask(arg); + } else if (cmd == SEEMP_CMD_SET_MAPPING) { + return seemp_logk_set_mapping(arg); + } else if (cmd == SEEMP_CMD_CHECK_FILTER) { + return seemp_logk_check_filter(arg); + } + pr_err("Invalid Request %X\n", cmd); + return -ENOIOCTLCMD; +} + +static long seemp_logk_reserve_rdblks( + struct seemp_logk_dev *sdev, unsigned long arg) +{ + int ret; + struct read_range rrange; + + DEFINE_WAIT(read_wait); + + mutex_lock(&sdev->lock); + if (sdev->num_writers > 0 || sdev->num_read_avail_blks <= 0) { + ret = -EPERM; + pr_debug("(reserve): blocking, cannot read.\n"); + pr_debug("num_writers=%d num_read_avail_blks=%d\n", + sdev->num_writers, + sdev->num_read_avail_blks); + mutex_unlock(&sdev->lock); + /* + * unlock the device + * wait on a wait queue + * after wait, grab the dev lock again + */ + while (1) { + mutex_lock(&sdev->lock); + prepare_to_wait(&sdev->readers_wq, &read_wait, + TASK_INTERRUPTIBLE); + ret = (sdev->num_writers > 0 || + sdev->num_read_avail_blks <= 0); + if (!ret) { + /*don't have to wait*/ + break; + } + mutex_unlock(&sdev->lock); + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + + finish_wait(&sdev->readers_wq, &read_wait); + if (ret) + return -EINTR; + } + + /*sdev->lock is held at this point*/ + sdev->num_read_in_prog_blks = sdev->num_read_avail_blks; + sdev->num_read_avail_blks = 0; + rrange.start_idx = sdev->read_idx; + rrange.num = sdev->num_read_in_prog_blks; + mutex_unlock(&sdev->lock); + + if (copy_to_user((unsigned int *)arg, &rrange, + sizeof(struct read_range))) + return -EFAULT; + + return 0; +} + +static long seemp_logk_set_mask(unsigned long arg) +{ + __u8 buffer[256]; + int i; + unsigned int num_elements; + + if (copy_from_user(&num_elements, + (unsigned int __user *) arg, sizeof(unsigned int))) + return -EFAULT; + + read_lock(&filter_lock); + if (num_sources == 0) { + read_unlock(&filter_lock); + return -EINVAL; + } + + if (num_elements == 0 || + DIV_ROUND_UP(num_sources, 8) > MASK_BUFFER_SIZE) { + read_unlock(&filter_lock); + return -EINVAL; + } + + if (copy_from_user(buffer, + (__u8 *)arg, DIV_ROUND_UP(num_sources, 8))) { + read_unlock(&filter_lock); + return -EFAULT; + } + + read_unlock(&filter_lock); + write_lock(&filter_lock); + if (num_elements != num_sources) { + write_unlock(&filter_lock); + return -EPERM; + } + + for (i = 0; i < num_sources; i++) { + pmask[i].isOn = + seemp_logk_get_bit_from_vector( + (__u8 *)buffer, i); + } + write_unlock(&filter_lock); + return 0; +} + +static long seemp_logk_set_mapping(unsigned long arg) +{ + __u32 num_elements; + __u32 *pbuffer; + int i; + struct seemp_source_mask *pnewmask; + + if (copy_from_user(&num_elements, + (__u32 __user *)arg, sizeof(__u32))) + return -EFAULT; + + if ((num_elements == 0) || (num_elements > + (UINT_MAX / sizeof(struct seemp_source_mask)))) + return -EFAULT; + + write_lock(&filter_lock); + if (pmask != NULL) { + /* + * Mask is getting set again. + * seemp_core was probably restarted. + */ + struct seemp_source_mask *ptempmask; + + num_sources = 0; + ptempmask = pmask; + pmask = NULL; + kfree(ptempmask); + } + write_unlock(&filter_lock); + pbuffer = kmalloc_array(num_elements, + sizeof(struct seemp_source_mask), GFP_KERNEL); + if (pbuffer == NULL) + return -ENOMEM; + + /* + * Use our new table as scratch space for now. + * We copy an ordered list of hash values into our buffer. + */ + if (copy_from_user(pbuffer, &((__u32 __user *)arg)[1], + num_elements*sizeof(unsigned int))) { + kfree(pbuffer); + return -EFAULT; + } + /* + * We arrange the user data into a more usable form. + * This is done in-place. + */ + pnewmask = (struct seemp_source_mask *) pbuffer; + for (i = num_elements - 1; i >= 0; i--) { + pnewmask[i].hash = pbuffer[i]; + /* Observer is off by default*/ + pnewmask[i].isOn = 0; + } + write_lock(&filter_lock); + pmask = pnewmask; + num_sources = num_elements; + write_unlock(&filter_lock); + return 0; +} + +static long seemp_logk_check_filter(unsigned long arg) +{ + int i; + unsigned int hash = (unsigned int) arg; + + /* + * This lock may be a bit long. + * If it is a problem, it can be fixed. + */ + read_lock(&filter_lock); + for (i = 0; i < num_sources; i++) { + if (hash == pmask[i].hash) { + int result = pmask[i].isOn; + + read_unlock(&filter_lock); + return result; + } + } + read_unlock(&filter_lock); + return 0; +} + +static int seemp_logk_mmap(struct file *filp, + struct vm_area_struct *vma) +{ + int ret; + char *vptr; + unsigned long length, pfn; + unsigned long start = vma->vm_start; + + length = vma->vm_end - vma->vm_start; + + if (length > (unsigned long) slogk_dev->ring_sz) { + pr_err("len check failed\n"); + return -EIO; + } + + vma->vm_flags |= VM_RESERVED | VM_SHARED; + vptr = (char *) slogk_dev->ring; + ret = 0; + + if (kmalloc_flag) { + ret = remap_pfn_range(vma, + start, + virt_to_phys((void *) + ((unsigned long)slogk_dev->ring)) >> PAGE_SHIFT, + length, + vma->vm_page_prot); + if (ret != 0) { + pr_err("remap_pfn_range() fails with ret = %d\n", + ret); + return -EAGAIN; + } + } else { + while (length > 0) { + pfn = vmalloc_to_pfn(vptr); + + ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, + vma->vm_page_prot); + if (ret < 0) { + pr_err("remap_pfn_range() fails with ret = %d\n", + ret); + return ret; + } + start += PAGE_SIZE; + vptr += PAGE_SIZE; + length -= PAGE_SIZE; + } + } + + return 0; +} + +static const struct file_operations seemp_logk_fops = { + .write = seemp_logk_write, + .open = seemp_logk_open, + .unlocked_ioctl = seemp_logk_ioctl, + .compat_ioctl = seemp_logk_ioctl, + .mmap = seemp_logk_mmap, +}; + +__init int seemp_logk_init(void) +{ + int ret; + int devno = 0; + + num_sources = 0; + kmalloc_flag = 0; + block_apps = 0; + pmask = NULL; + + if (kmalloc_flag && ring_sz > FOUR_MB) { + pr_err("kmalloc cannot allocate > 4MB\n"); + return -ENOMEM; + } + + ring_sz = ring_sz * SZ_1M; + if (ring_sz <= 0) { + pr_err("Too small a ring_sz=%d\n", ring_sz); + return -EINVAL; + } + + slogk_dev = kmalloc(sizeof(*slogk_dev), GFP_KERNEL); + if (slogk_dev == NULL) + return -ENOMEM; + + slogk_dev->ring_sz = ring_sz; + slogk_dev->blk_sz = sizeof(struct seemp_logk_blk); + /*initialize ping-pong buffers*/ + ret = ringbuf_init(slogk_dev); + if (ret < 0) { + pr_err("Init Failed, ret = %d\n", ret); + goto pingpong_fail; + } + + ret = alloc_chrdev_region(&devno, 0, seemp_LOGK_NUM_DEVS, + seemp_LOGK_DEV_NAME); + if (ret < 0) { + pr_err("alloc_chrdev_region failed with ret = %d\n", + ret); + goto register_fail; + } + + slogk_dev->major = MAJOR(devno); + + pr_debug("logk: major# = %d\n", slogk_dev->major); + + cl = class_create(THIS_MODULE, seemp_LOGK_DEV_NAME); + if (cl == NULL) { + pr_err("class create failed"); + goto cdev_fail; + } + if (device_create(cl, NULL, devno, NULL, + seemp_LOGK_DEV_NAME) == NULL) { + pr_err("device create failed"); + goto class_destroy_fail; + } + cdev_init(&(slogk_dev->cdev), &seemp_logk_fops); + + slogk_dev->cdev.owner = THIS_MODULE; + ret = cdev_add(&(slogk_dev->cdev), MKDEV(slogk_dev->major, 0), 1); + if (ret) { + pr_err("cdev_add failed with ret = %d", ret); + goto class_destroy_fail; + } + + seemp_logk_attach(); + mutex_init(&slogk_dev->lock); + init_waitqueue_head(&slogk_dev->readers_wq); + init_waitqueue_head(&slogk_dev->writers_wq); + rwlock_init(&filter_lock); + return 0; +class_destroy_fail: + class_destroy(cl); +cdev_fail: + unregister_chrdev_region(devno, seemp_LOGK_NUM_DEVS); +register_fail: + ringbuf_cleanup(slogk_dev); +pingpong_fail: + kfree(slogk_dev); + return -EPERM; +} + +__exit void seemp_logk_cleanup(void) +{ + dev_t devno = MKDEV(slogk_dev->major, slogk_dev->minor); + + seemp_logk_detach(); + + cdev_del(&slogk_dev->cdev); + + unregister_chrdev_region(devno, seemp_LOGK_NUM_DEVS); + ringbuf_cleanup(slogk_dev); + kfree(slogk_dev); + + if (pmask != NULL) { + kfree(pmask); + pmask = NULL; + } +} + +module_init(seemp_logk_init); +module_exit(seemp_logk_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("seemp Observer"); diff --git a/drivers/platform/msm/seemp_core/seemp_logk.h b/drivers/platform/msm/seemp_core/seemp_logk.h new file mode 100644 index 0000000000000000000000000000000000000000..acef9fa5955813669fd71220f32391dfa6e848e3 --- /dev/null +++ b/drivers/platform/msm/seemp_core/seemp_logk.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2014-2015, 2017-2018, 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 __SEEMP_LOGK_H__ +#define __SEEMP_LOGK_H__ + +#define OBSERVER_VERSION 0x01 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define seemp_LOGK_NUM_DEVS 1 +#define seemp_LOGK_DEV_NAME "seemplog" + +/* + * The logcat driver on Android uses four 256k ring buffers + * here, we use two ring buffers of the same size. + * we think this is reasonable + */ +#define FULL_BUF_SIZE (64 * 1024 * 1024) +#define HALF_BUF_SIZE (32 * 1024 * 1024) +#define FULL_BLOCKS (8 * 1024) +#define HALF_BLOCKS (4 * 1024) + +#define READER_NOT_READY 0 +#define READER_READY 1 + +#define MAGIC 'z' + +#define SEEMP_CMD_RESERVE_RDBLKS _IOR(MAGIC, 1, int) +#define SEEMP_CMD_RELEASE_RDBLKS _IO(MAGIC, 2) +#define SEEMP_CMD_GET_RINGSZ _IOR(MAGIC, 3, int) +#define SEEMP_CMD_GET_BLKSZ _IOR(MAGIC, 4, int) +#define SEEMP_CMD_SET_MASK _IO(MAGIC, 5) +#define SEEMP_CMD_SET_MAPPING _IO(MAGIC, 6) +#define SEEMP_CMD_CHECK_FILTER _IOR(MAGIC, 7, int) + +struct read_range { + int start_idx; + int num; +}; + +struct seemp_logk_dev { + unsigned int major; + unsigned int minor; + + struct cdev cdev; + struct class *cls; + /*the full buffer*/ + struct seemp_logk_blk *ring; + /*an array of blks*/ + unsigned int ring_sz; + unsigned int blk_sz; + + int num_tot_blks; + + int num_write_avail_blks; + int num_write_in_prog_blks; + + int num_read_avail_blks; + int num_read_in_prog_blks; + + int num_writers; + + /* + * there is always one reader + * which is the observer daemon + * therefore there is no necessity + * for num_readers variable + */ + + /* + * read_idx and write_idx loop through from zero to ring_sz, + * and then back to zero in a circle, as they advance + * based on the reader's and writers' accesses + */ + int read_idx; + + int write_idx; + + /* + * wait queues + * readers_wq: implement wait for readers + * writers_wq: implement wait for writers + * + * whether writers are blocked or not is driven by the policy: + * case 1: (best_effort_logging == 1) + * writers are not blocked, and + * when there is no mem in the ring to store logs, + * the logs are simply dropped. + * case 2: (best_effort_logging == 0) + * when there is no mem in the ring to store logs, + * the process gets blocked until there is space. + */ + wait_queue_head_t readers_wq; + wait_queue_head_t writers_wq; + + /* + * protects everything in the device + * including ring buffer and all the num_ variables + * spinlock_t lock; + */ + struct mutex lock; +}; + +#define BLK_SIZE 256 +#define BLK_HDR_SIZE 64 +#define TS_SIZE 20 +#define BLK_MAX_MSG_SZ (BLK_SIZE - BLK_HDR_SIZE) + +struct blk_payload { + __u32 api_id; /* event API id */ + char msg[BLK_MAX_MSG_SZ]; /* event parameters */ +} __packed; + +struct seemp_logk_blk { + __u8 status; /* bits: 0->valid/invalid; 1-7: unused as of now! */ + __u16 len; /* length of the payload */ + __u8 version; /* version number */ + __s32 pid; /* generating process's pid */ + __s32 uid; /* generating process's uid - app specific */ + __s32 tid; /* generating process's tid */ + __s32 sec; /* seconds since Epoch */ + __s32 nsec; /* nanoseconds */ + char ts[TS_SIZE]; /* Time Stamp */ + char appname[TASK_COMM_LEN]; + struct blk_payload payload; +} __packed; + + +extern unsigned int kmalloc_flag; + +struct seemp_source_mask { + __u32 hash; + bool isOn; +}; +#endif diff --git a/drivers/platform/msm/seemp_core/seemp_ringbuf.c b/drivers/platform/msm/seemp_core/seemp_ringbuf.c new file mode 100644 index 0000000000000000000000000000000000000000..6438032e743e8266ce72a453784063238db7636c --- /dev/null +++ b/drivers/platform/msm/seemp_core/seemp_ringbuf.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "seemp: %s: " fmt, __func__ + +#include "seemp_logk.h" +#include "seemp_ringbuf.h" +#include "seemp_event_encoder.h" + +/*initial function no need to hold ring_lock*/ +int ringbuf_init(struct seemp_logk_dev *sdev) +{ + char *buf; + unsigned long virt_addr; + + if (kmalloc_flag) { + sdev->ring = kmalloc(sdev->ring_sz, GFP_KERNEL); + if (sdev->ring == NULL) + return -ENOMEM; + + buf = (char *)sdev->ring; + + /*reserve kmalloc memory as pages to make them remapable*/ + for (virt_addr = (unsigned long)buf; + virt_addr < (unsigned long)buf + sdev->ring_sz; + virt_addr += PAGE_SIZE) { + SetPageReserved(virt_to_page((virt_addr))); + } + } else { + sdev->ring = vmalloc(sdev->ring_sz); + if (sdev->ring == NULL) + return -ENOMEM; + + buf = (char *)sdev->ring; + + /*reserve vmalloc memory as pages to make them remapable*/ + for (virt_addr = (unsigned long)buf; + virt_addr < (unsigned long)buf + sdev->ring_sz; + virt_addr += PAGE_SIZE) { + SetPageReserved(vmalloc_to_page( + (unsigned long *) virt_addr)); + } + } + + memset(sdev->ring, 0, sdev->ring_sz); + + sdev->num_tot_blks = (sdev->ring_sz / BLK_SIZE); + sdev->num_writers = 0; + sdev->write_idx = 0; + sdev->read_idx = 0; + + sdev->num_write_avail_blks = sdev->num_tot_blks; + /*no. of blocks available for write*/ + sdev->num_write_in_prog_blks = 0; + /*no. of blocks held by writers to perform writes*/ + + sdev->num_read_avail_blks = 0; + /*no. of blocks ready for read*/ + sdev->num_read_in_prog_blks = 0; + /*no. of blocks held by the reader to perform read*/ + + return 0; +} + +void ringbuf_cleanup(struct seemp_logk_dev *sdev) +{ + unsigned long virt_addr; + + if (kmalloc_flag) { + for (virt_addr = (unsigned long)sdev->ring; + virt_addr < (unsigned long)sdev->ring + sdev->ring_sz; + virt_addr += PAGE_SIZE) { + /*clear all pages*/ + ClearPageReserved(virt_to_page((unsigned long *) + virt_addr)); + } + kfree(sdev->ring); + } else { + for (virt_addr = (unsigned long)sdev->ring; + virt_addr < (unsigned long)sdev->ring + sdev->ring_sz; + virt_addr += PAGE_SIZE) { + /*clear all pages*/ + ClearPageReserved(vmalloc_to_page((unsigned long *) + virt_addr)); + } + vfree(sdev->ring); + } +} + +struct seemp_logk_blk *ringbuf_fetch_wr_block + (struct seemp_logk_dev *sdev) +{ + struct seemp_logk_blk *blk = NULL; + int idx; + + mutex_lock(&sdev->lock); + if (sdev->num_write_avail_blks == 0) { + idx = -1; + mutex_unlock(&sdev->lock); + return blk; + } + + idx = sdev->write_idx; + sdev->write_idx = (sdev->write_idx + 1) % sdev->num_tot_blks; + sdev->num_write_avail_blks--; + sdev->num_write_in_prog_blks++; + sdev->num_writers++; + + blk = &sdev->ring[idx]; + blk->status = 0x0; + + mutex_unlock(&sdev->lock); + return blk; +} + +void ringbuf_finish_writer(struct seemp_logk_dev *sdev, + struct seemp_logk_blk *blk) +{ + /* Encode seemp parameters in multi-threaded mode (before mutex lock) */ + encode_seemp_params(blk); + + /* + * finish writing... + * the calling process will no longer access this block. + */ + mutex_lock(&sdev->lock); + + sdev->num_writers--; + sdev->num_write_in_prog_blks--; + sdev->num_read_avail_blks++; + + /*wake up any readers*/ + if (sdev->num_writers == 0) + wake_up_interruptible(&sdev->readers_wq); + + mutex_unlock(&sdev->lock); +} + +int ringbuf_count_marked(struct seemp_logk_dev *sdev) +{ + int i; + unsigned int marked; + + mutex_lock(&sdev->lock); + for (marked = 0, i = 0; i < sdev->num_tot_blks; i++) + if (sdev->ring[i].status & 0x1) + marked++; + mutex_unlock(&sdev->lock); + + return marked; +} diff --git a/drivers/platform/msm/seemp_core/seemp_ringbuf.h b/drivers/platform/msm/seemp_core/seemp_ringbuf.h new file mode 100644 index 0000000000000000000000000000000000000000..944ef7774b765f647cd1f09bd6844a04e53a528e --- /dev/null +++ b/drivers/platform/msm/seemp_core/seemp_ringbuf.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014-2015, 2017-2018, 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 __SEEMP_RINGBUF_H__ +#define __SEEMP_RINGBUF_H__ + +/* + * This header exports pingpong's API + */ + +int ringbuf_init(struct seemp_logk_dev *sdev); +struct seemp_logk_blk *ringbuf_fetch_wr_block +(struct seemp_logk_dev *sdev); +void ringbuf_finish_writer(struct seemp_logk_dev *sdev, + struct seemp_logk_blk *blk); +void ringbuf_cleanup(struct seemp_logk_dev *sdev); +int ringbuf_count_marked(struct seemp_logk_dev *sdev); + +#endif diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 7424e53157b098b0efedf51006323805a3b23f39..dd5043a6a114f1b5ea0a1c4758f7dbee03062033 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -1177,6 +1177,7 @@ static u8 kbd_previous_mode_bit; static bool kbd_led_present; static DEFINE_MUTEX(kbd_led_mutex); +static enum led_brightness kbd_led_level; /* * NOTE: there are three ways to set the keyboard backlight level. @@ -2020,6 +2021,7 @@ static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev) static int kbd_led_level_set(struct led_classdev *led_cdev, enum led_brightness value) { + enum led_brightness new_value = value; struct kbd_state state; struct kbd_state new_state; u16 num; @@ -2049,6 +2051,9 @@ static int kbd_led_level_set(struct led_classdev *led_cdev, } out: + if (ret == 0) + kbd_led_level = new_value; + mutex_unlock(&kbd_led_mutex); return ret; } @@ -2076,6 +2081,9 @@ static int __init kbd_led_init(struct device *dev) if (kbd_led.max_brightness) kbd_led.max_brightness--; } + + kbd_led_level = kbd_led_level_get(NULL); + ret = led_classdev_register(dev, &kbd_led); if (ret) kbd_led_present = false; @@ -2100,13 +2108,25 @@ static void kbd_led_exit(void) static int dell_laptop_notifier_call(struct notifier_block *nb, unsigned long action, void *data) { + bool changed = false; + enum led_brightness new_kbd_led_level; + switch (action) { case DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED: if (!kbd_led_present) break; - led_classdev_notify_brightness_hw_changed(&kbd_led, - kbd_led_level_get(&kbd_led)); + mutex_lock(&kbd_led_mutex); + new_kbd_led_level = kbd_led_level_get(&kbd_led); + if (kbd_led_level != new_kbd_led_level) { + kbd_led_level = new_kbd_led_level; + changed = true; + } + mutex_unlock(&kbd_led_mutex); + + if (changed) + led_classdev_notify_brightness_hw_changed(&kbd_led, + kbd_led_level); break; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 2242d6035d9e7e1f8d042855973a2349c6d7eb9f..c66efa06ac14fa97b1e50a4417f2f2c01ee7af8b 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -214,6 +214,10 @@ enum tpacpi_hkey_event_t { /* AC-related events */ TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */ + /* Further user-interface events */ + TP_HKEY_EV_PALM_DETECTED = 0x60b0, /* palm hoveres keyboard */ + TP_HKEY_EV_PALM_UNDETECTED = 0x60b1, /* palm removed */ + /* Misc */ TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ }; @@ -3973,6 +3977,12 @@ static bool hotkey_notify_6xxx(const u32 hkey, *send_acpi_ev = false; break; + case TP_HKEY_EV_PALM_DETECTED: + case TP_HKEY_EV_PALM_UNDETECTED: + /* palm detected hovering the keyboard, forward to user-space + * via netlink for consumption */ + return true; + default: pr_warn("unknown possible thermal alarm or keyboard event received\n"); known = false; @@ -9543,7 +9553,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { }, }; -static int __init set_ibm_param(const char *val, struct kernel_param *kp) +static int __init set_ibm_param(const char *val, const struct kernel_param *kp) { unsigned int i; struct ibm_struct *ibm; diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 54d88019c764d4bcdcdde05827cc2ea617192b6f..832d1481f380ddcd817d178f39a47ddc7622bdf7 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -33,6 +33,7 @@ #include #include #include +#include #define EMERGENCY_DLOAD_MAGIC1 0x322A4F99 #define EMERGENCY_DLOAD_MAGIC2 0xC67E4350 @@ -42,10 +43,11 @@ #define SCM_IO_DISABLE_PMIC_ARBITER 1 #define SCM_IO_DEASSERT_PS_HOLD 2 #define SCM_WDOG_DEBUG_BOOT_PART 0x9 -#define SCM_DLOAD_MODE 0X10 +#define SCM_DLOAD_FULLDUMP 0X10 #define SCM_EDLOAD_MODE 0X01 #define SCM_DLOAD_CMD 0x10 - +#define SCM_DLOAD_MINIDUMP 0X20 +#define SCM_DLOAD_BOTHDUMPS (SCM_DLOAD_MINIDUMP | SCM_DLOAD_FULLDUMP) static int restart_mode; static void *restart_reason, *dload_type_addr; @@ -72,6 +74,7 @@ static struct kobject dload_kobj; #endif static int in_panic; +static int dload_type = SCM_DLOAD_FULLDUMP; static void *dload_mode_addr; static bool dload_mode_enabled; static void *emergency_dload_mode_addr; @@ -80,7 +83,7 @@ static void *kaslr_imem_addr; #endif static bool scm_dload_supported; -static int dload_set(const char *val, struct kernel_param *kp); +static int dload_set(const char *val, const struct kernel_param *kp); /* interface for exporting attributes */ struct reset_attribute { struct attribute attr; @@ -140,7 +143,7 @@ static void set_dload_mode(int on) mb(); } - ret = scm_set_dload_mode(on ? SCM_DLOAD_MODE : 0, 0); + ret = scm_set_dload_mode(on ? dload_type : 0, 0); if (ret) pr_err("Failed to set secure DLOAD mode: %d\n", ret); @@ -179,7 +182,7 @@ static void enable_emergency_dload_mode(void) pr_err("Failed to set secure EDLOAD mode: %d\n", ret); } -static int dload_set(const char *val, struct kernel_param *kp) +static int dload_set(const char *val, const struct kernel_param *kp) { int ret; @@ -438,6 +441,9 @@ static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr, { uint32_t read_val, show_val; + if (!dload_type_addr) + return -ENODEV; + read_val = __raw_readl(dload_type_addr); if (read_val == EMMC_DLOAD_TYPE) show_val = 1; @@ -453,6 +459,9 @@ static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr, uint32_t enabled; int ret; + if (!dload_type_addr) + return -ENODEV; + ret = kstrtouint(buf, 0, &enabled); if (ret < 0) return ret; @@ -467,10 +476,57 @@ static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr, return count; } + +#ifdef CONFIG_QCOM_MINIDUMP +static DEFINE_MUTEX(tcsr_lock); + +static ssize_t show_dload_mode(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "DLOAD dump type: %s\n", + (dload_type == SCM_DLOAD_BOTHDUMPS) ? "both" : + ((dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full")); +} + +static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + if (sysfs_streq(buf, "full")) { + dload_type = SCM_DLOAD_FULLDUMP; + } else if (sysfs_streq(buf, "mini")) { + if (!msm_minidump_enabled()) { + pr_err("Minidump is not enabled\n"); + return -ENODEV; + } + dload_type = SCM_DLOAD_MINIDUMP; + } else if (sysfs_streq(buf, "both")) { + if (!msm_minidump_enabled()) { + pr_err("Minidump not enabled, setting fulldump only\n"); + dload_type = SCM_DLOAD_FULLDUMP; + return count; + } + dload_type = SCM_DLOAD_BOTHDUMPS; + } else{ + pr_err("Invalid Dump setup request..\n"); + pr_err("Supported dumps:'full', 'mini', or 'both'\n"); + return -EINVAL; + } + + mutex_lock(&tcsr_lock); + /*Overwrite TCSR reg*/ + set_dload_mode(dload_type); + mutex_unlock(&tcsr_lock); + return count; +} +RESET_ATTR(dload_mode, 0644, show_dload_mode, store_dload_mode); +#endif RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload); static struct attribute *reset_attrs[] = { &reset_attr_emmc_dload.attr, +#ifdef CONFIG_QCOM_MINIDUMP + &reset_attr_dload_mode.attr, +#endif NULL }; diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index d51ebd1da65e77ab678d414ce633db39fa04d93e..9dc7590e07cbe97795406b9810544f95faa2ae2a 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -785,6 +785,14 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info) return 0; } +static void axp288_charger_cancel_work(void *data) +{ + struct axp288_chrg_info *info = data; + + cancel_work_sync(&info->otg.work); + cancel_work_sync(&info->cable.work); +} + static int axp288_charger_probe(struct platform_device *pdev) { int ret, i, pirq; @@ -836,6 +844,11 @@ static int axp288_charger_probe(struct platform_device *pdev) return ret; } + /* Cancel our work on cleanup, register this before the notifiers */ + ret = devm_add_action(dev, axp288_charger_cancel_work, info); + if (ret) + return ret; + /* Register for extcon notification */ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt; diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index f24ac5c2113bb08b281195e178a5d4ad94ad73a9..2e1975ee6761b3e5e545bee6e5aade49974ee5ce 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -367,6 +367,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(batt_profile_version), POWER_SUPPLY_ATTR(batt_full_current), POWER_SUPPLY_ATTR(recharge_soc), + POWER_SUPPLY_ATTR(hvdcp_opti_allowed), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ @@ -374,6 +375,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(manufacturer), POWER_SUPPLY_ATTR(serial_number), POWER_SUPPLY_ATTR(battery_type), + POWER_SUPPLY_ATTR(cycle_counts), }; static struct attribute * diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index b62c83a8880ff35c4a2667d503d56682ba2698d8..38da3b4006602f092e970ebc0eae50f24653c022 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -75,4 +75,12 @@ config QPNP_QNOVO module. It also allows userspace code to read diagnostics of voltage and current measured during certain phases of the pulses. +config SMB1390_CHARGE_PUMP + tristate "SMB1390 Charge Pump" + depends on MFD_I2C_PMIC + help + Say Y to include support for SMB1390 Charge Pump. + SMB1390 is a div2 charge pump capable of delivering 6A charge current + with very high efficiency. + endmenu diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index 911a77a7966c86908d704ad3dc93b5c059758f12..e4cf697b73b4d178f5f1c84b4abfee7460bd4ee7 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o -obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o pmic-voter.o +obj-$(CONFIG_QPNP_FG_GEN4) += qpnp-fg-gen4.o fg-memif.o fg-util.o fg-alg.o pmic-voter.o obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_SMB138X_CHARGER) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o +obj-$(CONFIG_SMB1390_CHARGE_PUMP) += smb1390-charger.o pmic-voter.o diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 4b88e04533ac5169f6717f5adc3c54c30ed774ae..1433373ff110fa25303941b4da4edc62cb80eb32 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -70,6 +70,7 @@ struct pl_data { int charge_type; int total_settled_ua; int pl_settled_ua; + u32 wa_flags; struct class qcom_batt_class; struct wakeup_source *pl_ws; struct notifier_block nb; @@ -81,6 +82,10 @@ enum print_reason { PR_PARALLEL = BIT(0), }; +enum { + AICL_RERUN_WA_BIT = BIT(0), +}; + static int debug_mask; module_param_named(debug_mask, debug_mask, int, 0600); @@ -539,7 +544,7 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, if (icl_ua > pval.intval) rerun_aicl = true; - if (rerun_aicl) { + if (rerun_aicl && (chip->wa_flags & AICL_RERUN_WA_BIT)) { /* set a lower ICL */ pval.intval = max(pval.intval - ICL_STEP_UA, ICL_STEP_UA); power_supply_set_property(chip->main_psy, @@ -1015,8 +1020,20 @@ static int pl_determine_initial_status(struct pl_data *chip) return 0; } +static void pl_config_init(struct pl_data *chip, int smb_version) +{ + switch (smb_version) { + case PMI8998_SUBTYPE: + case PM660_SUBTYPE: + chip->wa_flags = AICL_RERUN_WA_BIT; + break; + default: + break; + } +} + #define DEFAULT_RESTRICTED_CURRENT_UA 1000000 -int qcom_batt_init(void) +int qcom_batt_init(int smb_version) { struct pl_data *chip; int rc = 0; @@ -1031,6 +1048,7 @@ int qcom_batt_init(void) if (!chip) return -ENOMEM; chip->slave_pct = 50; + pl_config_init(chip, smb_version); chip->restricted_current = DEFAULT_RESTRICTED_CURRENT_UA; chip->pl_ws = wakeup_source_register("qcom-battery"); diff --git a/drivers/power/supply/qcom/battery.h b/drivers/power/supply/qcom/battery.h index 38626e733a09ae0c939da8fc9ce234d516f6bed0..94e8800adb68a1d6e6615152782e73d79f1474fa 100644 --- a/drivers/power/supply/qcom/battery.h +++ b/drivers/power/supply/qcom/battery.h @@ -12,6 +12,6 @@ #ifndef __BATTERY_H #define __BATTERY_H -int qcom_batt_init(void); +int qcom_batt_init(int smb_version); void qcom_batt_deinit(void); #endif /* __BATTERY_H */ diff --git a/drivers/power/supply/qcom/fg-alg.c b/drivers/power/supply/qcom/fg-alg.c new file mode 100644 index 0000000000000000000000000000000000000000..099e39c0eb4368a7e1c34e819cf2d96f09989df3 --- /dev/null +++ b/drivers/power/supply/qcom/fg-alg.c @@ -0,0 +1,670 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "ALG: %s: " fmt, __func__ + +#include +#include +#include +#include +#include "fg-alg.h" + +#define FULL_SOC_RAW 255 +#define CAPACITY_DELTA_DECIPCT 500 + +/* Cycle counter APIs */ + +/** + * restore_cycle_count - + * @counter: Cycle counter object + * + * Restores all the counters back from FG/QG during boot + * + */ +int restore_cycle_count(struct cycle_counter *counter) +{ + int rc = 0; + + if (!counter) + return -ENODEV; + + mutex_lock(&counter->lock); + rc = counter->restore_count(counter->data, counter->count, + BUCKET_COUNT); + if (rc < 0) + pr_err("failed to restore cycle counter rc=%d\n", rc); + mutex_unlock(&counter->lock); + + return rc; +} + +/** + * clear_cycle_count - + * @counter: Cycle counter object + * + * Clears all the counters stored by FG/QG when a battery is inserted + * or the profile is re-loaded. + * + */ +void clear_cycle_count(struct cycle_counter *counter) +{ + int rc = 0, i; + + if (!counter) + return; + + mutex_lock(&counter->lock); + memset(counter->count, 0, sizeof(counter->count)); + for (i = 0; i < BUCKET_COUNT; i++) { + counter->started[i] = false; + counter->last_soc[i] = 0; + } + + rc = counter->store_count(counter->data, counter->count, 0, + BUCKET_COUNT * 2); + if (rc < 0) + pr_err("failed to clear cycle counter rc=%d\n", rc); + + mutex_unlock(&counter->lock); +} + +/** + * store_cycle_count - + * @counter: Cycle counter object + * @id: Cycle counter bucket id + * + * Stores the cycle counter for a bucket in FG/QG. + * + */ +static int store_cycle_count(struct cycle_counter *counter, int id) +{ + int rc = 0; + u16 cyc_count; + + if (!counter) + return -ENODEV; + + if (id < 0 || (id > BUCKET_COUNT - 1)) { + pr_err("Invalid id %d\n", id); + return -EINVAL; + } + + cyc_count = counter->count[id]; + cyc_count++; + + rc = counter->store_count(counter->data, &cyc_count, id, 2); + if (rc < 0) { + pr_err("failed to write cycle_count[%d] rc=%d\n", + id, rc); + return rc; + } + + counter->count[id] = cyc_count; + pr_debug("Stored count %d in id %d\n", cyc_count, id); + + return rc; +} + +/** + * cycle_count_update - + * @counter: Cycle counter object + * @batt_soc: Battery State of Charge (SOC) + * @charge_status: Charging status from power supply + * @charge_done: Indicator for charge termination + * @input_present: Indicator for input presence + * + * Called by FG/QG whenever there is a state change (Charging status, SOC) + * + */ +void cycle_count_update(struct cycle_counter *counter, int batt_soc, + int charge_status, bool charge_done, bool input_present) +{ + int rc = 0, id, i, soc_thresh; + + if (!counter) + return; + + mutex_lock(&counter->lock); + + /* Find out which id the SOC falls in */ + id = batt_soc / BUCKET_SOC_PCT; + + if (charge_status == POWER_SUPPLY_STATUS_CHARGING) { + if (!counter->started[id] && id != counter->last_bucket) { + counter->started[id] = true; + counter->last_soc[id] = batt_soc; + } + } else if (charge_done || !input_present) { + for (i = 0; i < BUCKET_COUNT; i++) { + soc_thresh = counter->last_soc[i] + BUCKET_SOC_PCT / 2; + if (counter->started[i] && batt_soc > soc_thresh) { + rc = store_cycle_count(counter, i); + if (rc < 0) + pr_err("Error in storing cycle_ctr rc: %d\n", + rc); + counter->last_soc[i] = 0; + counter->started[i] = false; + counter->last_bucket = i; + } + } + } + + pr_debug("batt_soc: %d id: %d chg_status: %d\n", batt_soc, id, + charge_status); + mutex_unlock(&counter->lock); +} + +/** + * get_bucket_cycle_count - + * @counter: Cycle counter object + * + * Returns the cycle counter for a SOC bucket. + * + */ +static int get_bucket_cycle_count(struct cycle_counter *counter) +{ + int count; + + if (!counter) + return 0; + + if ((counter->id <= 0) || (counter->id > BUCKET_COUNT)) + return -EINVAL; + + mutex_lock(&counter->lock); + count = counter->count[counter->id - 1]; + mutex_unlock(&counter->lock); + return count; +} + +/** + * get_cycle_count - + * @counter: Cycle counter object + * @count: Average cycle count returned to the caller + * + * Get average cycle count for all buckets + * + */ +int get_cycle_count(struct cycle_counter *counter, int *count) +{ + int i, rc, temp = 0; + + for (i = 1; i <= BUCKET_COUNT; i++) { + counter->id = i; + rc = get_bucket_cycle_count(counter); + if (rc < 0) { + pr_err("Couldn't get cycle count rc=%d\n", rc); + return rc; + } + temp += rc; + } + + /* + * Normalize the counter across each bucket so that we can get + * the overall charge cycle count. + */ + + *count = temp / BUCKET_COUNT; + return 0; +} + +/** + * get_cycle_counts - + * @counter: Cycle counter object + * @buf: Bucket cycle counts formatted in a string returned to the caller + * + * Get cycle count for all buckets in a string format + * + */ +int get_cycle_counts(struct cycle_counter *counter, const char **buf) +{ + int i, rc, len = 0; + + for (i = 1; i <= BUCKET_COUNT; i++) { + counter->id = i; + rc = get_bucket_cycle_count(counter); + if (rc < 0) { + pr_err("Couldn't get cycle count rc=%d\n", rc); + return rc; + } + + if (sizeof(counter->str_buf) - len < 8) { + pr_err("Invalid length %d\n", len); + return -EINVAL; + } + + len += snprintf(counter->str_buf + len, 8, "%d ", rc); + } + + counter->str_buf[len] = '\0'; + *buf = counter->str_buf; + return 0; +} + +/** + * cycle_count_init - + * @counter: Cycle counter object + * + * FG/QG have to call this during driver probe to validate the required + * parameters after allocating cycle_counter object. + * + */ +int cycle_count_init(struct cycle_counter *counter) +{ + if (!counter) + return -ENODEV; + + if (!counter->data || !counter->restore_count || + !counter->store_count) { + pr_err("Invalid parameters for using cycle counter\n"); + return -EINVAL; + } + + mutex_init(&counter->lock); + counter->last_bucket = -1; + return 0; +} + +/* Capacity learning algorithm APIs */ + +/** + * cap_learning_post_process - + * @cl: Capacity learning object + * + * Does post processing on the learnt capacity based on the user specified + * or default parameters for the capacity learning algorithm. + * + */ +static void cap_learning_post_process(struct cap_learning *cl) +{ + int64_t max_inc_val, min_dec_val, old_cap; + int rc; + + if (cl->dt.skew_decipct) { + pr_debug("applying skew %d on current learnt capacity %lld\n", + cl->dt.skew_decipct, cl->final_cap_uah); + cl->final_cap_uah = cl->final_cap_uah * + (1000 + cl->dt.skew_decipct); + cl->final_cap_uah = div64_u64(cl->final_cap_uah, 1000); + } + + max_inc_val = cl->learned_cap_uah * (1000 + cl->dt.max_cap_inc); + max_inc_val = div64_u64(max_inc_val, 1000); + + min_dec_val = cl->learned_cap_uah * (1000 - cl->dt.max_cap_dec); + min_dec_val = div64_u64(min_dec_val, 1000); + + old_cap = cl->learned_cap_uah; + if (cl->final_cap_uah > max_inc_val) + cl->learned_cap_uah = max_inc_val; + else if (cl->final_cap_uah < min_dec_val) + cl->learned_cap_uah = min_dec_val; + else + cl->learned_cap_uah = cl->final_cap_uah; + + if (cl->dt.max_cap_limit) { + max_inc_val = (int64_t)cl->nom_cap_uah * (1000 + + cl->dt.max_cap_limit); + max_inc_val = div64_u64(max_inc_val, 1000); + if (cl->final_cap_uah > max_inc_val) { + pr_debug("learning capacity %lld goes above max limit %lld\n", + cl->final_cap_uah, max_inc_val); + cl->learned_cap_uah = max_inc_val; + } + } + + if (cl->dt.min_cap_limit) { + min_dec_val = (int64_t)cl->nom_cap_uah * (1000 - + cl->dt.min_cap_limit); + min_dec_val = div64_u64(min_dec_val, 1000); + if (cl->final_cap_uah < min_dec_val) { + pr_debug("learning capacity %lld goes below min limit %lld\n", + cl->final_cap_uah, min_dec_val); + cl->learned_cap_uah = min_dec_val; + } + } + + if (cl->store_learned_capacity) { + rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah); + if (rc < 0) + pr_err("Error in storing learned_cap_uah, rc=%d\n", rc); + } + + pr_debug("final cap_uah = %lld, learned capacity %lld -> %lld uah\n", + cl->final_cap_uah, old_cap, cl->learned_cap_uah); +} + +/** + * cap_learning_process_full_data - + * @cl: Capacity learning object + * + * Processes the coulomb counter during charge termination and calculates the + * delta w.r.to the coulomb counter obtained earlier when the learning begun. + * + */ +static int cap_learning_process_full_data(struct cap_learning *cl) +{ + int rc, cc_soc_sw, cc_soc_delta_pct; + int64_t delta_cap_uah; + + rc = cl->get_cc_soc(cl->data, &cc_soc_sw); + if (rc < 0) { + pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); + return rc; + } + + cc_soc_delta_pct = + div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 100, + cl->cc_soc_max); + + /* If the delta is < 50%, then skip processing full data */ + if (cc_soc_delta_pct < 50) { + pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct); + return -ERANGE; + } + + delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_pct, 100); + cl->final_cap_uah = cl->init_cap_uah + delta_cap_uah; + pr_debug("Current cc_soc=%d cc_soc_delta_pct=%d total_cap_uah=%lld\n", + cc_soc_sw, cc_soc_delta_pct, cl->final_cap_uah); + return 0; +} + +/** + * cap_learning_begin - + * @cl: Capacity learning object + * @batt_soc: Battery State of Charge (SOC) + * + * Gets the coulomb counter from FG/QG when the conditions are suitable for + * beginning capacity learning. Also, primes the coulomb counter based on + * battery SOC if required. + * + */ +static int cap_learning_begin(struct cap_learning *cl, u32 batt_soc) +{ + int rc, cc_soc_sw, batt_soc_msb; + + batt_soc_msb = batt_soc >> 24; + if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) > + cl->dt.start_soc) { + pr_debug("Battery SOC %d is high!, not starting\n", + batt_soc_msb); + return -EINVAL; + } + + cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc_msb, + FULL_SOC_RAW); + + if (cl->prime_cc_soc) { + /* + * Prime cc_soc_sw with battery SOC when capacity learning + * begins. + */ + rc = cl->prime_cc_soc(cl->data, batt_soc); + if (rc < 0) { + pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); + goto out; + } + } + + rc = cl->get_cc_soc(cl->data, &cc_soc_sw); + if (rc < 0) { + pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); + goto out; + } + + cl->init_cc_soc_sw = cc_soc_sw; + pr_debug("Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n", + batt_soc_msb, cl->init_cc_soc_sw); +out: + return rc; +} + +/** + * cap_learning_done - + * @cl: Capacity learning object + * + * Top level function for getting coulomb counter and post processing the + * data once the capacity learning is complete after charge termination. + * + */ +static int cap_learning_done(struct cap_learning *cl) +{ + int rc; + + rc = cap_learning_process_full_data(cl); + if (rc < 0) { + pr_err("Error in processing cap learning full data, rc=%d\n", + rc); + goto out; + } + + if (cl->prime_cc_soc) { + /* Write a FULL value to cc_soc_sw */ + rc = cl->prime_cc_soc(cl->data, cl->cc_soc_max); + if (rc < 0) { + pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); + goto out; + } + } + + cap_learning_post_process(cl); +out: + return rc; +} + +/** + * cap_learning_update - + * @cl: Capacity learning object + * @batt_temp - Battery temperature + * @batt_soc: Battery State of Charge (SOC) + * @charge_status: Charging status from power supply + * @charge_done: Indicator for charge termination + * @input_present: Indicator for input presence + * @qnovo_en: Indicator for Qnovo enable status + * + * Called by FG/QG driver when there is a state change (Charging status, SOC) + * + */ +void cap_learning_update(struct cap_learning *cl, int batt_temp, + int batt_soc, int charge_status, bool charge_done, + bool input_present, bool qnovo_en) +{ + int rc, batt_soc_msb, batt_soc_prime; + bool prime_cc = false; + + if (!cl) + return; + + mutex_lock(&cl->lock); + + if (batt_temp > cl->dt.max_temp || batt_temp < cl->dt.min_temp || + !cl->learned_cap_uah) { + cl->active = false; + cl->init_cap_uah = 0; + goto out; + } + + batt_soc_msb = (u32)batt_soc >> 24; + pr_debug("Charge_status: %d active: %d batt_soc: %d\n", + charge_status, cl->active, batt_soc_msb); + + /* Initialize the starting point of learning capacity */ + if (!cl->active) { + if (charge_status == POWER_SUPPLY_STATUS_CHARGING) { + rc = cap_learning_begin(cl, batt_soc); + cl->active = (rc == 0); + } else { + if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING || + charge_done) + prime_cc = true; + } + } else { + if (charge_done) { + rc = cap_learning_done(cl); + if (rc < 0) + pr_err("Error in completing capacity learning, rc=%d\n", + rc); + + cl->active = false; + cl->init_cap_uah = 0; + } + + if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING) { + if (!input_present) { + pr_debug("Capacity learning aborted @ battery SOC %d\n", + batt_soc_msb); + cl->active = false; + cl->init_cap_uah = 0; + prime_cc = true; + } + } + + if (charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { + if (qnovo_en && input_present) { + /* + * Don't abort the capacity learning when qnovo + * is enabled and input is present where the + * charging status can go to "not charging" + * intermittently. + */ + } else { + pr_debug("Capacity learning aborted @ battery SOC %d\n", + batt_soc_msb); + cl->active = false; + cl->init_cap_uah = 0; + prime_cc = true; + } + } + } + + /* + * Prime CC_SOC_SW when the device is not charging or during charge + * termination when the capacity learning is not active. + */ + + if (prime_cc && cl->prime_cc_soc) { + if (charge_done) + batt_soc_prime = cl->cc_soc_max; + else + batt_soc_prime = batt_soc; + + rc = cl->prime_cc_soc(cl->data, batt_soc_prime); + if (rc < 0) + pr_err("Error in writing cc_soc_sw, rc=%d\n", + rc); + } + +out: + mutex_unlock(&cl->lock); +} + +/** + * cap_learning_abort - + * @cl: Capacity learning object + * + * Aborts the capacity learning and initializes variables + * + */ +void cap_learning_abort(struct cap_learning *cl) +{ + if (!cl) + return; + + mutex_lock(&cl->lock); + pr_debug("Aborting cap_learning\n"); + cl->active = false; + cl->init_cap_uah = 0; + mutex_lock(&cl->lock); +} + +/** + * cap_learning_post_profile_init - + * @cl: Capacity learning object + * @nom_cap_uah: Nominal capacity of battery in uAh + * + * Called by FG/QG once the profile load is complete and nominal capacity + * of battery is known. This also gets the last learned capacity back from + * FG/QG to feed back to the algorithm. + * + */ +int cap_learning_post_profile_init(struct cap_learning *cl, int64_t nom_cap_uah) +{ + int64_t delta_cap_uah, pct_nom_cap_uah; + int rc; + + if (!cl || !cl->data) + return -EINVAL; + + mutex_lock(&cl->lock); + cl->nom_cap_uah = nom_cap_uah; + rc = cl->get_learned_capacity(cl->data, &cl->learned_cap_uah); + if (rc < 0) { + pr_err("Couldn't get learned capacity, rc=%d\n", rc); + goto out; + } + + if (cl->learned_cap_uah != cl->nom_cap_uah) { + if (cl->learned_cap_uah == 0) + cl->learned_cap_uah = cl->nom_cap_uah; + + delta_cap_uah = abs(cl->learned_cap_uah - cl->nom_cap_uah); + pct_nom_cap_uah = div64_s64((int64_t)cl->nom_cap_uah * + CAPACITY_DELTA_DECIPCT, 1000); + /* + * If the learned capacity is out of range by 50% from the + * nominal capacity, then overwrite the learned capacity with + * the nominal capacity. + */ + if (cl->nom_cap_uah && delta_cap_uah > pct_nom_cap_uah) { + pr_debug("learned_cap_uah: %lld is higher than expected, capping it to nominal: %lld\n", + cl->learned_cap_uah, cl->nom_cap_uah); + cl->learned_cap_uah = cl->nom_cap_uah; + } + + rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah); + if (rc < 0) + pr_err("Error in storing learned_cap_uah, rc=%d\n", rc); + } + +out: + mutex_unlock(&cl->lock); + return rc; +} + +/** + * cap_learning_init - + * @cl: Capacity learning object + * + * FG/QG have to call this during driver probe to validate the required + * parameters after allocating cap_learning object. + * + */ +int cap_learning_init(struct cap_learning *cl) +{ + if (!cl) + return -ENODEV; + + if (!cl->get_learned_capacity || !cl->store_learned_capacity || + !cl->get_cc_soc) { + pr_err("Insufficient functions for supporting capacity learning\n"); + return -EINVAL; + } + + if (!cl->cc_soc_max) { + pr_err("Insufficient parameters for supporting capacity learning\n"); + return -EINVAL; + } + + mutex_init(&cl->lock); + return 0; +} diff --git a/drivers/power/supply/qcom/fg-alg.h b/drivers/power/supply/qcom/fg-alg.h new file mode 100644 index 0000000000000000000000000000000000000000..41d278a386ac3dd5e17d4a8dcd0035d14c04d783 --- /dev/null +++ b/drivers/power/supply/qcom/fg-alg.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2018, 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 __FG_ALG_H__ +#define __FG_ALG_H__ + +#define BUCKET_COUNT 8 +#define BUCKET_SOC_PCT (256 / BUCKET_COUNT) + +struct cycle_counter { + void *data; + char str_buf[BUCKET_COUNT * 8]; + bool started[BUCKET_COUNT]; + u16 count[BUCKET_COUNT]; + u8 last_soc[BUCKET_COUNT]; + int id; + int last_bucket; + struct mutex lock; + int (*restore_count)(void *data, u16 *buf, int num_bytes); + int (*store_count)(void *data, u16 *buf, int id, int num_bytes); +}; + +struct cl_params { + int start_soc; + int max_temp; + int min_temp; + int max_cap_inc; + int max_cap_dec; + int max_cap_limit; + int min_cap_limit; + int skew_decipct; +}; + +struct cap_learning { + void *data; + int init_cc_soc_sw; + int cc_soc_max; + int64_t nom_cap_uah; + int64_t init_cap_uah; + int64_t final_cap_uah; + int64_t learned_cap_uah; + bool active; + struct mutex lock; + struct cl_params dt; + int (*get_learned_capacity)(void *data, int64_t *learned_cap_uah); + int (*store_learned_capacity)(void *data, int64_t learned_cap_uah); + int (*get_cc_soc)(void *data, int *cc_soc_sw); + int (*prime_cc_soc)(void *data, u32 cc_soc_sw); +}; + +int restore_cycle_count(struct cycle_counter *counter); +void clear_cycle_count(struct cycle_counter *counter); +void cycle_count_update(struct cycle_counter *counter, int batt_soc, + int charge_status, bool charge_done, bool input_present); +int get_cycle_count(struct cycle_counter *counter, int *count); +int get_cycle_counts(struct cycle_counter *counter, const char **buf); +int cycle_count_init(struct cycle_counter *counter); +void cap_learning_abort(struct cap_learning *cl); +void cap_learning_update(struct cap_learning *cl, int batt_temp, + int batt_soc, int charge_status, bool charge_done, + bool input_present, bool qnovo_en); +int cap_learning_init(struct cap_learning *cl); +int cap_learning_post_profile_init(struct cap_learning *cl, + int64_t nom_cap_uah); + +#endif diff --git a/drivers/power/supply/qcom/qpnp-fg-gen4.c b/drivers/power/supply/qcom/qpnp-fg-gen4.c index d8bc88bcbf898965123bb6f360b43406da66364a..4768a6704fd988650a090142da26ad8a8b943b18 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen4.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen4.c @@ -20,6 +20,7 @@ #include #include "fg-core.h" #include "fg-reg.h" +#include "fg-alg.h" #define FG_GEN4_DEV_NAME "qcom,fg-gen4" @@ -142,6 +143,8 @@ struct fg_dt_props { struct fg_gen4_chip { struct fg_dev fg; struct fg_dt_props dt; + struct cycle_counter *counter; + struct cap_learning *cl; struct ttf ttf; struct delayed_work ttf_work; char batt_profile[PROFILE_LEN]; @@ -321,7 +324,7 @@ static int fg_gen4_get_learned_capacity(void *data, int64_t *learned_cap_uah) int rc, act_cap_mah; if (!chip) - return -ENODATA; + return -ENODEV; fg = &chip->fg; rc = fg_get_sram_prop(fg, FG_SRAM_ACT_BATT_CAP, &act_cap_mah); @@ -442,7 +445,7 @@ static int fg_gen4_get_debug_batt_id(struct fg_dev *fg, int *batt_id) batt_id[0] = (30000 * tmp) / (MAX_BIAS_CODE - tmp); - rc = fg_read(fg, ADC_RR_FAKE_BATT_LOW_LSB(fg), (u8 *)&tmp, 2); + rc = fg_read(fg, ADC_RR_FAKE_BATT_HIGH_LSB(fg), (u8 *)&tmp, 2); if (rc < 0) { pr_err("failed to read addr=0x%04x, rc=%d\n", ADC_RR_FAKE_BATT_HIGH_LSB(fg), rc); @@ -542,6 +545,128 @@ static inline void get_esr_meas_current(int curr_ma, u8 *val) *val <<= ESR_PULL_DOWN_IVAL_SHIFT; } +/* ALG callback functions below */ + +static int fg_gen4_store_learned_capacity(void *data, int64_t learned_cap_uah) +{ + struct fg_gen4_chip *chip = data; + struct fg_dev *fg; + int16_t cc_mah; + int rc; + + if (!chip) + return -ENODEV; + + fg = &chip->fg; + if (fg->battery_missing || !learned_cap_uah) + return -EPERM; + + cc_mah = div64_s64(learned_cap_uah, 1000); + rc = fg_sram_write(fg, fg->sp[FG_SRAM_ACT_BATT_CAP].addr_word, + fg->sp[FG_SRAM_ACT_BATT_CAP].addr_byte, (u8 *)&cc_mah, + fg->sp[FG_SRAM_ACT_BATT_CAP].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing act_batt_cap_bkup, rc=%d\n", rc); + return rc; + } + + fg_dbg(fg, FG_CAP_LEARN, "learned capacity %llduah/%dmah stored\n", + chip->cl->learned_cap_uah, cc_mah); + return 0; +} + +static int fg_gen4_prime_cc_soc_sw(void *data, u32 batt_soc) +{ + struct fg_gen4_chip *chip = data; + struct fg_dev *fg; + int rc, cc_soc_sw; + + if (!chip) + return -ENODEV; + + fg = &chip->fg; + if (batt_soc == CC_SOC_30BIT) + cc_soc_sw = batt_soc; + else + cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT, + BATT_SOC_32BIT); + + rc = fg_sram_write(fg, fg->sp[FG_SRAM_CC_SOC_SW].addr_word, + fg->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, + fg->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); + if (rc < 0) + pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); + else + fg_dbg(fg, FG_STATUS, "cc_soc_sw: %x\n", cc_soc_sw); + + return rc; +} + +static int fg_gen4_get_cc_soc_sw(void *data, int *cc_soc_sw) +{ + struct fg_gen4_chip *chip = data; + struct fg_dev *fg; + int rc, temp; + + if (!chip) + return -ENODEV; + + fg = &chip->fg; + rc = fg_get_sram_prop(fg, FG_SRAM_CC_SOC_SW, &temp); + if (rc < 0) { + pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); + return rc; + } + + *cc_soc_sw = temp; + return rc; +} + +static int fg_gen4_restore_count(void *data, u16 *buf, int length) +{ + struct fg_gen4_chip *chip = data; + int id, rc = 0; + u8 tmp[2]; + + if (!chip) + return -ENODEV; + + if (!buf || length > BUCKET_COUNT) + return -EINVAL; + + for (id = 0; id < length; id++) { + rc = fg_sram_read(&chip->fg, CYCLE_COUNT_WORD + id, + CYCLE_COUNT_OFFSET, (u8 *)tmp, 2, + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to read bucket %d rc=%d\n", id, rc); + else + *buf++ = tmp[0] | tmp[1] << 8; + } + + return rc; +} + +static int fg_gen4_store_count(void *data, u16 *buf, int id, int length) +{ + struct fg_gen4_chip *chip = data; + int rc; + + if (!chip) + return -ENODEV; + + if (!buf || length > BUCKET_COUNT * 2 || id < 0 || + id > BUCKET_COUNT - 1 || ((id * 2) + length) > BUCKET_COUNT * 2) + return -EINVAL; + + rc = fg_sram_write(&chip->fg, CYCLE_COUNT_WORD + id, CYCLE_COUNT_OFFSET, + (u8 *)buf, length, FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to write bucket %d rc=%d\n", rc); + + return rc; +} + /* All worker and helper functions below */ #define KI_COEFF_MED_DISCHG_DEFAULT 245 @@ -828,6 +953,7 @@ static void profile_load_work(struct work_struct *work) profile_load_work.work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); + int64_t nom_cap_uah; u8 val; int rc; @@ -853,6 +979,8 @@ static void profile_load_work(struct work_struct *work) if (!is_profile_load_required(chip)) goto done; + clear_cycle_count(chip->counter); + fg_dbg(fg, FG_STATUS, "profile loading started\n"); rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT, 0); if (rc < 0) { @@ -892,6 +1020,14 @@ static void profile_load_work(struct work_struct *work) pr_err("Error in configuring battery profile params, rc:%d\n", rc); + rc = fg_gen4_get_nominal_capacity(chip, &nom_cap_uah); + if (!rc) { + rc = cap_learning_post_profile_init(chip->cl, nom_cap_uah); + if (rc < 0) + pr_err("Error in cap_learning_post_profile_init rc=%d\n", + rc); + } + batt_psy_initialized(fg); fg_notify_charger(fg); @@ -913,6 +1049,9 @@ static void get_batt_psy_props(struct fg_dev *fg) union power_supply_propval prop = {0, }; int rc; + if (!batt_psy_initialized(fg)) + return; + rc = power_supply_get_property(fg->batt_psy, POWER_SUPPLY_PROP_STATUS, &prop); if (rc < 0) { @@ -1230,7 +1369,9 @@ static irqreturn_t fg_delta_bsoc_irq_handler(int irq, void *data) static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) { struct fg_dev *fg = data; + struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int rc, batt_soc, batt_temp; + bool input_present = is_input_present(fg); fg_dbg(fg, FG_IRQ, "irq %d triggered\n", irq); @@ -1239,10 +1380,17 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &batt_soc); if (rc < 0) pr_err("Failed to read battery soc rc: %d\n", rc); + else + cycle_count_update(chip->counter, (u32)batt_soc >> 24, + fg->charge_status, fg->charge_done, input_present); rc = fg_gen4_get_battery_temp(fg, &batt_temp); if (rc < 0) pr_err("Failed to read battery temp rc: %d\n", rc); + else if (chip->cl->active) + cap_learning_update(chip->cl, batt_temp, batt_soc, + fg->charge_status, fg->charge_done, input_present, + is_qnovo_en(fg)); rc = fg_gen4_charge_full_update(fg); if (rc < 0) @@ -1482,7 +1630,9 @@ static void status_change_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, status_change_work); + struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int rc, batt_soc, batt_temp; + bool input_present, qnovo_en; if (!batt_psy_initialized(fg)) { fg_dbg(fg, FG_STATUS, "Charger not available?!\n"); @@ -1508,6 +1658,16 @@ static void status_change_work(struct work_struct *work) goto out; } + input_present = is_input_present(fg); + qnovo_en = is_qnovo_en(fg); + cycle_count_update(chip->counter, (u32)batt_soc >> 24, + fg->charge_status, fg->charge_done, input_present); + + if (fg->charge_status != fg->prev_charge_status) + cap_learning_update(chip->cl, batt_temp, batt_soc, + fg->charge_status, fg->charge_done, input_present, + qnovo_en); + rc = fg_gen4_charge_full_update(fg); if (rc < 0) pr_err("Error in charge_full_update, rc=%d\n", rc); @@ -1876,6 +2036,7 @@ static int fg_get_time_to_empty(struct fg_dev *fg, int *val) return 0; } + static void sram_dump_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, @@ -2053,6 +2214,14 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: rc = fg_gen4_get_charge_counter_shadow(chip, &pval->intval); break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + rc = get_cycle_count(chip->counter, &pval->intval); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNTS: + rc = get_cycle_counts(chip->counter, &pval->strval); + if (rc < 0) + pval->strval = NULL; + break; case POWER_SUPPLY_PROP_SOC_REPORTING_READY: pval->intval = fg->soc_reporting_ready; break; @@ -2084,9 +2253,25 @@ static int fg_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *pval) { + struct fg_gen4_chip *chip = power_supply_get_drvdata(psy); int rc = 0; switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + if (chip->cl->active) { + pr_warn("Capacity learning active!\n"); + return 0; + } + if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) { + pr_err("charge_full is out of bounds\n"); + return -EINVAL; + } + mutex_lock(&chip->cl->lock); + rc = fg_gen4_store_learned_capacity(chip, pval->intval); + if (!rc) + chip->cl->learned_cap_uah = pval->intval; + mutex_unlock(&chip->cl->lock); + break; default: break; } @@ -2098,6 +2283,8 @@ static int fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + return 1; default: break; } @@ -2121,6 +2308,7 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, + POWER_SUPPLY_PROP_CYCLE_COUNTS, POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_DEBUG_BATTERY, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, @@ -2201,6 +2389,59 @@ static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data, /* All init functions below this */ +static int fg_alg_init(struct fg_gen4_chip *chip) +{ + struct fg_dev *fg = &chip->fg; + struct cycle_counter *counter; + struct cap_learning *cl; + int rc; + + counter = devm_kzalloc(fg->dev, sizeof(*counter), GFP_KERNEL); + if (!counter) + return -ENOMEM; + + counter->restore_count = fg_gen4_restore_count; + counter->store_count = fg_gen4_store_count; + counter->data = chip; + + rc = cycle_count_init(counter); + if (rc < 0) { + dev_err(fg->dev, "Error in initializing cycle counter, rc:%d\n", + rc); + counter->data = NULL; + devm_kfree(fg->dev, counter); + return rc; + } + + chip->counter = counter; + + cl = devm_kzalloc(fg->dev, sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + cl->cc_soc_max = CC_SOC_30BIT; + cl->get_cc_soc = fg_gen4_get_cc_soc_sw; + cl->prime_cc_soc = fg_gen4_prime_cc_soc_sw; + cl->get_learned_capacity = fg_gen4_get_learned_capacity; + cl->store_learned_capacity = fg_gen4_store_learned_capacity; + cl->data = chip; + + rc = cap_learning_init(cl); + if (rc < 0) { + dev_err(fg->dev, "Error in initializing capacity learning, rc:%d\n", + rc); + counter->data = NULL; + cl->data = NULL; + devm_kfree(fg->dev, counter); + devm_kfree(fg->dev, cl); + return rc; + } + + chip->cl = cl; + + return 0; +} + #define BATT_TEMP_HYST_MASK GENMASK(3, 0) #define BATT_TEMP_DELTA_MASK GENMASK(7, 4) #define BATT_TEMP_DELTA_SHIFT 4 @@ -2416,6 +2657,12 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip) } } + rc = restore_cycle_count(chip->counter); + if (rc < 0) { + pr_err("Error in restoring cycle_count, rc=%d\n", rc); + return rc; + } + return 0; } @@ -2692,6 +2939,48 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.force_load_profile = of_property_read_bool(node, "qcom,fg-force-load-profile"); + rc = of_property_read_u32(node, "qcom,cl-start-capacity", &temp); + if (rc < 0) + chip->cl->dt.start_soc = DEFAULT_CL_START_SOC; + else + chip->cl->dt.start_soc = temp; + + rc = of_property_read_u32(node, "qcom,cl-min-temp", &temp); + if (rc < 0) + chip->cl->dt.min_temp = DEFAULT_CL_MIN_TEMP_DECIDEGC; + else + chip->cl->dt.min_temp = temp; + + rc = of_property_read_u32(node, "qcom,cl-max-temp", &temp); + if (rc < 0) + chip->cl->dt.max_temp = DEFAULT_CL_MAX_TEMP_DECIDEGC; + else + chip->cl->dt.max_temp = temp; + + rc = of_property_read_u32(node, "qcom,cl-max-increment", &temp); + if (rc < 0) + chip->cl->dt.max_cap_inc = DEFAULT_CL_MAX_INC_DECIPERC; + else + chip->cl->dt.max_cap_inc = temp; + + rc = of_property_read_u32(node, "qcom,cl-max-decrement", &temp); + if (rc < 0) + chip->cl->dt.max_cap_dec = DEFAULT_CL_MAX_DEC_DECIPERC; + else + chip->cl->dt.max_cap_dec = temp; + + rc = of_property_read_u32(node, "qcom,cl-min-limit", &temp); + if (rc < 0) + chip->cl->dt.min_cap_limit = DEFAULT_CL_MIN_LIM_DECIPERC; + else + chip->cl->dt.min_cap_limit = temp; + + rc = of_property_read_u32(node, "qcom,cl-max-limit", &temp); + if (rc < 0) + chip->cl->dt.max_cap_limit = DEFAULT_CL_MAX_LIM_DECIPERC; + else + chip->cl->dt.max_cap_limit = temp; + rc = of_property_read_u32(node, "qcom,fg-batt-temp-hot", &temp); if (rc < 0) chip->dt.batt_temp_hot_thresh = -EINVAL; @@ -2816,6 +3105,13 @@ static int fg_gen4_probe(struct platform_device *pdev) goto exit; } + rc = fg_alg_init(chip); + if (rc < 0) { + dev_err(fg->dev, "Error in alg_init, rc:%d\n", + rc); + goto exit; + } + rc = fg_gen4_parse_dt(chip); if (rc < 0) { dev_err(fg->dev, "Error in reading DT parameters, rc:%d\n", @@ -2826,6 +3122,7 @@ static int fg_gen4_probe(struct platform_device *pdev) mutex_init(&fg->bus_lock); mutex_init(&fg->sram_rw_lock); mutex_init(&fg->charge_full_lock); + mutex_init(&chip->ttf.lock); init_completion(&fg->soc_update); init_completion(&fg->soc_ready); INIT_WORK(&fg->status_change_work, status_change_work); @@ -2927,13 +3224,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev) struct fg_dev *fg = &chip->fg; int rc, bsoc; - if (fg->charge_full) { - rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &bsoc); - if (rc < 0) { - pr_err("Error in getting BATT_SOC, rc=%d\n", rc); - return; - } + rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &bsoc); + if (rc < 0) { + pr_err("Error in getting BATT_SOC, rc=%d\n", rc); + return; + } + if (fg->charge_full) { /* We need 2 most significant bytes here */ bsoc = (u32)bsoc >> 16; @@ -2943,6 +3240,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev) return; } } + + /* + * Charging status doesn't matter when the device shuts down and we + * have to treat this as charge done. Hence pass charge_done as true. + */ + cycle_count_update(chip->counter, (u32)bsoc >> 24, + POWER_SUPPLY_STATUS_NOT_CHARGING, true, is_input_present(fg)); } static int fg_gen4_suspend(struct device *dev) diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index 1b0e1cbb4b6e773cc933149e55248a27f325a21b..92360f96f20ccb64e67e65e019614e6b796916f6 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "smb5-reg.h" #include "smb5-lib.h" @@ -51,6 +52,13 @@ static struct smb_params smb5_pmi632_params = { .max_u = 3000000, .step_u = 50000, }, + .icl_max_stat = { + .name = "dcdc icl max status", + .reg = ICL_MAX_STATUS_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, .icl_stat = { .name = "input current limit status", .reg = AICL_ICL_STATUS_REG, @@ -89,13 +97,13 @@ static struct smb_params smb5_pmi632_params = { }, }; -static struct smb_params smb5_pmi855_params = { +static struct smb_params smb5_pm855b_params = { .fcc = { .name = "fast charge current", .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, .min_u = 0, .max_u = 8000000, - .step_u = 25000, + .step_u = 50000, }, .fv = { .name = "float voltage", @@ -111,8 +119,15 @@ static struct smb_params smb5_pmi855_params = { .max_u = 5000000, .step_u = 50000, }, + .icl_max_stat = { + .name = "dcdc icl max status", + .reg = ICL_MAX_STATUS_REG, + .min_u = 0, + .max_u = 5000000, + .step_u = 50000, + }, .icl_stat = { - .name = "input current limit status", + .name = "aicl icl status", .reg = AICL_ICL_STATUS_REG, .min_u = 0, .max_u = 5000000, @@ -215,7 +230,7 @@ static int smb5_chg_config_init(struct smb5 *chip) switch (pmic_rev_id->pmic_subtype) { case PM855B_SUBTYPE: chip->chg.smb_version = PM855B_SUBTYPE; - chg->param = smb5_pmi855_params; + chg->param = smb5_pm855b_params; chg->name = "pm855b_charger"; break; case PMI632_SUBTYPE: @@ -363,6 +378,50 @@ static int smb5_parse_dt(struct smb5 *chip) if (rc < 0) chg->otg_delay_ms = OTG_DEFAULT_DEGLITCH_TIME_MS; + rc = of_property_match_string(node, "io-channel-names", + "usb_in_voltage"); + if (rc >= 0) { + chg->iio.usbin_v_chan = iio_channel_get(chg->dev, + "usb_in_voltage"); + if (IS_ERR(chg->iio.usbin_v_chan)) { + rc = PTR_ERR(chg->iio.usbin_v_chan); + if (rc != -EPROBE_DEFER) + dev_err(chg->dev, "USBIN_V channel unavailable, %ld\n", + rc); + chg->iio.usbin_v_chan = NULL; + return rc; + } + } + + rc = of_property_match_string(node, "io-channel-names", + "chg_temp"); + if (rc >= 0) { + chg->iio.temp_chan = iio_channel_get(chg->dev, "chg_temp"); + if (IS_ERR(chg->iio.temp_chan)) { + rc = PTR_ERR(chg->iio.temp_chan); + if (rc != -EPROBE_DEFER) + dev_err(chg->dev, "CHG_TEMP channel unavailable, %ld\n", + rc); + chg->iio.temp_chan = NULL; + return rc; + } + } + + rc = of_property_match_string(node, "io-channel-names", + "usb_in_current"); + if (rc >= 0) { + chg->iio.usbin_i_chan = iio_channel_get(chg->dev, + "usb_in_current"); + if (IS_ERR(chg->iio.usbin_i_chan)) { + rc = PTR_ERR(chg->iio.usbin_i_chan); + if (rc != -EPROBE_DEFER) + dev_err(chg->dev, "USBIN_I channel unavailable, %ld\n", + rc); + chg->iio.usbin_i_chan = NULL; + return rc; + } + } + return 0; } @@ -372,6 +431,7 @@ static int smb5_parse_dt(struct smb5 *chip) static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_PD_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_TYPE, @@ -426,6 +486,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_prop_usb_voltage_max(chg, val); break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = smblib_get_prop_usb_voltage_now(chg, val); + break; case POWER_SUPPLY_PROP_PD_CURRENT_MAX: val->intval = get_client_vote(chg->usb_icl_votable, PD_VOTER); break; @@ -462,6 +525,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: rc = smblib_get_prop_input_current_settled(chg, val); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW: + rc = smblib_get_prop_usb_current_now(chg, val); + break; case POWER_SUPPLY_PROP_BOOST_CURRENT: val->intval = chg->boost_current_ua; break; @@ -947,6 +1013,7 @@ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_MAX, @@ -965,6 +1032,7 @@ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_RECHARGE_SOC, }; @@ -973,6 +1041,8 @@ static int smb5_batt_get_prop(struct power_supply *psy, union power_supply_propval *val) { struct smb_charger *chg = power_supply_get_drvdata(psy); + union power_supply_propval pval = {0, }; + int rc = 0; switch (psp) { @@ -1000,6 +1070,15 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: rc = smblib_get_prop_system_temp_level_max(chg, val); break; + case POWER_SUPPLY_PROP_CHARGER_TEMP: + rc = smblib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get usb present rc=%d\n", rc); + break; + } + if (pval.intval) + rc = smblib_get_prop_charger_temp(chg, val); + break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: rc = smblib_get_prop_input_current_limited(chg, val); break; @@ -1055,6 +1134,9 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_COUNTER: rc = smblib_get_prop_batt_charge_counter(chg, val); break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + rc = smblib_get_prop_batt_cycle_count(chg, val); + break; case POWER_SUPPLY_PROP_RECHARGE_SOC: val->intval = chg->auto_recharge_soc; break; @@ -1716,30 +1798,25 @@ static struct smb_irq_info smb5_irqs[] = { [CHG_STATE_CHANGE_IRQ] = { .name = "chg-state-change", .handler = chg_state_change_irq_handler, + .wake = true, }, [STEP_CHG_STATE_CHANGE_IRQ] = { .name = "step-chg-state-change", - .handler = default_irq_handler, }, [STEP_CHG_SOC_UPDATE_FAIL_IRQ] = { .name = "step-chg-soc-update-fail", - .handler = default_irq_handler, }, [STEP_CHG_SOC_UPDATE_REQ_IRQ] = { .name = "step-chg-soc-update-req", - .handler = default_irq_handler, }, [FG_FVCAL_QUALIFIED_IRQ] = { .name = "fg-fvcal-qualified", - .handler = default_irq_handler, }, [VPH_ALARM_IRQ] = { .name = "vph-alarm", - .handler = default_irq_handler, }, [VPH_DROP_PRECHG_IRQ] = { .name = "vph-drop-prechg", - .handler = default_irq_handler, }, /* DCDC IRQs */ [OTG_FAIL_IRQ] = { @@ -1748,19 +1825,17 @@ static struct smb_irq_info smb5_irqs[] = { }, [OTG_OC_DISABLE_SW_IRQ] = { .name = "otg-oc-disable-sw", - .handler = default_irq_handler, }, [OTG_OC_HICCUP_IRQ] = { .name = "otg-oc-hiccup", - .handler = default_irq_handler, }, [BSM_ACTIVE_IRQ] = { .name = "bsm-active", - .handler = default_irq_handler, }, [HIGH_DUTY_CYCLE_IRQ] = { .name = "high-duty-cycle", .handler = high_duty_cycle_irq_handler, + .wake = true, }, [INPUT_CURRENT_LIMITING_IRQ] = { .name = "input-current-limiting", @@ -1768,7 +1843,6 @@ static struct smb_irq_info smb5_irqs[] = { }, [CONCURRENT_MODE_DISABLE_IRQ] = { .name = "concurrent-mode-disable", - .handler = default_irq_handler, }, [SWITCHER_POWER_OK_IRQ] = { .name = "switcher-power-ok", @@ -1778,10 +1852,10 @@ static struct smb_irq_info smb5_irqs[] = { [BAT_TEMP_IRQ] = { .name = "bat-temp", .handler = batt_temp_changed_irq_handler, + .wake = true, }, [ALL_CHNL_CONV_DONE_IRQ] = { .name = "all-chnl-conv-done", - .handler = default_irq_handler, }, [BAT_OV_IRQ] = { .name = "bat-ov", @@ -1801,11 +1875,9 @@ static struct smb_irq_info smb5_irqs[] = { }, [BUCK_OC_IRQ] = { .name = "buck-oc", - .handler = default_irq_handler, }, [VPH_OV_IRQ] = { .name = "vph-ov", - .handler = default_irq_handler, }, /* USB INPUT IRQs */ [USBIN_COLLAPSE_IRQ] = { @@ -1827,23 +1899,24 @@ static struct smb_irq_info smb5_irqs[] = { [USBIN_PLUGIN_IRQ] = { .name = "usbin-plugin", .handler = usb_plugin_irq_handler, + .wake = true, }, [USBIN_REVI_CHANGE_IRQ] = { .name = "usbin-revi-change", - .handler = default_irq_handler, }, [USBIN_SRC_CHANGE_IRQ] = { .name = "usbin-src-change", .handler = usb_source_change_irq_handler, + .wake = true, }, [USBIN_ICL_CHANGE_IRQ] = { .name = "usbin-icl-change", .handler = icl_change_irq_handler, + .wake = true, }, /* DC INPUT IRQs */ [DCIN_VASHDN_IRQ] = { .name = "dcin-vashdn", - .handler = default_irq_handler, }, [DCIN_UV_IRQ] = { .name = "dcin-uv", @@ -1860,7 +1933,6 @@ static struct smb_irq_info smb5_irqs[] = { }, [DCIN_REVI_IRQ] = { .name = "dcin-revi", - .handler = default_irq_handler, }, [DCIN_PON_IRQ] = { .name = "dcin-pon", @@ -1874,14 +1946,15 @@ static struct smb_irq_info smb5_irqs[] = { [TYPEC_OR_RID_DETECTION_CHANGE_IRQ] = { .name = "typec-or-rid-detect-change", .handler = typec_or_rid_detection_change_irq_handler, + .wake = true, }, [TYPEC_VPD_DETECT_IRQ] = { .name = "typec-vpd-detect", - .handler = default_irq_handler, }, [TYPEC_CC_STATE_CHANGE_IRQ] = { .name = "typec-cc-state-change", .handler = typec_state_change_irq_handler, + .wake = true, }, [TYPEC_VCONN_OC_IRQ] = { .name = "typec-vconn-oc", @@ -1889,11 +1962,11 @@ static struct smb_irq_info smb5_irqs[] = { }, [TYPEC_VBUS_CHANGE_IRQ] = { .name = "typec-vbus-change", - .handler = default_irq_handler, }, [TYPEC_ATTACH_DETACH_IRQ] = { .name = "typec-attach-detach", .handler = typec_attach_detach_irq_handler, + .wake = true, }, [TYPEC_LEGACY_CABLE_DETECT_IRQ] = { .name = "typec-legacy-cable-detect", @@ -1901,12 +1974,10 @@ static struct smb_irq_info smb5_irqs[] = { }, [TYPEC_TRY_SNK_SRC_DETECT_IRQ] = { .name = "typec-try-snk-src-detect", - .handler = default_irq_handler, }, /* MISCELLANEOUS IRQs */ [WDOG_SNARL_IRQ] = { .name = "wdog-snarl", - .handler = NULL, }, [WDOG_BARK_IRQ] = { .name = "wdog-bark", @@ -1914,7 +1985,6 @@ static struct smb_irq_info smb5_irqs[] = { }, [AICL_FAIL_IRQ] = { .name = "aicl-fail", - .handler = default_irq_handler, }, [AICL_DONE_IRQ] = { .name = "aicl-done", @@ -1922,24 +1992,19 @@ static struct smb_irq_info smb5_irqs[] = { }, [SMB_EN_IRQ] = { .name = "smb-en", - .handler = default_irq_handler, }, [IMP_TRIGGER_IRQ] = { .name = "imp-trigger", - .handler = default_irq_handler, }, [TEMP_CHANGE_IRQ] = { .name = "temp-change", - .handler = default_irq_handler, }, [TEMP_CHANGE_SMB_IRQ] = { .name = "temp-change-smb", - .handler = default_irq_handler, }, /* FLASH */ [VREG_OK_IRQ] = { .name = "vreg-ok", - .handler = schgm_flash_default_irq_handler, }, [ILIM_S2_IRQ] = { .name = "ilim2-s2", @@ -1947,15 +2012,12 @@ static struct smb_irq_info smb5_irqs[] = { }, [ILIM_S1_IRQ] = { .name = "ilim1-s1", - .handler = schgm_flash_default_irq_handler, }, [VOUT_DOWN_IRQ] = { .name = "vout-down", - .handler = schgm_flash_default_irq_handler, }, [VOUT_UP_IRQ] = { .name = "vout-up", - .handler = schgm_flash_default_irq_handler, }, [FLASH_STATE_CHANGE_IRQ] = { .name = "flash-state-change", @@ -1963,11 +2025,9 @@ static struct smb_irq_info smb5_irqs[] = { }, [TORCH_REQ_IRQ] = { .name = "torch-req", - .handler = schgm_flash_default_irq_handler, }, [FLASH_EN_IRQ] = { .name = "flash-en", - .handler = schgm_flash_default_irq_handler, }, }; diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 7a8b520b5ec1e0ce0eb1c0e905300340a374f39e..fc0ba98cf341f8f974eff7b4112d3b27d5be624f 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -5047,7 +5047,7 @@ int smblib_init(struct smb_charger *chg) switch (chg->mode) { case PARALLEL_MASTER: - rc = qcom_batt_init(); + rc = qcom_batt_init(chg->smb_version); if (rc < 0) { smblib_err(chg, "Couldn't init qcom_batt_init rc=%d\n", rc); diff --git a/drivers/power/supply/qcom/smb1390-charger.c b/drivers/power/supply/qcom/smb1390-charger.c new file mode 100644 index 0000000000000000000000000000000000000000..a181b386ea70e0fb2c7dab57a2ae5a7eca45b237 --- /dev/null +++ b/drivers/power/supply/qcom/smb1390-charger.c @@ -0,0 +1,744 @@ +/* Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "SMB1390: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CORE_STATUS1_REG 0x1006 +#define WIN_OV_BIT BIT(0) +#define WIN_UV_BIT BIT(1) +#define EN_PIN_OUT_BIT BIT(2) +#define LCM_AUTO_BIT BIT(3) +#define LCM_PIN_BIT BIT(4) +#define ILIM_BIT BIT(5) +#define TEMP_ALARM_BIT BIT(6) +#define VPH_OV_SOFT_BIT BIT(7) + +#define CORE_STATUS2_REG 0x1007 +#define SWITCHER_HOLD_OFF_BIT BIT(0) +#define VPH_OV_HARD_BIT BIT(1) +#define TSD_BIT BIT(2) +#define IREV_BIT BIT(3) +#define IOC_BIT BIT(4) +#define VIN_UV_BIT BIT(5) +#define VIN_OV_BIT BIT(6) +#define EN_PIN_OUT2_BIT BIT(7) + +#define CORE_STATUS3_REG 0x1008 +#define EN_SL_BIT BIT(0) +#define IIN_REF_SS_DONE_BIT BIT(1) +#define FLYCAP_SS_DONE_BIT BIT(2) +#define SL_DETECTED_BIT BIT(3) + +#define CORE_INT_RT_STS_REG 0x1010 +#define SWITCHER_OFF_WINDOW_STS_BIT BIT(0) +#define SWITCHER_OFF_FAULT_STS_BIT BIT(1) +#define TSD_STS_BIT BIT(2) +#define IREV_STS_BIT BIT(3) +#define VPH_OV_HARD_STS_BIT BIT(4) +#define VPH_OV_SOFT_STS_BIT BIT(5) +#define ILIM_STS_BIT BIT(6) +#define TEMP_ALARM_STS_BIT BIT(7) + +#define CORE_CONTROL1_REG 0x1020 +#define CMD_EN_SWITCHER_BIT BIT(0) +#define CMD_EN_SL_BIT BIT(1) + +#define CORE_FTRIM_ILIM_REG 0x1030 +#define CFG_ILIM_MASK GENMASK(4, 0) + +#define CP_VOTER "CP_VOTER" +#define USER_VOTER "USER_VOTER" +#define ILIM_VOTER "ILIM_VOTER" +#define FCC_VOTER "FCC_VOTER" +#define ICL_VOTER "ICL_VOTER" +#define USB_VOTER "USB_VOTER" + +enum { + SWITCHER_OFF_WINDOW_IRQ = 0, + SWITCHER_OFF_FAULT_IRQ, + TSD_IRQ, + IREV_IRQ, + VPH_OV_HARD_IRQ, + VPH_OV_SOFT_IRQ, + ILIM_IRQ, + TEMP_ALARM_IRQ, + NUM_IRQS, +}; + +struct smb1390_iio { + struct iio_channel *die_temp_chan; +}; + +struct smb1390 { + struct device *dev; + struct regmap *regmap; + struct notifier_block nb; + struct class cp_class; + + /* work structs */ + struct work_struct status_change_work; + struct work_struct taper_work; + + /* mutexes */ + spinlock_t status_change_lock; + + /* votables */ + struct votable *disable_votable; + struct votable *ilim_votable; + struct votable *fcc_votable; + + /* power supplies */ + struct power_supply *usb_psy; + struct power_supply *batt_psy; + + int irqs[NUM_IRQS]; + bool status_change_running; + bool taper_work_running; + struct smb1390_iio iio; +}; + +struct smb_irq { + const char *name; + const irq_handler_t handler; + const bool wake; +}; + +static const struct smb_irq smb_irqs[]; + +static int smb1390_read(struct smb1390 *chip, int reg, int *val) +{ + int rc; + + rc = regmap_read(chip->regmap, reg, val); + if (rc < 0) + pr_err("Couldn't read 0x%04x\n", reg); + + return rc; +} + +static int smb1390_masked_write(struct smb1390 *chip, int reg, int mask, + int val) +{ + int rc; + + pr_debug("Writing 0x%02x to 0x%04x with mask 0x%02x\n", val, reg, mask); + rc = regmap_update_bits(chip->regmap, reg, mask, val); + if (rc < 0) + pr_err("Couldn't write 0x%02x to 0x%04x with mask 0x%02x\n", + val, reg, mask); + + return rc; +} + +static bool is_psy_voter_available(struct smb1390 *chip) +{ + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) { + pr_debug("Couldn't find battery psy\n"); + return false; + } + } + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + pr_debug("Couldn't find usb psy\n"); + return false; + } + } + + if (!chip->fcc_votable) { + chip->fcc_votable = find_votable("FCC"); + if (!chip->fcc_votable) { + pr_debug("Couldn't find FCC votable\n"); + return false; + } + } + + return true; +} + +static irqreturn_t default_irq_handler(int irq, void *data) +{ + struct smb1390 *chip = data; + int i; + + for (i = 0; i < NUM_IRQS; ++i) { + if (irq == chip->irqs[i]) + pr_debug("%s IRQ triggered\n", smb_irqs[i].name); + } + + kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); + return IRQ_HANDLED; +} + +static const struct smb_irq smb_irqs[] = { + [SWITCHER_OFF_WINDOW_IRQ] = { + .name = "switcher-off-window", + .handler = default_irq_handler, + .wake = true, + }, + [SWITCHER_OFF_FAULT_IRQ] = { + .name = "switcher-off-fault", + .handler = default_irq_handler, + .wake = true, + }, + [TSD_IRQ] = { + .name = "tsd-fault", + .handler = default_irq_handler, + .wake = true, + }, + [IREV_IRQ] = { + .name = "irev-fault", + .handler = default_irq_handler, + .wake = true, + }, + [VPH_OV_HARD_IRQ] = { + .name = "vph-ov-hard", + .handler = default_irq_handler, + .wake = true, + }, + [VPH_OV_SOFT_IRQ] = { + .name = "vph-ov-soft", + .handler = default_irq_handler, + .wake = true, + }, + [ILIM_IRQ] = { + .name = "ilim", + .handler = default_irq_handler, + .wake = true, + }, + [TEMP_ALARM_IRQ] = { + .name = "temp-alarm", + .handler = default_irq_handler, + .wake = true, + }, +}; + +/* SYSFS functions for reporting smb1390 charge pump state */ +static ssize_t stat1_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct smb1390 *chip = container_of(c, struct smb1390, cp_class); + int rc, val; + + rc = smb1390_read(chip, CORE_STATUS1_REG, &val); + if (rc < 0) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%x\n", val); +} +static CLASS_ATTR_RO(stat1); + +static ssize_t stat2_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct smb1390 *chip = container_of(c, struct smb1390, cp_class); + int rc, val; + + rc = smb1390_read(chip, CORE_STATUS2_REG, &val); + if (rc < 0) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%x\n", val); +} +static CLASS_ATTR_RO(stat2); + +static ssize_t enable_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct smb1390 *chip = container_of(c, struct smb1390, cp_class); + + return snprintf(buf, PAGE_SIZE, "%d\n", + !get_effective_result(chip->disable_votable)); +} + +static ssize_t enable_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct smb1390 *chip = container_of(c, struct smb1390, cp_class); + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + vote(chip->disable_votable, USER_VOTER, !val, 0); + return count; +} +static CLASS_ATTR_RW(enable); + +static ssize_t die_temp_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct smb1390 *chip = container_of(c, struct smb1390, cp_class); + int die_temp_deciC = 0; + int rc; + + rc = iio_read_channel_processed(chip->iio.die_temp_chan, + &die_temp_deciC); + + return snprintf(buf, PAGE_SIZE, "%d\n", die_temp_deciC / 100); +} +static CLASS_ATTR_RO(die_temp); + +static struct attribute *cp_class_attrs[] = { + &class_attr_stat1.attr, + &class_attr_stat2.attr, + &class_attr_enable.attr, + &class_attr_die_temp.attr, + NULL, +}; +ATTRIBUTE_GROUPS(cp_class); + +/* voter callbacks */ +static int smb1390_disable_vote_cb(struct votable *votable, void *data, + int disable, const char *client) +{ + struct smb1390 *chip = data; + int rc = 0; + + if (!is_psy_voter_available(chip)) + return -EAGAIN; + + if (disable) { + rc = smb1390_masked_write(chip, CORE_CONTROL1_REG, + CMD_EN_SWITCHER_BIT, 0); + if (rc < 0) + return rc; + + } else { + rc = smb1390_masked_write(chip, CORE_CONTROL1_REG, + CMD_EN_SWITCHER_BIT, CMD_EN_SWITCHER_BIT); + if (rc < 0) + return rc; + } + + /* charging may have been disabled by ILIM; send uevent */ + kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); + return rc; +} + +static int smb1390_ilim_vote_cb(struct votable *votable, void *data, + int ilim_uA, const char *client) +{ + struct smb1390 *chip = data; + int rc = 0; + + if (!is_psy_voter_available(chip)) + return -EAGAIN; + + /* ILIM should always have at least one active vote */ + if (!client) { + pr_err("Client missing\n"); + return -EINVAL; + } + + /* ILIM less than 1A is not accurate; disable charging */ + if (ilim_uA < 1000000) { + pr_debug("ILIM %duA is too low to allow charging\n", ilim_uA); + vote(chip->disable_votable, ILIM_VOTER, true, 0); + } else { + pr_debug("setting ILIM to %duA\n", ilim_uA); + rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG, + CFG_ILIM_MASK, + DIV_ROUND_CLOSEST(ilim_uA - 500000, 100000)); + if (rc < 0) + pr_err("Failed to write ILIM Register, rc=%d\n", rc); + if (rc >= 0) + vote(chip->disable_votable, ILIM_VOTER, false, 0); + } + + return rc; +} + +static int smb1390_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct smb1390 *chip = container_of(nb, struct smb1390, nb); + struct power_supply *psy = data; + unsigned long flags; + + if (event != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (strcmp(psy->desc->name, "battery") == 0 + || strcmp(psy->desc->name, "usb") == 0 + || strcmp(psy->desc->name, "main") == 0) { + spin_lock_irqsave(&chip->status_change_lock, flags); + if (!chip->status_change_running) { + chip->status_change_running = true; + pm_stay_awake(chip->dev); + schedule_work(&chip->status_change_work); + } + spin_unlock_irqrestore(&chip->status_change_lock, flags); + } + + return NOTIFY_OK; +} + +static void smb1390_status_change_work(struct work_struct *work) +{ + struct smb1390 *chip = container_of(work, struct smb1390, + status_change_work); + union power_supply_propval pval = {0, }; + int rc; + + if (!is_psy_voter_available(chip)) + goto out; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &pval); + if (rc < 0) { + pr_err("Couldn't get usb present rc=%d\n", rc); + goto out; + } + + if (pval.intval != POWER_SUPPLY_TYPEC_SOURCE_DEFAULT + && pval.intval != POWER_SUPPLY_TYPEC_SOURCE_MEDIUM + && pval.intval != POWER_SUPPLY_TYPEC_SOURCE_HIGH) { + vote(chip->disable_votable, USB_VOTER, true, 0); + vote(chip->fcc_votable, CP_VOTER, false, 0); + } else { + vote(chip->disable_votable, USB_VOTER, false, 0); + + /* + * ILIM is set based on the primary chargers AICL result. This + * ensures VBUS does not collapse due to the current drawn via + * MID. + */ + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval); + if (rc < 0) + pr_err("Couldn't get usb icl rc=%d\n", rc); + else + vote(chip->ilim_votable, ICL_VOTER, true, pval.intval); + + /* input current is always half the charge current */ + vote(chip->ilim_votable, FCC_VOTER, true, + get_effective_result(chip->fcc_votable) / 2); + + /* + * all votes that would result in disabling the charge pump have + * been cast; ensure the charhe pump is still enabled before + * continuing. + */ + if (get_effective_result(chip->disable_votable)) + goto out; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); + if (rc < 0) { + pr_err("Couldn't get charge type rc=%d\n", rc); + } else if (pval.intval == + POWER_SUPPLY_CHARGE_TYPE_TAPER) { + /* + * mutual exclusion is already guaranteed by + * chip->status_change_running + */ + if (!chip->taper_work_running) { + chip->taper_work_running = true; + queue_work(system_long_wq, + &chip->taper_work); + } + } + } + +out: + pm_relax(chip->dev); + chip->status_change_running = false; +} + +static void smb1390_taper_work(struct work_struct *work) +{ + struct smb1390 *chip = container_of(work, struct smb1390, taper_work); + union power_supply_propval pval = {0, }; + int rc, fcc_uA; + + if (!is_psy_voter_available(chip)) + goto out; + + do { + fcc_uA = get_effective_result(chip->fcc_votable) - 100000; + pr_debug("taper work reducing FCC to %duA\n", fcc_uA); + vote(chip->fcc_votable, CP_VOTER, true, fcc_uA); + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); + if (rc < 0) { + pr_err("Couldn't get charge type rc=%d\n", rc); + goto out; + } + + msleep(500); + } while (fcc_uA >= 2000000 + && pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER); + +out: + pr_debug("taper work exit\n"); + chip->taper_work_running = false; +} + +static int smb1390_parse_dt(struct smb1390 *chip) +{ + int rc; + + rc = of_property_match_string(chip->dev->of_node, "io-channel-names", + "cp_die_temp"); + if (rc >= 0) { + chip->iio.die_temp_chan = + iio_channel_get(chip->dev, "cp_die_temp"); + if (IS_ERR(chip->iio.die_temp_chan)) { + rc = PTR_ERR(chip->iio.die_temp_chan); + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, + "cp_die_temp channel unavailable %ld\n", + rc); + chip->iio.die_temp_chan = NULL; + return rc; + } + } + + return rc; +} + +static void smb1390_release_channels(struct smb1390 *chip) +{ + if (!IS_ERR_OR_NULL(chip->iio.die_temp_chan)) + iio_channel_release(chip->iio.die_temp_chan); +} + +static int smb1390_create_votables(struct smb1390 *chip) +{ + chip->disable_votable = create_votable("CP_DISABLE", + VOTE_SET_ANY, smb1390_disable_vote_cb, chip); + if (IS_ERR(chip->disable_votable)) + return PTR_ERR(chip->disable_votable); + + chip->ilim_votable = create_votable("CP_ILIM", + VOTE_MIN, smb1390_ilim_vote_cb, chip); + if (IS_ERR(chip->ilim_votable)) + return PTR_ERR(chip->ilim_votable); + + return 0; +} + +static void smb1390_destroy_votables(struct smb1390 *chip) +{ + destroy_votable(chip->disable_votable); + destroy_votable(chip->ilim_votable); +} + +static int smb1390_init_hw(struct smb1390 *chip) +{ + /* + * charge pump is initially disabled; this indirectly votes to allow + * traditional parallel charging if present + */ + vote(chip->disable_votable, USER_VOTER, true, 0); + return 0; +} + +static int smb1390_get_irq_index_byname(const char *irq_name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(smb_irqs); i++) { + if (strcmp(smb_irqs[i].name, irq_name) == 0) + return i; + } + + return -ENOENT; +} + +static int smb1390_request_interrupt(struct smb1390 *chip, + struct device_node *node, + const char *irq_name) +{ + int rc = 0, irq, irq_index; + + irq = of_irq_get_byname(node, irq_name); + if (irq < 0) { + pr_err("Couldn't get irq %s byname\n", irq_name); + return irq; + } + + irq_index = smb1390_get_irq_index_byname(irq_name); + if (irq_index < 0) { + pr_err("%s is not a defined irq\n", irq_name); + return irq_index; + } + + if (!smb_irqs[irq_index].handler) + return 0; + + rc = devm_request_threaded_irq(chip->dev, irq, NULL, + smb_irqs[irq_index].handler, + IRQF_ONESHOT, irq_name, chip); + if (rc < 0) { + pr_err("Couldn't request irq %d rc=%d\n", irq, rc); + return rc; + } + + chip->irqs[irq_index] = irq; + if (smb_irqs[irq_index].wake) + enable_irq_wake(irq); + + return rc; +} + +static int smb1390_request_interrupts(struct smb1390 *chip) +{ + struct device_node *node = chip->dev->of_node; + struct device_node *child; + int rc = 0; + const char *name; + struct property *prop; + + for_each_available_child_of_node(node, child) { + of_property_for_each_string(child, "interrupt-names", + prop, name) { + rc = smb1390_request_interrupt(chip, child, name); + if (rc < 0) { + pr_err("Couldn't request interrupt %s rc=%d\n", + name, rc); + return rc; + } + } + } + + return rc; +} + +static int smb1390_probe(struct platform_device *pdev) +{ + struct smb1390 *chip; + int rc; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + spin_lock_init(&chip->status_change_lock); + + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + pr_err("Couldn't get regmap\n"); + return -EINVAL; + } + + INIT_WORK(&chip->status_change_work, smb1390_status_change_work); + INIT_WORK(&chip->taper_work, smb1390_taper_work); + + rc = smb1390_parse_dt(chip); + if (rc < 0) { + pr_err("Couldn't parse device tree rc=%d\n", rc); + goto out_work; + } + + rc = smb1390_create_votables(chip); + if (rc < 0) { + pr_err("Couldn't create votables rc=%d\n", rc); + goto out_work; + } + + rc = smb1390_init_hw(chip); + if (rc < 0) { + pr_err("Couldn't init hardware rc=%d\n", rc); + goto out_votables; + } + + chip->nb.notifier_call = smb1390_notifier_cb; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc=%d\n", rc); + goto out_votables; + } + + chip->cp_class.name = "charge_pump"; + chip->cp_class.owner = THIS_MODULE; + chip->cp_class.class_groups = cp_class_groups; + rc = class_register(&chip->cp_class); + if (rc < 0) { + pr_err("Couldn't register charge_pump sysfs class rc=%d\n", rc); + goto out_notifier; + + } + + rc = smb1390_request_interrupts(chip); + if (rc < 0) { + pr_err("Couldn't request interrupts rc=%d\n", rc); + goto out_class; + } + + pr_info("smb1390 probed successfully"); + return 0; + +out_class: + class_unregister(&chip->cp_class); +out_notifier: + power_supply_unreg_notifier(&chip->nb); +out_votables: + smb1390_destroy_votables(chip); +out_work: + cancel_work(&chip->taper_work); + cancel_work(&chip->status_change_work); + return rc; +} + +static int smb1390_remove(struct platform_device *pdev) +{ + struct smb1390 *chip = platform_get_drvdata(pdev); + + class_unregister(&chip->cp_class); + power_supply_unreg_notifier(&chip->nb); + + /* explicitly disable charging */ + vote(chip->disable_votable, USER_VOTER, true, 0); + cancel_work(&chip->taper_work); + cancel_work(&chip->status_change_work); + smb1390_destroy_votables(chip); + smb1390_release_channels(chip); + return 0; +} + +static const struct of_device_id match_table[] = { + { .compatible = "qcom,smb1390-charger", }, + { }, +}; + +static struct platform_driver smb1390_driver = { + .driver = { + .name = "qcom,smb1390-charger", + .owner = THIS_MODULE, + .of_match_table = match_table, + }, + .probe = smb1390_probe, + .remove = smb1390_remove, +}; +module_platform_driver(smb1390_driver); + +MODULE_DESCRIPTION("SMB1390 Charge Pump Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index fff32eccde99d3202c2813445483c055c9f1ef17..0435a94907ec006ecd69b32c5535cf19404a3483 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.c @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include #include "smb5-lib.h" #include "smb5-reg.h" #include "battery.h" @@ -38,6 +40,10 @@ __func__, ##__VA_ARGS__); \ } while (0) +#define typec_rp_med_high(chg, typec_mode) \ + ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM \ + || typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH) \ + && !chg->typec_legacy) int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) { @@ -592,6 +598,8 @@ static int smblib_notifier_call(struct notifier_block *nb, chg->bms_psy = psy; if (ev == PSY_EVENT_PROP_CHANGED) schedule_work(&chg->bms_update_work); + if (!chg->jeita_configured) + schedule_work(&chg->jeita_update_work); } if (!chg->pl.psy && !strcmp(psy->desc->name, "parallel")) { @@ -651,6 +659,7 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param, return 0; } +#define SDP_100_MA 100000 static void smblib_uusb_removal(struct smb_charger *chg) { int rc; @@ -675,6 +684,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) /* reset both usbin current and voltage votes */ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); /* reconfigure allowed voltage for HVDCP */ @@ -690,6 +700,13 @@ static void smblib_uusb_removal(struct smb_charger *chg) chg->pulse_cnt = 0; chg->uusb_apsd_rerun_done = false; + /* write back the default FLOAT charger configuration */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + (u8)FLOAT_OPTIONS_MASK, chg->float_cfg); + if (rc < 0) + smblib_err(chg, "Couldn't write float charger options rc=%d\n", + rc); + /* clear USB ICL vote for USB_PSY_VOTER */ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); if (rc < 0) @@ -707,20 +724,21 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg) int rc; union power_supply_propval val; - if (!chg->suspend_input_on_debug_batt) - return; - rc = power_supply_get_property(chg->bms_psy, POWER_SUPPLY_PROP_DEBUG_BATTERY, &val); if (rc < 0) { smblib_err(chg, "Couldn't get debug battery prop rc=%d\n", rc); return; } - - vote(chg->usb_icl_votable, DEBUG_BOARD_VOTER, val.intval, 0); - vote(chg->dc_suspend_votable, DEBUG_BOARD_VOTER, val.intval, 0); - if (val.intval) - pr_info("Input suspended: Fake battery\n"); + if (chg->suspend_input_on_debug_batt) { + vote(chg->usb_icl_votable, DEBUG_BOARD_VOTER, val.intval, 0); + vote(chg->dc_suspend_votable, DEBUG_BOARD_VOTER, val.intval, 0); + if (val.intval) + pr_info("Input suspended: Fake battery\n"); + } else { + vote(chg->chg_disable_votable, DEBUG_BOARD_VOTER, + val.intval, 0); + } } int smblib_rerun_apsd_if_required(struct smb_charger *chg) @@ -868,6 +886,10 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) set_mode: rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, USBIN_MODE_CHG_BIT, hc_mode ? USBIN_MODE_CHG_BIT : 0); + if (rc < 0) { + smblib_err(chg, "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc); + goto out; + } /* unsuspend after configuring current and override */ rc = smblib_set_usb_suspend(chg, false); @@ -876,6 +898,9 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) goto out; } + /* Re-run AICL */ + if (chg->real_charger_type != POWER_SUPPLY_TYPE_USB) + rc = smblib_rerun_aicl(chg); out: return rc; } @@ -905,7 +930,8 @@ int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua) return INT_MAX; /* override is set */ - rc = smblib_get_charge_param(chg, &chg->param.usb_icl, icl_ua); + rc = smblib_get_charge_param(chg, &chg->param.icl_max_stat, + icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't get HC ICL rc=%d\n", rc); return rc; @@ -1427,6 +1453,19 @@ int smblib_get_prop_batt_charge_counter(struct smb_charger *chg, return rc; } +int smblib_get_prop_batt_cycle_count(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CYCLE_COUNT, val); + return rc; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -1788,6 +1827,36 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, return 0; } +int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + if (chg->iio.usbin_v_chan) + return iio_read_channel_processed(chg->iio.usbin_v_chan, + &val->intval); + else + return -ENODATA; +} + +int smblib_get_prop_charger_temp(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (chg->iio.temp_chan) { + rc = iio_read_channel_processed(chg->iio.temp_chan, + &val->intval); + if (rc < 0) { + pr_err("Error in reading temp channel, rc=%d", rc); + return rc; + } + val->intval /= 100; + } else { + return -ENODATA; + } + + return rc; +} + int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg, union power_supply_propval *val) { @@ -1936,6 +2005,36 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, return rc; } +int smblib_get_prop_usb_current_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + + if (chg->iio.usbin_i_chan) { + rc = iio_read_channel_processed(chg->iio.usbin_i_chan, + &val->intval); + + /* + * For PM855B, scaling factor = reciprocal of + * 0.2V/A in Buck mode, 0.4V/A in Boost mode. + */ + if (smblib_get_prop_ufp_mode(chg) != POWER_SUPPLY_TYPEC_NONE) { + val->intval *= 5; + return rc; + } + + if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE) { + val->intval = DIV_ROUND_CLOSEST(val->intval * 100, 40); + return rc; + } + } else { + rc = -ENODATA; + } + + val->intval = 0; + return rc; +} + int smblib_get_prop_input_current_settled(struct smb_charger *chg, union power_supply_propval *val) { @@ -2066,16 +2165,40 @@ static int smblib_handle_usb_current(struct smb_charger *chg, if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { if (usb_current == -ETIMEDOUT) { - /* - * Valid FLOAT charger, report the current based - * of Rp - */ - typec_mode = smblib_get_prop_typec_mode(chg); - rp_ua = get_rp_based_dcp_current(chg, typec_mode); - rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, - true, rp_ua); - if (rc < 0) + if ((chg->float_cfg & FLOAT_OPTIONS_MASK) + == FORCE_FLOAT_SDP_CFG_BIT) { + /* + * Confiugure USB500 mode if Float charger is + * configured for SDP mode. + */ + rc = set_sdp_current(chg, USBIN_500MA); + if (rc < 0) + smblib_err(chg, + "Couldn't set SDP ICL rc=%d\n", + rc); + return rc; + } + + if (chg->connector_type == + POWER_SUPPLY_CONNECTOR_TYPEC) { + /* + * Valid FLOAT charger, report the current + * based of Rp. + */ + typec_mode = smblib_get_prop_typec_mode(chg); + rp_ua = get_rp_based_dcp_current(chg, + typec_mode); + rc = vote(chg->usb_icl_votable, + SW_ICL_MAX_VOTER, true, rp_ua); + if (rc < 0) + return rc; + } else { + rc = vote(chg->usb_icl_votable, + SW_ICL_MAX_VOTER, true, DCP_CURRENT_UA); + if (rc < 0) + return rc; + } } else { /* * FLOAT charger detected as SDP by USB driver, @@ -2087,7 +2210,7 @@ static int smblib_handle_usb_current(struct smb_charger *chg, true, usb_current); if (rc < 0) return rc; - rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, + rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); if (rc < 0) return rc; @@ -2095,9 +2218,20 @@ static int smblib_handle_usb_current(struct smb_charger *chg, } else { rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, true, usb_current); + if (rc < 0) { + pr_err("Couldn't vote ICL USB_PSY_VOTER rc=%d\n", rc); + return rc; + } + + rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + if (rc < 0) { + pr_err("Couldn't remove SW_ICL_MAX vote rc=%d\n", rc); + return rc; + } + } - return rc; + return 0; } int smblib_set_prop_sdp_current_max(struct smb_charger *chg, @@ -2230,7 +2364,9 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, * pd_current_max */ vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); } else { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA); vote(chg->usb_icl_votable, PD_VOTER, false, 0); vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); @@ -2773,17 +2909,16 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, { if (rising) { - /* enable HDC and ICL irq for QC2/3 charger */ - if (qc_charger) + if (qc_charger) { + /* enable HDC and ICL irq for QC2/3 charger */ vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0); - else - /* - * HVDCP detection timeout done - * If adapter is not QC2.0/QC3.0 - it is a plain old DCP. - * enforce DCP ICL if specified - */ + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CURRENT_UA); + } else { + /* A plain DCP, enforce DCP ICL if specified */ vote(chg->usb_icl_votable, DCP_VOTER, chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua); + } } smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", __func__, @@ -2798,6 +2933,69 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +static void update_sw_icl_max(struct smb_charger *chg, int pst) +{ + int typec_mode; + int rp_ua; + + /* while PD is active it should have complete ICL control */ + if (chg->pd_active) + return; + + /* + * HVDCP 2/3, handled separately + * For UNKNOWN(input not present) return without updating ICL + */ + if (pst == POWER_SUPPLY_TYPE_USB_HVDCP + || pst == POWER_SUPPLY_TYPE_USB_HVDCP_3 + || pst == POWER_SUPPLY_TYPE_UNKNOWN) + return; + + /* TypeC rp med or high, use rp value */ + typec_mode = smblib_get_prop_typec_mode(chg); + if (typec_rp_med_high(chg, typec_mode)) { + rp_ua = get_rp_based_dcp_current(chg, typec_mode); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); + return; + } + + /* rp-std or legacy, USB BC 1.2 */ + switch (pst) { + case POWER_SUPPLY_TYPE_USB: + /* + * USB_PSY will vote to increase the current to 500/900mA once + * enumeration is done. + */ + if (!is_client_vote_enabled(chg->usb_icl_votable, + USB_PSY_VOTER)) + vote(chg->usb_icl_votable, USB_PSY_VOTER, true, + SDP_100_MA); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + break; + case POWER_SUPPLY_TYPE_USB_CDP: + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + CDP_CURRENT_UA); + break; + case POWER_SUPPLY_TYPE_USB_DCP: + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + DCP_CURRENT_UA); + break; + case POWER_SUPPLY_TYPE_USB_FLOAT: + /* + * limit ICL to 100mA, the USB driver will enumerate to check + * if this is a SDP and appropriately set the current + */ + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + SDP_100_MA); + break; + default: + smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + SDP_CURRENT_UA); + break; + } +} + static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { const struct apsd_result *apsd_result; @@ -2807,15 +3005,17 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) apsd_result = smblib_update_usb_type(chg); + update_sw_icl_max(chg, apsd_result->pst); + switch (apsd_result->bit) { case SDP_CHARGER_BIT: case CDP_CHARGER_BIT: + case FLOAT_CHARGER_BIT: if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) || chg->use_extcon) smblib_notify_device_mode(chg, true); break; case OCP_CHARGER_BIT: - case FLOAT_CHARGER_BIT: case DCP_CHARGER_BIT: break; default: @@ -2972,7 +3172,7 @@ static void typec_src_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->pl_enable_work); /* reset input current limit voters */ - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA); vote(chg->usb_icl_votable, PD_VOTER, false, 0); vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, DCP_VOTER, false, 0); @@ -3023,41 +3223,27 @@ static void typec_src_removal(struct smb_charger *chg) static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) { - int rp_ua; const struct apsd_result *apsd = smblib_get_apsd_result(chg); - if ((apsd->pst != POWER_SUPPLY_TYPE_USB_DCP) - && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT)) - return; - - /* - * if APSD indicates FLOAT and the USB stack had detected SDP, - * do not respond to Rp changes as we do not confirm that its - * a legacy cable - */ - if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) - return; /* * We want the ICL vote @ 100mA for a FLOAT charger * until the detection by the USB stack is complete. * Ignore the Rp changes unless there is a - * pre-existing valid vote. + * pre-existing valid vote or FLOAT is configured for + * SDP current. */ - if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT && - get_client_vote(chg->usb_icl_votable, - LEGACY_UNKNOWN_VOTER) <= 100000) - return; + if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT) { + if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER) + <= USBIN_100MA + || (chg->float_cfg & FLOAT_OPTIONS_MASK) + == FORCE_FLOAT_SDP_CFG_BIT) + return; + } + + update_sw_icl_max(chg, apsd->pst); - /* - * handle Rp change for DCP/FLOAT/OCP. - * Update the current only if the Rp is different from - * the last Rp value. - */ smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n", chg->typec_mode, typec_mode); - - rp_ua = get_rp_based_dcp_current(chg, typec_mode); - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); } irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data) @@ -3436,6 +3622,100 @@ static void smblib_pl_enable_work(struct work_struct *work) vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); } +#define JEITA_SOFT 0 +#define JEITA_HARD 1 +static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds, + int type) +{ + int rc; + u16 temp, base; + + base = CHGR_JEITA_THRESHOLD_BASE_REG(type); + + temp = thresholds[1] & 0xFFFF; + temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); + rc = smblib_batch_write(chg, base, (u8 *)&temp, 2); + if (rc < 0) { + smblib_err(chg, + "Couldn't configure Jeita %s hot threshold rc=%d\n", + (type == JEITA_SOFT) ? "Soft" : "Hard", rc); + return rc; + } + + temp = thresholds[0] & 0xFFFF; + temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); + rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2); + if (rc < 0) { + smblib_err(chg, + "Couldn't configure Jeita %s cold threshold rc=%d\n", + (type == JEITA_SOFT) ? "Soft" : "Hard", rc); + return rc; + } + + smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n", + (type == JEITA_SOFT) ? "Soft" : "Hard"); + + return 0; +} + +static void jeita_update_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + jeita_update_work); + struct device_node *node = chg->dev->of_node; + struct device_node *batt_node, *pnode; + union power_supply_propval val; + int rc; + u32 jeita_thresholds[2]; + + batt_node = of_find_node_by_name(node, "qcom,battery-data"); + if (!batt_node) { + smblib_err(chg, "Batterydata not available\n"); + goto out; + } + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_RESISTANCE_ID, &val); + if (rc < 0) { + smblib_err(chg, "Failed to get batt-id rc=%d\n", rc); + goto out; + } + + pnode = of_batterydata_get_best_profile(batt_node, + val.intval / 1000, NULL); + if (IS_ERR(pnode)) { + rc = PTR_ERR(pnode); + smblib_err(chg, "Failed to detect valid battery profile %d\n", + rc); + goto out; + } + + rc = of_property_read_u32_array(pnode, "qcom,jeita-hard-thresholds", + jeita_thresholds, 2); + if (!rc) { + rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_HARD); + if (rc < 0) { + smblib_err(chg, "Couldn't configure Hard Jeita rc=%d\n", + rc); + goto out; + } + } + + rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-thresholds", + jeita_thresholds, 2); + if (!rc) { + rc = smblib_update_jeita(chg, jeita_thresholds, JEITA_SOFT); + if (rc < 0) { + smblib_err(chg, "Couldn't configure Soft Jeita rc=%d\n", + rc); + goto out; + } + } + +out: + chg->jeita_configured = true; +} + static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; @@ -3531,6 +3811,16 @@ static void smblib_destroy_votables(struct smb_charger *chg) destroy_votable(chg->chg_disable_votable); } +static void smblib_iio_deinit(struct smb_charger *chg) +{ + if (!IS_ERR_OR_NULL(chg->iio.usbin_v_chan)) + iio_channel_release(chg->iio.usbin_v_chan); + if (!IS_ERR_OR_NULL(chg->iio.usbin_i_chan)) + iio_channel_release(chg->iio.usbin_i_chan); + if (!IS_ERR_OR_NULL(chg->iio.temp_chan)) + iio_channel_release(chg->iio.temp_chan); +} + int smblib_init(struct smb_charger *chg) { int rc = 0; @@ -3538,6 +3828,7 @@ int smblib_init(struct smb_charger *chg) mutex_init(&chg->lock); INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->pl_update_work, pl_update_work); + INIT_WORK(&chg->jeita_update_work, jeita_update_work); INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); @@ -3547,10 +3838,11 @@ int smblib_init(struct smb_charger *chg) chg->fake_input_current_limited = -EINVAL; chg->fake_batt_status = -EINVAL; chg->sink_src_mode = UNATTACHED_MODE; + chg->jeita_configured = false; switch (chg->mode) { case PARALLEL_MASTER: - rc = qcom_batt_init(); + rc = qcom_batt_init(chg->smb_version); if (rc < 0) { smblib_err(chg, "Couldn't init qcom_batt_init rc=%d\n", rc); @@ -3604,6 +3896,7 @@ int smblib_deinit(struct smb_charger *chg) switch (chg->mode) { case PARALLEL_MASTER: cancel_work_sync(&chg->bms_update_work); + cancel_work_sync(&chg->jeita_update_work); cancel_work_sync(&chg->pl_update_work); cancel_delayed_work_sync(&chg->clear_hdc_work); cancel_delayed_work_sync(&chg->icl_change_work); @@ -3622,5 +3915,7 @@ int smblib_deinit(struct smb_charger *chg) return -EINVAL; } + smblib_iio_deinit(chg); + return 0; } diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h index 3a4c4cf2ee3f1096b87773f41f32eb97eee1ee87..b7a2b5a0d8e2c1529b4c4db89d9fa8c7cb4b63f9 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -56,7 +56,7 @@ enum print_reason { #define CTM_VOTER "CTM_VOTER" #define SW_QC3_VOTER "SW_QC3_VOTER" #define AICL_RERUN_VOTER "AICL_RERUN_VOTER" -#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER" +#define SW_ICL_MAX_VOTER "SW_ICL_MAX_VOTER" #define QNOVO_VOTER "QNOVO_VOTER" #define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER" #define OTG_DELAY_VOTER "OTG_DELAY_VOTER" @@ -232,6 +232,7 @@ struct smb_params { struct smb_chg_param fcc; struct smb_chg_param fv; struct smb_chg_param usb_icl; + struct smb_chg_param icl_max_stat; struct smb_chg_param icl_stat; struct smb_chg_param otg_cl; struct smb_chg_param jeita_cc_comp_hot; @@ -245,14 +246,10 @@ struct parallel_params { struct smb_iio { struct iio_channel *temp_chan; - struct iio_channel *temp_max_chan; struct iio_channel *usbin_i_chan; struct iio_channel *usbin_v_chan; struct iio_channel *batt_i_chan; struct iio_channel *connector_temp_chan; - struct iio_channel *connector_temp_thr1_chan; - struct iio_channel *connector_temp_thr2_chan; - struct iio_channel *connector_temp_thr3_chan; }; struct smb_charger { @@ -308,6 +305,7 @@ struct smb_charger { /* work */ struct work_struct bms_update_work; struct work_struct pl_update_work; + struct work_struct jeita_update_work; struct delayed_work ps_change_timeout_work; struct delayed_work clear_hdc_work; struct delayed_work icl_change_work; @@ -355,6 +353,7 @@ struct smb_charger { int hw_max_icl_ua; int auto_recharge_soc; enum sink_src_mode sink_src_mode; + bool jeita_configured; /* workaround flag */ u32 wa_flags; @@ -458,6 +457,8 @@ int smblib_get_prop_batt_temp(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_charge_counter(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_batt_cycle_count(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_batt_capacity(struct smb_charger *chg, @@ -485,6 +486,10 @@ int smblib_get_prop_usb_suspend(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_usb_current_now(struct smb_charger *chg, + union power_supply_propval *val); int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_typec_power_role(struct smb_charger *chg, @@ -497,6 +502,8 @@ int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_pe_start(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_charger_temp(struct smb_charger *chg, + union power_supply_propval *val); int smblib_get_prop_die_health(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_pd_current_max(struct smb_charger *chg, diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h index 92a52b2cf801782e7ec67f94ee08f51115e756df..035e8c0774d3066a7c3c1e9a8b93ce099850c0c9 100644 --- a/drivers/power/supply/qcom/smb5-reg.h +++ b/drivers/power/supply/qcom/smb5-reg.h @@ -101,9 +101,12 @@ enum { #define JEITA_CCCOMP_CFG_HOT_REG (CHGR_BASE + 0x92) #define JEITA_CCCOMP_CFG_COLD_REG (CHGR_BASE + 0x93) +#define CHGR_JEITA_THRESHOLD_BASE_REG(i) (CHGR_BASE + 0x94 + (i * 4)) /******************************** * DCDC Peripheral Registers * ********************************/ +#define ICL_MAX_STATUS_REG (DCDC_BASE + 0x06) + #define AICL_ICL_STATUS_REG (DCDC_BASE + 0x08) #define AICL_STATUS_REG (DCDC_BASE + 0x0A) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1581f6ab1b1f425986cb8693a961cee7fa4f42f8..89ff9991cc1ea62c8e55f183b3cbc2e0b1e373db 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -294,6 +294,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, pwm->pwm = chip->base + i; pwm->hwpwm = i; pwm->state.polarity = polarity; + pwm->state.output_type = PWM_OUTPUT_FIXED; if (chip->ops->get_state) chip->ops->get_state(chip, pwm, &pwm->state); @@ -507,6 +508,31 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) pwm->state.polarity = state->polarity; } + if (state->output_type != pwm->state.output_type) { + if (!pwm->chip->ops->set_output_type) + return -ENOTSUPP; + + err = pwm->chip->ops->set_output_type(pwm->chip, pwm, + state->output_type); + if (err) + return err; + + pwm->state.output_type = state->output_type; + } + + if (state->output_pattern != pwm->state.output_pattern && + state->output_pattern != NULL) { + if (!pwm->chip->ops->set_output_pattern) + return -ENOTSUPP; + + err = pwm->chip->ops->set_output_pattern(pwm->chip, + pwm, state->output_pattern); + if (err) + return err; + + pwm->state.output_pattern = state->output_pattern; + } + if (state->period != pwm->state.period || state->duty_cycle != pwm->state.duty_cycle) { err = pwm->chip->ops->config(pwm->chip, pwm, diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c index d31a18d8142b0e9a4e787a158515896def945db5..aca41a8d5b9c9cca1538b15705179f764a53b158 100644 --- a/drivers/pwm/pwm-qti-lpg.c +++ b/drivers/pwm/pwm-qti-lpg.c @@ -24,11 +24,16 @@ #include #include #include +#include #include #define REG_SIZE_PER_LPG 0x100 +#define LPG_BASE "lpg-base" +#define LUT_BASE "lut-base" +/* LPG module registers */ #define REG_LPG_PERPH_SUBTYPE 0x05 +#define REG_LPG_PATTERN_CONFIG 0x40 #define REG_LPG_PWM_SIZE_CLK 0x41 #define REG_LPG_PWM_FREQ_PREDIV_CLK 0x42 #define REG_LPG_PWM_TYPE_CONFIG 0x43 @@ -36,16 +41,29 @@ #define REG_LPG_PWM_VALUE_MSB 0x45 #define REG_LPG_ENABLE_CONTROL 0x46 #define REG_LPG_PWM_SYNC 0x47 +#define REG_LPG_RAMP_STEP_DURATION_LSB 0x50 +#define REG_LPG_RAMP_STEP_DURATION_MSB 0x51 +#define REG_LPG_PAUSE_HI_MULTIPLIER 0x52 +#define REG_LPG_PAUSE_LO_MULTIPLIER 0x54 +#define REG_LPG_HI_INDEX 0x56 +#define REG_LPG_LO_INDEX 0x57 + +/* REG_LPG_PATTERN_CONFIG */ +#define LPG_PATTERN_EN_PAUSE_LO BIT(0) +#define LPG_PATTERN_EN_PAUSE_HI BIT(1) +#define LPG_PATTERN_RAMP_TOGGLE BIT(2) +#define LPG_PATTERN_REPEAT BIT(3) +#define LPG_PATTERN_RAMP_LO_TO_HI BIT(4) /* REG_LPG_PERPH_SUBTYPE */ #define SUBTYPE_PWM 0x0b #define SUBTYPE_LPG_LITE 0x11 /* REG_LPG_PWM_SIZE_CLK */ -#define LPG_PWM_SIZE_MASK_LPG BIT(4) -#define LPG_PWM_SIZE_MASK_PWM BIT(2) -#define LPG_PWM_SIZE_SHIFT_LPG 4 -#define LPG_PWM_SIZE_SHIFT_PWM 2 +#define LPG_PWM_SIZE_LPG_MASK BIT(4) +#define LPG_PWM_SIZE_PWM_MASK BIT(2) +#define LPG_PWM_SIZE_LPG_SHIFT 4 +#define LPG_PWM_SIZE_PWM_SHIFT 2 #define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0) /* REG_LPG_PWM_FREQ_PREDIV_CLK */ @@ -64,6 +82,7 @@ /* REG_LPG_ENABLE_CONTROL */ #define LPG_EN_LPG_OUT_BIT BIT(7) +#define LPG_EN_LPG_OUT_SHIFT 7 #define LPG_PWM_SRC_SELECT_MASK BIT(2) #define LPG_PWM_SRC_SELECT_SHIFT 2 #define LPG_EN_RAMP_GEN_MASK BIT(1) @@ -77,9 +96,18 @@ #define NUM_CLK_PREDIV 4 #define NUM_PWM_EXP 8 -enum { +#define LPG_HI_LO_IDX_MASK GENMASK(5, 0) + +/* LUT module registers */ +#define REG_LPG_LUT_1_LSB 0x42 +#define REG_LPG_LUT_RAMP_CONTROL 0xc8 + +#define LPG_LUT_VALUE_MSB_MASK BIT(0) +#define LPG_LUT_COUNT_MAX 47 + +enum lpg_src { LUT_PATTERN = 0, - PWM_OUTPUT, + PWM_VALUE, }; static const int pwm_size[NUM_PWM_SIZE] = {6, 9}; @@ -87,6 +115,19 @@ static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000}; static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6}; static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7}; +struct lpg_ramp_config { + u16 step_ms; + u8 pause_hi_count; + u8 pause_lo_count; + u8 hi_idx; + u8 lo_idx; + bool ramp_dir_low_to_hi; + bool pattern_repeat; + bool toggle; + u32 *pattern; + u32 pattern_length; +}; + struct lpg_pwm_config { u32 pwm_size; u32 pwm_clk; @@ -96,13 +137,23 @@ struct lpg_pwm_config { u32 best_period_ns; }; +struct qpnp_lpg_lut { + struct qpnp_lpg_chip *chip; + struct mutex lock; + u32 reg_base; + u32 *pattern; /* patterns in percentage */ +}; + struct qpnp_lpg_channel { struct qpnp_lpg_chip *chip; struct lpg_pwm_config pwm_config; + struct lpg_ramp_config ramp_config; u32 lpg_idx; u32 reg_base; + u32 max_pattern_length; u8 src_sel; u8 subtype; + bool lut_written; int current_period_ns; int current_duty_ns; }; @@ -112,6 +163,7 @@ struct qpnp_lpg_chip { struct regmap *regmap; struct device *dev; struct qpnp_lpg_channel *lpgs; + struct qpnp_lpg_lut *lut; struct mutex bus_lock; u32 num_lpgs; }; @@ -163,6 +215,36 @@ static int qpnp_lpg_masked_write(struct qpnp_lpg_channel *lpg, return rc; } +static int qpnp_lut_write(struct qpnp_lpg_lut *lut, u16 addr, u8 val) +{ + int rc; + + mutex_lock(&lut->chip->bus_lock); + rc = regmap_write(lut->chip->regmap, lut->reg_base + addr, val); + if (rc < 0) + dev_err(lut->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n", + lut->reg_base + addr, val, rc); + mutex_unlock(&lut->chip->bus_lock); + + return rc; +} + +static int qpnp_lut_masked_write(struct qpnp_lpg_lut *lut, + u16 addr, u8 mask, u8 val) +{ + int rc; + + mutex_lock(&lut->chip->bus_lock); + rc = regmap_update_bits(lut->chip->regmap, lut->reg_base + addr, + mask, val); + if (rc < 0) + dev_err(lut->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n", + lut->reg_base + addr, val, mask, rc); + mutex_unlock(&lut->chip->bus_lock); + + return rc; +} + static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip, struct pwm_device *pwm) { @@ -228,11 +310,11 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg) /* pwm_clk_idx is 1 bit lower than the register value */ pwm_clk_idx += 1; if (lpg->subtype == SUBTYPE_PWM) { - shift = LPG_PWM_SIZE_SHIFT_PWM; - mask = LPG_PWM_SIZE_MASK_PWM; + shift = LPG_PWM_SIZE_PWM_SHIFT; + mask = LPG_PWM_SIZE_PWM_MASK; } else { - shift = LPG_PWM_SIZE_SHIFT_LPG; - mask = LPG_PWM_SIZE_MASK_LPG; + shift = LPG_PWM_SIZE_LPG_SHIFT; + mask = LPG_PWM_SIZE_LPG_MASK; } val = pwm_size_idx << shift | pwm_clk_idx; @@ -253,6 +335,9 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg) return rc; } + if (lpg->src_sel == LUT_PATTERN) + return 0; + val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK; rc = qpnp_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val); if (rc < 0) { @@ -281,6 +366,145 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg) return rc; } +static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg, + unsigned int *pattern, unsigned int length) +{ + struct qpnp_lpg_lut *lut = lpg->chip->lut; + int i, rc = 0; + u16 full_duty_value, pwm_values[LPG_LUT_COUNT_MAX + 1] = {0}; + u8 lsb, msb, addr; + + if (length > lpg->max_pattern_length) { + dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n", + length, lpg->max_pattern_length); + return -EINVAL; + } + + /* Program LUT pattern */ + mutex_lock(&lut->lock); + addr = REG_LPG_LUT_1_LSB + lpg->ramp_config.lo_idx * 2; + for (i = 0; i < length; i++) { + full_duty_value = 1 << lpg->pwm_config.pwm_size; + pwm_values[i] = pattern[i] * full_duty_value / 100; + + if (unlikely(pwm_values[i] > full_duty_value)) { + dev_err(lpg->chip->dev, "PWM value %d exceed the max %d\n", + pwm_values[i], full_duty_value); + rc = -EINVAL; + goto unlock; + } + + if (pwm_values[i] == full_duty_value) + pwm_values[i] = full_duty_value - 1; + + lsb = pwm_values[i] & 0xff; + msb = pwm_values[i] >> 8; + rc = qpnp_lut_write(lut, addr++, lsb); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write NO.%d LUT pattern LSB (%d) failed, rc=%d", + i, lsb, rc); + goto unlock; + } + + rc = qpnp_lut_masked_write(lut, addr++, + LPG_LUT_VALUE_MSB_MASK, msb); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write NO.%d LUT pattern MSB (%d) failed, rc=%d", + i, msb, rc); + goto unlock; + } + } + lpg->ramp_config.pattern_length = length; +unlock: + mutex_unlock(&lut->lock); + + return rc; +} + +static int qpnp_lpg_set_ramp_config(struct qpnp_lpg_channel *lpg) +{ + struct lpg_ramp_config *ramp = &lpg->ramp_config; + u8 lsb, msb, addr, mask, val; + int rc = 0; + + /* Set ramp step duration */ + lsb = ramp->step_ms & 0xff; + msb = ramp->step_ms >> 8; + addr = REG_LPG_RAMP_STEP_DURATION_LSB; + rc = qpnp_lpg_write(lpg, addr, lsb); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_LSB failed, rc=%d\n", + rc); + return rc; + } + rc = qpnp_lpg_write(lpg, addr + 1, msb); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_MSB failed, rc=%d\n", + rc); + return rc; + } + + /* Set hi_idx and lo_idx */ + rc = qpnp_lpg_masked_write(lpg, REG_LPG_HI_INDEX, + LPG_HI_LO_IDX_MASK, ramp->hi_idx); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write LPG_HI_IDX failed, rc=%d\n", + rc); + return rc; + } + + rc = qpnp_lpg_masked_write(lpg, REG_LPG_LO_INDEX, + LPG_HI_LO_IDX_MASK, ramp->lo_idx); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write LPG_LO_IDX failed, rc=%d\n", + rc); + return rc; + } + + /* Set pause_hi/lo_count */ + rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_HI_MULTIPLIER, + ramp->pause_hi_count); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write LPG_PAUSE_HI_MULTIPLIER failed, rc=%d\n", + rc); + return rc; + } + + rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_LO_MULTIPLIER, + ramp->pause_lo_count); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write LPG_PAUSE_LO_MULTIPLIER failed, rc=%d\n", + rc); + return rc; + } + + /* Set LPG_PATTERN_CONFIG */ + addr = REG_LPG_PATTERN_CONFIG; + mask = LPG_PATTERN_EN_PAUSE_LO | LPG_PATTERN_EN_PAUSE_HI + | LPG_PATTERN_RAMP_TOGGLE | LPG_PATTERN_REPEAT + | LPG_PATTERN_RAMP_LO_TO_HI; + val = 0; + if (ramp->pause_lo_count != 0) + val |= LPG_PATTERN_EN_PAUSE_LO; + if (ramp->pause_hi_count != 0) + val |= LPG_PATTERN_EN_PAUSE_HI; + if (ramp->ramp_dir_low_to_hi) + val |= LPG_PATTERN_RAMP_LO_TO_HI; + if (ramp->pattern_repeat) + val |= LPG_PATTERN_REPEAT; + if (ramp->toggle) + val |= LPG_PATTERN_RAMP_TOGGLE; + + rc = qpnp_lpg_masked_write(lpg, addr, mask, val); + if (rc < 0) { + dev_err(lpg->chip->dev, "Write LPG_PATTERN_CONFIG failed, rc=%d\n", + rc); + return rc; + } + + return rc; +} + static void __qpnp_lpg_calc_pwm_period(int period_ns, struct lpg_pwm_config *pwm_config) { @@ -397,17 +621,202 @@ static int qpnp_lpg_pwm_config(struct pwm_chip *pwm_chip, return -EINVAL; } - if (period_ns != lpg->current_period_ns) + if (period_ns != lpg->current_period_ns) { __qpnp_lpg_calc_pwm_period(period_ns, &lpg->pwm_config); + /* program LUT if PWM period is changed */ + if (lpg->src_sel == LUT_PATTERN) { + rc = qpnp_lpg_set_lut_pattern(lpg, + lpg->ramp_config.pattern, + lpg->ramp_config.pattern_length); + if (rc < 0) { + dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n", + lpg->lpg_idx, rc); + return rc; + } + lpg->lut_written = true; + } + } + if (period_ns != lpg->current_period_ns || duty_ns != lpg->current_duty_ns) __qpnp_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config); rc = qpnp_lpg_set_pwm_config(lpg); - if (rc < 0) + if (rc < 0) { dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n", lpg->lpg_idx, rc); + return rc; + } + + lpg->current_period_ns = period_ns; + lpg->current_duty_ns = duty_ns; + + return rc; +} + +static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en) +{ + struct qpnp_lpg_chip *chip = lpg->chip; + struct qpnp_lpg_lut *lut = chip->lut; + u8 mask, val; + int rc; + + mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT | + LPG_EN_RAMP_GEN_MASK; + val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT; + + if (lpg->src_sel == LUT_PATTERN) + val |= 1 << LPG_EN_RAMP_GEN_SHIFT; + + if (en) + val |= 1 << LPG_EN_LPG_OUT_SHIFT; + + rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val); + if (rc < 0) { + dev_err(chip->dev, "Write LPG_ENABLE_CONTROL failed, rc=%d\n", + rc); + return rc; + } + + if (lpg->src_sel == LUT_PATTERN && en) { + mutex_lock(&lut->lock); + val = 1 << lpg->lpg_idx; + rc = qpnp_lut_write(lut, REG_LPG_LUT_RAMP_CONTROL, val); + if (rc < 0) + dev_err(chip->dev, "Write LPG_LUT_RAMP_CONTROL failed, rc=%d\n", + rc); + mutex_unlock(&lut->lock); + } + + return rc; +} + +static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip, + struct pwm_device *pwm, enum pwm_output_type output_type) +{ + struct qpnp_lpg_channel *lpg; + enum lpg_src src_sel; + int rc; + + lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm); + if (lpg == NULL) { + dev_err(pwm_chip->dev, "lpg not found\n"); + return -ENODEV; + } + + if (lpg->chip->lut == NULL) { + pr_debug("lpg%d only support PWM mode\n", lpg->lpg_idx); + return 0; + } + + src_sel = (output_type == PWM_OUTPUT_MODULATED) ? + LUT_PATTERN : PWM_VALUE; + if (src_sel == lpg->src_sel) + return 0; + + if (src_sel == LUT_PATTERN) { + /* program LUT if it's never been programmed */ + if (!lpg->lut_written) { + rc = qpnp_lpg_set_lut_pattern(lpg, + lpg->ramp_config.pattern, + lpg->ramp_config.pattern_length); + if (rc < 0) { + dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n", + lpg->lpg_idx, rc); + return rc; + } + lpg->lut_written = true; + } + + rc = qpnp_lpg_set_ramp_config(lpg); + if (rc < 0) { + dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n", + lpg->lpg_idx, rc); + return rc; + } + } + + lpg->src_sel = src_sel; + + if (pwm_is_enabled(pwm)) { + rc = qpnp_lpg_pwm_src_enable(lpg, true); + if (rc < 0) { + dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n", + lpg->lpg_idx, rc); + return rc; + } + } + + return 0; +} + +static int qpnp_lpg_pwm_set_output_pattern(struct pwm_chip *pwm_chip, + struct pwm_device *pwm, struct pwm_output_pattern *output_pattern) +{ + struct qpnp_lpg_channel *lpg; + int rc = 0, i, period_ns, duty_ns; + u32 *percentages; + + lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm); + if (lpg == NULL) { + dev_err(pwm_chip->dev, "lpg not found\n"); + return -ENODEV; + } + + if (output_pattern->num_entries > lpg->max_pattern_length) { + dev_err(lpg->chip->dev, "pattern length %d shouldn't exceed %d\n", + output_pattern->num_entries, + lpg->max_pattern_length); + return -EINVAL; + } + + percentages = kcalloc(output_pattern->num_entries, + sizeof(u32), GFP_KERNEL); + if (!percentages) + return -ENOMEM; + + period_ns = pwm_get_period(pwm); + for (i = 0; i < output_pattern->num_entries; i++) { + duty_ns = output_pattern->duty_pattern[i]; + if (duty_ns > period_ns) { + dev_err(lpg->chip->dev, "duty %dns is larger than period %dns\n", + duty_ns, period_ns); + goto err; + } + /* Translate the pattern in duty_ns to percentage */ + if ((INT_MAX / duty_ns) < 100) + percentages[i] = duty_ns / (period_ns / 100); + else + percentages[i] = (duty_ns * 100) / period_ns; + } + + rc = qpnp_lpg_set_lut_pattern(lpg, percentages, + output_pattern->num_entries); + if (rc < 0) { + dev_err(lpg->chip->dev, "Set LUT pattern failed for LPG%d, rc=%d\n", + lpg->lpg_idx, rc); + goto err; + } + + lpg->lut_written = true; + memcpy(lpg->ramp_config.pattern, percentages, + output_pattern->num_entries); + lpg->ramp_config.hi_idx = lpg->ramp_config.lo_idx + + output_pattern->num_entries - 1; + if ((INT_MAX / period_ns) > output_pattern->cycles_per_duty) + lpg->ramp_config.step_ms = output_pattern->cycles_per_duty * + period_ns / NSEC_PER_MSEC; + else + lpg->ramp_config.step_ms = (period_ns / NSEC_PER_MSEC) * + output_pattern->cycles_per_duty; + + rc = qpnp_lpg_set_ramp_config(lpg); + if (rc < 0) + dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n", + lpg->lpg_idx, rc); +err: + kfree(percentages); return rc; } @@ -417,7 +826,6 @@ static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip, { struct qpnp_lpg_channel *lpg; int rc = 0; - u8 mask, val; lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm); if (lpg == NULL) { @@ -432,10 +840,7 @@ static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip, return rc; } - mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT; - val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT; - - rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val); + rc = qpnp_lpg_pwm_src_enable(lpg, true); if (rc < 0) dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n", lpg->lpg_idx, rc); @@ -448,7 +853,6 @@ static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip, { struct qpnp_lpg_channel *lpg; int rc; - u8 mask, val; lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm); if (lpg == NULL) { @@ -456,10 +860,7 @@ static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip, return; } - mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT; - val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT; - - rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val); + rc = qpnp_lpg_pwm_src_enable(lpg, false); if (rc < 0) { dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n", lpg->lpg_idx, rc); @@ -472,13 +873,32 @@ static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip, rc); } +static int qpnp_lpg_pwm_output_types_supported(struct pwm_chip *pwm_chip, + struct pwm_device *pwm) +{ + enum pwm_output_type type = PWM_OUTPUT_FIXED; + struct qpnp_lpg_channel *lpg; + + lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm); + if (lpg == NULL) { + dev_err(pwm_chip->dev, "lpg not found\n"); + return type; + } + + if (lpg->chip->lut != NULL) + type |= PWM_OUTPUT_MODULATED; + + return type; +} + #ifdef CONFIG_DEBUG_FS static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s) { struct qpnp_lpg_channel *lpg; struct lpg_pwm_config *cfg; + struct lpg_ramp_config *ramp; struct pwm_device *pwm; - int i; + int i, j; for (i = 0; i < pwm_chip->npwm; i++) { pwm = &pwm_chip->pwms[i]; @@ -513,12 +933,39 @@ static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s) seq_printf(s, " pwm_value = %d\n", cfg->pwm_value); seq_printf(s, " Requested period: %dns, best period = %dns\n", pwm_get_period(pwm), cfg->best_period_ns); + + ramp = &lpg->ramp_config; + if (pwm_get_output_type(pwm) == PWM_OUTPUT_MODULATED) { + seq_puts(s, " ramping duty percentages:"); + for (j = 0; j < ramp->pattern_length; j++) + seq_printf(s, " %d", ramp->pattern[j]); + seq_puts(s, "\n"); + seq_printf(s, " ramping time per step: %dms\n", + ramp->step_ms); + seq_printf(s, " ramping low index: %d\n", + ramp->lo_idx); + seq_printf(s, " ramping high index: %d\n", + ramp->hi_idx); + seq_printf(s, " ramping from low to high: %d\n", + ramp->ramp_dir_low_to_hi); + seq_printf(s, " ramping pattern repeat: %d\n", + ramp->pattern_repeat); + seq_printf(s, " ramping toggle: %d\n", + ramp->toggle); + seq_printf(s, " ramping pause count at low index: %d\n", + ramp->pause_lo_count); + seq_printf(s, " ramping pause count at high index: %d\n", + ramp->pause_hi_count); + } } } #endif static const struct pwm_ops qpnp_lpg_pwm_ops = { .config = qpnp_lpg_pwm_config, + .get_output_type_supported = qpnp_lpg_pwm_output_types_supported, + .set_output_type = qpnp_lpg_pwm_set_output_type, + .set_output_pattern = qpnp_lpg_pwm_set_output_pattern, .enable = qpnp_lpg_pwm_enable, .disable = qpnp_lpg_pwm_disable, #ifdef CONFIG_DEBUG_FS @@ -529,15 +976,19 @@ static const struct pwm_ops qpnp_lpg_pwm_ops = { static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip) { + struct device_node *child; + struct qpnp_lpg_channel *lpg; + struct lpg_ramp_config *ramp; int rc = 0, i; - u64 base, length; + u32 base, length, lpg_chan_id, tmp; const __be32 *addr; addr = of_get_address(chip->dev->of_node, 0, NULL, NULL); if (!addr) { - dev_err(chip->dev, "Getting address failed\n"); + dev_err(chip->dev, "Get %s address failed\n", LPG_BASE); return -EINVAL; } + base = be32_to_cpu(addr[0]); length = be32_to_cpu(addr[1]); @@ -551,7 +1002,7 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip) chip->lpgs[i].chip = chip; chip->lpgs[i].lpg_idx = i; chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG; - chip->lpgs[i].src_sel = PWM_OUTPUT; + chip->lpgs[i].src_sel = PWM_VALUE; rc = qpnp_lpg_read(&chip->lpgs[i], REG_LPG_PERPH_SUBTYPE, &chip->lpgs[i].subtype); if (rc < 0) { @@ -560,7 +1011,142 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip) } } - return rc; + addr = of_get_address(chip->dev->of_node, 1, NULL, NULL); + if (!addr) { + pr_debug("NO LUT address assigned\n"); + return 0; + } + + chip->lut = devm_kmalloc(chip->dev, sizeof(*chip->lut), GFP_KERNEL); + if (!chip->lut) + return -ENOMEM; + + chip->lut->chip = chip; + chip->lut->reg_base = be32_to_cpu(*addr); + mutex_init(&chip->lut->lock); + + rc = of_property_count_elems_of_size(chip->dev->of_node, + "qcom,lut-patterns", sizeof(u32)); + if (rc < 0) { + dev_err(chip->dev, "Read qcom,lut-patterns failed, rc=%d\n", + rc); + return rc; + } + + length = rc; + if (length > LPG_LUT_COUNT_MAX) { + dev_err(chip->dev, "qcom,lut-patterns length %d exceed max %d\n", + length, LPG_LUT_COUNT_MAX); + return -EINVAL; + } + + chip->lut->pattern = devm_kcalloc(chip->dev, LPG_LUT_COUNT_MAX, + sizeof(*chip->lut->pattern), GFP_KERNEL); + if (!chip->lut->pattern) + return -ENOMEM; + + rc = of_property_read_u32_array(chip->dev->of_node, "qcom,lut-patterns", + chip->lut->pattern, length); + if (rc < 0) { + dev_err(chip->dev, "Get qcom,lut-patterns failed, rc=%d\n", + rc); + return rc; + } + + if (of_get_available_child_count(chip->dev->of_node) == 0) { + dev_err(chip->dev, "No ramp configuration for any LPG\n"); + return -EINVAL; + } + + for_each_available_child_of_node(chip->dev->of_node, child) { + rc = of_property_read_u32(child, "qcom,lpg-chan-id", + &lpg_chan_id); + if (rc < 0) { + dev_err(chip->dev, "Get qcom,lpg-chan-id failed for node %s, rc=%d\n", + child->name, rc); + return rc; + } + + if (lpg_chan_id > chip->num_lpgs) { + dev_err(chip->dev, "lpg-chann-id %d is out of range 1~%d\n", + lpg_chan_id, chip->num_lpgs); + return -EINVAL; + } + + /* lpg channel id is indexed from 1 in hardware */ + lpg = &chip->lpgs[lpg_chan_id - 1]; + ramp = &lpg->ramp_config; + + rc = of_property_read_u32(child, "qcom,ramp-step-ms", &tmp); + if (rc < 0) { + dev_err(chip->dev, "get qcom,ramp-step-ms failed for lpg%d, rc=%d\n", + lpg_chan_id, rc); + return rc; + } + ramp->step_ms = (u16)tmp; + + rc = of_property_read_u32(child, "qcom,ramp-low-index", &tmp); + if (rc < 0) { + dev_err(chip->dev, "get qcom,ramp-low-index failed for lpg%d, rc=%d\n", + lpg_chan_id, rc); + return rc; + } + ramp->lo_idx = (u8)tmp; + if (ramp->lo_idx >= LPG_LUT_COUNT_MAX) { + dev_err(chip->dev, "qcom,ramp-low-index should less than max %d\n", + LPG_LUT_COUNT_MAX); + return -EINVAL; + } + + rc = of_property_read_u32(child, "qcom,ramp-high-index", &tmp); + if (rc < 0) { + dev_err(chip->dev, "get qcom,ramp-high-index failed for lpg%d, rc=%d\n", + lpg_chan_id, rc); + return rc; + } + ramp->hi_idx = (u8)tmp; + + if (ramp->hi_idx > LPG_LUT_COUNT_MAX) { + dev_err(chip->dev, "qcom,ramp-high-index shouldn't exceed max %d\n", + LPG_LUT_COUNT_MAX); + return -EINVAL; + } + + if (ramp->hi_idx <= ramp->lo_idx) { + dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d)\n", + ramp->hi_idx, ramp->lo_idx); + return -EINVAL; + } + + ramp->pattern_length = ramp->hi_idx - ramp->lo_idx + 1; + ramp->pattern = &chip->lut->pattern[ramp->lo_idx]; + lpg->max_pattern_length = ramp->pattern_length; + + rc = of_property_read_u32(child, + "qcom,ramp-pause-hi-count", &tmp); + if (rc < 0) + ramp->pause_hi_count = 0; + else + ramp->pause_hi_count = (u8)tmp; + + rc = of_property_read_u32(child, + "qcom,ramp-pause-lo-count", &tmp); + if (rc < 0) + ramp->pause_lo_count = 0; + else + ramp->pause_lo_count = (u8)tmp; + + ramp->ramp_dir_low_to_hi = of_property_read_bool(child, + "qcom,ramp-from-low-to-high"); + + ramp->pattern_repeat = of_property_read_bool(child, + "qcom,ramp-pattern-repeat"); + + ramp->toggle = of_property_read_bool(child, + "qcom,ramp-toggle"); + } + + return 0; } static int qpnp_lpg_probe(struct platform_device *pdev) @@ -584,7 +1170,7 @@ static int qpnp_lpg_probe(struct platform_device *pdev) if (rc < 0) { dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n", rc); - goto destroy; + goto err_out; } dev_set_drvdata(chip->dev, chip); @@ -596,11 +1182,11 @@ static int qpnp_lpg_probe(struct platform_device *pdev) rc = pwmchip_add(&chip->pwm_chip); if (rc < 0) { dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc); - goto destroy; + goto err_out; } return 0; -destroy: +err_out: mutex_destroy(&chip->bus_lock); return rc; } diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 1c85ecc9e7ac076e7ad6e13333a1a6f4b15b07b6..0fcf94ffad321d8bb10911c6cdb65eebd8c8161b 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -156,8 +156,12 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (div < 0) return div; - /* Let the core driver set pwm->period if disabled and duty_ns == 0 */ - if (!pwm_is_enabled(pwm) && !duty_ns) + /* + * Let the core driver set pwm->period if disabled and duty_ns == 0. + * But, this driver should prevent to set the new duty_ns if current + * duty_cycle is not set + */ + if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle) return 0; rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index a813239300c3d46bba1320caac77ec7adc8363a8..ea2b53d171991c100104f05f56f7fc74a4563f1e 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -223,11 +223,60 @@ static ssize_t capture_show(struct device *child, return sprintf(buf, "%u %u\n", result.period, result.duty_cycle); } +static ssize_t output_type_show(struct device *child, + struct device_attribute *attr, + char *buf) +{ + const struct pwm_device *pwm = child_to_pwm_device(child); + const char *output_type = "unknown"; + struct pwm_state state; + + pwm_get_state(pwm, &state); + switch (state.output_type) { + case PWM_OUTPUT_FIXED: + output_type = "fixed"; + break; + case PWM_OUTPUT_MODULATED: + output_type = "modulated"; + break; + default: + break; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", output_type); +} + +static ssize_t output_type_store(struct device *child, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct pwm_export *export = child_to_pwm_export(child); + struct pwm_device *pwm = export->pwm; + struct pwm_state state; + int ret = -EINVAL; + + mutex_lock(&export->lock); + pwm_get_state(pwm, &state); + if (sysfs_streq(buf, "fixed")) + state.output_type = PWM_OUTPUT_FIXED; + else if (sysfs_streq(buf, "modulated")) + state.output_type = PWM_OUTPUT_MODULATED; + else + goto unlock; + + ret = pwm_apply_state(pwm, &state); +unlock: + mutex_unlock(&export->lock); + + return ret ? : size; +} + static DEVICE_ATTR_RW(period); static DEVICE_ATTR_RW(duty_cycle); static DEVICE_ATTR_RW(enable); static DEVICE_ATTR_RW(polarity); static DEVICE_ATTR_RO(capture); +static DEVICE_ATTR_RW(output_type); static struct attribute *pwm_attrs[] = { &dev_attr_period.attr, @@ -235,6 +284,7 @@ static struct attribute *pwm_attrs[] = { &dev_attr_enable.attr, &dev_attr_polarity.attr, &dev_attr_capture.attr, + &dev_attr_output_type.attr, NULL }; ATTRIBUTE_GROUPS(pwm); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 65f9b7c04d1a0c4443be484632262e8454aa0b83..e1ed479d1a01d05385e68350a177146098c22b6e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2661,6 +2661,40 @@ int regulator_list_hardware_vsel(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_list_hardware_vsel); +/** + * regulator_list_corner_voltage - return the maximum voltage in microvolts that + * can be physically configured for the regulator when operating at the + * specified voltage corner + * @regulator: regulator source + * @corner: voltage corner value + * Context: can sleep + * + * This function can be used for regulators which allow scaling between + * different voltage corners as opposed to be different absolute voltages. The + * absolute voltage for a given corner may vary part-to-part or for a given part + * at runtime based upon various factors. + * + * Returns a voltage corresponding to the specified voltage corner or a negative + * errno if the corner value can't be used on this system. + */ +int regulator_list_corner_voltage(struct regulator *regulator, int corner) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + if (corner < rdev->constraints->min_uV || + corner > rdev->constraints->max_uV || + !rdev->desc->ops->list_corner_voltage) + return -EINVAL; + + mutex_lock(&rdev->mutex); + ret = rdev->desc->ops->list_corner_voltage(rdev, corner); + mutex_unlock(&rdev->mutex); + + return ret; +} +EXPORT_SYMBOL(regulator_list_corner_voltage); + /** * regulator_get_linear_step - return the voltage step size between VSEL values * @regulator: regulator source diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c index d263193ab8051e5db70b7aae332cb76ec79b6645..31a202ec100884a893a2e42352108016d9ff8cb3 100644 --- a/drivers/regulator/qpnp-lcdb-regulator.c +++ b/drivers/regulator/qpnp-lcdb-regulator.c @@ -991,19 +991,21 @@ static irqreturn_t qpnp_lcdb_sc_irq_handler(int irq, void *data) } #define MIN_BST_VOLTAGE_MV 4700 -#define MAX_BST_VOLTAGE_MV 6250 +#define PM660_MAX_BST_VOLTAGE_MV 6250 +#define MAX_BST_VOLTAGE_MV 6275 #define MIN_VOLTAGE_MV 4000 #define MAX_VOLTAGE_MV 6000 #define VOLTAGE_MIN_STEP_100_MV 4000 #define VOLTAGE_MIN_STEP_50_MV 4950 #define VOLTAGE_STEP_100_MV 100 #define VOLTAGE_STEP_50_MV 50 +#define VOLTAGE_STEP_25_MV 25 #define VOLTAGE_STEP_50MV_OFFSET 0xA static int qpnp_lcdb_set_bst_voltage(struct qpnp_lcdb *lcdb, int voltage_mv, u8 type) { int rc = 0; - u8 val, mask = 0; + u8 val, voltage_step, mask = 0; int bst_voltage_mv; u16 pmic_subtype = lcdb->pmic_rev_id->pmic_subtype; struct ldo_regulator *ldo = &lcdb->ldo; @@ -1016,14 +1018,26 @@ static int qpnp_lcdb_set_bst_voltage(struct qpnp_lcdb *lcdb, if (bst_voltage_mv < MIN_BST_VOLTAGE_MV) bst_voltage_mv = MIN_BST_VOLTAGE_MV; - else if (bst_voltage_mv > MAX_BST_VOLTAGE_MV) - bst_voltage_mv = MAX_BST_VOLTAGE_MV; + + if (pmic_subtype == PM660L_SUBTYPE) { + if (bst_voltage_mv > PM660_MAX_BST_VOLTAGE_MV) + bst_voltage_mv = PM660_MAX_BST_VOLTAGE_MV; + } else { + if (bst_voltage_mv > MAX_BST_VOLTAGE_MV) + bst_voltage_mv = MAX_BST_VOLTAGE_MV; + } if (bst_voltage_mv != bst->voltage_mv) { + if (pmic_subtype == PM660L_SUBTYPE) { + mask = PM660_BST_OUTPUT_VOLTAGE_MASK; + voltage_step = VOLTAGE_STEP_50_MV; + } else { + mask = BST_OUTPUT_VOLTAGE_MASK; + voltage_step = VOLTAGE_STEP_25_MV; + } + val = DIV_ROUND_UP(bst_voltage_mv - MIN_BST_VOLTAGE_MV, - VOLTAGE_STEP_50_MV); - mask = (pmic_subtype == PM660L_SUBTYPE) ? - PM660_BST_OUTPUT_VOLTAGE_MASK : BST_OUTPUT_VOLTAGE_MASK; + voltage_step); rc = qpnp_lcdb_masked_write(lcdb, lcdb->base + LCDB_BST_OUTPUT_VOLTAGE_REG, mask, val); @@ -1044,7 +1058,7 @@ static int qpnp_lcdb_get_bst_voltage(struct qpnp_lcdb *lcdb, int *voltage_mv) { int rc; - u8 val, mask = 0; + u8 val, voltage_step, mask = 0; u16 pmic_subtype = lcdb->pmic_rev_id->pmic_subtype; rc = qpnp_lcdb_read(lcdb, lcdb->base + LCDB_BST_OUTPUT_VOLTAGE_REG, @@ -1054,10 +1068,16 @@ static int qpnp_lcdb_get_bst_voltage(struct qpnp_lcdb *lcdb, return rc; } - mask = (pmic_subtype == PM660L_SUBTYPE) ? - PM660_BST_OUTPUT_VOLTAGE_MASK : BST_OUTPUT_VOLTAGE_MASK; + if (pmic_subtype == PM660L_SUBTYPE) { + mask = PM660_BST_OUTPUT_VOLTAGE_MASK; + voltage_step = VOLTAGE_STEP_50_MV; + } else { + mask = BST_OUTPUT_VOLTAGE_MASK; + voltage_step = VOLTAGE_STEP_25_MV; + } + val &= mask; - *voltage_mv = (val * VOLTAGE_STEP_50_MV) + MIN_BST_VOLTAGE_MV; + *voltage_mv = (val * voltage_step) + MIN_BST_VOLTAGE_MV; return 0; } @@ -1870,6 +1890,8 @@ static int qpnp_lcdb_init_bst(struct qpnp_lcdb *lcdb) return rc; } lcdb->bst.soft_start_us = (val & SOFT_START_MASK) * 200 + 200; + if (!lcdb->bst.headroom_mv) + lcdb->bst.headroom_mv = PM660_BST_HEADROOM_DEFAULT_MV; } else { rc = qpnp_lcdb_read(lcdb, lcdb->base + LCDB_BST_SS_CTL_REG, &val, 1); @@ -1878,6 +1900,8 @@ static int qpnp_lcdb_init_bst(struct qpnp_lcdb *lcdb) return rc; } lcdb->bst.soft_start_us = soft_start_us[val & SOFT_START_MASK]; + if (!lcdb->bst.headroom_mv) + lcdb->bst.headroom_mv = BST_HEADROOM_DEFAULT_MV; } return 0; diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index cb27c4ef6a333ac66a32d9baefabe40e3fb867d9..a3b621d37ff55b1cdd052cf6ba8bfa7674cee042 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -50,6 +50,15 @@ config RPMSG_QCOM_GLINK_SPSS region with the remote proc by writing the smem descriptor location and size into shared registers. +config RPMSG_QCOM_GLINK_SPI + tristate "Qualcomm SPI Glink driver" + help + Say y here to enable support for the GLINK SPI communication driver, + which provides support for using the GLINK communication protocol + over SPI. This transport performs marshaling of GLINK commands and + data to the appropriate SPI bus wire format and allows for GLINK + communication with remote subsystems that are external to the SoC. + config RPMSG_QCOM_SMD tristate "Qualcomm Shared Memory Driver (SMD)" depends on QCOM_SMEM diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 58a46fd77a2226d18296ef764422e6b19574bab2..1680763bb8b5580a89301e398ef105f6b2f47671 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SPSS) += qcom_glink_spss.o +obj-$(CONFIG_RPMSG_QCOM_GLINK_SPI) += qcom_glink_spi.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 86863eaf0237d14f9e00ff3c71cef477efb38c33..725d6d00746473f087377f4650eb7e94b11bca2d 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -317,6 +317,15 @@ static void qcom_glink_tx_write(struct qcom_glink *glink, glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen); } +static void qcom_glink_pipe_reset(struct qcom_glink *glink) +{ + if (glink->tx_pipe->reset) + glink->tx_pipe->reset(glink->tx_pipe); + + if (glink->rx_pipe->reset) + glink->rx_pipe->reset(glink->rx_pipe); +} + static int qcom_glink_tx(struct qcom_glink *glink, const void *hdr, size_t hlen, const void *data, size_t dlen, bool wait) @@ -450,7 +459,7 @@ static int qcom_glink_send_open_req(struct qcom_glink *glink, req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN); req.msg.param1 = cpu_to_le16(channel->lcid); req.msg.param2 = cpu_to_le32(name_len); - strcpy(req.name, channel->name); + strlcpy(req.name, channel->name, GLINK_NAME_SIZE); ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true); if (ret) @@ -1549,7 +1558,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, } rpdev->ept = &channel->ept; - strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE); + strlcpy(rpdev->id.name, name, RPMSG_NAME_SIZE); rpdev->src = RPMSG_ADDR_ANY; rpdev->dst = RPMSG_ADDR_ANY; rpdev->ops = &glink_device_ops; @@ -1604,7 +1613,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) cancel_work_sync(&channel->intent_work); if (channel->rpdev) { - strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + strlcpy(chinfo.name, channel->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; @@ -1845,9 +1854,15 @@ void qcom_glink_native_remove(struct qcom_glink *glink) idr_for_each_entry(&glink->lcids, channel, cid) kref_put(&channel->refcount, qcom_glink_channel_release); + /* Release any defunct local channels, waiting for close-req */ + idr_for_each_entry(&glink->lcids, channel, cid) + kref_put(&channel->refcount, qcom_glink_channel_release); + idr_destroy(&glink->lcids); idr_destroy(&glink->rcids); spin_unlock_irqrestore(&glink->idr_lock, flags); + + qcom_glink_pipe_reset(glink); mbox_free_channel(glink->mbox_chan); } EXPORT_SYMBOL_GPL(qcom_glink_native_remove); diff --git a/drivers/rpmsg/qcom_glink_native.h b/drivers/rpmsg/qcom_glink_native.h index 0cae8a8199f86938ccb92089b9f3e8f7e351e1d3..32f1b57868cc65f94ad0aad9d7b1ef4f57923175 100644 --- a/drivers/rpmsg/qcom_glink_native.h +++ b/drivers/rpmsg/qcom_glink_native.h @@ -30,6 +30,8 @@ struct qcom_glink_pipe { void (*write)(struct qcom_glink_pipe *glink_pipe, const void *hdr, size_t hlen, const void *data, size_t dlen); + + void (*reset)(struct qcom_glink_pipe *glink_pipe); }; struct qcom_glink; diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c index b776adc6702d4bfd238f7a74ade8c10cfe9e4a5a..a914914df4f5845227c64ffb9e12e81b66abde39 100644 --- a/drivers/rpmsg/qcom_glink_smem.c +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -116,7 +116,7 @@ static void glink_smem_rx_advance(struct qcom_glink_pipe *np, tail = le32_to_cpu(*pipe->tail); tail += count; - if (tail > pipe->native.length) + if (tail >= pipe->native.length) tail -= pipe->native.length; *pipe->tail = cpu_to_le32(tail); diff --git a/drivers/rpmsg/qcom_glink_spi.c b/drivers/rpmsg/qcom_glink_spi.c new file mode 100644 index 0000000000000000000000000000000000000000..a3ef2642ff11e249814827fb562e2e937fc2d88d --- /dev/null +++ b/drivers/rpmsg/qcom_glink_spi.c @@ -0,0 +1,2473 @@ +/* Copyright (c) 2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rpmsg_internal.h" +#include "qcom_glink_native.h" + +#define GLINK_LOG_PAGE_CNT 2 +#define GLINK_INFO(ctxt, x, ...) \ +do { \ + if (ctxt->ilc) \ + ipc_log_string(ctxt->ilc, "[%s]: "x, __func__, ##__VA_ARGS__); \ +} while (0) + +#define CH_INFO(ch, x, ...) \ +do { \ + if (ch->glink && ch->glink->ilc) \ + ipc_log_string(ch->glink->ilc, "%s[%d:%d] %s: "x, ch->name, \ + ch->lcid, ch->rcid, __func__, ##__VA_ARGS__); \ +} while (0) + + +#define GLINK_ERR(ctxt, x, ...) \ +do { \ + pr_err_ratelimited("[%s]: "x, __func__, ##__VA_ARGS__); \ + if (ctxt->ilc) \ + ipc_log_string(ctxt->ilc, "[%s]: "x, __func__, ##__VA_ARGS__); \ +} while (0) + +#define SPI_ALIGNMENT 16 +#define FIFO_FULL_RESERVE 8 +#define TX_BLOCKED_CMD_RESERVE 16 +#define DEFAULT_FIFO_SIZE 1024 +#define SHORT_PKT_SIZE 16 +#define XPRT_ALIGNMENT 4 + +#define MAX_INACTIVE_CYCLES 50 +#define POLL_INTERVAL_US 500 + +#define ACTIVE_TX BIT(0) +#define ACTIVE_RX BIT(1) + +#define ID_MASK 0xFFFFFF + +#define GLINK_NAME_SIZE 32 +#define GLINK_VERSION_1 1 + +#define SPI_GLINK_CID_MIN 1 +#define SPI_GLINK_CID_MAX 65536 + +struct glink_msg { + __le16 cmd; + __le16 param1; + __le32 param2; + __le32 param3; + __le32 param4; + u8 data[]; +} __packed; + +/** + * struct glink_defer_cmd - deferred incoming control message + * @node: list node + * @msg: message header + * data: payload of the message + * + * Copy of a received control message, to be added to @rx_queue and processed + * by @rx_work of @glink_spi. + */ +struct glink_defer_cmd { + struct list_head node; + + struct glink_msg msg; + u8 data[]; +}; + +/** + * struct glink_core_rx_intent - RX intent + * RX intent + * + * @data: pointer to the data (may be NULL for zero-copy) + * @id: remote or local intent ID + * @size: size of the original intent (do not modify) + * @addr: addr to read/write the data from + * @reuse: To mark if the intent can be reused after first use + * @in_use: To mark if intent is already in use for the channel + * @offset: next write offset (initially 0) + */ +struct glink_core_rx_intent { + void *data; + u32 id; + size_t size; + u32 addr; + bool reuse; + bool in_use; + u32 offset; + + struct list_head node; +}; + +/** + * @fifo_base: Base Address of the RX FIFO. + * @length: End Address of the RX FIFO. + * @tail_addr: Address of the TX FIFO Read Index Register. + * @head_addr: Address of the TX FIFO Write Index Register. + * @local_addr: Address of the RX FIFO Read Index Register. + */ +struct glink_spi_pipe { + u32 fifo_base; + u32 length; + + u32 tail_addr; + u32 head_addr; + + u32 local_addr; +}; + +/** + * struct glink_cmpnt - Component to cache spi component and its operations + * @master_dev: Device structure corresponding to spi device. + * @master_ops: Operations supported by the spi device. + */ +struct glink_cmpnt { + struct device *master_dev; + struct wdsp_mgr_ops *master_ops; +}; + +/** + * struct glink_spi - driver context, relates to one remote subsystem + * @dev: reference to the associated struct device + * @name: name of this edge + * @rx_pipe: pipe object for receive FIFO + * @tx_pipe: pipe object for transmit FIFO + * @rx_work: worker for handling received control messages + * @rx_worker: worker struct for handling received control messages + * @rx_task: task that runs the rx_worker + * @rx_lock: protects the @rx_queue + * @rx_queue: queue of received control messages to be processed in @rx_work + * @tx_lock: synchronizes operations on the tx fifo + * @idr_lock: synchronizes @lcids and @rcids modifications + * @lcids: idr of all channels with a known local channel id + * @rcids: idr of all channels with a known remote channel id + * @spi_ops: spi ops for sending data to the remote + * @cmpnt: component to be registered with the wdsp component manager + * @in_reset indicates that remote processor is in reset + * @ilc: ipc logging context reference + */ +struct glink_spi { + struct device dev; + + const char *name; + + struct glink_spi_pipe rx_pipe; + struct glink_spi_pipe tx_pipe; + + struct kthread_work rx_work; + struct kthread_worker rx_worker; + struct task_struct *rx_task; + + spinlock_t rx_lock; + struct list_head rx_queue; + struct work_struct rx_defer_work; + + struct mutex tx_lock; + + spinlock_t idr_lock; + struct idr lcids; + struct idr rcids; + u32 features; + + bool intentless; + + struct wcd_spi_ops spi_ops; + struct glink_cmpnt cmpnt; + u32 activity_flag; + spinlock_t activity_lock; + bool in_reset; + + void *ilc; +}; + +enum { + GLINK_STATE_CLOSED, + GLINK_STATE_OPENING, + GLINK_STATE_OPEN, + GLINK_STATE_CLOSING, +}; + +/** + * struct glink_channel - internal representation of a channel + * @rpdev: rpdev reference, only used for primary endpoints + * @ept: rpmsg endpoint this channel is associated with + * @glink: glink_spi context handle + * @refcount: refcount for the channel object + * @recv_lock: guard for @ept.cb + * @name: unique channel name/identifier + * @lcid: channel id, in local space + * @rcid: channel id, in remote space + * @intent_lock: lock for protection of @liids, @riids + * @liids: idr of all local intents + * @riids: idr of all remote intents + * @intent_work: worker responsible for transmitting rx_done packets + * @done_intents: list of intents that needs to be announced rx_done + * @buf: receive buffer, for gathering fragments + * @buf_offset: write offset in @buf + * @buf_size: size of current @buf + * @open_ack: completed once remote has acked the open-request + * @open_req: completed once open-request has been received + * @intent_req_lock: Synchronises multiple intent requests + * @intent_req_result: Result of intent request + * @intent_req_comp: Completion for intent_req signalling + */ +struct glink_channel { + struct rpmsg_endpoint ept; + + struct rpmsg_device *rpdev; + struct glink_spi *glink; + + struct kref refcount; + + spinlock_t recv_lock; + + char *name; + unsigned int lcid; + unsigned int rcid; + + spinlock_t intent_lock; + struct idr liids; + struct idr riids; + struct work_struct intent_work; + struct list_head done_intents; + + struct glink_core_rx_intent *buf; + int buf_offset; + int buf_size; + + unsigned int lsigs; + unsigned int rsigs; + + struct completion open_ack; + struct completion open_req; + + struct mutex intent_req_lock; + bool intent_req_result; + struct completion intent_req_comp; +}; + +#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) + +static const struct rpmsg_endpoint_ops glink_endpoint_ops; + +#define SPI_CMD_VERSION 0 +#define SPI_CMD_VERSION_ACK 1 +#define SPI_CMD_OPEN 2 +#define SPI_CMD_CLOSE 3 +#define SPI_CMD_OPEN_ACK 4 +#define SPI_CMD_CLOSE_ACK 5 +#define SPI_CMD_INTENT 6 +#define SPI_CMD_RX_DONE 7 +#define SPI_CMD_RX_DONE_W_REUSE 8 +#define SPI_CMD_RX_INTENT_REQ 9 +#define SPI_CMD_RX_INTENT_REQ_ACK 10 +#define SPI_CMD_TX_DATA 11 +#define SPI_CMD_TX_DATA_CONT 12 +#define SPI_CMD_READ_NOTIF 13 +#define SPI_CMD_SIGNALS 14 +#define SPI_CMD_TX_SHORT_DATA 17 + +static void glink_spi_rx_done_work(struct work_struct *work); +static void glink_spi_remove(struct glink_spi *glink); + +/** + * spi_suspend() - Vote for the spi device suspend + * @cmpnt: Component to identify the spi device. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +static int spi_suspend(struct glink_cmpnt *cmpnt) +{ + if (!cmpnt || !cmpnt->master_dev || !cmpnt->master_ops || + !cmpnt->master_ops->suspend) + return 0; + + return cmpnt->master_ops->suspend(cmpnt->master_dev); +} + +/** + * spi_resume() - Vote for the spi device resume + * @cmpnt: Component to identify the spi device. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +static int spi_resume(struct glink_cmpnt *cmpnt) +{ + if (!cmpnt || !cmpnt->master_dev || !cmpnt->master_ops || + !cmpnt->master_ops->resume) + return 0; + + return cmpnt->master_ops->resume(cmpnt->master_dev); +} + +/** + * glink_spi_xprt_set_poll_mode() - Set the transport to polling mode + * @glink: Edge information corresponding to the transport. + * + * This helper function indicates the start of RX polling. This will + * prevent the system from suspending and keeps polling for RX for a + * pre-defined duration. + */ +static void glink_spi_xprt_set_poll_mode(struct glink_spi *glink) +{ + unsigned long flags; + + spin_lock_irqsave(&glink->activity_lock, flags); + glink->activity_flag |= ACTIVE_RX; + spin_unlock_irqrestore(&glink->activity_lock, flags); + + spi_resume(&glink->cmpnt); +} + +/** + * glink_spi_xprt_set_irq_mode() - Set the transport to IRQ mode + * @glink: Edge information corresponding to the transport. + * + * This helper indicates the end of RX polling. This will allow the + * system to suspend and new RX data can be handled only through an IRQ. + */ +static void glink_spi_xprt_set_irq_mode(struct glink_spi *glink) +{ + unsigned long flags; + + spin_lock_irqsave(&glink->activity_lock, flags); + glink->activity_flag &= ~ACTIVE_RX; + spin_unlock_irqrestore(&glink->activity_lock, flags); + + spi_suspend(&glink->cmpnt); +} + +static struct glink_channel *glink_spi_alloc_channel(struct glink_spi *glink, + const char *name) +{ + struct glink_channel *channel; + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return ERR_PTR(-ENOMEM); + + /* Setup glink internal glink_channel data */ + spin_lock_init(&channel->recv_lock); + spin_lock_init(&channel->intent_lock); + mutex_init(&channel->intent_req_lock); + + channel->glink = glink; + channel->name = kstrdup(name, GFP_KERNEL); + + init_completion(&channel->open_req); + init_completion(&channel->open_ack); + init_completion(&channel->intent_req_comp); + + INIT_LIST_HEAD(&channel->done_intents); + INIT_WORK(&channel->intent_work, glink_spi_rx_done_work); + + idr_init(&channel->liids); + idr_init(&channel->riids); + kref_init(&channel->refcount); + + return channel; +} + +static void glink_spi_channel_release(struct kref *ref) +{ + struct glink_channel *channel = container_of(ref, struct glink_channel, + refcount); + unsigned long flags; + + CH_INFO(channel, "\n"); + spin_lock_irqsave(&channel->intent_lock, flags); + idr_destroy(&channel->liids); + idr_destroy(&channel->riids); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + kfree(channel->name); + kfree(channel); +} + +/** + * glink_spi_read() - Receive data over SPI bus + * @glink: Edge from which the data has to be received. + * @src: Source Address of the RX data. + * @dst: Address of the destination RX buffer. + * @size: Size of the RX data. + * + * This function is used to receive data or command as a byte stream from + * the remote subsystem over the SPI bus. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +static int glink_spi_read(struct glink_spi *glink, u32 src, void *dst, + size_t size) +{ + struct wcd_spi_msg spi_msg = { 0 }; + + if (unlikely(!glink->spi_ops.read_dev)) + return -EINVAL; + + spi_msg.data = dst; + spi_msg.remote_addr = src; + spi_msg.len = size; + return glink->spi_ops.read_dev(glink->spi_ops.spi_dev, &spi_msg); +} + +/** + * glink_spi_write() - Transmit data over SPI bus + * @glink: Edge from which the data has to be received. + * @src: Address of the TX buffer. + * @dst: Destination Address of the TX Data. + * @size: Size of the TX data. + * + * This function is used to transmit data or command as a byte stream to + * the remote subsystem over the SPI bus. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +static int glink_spi_write(struct glink_spi *glink, void *src, u32 dst, + size_t size) +{ + struct wcd_spi_msg spi_msg = { 0 }; + + if (unlikely(!glink->spi_ops.write_dev)) + return -EINVAL; + + spi_msg.data = src; + spi_msg.remote_addr = dst; + spi_msg.len = size; + return glink->spi_ops.write_dev(glink->spi_ops.spi_dev, &spi_msg); +} + +/** + * glink_spi_reg_read() - Read the TX/RX FIFO Read/Write Index registers + * @glink: Edge from which the registers have to be read. + * @reg_addr: Address of the register to be read. + * @data: Buffer into which the register data has to be read. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +static int glink_spi_reg_read(struct glink_spi *glink, u32 reg_addr, u32 *data) +{ + int ret; + + ret = glink_spi_read(glink, reg_addr, data, sizeof(*data)); + if (ret) + return ret; + + /* SPI register reads need to be masked */ + *data = *data & ID_MASK; + return 0; +} + +/** + * glink_spi_reg_write() - Write the TX/RX FIFO Read/Write Index registers + * @glink: Edge to which the registers have to be written. + * @reg_addr: Address of the registers to be written. + * @data: Data to be written to the registers. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +static int glink_spi_reg_write(struct glink_spi *glink, u32 reg_addr, u32 data) +{ + return glink_spi_write(glink, &data, reg_addr, sizeof(data)); +} + +static size_t glink_spi_rx_avail(struct glink_spi *glink) +{ + struct glink_spi_pipe *pipe = &glink->rx_pipe; + u32 head; + u32 tail; + int ret; + + if (unlikely(glink->in_reset)) + return 0; + + if (unlikely(!pipe->fifo_base)) { + ret = glink_spi_reg_read(glink, pipe->tail_addr, + &pipe->local_addr); + if (ret < 0) { + GLINK_ERR(glink, "Error %d reading rx tail\n", ret); + return 0; + } + pipe->fifo_base = pipe->local_addr; + } + + tail = pipe->local_addr; + ret = glink_spi_reg_read(glink, pipe->head_addr, &head); + if (ret < 0) { + GLINK_ERR(glink, "Error %d reading rx head\n", ret); + return 0; + } + + if (head < tail) + return pipe->length - (tail - head); + else + return head - tail; +} + +static void glink_spi_rx_peak(struct glink_spi *glink, + void *data, unsigned int offset, size_t count) +{ + struct glink_spi_pipe *pipe = &glink->rx_pipe; + u32 fifo_end; + size_t len; + u32 tail; + + fifo_end = pipe->fifo_base + pipe->length; + tail = pipe->local_addr; + tail += offset; + if (tail >= fifo_end) + tail -= pipe->length; + + len = min_t(size_t, count, fifo_end - tail); + if (len) + glink_spi_read(glink, tail, data, len); + + if (len != count) + glink_spi_read(glink, pipe->fifo_base, data + len, count - len); +} + +static void glink_spi_rx_advance(struct glink_spi *glink, size_t count) +{ + struct glink_spi_pipe *pipe = &glink->rx_pipe; + u32 tail; + int ret; + + tail = pipe->local_addr; + tail += count; + + if (tail > pipe->fifo_base + pipe->length) + tail -= pipe->length; + + pipe->local_addr = tail; + ret = glink_spi_reg_write(glink, pipe->tail_addr, tail); + if (ret) + GLINK_ERR(glink, "Error writing rx tail\n", ret); +} + +static size_t glink_spi_tx_avail(struct glink_spi *glink) +{ + struct glink_spi_pipe *pipe = &glink->tx_pipe; + u32 avail; + u32 head; + u32 tail; + int ret; + + if (unlikely(glink->in_reset)) + return 0; + + if (unlikely(!pipe->fifo_base)) { + ret = glink_spi_reg_read(glink, pipe->head_addr, + &pipe->local_addr); + if (ret < 0) { + GLINK_ERR(glink, "Error %d reading tx head\n", ret); + return 0; + } + pipe->fifo_base = pipe->local_addr; + } + + head = pipe->local_addr; + ret = glink_spi_reg_read(glink, pipe->tail_addr, &tail); + if (ret < 0) { + GLINK_ERR(glink, "Error %d reading tx tail\n", ret); + return 0; + } + + if (tail <= head) + avail = pipe->fifo_base + pipe->length - head + tail; + else + avail = tail - head; + + if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE)) + avail = 0; + else + avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE; + + return avail; +} + +static unsigned int glink_spi_tx_write_one(struct glink_spi *glink, u32 head, + void *data, size_t count) +{ + struct glink_spi_pipe *pipe = &glink->tx_pipe; + size_t len; + int ret; + + len = min_t(size_t, count, pipe->fifo_base + pipe->length - head); + if (len) { + ret = glink_spi_write(glink, data, head, len); + if (ret) + GLINK_ERR(glink, "Error %d writing tx data\n", ret); + } + + if (len != count) { + ret = glink_spi_write(glink, data + len, pipe->fifo_base, + count - len); + if (ret) + GLINK_ERR(glink, "Error %d writing tx data\n", ret); + } + + head += count; + if (head >= pipe->fifo_base + pipe->length) + head -= pipe->length; + + return head; +} + +static void glink_spi_tx_write(struct glink_spi *glink, void *hdr, size_t hlen, + void *data, size_t dlen) +{ + struct glink_spi_pipe *pipe = &glink->tx_pipe; + u32 head; + int ret; + + head = pipe->local_addr; + + if (hlen) + head = glink_spi_tx_write_one(glink, head, hdr, hlen); + if (dlen) + head = glink_spi_tx_write_one(glink, head, data, dlen); + + /* Ensure head is always aligned to 8 bytes */ + head = ALIGN(head, SPI_ALIGNMENT); + if (head >= pipe->fifo_base + pipe->length) + head -= pipe->length; + + pipe->local_addr = head; + ret = glink_spi_reg_write(glink, pipe->head_addr, head); + if (ret) + GLINK_ERR(glink, "Error %d writing tx head\n", ret); + +} + +static int glink_spi_tx(struct glink_spi *glink, void *hdr, size_t hlen, + void *data, size_t dlen, bool wait) +{ + unsigned int tlen = hlen + dlen; + int ret = 0; + + if (tlen >= glink->tx_pipe.length) + return -EINVAL; + + mutex_lock(&glink->tx_lock); + + while (glink_spi_tx_avail(glink) < tlen) { + if (!wait) { + ret = -EAGAIN; + goto out; + } + + if (unlikely(glink->in_reset)) { + ret = -ENXIO; + goto out; + } + + /* Wait without holding the tx_lock */ + mutex_unlock(&glink->tx_lock); + + usleep_range(10000, 15000); + + mutex_lock(&glink->tx_lock); + } + + glink_spi_tx_write(glink, hdr, hlen, data, dlen); + +out: + mutex_unlock(&glink->tx_lock); + + + return ret; +} + +static int glink_spi_send_version(struct glink_spi *glink) +{ + struct glink_msg msg = { 0 }; + + msg.cmd = cpu_to_le16(SPI_CMD_VERSION); + msg.param1 = cpu_to_le16(GLINK_VERSION_1); + msg.param2 = cpu_to_le32(glink->features); + + GLINK_INFO(glink, "vers:%d features:%d\n", msg.param1, msg.param2); + return glink_spi_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static void glink_spi_send_version_ack(struct glink_spi *glink) +{ + struct glink_msg msg = { 0 }; + + msg.cmd = cpu_to_le16(SPI_CMD_VERSION_ACK); + msg.param1 = cpu_to_le16(GLINK_VERSION_1); + msg.param2 = cpu_to_le32(glink->features); + + GLINK_INFO(glink, "vers:%d features:%d\n", msg.param1, msg.param2); + glink_spi_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +/** + * glink_spi_receive_version() - receive version/features from remote system + * + * @glink: pointer to transport interface + * @r_version: remote version + * @r_features: remote features + * + * This function is called in response to a remote-initiated version/feature + * negotiation sequence. + */ +static void glink_spi_receive_version(struct glink_spi *glink, + u32 version, + u32 features) +{ + GLINK_INFO(glink, "vers:%d features:%d\n", version, features); + + switch (version) { + case 0: + break; + case GLINK_VERSION_1: + glink->features &= features; + /* FALLTHROUGH */ + default: + glink_spi_send_version_ack(glink); + break; + } +} + +/** + * glink_spi_receive_version_ack() - receive negotiation ack from remote system + * + * @glink: pointer to transport interface + * @r_version: remote version response + * @r_features: remote features response + * + * This function is called in response to a local-initiated version/feature + * negotiation sequence and is the counter-offer from the remote side based + * upon the initial version and feature set requested. + */ +static void glink_spi_receive_version_ack(struct glink_spi *glink, + u32 version, + u32 features) +{ + GLINK_INFO(glink, "vers:%d features:%d\n", version, features); + + switch (version) { + case 0: + /* Version negotiation failed */ + break; + case GLINK_VERSION_1: + if (features == glink->features) + break; + + glink->features &= features; + /* FALLTHROUGH */ + default: + glink_spi_send_version(glink); + break; + } +} + +/** + * glink_spi_send_open_req() - send a SPI_CMD_OPEN request to the remote + * @glink: Ptr to the glink edge + * @channel: Ptr to the channel that the open req is sent + * + * Allocates a local channel id and sends a SPI_CMD_OPEN message to the remote. + * Will return with refcount held, regardless of outcome. + * + * Returns 0 on success, negative errno otherwise. + */ +static int glink_spi_send_open_req(struct glink_spi *glink, + struct glink_channel *channel) +{ + + struct cmd_msg { + __le16 cmd; + __le16 lcid; + __le16 length; + __le16 req_xprt; + __le64 reserved; + }; + struct { + struct cmd_msg msg; + u8 name[GLINK_NAME_SIZE]; + } __packed req; + int name_len = strlen(channel->name) + 1; + int req_len = ALIGN(sizeof(req.msg) + name_len, SPI_ALIGNMENT); + int ret; + unsigned long flags; + + kref_get(&channel->refcount); + + spin_lock_irqsave(&glink->idr_lock, flags); + ret = idr_alloc_cyclic(&glink->lcids, channel, + SPI_GLINK_CID_MIN, SPI_GLINK_CID_MAX, + GFP_ATOMIC); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (ret < 0) + return ret; + + channel->lcid = ret; + CH_INFO(channel, "\n"); + + memset(&req, 0, sizeof(req)); + req.msg.cmd = cpu_to_le16(SPI_CMD_OPEN); + req.msg.lcid = cpu_to_le16(channel->lcid); + req.msg.length = cpu_to_le16(name_len); + strlcpy(req.name, channel->name, GLINK_NAME_SIZE); + + ret = glink_spi_tx(glink, &req, req_len, NULL, 0, true); + if (ret) + goto remove_idr; + + return 0; + +remove_idr: + CH_INFO(channel, "remove_idr\n"); + + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->lcids, channel->lcid); + channel->lcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + return ret; +} + +static void glink_spi_send_open_ack(struct glink_spi *glink, + struct glink_channel *channel) +{ + struct glink_msg msg = { 0 }; + + msg.cmd = cpu_to_le16(SPI_CMD_OPEN_ACK); + msg.param1 = cpu_to_le16(channel->rcid); + + CH_INFO(channel, "\n"); + glink_spi_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int glink_spi_rx_open_ack(struct glink_spi *glink, unsigned int lcid) +{ + struct glink_channel *channel; + + spin_lock(&glink->idr_lock); + channel = idr_find(&glink->lcids, lcid); + spin_unlock(&glink->idr_lock); + if (!channel) { + GLINK_ERR(glink, "Invalid open ack packet %d\n", lcid); + return -EINVAL; + } + + CH_INFO(channel, "\n"); + complete_all(&channel->open_ack); + + return 0; +} + +static void glink_spi_send_close_req(struct glink_spi *glink, + struct glink_channel *channel) +{ + struct glink_msg req = { 0 }; + + req.cmd = cpu_to_le16(SPI_CMD_CLOSE); + req.param1 = cpu_to_le16(channel->lcid); + + CH_INFO(channel, "\n"); + glink_spi_tx(glink, &req, sizeof(req), NULL, 0, true); +} + +static void glink_spi_send_close_ack(struct glink_spi *glink, + unsigned int rcid) +{ + struct glink_msg req = { 0 }; + + req.cmd = cpu_to_le16(SPI_CMD_CLOSE_ACK); + req.param1 = cpu_to_le16(rcid); + + GLINK_INFO(glink, "rcid:%d\n", rcid); + glink_spi_tx(glink, &req, sizeof(req), NULL, 0, true); +} + +static int glink_spi_request_intent(struct glink_spi *glink, + struct glink_channel *channel, + size_t size) +{ + struct glink_msg req = { 0 }; + int ret; + + mutex_lock(&channel->intent_req_lock); + + reinit_completion(&channel->intent_req_comp); + + req.cmd = cpu_to_le16(SPI_CMD_RX_INTENT_REQ); + req.param1 = cpu_to_le16(channel->lcid); + req.param2 = cpu_to_le32(size); + + CH_INFO(channel, "size:%d\n", size); + + ret = glink_spi_tx(glink, &req, sizeof(req), NULL, 0, true); + if (ret) + goto unlock; + + ret = wait_for_completion_timeout(&channel->intent_req_comp, 10 * HZ); + if (!ret) { + dev_err(&glink->dev, "intent request timed out\n"); + ret = -ETIMEDOUT; + } else { + ret = channel->intent_req_result ? 0 : -ECANCELED; + } + +unlock: + mutex_unlock(&channel->intent_req_lock); + return ret; +} + +static int glink_spi_handle_intent(struct glink_spi *glink, + unsigned int cid, + unsigned int count, + void *rx_data, + size_t avail) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + struct intent_pair { + __le32 size; + __le32 iid; + __le64 addr; + }; + struct intent_pair *intents; + const size_t msglen = sizeof(struct intent_pair) * count; + int ret; + int i; + unsigned long flags; + + if (avail < msglen) { + dev_err(&glink->dev, "Not enough data in buf\n"); + return avail; + } + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(&glink->dev, "intents for non-existing channel\n"); + return msglen; + } + + intents = (struct intent_pair *)rx_data; + for (i = 0; i < count; ++i) { + intent = kzalloc(sizeof(*intent), GFP_ATOMIC); + if (!intent) + break; + + intent->id = le32_to_cpu(intents[i].iid); + intent->size = le32_to_cpu(intents[i].size); + intent->addr = (u32)le64_to_cpu(intents[i].addr); + + CH_INFO(channel, "riid:%d size:%d\n", intent->id, intent->size); + + spin_lock_irqsave(&channel->intent_lock, flags); + ret = idr_alloc(&channel->riids, intent, + intent->id, intent->id + 1, GFP_ATOMIC); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (ret < 0) + dev_err(&glink->dev, "failed to store remote intent\n"); + } + + return msglen; +} + +static void glink_spi_handle_intent_req_ack(struct glink_spi *glink, + unsigned int cid, bool granted) +{ + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(&glink->dev, "unable to find channel\n"); + return; + } + + channel->intent_req_result = granted; + complete(&channel->intent_req_comp); + CH_INFO(channel, "\n"); +} + +/** + * glink_spi_send_intent_req_ack() - convert an rx intent request ack cmd to + wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @granted: The request response to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int glink_spi_send_intent_req_ack(struct glink_spi *glink, + struct glink_channel *channel, + bool granted) +{ + struct glink_msg msg = { 0 }; + + msg.cmd = cpu_to_le16(SPI_CMD_RX_INTENT_REQ_ACK); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(granted); + + CH_INFO(channel, "\n"); + glink_spi_tx(glink, &msg, sizeof(msg), NULL, 0, true); + + return 0; +} + +static struct glink_core_rx_intent * +glink_spi_alloc_intent(struct glink_spi *glink, + struct glink_channel *channel, + size_t size, + bool reuseable) +{ + struct glink_core_rx_intent *intent; + int ret; + unsigned long flags; + + intent = kzalloc(sizeof(*intent), GFP_KERNEL); + if (!intent) + return NULL; + + intent->data = kzalloc(size, GFP_KERNEL); + if (!intent->data) + goto free_intent; + + spin_lock_irqsave(&channel->intent_lock, flags); + ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC); + if (ret < 0) { + spin_unlock_irqrestore(&channel->intent_lock, flags); + goto free_data; + } + spin_unlock_irqrestore(&channel->intent_lock, flags); + + intent->id = ret; + intent->size = size; + intent->reuse = reuseable; + + return intent; + +free_data: + kfree(intent->data); +free_intent: + kfree(intent); + return NULL; +} + +/** + * glink_spi_advertise_intent - convert an rx intent cmd to wire format and + * transmit + * @glink: The transport to transmit on. + * @channel: The local channel + * @size: The intent to pass on to remote. + * + * Return: 0 on success or standard Linux error code. + */ +static int glink_spi_advertise_intent(struct glink_spi *glink, + struct glink_channel *channel, + struct glink_core_rx_intent *intent) +{ + struct command { + struct glink_msg msg; + __le32 size; + __le32 liid; + __le64 addr; + } __packed; + struct command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.msg.cmd = cpu_to_le16(SPI_CMD_INTENT); + cmd.msg.param1 = cpu_to_le16(channel->lcid); + cmd.msg.param2 = cpu_to_le32(1); + cmd.size = cpu_to_le32(intent->size); + cmd.liid = cpu_to_le32(intent->id); + + CH_INFO(channel, "count:%d size:%d liid:%d\n", 1, + intent->size, intent->id); + + glink_spi_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); + + return 0; +} + +/** + * glink_spi_handle_intent_req() - Receive a request for rx_intent + * from remote side + * if_ptr: Pointer to the transport interface + * rcid: Remote channel ID + * size: size of the intent + * + * The function searches for the local channel to which the request for + * rx_intent has arrived and allocates and notifies the remote back + */ +static void glink_spi_handle_intent_req(struct glink_spi *glink, + u32 cid, size_t size) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + + if (!channel) { + pr_err("%s channel not found for cid %d\n", __func__, cid); + return; + } + + intent = glink_spi_alloc_intent(glink, channel, size, false); + if (intent) + glink_spi_advertise_intent(glink, channel, intent); + + glink_spi_send_intent_req_ack(glink, channel, !!intent); +} + +static int glink_spi_send_short(struct glink_channel *channel, + void *data, int len, + struct glink_core_rx_intent *intent, bool wait) +{ + struct glink_spi *glink = channel->glink; + struct { + struct glink_msg msg; + u8 data[SHORT_PKT_SIZE]; + } __packed req; + int ret = 0; + + req.msg.cmd = cpu_to_le16(SPI_CMD_TX_SHORT_DATA); + req.msg.param1 = cpu_to_le16(channel->lcid); + req.msg.param2 = cpu_to_le32(intent->id); + req.msg.param3 = cpu_to_le32(len); + req.msg.param4 = cpu_to_be32(0); + memcpy(req.data, data, len); + + mutex_lock(&glink->tx_lock); + while (glink_spi_tx_avail(glink) < sizeof(req)) { + if (!wait) { + ret = -EAGAIN; + goto out; + } + + if (unlikely(glink->in_reset)) { + ret = -ENXIO; + goto out; + } + + /* Wait without holding the tx_lock */ + mutex_unlock(&glink->tx_lock); + + usleep_range(10000, 15000); + + mutex_lock(&glink->tx_lock); + } + glink_spi_tx_write(glink, &req, sizeof(req), NULL, 0); + +out: + mutex_unlock(&glink->tx_lock); + return ret; +} + +static int __glink_spi_send(struct glink_channel *channel, + void *data, int len, bool wait) +{ + struct glink_spi *glink = channel->glink; + struct glink_core_rx_intent *intent = NULL; + struct glink_core_rx_intent *tmp; + int iid = 0; + struct { + struct glink_msg msg; + __le32 chunk_size; + __le32 left_size; + } __packed req; + int ret = 0; + unsigned long flags; + + CH_INFO(channel, "size:%d, wait:%d\n", len, wait); + + spin_lock_irqsave(&glink->activity_lock, flags); + glink->activity_flag |= ACTIVE_TX; + spin_unlock_irqrestore(&glink->activity_lock, flags); + + while (!intent) { + spin_lock_irqsave(&channel->intent_lock, flags); + idr_for_each_entry(&channel->riids, tmp, iid) { + if (tmp->size >= len && !tmp->in_use) { + if (!intent) + intent = tmp; + else if (intent->size > tmp->size) + intent = tmp; + if (intent->size == len) + break; + } + } + if (intent) + intent->in_use = true; + spin_unlock_irqrestore(&channel->intent_lock, flags); + + /* We found an available intent */ + if (intent) + break; + + if (!wait) { + ret = -EBUSY; + goto tx_exit; + } + + ret = glink_spi_request_intent(glink, channel, len); + if (ret < 0) + goto tx_exit; + } + + memset(&req, 0, sizeof(req)); + iid = intent->id; + + if (len <= SHORT_PKT_SIZE) { + ret = glink_spi_send_short(channel, data, len, intent, wait); + goto tx_exit; + } + + if (intent->offset) + req.msg.cmd = cpu_to_le16(SPI_CMD_TX_DATA_CONT); + else + req.msg.cmd = cpu_to_le16(SPI_CMD_TX_DATA); + + req.msg.param1 = cpu_to_le16(channel->lcid); + req.msg.param2 = cpu_to_le32(iid); + req.chunk_size = cpu_to_le32(len); + req.left_size = cpu_to_le32(0); + + mutex_lock(&glink->tx_lock); + while (glink_spi_tx_avail(glink) < sizeof(req)) { + if (!wait) { + ret = -EAGAIN; + mutex_unlock(&glink->tx_lock); + goto tx_exit; + } + + if (unlikely(glink->in_reset)) { + ret = -EINVAL; + mutex_unlock(&glink->tx_lock); + goto tx_exit; + } + + /* Wait without holding the tx_lock */ + mutex_unlock(&glink->tx_lock); + + usleep_range(10000, 15000); + + mutex_lock(&glink->tx_lock); + } + + glink_spi_write(glink, data, intent->addr, len); + glink_spi_tx_write(glink, &req, sizeof(req), NULL, 0); + mutex_unlock(&glink->tx_lock); + +tx_exit: + /* Mark intent available if we failed */ + if (ret && intent) + intent->in_use = false; + + spin_lock_irqsave(&glink->activity_lock, flags); + glink->activity_flag &= ~ACTIVE_TX; + spin_unlock_irqrestore(&glink->activity_lock, flags); + + return ret; +} + +static void glink_spi_handle_rx_done(struct glink_spi *glink, + u32 cid, uint32_t iid, + bool reuse) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(&glink->dev, "invalid channel id received\n"); + return; + } + + spin_lock_irqsave(&channel->intent_lock, flags); + intent = idr_find(&channel->riids, iid); + + if (!intent) { + spin_unlock_irqrestore(&channel->intent_lock, flags); + dev_err(&glink->dev, "invalid intent id received\n"); + return; + } + + intent->in_use = false; + CH_INFO(channel, "reuse:%d iid:%d\n", reuse, intent->id); + + if (!reuse) { + idr_remove(&channel->riids, intent->id); + kfree(intent); + } + spin_unlock_irqrestore(&channel->intent_lock, flags); +} + +static void glink_spi_rx_done(struct glink_spi *glink, + struct glink_channel *channel, + struct glink_core_rx_intent *intent) +{ + /* We don't send RX_DONE to intentless systems */ + if (glink->intentless) { + kfree(intent->data); + kfree(intent); + return; + } + + /* Take it off the tree of receive intents */ + if (!intent->reuse) { + spin_lock(&channel->intent_lock); + idr_remove(&channel->liids, intent->id); + spin_unlock(&channel->intent_lock); + } + + /* Schedule the sending of a rx_done indication */ + spin_lock(&channel->intent_lock); + list_add_tail(&intent->node, &channel->done_intents); + spin_unlock(&channel->intent_lock); + + schedule_work(&channel->intent_work); +} + +static void glink_spi_rx_done_work(struct work_struct *work) +{ + struct glink_channel *channel = container_of(work, struct glink_channel, + intent_work); + struct glink_spi *glink = channel->glink; + struct glink_core_rx_intent *intent, *tmp; + struct { + u16 id; + u16 lcid; + u32 liid; + u64 reserved; + } __packed cmd; + + unsigned int lcid = channel->lcid; + unsigned int iid; + bool reuse; + unsigned long flags; + + spin_lock_irqsave(&channel->intent_lock, flags); + list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) { + list_del(&intent->node); + spin_unlock_irqrestore(&channel->intent_lock, flags); + iid = intent->id; + reuse = intent->reuse; + + cmd.id = reuse ? SPI_CMD_RX_DONE_W_REUSE : SPI_CMD_RX_DONE; + cmd.lcid = lcid; + cmd.liid = iid; + + CH_INFO(channel, "reuse:%d liid:%d", reuse, iid); + glink_spi_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); + if (!reuse) { + kfree(intent->data); + kfree(intent); + } + spin_lock_irqsave(&channel->intent_lock, flags); + } + spin_unlock_irqrestore(&channel->intent_lock, flags); +} + +/* Locally initiated rpmsg_create_ept */ +static struct glink_channel *glink_spi_create_local(struct glink_spi *glink, + const char *name) +{ + struct glink_channel *channel; + int ret; + unsigned long flags; + + channel = glink_spi_alloc_channel(glink, name); + if (IS_ERR(channel)) + return ERR_CAST(channel); + + CH_INFO(channel, "\n"); + ret = glink_spi_send_open_req(glink, channel); + if (ret) + goto release_channel; + + ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); + if (!ret) + goto err_timeout; + + ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ); + if (!ret) + goto err_timeout; + + glink_spi_send_open_ack(glink, channel); + + return channel; + +err_timeout: + CH_INFO(channel, "err_timeout\n"); + + /* glink_spi_send_open_req() did register the channel in lcids*/ + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->lcids, channel->lcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + +release_channel: + CH_INFO(channel, "release_channel\n"); + /* Release glink_spi_send_open_req() reference */ + kref_put(&channel->refcount, glink_spi_channel_release); + /* Release glink_spi_alloc_channel() reference */ + kref_put(&channel->refcount, glink_spi_channel_release); + + return ERR_PTR(-ETIMEDOUT); +} + +/* Remote initiated rpmsg_create_ept */ +static int glink_spi_create_remote(struct glink_spi *glink, + struct glink_channel *channel) +{ + int ret; + + CH_INFO(channel, "\n"); + + glink_spi_send_open_ack(glink, channel); + + ret = glink_spi_send_open_req(glink, channel); + if (ret) + goto close_link; + + ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); + if (!ret) { + ret = -ETIMEDOUT; + goto close_link; + } + + return 0; + +close_link: + CH_INFO(channel, "close_link %d\n", ret); + + /* + * Send a close request to "undo" our open-ack. The close-ack will + * release the last reference. + */ + glink_spi_send_close_req(glink, channel); + + /* Release glink_spi_send_open_req() reference */ + kref_put(&channel->refcount, glink_spi_channel_release); + + return ret; +} + +static struct rpmsg_endpoint *glink_spi_create_ept(struct rpmsg_device *rpdev, + rpmsg_rx_cb_t cb, + void *priv, + struct rpmsg_channel_info + chinfo) +{ + struct glink_channel *parent = to_glink_channel(rpdev->ept); + struct glink_channel *channel; + struct glink_spi *glink = parent->glink; + struct rpmsg_endpoint *ept; + const char *name = chinfo.name; + int cid; + int ret; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + idr_for_each_entry(&glink->rcids, channel, cid) { + if (!strcmp(channel->name, name)) + break; + } + spin_unlock_irqrestore(&glink->idr_lock, flags); + + if (!channel) { + channel = glink_spi_create_local(glink, name); + if (IS_ERR(channel)) + return NULL; + } else { + ret = glink_spi_create_remote(glink, channel); + if (ret) + return NULL; + } + + ept = &channel->ept; + ept->rpdev = rpdev; + ept->cb = cb; + ept->priv = priv; + ept->ops = &glink_endpoint_ops; + + return ept; +} + +static int glink_spi_announce_create(struct rpmsg_device *rpdev) +{ + struct glink_channel *channel = to_glink_channel(rpdev->ept); + struct device_node *np = rpdev->dev.of_node; + struct glink_spi *glink = channel->glink; + struct glink_core_rx_intent *intent; + const struct property *prop = NULL; + __be32 defaults[] = { cpu_to_be32(SZ_1K), cpu_to_be32(5) }; + int num_intents; + int num_groups = 1; + __be32 *val = defaults; + int size; + + if (glink->intentless || !completion_done(&channel->open_ack)) + return 0; + + prop = of_find_property(np, "qcom,intents", NULL); + if (prop) { + val = prop->value; + num_groups = prop->length / sizeof(u32) / 2; + } + + /* Channel is now open, advertise base set of intents */ + while (num_groups--) { + size = be32_to_cpup(val++); + num_intents = be32_to_cpup(val++); + while (num_intents--) { + intent = glink_spi_alloc_intent(glink, channel, size, + true); + if (!intent) + break; + + glink_spi_advertise_intent(glink, channel, intent); + } + } + return 0; +} + +static void glink_spi_destroy_ept(struct rpmsg_endpoint *ept) +{ + struct glink_channel *channel = to_glink_channel(ept); + struct glink_spi *glink = channel->glink; + unsigned long flags; + + spin_lock_irqsave(&channel->recv_lock, flags); + channel->ept.cb = NULL; + spin_unlock_irqrestore(&channel->recv_lock, flags); + + /* Decouple the potential rpdev from the channel */ + channel->rpdev = NULL; + + glink_spi_send_close_req(glink, channel); +} + +static void glink_spi_rx_close(struct glink_spi *glink, unsigned int rcid) +{ + struct rpmsg_channel_info chinfo; + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (WARN(!channel, "close request on unknown channel\n")) + return; + CH_INFO(channel, "\n"); + + /* cancel pending rx_done work */ + cancel_work_sync(&channel->intent_work); + + if (channel->rpdev) { + strlcpy(chinfo.name, channel->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + + rpmsg_unregister_device(&glink->dev, &chinfo); + } + + glink_spi_send_close_ack(glink, channel->rcid); + + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->rcids, channel->rcid); + channel->rcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + kref_put(&channel->refcount, glink_spi_channel_release); +} + +static void glink_spi_rx_close_ack(struct glink_spi *glink, unsigned int lcid) +{ + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->lcids, lcid); + if (WARN(!channel, "close ack on unknown channel\n")) { + spin_unlock_irqrestore(&glink->idr_lock, flags); + return; + } + CH_INFO(channel, "\n"); + + idr_remove(&glink->lcids, channel->lcid); + channel->lcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + kref_put(&channel->refcount, glink_spi_channel_release); +} + +static int glink_spi_send(struct rpmsg_endpoint *ept, void *data, int len) +{ + struct glink_channel *channel = to_glink_channel(ept); + + return __glink_spi_send(channel, data, len, true); +} + +static int glink_spi_trysend(struct rpmsg_endpoint *ept, void *data, int len) +{ + struct glink_channel *channel = to_glink_channel(ept); + + return __glink_spi_send(channel, data, len, false); +} + +/** + * glink_spi_send_signals() - convert a signal cmd to wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @sigs: The signals to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int glink_spi_send_signals(struct glink_spi *glink, + struct glink_channel *channel, + u32 sigs) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(SPI_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + GLINK_INFO(glink, "sigs:%d\n", sigs); + return glink_spi_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int glink_spi_handle_signals(struct glink_spi *glink, + unsigned int rcid, unsigned int signals) +{ + struct glink_channel *channel; + unsigned long flags; + u32 old; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(&glink->dev, "signal for non-existing channel\n"); + return -EINVAL; + } + + old = channel->rsigs; + channel->rsigs = signals; + + if (channel->ept.sig_cb) + channel->ept.sig_cb(channel->ept.rpdev, old, channel->rsigs); + + CH_INFO(channel, "old:%d new:%d\n", old, channel->rsigs); + + return 0; +} + +static int glink_spi_get_sigs(struct rpmsg_endpoint *ept, + u32 *lsigs, u32 *rsigs) +{ + struct glink_channel *channel = to_glink_channel(ept); + + *lsigs = channel->lsigs; + *rsigs = channel->rsigs; + + return 0; +} + +static int glink_spi_set_sigs(struct rpmsg_endpoint *ept, u32 sigs) +{ + struct glink_channel *channel = to_glink_channel(ept); + struct glink_spi *glink = channel->glink; + + channel->lsigs = sigs; + + return glink_spi_send_signals(glink, channel, sigs); +} + +/* + * Finds the device_node for the glink child interested in this channel. + */ +static struct device_node *glink_spi_match_channel(struct device_node *node, + const char *channel) +{ + struct device_node *child; + const char *name; + const char *key; + int ret; + + for_each_available_child_of_node(node, child) { + key = "qcom,glink-channels"; + ret = of_property_read_string(child, key, &name); + if (ret) + continue; + + if (strcmp(name, channel) == 0) + return child; + } + + return NULL; +} + +static const struct rpmsg_device_ops glink_device_ops = { + .create_ept = glink_spi_create_ept, + .announce_create = glink_spi_announce_create, +}; + +static const struct rpmsg_endpoint_ops glink_endpoint_ops = { + .destroy_ept = glink_spi_destroy_ept, + .send = glink_spi_send, + .trysend = glink_spi_trysend, + .get_sigs = glink_spi_get_sigs, + .set_sigs = glink_spi_set_sigs, +}; + +static void glink_spi_rpdev_release(struct device *dev) +{ + struct rpmsg_device *rpdev = to_rpmsg_device(dev); + struct glink_channel *channel = to_glink_channel(rpdev->ept); + + channel->rpdev = NULL; + kfree(rpdev); +} + +static int glink_spi_rx_open(struct glink_spi *glink, unsigned int rcid, + char *name) +{ + struct glink_channel *channel; + struct rpmsg_device *rpdev; + bool create_device = false; + struct device_node *node; + int lcid; + int ret; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + idr_for_each_entry(&glink->lcids, channel, lcid) { + if (!strcmp(channel->name, name)) + break; + } + spin_unlock_irqrestore(&glink->idr_lock, flags); + + if (!channel) { + channel = glink_spi_alloc_channel(glink, name); + if (IS_ERR(channel)) + return PTR_ERR(channel); + + /* The opening dance was initiated by the remote */ + create_device = true; + } + + spin_lock_irqsave(&glink->idr_lock, flags); + ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_ATOMIC); + if (ret < 0) { + dev_err(&glink->dev, "Unable to insert channel into rcid list\n"); + spin_unlock_irqrestore(&glink->idr_lock, flags); + goto free_channel; + } + channel->rcid = ret; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + complete_all(&channel->open_req); + + if (create_device) { + rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); + if (!rpdev) { + ret = -ENOMEM; + goto rcid_remove; + } + + rpdev->ept = &channel->ept; + strlcpy(rpdev->id.name, name, RPMSG_NAME_SIZE); + rpdev->src = RPMSG_ADDR_ANY; + rpdev->dst = RPMSG_ADDR_ANY; + rpdev->ops = &glink_device_ops; + + node = glink_spi_match_channel(glink->dev.of_node, name); + rpdev->dev.of_node = node; + rpdev->dev.parent = &glink->dev; + rpdev->dev.release = glink_spi_rpdev_release; + + ret = rpmsg_register_device(rpdev); + if (ret) + goto free_rpdev; + + channel->rpdev = rpdev; + } + CH_INFO(channel, "\n"); + + return 0; + +free_rpdev: + CH_INFO(channel, "free_rpdev\n"); + kfree(rpdev); +rcid_remove: + CH_INFO(channel, "rcid_remove\n"); + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->rcids, channel->rcid); + channel->rcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); +free_channel: + CH_INFO(channel, "free_channel\n"); + /* Release the reference, iff we took it */ + if (create_device) + kref_put(&channel->refcount, glink_spi_channel_release); + + return ret; +} + +static int glink_spi_rx_data(struct glink_spi *glink, + unsigned int rcid, unsigned int liid, + void *rx_data, size_t avail) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + struct data_desc { + __le32 chunk_size; + __le32 left_size; + __le64 addr; + }; + struct data_desc *hdr; + unsigned int chunk_size; + unsigned int left_size; + u32 addr; + size_t msglen; + unsigned long flags; + + msglen = sizeof(*hdr); + if (avail < msglen) { + dev_dbg(&glink->dev, "Not enough data in fifo\n"); + return avail; + } + hdr = (struct data_desc *)rx_data; + + chunk_size = le32_to_cpu(hdr->chunk_size); + left_size = le32_to_cpu(hdr->left_size); + addr = (u32)le64_to_cpu(hdr->addr); + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_dbg(&glink->dev, "Data on non-existing channel\n"); + return msglen; + } + CH_INFO(channel, "chunk_size:%d left_size:%d\n", chunk_size, left_size); + + spin_lock_irqsave(&channel->intent_lock, flags); + intent = idr_find(&channel->liids, liid); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (!intent) { + dev_err(&glink->dev, + "no intent found for channel %s intent %d", + channel->name, liid); + return msglen; + } + + if (intent->size - intent->offset < chunk_size) { + dev_err(&glink->dev, "Insufficient space in intent\n"); + + /* The packet header lied, drop payload */ + return msglen; + } + + /* Read message from addr sent by WDSP */ + glink_spi_read(glink, addr, intent->data + intent->offset, chunk_size); + intent->offset += chunk_size; + + /* Handle message when no fragments remain to be received */ + if (!left_size) { + spin_lock(&channel->recv_lock); + if (channel->ept.cb) { + channel->ept.cb(channel->ept.rpdev, + intent->data, + intent->offset, + channel->ept.priv, + RPMSG_ADDR_ANY); + } + spin_unlock(&channel->recv_lock); + + intent->offset = 0; + channel->buf = NULL; + + glink_spi_rx_done(glink, channel, intent); + } + return msglen; +} + +static int glink_spi_rx_short_data(struct glink_spi *glink, + unsigned int rcid, unsigned int liid, + unsigned int chunk_size, + unsigned int left_size, + void *src, size_t avail) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + size_t msglen = SHORT_PKT_SIZE; + unsigned long flags; + + if (avail < msglen) { + dev_dbg(&glink->dev, "Not enough data in fifo\n"); + return avail; + } + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_dbg(&glink->dev, "Data on non-existing channel\n"); + return msglen; + } + CH_INFO(channel, "chunk_size:%d left_size:%d\n", chunk_size, left_size); + + spin_lock_irqsave(&channel->intent_lock, flags); + intent = idr_find(&channel->liids, liid); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (!intent) { + dev_err(&glink->dev, + "no intent found for channel %s intent %d", + channel->name, liid); + return msglen; + } + + if (intent->size - intent->offset < chunk_size) { + dev_err(&glink->dev, "Insufficient space in intent\n"); + + /* The packet header lied, drop payload */ + return msglen; + } + + /* Read message from addr sent by WDSP */ + memcpy(intent->data + intent->offset, src, chunk_size); + intent->offset += chunk_size; + + /* Handle message when no fragments remain to be received */ + if (!left_size) { + spin_lock(&channel->recv_lock); + if (channel->ept.cb) { + channel->ept.cb(channel->ept.rpdev, + intent->data, + intent->offset, + channel->ept.priv, + RPMSG_ADDR_ANY); + } + spin_unlock(&channel->recv_lock); + + intent->offset = 0; + channel->buf = NULL; + + glink_spi_rx_done(glink, channel, intent); + } + return msglen; +} + +static void glink_spi_defer_work(struct work_struct *work) +{ + struct glink_spi *glink = container_of(work, struct glink_spi, + rx_defer_work); + + struct glink_defer_cmd *dcmd; + struct glink_msg *msg; + unsigned long flags; + unsigned int param1; + unsigned int param2; + unsigned int param3; + unsigned int param4; + unsigned int cmd; + + for (;;) { + spin_lock_irqsave(&glink->rx_lock, flags); + if (list_empty(&glink->rx_queue)) { + spin_unlock_irqrestore(&glink->rx_lock, flags); + break; + } + dcmd = list_first_entry(&glink->rx_queue, + struct glink_defer_cmd, node); + list_del(&dcmd->node); + spin_unlock_irqrestore(&glink->rx_lock, flags); + + msg = &dcmd->msg; + cmd = le16_to_cpu(msg->cmd); + param1 = le16_to_cpu(msg->param1); + param2 = le32_to_cpu(msg->param2); + param3 = le32_to_cpu(msg->param3); + param4 = le32_to_cpu(msg->param4); + + switch (cmd) { + case SPI_CMD_OPEN: + glink_spi_rx_open(glink, param1, msg->data); + break; + case SPI_CMD_CLOSE: + glink_spi_rx_close(glink, param1); + break; + case SPI_CMD_CLOSE_ACK: + glink_spi_rx_close_ack(glink, param1); + break; + default: + WARN(1, "Unknown defer object %d\n", cmd); + break; + } + + kfree(dcmd); + } +} + +static int glink_spi_rx_defer(struct glink_spi *glink, + void *rx_data, u32 rx_avail, size_t extra) +{ + struct glink_defer_cmd *dcmd; + + extra = ALIGN(extra, SPI_ALIGNMENT); + + if (rx_avail < sizeof(struct glink_msg) + extra) { + dev_dbg(&glink->dev, "Insufficient data in rx fifo"); + return -ENXIO; + } + + dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_KERNEL); + if (!dcmd) + return -ENOMEM; + + INIT_LIST_HEAD(&dcmd->node); + + memcpy(&dcmd->msg, rx_data, sizeof(dcmd->msg) + extra); + + spin_lock(&glink->rx_lock); + list_add_tail(&dcmd->node, &glink->rx_queue); + spin_unlock(&glink->rx_lock); + + schedule_work(&glink->rx_defer_work); + + return 0; +} + +static void glink_spi_process_cmd(struct glink_spi *glink, void *rx_data, + u32 rx_size) +{ + struct glink_msg *msg; + unsigned int param1; + unsigned int param2; + unsigned int param3; + unsigned int param4; + unsigned int cmd; + int offset = 0; + int ret; + u16 name_len; + char *name; + + while (offset < rx_size) { + msg = (struct glink_msg *)(rx_data + offset); + offset += sizeof(*msg); + + cmd = le16_to_cpu(msg->cmd); + param1 = le16_to_cpu(msg->param1); + param2 = le32_to_cpu(msg->param2); + param3 = le32_to_cpu(msg->param3); + param4 = le32_to_cpu(msg->param4); + + switch (cmd) { + case SPI_CMD_VERSION: + if (param3) { + glink->rx_pipe.length = param3; + glink->tx_pipe.length = param3; + } + glink_spi_receive_version(glink, param1, param2); + break; + case SPI_CMD_VERSION_ACK: + glink_spi_receive_version_ack(glink, param1, param2); + break; + case SPI_CMD_CLOSE: + case SPI_CMD_CLOSE_ACK: + glink_spi_rx_defer(glink, + rx_data + offset - sizeof(*msg), + rx_size + offset - sizeof(*msg), 0); + break; + case SPI_CMD_RX_INTENT_REQ: + glink_spi_handle_intent_req(glink, param1, param2); + break; + case SPI_CMD_OPEN_ACK: + ret = glink_spi_rx_open_ack(glink, param1); + break; + case SPI_CMD_OPEN: + name_len = (u16)(param2 & 0xFFFF); + name = rx_data + offset; + glink_spi_rx_defer(glink, + rx_data + offset - sizeof(*msg), + rx_size + offset - sizeof(*msg), + ALIGN(name_len, SPI_ALIGNMENT)); + + offset += ALIGN(name_len, SPI_ALIGNMENT); + break; + case SPI_CMD_TX_DATA: + case SPI_CMD_TX_DATA_CONT: + ret = glink_spi_rx_data(glink, param1, param2, + rx_data + offset, + rx_size - offset); + offset += ALIGN(ret, SPI_ALIGNMENT); + break; + case SPI_CMD_TX_SHORT_DATA: + ret = glink_spi_rx_short_data(glink, + param1, param2, + param3, param4, + rx_data + offset, + rx_size - offset); + offset += ALIGN(ret, SPI_ALIGNMENT); + break; + case SPI_CMD_READ_NOTIF: + break; + case SPI_CMD_INTENT: + ret = glink_spi_handle_intent(glink, + param1, param2, + rx_data + offset, + rx_size - offset); + offset += ALIGN(ret, SPI_ALIGNMENT); + break; + case SPI_CMD_RX_DONE: + glink_spi_handle_rx_done(glink, param1, param2, false); + break; + case SPI_CMD_RX_DONE_W_REUSE: + glink_spi_handle_rx_done(glink, param1, param2, true); + break; + case SPI_CMD_RX_INTENT_REQ_ACK: + glink_spi_handle_intent_req_ack(glink, param1, param2); + break; + case SPI_CMD_SIGNALS: + glink_spi_handle_signals(glink, param1, param2); + break; + default: + dev_err(&glink->dev, "unhandled rx cmd: %d\n", cmd); + break; + } + } +} + +static void glink_spi_work(struct kthread_work *work) +{ + struct glink_spi *glink = container_of(work, struct glink_spi, + rx_work); + u32 inactive_cycles = 0; + u32 rx_avail; + void *rx_data; + + glink_spi_xprt_set_poll_mode(glink); + do { + rx_avail = glink_spi_rx_avail(glink); + if (!rx_avail) { + usleep_range(POLL_INTERVAL_US, POLL_INTERVAL_US + 50); + inactive_cycles++; + continue; + } + inactive_cycles = 0; + + rx_data = kzalloc(rx_avail, GFP_KERNEL); + if (!rx_data) + break; + + glink_spi_rx_peak(glink, rx_data, 0, rx_avail); + glink_spi_process_cmd(glink, rx_data, rx_avail); + kfree(rx_data); + glink_spi_rx_advance(glink, rx_avail); + + } while (inactive_cycles < MAX_INACTIVE_CYCLES && !glink->in_reset); + glink_spi_xprt_set_irq_mode(glink); +} + +static int glink_spi_cmpnt_init(struct device *dev, void *priv) +{ + return 0; +} + +static int glink_spi_cmpnt_deinit(struct device *dev, void *priv) +{ + return 0; +} + +static int glink_spi_cmpnt_event_handler(struct device *dev, void *priv, + enum wdsp_event_type event, + void *data) +{ + struct glink_spi *glink = dev_get_drvdata(dev); + struct glink_cmpnt *cmpnt = &glink->cmpnt; + int ret; + + switch (event) { + case WDSP_EVENT_PRE_BOOTUP: + if (!cmpnt || !cmpnt->master_dev || !cmpnt->master_ops || + !cmpnt->master_ops->get_devops_for_cmpnt) + break; + + ret = cmpnt->master_ops->get_devops_for_cmpnt(cmpnt->master_dev, + WDSP_CMPNT_TRANSPORT, &glink->spi_ops); + if (ret) + GLINK_ERR(glink, "Failed to get transport device\n"); + break; + case WDSP_EVENT_POST_BOOTUP: + glink->in_reset = false; + ret = glink_spi_send_version(glink); + if (ret) + GLINK_ERR(glink, "failed to send version %d\n", ret); + + /* FALLTHROUGH */ + case WDSP_EVENT_IPC1_INTR: + kthread_queue_work(&glink->rx_worker, &glink->rx_work); + break; + case WDSP_EVENT_PRE_SHUTDOWN: + glink_spi_remove(glink); + break; + case WDSP_EVENT_RESUME: + break; + case WDSP_EVENT_SUSPEND: + break; + default: + GLINK_INFO(glink, "unhandled event %d", event); + break; + } + + return 0; +} + +/* glink_spi_cmpnt_ops - Callback operations registered wtih wdsp framework */ +static struct wdsp_cmpnt_ops glink_spi_cmpnt_ops = { + .init = glink_spi_cmpnt_init, + .deinit = glink_spi_cmpnt_deinit, + .event_handler = glink_spi_cmpnt_event_handler, +}; + +static int glink_component_bind(struct device *dev, struct device *master, + void *data) +{ + struct glink_spi *glink = dev_get_drvdata(dev); + struct glink_cmpnt *cmpnt = &glink->cmpnt; + int ret = 0; + + cmpnt->master_dev = master; + cmpnt->master_ops = data; + + if (cmpnt->master_ops && cmpnt->master_ops->register_cmpnt_ops) + ret = cmpnt->master_ops->register_cmpnt_ops(master, dev, glink, + &glink_spi_cmpnt_ops); + else + ret = -EINVAL; + + if (ret) + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + return ret; +} + +static void glink_component_unbind(struct device *dev, struct device *master, + void *data) +{ + struct glink_spi *glink = dev_get_drvdata(dev); + struct glink_cmpnt *cmpnt = &glink->cmpnt; + + cmpnt->master_dev = NULL; + cmpnt->master_ops = NULL; +} + +static const struct component_ops glink_component_ops = { + .bind = glink_component_bind, + .unbind = glink_component_unbind, +}; + +static int glink_spi_init_pipe(const char *key, struct device_node *node, + struct glink_spi_pipe *pipe) +{ + const struct property *prop = NULL; + __be32 *addrs; + + prop = of_find_property(node, key, NULL); + if (!prop) { + pr_err("%s failed to find prop %s", __func__, key); + return -ENODEV; + } + + if ((prop->length / sizeof(u32)) != 2) { + pr_err("%s %s wrong length %d", __func__, key, prop->length); + return -EINVAL; + } + addrs = prop->value; + + pipe->tail_addr = be32_to_cpup(addrs++); + pipe->head_addr = be32_to_cpup(addrs++); + pipe->length = DEFAULT_FIFO_SIZE; + + return 0; +} + +static void glink_spi_release(struct device *dev) +{ + struct glink_spi *glink = container_of(dev, struct glink_spi, dev); + + kfree(glink); +} + +struct glink_spi *qcom_glink_spi_register(struct device *parent, + struct device_node *node) +{ + struct glink_spi *glink; + struct device *dev; + int ret; + + glink = kzalloc(sizeof(*glink), GFP_KERNEL); + if (!glink) + return ERR_PTR(-ENOMEM); + + dev = &glink->dev; + dev->parent = parent; + dev->of_node = node; + dev->release = glink_spi_release; + dev_set_name(dev, "%s:%s", node->parent->name, node->name); + ret = device_register(dev); + if (ret) { + pr_err("failed to register glink edge\n"); + return ERR_PTR(ret); + } + dev_set_drvdata(dev, glink); + + ret = of_property_read_string(dev->of_node, "label", &glink->name); + if (ret < 0) + glink->name = dev->of_node->name; + + glink->features = GLINK_FEATURE_INTENT_REUSE; + glink->intentless = false; + + mutex_init(&glink->tx_lock); + spin_lock_init(&glink->rx_lock); + INIT_LIST_HEAD(&glink->rx_queue); + INIT_WORK(&glink->rx_defer_work, glink_spi_defer_work); + + kthread_init_work(&glink->rx_work, glink_spi_work); + kthread_init_worker(&glink->rx_worker); + + spin_lock_init(&glink->idr_lock); + idr_init(&glink->lcids); + idr_init(&glink->rcids); + + glink->in_reset = true; + glink->activity_flag = 0; + spin_lock_init(&glink->activity_lock); + + ret = glink_spi_init_pipe("tx-descriptors", node, &glink->tx_pipe); + if (ret) + goto err_put_dev; + + ret = glink_spi_init_pipe("rx-descriptors", node, &glink->rx_pipe); + if (ret) + goto err_put_dev; + + ret = component_add(dev, &glink_component_ops); + if (ret) { + dev_err(dev, "component_add failed, err = %d\n", ret); + goto err_put_dev; + } + + glink->ilc = ipc_log_context_create(GLINK_LOG_PAGE_CNT, glink->name, 0); + + glink->rx_task = kthread_run(kthread_worker_fn, &glink->rx_worker, + "spi_%s", glink->name); + if (IS_ERR(glink->rx_task)) { + ret = PTR_ERR(glink->rx_task); + dev_err(dev, "kthread run failed %d\n", ret); + goto err_put_dev; + } + + return glink; + +err_put_dev: + dev_set_drvdata(dev, NULL); + put_device(dev); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL(qcom_glink_spi_register); + +static int glink_spi_remove_device(struct device *dev, void *data) +{ + device_unregister(dev); + + return 0; +} + +static void glink_spi_remove(struct glink_spi *glink) +{ + struct glink_spi_pipe *rx_pipe = &glink->rx_pipe; + struct glink_spi_pipe *tx_pipe = &glink->tx_pipe; + struct glink_channel *channel; + int cid; + int ret; + unsigned long flags; + + GLINK_INFO(glink, "\n"); + + glink->in_reset = true; + + ret = device_for_each_child(&glink->dev, NULL, glink_spi_remove_device); + if (ret) + dev_warn(&glink->dev, "Can't remove GLINK devices: %d\n", ret); + + spin_lock_irqsave(&glink->idr_lock, flags); + /* Release any defunct local channels, waiting for close-ack */ + idr_for_each_entry(&glink->lcids, channel, cid) + kref_put(&channel->refcount, glink_spi_channel_release); + + /* Release any defunct local channels, waiting for close-req */ + idr_for_each_entry(&glink->lcids, channel, cid) + kref_put(&channel->refcount, glink_spi_channel_release); + + idr_destroy(&glink->lcids); + idr_destroy(&glink->rcids); + spin_unlock_irqrestore(&glink->idr_lock, flags); + + tx_pipe->fifo_base = 0; + tx_pipe->local_addr = 0; + tx_pipe->length = DEFAULT_FIFO_SIZE; + + rx_pipe->fifo_base = 0; + rx_pipe->local_addr = 0; + rx_pipe->length = DEFAULT_FIFO_SIZE; +} + +void qcom_glink_spi_unregister(struct glink_spi *glink) +{ + device_unregister(&glink->dev); +} +EXPORT_SYMBOL(qcom_glink_spi_unregister); + +MODULE_DESCRIPTION("QTI GLINK SPI Transport"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rpmsg/qcom_glink_spss.c b/drivers/rpmsg/qcom_glink_spss.c index 1032f12372e64e79dfc8aba4149186a892c06230..1df692388add4b23665977ec9dced270c9dd2fe6 100644 --- a/drivers/rpmsg/qcom_glink_spss.c +++ b/drivers/rpmsg/qcom_glink_spss.c @@ -53,6 +53,14 @@ struct glink_spss_pipe { #define to_spss_pipe(p) container_of(p, struct glink_spss_pipe, native) +static void glink_spss_reset(struct qcom_glink_pipe *np) +{ + struct glink_spss_pipe *pipe = to_spss_pipe(np); + + *pipe->head = cpu_to_le32(0); + *pipe->tail = cpu_to_le32(0); +} + static size_t glink_spss_rx_avail(struct qcom_glink_pipe *np) { struct glink_spss_pipe *pipe = to_spss_pipe(np); @@ -97,7 +105,7 @@ static void glink_spss_rx_advance(struct qcom_glink_pipe *np, tail = le32_to_cpu(*pipe->tail); tail += count; - if (tail > pipe->native.length) + if (tail >= pipe->native.length) tail -= pipe->native.length; *pipe->tail = cpu_to_le32(tail); @@ -210,8 +218,8 @@ static int glink_spss_advertise_cfg(struct device *dev, *spss_addr = cpu_to_le64(addr); *spss_size = cpu_to_le32(size); - iounmap(spss_addr); - iounmap(spss_size); + devm_iounmap(dev, spss_addr); + devm_iounmap(dev, spss_size); return 0; } @@ -296,10 +304,12 @@ struct qcom_glink *qcom_glink_spss_register(struct device *parent, rx_pipe->native.avail = glink_spss_rx_avail; rx_pipe->native.peak = glink_spss_rx_peak; rx_pipe->native.advance = glink_spss_rx_advance; + rx_pipe->native.reset = glink_spss_reset; rx_pipe->remote_pid = remote_pid; tx_pipe->native.avail = glink_spss_tx_avail; tx_pipe->native.write = glink_spss_tx_write; + tx_pipe->native.reset = glink_spss_reset; tx_pipe->remote_pid = remote_pid; *rx_pipe->tail = 0; diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 1b4bc4442083d9f76194681e3f888008618444fc..3799974b0b2f9757dbeb56a7abfaf9c560634724 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -89,7 +89,7 @@ EXPORT_SYMBOL(rpmsg_create_ept); */ void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) { - if (ept) + if (ept && ept->ops) ept->ops->destroy_ept(ept); } EXPORT_SYMBOL(rpmsg_destroy_ept); @@ -483,7 +483,7 @@ static int rpmsg_dev_probe(struct device *dev) goto out; } - if (rpdev->ops->announce_create) + if (ept && rpdev->ops->announce_create) err = rpdev->ops->announce_create(rpdev); out: return err; diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index 0e358d4b67384b1de1a374d7cae9d010ade8387f..8ff9dc3fe5bf06ea15a2321df4a4383d32414527 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -137,13 +137,15 @@ static unsigned long ac100_clkout_recalc_rate(struct clk_hw *hw, div = (reg >> AC100_CLKOUT_PRE_DIV_SHIFT) & ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1); prate = divider_recalc_rate(hw, prate, div, - ac100_clkout_prediv, 0); + ac100_clkout_prediv, 0, + AC100_CLKOUT_PRE_DIV_WIDTH); } div = (reg >> AC100_CLKOUT_DIV_SHIFT) & (BIT(AC100_CLKOUT_DIV_WIDTH) - 1); return divider_recalc_rate(hw, prate, div, NULL, - CLK_DIVIDER_POWER_OF_TWO); + CLK_DIVIDER_POWER_OF_TWO, + AC100_CLKOUT_DIV_WIDTH); } static long ac100_clkout_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c index 304e891e35fcb060a8ff26f78122fa3c63a1eb87..60f2250fd96bec2cec1dd33f652edaac474d2182 100644 --- a/drivers/rtc/rtc-opal.c +++ b/drivers/rtc/rtc-opal.c @@ -57,7 +57,7 @@ static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms) static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm) { - long rc = OPAL_BUSY; + s64 rc = OPAL_BUSY; int retries = 10; u32 y_m_d; u64 h_m_s_ms; @@ -66,13 +66,17 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm) while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); - if (rc == OPAL_BUSY_EVENT) + if (rc == OPAL_BUSY_EVENT) { + msleep(OPAL_BUSY_DELAY_MS); opal_poll_events(NULL); - else if (retries-- && (rc == OPAL_HARDWARE - || rc == OPAL_INTERNAL_ERROR)) - msleep(10); - else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT) - break; + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) { + if (retries--) { + msleep(10); /* Wait 10ms before retry */ + rc = OPAL_BUSY; /* go around again */ + } + } } if (rc != OPAL_SUCCESS) @@ -87,21 +91,26 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm) static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm) { - long rc = OPAL_BUSY; + s64 rc = OPAL_BUSY; int retries = 10; u32 y_m_d = 0; u64 h_m_s_ms = 0; tm_to_opal(tm, &y_m_d, &h_m_s_ms); + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_rtc_write(y_m_d, h_m_s_ms); - if (rc == OPAL_BUSY_EVENT) + if (rc == OPAL_BUSY_EVENT) { + msleep(OPAL_BUSY_DELAY_MS); opal_poll_events(NULL); - else if (retries-- && (rc == OPAL_HARDWARE - || rc == OPAL_INTERNAL_ERROR)) - msleep(10); - else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT) - break; + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) { + if (retries--) { + msleep(10); /* Wait 10ms before retry */ + rc = OPAL_BUSY; /* go around again */ + } + } } return rc == OPAL_SUCCESS ? 0 : -EIO; diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index fac835530671ff003657a5807f750a73586be87e..5309edcee7b7cad5fb76c98bcda2240287df5137 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -74,16 +74,18 @@ struct pm8xxx_rtc { /* * Steps to write the RTC registers. * 1. Disable alarm if enabled. - * 2. Write 0x00 to LSB. - * 3. Write Byte[1], Byte[2], Byte[3] then Byte[0]. - * 4. Enable alarm if disabled in step 1. + * 2. Disable rtc if enabled. + * 3. Write 0x00 to LSB. + * 4. Write Byte[1], Byte[2], Byte[3] then Byte[0]. + * 5. Enable rtc if disabled in step 2. + * 6. Enable alarm if disabled in step 1. */ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) { int rc, i; unsigned long secs, irq_flags; - u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0; - unsigned int ctrl_reg; + u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, rtc_disabled = 0; + unsigned int ctrl_reg, rtc_ctrl_reg; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; @@ -92,23 +94,38 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) rtc_tm_to_time(tm, &secs); + dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs); + for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) { value[i] = secs & 0xFF; secs >>= 8; } - dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs); - spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); - rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg); + rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); if (rc) goto rtc_rw_fail; if (ctrl_reg & regs->alarm_en) { alarm_enabled = 1; ctrl_reg &= ~regs->alarm_en; - rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg); + rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); + if (rc) { + dev_err(dev, "Write to RTC Alarm control register failed\n"); + goto rtc_rw_fail; + } + } + + /* Disable RTC H/w before writing on RTC register */ + rc = regmap_read(rtc_dd->regmap, regs->ctrl, &rtc_ctrl_reg); + if (rc) + goto rtc_rw_fail; + + if (rtc_ctrl_reg & PM8xxx_RTC_ENABLE) { + rtc_disabled = 1; + rtc_ctrl_reg &= ~PM8xxx_RTC_ENABLE; + rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg); if (rc) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; @@ -137,11 +154,21 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) goto rtc_rw_fail; } + /* Enable RTC H/w after writing on RTC register */ + if (rtc_disabled) { + rtc_ctrl_reg |= PM8xxx_RTC_ENABLE; + rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg); + if (rc) { + dev_err(dev, "Write to RTC control register failed\n"); + goto rtc_rw_fail; + } + } + if (alarm_enabled) { ctrl_reg |= regs->alarm_en; - rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg); + rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg); if (rc) { - dev_err(dev, "Write to RTC control register failed\n"); + dev_err(dev, "Write to RTC Alarm control register failed\n"); goto rtc_rw_fail; } } diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 62f5f04d8f615e393c88824606f58d13801c5e6b..5e963fe0e38d4c2125c43ae801ca7e9b28d98d07 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -592,13 +592,22 @@ static int _schedule_lcu_update(struct alias_lcu *lcu, int dasd_alias_add_device(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; - struct alias_lcu *lcu; + __u8 uaddr = private->uid.real_unit_addr; + struct alias_lcu *lcu = private->lcu; unsigned long flags; int rc; - lcu = private->lcu; rc = 0; spin_lock_irqsave(&lcu->lock, flags); + /* + * Check if device and lcu type differ. If so, the uac data may be + * outdated and needs to be updated. + */ + if (private->uid.type != lcu->uac->unit[uaddr].ua_type) { + lcu->flags |= UPDATE_PENDING; + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "uid type mismatch - trigger rescan"); + } if (!(lcu->flags & UPDATE_PENDING)) { rc = _add_device_to_lcu(lcu, device, device); if (rc) diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 05ac6ba15a53285f41e01ab415b776366ddb5e54..ecc24a46e71a2fa538b9261071f6fc1986d20c4a 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -17,6 +17,8 @@ CFLAGS_REMOVE_sclp_early_core.o += $(CC_FLAGS_MARCH) CFLAGS_sclp_early_core.o += -march=z900 endif +CFLAGS_REMOVE_sclp_early_core.o += $(CC_FLAGS_EXPOLINE) + obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \ sclp_early.o sclp_early_core.o diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 7b0b295b2313b8f3056378ed64c4249381a0213b..69687c16a150a4a4f839bb7357217f0616d13558 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -451,6 +451,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area) static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area) { + struct channel_path *chp; struct chp_link link; struct chp_id chpid; int status; @@ -463,10 +464,17 @@ static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area) chpid.id = sei_area->rsid; /* allocate a new channel path structure, if needed */ status = chp_get_status(chpid); - if (status < 0) - chp_new(chpid); - else if (!status) + if (!status) return; + + if (status < 0) { + chp_new(chpid); + } else { + chp = chpid_to_chp(chpid); + mutex_lock(&chp->lock); + chp_update_desc(chp); + mutex_unlock(&chp->lock); + } memset(&link, 0, sizeof(struct chp_link)); link.chpid = chpid; if ((sei_area->vf & 0xc0) != 0) { diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index a4ad39ba3873f64911802e567b8c19a6abbad5c6..8941e7caaf4dc9c3ae180569329457f5342d1ae8 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -126,7 +126,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, int start, int count, int auto_ack) { - int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0; + int rc, tmp_count = count, tmp_start = start, nr = q->nr; unsigned int ccq = 0; qperf_inc(q, eqbs); @@ -149,14 +149,7 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, qperf_inc(q, eqbs_partial); DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x", tmp_count); - /* - * Retry once, if that fails bail out and process the - * extracted buffers before trying again. - */ - if (!retried++) - goto again; - else - return count - tmp_count; + return count - tmp_count; } DBF_ERROR("%4x EQBS ERROR", SCH_NO(q)); @@ -212,7 +205,10 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, return 0; } -/* returns number of examined buffers and their common state in *state */ +/* + * Returns number of examined buffers and their common state in *state. + * Requested number of buffers-to-examine must be > 0. + */ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, unsigned char *state, unsigned int count, int auto_ack, int merge_pending) @@ -223,17 +219,23 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, if (is_qebsm(q)) return qdio_do_eqbs(q, state, bufnr, count, auto_ack); - for (i = 0; i < count; i++) { - if (!__state) { - __state = q->slsb.val[bufnr]; - if (merge_pending && __state == SLSB_P_OUTPUT_PENDING) - __state = SLSB_P_OUTPUT_EMPTY; - } else if (merge_pending) { - if ((q->slsb.val[bufnr] & __state) != __state) - break; - } else if (q->slsb.val[bufnr] != __state) - break; + /* get initial state: */ + __state = q->slsb.val[bufnr]; + if (merge_pending && __state == SLSB_P_OUTPUT_PENDING) + __state = SLSB_P_OUTPUT_EMPTY; + + for (i = 1; i < count; i++) { bufnr = next_buf(bufnr); + + /* merge PENDING into EMPTY: */ + if (merge_pending && + q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING && + __state == SLSB_P_OUTPUT_EMPTY) + continue; + + /* stop if next state differs from initial state: */ + if (q->slsb.val[bufnr] != __state) + break; } *state = __state; return i; diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index c30420c517b17961e1b0d94f908f557db8c8ee2f..e96b85579f21bdc791add2351449daf9dbc833db 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -20,12 +20,12 @@ static int fsm_io_helper(struct vfio_ccw_private *private) int ccode; __u8 lpm; unsigned long flags; + int ret; sch = private->sch; spin_lock_irqsave(sch->lock, flags); private->state = VFIO_CCW_STATE_BUSY; - spin_unlock_irqrestore(sch->lock, flags); orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm); @@ -38,10 +38,12 @@ static int fsm_io_helper(struct vfio_ccw_private *private) * Initialize device status information */ sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; - return 0; + ret = 0; + break; case 1: /* Status pending */ case 2: /* Busy */ - return -EBUSY; + ret = -EBUSY; + break; case 3: /* Device/path not operational */ { lpm = orb->cmd.lpm; @@ -51,13 +53,16 @@ static int fsm_io_helper(struct vfio_ccw_private *private) sch->lpm = 0; if (cio_update_schib(sch)) - return -ENODEV; - - return sch->lpm ? -EACCES : -ENODEV; + ret = -ENODEV; + else + ret = sch->lpm ? -EACCES : -ENODEV; + break; } default: - return ccode; + ret = ccode; } + spin_unlock_irqrestore(sch->lock, flags); + return ret; } static void fsm_notoper(struct vfio_ccw_private *private, diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index 24388795ee9a30ea933a3c11b60009fcff7b656f..936e8c73565625783627ed941af524f89786a49c 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -2011,7 +2011,7 @@ static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, * have valid data in the sense buffer that could * confuse the higher levels. */ - memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); + memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); //printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id); //{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); } /* diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 375c536cbc688f0e5b9467ca65e9885b96ea130d..c5eb0c468f0b9c5f2004f02f6f87939756105bd7 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -32,13 +32,13 @@ MODULE_AUTHOR("Open-FCoE.org"); MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs"); MODULE_LICENSE("GPL v2"); -static int fcoe_transport_create(const char *, struct kernel_param *); -static int fcoe_transport_destroy(const char *, struct kernel_param *); +static int fcoe_transport_create(const char *, const struct kernel_param *); +static int fcoe_transport_destroy(const char *, const struct kernel_param *); static int fcoe_transport_show(char *buffer, const struct kernel_param *kp); static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device); static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device); -static int fcoe_transport_enable(const char *, struct kernel_param *); -static int fcoe_transport_disable(const char *, struct kernel_param *); +static int fcoe_transport_enable(const char *, const struct kernel_param *); +static int fcoe_transport_disable(const char *, const struct kernel_param *); static int libfcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr); @@ -865,7 +865,8 @@ EXPORT_SYMBOL(fcoe_ctlr_destroy_store); * * Returns: 0 for success */ -static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_create(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; @@ -930,7 +931,8 @@ static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) * * Returns: 0 for success */ -static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_destroy(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; @@ -974,7 +976,8 @@ static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) * * Returns: 0 for success */ -static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_disable(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; @@ -1008,7 +1011,8 @@ static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) * * Returns: 0 for success */ -static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_enable(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index f8dc1601efd5f1eb51b4d776087d6ea20534d09e..bddbe2da528340b930e93c49134b9ab7b5c15067 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1696,6 +1696,15 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) */ switch (session->state) { case ISCSI_STATE_FAILED: + /* + * cmds should fail during shutdown, if the session + * state is bad, allowing completion to happen + */ + if (unlikely(system_state != SYSTEM_RUNNING)) { + reason = FAILURE_SESSION_FAILED; + sc->result = DID_NO_CONNECT << 16; + break; + } case ISCSI_STATE_IN_RECOVERY: reason = FAILURE_SESSION_IN_RECOVERY; sc->result = DID_IMM_RETRY << 16; @@ -1980,6 +1989,19 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) } if (session->state != ISCSI_STATE_LOGGED_IN) { + /* + * During shutdown, if session is prematurely disconnected, + * recovery won't happen and there will be hung cmds. Not + * handling cmds would trigger EH, also bad in this case. + * Instead, handle cmd, allow completion to happen and let + * upper layer to deal with the result. + */ + if (unlikely(system_state != SYSTEM_RUNNING)) { + sc->result = DID_NO_CONNECT << 16; + ISCSI_DBG_EH(session, "sc on shutdown, handled\n"); + rc = BLK_EH_HANDLED; + goto done; + } /* * We are probably in the middle of iscsi recovery so let * that complete and handle the error. @@ -2084,7 +2106,7 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) task->last_timeout = jiffies; spin_unlock(&session->frwd_lock); ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ? - "timer reset" : "nh"); + "timer reset" : "shutdown or nh"); return rc; } EXPORT_SYMBOL_GPL(iscsi_eh_cmd_timed_out); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 324d8d8c62decb18ad57a9c24781efd10792d8ce..e2ea389fbec3793203ed6346cb6edfff78b957e5 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -293,6 +293,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) phy->phy->minimum_linkrate = dr->pmin_linkrate; phy->phy->maximum_linkrate = dr->pmax_linkrate; phy->phy->negotiated_linkrate = phy->linkrate; + phy->phy->enabled = (phy->linkrate != SAS_PHY_DISABLED); skip: if (new_phy) @@ -686,7 +687,7 @@ int sas_smp_get_phy_events(struct sas_phy *phy) res = smp_execute_task(dev, req, RPEL_REQ_SIZE, resp, RPEL_RESP_SIZE); - if (!res) + if (res) goto out; phy->invalid_dword_count = scsi_to_u32(&resp[12]); @@ -695,6 +696,7 @@ int sas_smp_get_phy_events(struct sas_phy *phy) phy->phy_reset_problem_count = scsi_to_u32(&resp[24]); out: + kfree(req); kfree(resp); return res; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index e518dadc81615fd4e981b9127e840b9c7adc2d2b..4beb4dd2bee84013d14635c84221c43826913b95 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -6605,7 +6605,6 @@ static void megasas_detach_one(struct pci_dev *pdev) u32 pd_seq_map_sz; instance = pci_get_drvdata(pdev); - instance->unload = 1; host = instance->host; fusion = instance->ctrl_context; @@ -6616,6 +6615,7 @@ static void megasas_detach_one(struct pci_dev *pdev) if (instance->fw_crash_state != UNAVAILABLE) megasas_free_host_crash_buffer(instance); scsi_remove_host(instance->host); + instance->unload = 1; if (megasas_wait_for_adapter_operational(instance)) goto skip_firing_dcmds; diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index ecc699a65bacb455061999f031465efe4be2b830..08945142b9f8ffd6a76096e2951930fdfee7b122 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -168,7 +168,7 @@ static struct MR_LD_SPAN *MR_LdSpanPtrGet(u32 ld, u32 span, /* * This function will Populate Driver Map using firmware raid map */ -void MR_PopulateDrvRaidMap(struct megasas_instance *instance) +static int MR_PopulateDrvRaidMap(struct megasas_instance *instance) { struct fusion_context *fusion = instance->ctrl_context; struct MR_FW_RAID_MAP_ALL *fw_map_old = NULL; @@ -259,7 +259,7 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) ld_count = (u16)le16_to_cpu(fw_map_ext->ldCount); if (ld_count > MAX_LOGICAL_DRIVES_EXT) { dev_dbg(&instance->pdev->dev, "megaraid_sas: LD count exposed in RAID map in not valid\n"); - return; + return 1; } pDrvRaidMap->ldCount = (__le16)cpu_to_le16(ld_count); @@ -285,6 +285,12 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) fusion->ld_map[(instance->map_id & 1)]; pFwRaidMap = &fw_map_old->raidMap; ld_count = (u16)le32_to_cpu(pFwRaidMap->ldCount); + if (ld_count > MAX_LOGICAL_DRIVES) { + dev_dbg(&instance->pdev->dev, + "LD count exposed in RAID map in not valid\n"); + return 1; + } + pDrvRaidMap->totalSize = pFwRaidMap->totalSize; pDrvRaidMap->ldCount = (__le16)cpu_to_le16(ld_count); pDrvRaidMap->fpPdIoTimeoutSec = pFwRaidMap->fpPdIoTimeoutSec; @@ -300,6 +306,8 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) sizeof(struct MR_DEV_HANDLE_INFO) * MAX_RAIDMAP_PHYSICAL_DEVICES); } + + return 0; } /* @@ -317,8 +325,8 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance) u16 ld; u32 expected_size; - - MR_PopulateDrvRaidMap(instance); + if (MR_PopulateDrvRaidMap(instance)) + return 0; fusion = instance->ctrl_context; drv_map = fusion->ld_drv_map[(instance->map_id & 1)]; diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 6efa739a19127dc47adedc8b4db811ab695a4278..508ae4bc5ab51399cd8c34a22721a75ff4734742 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -105,7 +105,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); * */ static int -_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) +_scsih_set_fwfault_debug(const char *val, const struct kernel_param *kp) { int ret = param_set_int(val, kp); struct MPT3SAS_ADAPTER *ioc; diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index beb4bf8fe9b08202ec313fe2af12080a762a634f..a1a7c45497f5fbef442dc95ea4dc2e70534eafbf 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -281,7 +281,7 @@ struct _scsi_io_transfer { * Note: The logging levels are defined in mpt3sas_debug.h. */ static int -_scsih_set_debug_level(const char *val, struct kernel_param *kp) +_scsih_set_debug_level(const char *val, const struct kernel_param *kp) { int ret = param_set_int(val, kp); struct MPT3SAS_ADAPTER *ioc; @@ -4106,19 +4106,6 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) return 0; } - /* - * Bug work around for firmware SATL handling. The loop - * is based on atomic operations and ensures consistency - * since we're lockless at this point - */ - do { - if (test_bit(0, &sas_device_priv_data->ata_command_pending)) { - scmd->result = SAM_STAT_BUSY; - scmd->scsi_done(scmd); - return 0; - } - } while (_scsih_set_satl_pending(scmd, true)); - sas_target_priv_data = sas_device_priv_data->sas_target; /* invalid device handle */ @@ -4144,6 +4131,19 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) sas_device_priv_data->block) return SCSI_MLQUEUE_DEVICE_BUSY; + /* + * Bug work around for firmware SATL handling. The loop + * is based on atomic operations and ensures consistency + * since we're lockless at this point + */ + do { + if (test_bit(0, &sas_device_priv_data->ata_command_pending)) { + scmd->result = SAM_STAT_BUSY; + scmd->scsi_done(scmd); + return 0; + } + } while (_scsih_set_satl_pending(scmd, true)); + if (scmd->sc_data_direction == DMA_FROM_DEVICE) mpi_control = MPI2_SCSIIO_CONTROL_READ; else if (scmd->sc_data_direction == DMA_TO_DEVICE) @@ -4170,6 +4170,7 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) if (!smid) { pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); + _scsih_set_satl_pending(scmd, false); goto out; } mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); @@ -4200,6 +4201,7 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) if (mpi_request->DataLength) { if (ioc->build_sg_scmd(ioc, scmd, smid)) { mpt3sas_base_free_smid(ioc, smid); + _scsih_set_satl_pending(scmd, false); goto out; } } else diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 2300c02ab5e698afe23f7ef8edded4c1c4e59e62..e24f57946a17249c9857b7a8c46fdf486c887d83 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -115,6 +115,8 @@ qla2x00_async_iocb_timeout(void *data) switch (sp->type) { case SRB_LOGIN_CMD: + if (!fcport) + break; /* Retry as needed. */ lio->u.logio.data[0] = MBS_COMMAND_ERROR; lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? @@ -128,6 +130,8 @@ qla2x00_async_iocb_timeout(void *data) qla24xx_handle_plogi_done_event(fcport->vha, &ea); break; case SRB_LOGOUT_CMD: + if (!fcport) + break; qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT); break; case SRB_CT_PTHRU_CMD: diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 1e17175692d3d7da1bfbbed630bf6f3057d283d1..8e7c0626f8b5fd743fbcca48dc7b385de39fb3bd 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -459,9 +459,6 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req, static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) { - if (!ha->req_q_map) - return; - if (IS_QLAFX00(ha)) { if (req && req->ring_fx00) dma_free_coherent(&ha->pdev->dev, @@ -472,17 +469,14 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) (req->length + 1) * sizeof(request_t), req->ring, req->dma); - if (req) { + if (req) kfree(req->outstanding_cmds); - kfree(req); - } + + kfree(req); } static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp) { - if (!ha->rsp_q_map) - return; - if (IS_QLAFX00(ha)) { if (rsp && rsp->ring) dma_free_coherent(&ha->pdev->dev, @@ -493,8 +487,7 @@ static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp) (rsp->length + 1) * sizeof(response_t), rsp->ring, rsp->dma); } - if (rsp) - kfree(rsp); + kfree(rsp); } static void qla2x00_free_queues(struct qla_hw_data *ha) @@ -3075,7 +3068,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) goto probe_failed; /* Alloc arrays of request and response ring ptrs */ - if (qla2x00_alloc_queues(ha, req, rsp)) { + ret = qla2x00_alloc_queues(ha, req, rsp); + if (ret) { ql_log(ql_log_fatal, base_vha, 0x003d, "Failed to allocate memory for queue pointers..." "aborting.\n"); @@ -3368,8 +3362,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) } qla2x00_free_device(base_vha); - scsi_host_put(base_vha->host); + /* + * Need to NULL out local req/rsp after + * qla2x00_free_device => qla2x00_free_queues frees + * what these are pointing to. Or else we'll + * fall over below in qla2x00_free_req/rsp_que. + */ + req = NULL; + rsp = NULL; probe_hw_failed: qla2x00_mem_free(ha); @@ -4062,6 +4063,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, (*rsp)->dma = 0; fail_rsp_ring: kfree(*rsp); + *rsp = NULL; fail_rsp: dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) * sizeof(request_t), (*req)->ring, (*req)->dma); @@ -4069,6 +4071,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, (*req)->dma = 0; fail_req_ring: kfree(*req); + *req = NULL; fail_req: dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt), ha->ct_sns, ha->ct_sns_dma); @@ -4436,16 +4439,11 @@ qla2x00_mem_free(struct qla_hw_data *ha) dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb, ha->init_cb_dma); - if (ha->optrom_buffer) - vfree(ha->optrom_buffer); - if (ha->nvram) - kfree(ha->nvram); - if (ha->npiv_info) - kfree(ha->npiv_info); - if (ha->swl) - kfree(ha->swl); - if (ha->loop_id_map) - kfree(ha->loop_id_map); + vfree(ha->optrom_buffer); + kfree(ha->nvram); + kfree(ha->npiv_info); + kfree(ha->swl); + kfree(ha->loop_id_map); ha->srb_mempool = NULL; ha->ctx_mempool = NULL; diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index cfc095f45e261d3abfbcbe3004047f7046800e88..ea947a7c25967cc9ac8305da885a5dc2e23adb99 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -109,8 +109,8 @@ static struct { * seagate controller, which causes SCSI code to reset bus. */ {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ - {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ - {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ + {"HP", "C1790A", NULL, BLIST_NOLUN}, /* scanjet iip */ + {"HP", "C2500A", NULL, BLIST_NOLUN}, /* scanjet iicx */ {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* locks up */ {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */ {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */ @@ -120,7 +120,7 @@ static struct { {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* locks up */ {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */ - {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, + {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN | BLIST_BORKEN}, {"transtec", "T5008", "0001", BLIST_NOREPORTLUN }, {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* locks up */ @@ -255,7 +255,6 @@ static struct { {"ST650211", "CF", NULL, BLIST_RETRY_HWERROR}, {"SUN", "T300", "*", BLIST_SPARSELUN}, {"SUN", "T4", "*", BLIST_SPARSELUN}, - {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN}, {"Tornado-", "F4", "*", BLIST_NOREPORTLUN}, {"TOSHIBA", "CDROM", NULL, BLIST_ISROM}, {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM}, diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 12928049ad29a1e7db5cbdfc5e50bdb4fcd87fd0..26e9dc2e0019b4005a2eac54bc741856cc54a803 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2057,6 +2057,8 @@ sd_spinup_disk(struct scsi_disk *sdkp) break; /* standby */ if (sshdr.asc == 4 && sshdr.ascq == 0xc) break; /* unavailable */ + if (sshdr.asc == 4 && sshdr.ascq == 0x1b) + break; /* sanitize in progress */ /* * Issue command to spin up drive when not ready */ diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c index 44b50c04e22cc7e43c760f5590a8c775f480ab4a..ee805a01c2f24b0e68e59d46315b8566eacf9a5e 100644 --- a/drivers/scsi/ufs/ufs-debugfs.c +++ b/drivers/scsi/ufs/ufs-debugfs.c @@ -100,6 +100,16 @@ static const int err_inject_query_err_codes[] = { 0xFF, }; +static const int err_inject_hibern8_err_codes[] = { + -EIO, + -ETIMEDOUT, + -1, + PWR_REMOTE, + PWR_BUSY, + PWR_ERROR_CAP, + PWR_FATAL_ERROR, +}; + static struct ufsdbg_err_scenario err_scen_arr[] = { { "ERR_INJECT_INTR", @@ -126,6 +136,16 @@ static struct ufsdbg_err_scenario err_scen_arr[] = { err_inject_query_err_codes, ARRAY_SIZE(err_inject_query_err_codes), }, + { + "ERR_INJECT_HIBERN8_ENTER", + err_inject_hibern8_err_codes, + ARRAY_SIZE(err_inject_hibern8_err_codes), + }, + { + "ERR_INJECT_HIBERN8_EXIT", + err_inject_hibern8_err_codes, + ARRAY_SIZE(err_inject_hibern8_err_codes), + }, }; static bool inject_fatal_err_tr(struct ufs_hba *hba, u8 ocs_err) @@ -281,6 +301,8 @@ void ufsdbg_error_inject_dispatcher(struct ufs_hba *hba, case ERR_INJECT_UIC: case ERR_INJECT_DME_ATTR: case ERR_INJECT_QUERY: + case ERR_INJECT_HIBERN8_ENTER: + case ERR_INJECT_HIBERN8_EXIT: goto should_fail; default: dev_err(hba->dev, "%s: unsupported error scenario\n", diff --git a/drivers/scsi/ufs/ufs-debugfs.h b/drivers/scsi/ufs/ufs-debugfs.h index bad918e12c59e8935f6f9bf405f2961ee1ca8bad..d8907a6827cd192b9e02d113f1c5aea8732e56e6 100644 --- a/drivers/scsi/ufs/ufs-debugfs.h +++ b/drivers/scsi/ufs/ufs-debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,8 @@ enum ufsdbg_err_inject_scenario { ERR_INJECT_UIC, ERR_INJECT_DME_ATTR, ERR_INJECT_QUERY, + ERR_INJECT_HIBERN8_ENTER, + ERR_INJECT_HIBERN8_EXIT, ERR_INJECT_MAX_ERR_SCENARIOS, }; diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 0fc6602bce11c2f432c053d6c017d52a51aedc85..841f01104ba8de76f9bde2ab24fb3d78d407360b 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -2325,6 +2325,9 @@ static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba) struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_pa_layer_attr *attr = &host->dev_req_params; int err = 0; + struct ufs_clk_info *clki; + struct list_head *head = &hba->clk_list_head; + u32 max_freq = 0; if (!ufs_qcom_cap_qunipro(host)) goto out; @@ -2333,8 +2336,25 @@ static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba) __ufs_qcom_cfg_timers(hba, attr->gear_rx, attr->pwr_rx, attr->hs_rate, false, true); - /* set unipro core clock cycles to 150 and clear clock divider */ - err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150, 6); + list_for_each_entry(clki, head, list) { + if (!IS_ERR_OR_NULL(clki->clk) && + (!strcmp(clki->name, "core_clk_unipro"))) { + max_freq = clki->max_freq; + break; + } + } + + switch (max_freq) { + case 300000000: + err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 300, 12); + break; + case 150000000: + err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150, 6); + break; + default: + err = -EINVAL; + break; + } out: return err; } @@ -2344,6 +2364,9 @@ static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba) struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_pa_layer_attr *attr = &host->dev_req_params; int err = 0; + struct ufs_clk_info *clki; + struct list_head *head = &hba->clk_list_head; + u32 curr_freq = 0; if (!ufs_qcom_cap_qunipro(host)) return 0; @@ -2352,18 +2375,25 @@ static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba) ufs_qcom_cfg_timers(hba, attr->gear_rx, attr->pwr_rx, attr->hs_rate, false); - if (ufs_qcom_cap_svs2(host)) - /* - * For SVS2 set unipro core clock cycles to 38 and - * clear clock divider - */ + list_for_each_entry(clki, head, list) { + if (!IS_ERR_OR_NULL(clki->clk) && + (!strcmp(clki->name, "core_clk_unipro"))) { + curr_freq = clk_get_rate(clki->clk); + break; + } + } + + switch (curr_freq) { + case 37500000: err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 38, 2); - else - /* - * For SVS set unipro core clock cycles to 75 and - * clear clock divider - */ + break; + case 75000000: err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 75, 3); + break; + default: + err = -EINVAL; + break; + } return err; } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b1f6a91e860e83779215b1a4062c73285060aaac..f34cec37711cfc7710715773cde518c759948230 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -753,9 +753,6 @@ static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, sector_t lba = 0; int transfer_len = 0; - if (!trace_ufshcd_command_enabled()) - return; - lrbp = &hba->lrb[tag]; if (lrbp->cmd) { /* data phase exists */ @@ -3703,7 +3700,6 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) if (err) { err = SCSI_MLQUEUE_HOST_BUSY; clear_bit_unlock(tag, &hba->lrb_in_use); - ufshcd_release_all(hba); goto out; } if (ufshcd_is_clkgating_allowed(hba)) @@ -5194,6 +5190,8 @@ static int __ufshcd_uic_hibern8_enter(struct ufs_hba *hba) trace_ufshcd_profile_hibern8(dev_name(hba->dev), "enter", ktime_to_us(ktime_sub(ktime_get(), start)), ret); + ufsdbg_error_inject_dispatcher(hba, ERR_INJECT_HIBERN8_ENTER, 0, &ret); + /* * Do full reinit if enter failed or if LINERESET was detected during * Hibern8 operation. After LINERESET, link moves to default PWM-G1 @@ -5259,6 +5257,8 @@ int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) trace_ufshcd_profile_hibern8(dev_name(hba->dev), "exit", ktime_to_us(ktime_sub(ktime_get(), start)), ret); + ufsdbg_error_inject_dispatcher(hba, ERR_INJECT_HIBERN8_EXIT, 0, &ret); + /* Do full reinit if exit failed */ if (ret) { ufshcd_update_error_stats(hba, UFS_ERR_HIBERN8_EXIT); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 4e96e3994756b7394ad24858e597f2846bdfb174..388f3d46c9c4d45489ddcfc19cc85103a14a15bc 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -892,10 +892,10 @@ struct ufs_hba { #define UFSHCD_BROKEN_LCC_PROCESSING_ON_HOST UFS_BIT(13) - #define UFSHCD_QUIRK_DME_PEER_GET_FAST_MODE UFS_BIT(8) + #define UFSHCD_QUIRK_DME_PEER_GET_FAST_MODE UFS_BIT(14) /* Auto hibern8 support is broken */ - #define UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8 UFS_BIT(9) + #define UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8 UFS_BIT(15) unsigned int quirks; /* Deviations from standard UFSHCI spec. */ diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index e1ce8b1b5090aa0a7b91b6abcd73ad7fa0760261..fb2a8b1e7979169c74d56badcb296fe774938ae8 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -892,7 +892,7 @@ static int scpsys_probe(struct platform_device *pdev) pd_data = &scp->pd_data; - for (i = 0, sd = soc->subdomains ; i < soc->num_subdomains ; i++) { + for (i = 0, sd = soc->subdomains; i < soc->num_subdomains; i++, sd++) { ret = pm_genpd_add_subdomain(pd_data->domains[sd->origin], pd_data->domains[sd->subdomain]); if (ret && IS_ENABLED(CONFIG_PM)) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 5f7e37bcb8ac9afd753b536988773d91703a0b17..6022afe7776aa54f997bfd145d47b4ae665d54d6 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -67,6 +67,14 @@ config QCOM_SDMSHRIKE_LLCC data required to configure LLCC so that clients can start using the LLCC slices. +config QCOM_SM6150_LLCC + tristate "Qualcomm Technologies, Inc. SM6150 LLCC driver" + depends on QCOM_LLCC + help + Say yes here to enable the LLCC driver for SM6150. This is provides + data required to configure LLCC so that clients can start using the + LLCC slices. + config QCOM_LLCC_AMON tristate "Qualcomm Technologies, Inc. LLCC Activity Monitor(AMON) driver" depends on QCOM_LLCC @@ -382,6 +390,31 @@ config QCOM_EUD Embedded USB Debugger (EUD). If unsure, say N. +config QCOM_MINIDUMP + bool "QCOM Minidump Support" + depends on QCOM_SMEM && QCOM_DLOAD_MODE + help + This enables minidump feature. It allows various clients to + register to dump their state at system bad state (panic/WDT,etc.,). + Minidump would dump all registered entries, only when DLOAD mode + is enabled. + +config MINIDUMP_MAX_ENTRIES + int "Minidump Maximum num of entries" + default 200 + depends on QCOM_MINIDUMP + help + This defines maximum number of entries to be allocated for application + subsytem in Minidump table. +config MSM_RPM_SMD + bool "RPM driver using SMD protocol" + help + RPM is the dedicated hardware engine for managing shared SoC + resources. This config adds driver support for using SMD as a + transport layer communication with RPM hardware. It also selects + the MSM_MPM config that programs the MPM module to monitor interrupts + during sleep modes. + config QCOM_BUS_SCALING bool "Bus scaling driver" help @@ -424,6 +457,19 @@ config MSM_SPSS_UTILS because the SPSS firmware size is too small to support multiple HW versions. +config MSM_SPCOM + depends on QCOM_GLINK + bool "Secure Processor Communication over GLINK" + help + spcom driver allows loading Secure Processor Applications and + sending messages to Secure Processor Applications. + spcom provides interface to both user space app and kernel driver. + It is using glink as the transport layer, which provides multiple + logical channels over single physical channel. + The physical layer is based on shared memory and interrupts. + spcom provides clients/server API, although currently only one client + or server is allowed per logical channel. + config QTI_RPMH_API bool "QTI RPMH (h/w accelerators) Communication API" select MAILBOX @@ -445,6 +491,16 @@ config QSEE_IPC_IRQ Clients can use this driver to avoid adding common interrupt handling code. +config QSEE_IPC_IRQ_BRIDGE + tristate "QSEE IPC Interrupt Bridge" + select QSEE_IPC_IRQ + help + This module enables bridging an Inter-Processor Communication(IPC) + interrupt from a remote subsystem directed towards Qualcomm + Technologies, Inc. Secure Execution Environment(QSEE) to userspace. + The interrupt will be propagated through a character device that + userspace clients can poll on. + config QCOM_GLINK tristate "GLINK Probe Helper" depends on RPMSG_QCOM_GLINK_SMEM @@ -499,6 +555,14 @@ config MSM_CDSP_LOADER for platforms that have compute DSP. Say M if you want to enable this module. +config QCOM_SMCINVOKE + bool "Secure QSEE Support" + help + Enable SMCInvoke driver which supports capability based secure + communication between QTI Secure Execution Environment (QSEE) + and high level operating system. It exposes APIs for both + userspace and kernel clients. + config MSM_EVENT_TIMER bool "Event timer" help @@ -526,6 +590,15 @@ config MSM_NOPM This enables bare minimum support of power management at platform level. i.e WFI +config MSM_QBT1000 + bool "QBT1000 Ultrasonic Fingerprint Sensor" + help + This driver provides services for configuring the fingerprint + sensor hardware and for communicating with the trusted app which + uses it. It enables clocks and provides commands for loading + trusted apps, unloading them and marshalling buffers to the + trusted fingerprint app. + config APSS_CORE_EA depends on CPU_FREQ && PM_OPP bool "Qualcomm Technology Inc specific power aware driver" @@ -595,4 +668,21 @@ config QMP_DEBUGFS_CLIENT help This options enables a driver which allows clients to send messages to Alway On processor using QMP transport. + +config QCOM_QHEE_ENABLE_MEM_PROTECTION + bool "QHEE enable kernel memory protection" + depends on QCOM_SCM + default y + help + When this option is enabled, an SCM call will be invoked to enable + kernel memory protection in stage 2 memory mappings on kernel boot. + This is part of a security feature enabled in QHEE. + +config QCOM_SMP2P_SLEEPSTATE + bool "SMP2P Sleepstate notifier" + depends on QCOM_SMP2P + help + When this option is enabled, notifications are sent to remote procs + for the power state changes on the local processor. The notifications + are sent through the smp2p framework. endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 7a02a524f99faf353f5f0098e16c67a57e5b5b8c..f5a27a569877fa62e4c2283593667142114fb569 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_LLCC) += llcc-core.o llcc-slice.o obj-$(CONFIG_QCOM_SM8150_LLCC) += llcc-sm8150.o obj-$(CONFIG_QCOM_SDMSHRIKE_LLCC) += llcc-sdmshrike.o +obj-$(CONFIG_QCOM_SM6150_LLCC) += llcc-sm6150.o obj-$(CONFIG_QCOM_LLCC_AMON) += llcc-amon.o obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o @@ -27,6 +28,7 @@ obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o obj-$(CONFIG_QCOM_DCC_V2) += dcc_v2.o obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o obj-$(CONFIG_MSM_GLADIATOR_ERP) += gladiator_erp.o +obj-$(CONFIG_QCOM_MINIDUMP) += msm_minidump.o minidump_log.o obj-$(CONFIG_QCOM_SECURE_BUFFER) += secure_buffer.o obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o @@ -42,6 +44,7 @@ obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) += memshare/ obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o obj-$(CONFIG_MSM_PIL) += peripheral-loader.o obj-$(CONFIG_QCOM_RUN_QUEUE_STATS) += rq_stats.o +obj-$(CONFIG_MSM_SPCOM) += spcom.o obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/ obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QTI_RPMH_API) += rpmh.o @@ -49,6 +52,7 @@ obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o obj-$(MSM_REMOTEQDSS) += remoteqdss.o obj-$(CONFIG_MSM_JTAGV8) += jtagv8.o jtagv8-etm.o obj-$(CONFIG_MSM_CDSP_LOADER) += qdsp6v2/ +obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += subsystem_notif.o @@ -57,12 +61,22 @@ ifdef CONFIG_MSM_SUBSYSTEM_RESTART endif obj-$(CONFIG_QCOM_EUD) += eud.o obj-$(CONFIG_QSEE_IPC_IRQ) += qsee_ipc_irq.o +obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o obj-$(CONFIG_QCOM_GLINK) += glink_probe.o obj-$(CONFIG_QCOM_GLINK_PKT) += glink_pkt.o obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o +obj-$(CONFIG_MSM_QBT1000) += qbt1000.o obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpm_stats.o obj-$(CONFIG_QCOM_FSA4480_I2C) += fsa4480-i2c.o +ifdef CONFIG_QTI_RPMH_API + obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpmh_master_stat.o +endif obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o +obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd.o +ifdef CONFIG_DEBUG_FS +obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd-debug.o +endif +obj-$(CONFIG_QCOM_SMP2P_SLEEPSTATE) += smp2p_sleepstate.o diff --git a/drivers/soc/qcom/cpuss_dump.c b/drivers/soc/qcom/cpuss_dump.c index 886a32fcf131914139efa5b37d0449cb7ff9cb05..eba11283bea375640d2307f0a1ec9dc074d01530 100644 --- a/drivers/soc/qcom/cpuss_dump.c +++ b/drivers/soc/qcom/cpuss_dump.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -74,6 +74,8 @@ static int cpuss_dump_probe(struct platform_device *pdev) dump_data->addr = dump_addr; dump_data->len = size; + scnprintf(dump_data->name, sizeof(dump_data->name), + "KCPUSS%X", id); dump_entry.id = id; dump_entry.addr = virt_to_phys(dump_data); ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); diff --git a/drivers/soc/qcom/fsa4480-i2c.c b/drivers/soc/qcom/fsa4480-i2c.c index 626e9832b55775d95725103c629b94b46f7e4283..ad4b233b2054215bac54673e4fe730c43802e201 100644 --- a/drivers/soc/qcom/fsa4480-i2c.c +++ b/drivers/soc/qcom/fsa4480-i2c.c @@ -65,6 +65,7 @@ static const struct fsa4480_reg_val fsa_reg_i2c_defaults[] = { {FSA4480_DELAY_L_MIC, 0x00}, {FSA4480_DELAY_L_SENSE, 0x00}, {FSA4480_DELAY_L_AGND, 0x09}, + {FSA4480_SWITCH_SETTINGS, 0x98}, }; static void fsa4480_usbc_update_settings(struct fsa4480_priv *fsa_priv, @@ -193,7 +194,7 @@ int fsa4480_unreg_notifier(struct notifier_block *nb, if (!fsa_priv) return -EINVAL; - fsa4480_usbc_update_settings(fsa_priv, 0x18, 0xF8); + fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); return blocking_notifier_chain_unregister (&fsa_priv->fsa4480_notifier, nb); } @@ -253,6 +254,9 @@ int fsa4480_switch_event(struct device_node *node, case FSA_USBC_ORIENTATION_CC2: fsa4480_usbc_update_settings(fsa_priv, 0x60, 0xE0); return fsa4480_validate_display_port_settings(fsa_priv); + case FSA_USBC_DISPLAYPORT_DISCONNECTED: + fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); + break; default: break; } @@ -294,7 +298,7 @@ static int fsa4480_usbc_analog_setup_switches POWER_SUPPLY_TYPEC_NONE, NULL); /* deactivate switches */ - fsa4480_usbc_update_settings(fsa_priv, 0x18, 0xF8); + fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); if (fsa_priv->usbc_force_pr_mode) { pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL; @@ -405,7 +409,7 @@ static int fsa4480_remove(struct i2c_client *i2c) if (!fsa_priv) return -EINVAL; - fsa4480_usbc_update_settings(fsa_priv, 0x18, 0xF8); + fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98); /* deregister from PMI */ power_supply_unreg_notifier(&fsa_priv->psy_nb); diff --git a/drivers/soc/qcom/glink_pkt.c b/drivers/soc/qcom/glink_pkt.c index aa95d72d0980568159b5bc9e43b292277ad46415..3d3766b90443f75472ae4ab0ae468d3ba3beee16 100644 --- a/drivers/soc/qcom/glink_pkt.c +++ b/drivers/soc/qcom/glink_pkt.c @@ -308,7 +308,8 @@ int glink_pkt_release(struct inode *inode, struct file *file) gpdev->ch_name, current->comm, task_pid_nr(current), refcount_read(&gpdev->refcount)); - if (refcount_dec_and_test(&gpdev->refcount)) { + refcount_dec(&gpdev->refcount); + if (refcount_read(&gpdev->refcount) == 1) { spin_lock_irqsave(&gpdev->queue_lock, flags); /* Discard all SKBs */ @@ -345,16 +346,14 @@ ssize_t glink_pkt_read(struct file *file, char __user *buf, struct sk_buff *skb; int use; - if (!gpdev || !refcount_read(&gpdev->refcount)) { + if (!gpdev || refcount_read(&gpdev->refcount) == 1) { GLINK_PKT_ERR("invalid device handle\n", __func__); return -EINVAL; } if (!completion_done(&gpdev->ch_open)) { GLINK_PKT_ERR("%s channel in reset\n", gpdev->ch_name); - mutex_unlock(&gpdev->lock); return -ENETRESET; - } GLINK_PKT_INFO("begin for %s by %s:%ld ref_cnt[%d]\n", @@ -418,7 +417,7 @@ ssize_t glink_pkt_write(struct file *file, const char __user *buf, int ret; gpdev = file->private_data; - if (!gpdev || !refcount_read(&gpdev->refcount)) { + if (!gpdev || refcount_read(&gpdev->refcount) == 1) { GLINK_PKT_ERR("invalid device handle\n", __func__); return -EINVAL; } @@ -468,7 +467,7 @@ static unsigned int glink_pkt_poll(struct file *file, poll_table *wait) unsigned long flags; gpdev = file->private_data; - if (!gpdev || !refcount_read(&gpdev->refcount)) { + if (!gpdev || refcount_read(&gpdev->refcount) == 1) { GLINK_PKT_ERR("invalid device handle\n", __func__); return POLLERR; } @@ -563,7 +562,7 @@ static long glink_pkt_ioctl(struct file *file, unsigned int cmd, int ret; gpdev = file->private_data; - if (!gpdev || !refcount_read(&gpdev->refcount)) { + if (!gpdev || refcount_read(&gpdev->refcount) == 1) { GLINK_PKT_ERR("invalid device handle\n", __func__); return -EINVAL; } @@ -735,7 +734,7 @@ static int glink_pkt_create_device(struct device *parent, dev = &gpdev->dev; mutex_init(&gpdev->lock); - refcount_set(&gpdev->refcount, 0); + refcount_set(&gpdev->refcount, 1); init_completion(&gpdev->ch_open); /* Default open timeout for open is 120 sec */ diff --git a/drivers/soc/qcom/glink_probe.c b/drivers/soc/qcom/glink_probe.c index 5d32a6b686e09e4138041864968bc58b548a6647..89655eda90d04f1055eaf40a4fef6e54c9bba118 100644 --- a/drivers/soc/qcom/glink_probe.c +++ b/drivers/soc/qcom/glink_probe.c @@ -11,47 +11,393 @@ */ #include +#include +#include #include +#include #include +#include #include +#include +#include -#define NUM_SUBSYSTEMS 10 -static struct qcom_glink *edge_infos[NUM_SUBSYSTEMS]; +#define GLINK_PROBE_LOG_PAGE_CNT 4 +static void *glink_ilc; + +#define GLINK_INFO(x, ...) \ +do { \ + if (glink_ilc) \ + ipc_log_string(glink_ilc, "[%s]: "x, __func__, ##__VA_ARGS__); \ +} while (0) + +#define GLINK_ERR(dev, x, ...) \ +do { \ + dev_err(dev, "[%s]: "x, __func__, ##__VA_ARGS__); \ + if (glink_ilc) \ + ipc_log_string(glink_ilc, "[%s]: "x, __func__, ##__VA_ARGS__); \ +} while (0) + + +#define GLINK_SSR_DO_CLEANUP 0 +#define GLINK_SSR_CLEANUP_DONE 1 +#define GLINK_SSR_PRIORITY 1 +#define GLINK_SSR_REPLY_TIMEOUT HZ + +struct do_cleanup_msg { + __le32 version; + __le32 command; + __le32 seq_num; + __le32 name_len; + char name[32]; +}; + +struct cleanup_done_msg { + __le32 version; + __le32 response; + __le32 seq_num; +}; + +struct glink_ssr_nb { + struct list_head list; + struct glink_ssr *ssr; + void *ssr_register_handle; + + const char *glink_label; + const char *ssr_label; + + struct notifier_block nb; +}; + +struct glink_ssr { + struct device *dev; + struct rpmsg_endpoint *ept; + + struct list_head notify_list; + + u32 seq_num; + struct completion completion; +}; + +struct edge_info { + struct list_head list; + struct device *dev; + struct device_node *node; + + const char *glink_label; + const char *ssr_label; + void *glink; + + int (*register_fn)(struct edge_info *); + void (*unregister_fn)(struct edge_info *); + struct notifier_block nb; +}; +LIST_HEAD(edge_infos); + +static int glink_ssr_ssr_cb(struct notifier_block *this, + unsigned long code, void *data) +{ + struct glink_ssr_nb *nb = container_of(this, struct glink_ssr_nb, nb); + struct glink_ssr *ssr = nb->ssr; + struct device *dev = ssr->dev; + struct do_cleanup_msg msg; + int ret; + + if (code == SUBSYS_AFTER_SHUTDOWN) { + ssr->seq_num++; + reinit_completion(&ssr->completion); + + memset(&msg, 0, sizeof(msg)); + msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP); + msg.seq_num = cpu_to_le32(ssr->seq_num); + msg.name_len = cpu_to_le32(strlen(nb->glink_label)); + strlcpy(msg.name, nb->glink_label, sizeof(msg.name)); + + GLINK_INFO("%s: notify of %s seq_num:%d\n", + dev->parent->of_node->name, nb->glink_label, + ssr->seq_num); + + ret = rpmsg_send(ssr->ept, &msg, sizeof(msg)); + if (ret) + GLINK_ERR(dev, "fail to send do cleanup to %s %d\n", + nb->ssr_label, ret); + + ret = wait_for_completion_timeout(&ssr->completion, HZ); + if (!ret) + GLINK_ERR(dev, "timeout waiting for cleanup resp\n"); + + } + return NOTIFY_DONE; +} + +static int glink_ssr_callback(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 addr) +{ + struct cleanup_done_msg *msg = data; + struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); + + if (len < sizeof(*msg)) { + GLINK_ERR(ssr->dev, "message too short\n"); + return -EINVAL; + } + + if (le32_to_cpu(msg->version) != 0) { + GLINK_ERR(ssr->dev, "invalid version\n"); + return -EINVAL; + } + + if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE) + return 0; + + if (le32_to_cpu(msg->seq_num) != ssr->seq_num) { + GLINK_ERR(ssr->dev, "invalid response sequence number %d\n", + msg->seq_num); + return -EINVAL; + } + + complete(&ssr->completion); + + GLINK_INFO("%s: received seq_num:%d\n", ssr->dev->parent->of_node->name, + le32_to_cpu(msg->seq_num)); + + return 0; +} + +static void glink_ssr_init_notify(struct glink_ssr *ssr) +{ + struct device *dev = ssr->dev; + struct device_node *node; + struct glink_ssr_nb *nb; + void *handle; + int ret; + int i = 0; + + while (1) { + node = of_parse_phandle(dev->of_node, "qcom,notify-edges", i++); + if (!node) + break; + + nb = devm_kzalloc(dev, sizeof(*nb), GFP_KERNEL); + if (!nb) + return; + + ret = of_property_read_string(node, "label", &nb->ssr_label); + if (ret < 0) + nb->ssr_label = node->name; + + ret = of_property_read_string(node, "qcom,glink-label", + &nb->glink_label); + if (ret < 0) { + GLINK_ERR(dev, "no qcom,glink-label for %s\n", + nb->ssr_label); + continue; + } + + nb->nb.notifier_call = glink_ssr_ssr_cb; + nb->nb.priority = GLINK_SSR_PRIORITY; + + handle = subsys_notif_register_notifier(nb->ssr_label, &nb->nb); + if (IS_ERR_OR_NULL(handle)) { + GLINK_ERR(dev, "register fail for %s SSR notifier\n", + nb->ssr_label); + continue; + } + + nb->ssr = ssr; + nb->ssr_register_handle = handle; + list_add_tail(&nb->list, &ssr->notify_list); + } +} + +static int glink_ssr_probe(struct rpmsg_device *rpdev) +{ + struct glink_ssr *ssr; + + ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL); + if (!ssr) + return -ENOMEM; + + INIT_LIST_HEAD(&ssr->notify_list); + init_completion(&ssr->completion); + + ssr->dev = &rpdev->dev; + ssr->ept = rpdev->ept; + + glink_ssr_init_notify(ssr); + + dev_set_drvdata(&rpdev->dev, ssr); + + return 0; +} + +static void glink_ssr_remove(struct rpmsg_device *rpdev) +{ + struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); + struct glink_ssr_nb *nb; + + list_for_each_entry(nb, &ssr->notify_list, list) { + subsys_notif_unregister_notifier(nb->ssr_register_handle, + &nb->nb); + } + + dev_set_drvdata(&rpdev->dev, NULL); +} + +static const struct rpmsg_device_id glink_ssr_match[] = { + { "glink_ssr" }, + {} +}; + +static struct rpmsg_driver glink_ssr_driver = { + .probe = glink_ssr_probe, + .remove = glink_ssr_remove, + .callback = glink_ssr_callback, + .id_table = glink_ssr_match, + .drv = { + .name = "glink_ssr", + }, +}; +module_rpmsg_driver(glink_ssr_driver); + +static int glink_probe_ssr_cb(struct notifier_block *this, + unsigned long code, void *data) +{ + struct edge_info *einfo = container_of(this, struct edge_info, nb); + + GLINK_INFO("received %d for %s", code, einfo->ssr_label); + + switch (code) { + case SUBSYS_AFTER_POWERUP: + einfo->register_fn(einfo); + break; + case SUBSYS_AFTER_SHUTDOWN: + einfo->unregister_fn(einfo); + break; + default: + break; + } + return NOTIFY_DONE; +} + +static int glink_probe_smem_reg(struct edge_info *einfo) +{ + struct device *dev = einfo->dev; + + einfo->glink = qcom_glink_smem_register(dev, einfo->node); + if (IS_ERR_OR_NULL(einfo->glink)) { + GLINK_ERR(dev, "register failed for %s\n", einfo->ssr_label); + einfo->glink = NULL; + } + GLINK_INFO("register successful for %s\n", einfo->ssr_label); + + return 0; +} + +static void glink_probe_smem_unreg(struct edge_info *einfo) +{ + if (einfo->glink) + qcom_glink_smem_unregister(einfo->glink); + + einfo->glink = NULL; + GLINK_INFO("unregister for %s\n", einfo->ssr_label); + +} + +static int glink_probe_spss_reg(struct edge_info *einfo) +{ + struct device *dev = einfo->dev; + + einfo->glink = qcom_glink_spss_register(dev, einfo->node); + if (IS_ERR_OR_NULL(einfo->glink)) { + GLINK_ERR(dev, "register failed for %s\n", einfo->ssr_label); + einfo->glink = NULL; + } + GLINK_INFO("register successful for %s\n", einfo->ssr_label); + + return 0; +} + +static void glink_probe_spss_unreg(struct edge_info *einfo) +{ + if (einfo->glink) + qcom_glink_spss_unregister(einfo->glink); + + einfo->glink = NULL; + GLINK_INFO("unregister for %s\n", einfo->ssr_label); +} static void probe_subsystem(struct device *dev, struct device_node *np) { - struct qcom_glink *glink = ERR_PTR(-EINVAL); + struct edge_info *einfo; const char *transport; - u32 pid; + void *handle; int ret; - ret = of_property_read_u32(np, "qcom,remote-pid", &pid); - if (ret || pid >= NUM_SUBSYSTEMS) { - dev_err(dev, "invalid pid:%d ret:%d\n", pid, ret); + einfo = devm_kzalloc(dev, sizeof(*einfo), GFP_KERNEL); + if (!einfo) return; + + ret = of_property_read_string(np, "label", &einfo->ssr_label); + if (ret < 0) + einfo->ssr_label = np->name; + + ret = of_property_read_string(np, "qcom,glink-label", + &einfo->glink_label); + if (ret < 0) { + GLINK_ERR(dev, "no qcom,glink-label for %s\n", + einfo->ssr_label); + goto free_einfo; } + einfo->dev = dev; + einfo->node = np; + ret = of_property_read_string(np, "transport", &transport); if (ret < 0) { - dev_err(dev, "missing transport pid:%d\n", pid); - return; + GLINK_ERR(dev, "%s missing transport\n", einfo->ssr_label); + goto free_einfo; } - if (!strcmp(transport, "smem")) - glink = qcom_glink_smem_register(dev, np); - else if (!strcmp(transport, "spss")) - glink = qcom_glink_spss_register(dev, np); - if (IS_ERR(glink)) { - dev_err(dev, "%s failed %d\n", np->name, PTR_ERR(glink)); + if (!strcmp(transport, "smem")) { + einfo->register_fn = glink_probe_smem_reg; + einfo->unregister_fn = glink_probe_smem_unreg; + } else if (!strcmp(transport, "spss")) { + einfo->register_fn = glink_probe_spss_reg; + einfo->unregister_fn = glink_probe_spss_unreg; + } else if (!strcmp(transport, "spi")) { + /* SPI SSR is self contained */ + einfo->glink = qcom_glink_spi_register(dev, np); + if (IS_ERR_OR_NULL(einfo->glink)) { + GLINK_ERR(dev, "%s failed\n", einfo->ssr_label); + goto free_einfo; + } + list_add_tail(&einfo->list, &edge_infos); return; } - edge_infos[pid] = glink; + + einfo->nb.notifier_call = glink_probe_ssr_cb; + + handle = subsys_notif_register_notifier(einfo->ssr_label, &einfo->nb); + if (IS_ERR_OR_NULL(handle)) { + GLINK_ERR(dev, "could not register for SSR notifier for %s\n", + einfo->ssr_label); + goto free_einfo; + } + + list_add_tail(&einfo->list, &edge_infos); + GLINK_INFO("probe successful for %s\n", einfo->ssr_label); + + return; + +free_einfo: + devm_kfree(dev, einfo); + return; } static int glink_probe(struct platform_device *pdev) { - struct device_node *cn, *pn = pdev->dev.of_node; + struct device_node *pn = pdev->dev.of_node; + struct device_node *cn; for_each_available_child_of_node(pn, cn) { probe_subsystem(&pdev->dev, cn); @@ -77,6 +423,9 @@ static int __init glink_probe_init(void) { int ret; + glink_ilc = ipc_log_context_create(GLINK_PROBE_LOG_PAGE_CNT, + "glink_probe", 0); + ret = platform_driver_register(&glink_probe_driver); if (ret) { pr_err("%s: glink_probe register failed %d\n", diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index f17abed813ede873f684c1a3c622a23e59af3e80..759db12ba16497db71104552e6f3269188931b5c 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -272,6 +272,8 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "UNREGISTER_DRIVER"; case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN: return "PD_SERVICE_DOWN"; + case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND: + return "FW_EARLY_CRASH_IND"; case ICNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } @@ -633,7 +635,24 @@ static irqreturn_t fw_error_fatal_handler(int irq, void *ctx) return IRQ_HANDLED; } -static void icnss_register_force_error_fatal(struct icnss_priv *priv) +static irqreturn_t fw_crash_indication_handler(int irq, void *ctx) +{ + struct icnss_priv *priv = ctx; + + icnss_pr_err("Received early crash indication from FW\n"); + + if (priv) { + set_bit(ICNSS_FW_DOWN, &priv->state); + icnss_ignore_fw_timeout(true); + } + + icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND, + 0, NULL); + + return IRQ_HANDLED; +} + +static void register_fw_error_notifications(struct icnss_priv *priv) { int gpio, irq, ret; @@ -656,11 +675,38 @@ static void icnss_register_force_error_fatal(struct icnss_priv *priv) ret = request_irq(irq, fw_error_fatal_handler, IRQF_TRIGGER_RISING, "wlanfw-err", priv); if (ret < 0) { - icnss_pr_err("Unable to regiser for error fatal IRQ handler %d", + icnss_pr_err("Unable to register for error fatal IRQ handler %d", irq); return; } icnss_pr_dbg("FW force error fatal handler registered\n"); + + if (!of_find_property(priv->pdev->dev.of_node, + "qcom,gpio-early-crash-ind", NULL)) { + icnss_pr_dbg("FW early crash indication handler not registered\n"); + return; + } + gpio = of_get_named_gpio(priv->pdev->dev.of_node, + "qcom,gpio-early-crash-ind", 0); + if (!gpio_is_valid(gpio)) { + icnss_pr_err("Invalid GPIO for early crash indication %d\n", + gpio); + return; + } + irq = gpio_to_irq(gpio); + if (irq < 0) { + icnss_pr_err("Invalid IRQ for early crash indication %u\n", + irq); + return; + } + ret = request_irq(irq, fw_crash_indication_handler, + IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind", priv); + if (ret < 0) { + icnss_pr_err("Unable to register for early crash indication IRQ handler %d", + irq); + return; + } + icnss_pr_dbg("FW crash indication handler registered\n"); } int icnss_call_driver_uevent(struct icnss_priv *priv, @@ -733,7 +779,7 @@ static int icnss_driver_event_server_arrive(void *data) wlfw_dynamic_feature_mask_send_sync_msg(penv, dynamic_feature_mask); - icnss_register_force_error_fatal(penv); + register_fw_error_notifications(penv); return ret; @@ -825,6 +871,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) icnss_call_driver_shutdown(priv); clear_bit(ICNSS_PD_RESTART, &priv->state); + priv->early_crash_ind = false; if (!priv->ops || !priv->ops->reinit) goto out; @@ -979,7 +1026,7 @@ static int icnss_fw_crashed(struct icnss_priv *priv, if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL); - if (event_data->fw_rejuvenate) + if (event_data && event_data->fw_rejuvenate) wlfw_rejuvenate_ack_send_sync_msg(priv); return 0; @@ -994,6 +1041,12 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) goto out; + if (priv->early_crash_ind) { + icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n", + event_data->crashed, priv->state); + goto out; + } + if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) { icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n", event_data->crashed, priv->state); @@ -1015,6 +1068,25 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, return ret; } +static int icnss_driver_event_early_crash_ind(struct icnss_priv *priv, + void *data) +{ + int ret = 0; + + if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) + goto out; + + priv->early_crash_ind = true; + icnss_fw_crashed(priv, NULL); + +out: + kfree(data); + icnss_ignore_fw_timeout(false); + + return ret; +} + + static void icnss_driver_event_work(struct work_struct *work) { struct icnss_driver_event *event; @@ -1056,6 +1128,10 @@ static void icnss_driver_event_work(struct work_struct *work) ret = icnss_driver_event_pd_service_down(penv, event->data); break; + case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND: + ret = icnss_driver_event_early_crash_ind(penv, + event->data); + break; default: icnss_pr_err("Invalid Event type: %d", event->type); kfree(event); @@ -1655,6 +1731,12 @@ int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode) if (!dev) return -ENODEV; + if (test_bit(ICNSS_FW_DOWN, &penv->state)) { + icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n", + penv->state); + return -EINVAL; + } + icnss_pr_dbg("FW log mode: %u\n", fw_log_mode); ret = wlfw_ini_send_sync_msg(penv, fw_log_mode); @@ -1741,6 +1823,12 @@ int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config, enum icnss_driver_mode mode, const char *host_version) { + if (test_bit(ICNSS_FW_DOWN, &penv->state)) { + icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n", + penv->state); + return -EINVAL; + } + return icnss_send_wlan_enable_to_fw(penv, config, mode, host_version); } EXPORT_SYMBOL(icnss_wlan_enable); diff --git a/drivers/soc/qcom/icnss_private.h b/drivers/soc/qcom/icnss_private.h index 7e77037c9248a0605eca265f71036915c30f4afa..38b0fcf1ee0aea58fcda884c1836791f727d3202 100644 --- a/drivers/soc/qcom/icnss_private.h +++ b/drivers/soc/qcom/icnss_private.h @@ -111,6 +111,7 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_REGISTER_DRIVER, ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, + ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND, ICNSS_DRIVER_EVENT_MAX, }; @@ -340,6 +341,7 @@ struct icnss_priv { bool bypass_s1_smmu; bool force_err_fatal; bool allow_recursive_recovery; + bool early_crash_ind; u8 cause_for_rejuvenation; u8 requesting_sub_system; u16 line_number; diff --git a/drivers/soc/qcom/llcc-sm6150.c b/drivers/soc/qcom/llcc-sm6150.c new file mode 100644 index 0000000000000000000000000000000000000000..b6dfbd3d55d42c0b98f93412fa3b65de8aabbb9c --- /dev/null +++ b/drivers/soc/qcom/llcc-sm6150.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2018, 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 +#include +#include +#include +#include + +/* + * SCT entry contains of the following parameters + * name: Name of the client's use case for which the llcc slice is used + * uid: Unique id for the client's use case + * slice_id: llcc slice id for each client + * max_cap: The maximum capacity of the cache slice provided in KB + * priority: Priority of the client used to select victim line for replacement + * fixed_size: Determine of the slice has a fixed capacity + * bonus_ways: Bonus ways to be used by any slice, bonus way is used only if + * it't not a reserved way. + * res_ways: Reserved ways for the cache slice, the reserved ways cannot be used + * by any other client than the one its assigned to. + * cache_mode: Each slice operates as a cache, this controls the mode of the + * slice normal or TCM + * probe_target_ways: Determines what ways to probe for access hit. When + * configured to 1 only bonus and reseved ways are probed. + * when configured to 0 all ways in llcc are probed. + * dis_cap_alloc: Disable capacity based allocation for a client + * retain_on_pc: If this bit is set and client has maitained active vote + * then the ways assigned to this client are not flushed on power + * collapse. + * activate_on_init: Activate the slice immidiately after the SCT is programmed + */ +#define SCT_ENTRY(n, uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \ + { \ + .name = n, \ + .usecase_id = uid, \ + .slice_id = sid, \ + .max_cap = mc, \ + .priority = p, \ + .fixed_size = fs, \ + .bonus_ways = bway, \ + .res_ways = rway, \ + .cache_mode = cmod, \ + .probe_target_ways = ptw, \ + .dis_cap_alloc = dca, \ + .retain_on_pc = rp, \ + .activate_on_init = a, \ + } + +static struct llcc_slice_config sm6150_data[] = { + SCT_ENTRY("cpuss", 1, 1, 256, 1, 0, 0xF, 0x0, 0, 0, 0, 1, 1), + SCT_ENTRY("modem", 8, 8, 256, 1, 0, 0xF, 0x0, 0, 0, 0, 1, 0), + SCT_ENTRY("mmuhwt", 13, 13, 256, 1, 0, 0xF, 0x0, 0, 0, 0, 0, 1), +}; + +static int sm6150_qcom_llcc_probe(struct platform_device *pdev) +{ + return qcom_llcc_probe(pdev, sm6150_data, + ARRAY_SIZE(sm6150_data)); +} + +static const struct of_device_id sm6150_qcom_llcc_of_match[] = { + { .compatible = "qcom,sm6150-llcc", }, + { }, +}; + +static struct platform_driver sm6150_qcom_llcc_driver = { + .driver = { + .name = "sm6150-llcc", + .owner = THIS_MODULE, + .of_match_table = sm6150_qcom_llcc_of_match, + }, + .probe = sm6150_qcom_llcc_probe, + .remove = qcom_llcc_remove, +}; + +static int __init sm6150_init_qcom_llcc_init(void) +{ + return platform_driver_register(&sm6150_qcom_llcc_driver); +} +module_init(sm6150_init_qcom_llcc_init); + +static void __exit sm6150_exit_qcom_llcc_exit(void) +{ + platform_driver_unregister(&sm6150_qcom_llcc_driver); +} +module_exit(sm6150_exit_qcom_llcc_exit); + +MODULE_DESCRIPTION("Qualcomm Technologies Inc sm6150 LLCC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/lpm-stats.c b/drivers/soc/qcom/lpm-stats.c index 2a86aeb0713932289f874c560b690ff8a6edcb49..267588af0e27a4c759e10f3a6fb082c653f8c73b 100644 --- a/drivers/soc/qcom/lpm-stats.c +++ b/drivers/soc/qcom/lpm-stats.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,7 @@ struct level_stats { int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; int success_count; int failed_count; - int64_t total_time; + uint64_t total_time; uint64_t enter_time; }; @@ -103,7 +104,7 @@ static void level_stats_print(struct seq_file *m, struct level_stats *stats) int i = 0; int64_t bucket_time = 0; char seqs[MAX_STR_LEN] = {0}; - int64_t s = stats->total_time; + uint64_t s = stats->total_time; uint32_t ns = do_div(s, NSEC_PER_SEC); snprintf(seqs, MAX_STR_LEN, @@ -254,6 +255,15 @@ static ssize_t level_stats_file_write(struct file *file, return count; } +static void reset_cpu_stats(void *info) +{ + struct lpm_stats *stats = &(*this_cpu_ptr(&(cpu_stats))); + int i; + + for (i = 0; i < stats->num_levels; i++) + level_stats_reset(&stats->time_stats[i]); +} + static ssize_t lpm_stats_file_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { @@ -275,6 +285,12 @@ static ssize_t lpm_stats_file_write(struct file *file, return -EINVAL; level_stats_reset_all(stats); + /* + * Wake up each CPU and reset the stats from that CPU, + * for that CPU, so we could have better timestamp for + * accounting. + */ + on_each_cpu(reset_cpu_stats, NULL, 1); return count; } diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index 5ed66bf02177f4e8ef9646da426544eab05fd21f..b0b9415d672c8f8fbe7a4783e1c235a3b8c571c2 100644 --- a/drivers/soc/qcom/memory_dump_v2.c +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,33 @@ static struct msm_dump_table *msm_dump_get_table(enum msm_dump_table_ids id) return table; } +static int msm_dump_data_add_minidump(struct msm_dump_entry *entry) +{ + struct msm_dump_data *data; + struct md_region md_entry; + + data = (struct msm_dump_data *)(phys_to_virt(entry->addr)); + + if (!data->addr || !data->len) + return -EINVAL; + + if (!strcmp(data->name, "")) { + pr_debug("Entry name is NULL, Use ID %d for minidump\n", + entry->id); + snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X", + entry->id); + } else { + strlcpy(md_entry.name, data->name, sizeof(md_entry.name)); + } + + md_entry.phys_addr = data->addr; + md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr); + md_entry.size = data->len; + md_entry.id = entry->id; + + return msm_minidump_add_region(&md_entry); +} + int msm_dump_data_register(enum msm_dump_table_ids id, struct msm_dump_entry *entry) { @@ -109,6 +137,10 @@ int msm_dump_data_register(enum msm_dump_table_ids id, table->num_entries++; dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table)); + + if (msm_dump_data_add_minidump(entry)) + pr_err("Failed to add entry in Minidump table\n"); + return 0; } EXPORT_SYMBOL(msm_dump_data_register); @@ -245,6 +277,9 @@ static int mem_dump_probe(struct platform_device *pdev) dump_data->addr = dump_addr; dump_data->len = size; + strlcpy(dump_data->name, child_node->name, + strlen(child_node->name) + 1); + dump_entry.id = id; dump_entry.addr = virt_to_phys(dump_data); ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c new file mode 100644 index 0000000000000000000000000000000000000000..d85c506fb7968d6e07697b126a5c10cb9b550cae --- /dev/null +++ b/drivers/soc/qcom/minidump_log.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2017, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __init register_log_buf(void) +{ + char **log_bufp; + uint32_t *log_buf_lenp; + struct md_region md_entry; + + log_bufp = (char **)kallsyms_lookup_name("log_buf"); + log_buf_lenp = (uint32_t *)kallsyms_lookup_name("log_buf_len"); + if (!log_bufp || !log_buf_lenp) { + pr_err("Unable to find log_buf by kallsyms!\n"); + return; + } + /*Register logbuf to minidump, first idx would be from bss section */ + strlcpy(md_entry.name, "KLOGBUF", sizeof(md_entry.name)); + md_entry.virt_addr = (uintptr_t) (*log_bufp); + md_entry.phys_addr = virt_to_phys(*log_bufp); + md_entry.size = *log_buf_lenp; + if (msm_minidump_add_region(&md_entry)) + pr_err("Failed to add logbuf in Minidump\n"); +} + +static void register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size, + u32 cpu) +{ + struct page *sp_page = vmalloc_to_page((const void *) sp); + struct vm_struct *stack_vm_area = task_stack_vm_area(current); + + ksp_entry->virt_addr = sp; + ksp_entry->size = size; + if (stack_vm_area) { + sp_page = vmalloc_to_page((const void *) sp); + ksp_entry->phys_addr = page_to_phys(sp_page); + } else { + ksp_entry->phys_addr = virt_to_phys((uintptr_t *)sp); + } + + if (msm_minidump_add_region(ksp_entry)) + pr_err("Failed to add stack of cpu %d in Minidump\n", cpu); +} + +static void __init register_kernel_sections(void) +{ + struct md_region ksec_entry; + char *data_name = "KDATABSS"; + const size_t static_size = __per_cpu_end - __per_cpu_start; + void __percpu *base = (void __percpu *)__per_cpu_start; + unsigned int cpu; + + strlcpy(ksec_entry.name, data_name, sizeof(ksec_entry.name)); + ksec_entry.virt_addr = (uintptr_t)_sdata; + ksec_entry.phys_addr = virt_to_phys(_sdata); + ksec_entry.size = roundup((__bss_stop - _sdata), 4); + if (msm_minidump_add_region(&ksec_entry)) + pr_err("Failed to add data section in Minidump\n"); + + /* Add percpu static sections */ + for_each_possible_cpu(cpu) { + void *start = per_cpu_ptr(base, cpu); + + memset(&ksec_entry, 0, sizeof(ksec_entry)); + scnprintf(ksec_entry.name, sizeof(ksec_entry.name), + "KSPERCPU%d", cpu); + ksec_entry.virt_addr = (uintptr_t)start; + ksec_entry.phys_addr = per_cpu_ptr_to_phys(start); + ksec_entry.size = static_size; + if (msm_minidump_add_region(&ksec_entry)) + pr_err("Failed to add percpu sections in Minidump\n"); + } +} + +void dump_stack_minidump(u64 sp) +{ + struct md_region ksp_entry, ktsk_entry; + u32 cpu = smp_processor_id(); + struct vm_struct *stack_vm_area; + unsigned int stack_pages, i, copy_pages; + u64 base_addr; + + if (is_idle_task(current)) + return; + + /* + * Since stacks are now allocated with vmalloc, the translation to + * physical address is not a simple linear transformation like it is + * for kernel logical addresses, since vmalloc creates a virtual + * mapping. Thus, virt_to_phys() should not be used in this context; + * instead the page table must be walked to acquire the physical + * address of one page of the stack. + */ + stack_vm_area = task_stack_vm_area(current); + if (stack_vm_area) { + sp = PAGE_ALIGN(sp); + scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d", + cpu); + base_addr = (u64) stack_vm_area->addr; + stack_pages = get_vm_area_size(stack_vm_area) >> PAGE_SHIFT; + copy_pages = stack_pages - ((sp - base_addr) / PAGE_SIZE); + for (i = 0; i < copy_pages; i++) { + register_stack_entry(&ksp_entry, sp, PAGE_SIZE, cpu); + sp += PAGE_SIZE; + } + } else { + sp &= (THREAD_SIZE - 1); + scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d", + cpu); + register_stack_entry(&ksp_entry, sp, THREAD_SIZE, cpu); + } + + scnprintf(ktsk_entry.name, sizeof(ktsk_entry.name), "KTASK%d", cpu); + ktsk_entry.virt_addr = (u64)current; + ktsk_entry.phys_addr = virt_to_phys((uintptr_t *)current); + ktsk_entry.size = sizeof(struct task_struct); + if (msm_minidump_add_region(&ktsk_entry)) + pr_err("Failed to add current task %d in Minidump\n", cpu); +} + +static int __init msm_minidump_log_init(void) +{ + register_kernel_sections(); + register_log_buf(); + return 0; +} +late_initcall(msm_minidump_log_init); diff --git a/drivers/soc/qcom/minidump_private.h b/drivers/soc/qcom/minidump_private.h new file mode 100644 index 0000000000000000000000000000000000000000..1e61dd7ec92f7f9aafa2aed5568dd9957aa63c98 --- /dev/null +++ b/drivers/soc/qcom/minidump_private.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2017, 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 __MINIDUMP_PRIVATE_H +#define __MINIDUMP_PRIVATE_H + +#define MD_REVISION 1 +#define SBL_MINIDUMP_SMEM_ID 602 +#define MAX_NUM_OF_SS 10 +#define MD_SS_HLOS_ID 0 +#define SMEM_ENTRY_SIZE 40 + +/* Bootloader has 16 byte support, 4 bytes reserved for itself */ +#define MAX_REGION_NAME_LENGTH 16 + +#define MD_REGION_VALID ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0) +#define MD_REGION_INVALID ('I' << 24 | 'N' << 16 | 'V' << 8 | 'A' << 0) +#define MD_REGION_INIT ('I' << 24 | 'N' << 16 | 'I' << 8 | 'T' << 0) +#define MD_REGION_NOINIT 0 + +#define MD_SS_ENCR_REQ (0 << 24 | 'Y' << 16 | 'E' << 8 | 'S' << 0) +#define MD_SS_ENCR_NOTREQ (0 << 24 | 0 << 16 | 'N' << 8 | 'R' << 0) +#define MD_SS_ENCR_NONE ('N' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0) +#define MD_SS_ENCR_DONE ('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0) +#define MD_SS_ENCR_START ('S' << 24 | 'T' << 16 | 'R' << 8 | 'T' << 0) +#define MD_SS_ENABLED ('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0) +#define MD_SS_DISABLED ('D' << 24 | 'S' << 16 | 'B' << 8 | 'L' << 0) + +/** + * md_ss_region - Minidump region + * @name : Name of the region to be dumped + * @seq_num: : Use to differentiate regions with same name. + * @md_valid : This entry to be dumped (if set to 1) + * @region_base_address : Physical address of region to be dumped + * @region_size : Size of the region + */ +struct md_ss_region { + char name[MAX_REGION_NAME_LENGTH]; + u32 seq_num; + u32 md_valid; + u64 region_base_address; + u64 region_size; +}; + +/** + * md_ss_toc: Sub system SMEM Table of content + * @md_ss_toc_init : SS toc init status + * @md_ss_enable_status : if set to 1, Bootloader would dump this SS regions + * @encryption_status: Encryption status for this subsystem + * @encryption_required : Decides to encrypt the SS regions or not + * @ss_region_count : Number of regions added in this SS toc + * @md_ss_smem_regions_baseptr : regions base pointer of the Subsystem + */ +struct md_ss_toc { + u32 md_ss_toc_init; + u32 md_ss_enable_status; + u32 encryption_status; + u32 encryption_required; + u32 ss_region_count; + u64 md_ss_smem_regions_baseptr; +}; + +/** + * md_global_toc: Global Table of Content + * @md_toc_init : Global Minidump init status + * @md_revision : Minidump revision + * @md_enable_status : Minidump enable status + * @md_ss_toc : Array of subsystems toc + */ +struct md_global_toc { + u32 md_toc_init; + u32 md_revision; + u32 md_enable_status; + struct md_ss_toc md_ss_toc[MAX_NUM_OF_SS]; +}; + +#endif diff --git a/drivers/soc/qcom/msm_bus/Makefile b/drivers/soc/qcom/msm_bus/Makefile index 1103360e182acbe8c659be47cb61109342956fbf..b33835657e1f5a63a60180072bb00631fb0154d5 100644 --- a/drivers/soc/qcom/msm_bus/Makefile +++ b/drivers/soc/qcom/msm_bus/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_MSM_RPM_SMD) += msm_bus_rpm_smd.o ifdef CONFIG_QCOM_BUS_CONFIG_RPMH obj-y += msm_bus_fabric_rpmh.o msm_bus_arb_rpmh.o msm_bus_rules.o \ - msm_bus_bimc_rpmh.o msm_bus_noc_rpmh.o + msm_bus_bimc_rpmh.o msm_bus_noc_rpmh.o msm_bus_proxy_client.o obj-$(CONFIG_OF) += msm_bus_of_rpmh.o else obj-y += msm_bus_fabric_adhoc.o msm_bus_arb_adhoc.o \ diff --git a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c index 1f35f49dad5ea72c631a2ed95b5ebfcfe8d49eb6..622a90fc2c32e881a0219a1ecf0c1cf4757cb228 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c @@ -27,6 +27,7 @@ struct msm_bus_floor_client_type { }; static struct class *bus_floor_class; +static DEFINE_RT_MUTEX(msm_bus_floor_vote_lock); #define MAX_VOTER_NAME (50) #define DEFAULT_NODE_WIDTH (8) #define DBG_NAME(s) (strnstr(s, "-", 7) + 1) @@ -64,18 +65,22 @@ static ssize_t bus_floor_active_only_store(struct device *dev, { struct msm_bus_floor_client_type *cl; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (kstrtoint(buf, 10, &cl->active_only) != 0) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } @@ -100,20 +105,24 @@ static ssize_t bus_floor_vote_store(struct device *dev, struct msm_bus_floor_client_type *cl; int ret = 0; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (kstrtoull(buf, 10, &cl->cur_vote_hz) != 0) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } ret = msm_bus_floor_vote_context(dev_name(dev), cl->cur_vote_hz, cl->active_only); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } @@ -126,15 +135,18 @@ static ssize_t bus_floor_vote_store_api(struct device *dev, char name[10]; u64 vote_khz = 0; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (sscanf(buf, "%9s %llu", name, &vote_khz) != 2) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } @@ -142,6 +154,7 @@ static ssize_t bus_floor_vote_store_api(struct device *dev, __func__, name, vote_khz); ret = msm_bus_floor_vote(name, vote_khz); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } diff --git a/drivers/soc/qcom/msm_bus/msm_bus_proxy_client.c b/drivers/soc/qcom/msm_bus/msm_bus_proxy_client.c new file mode 100644 index 0000000000000000000000000000000000000000..cdf61f6c8644ce306adb341110f556f90cb755db --- /dev/null +++ b/drivers/soc/qcom/msm_bus/msm_bus_proxy_client.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2018, 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 + +struct proxy_client { + struct msm_bus_scale_pdata *pdata; + unsigned int client_handle; +}; + +static struct proxy_client proxy_client_info; + +static int msm_bus_device_proxy_client_probe(struct platform_device *pdev) +{ + int ret; + + proxy_client_info.pdata = msm_bus_cl_get_pdata(pdev); + + if (!proxy_client_info.pdata) + return 0; + + proxy_client_info.client_handle = + msm_bus_scale_register_client(proxy_client_info.pdata); + + if (!proxy_client_info.client_handle) { + dev_err(&pdev->dev, "Unable to register bus client\n"); + return -ENODEV; + } + + ret = msm_bus_scale_client_update_request( + proxy_client_info.client_handle, 1); + if (ret) + dev_err(&pdev->dev, "Bandwidth update failed (%d)\n", ret); + + return ret; +} + +static const struct of_device_id proxy_client_match[] = { + {.compatible = "qcom,bus-proxy-client"}, + {} +}; + +static struct platform_driver msm_bus_proxy_client_driver = { + .probe = msm_bus_device_proxy_client_probe, + .driver = { + .name = "msm_bus_proxy_client_device", + .owner = THIS_MODULE, + .of_match_table = proxy_client_match, + }, +}; + +static int __init msm_bus_proxy_client_init_driver(void) +{ + int rc; + + rc = platform_driver_register(&msm_bus_proxy_client_driver); + if (rc) { + pr_err("Failed to register proxy client device driver"); + return rc; + } + + return rc; +} + +static int __init msm_bus_proxy_client_unvote(void) +{ + int ret; + + if (!proxy_client_info.pdata || !proxy_client_info.client_handle) + return 0; + + ret = msm_bus_scale_client_update_request( + proxy_client_info.client_handle, 0); + if (ret) + pr_err("%s: bandwidth update request failed (%d)\n", + __func__, ret); + + msm_bus_scale_unregister_client(proxy_client_info.client_handle); + + return 0; +} + +subsys_initcall_sync(msm_bus_proxy_client_init_driver); +late_initcall_sync(msm_bus_proxy_client_unvote); diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c new file mode 100644 index 0000000000000000000000000000000000000000..38843f760c2e83e988a7aa7ee7479f83854e6cf0 --- /dev/null +++ b/drivers/soc/qcom/msm_minidump.c @@ -0,0 +1,381 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "Minidump: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "minidump_private.h" + +#define MAX_NUM_ENTRIES (CONFIG_MINIDUMP_MAX_ENTRIES + 1) +#define MAX_STRTBL_SIZE (MAX_NUM_ENTRIES * MAX_REGION_NAME_LENGTH) + +/** + * md_table : Local Minidump toc holder + * @num_regions : Number of regions requested + * @md_ss_toc : HLOS toc pointer + * @md_gbl_toc : Global toc pointer + * @md_regions : HLOS regions base pointer + * @entry : array of HLOS regions requested + */ +struct md_table { + u32 revision; + u32 num_regions; + struct md_ss_toc *md_ss_toc; + struct md_global_toc *md_gbl_toc; + struct md_ss_region *md_regions; + struct md_region entry[MAX_NUM_ENTRIES]; +}; + +/** + * md_elfhdr: Minidump table elf header + * @ehdr: elf main header + * @shdr: Section header + * @phdr: Program header + * @elf_offset: section offset in elf + * @strtable_idx: string table current index position + */ +struct md_elfhdr { + struct elfhdr *ehdr; + struct elf_shdr *shdr; + struct elf_phdr *phdr; + u64 elf_offset; + u64 strtable_idx; +}; + +/* Protect elfheader and smem table from deferred calls contention */ +static DEFINE_SPINLOCK(mdt_lock); +static struct md_table minidump_table; +static struct md_elfhdr minidump_elfheader; + +/* Number of pending entries to be added in ToC regions */ +static unsigned int pendings; + +static inline char *elf_lookup_string(struct elfhdr *hdr, int offset) +{ + char *strtab = elf_str_table(hdr); + + if ((strtab == NULL) || (minidump_elfheader.strtable_idx < offset)) + return NULL; + return strtab + offset; +} + +static inline unsigned int set_section_name(const char *name) +{ + char *strtab = elf_str_table(minidump_elfheader.ehdr); + int idx = minidump_elfheader.strtable_idx; + int ret = 0; + + if ((strtab == NULL) || (name == NULL)) + return 0; + + ret = idx; + idx += strlcpy((strtab + idx), name, MAX_REGION_NAME_LENGTH); + minidump_elfheader.strtable_idx = idx + 1; + + return ret; +} + +static inline bool md_check_name(const char *name) +{ + struct md_region *mde = minidump_table.entry; + int i, regno = minidump_table.num_regions; + + for (i = 0; i < regno; i++, mde++) + if (!strcmp(mde->name, name)) + return true; + return false; +} + +/* Return next seq no, if name already exists in the table */ +static inline int md_get_seq_num(const char *name) +{ + struct md_ss_region *mde = minidump_table.md_regions; + int i, regno = minidump_table.md_ss_toc->ss_region_count; + int seqno = 0; + + for (i = 0; i < (regno - 1); i++, mde++) { + if (!strcmp(mde->name, name)) { + if (mde->seq_num >= seqno) + seqno = mde->seq_num + 1; + } + } + return seqno; +} + +/* Update Mini dump table in SMEM */ +static void md_update_ss_toc(const struct md_region *entry) +{ + struct md_ss_region *mdr; + struct elfhdr *hdr = minidump_elfheader.ehdr; + struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++); + struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++); + int reg_cnt = minidump_table.md_ss_toc->ss_region_count++; + + mdr = &minidump_table.md_regions[reg_cnt]; + + strlcpy(mdr->name, entry->name, sizeof(mdr->name)); + mdr->region_base_address = entry->phys_addr; + mdr->region_size = entry->size; + mdr->seq_num = md_get_seq_num(entry->name); + + /* Update elf header */ + shdr->sh_type = SHT_PROGBITS; + shdr->sh_name = set_section_name(mdr->name); + shdr->sh_addr = (elf_addr_t)entry->virt_addr; + shdr->sh_size = mdr->region_size; + shdr->sh_flags = SHF_WRITE; + shdr->sh_offset = minidump_elfheader.elf_offset; + shdr->sh_entsize = 0; + + phdr->p_type = PT_LOAD; + phdr->p_offset = minidump_elfheader.elf_offset; + phdr->p_vaddr = entry->virt_addr; + phdr->p_paddr = entry->phys_addr; + phdr->p_filesz = phdr->p_memsz = mdr->region_size; + phdr->p_flags = PF_R | PF_W; + + minidump_elfheader.elf_offset += shdr->sh_size; + mdr->md_valid = MD_REGION_VALID; +} + +bool msm_minidump_enabled(void) +{ + bool ret = false; + + spin_lock(&mdt_lock); + if (minidump_table.md_ss_toc && + (minidump_table.md_ss_toc->md_ss_enable_status == + MD_SS_ENABLED)) + ret = true; + spin_unlock(&mdt_lock); + return ret; +} +EXPORT_SYMBOL(msm_minidump_enabled); + +int msm_minidump_add_region(const struct md_region *entry) +{ + u32 entries; + struct md_region *mdr; + int ret = 0; + + if (!entry) + return -EINVAL; + + if ((strlen(entry->name) > MAX_NAME_LENGTH) || + md_check_name(entry->name) || !entry->virt_addr) { + pr_err("Invalid entry details\n"); + return -EINVAL; + } + + if (!IS_ALIGNED(entry->size, 4)) { + pr_err("size should be 4 byte aligned\n"); + return -EINVAL; + } + + spin_lock(&mdt_lock); + entries = minidump_table.num_regions; + if (entries >= MAX_NUM_ENTRIES) { + pr_err("Maximum entries reached.\n"); + spin_unlock(&mdt_lock); + return -ENOMEM; + } + + mdr = &minidump_table.entry[entries]; + strlcpy(mdr->name, entry->name, sizeof(mdr->name)); + mdr->virt_addr = entry->virt_addr; + mdr->phys_addr = entry->phys_addr; + mdr->size = entry->size; + mdr->id = entry->id; + + minidump_table.num_regions = entries + 1; + + if (minidump_table.md_ss_toc && + (minidump_table.md_ss_toc->md_ss_enable_status == + MD_SS_ENABLED)) + md_update_ss_toc(entry); + else + pendings++; + + spin_unlock(&mdt_lock); + + return ret; +} +EXPORT_SYMBOL(msm_minidump_add_region); + +static int msm_minidump_add_header(void) +{ + struct md_ss_region *mdreg = &minidump_table.md_regions[0]; + struct elfhdr *ehdr; + struct elf_shdr *shdr; + struct elf_phdr *phdr; + unsigned int strtbl_off, elfh_size, phdr_off; + char *banner; + + /* Header buffer contains: + * elf header, MAX_NUM_ENTRIES+4 of section and program elf headers, + * string table section and linux banner. + */ + elfh_size = sizeof(*ehdr) + MAX_STRTBL_SIZE + (strlen(linux_banner) + + 1) + ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 4)); + elfh_size = ALIGN(elfh_size, 4); + + minidump_elfheader.ehdr = kzalloc(elfh_size, GFP_KERNEL); + if (!minidump_elfheader.ehdr) + return -ENOMEM; + + strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name)); + mdreg->region_base_address = virt_to_phys(minidump_elfheader.ehdr); + mdreg->region_size = elfh_size; + + ehdr = minidump_elfheader.ehdr; + /* Assign section/program headers offset */ + minidump_elfheader.shdr = shdr = (struct elf_shdr *)(ehdr + 1); + minidump_elfheader.phdr = phdr = + (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES); + phdr_off = sizeof(*ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES); + + memcpy(ehdr->e_ident, ELFMAG, SELFMAG); + ehdr->e_ident[EI_CLASS] = ELF_CLASS; + ehdr->e_ident[EI_DATA] = ELF_DATA; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELF_OSABI; + ehdr->e_type = ET_CORE; + ehdr->e_machine = ELF_ARCH; + ehdr->e_version = EV_CURRENT; + ehdr->e_ehsize = sizeof(*ehdr); + ehdr->e_phoff = phdr_off; + ehdr->e_phentsize = sizeof(*phdr); + ehdr->e_shoff = sizeof(*ehdr); + ehdr->e_shentsize = sizeof(*shdr); + ehdr->e_shstrndx = 1; + + minidump_elfheader.elf_offset = elfh_size; + + /* + * First section header should be NULL, + * 2nd section is string table. + */ + minidump_elfheader.strtable_idx = 1; + strtbl_off = sizeof(*ehdr) + + ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES); + shdr++; + shdr->sh_type = SHT_STRTAB; + shdr->sh_offset = (elf_addr_t)strtbl_off; + shdr->sh_size = MAX_STRTBL_SIZE; + shdr->sh_entsize = 0; + shdr->sh_flags = 0; + shdr->sh_name = set_section_name("STR_TBL"); + shdr++; + + /* 3rd section is for minidump_table VA, used by parsers */ + shdr->sh_type = SHT_PROGBITS; + shdr->sh_entsize = 0; + shdr->sh_flags = 0; + shdr->sh_addr = (elf_addr_t)&minidump_table; + shdr->sh_name = set_section_name("minidump_table"); + shdr++; + + /* 4th section is linux banner */ + banner = (char *)ehdr + strtbl_off + MAX_STRTBL_SIZE; + strlcpy(banner, linux_banner, strlen(linux_banner) + 1); + + shdr->sh_type = SHT_PROGBITS; + shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE); + shdr->sh_size = strlen(linux_banner) + 1; + shdr->sh_addr = (elf_addr_t)linux_banner; + shdr->sh_entsize = 0; + shdr->sh_flags = SHF_WRITE; + shdr->sh_name = set_section_name("linux_banner"); + + phdr->p_type = PT_LOAD; + phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE); + phdr->p_vaddr = (elf_addr_t)linux_banner; + phdr->p_paddr = virt_to_phys(linux_banner); + phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1; + phdr->p_flags = PF_R | PF_W; + + /* Update headers count*/ + ehdr->e_phnum = 1; + ehdr->e_shnum = 4; + + mdreg->md_valid = MD_REGION_VALID; + return 0; +} + +static int __init msm_minidump_init(void) +{ + unsigned int i; + size_t size; + struct md_region *mdr; + struct md_global_toc *md_global_toc; + struct md_ss_toc *md_ss_toc; + + /* Get Minidump table */ + md_global_toc = qcom_smem_get(QCOM_SMEM_HOST_ANY, SBL_MINIDUMP_SMEM_ID, + &size); + if (IS_ERR_OR_NULL(md_global_toc)) { + pr_err("SMEM is not initialized.\n"); + return -ENODEV; + } + + /*Check global minidump support initialization */ + if (!md_global_toc->md_toc_init) { + pr_err("System Minidump TOC not initialized\n"); + return -ENODEV; + } + + minidump_table.md_gbl_toc = md_global_toc; + minidump_table.revision = md_global_toc->md_revision; + md_ss_toc = &md_global_toc->md_ss_toc[MD_SS_HLOS_ID]; + + md_ss_toc->encryption_status = MD_SS_ENCR_NONE; + md_ss_toc->encryption_required = MD_SS_ENCR_REQ; + + minidump_table.md_ss_toc = md_ss_toc; + minidump_table.md_regions = kzalloc((MAX_NUM_ENTRIES * + sizeof(struct md_ss_region)), GFP_KERNEL); + if (!minidump_table.md_regions) + return -ENOMEM; + + md_ss_toc->md_ss_smem_regions_baseptr = + virt_to_phys(minidump_table.md_regions); + + /* First entry would be ELF header */ + md_ss_toc->ss_region_count = 1; + msm_minidump_add_header(); + + /* Add pending entries to HLOS TOC */ + spin_lock(&mdt_lock); + md_ss_toc->md_ss_toc_init = 1; + md_ss_toc->md_ss_enable_status = MD_SS_ENABLED; + for (i = 0; i < pendings; i++) { + mdr = &minidump_table.entry[i]; + md_update_ss_toc(mdr); + } + + pendings = 0; + spin_unlock(&mdt_lock); + + pr_info("Enabled with max number of regions %d\n", + CONFIG_MINIDUMP_MAX_ENTRIES); + + return 0; +} +subsys_initcall(msm_minidump_init) diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c new file mode 100644 index 0000000000000000000000000000000000000000..0b2a543658a7d39a95ff7e04a73fcf98672d2760 --- /dev/null +++ b/drivers/soc/qcom/qbt1000.c @@ -0,0 +1,1214 @@ +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define DEBUG +#define pr_fmt(fmt) "qbt1000:%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../misc/qseecom_kernel.h" + +#define QBT1000_DEV "qbt1000" +#define QBT1000_IN_DEV_NAME "qbt1000_key_input" +#define QBT1000_IN_DEV_VERSION 0x0100 +#define MAX_FW_EVENTS 128 +#define FP_APP_CMD_RX_IPC 132 +#define FW_MAX_IPC_MSG_DATA_SIZE 0x500 +#define IPC_MSG_ID_CBGE_REQUIRED 29 + +/* + * shared buffer size - init with max value, + * user space will provide new value upon tz app load + */ +static uint32_t g_app_buf_size = SZ_256K; +static char const *const FP_APP_NAME = "fingerpr"; + +struct finger_detect_gpio { + int gpio; + int active_low; + int irq; + struct work_struct work; + unsigned int key_code; + int power_key_enabled; + int last_gpio_state; + int event_reported; +}; + +struct fw_event_desc { + enum qbt1000_fw_event ev; +}; + +struct fw_ipc_info { + int gpio; + int irq; +}; + +struct qbt1000_drvdata { + struct class *qbt1000_class; + struct cdev qbt1000_cdev; + struct device *dev; + char *qbt1000_node; + struct clk **clocks; + unsigned int clock_count; + uint8_t clock_state; + unsigned int root_clk_idx; + unsigned int frequency; + atomic_t available; + struct mutex mutex; + struct mutex fw_events_mutex; + struct input_dev *in_dev; + struct fw_ipc_info fw_ipc; + struct finger_detect_gpio fd_gpio; + DECLARE_KFIFO(fw_events, struct fw_event_desc, MAX_FW_EVENTS); + wait_queue_head_t read_wait_queue; + struct qseecom_handle *app_handle; + struct qseecom_handle *fp_app_handle; +}; + +/* + * struct fw_ipc_cmd - + * used to store IPC commands to/from firmware + * @status - indicates whether sending/getting the IPC message was successful + * @msg_type - the type of IPC message + * @msg_len - the length of the message data + * @resp_needed - whether a response is needed for this message + * @msg_data - any extra data associated with the message + */ +struct fw_ipc_cmd { + uint32_t status; + uint32_t numMsgs; + uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE]; +}; + +struct fw_ipc_header { + uint32_t msg_type; + uint32_t msg_len; + uint32_t resp_needed; +}; + +/* + * struct ipc_msg_type_to_fw_event - + * entry in mapping between an IPC message type to a firmware event + * @msg_type - IPC message type, as reported by firmware + * @fw_event - corresponding firmware event code to report to driver client + */ +struct ipc_msg_type_to_fw_event { + uint32_t msg_type; + enum qbt1000_fw_event fw_event; +}; + +/* mapping between firmware IPC message types to HLOS firmware events */ +struct ipc_msg_type_to_fw_event g_msg_to_event[] = { + {IPC_MSG_ID_CBGE_REQUIRED, FW_EVENT_CBGE_REQUIRED} +}; + +/** + * get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and + * aligns buffer lengths + * @hdl: index of qseecom_handle + * @cmd: req buffer - set to qseecom_handle.sbuf + * @cmd_len: ptr to req buffer len + * @rsp: rsp buffer - set to qseecom_handle.sbuf + offset + * @rsp_len: ptr to rsp buffer len + * + * Return: 0 on success. Error code on failure. + */ +static int get_cmd_rsp_buffers(struct qseecom_handle *hdl, + void **cmd, + uint32_t *cmd_len, + void **rsp, + uint32_t *rsp_len) +{ + /* 64 bytes alignment for QSEECOM */ + uint64_t aligned_cmd_len = ALIGN((uint64_t)*cmd_len, 64); + uint64_t aligned_rsp_len = ALIGN((uint64_t)*rsp_len, 64); + + if ((aligned_rsp_len + aligned_cmd_len) > (uint64_t)g_app_buf_size) + return -ENOMEM; + + *cmd = hdl->sbuf; + *cmd_len = aligned_cmd_len; + *rsp = hdl->sbuf + *cmd_len; + *rsp_len = aligned_rsp_len; + + return 0; +} + +/** + * send_tz_cmd() - Function sends a command to TZ + * + * @drvdata: pointer to driver data + * @app_handle: handle to tz app + * @is_user_space: 1 if the cmd buffer is in user space, 0 + * otherwise + * @cmd: command buffer to send + * @cmd_len: length of the command buffer + * @rsp: output, will be set to location of response buffer + * @rsp_len: max size of response + * + * Return: 0 on success. + */ +static int send_tz_cmd(struct qbt1000_drvdata *drvdata, + struct qseecom_handle *app_handle, + int is_user_space, + void *cmd, uint32_t cmd_len, + void **rsp, uint32_t rsp_len) +{ + int rc = 0; + void *aligned_cmd; + void *aligned_rsp; + uint32_t aligned_cmd_len; + uint32_t aligned_rsp_len; + + /* init command and response buffers and align lengths */ + aligned_cmd_len = cmd_len; + aligned_rsp_len = rsp_len; + + rc = get_cmd_rsp_buffers(app_handle, + (void **)&aligned_cmd, + &aligned_cmd_len, + (void **)&aligned_rsp, + &aligned_rsp_len); + + if (rc != 0) + goto end; + + if (!aligned_cmd) { + dev_err(drvdata->dev, "%s: Null command buffer\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (is_user_space) { + rc = copy_from_user(aligned_cmd, (void __user *)cmd, + cmd_len); + if (rc != 0) { + pr_err("failure to copy user space buf %d\n", rc); + rc = -EFAULT; + goto end; + } + } else + memcpy(aligned_cmd, cmd, cmd_len); + + /* send cmd to TZ */ + rc = qseecom_send_command(app_handle, + aligned_cmd, + aligned_cmd_len, + aligned_rsp, + aligned_rsp_len); + + if (rc != 0) { + pr_err("failure to send tz cmd %d\n", rc); + goto end; + } + + *rsp = aligned_rsp; + +end: + return rc; +} + +/** + * qbt1000_open() - Function called when user space opens device. + * Successful if driver not currently open. + * @inode: ptr to inode object + * @file: ptr to file object + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + struct qbt1000_drvdata *drvdata = container_of(inode->i_cdev, + struct qbt1000_drvdata, + qbt1000_cdev); + file->private_data = drvdata; + + pr_debug("%s begin\n", __func__); + /* disallowing concurrent opens */ + if (!atomic_dec_and_test(&drvdata->available)) { + atomic_inc(&drvdata->available); + rc = -EBUSY; + } + + pr_debug("%s end : %d\n", __func__, rc); + return rc; +} + +/** + * qbt1000_release() - Function called when user space closes device. + + * @inode: ptr to inode object + * @file: ptr to file object + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_release(struct inode *inode, struct file *file) +{ + struct qbt1000_drvdata *drvdata; + + if (!file || !file->private_data) { + pr_err("%s: NULL pointer passed", __func__); + return -EINVAL; + } + drvdata = file->private_data; + atomic_inc(&drvdata->available); + return 0; +} + +/** + * qbt1000_ioctl() - Function called when user space calls ioctl. + * @file: struct file - not used + * @cmd: cmd identifier:QBT1000_LOAD_APP,QBT1000_UNLOAD_APP, + * QBT1000_SEND_TZCMD + * @arg: ptr to relevant structe: either qbt1000_app or + * qbt1000_send_tz_cmd depending on which cmd is passed + * + * Return: 0 on success. Error code on failure. + */ +static long qbt1000_ioctl( + struct file *file, unsigned int cmd, unsigned long arg) +{ + int rc = 0; + void __user *priv_arg = (void __user *)arg; + struct qbt1000_drvdata *drvdata; + + if (!file || !file->private_data) { + pr_err("%s: NULL pointer passed", __func__); + return -EINVAL; + } + + drvdata = file->private_data; + + if (IS_ERR(priv_arg)) { + dev_err(drvdata->dev, "%s: invalid user space pointer %lu\n", + __func__, arg); + return -EINVAL; + } + + mutex_lock(&drvdata->mutex); + + pr_debug("%s %d\n", __func__, cmd); + + switch (cmd) { + case QBT1000_LOAD_APP: + { + struct qbt1000_app app; + struct qseecom_handle *app_handle; + + if (copy_from_user(&app, priv_arg, + sizeof(app)) != 0) { + rc = -EFAULT; + pr_err("failed copy from user space-LOAD\n"); + goto end; + } + + if (!app.app_handle) { + dev_err(drvdata->dev, "%s: LOAD app_handle is null\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (strcmp(app.name, FP_APP_NAME)) { + dev_err(drvdata->dev, "%s: Invalid app name\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (drvdata->app_handle) { + dev_err(drvdata->dev, "%s: LOAD app already loaded, unloading first\n", + __func__); + drvdata->fp_app_handle = 0; + rc = qseecom_shutdown_app(&drvdata->app_handle); + if (rc != 0) { + dev_err(drvdata->dev, "%s: LOAD current app failed to shutdown\n", + __func__); + goto end; + } + } + + pr_debug("app %s load before\n", app.name); + app.name[MAX_NAME_SIZE - 1] = '\0'; + + /* start the TZ app */ + rc = qseecom_start_app( + &drvdata->app_handle, app.name, app.size); + if (rc == 0) { + g_app_buf_size = app.size; + rc = qseecom_set_bandwidth(drvdata->app_handle, + app.high_band_width == 1 ? true : false); + if (rc != 0) { + /* log error, allow to continue */ + pr_err("App %s failed to set bw\n", app.name); + } + } else { + dev_err(drvdata->dev, "%s: Fingerprint Trusted App failed to load\n", + __func__); + goto end; + } + + /* copy a fake app handle to user */ + app_handle = drvdata->app_handle ? + (struct qseecom_handle *)123456 : 0; + rc = copy_to_user((void __user *)app.app_handle, &app_handle, + sizeof(*app.app_handle)); + + if (rc != 0) { + dev_err(drvdata->dev, + "%s: Failed copy 2us LOAD rc:%d\n", + __func__, rc); + rc = -ENOMEM; + goto end; + } + + pr_debug("app %s load after\n", app.name); + + drvdata->fp_app_handle = drvdata->app_handle; + break; + } + case QBT1000_UNLOAD_APP: + { + struct qbt1000_app app; + struct qseecom_handle *app_handle = 0; + + if (copy_from_user(&app, priv_arg, + sizeof(app)) != 0) { + rc = -ENOMEM; + pr_err("failed copy from user space-UNLOAD\n"); + goto end; + } + + if (!app.app_handle) { + dev_err(drvdata->dev, "%s: UNLOAD app_handle is null\n", + __func__); + rc = -EINVAL; + goto end; + } + + rc = copy_from_user(&app_handle, app.app_handle, + sizeof(app_handle)); + + if (rc != 0) { + dev_err(drvdata->dev, + "%s: Failed copy from user space-UNLOAD handle rc:%d\n", + __func__, rc); + rc = -ENOMEM; + goto end; + } + + /* if the app hasn't been loaded already, return err */ + if (!drvdata->app_handle) { + pr_err("app not loaded\n"); + rc = -EINVAL; + goto end; + } + + if (drvdata->fp_app_handle == drvdata->app_handle) + drvdata->fp_app_handle = 0; + + /* set bw & shutdown the TZ app */ + qseecom_set_bandwidth(drvdata->app_handle, + app.high_band_width == 1 ? true : false); + rc = qseecom_shutdown_app(&drvdata->app_handle); + if (rc != 0) { + pr_err("app failed to shutdown\n"); + goto end; + } + + /* copy the app handle (should be null) to user */ + rc = copy_to_user((void __user *)app.app_handle, &app_handle, + sizeof(*app.app_handle)); + + if (rc != 0) { + dev_err(drvdata->dev, + "%s: Failed copy 2us UNLOAD rc:%d\n", + __func__, rc); + rc = -ENOMEM; + goto end; + } + + break; + } + case QBT1000_SEND_TZCMD: + { + struct qbt1000_send_tz_cmd tzcmd; + void *rsp_buf; + + if (copy_from_user(&tzcmd, priv_arg, + sizeof(tzcmd)) + != 0) { + rc = -EFAULT; + pr_err("failed copy from user space %d\n", rc); + goto end; + } + + if (tzcmd.req_buf_len > g_app_buf_size || + tzcmd.rsp_buf_len > g_app_buf_size) { + rc = -ENOMEM; + pr_err("invalid cmd buf len, req=%d, rsp=%d\n", + tzcmd.req_buf_len, tzcmd.rsp_buf_len); + goto end; + } + + /* if the app hasn't been loaded already, return err */ + if (!drvdata->app_handle) { + pr_err("app not loaded\n"); + rc = -EINVAL; + goto end; + } + + rc = send_tz_cmd(drvdata, + drvdata->app_handle, 1, + tzcmd.req_buf, tzcmd.req_buf_len, + &rsp_buf, tzcmd.rsp_buf_len); + + if (rc < 0) { + pr_err("failure sending command to tz\n"); + goto end; + } + + /* copy rsp buf back to user space buffer */ + rc = copy_to_user((void __user *)tzcmd.rsp_buf, + rsp_buf, tzcmd.rsp_buf_len); + if (rc != 0) { + pr_err("failed copy 2us rc:%d bytes %d:\n", + rc, tzcmd.rsp_buf_len); + rc = -EFAULT; + goto end; + } + + break; + } + case QBT1000_SET_FINGER_DETECT_KEY: + { + struct qbt1000_set_finger_detect_key set_fd_key; + + if (copy_from_user(&set_fd_key, priv_arg, + sizeof(set_fd_key)) + != 0) { + rc = -EFAULT; + pr_err("failed copy from user space %d\n", rc); + goto end; + } + + drvdata->fd_gpio.key_code = set_fd_key.key_code; + + break; + } + case QBT1000_CONFIGURE_POWER_KEY: + { + struct qbt1000_configure_power_key power_key; + + if (copy_from_user(&power_key, priv_arg, + sizeof(power_key)) + != 0) { + rc = -EFAULT; + pr_err("failed copy from user space %d\n", rc); + goto end; + } + + drvdata->fd_gpio.power_key_enabled = power_key.enable; + + break; + } + default: + pr_err("invalid cmd %d\n", cmd); + rc = -ENOIOCTLCMD; + goto end; + } + +end: + mutex_unlock(&drvdata->mutex); + return rc; +} + +static int get_events_fifo_len_locked(struct qbt1000_drvdata *drvdata) +{ + int len; + + mutex_lock(&drvdata->fw_events_mutex); + len = kfifo_len(&drvdata->fw_events); + mutex_unlock(&drvdata->fw_events_mutex); + + return len; +} + +static ssize_t qbt1000_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct fw_event_desc fw_event; + struct qbt1000_drvdata *drvdata = filp->private_data; + + if (cnt < sizeof(fw_event.ev)) + return -EINVAL; + + mutex_lock(&drvdata->fw_events_mutex); + + while (kfifo_len(&drvdata->fw_events) == 0) { + mutex_unlock(&drvdata->fw_events_mutex); + + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + pr_debug("fw_events fifo: empty, waiting\n"); + + if (wait_event_interruptible(drvdata->read_wait_queue, + (get_events_fifo_len_locked(drvdata) > 0))) + return -ERESTARTSYS; + + mutex_lock(&drvdata->fw_events_mutex); + } + + if (!kfifo_get(&drvdata->fw_events, &fw_event)) { + pr_debug("fw_events fifo: unexpectedly empty\n"); + + mutex_unlock(&drvdata->fw_events_mutex); + return -EINVAL; + } + + mutex_unlock(&drvdata->fw_events_mutex); + + pr_debug("fw_event: %d\n", (int)fw_event.ev); + return copy_to_user(ubuf, &fw_event.ev, sizeof(fw_event.ev)); +} + +static unsigned int qbt1000_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct qbt1000_drvdata *drvdata = filp->private_data; + unsigned int mask = 0; + + poll_wait(filp, &drvdata->read_wait_queue, wait); + + if (kfifo_len(&drvdata->fw_events) > 0) + mask |= (POLLIN | POLLRDNORM); + + return mask; +} + +static const struct file_operations qbt1000_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = qbt1000_ioctl, + .open = qbt1000_open, + .release = qbt1000_release, + .read = qbt1000_read, + .poll = qbt1000_poll +}; + +static int qbt1000_dev_register(struct qbt1000_drvdata *drvdata) +{ + dev_t dev_no; + int ret = 0; + size_t node_size; + char *node_name = QBT1000_DEV; + struct device *dev = drvdata->dev; + struct device *device; + + node_size = strlen(node_name) + 1; + + drvdata->qbt1000_node = devm_kzalloc(dev, node_size, GFP_KERNEL); + if (!drvdata->qbt1000_node) { + ret = -ENOMEM; + goto err_alloc; + } + + strlcpy(drvdata->qbt1000_node, node_name, node_size); + + ret = alloc_chrdev_region(&dev_no, 0, 1, drvdata->qbt1000_node); + if (ret) { + pr_err("alloc_chrdev_region failed %d\n", ret); + goto err_alloc; + } + + cdev_init(&drvdata->qbt1000_cdev, &qbt1000_fops); + + drvdata->qbt1000_cdev.owner = THIS_MODULE; + ret = cdev_add(&drvdata->qbt1000_cdev, dev_no, 1); + if (ret) { + pr_err("cdev_add failed %d\n", ret); + goto err_cdev_add; + } + + drvdata->qbt1000_class = class_create(THIS_MODULE, + drvdata->qbt1000_node); + if (IS_ERR(drvdata->qbt1000_class)) { + ret = PTR_ERR(drvdata->qbt1000_class); + pr_err("class_create failed %d\n", ret); + goto err_class_create; + } + + device = device_create(drvdata->qbt1000_class, NULL, + drvdata->qbt1000_cdev.dev, drvdata, + drvdata->qbt1000_node); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + pr_err("device_create failed %d\n", ret); + goto err_dev_create; + } + + return 0; +err_dev_create: + class_destroy(drvdata->qbt1000_class); +err_class_create: + cdev_del(&drvdata->qbt1000_cdev); +err_cdev_add: + unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1); +err_alloc: + return ret; +} + +/** + * qbt1000_create_input_device() - Function allocates an input + * device, configures it for key events and registers it + * + * @drvdata: ptr to driver data + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + + drvdata->in_dev = input_allocate_device(); + if (drvdata->in_dev == NULL) { + dev_err(drvdata->dev, "%s: input_allocate_device() failed\n", + __func__); + rc = -ENOMEM; + goto end; + } + + drvdata->in_dev->name = QBT1000_IN_DEV_NAME; + drvdata->in_dev->phys = NULL; + drvdata->in_dev->id.bustype = BUS_HOST; + drvdata->in_dev->id.vendor = 0x0001; + drvdata->in_dev->id.product = 0x0001; + drvdata->in_dev->id.version = QBT1000_IN_DEV_VERSION; + + drvdata->in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + drvdata->in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + drvdata->in_dev->keybit[BIT_WORD(KEY_HOMEPAGE)] |= + BIT_MASK(KEY_HOMEPAGE); + drvdata->in_dev->keybit[BIT_WORD(KEY_CAMERA)] |= + BIT_MASK(KEY_CAMERA); + drvdata->in_dev->keybit[BIT_WORD(KEY_VOLUMEDOWN)] |= + BIT_MASK(KEY_VOLUMEDOWN); + drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |= + BIT_MASK(KEY_POWER); + + input_set_abs_params(drvdata->in_dev, ABS_X, + 0, + 1000, + 0, 0); + input_set_abs_params(drvdata->in_dev, ABS_Y, + 0, + 1000, + 0, 0); + + rc = input_register_device(drvdata->in_dev); + if (rc) { + dev_err(drvdata->dev, "%s: input_reg_dev() failed %d\n", + __func__, rc); + goto end; + } + +end: + if (rc) + input_free_device(drvdata->in_dev); + return rc; +} + +static void purge_finger_events(struct qbt1000_drvdata *drvdata) +{ + int i, fifo_len; + struct fw_event_desc fw_event; + + fifo_len = kfifo_len(&drvdata->fw_events); + + for (i = 0; i < fifo_len; i++) { + if (!kfifo_get(&drvdata->fw_events, &fw_event)) + pr_err("fw events fifo: could not remove oldest item\n"); + else if (fw_event.ev != FW_EVENT_FINGER_DOWN + && fw_event.ev != FW_EVENT_FINGER_UP) + kfifo_put(&drvdata->fw_events, fw_event); + } +} + +static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata) +{ + int state; + struct fw_event_desc fw_event; + + state = (__gpio_get_value(drvdata->fd_gpio.gpio) ? 1 : 0) + ^ drvdata->fd_gpio.active_low; + + if (drvdata->fd_gpio.event_reported + && state == drvdata->fd_gpio.last_gpio_state) + return; + + pr_debug("gpio %d: report state %d\n", drvdata->fd_gpio.gpio, state); + + drvdata->fd_gpio.event_reported = 1; + drvdata->fd_gpio.last_gpio_state = state; + + if (drvdata->fd_gpio.key_code) { + input_event(drvdata->in_dev, EV_KEY, + drvdata->fd_gpio.key_code, !!state); + input_sync(drvdata->in_dev); + } + + if (state && drvdata->fd_gpio.power_key_enabled) { + input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 1); + input_sync(drvdata->in_dev); + input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 0); + input_sync(drvdata->in_dev); + } + + fw_event.ev = (state ? FW_EVENT_FINGER_DOWN : FW_EVENT_FINGER_UP); + + mutex_lock(&drvdata->fw_events_mutex); + + if (kfifo_is_full(&drvdata->fw_events)) { + struct fw_event_desc dummy_fw_event; + + pr_warn("fw events fifo: full, dropping oldest item\n"); + if (!kfifo_get(&drvdata->fw_events, &dummy_fw_event)) + pr_err("fw events fifo: could not remove oldest item\n"); + } + + purge_finger_events(drvdata); + + if (!kfifo_put(&drvdata->fw_events, fw_event)) + pr_err("fw events fifo: error adding item\n"); + + mutex_unlock(&drvdata->fw_events_mutex); + wake_up_interruptible(&drvdata->read_wait_queue); +} + +static void qbt1000_gpio_work_func(struct work_struct *work) +{ + struct qbt1000_drvdata *drvdata = + container_of(work, struct qbt1000_drvdata, fd_gpio.work); + + qbt1000_gpio_report_event(drvdata); + + pm_relax(drvdata->dev); +} + +static irqreturn_t qbt1000_gpio_isr(int irq, void *dev_id) +{ + struct qbt1000_drvdata *drvdata = dev_id; + + if (irq != drvdata->fd_gpio.irq) { + pr_warn("invalid irq %d (expected %d)\n", + irq, drvdata->fd_gpio.irq); + return IRQ_HANDLED; + } + + pm_stay_awake(drvdata->dev); + schedule_work(&drvdata->fd_gpio.work); + + return IRQ_HANDLED; +} + +/** + * qbt1000_ipc_irq_handler() - function processes IPC + * interrupts on its own thread + * @irq: the interrupt that occurred + * @dev_id: pointer to the qbt1000_drvdata + * + * Return: IRQ_HANDLED when complete + */ +static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id) +{ + uint8_t *msg_buffer; + struct fw_ipc_cmd *rx_cmd; + struct fw_ipc_header *header; + int i, j; + uint32_t rxipc = FP_APP_CMD_RX_IPC; + struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id; + int rc = 0; + uint32_t retry_count = 10; + + pm_stay_awake(drvdata->dev); + + mutex_lock(&drvdata->mutex); + + if (irq != drvdata->fw_ipc.irq) { + pr_warn("invalid irq %d (expected %d)\n", + irq, drvdata->fw_ipc.irq); + goto end; + } + + pr_debug("firmware interrupt received (irq %d)\n", irq); + + if (!drvdata->fp_app_handle) + goto end; + + while (retry_count > 0) { + /* + * send the TZ command to fetch the message from firmware + * TZ will process the message if it can + */ + rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0, + &rxipc, sizeof(rxipc), + (void *)&rx_cmd, sizeof(*rx_cmd)); + if (rc < 0) { + msleep(50); // sleep for 50ms before retry + retry_count -= 1; + continue; + } else { + pr_err("retry_count %d\n", retry_count); + break; + } + } + + if (rc < 0) { + pr_err("failure sending tz cmd %d\n", rxipc); + goto end; + } + + if (rx_cmd->status != 0) { + pr_err("tz command failed to complete\n"); + goto end; + } + + msg_buffer = rx_cmd->msg_data; + + for (j = 0; j < rx_cmd->numMsgs; j++) { + header = (struct fw_ipc_header *) msg_buffer; + /* + * given the IPC message type, search for a corresponding + * event for the driver client. If found, add to the events + * FIFO + */ + for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) { + if (g_msg_to_event[i].msg_type == header->msg_type) { + enum qbt1000_fw_event ev = + g_msg_to_event[i].fw_event; + struct fw_event_desc fw_ev_desc; + + mutex_lock(&drvdata->fw_events_mutex); + pr_debug("fw events: add %d\n", (int) ev); + fw_ev_desc.ev = ev; + + if (!kfifo_put(&drvdata->fw_events, fw_ev_desc)) + pr_err("fw events: fifo full, drop event %d\n", + (int) ev); + + mutex_unlock(&drvdata->fw_events_mutex); + break; + } + } + msg_buffer += sizeof(*header) + header->msg_len; + } + wake_up_interruptible(&drvdata->read_wait_queue); +end: + mutex_unlock(&drvdata->mutex); + pm_relax(drvdata->dev); + return IRQ_HANDLED; +} + +static int setup_fd_gpio_irq(struct platform_device *pdev, + struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + int irq; + const char *desc = "qbt_finger_detect"; + + rc = devm_gpio_request_one(&pdev->dev, drvdata->fd_gpio.gpio, + GPIOF_IN, desc); + + if (rc < 0) { + pr_err("failed to request gpio %d, error %d\n", + drvdata->fd_gpio.gpio, rc); + goto end; + } + + irq = gpio_to_irq(drvdata->fd_gpio.gpio); + if (irq < 0) { + rc = irq; + pr_err("unable to get irq number for gpio %d, error %d\n", + drvdata->fd_gpio.gpio, rc); + goto end; + } + + drvdata->fd_gpio.irq = irq; + INIT_WORK(&drvdata->fd_gpio.work, qbt1000_gpio_work_func); + + rc = devm_request_any_context_irq(&pdev->dev, drvdata->fd_gpio.irq, + qbt1000_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + desc, drvdata); + + if (rc < 0) { + pr_err("unable to claim irq %d; error %d\n", + drvdata->fd_gpio.irq, rc); + goto end; + } + +end: + return rc; +} + +static int setup_ipc_irq(struct platform_device *pdev, + struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + const char *desc = "qbt_ipc"; + + drvdata->fw_ipc.irq = gpio_to_irq(drvdata->fw_ipc.gpio); + pr_debug("\nirq %d gpio %d\n", + drvdata->fw_ipc.irq, drvdata->fw_ipc.gpio); + if (drvdata->fw_ipc.irq < 0) { + rc = drvdata->fw_ipc.irq; + pr_err("no irq for gpio %d, error=%d\n", + drvdata->fw_ipc.gpio, rc); + goto end; + } + + rc = devm_gpio_request_one(&pdev->dev, drvdata->fw_ipc.gpio, + GPIOF_IN, desc); + + if (rc < 0) { + pr_err("failed to request gpio %d, error %d\n", + drvdata->fw_ipc.gpio, rc); + goto end; + } + + rc = devm_request_threaded_irq(&pdev->dev, + drvdata->fw_ipc.irq, + NULL, + qbt1000_ipc_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + desc, + drvdata); + + if (rc < 0) { + pr_err("failed to register for ipc irq %d, rc = %d\n", + drvdata->fw_ipc.irq, rc); + goto end; + } + +end: + return rc; +} + +/** + * qbt1000_read_device_tree() - Function reads device tree + * properties into driver data + * @pdev: ptr to platform device object + * @drvdata: ptr to driver data + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_read_device_tree(struct platform_device *pdev, + struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + uint32_t rate; + int gpio; + enum of_gpio_flags flags; + + /* read clock frequency */ + if (of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &rate) == 0) { + pr_debug("clk frequency %d\n", rate); + drvdata->frequency = rate; + } + + /* read IPC gpio */ + drvdata->fw_ipc.gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,ipc-gpio", 0); + if (drvdata->fw_ipc.gpio < 0) { + rc = drvdata->fw_ipc.gpio; + pr_err("ipc gpio not found, error=%d\n", rc); + goto end; + } + + /** + * TODO: Need to revisit after adding GPIO in DTSI- read + * finger detect GPIO configuration + */ + + gpio = of_get_named_gpio_flags(pdev->dev.of_node, + "qcom,finger-detect-gpio", 0, &flags); + if (gpio < 0) { + pr_err("failed to get gpio flags\n"); + rc = gpio; + goto end; + } + + drvdata->fd_gpio.gpio = gpio; + drvdata->fd_gpio.active_low = flags & OF_GPIO_ACTIVE_LOW; + +end: + return rc; +} + +/** + * qbt1000_probe() - Function loads hardware config from device tree + * @pdev: ptr to platform device object + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qbt1000_drvdata *drvdata; + int rc = 0; + + pr_debug("%s begin\n", __func__); + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + rc = qbt1000_read_device_tree(pdev, drvdata); + if (rc < 0) + goto end; + + atomic_set(&drvdata->available, 1); + + mutex_init(&drvdata->mutex); + mutex_init(&drvdata->fw_events_mutex); + + rc = qbt1000_dev_register(drvdata); + if (rc < 0) + goto end; + + INIT_KFIFO(drvdata->fw_events); + init_waitqueue_head(&drvdata->read_wait_queue); + + rc = qbt1000_create_input_device(drvdata); + if (rc < 0) + goto end; + + rc = setup_fd_gpio_irq(pdev, drvdata); + if (rc < 0) + goto end; + + rc = setup_ipc_irq(pdev, drvdata); + if (rc < 0) + goto end; + + rc = device_init_wakeup(&pdev->dev, 1); + if (rc < 0) + goto end; + +end: + pr_debug("%s : %d\n", __func__, rc); + return rc; +} + +static int qbt1000_remove(struct platform_device *pdev) +{ + struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev); + + input_unregister_device(drvdata->in_dev); + + mutex_destroy(&drvdata->mutex); + mutex_destroy(&drvdata->fw_events_mutex); + + device_destroy(drvdata->qbt1000_class, drvdata->qbt1000_cdev.dev); + class_destroy(drvdata->qbt1000_class); + cdev_del(&drvdata->qbt1000_cdev); + unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1); + + device_init_wakeup(&pdev->dev, 0); + + return 0; +} + +static int qbt1000_suspend(struct platform_device *pdev, pm_message_t state) +{ + int rc = 0; + struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev); + + /* + * Returning an error code if driver currently making a TZ call. + * Note: The purpose of this driver is to ensure that the clocks are on + * while making a TZ call. Hence the clock check to determine if the + * driver will allow suspend to occur. + */ + if (!mutex_trylock(&drvdata->mutex)) + return -EBUSY; + + if (drvdata->clock_state) + rc = -EBUSY; + else { + enable_irq_wake(drvdata->fd_gpio.irq); + enable_irq_wake(drvdata->fw_ipc.irq); + } + + mutex_unlock(&drvdata->mutex); + + return rc; +} + +static int qbt1000_resume(struct platform_device *pdev) +{ + struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev); + + disable_irq_wake(drvdata->fd_gpio.irq); + disable_irq_wake(drvdata->fw_ipc.irq); + + return 0; +} + +static const struct of_device_id qbt1000_match[] = { + { .compatible = "qcom,qbt1000" }, + {} +}; + +static struct platform_driver qbt1000_plat_driver = { + .probe = qbt1000_probe, + .remove = qbt1000_remove, + .suspend = qbt1000_suspend, + .resume = qbt1000_resume, + .driver = { + .name = "qbt1000", + .owner = THIS_MODULE, + .of_match_table = qbt1000_match, + }, +}; + +module_platform_driver(qbt1000_plat_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QBT1000 driver"); diff --git a/drivers/soc/qcom/qsee_ipc_irq.c b/drivers/soc/qcom/qsee_ipc_irq.c index 4242b85e58d71ca2d15e9e00b5d2b09690f78a9f..99a29c61f560afff336976707512d629d1d07602 100644 --- a/drivers/soc/qcom/qsee_ipc_irq.c +++ b/drivers/soc/qcom/qsee_ipc_irq.c @@ -26,6 +26,7 @@ #define hwirq_to_index(_irq) (_irq / MAX_BANK_IRQ) #define hwirq_to_bit(_irq) (_irq % MAX_BANK_IRQ) +#define to_hwirq(_index, _bit) ((_index * MAX_BANK_IRQ) + _bit) struct qsee_irq_data { const char *name; @@ -71,6 +72,7 @@ static irqreturn_t qsee_intr(int irq, void *data) u32 status; u32 mask; int i; + int j; for (i = 0; i < qirq->num_banks; i++) { if (qirq->banks[i].irq == irq) { @@ -92,14 +94,14 @@ static irqreturn_t qsee_intr(int irq, void *data) return IRQ_HANDLED; } - for_each_set_bit(i, bank->irq_enabled, bank->data->msb) { - if (!(status & BIT(i))) + for_each_set_bit(j, bank->irq_enabled, bank->data->msb) { + if (!(status & BIT(j))) continue; - irq_pin = irq_find_mapping(qirq->domain, i); + irq_pin = irq_find_mapping(qirq->domain, to_hwirq(i, j)); desc = irq_to_desc(irq_pin); handle_simple_irq(desc); - regmap_write(qirq->regmap, bank->data->clear, BIT(i)); + regmap_write(qirq->regmap, bank->data->clear, BIT(j)); } return IRQ_HANDLED; diff --git a/drivers/soc/qcom/qsee_ipc_irq_bridge.c b/drivers/soc/qcom/qsee_ipc_irq_bridge.c new file mode 100644 index 0000000000000000000000000000000000000000..6da4b6626d371967a5fdad575a9d5bdeeb2591f7 --- /dev/null +++ b/drivers/soc/qcom/qsee_ipc_irq_bridge.c @@ -0,0 +1,618 @@ +/* Copyright (c) 2016-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "qsee_ipc_irq_bridge" +#define DEVICE_NAME MODULE_NAME +#define NUM_LOG_PAGES 4 + +#define QIIB_DBG(x...) do { \ + if (qiib_info->log_ctx) \ + ipc_log_string(qiib_info->log_ctx, x); \ + else \ + pr_debug(x); \ + } while (0) + +#define QIIB_ERR(x...) do { \ + pr_err(x); \ + if (qiib_info->log_ctx) \ + ipc_log_string(qiib_info->log_ctx, x); \ + } while (0) + +static void qiib_cleanup(void); + +/** + * qiib_dev - QSEE IPC IRQ bridge device + * @dev_list: qiib device list. + * @i: Index to this character device. + * @dev_name: Device node name used by the clients. + * @cdev: structure to the internal character device. + * @devicep: Pointer to the qiib class device structure. + * @poll_wait_queue: poll thread wait queue. + * @irq_num: IRQ number usd for this device. + * @rx_irq_reset_reg: Reference to the register to reset the rx irq + * line, if applicable. + * @irq_mask: Mask written to @rx_irq_reset_reg to clear the irq. + * @irq_pending_count: The number of IRQs pending. + * @irq_pending_count_lock: Lock to protect @irq_pending_cont. + * @ssr_name: Name of the subsystem recognized by the SSR framework. + * @nb: SSR Notifier callback. + * @notifier_handle: SSR Notifier handle. + * @in_reset: Flag to check the SSR state. + */ +struct qiib_dev { + struct list_head dev_list; + uint32_t i; + + const char *dev_name; + struct cdev cdev; + struct device *devicep; + + wait_queue_head_t poll_wait_queue; + + uint32_t irq_line; + void __iomem *rx_irq_reset_reg; + uint32_t irq_mask; + uint32_t irq_pending_count; + spinlock_t irq_pending_count_lock; + + const char *ssr_name; + struct notifier_block nb; + void *notifier_handle; + bool in_reset; +}; + +/** + * qiib_driver_data - QSEE IPC IRQ bridge driver data + * @list: list of all nodes devices. + * @list_lock: lock to synchronize the @list access. + * @nprots: Number of device nodes. + * @classp: Pointer to the device class. + * @dev_num: qiib device number. + * @log_ctx: pointer to the ipc logging context. + */ +struct qiib_driver_data { + struct list_head list; + struct mutex list_lock; + + int nports; + struct class *classp; + dev_t dev_num; + + void *log_ctx; +}; + +static struct qiib_driver_data *qiib_info; + +/** + * qiib_driver_data_init() - Initialize the QIIB driver data. + * + * This function used to initialize the driver specific data + * during the module init. + * + * Return: 0 for success, Standard Linux errors + */ +static int qiib_driver_data_init(void) +{ + qiib_info = kzalloc(sizeof(*qiib_info), GFP_KERNEL); + if (!qiib_info) + return -ENOMEM; + + INIT_LIST_HEAD(&qiib_info->list); + mutex_init(&qiib_info->list_lock); + + qiib_info->log_ctx = ipc_log_context_create(NUM_LOG_PAGES, + "qsee_ipc_irq_bridge", 0); + if (!qiib_info->log_ctx) + QIIB_ERR("%s: unable to create logging context\n", __func__); + + return 0; +} + +/** + * qiib_driver_data_deinit() - De-Initialize the QIIB driver data. + * + * This function used to de-initialize the driver specific data + * during the module exit. + */ +static void qiib_driver_data_deinit(void) +{ + qiib_cleanup(); + if (!qiib_info->log_ctx) + ipc_log_context_destroy(qiib_info->log_ctx); + kfree(qiib_info); + qiib_info = NULL; +} + +/** + * qiib_restart_notifier_cb() - SSR restart notifier callback function + * @this: Notifier block used by the SSR framework + * @code: The SSR code for which stage of restart is occurring + * @data: Structure containing private data - not used here. + * + * This function is a callback for the SSR framework. From here we initiate + * our handling of SSR. + * + * Return: Status of SSR handling + */ +static int qiib_restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data) +{ + struct qiib_dev *devp = container_of(this, struct qiib_dev, nb); + + if (code == SUBSYS_BEFORE_SHUTDOWN) { + QIIB_DBG("%s: %s: subsystem restart for %s\n", __func__, + "SUBSYS_BEFORE_SHUTDOWN", + devp->ssr_name); + devp->in_reset = true; + wake_up_interruptible(&devp->poll_wait_queue); + } else if (code == SUBSYS_AFTER_POWERUP) { + QIIB_DBG("%s: %s: subsystem restart for %s\n", __func__, + "SUBSYS_AFTER_POWERUP", + devp->ssr_name); + devp->in_reset = false; + } + return NOTIFY_DONE; +} + +/** + * qiib_poll() - poll() syscall for the qiib device + * @file: Pointer to the file structure. + * @wait: pointer to Poll table. + * + * This function is used to poll on the qiib device when + * userspace client do a poll() system call. All input arguments are + * validated by the virtual file system before calling this function. + * + * Return: POLLIN for interrupt intercepted case and POLLRDHUP for SSR. + */ +static unsigned int qiib_poll(struct file *file, poll_table *wait) +{ + struct qiib_dev *devp = file->private_data; + unsigned int mask = 0; + unsigned long flags; + + if (!devp) { + QIIB_ERR("%s on NULL device\n", __func__); + return POLLERR; + } + + if (devp->in_reset) + return POLLRDHUP; + + poll_wait(file, &devp->poll_wait_queue, wait); + spin_lock_irqsave(&devp->irq_pending_count_lock, flags); + if (devp->irq_pending_count) { + mask |= POLLIN; + QIIB_DBG("%s set POLLIN on [%s] count[%d]\n", + __func__, devp->dev_name, + devp->irq_pending_count); + devp->irq_pending_count = 0; + } + spin_unlock_irqrestore(&devp->irq_pending_count_lock, flags); + + if (devp->in_reset) { + mask |= POLLRDHUP; + QIIB_DBG("%s set POLLRDHUP on [%s] count[%d]\n", + __func__, devp->dev_name, + devp->irq_pending_count); + } + return mask; +} + +/** + * qiib_open() - open() syscall for the qiib device + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * This function is used to open the qiib device when + * userspace client do a open() system call. All input arguments are + * validated by the virtual file system before calling this function. + * + * Return: 0 for success, Standard Linux errors + */ +static int qiib_open(struct inode *inode, struct file *file) +{ + struct qiib_dev *devp = NULL; + + devp = container_of(inode->i_cdev, struct qiib_dev, cdev); + if (!devp) { + QIIB_ERR("%s on NULL device\n", __func__); + return -EINVAL; + } + file->private_data = devp; + QIIB_DBG("%s on [%s]\n", __func__, devp->dev_name); + return 0; +} + +/** + * qiib_release() - release operation on qiibdevice + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * This function is used to release the qiib device when + * userspace client do a close() system call. All input arguments are + * validated by the virtual file system before calling this function. + */ +static int qiib_release(struct inode *inode, struct file *file) +{ + struct qiib_dev *devp = file->private_data; + + if (!devp) { + QIIB_ERR("%s on NULL device\n", __func__); + return -EINVAL; + } + + QIIB_DBG("%s on [%s]\n", __func__, devp->dev_name); + return 0; +} + +static const struct file_operations qiib_fops = { + .owner = THIS_MODULE, + .open = qiib_open, + .release = qiib_release, + .poll = qiib_poll, +}; + +/** + * qiib_add_device() - Initialize qiib device and add cdev + * @devp: pointer to the qiib device. + * @i: index of the qiib device. + * + * Return: 0 for success, Standard Linux errors + */ +static int qiib_add_device(struct qiib_dev *devp, int i) +{ + int ret = 0; + + devp->i = i; + init_waitqueue_head(&devp->poll_wait_queue); + spin_lock_init(&devp->irq_pending_count_lock); + + cdev_init(&devp->cdev, &qiib_fops); + devp->cdev.owner = THIS_MODULE; + + ret = cdev_add(&devp->cdev, qiib_info->dev_num + i, 1); + if (IS_ERR_VALUE((unsigned long)ret)) { + QIIB_ERR("%s: cdev_add() failed for dev [%s] ret:%i\n", + __func__, devp->dev_name, ret); + return ret; + } + + devp->devicep = device_create(qiib_info->classp, + NULL, + (qiib_info->dev_num + i), + NULL, + devp->dev_name); + + if (IS_ERR_OR_NULL(devp->devicep)) { + QIIB_ERR("%s: device_create() failed for dev [%s]\n", + __func__, devp->dev_name); + ret = -ENOMEM; + cdev_del(&devp->cdev); + return ret; + } + + mutex_lock(&qiib_info->list_lock); + list_add(&devp->dev_list, &qiib_info->list); + mutex_unlock(&qiib_info->list_lock); + + return ret; +} + +static irqreturn_t qiib_irq_handler(int irq, void *priv) +{ + struct qiib_dev *devp = priv; + unsigned long flags; + + spin_lock_irqsave(&devp->irq_pending_count_lock, flags); + devp->irq_pending_count++; + spin_unlock_irqrestore(&devp->irq_pending_count_lock, flags); + wake_up_interruptible(&devp->poll_wait_queue); + + if (devp->rx_irq_reset_reg) + writel_relaxed(devp->irq_mask, devp->rx_irq_reset_reg); + + QIIB_DBG("%s name[%s] pend_count[%d]\n", __func__, + devp->dev_name, devp->irq_pending_count); + + return IRQ_HANDLED; +} + +/** + * qiib_parse_node() - parse node from device tree binding + * @node: pointer to device tree node + * @devp: pointer to the qiib device + * + * Return: 0 on success, -ENODEV on failure. + */ +static int qiib_parse_node(struct device_node *node, struct qiib_dev *devp) +{ + char *key; + const char *subsys_name; + const char *dev_name; + uint32_t irqtype; + uint32_t irq_clear[2]; + struct irq_data *irqtype_data; + int ret = -ENODEV; + + key = "qcom,dev-name"; + ret = of_property_read_string(node, key, &dev_name); + if (ret) { + QIIB_ERR("%s: missing key: %s\n", __func__, key); + goto missing_key; + } + QIIB_DBG("%s: %s = %s\n", __func__, key, dev_name); + + key = "interrupts"; + devp->irq_line = irq_of_parse_and_map(node, 0); + if (!devp->irq_line) { + QIIB_ERR("%s: missing key: %s\n", __func__, key); + goto missing_key; + } + QIIB_DBG("%s: %s = %d\n", __func__, key, devp->irq_line); + + irqtype_data = irq_get_irq_data(devp->irq_line); + if (!irqtype_data) { + QIIB_ERR("%s: get irqdata fail:%d\n", __func__, devp->irq_line); + goto missing_key; + } + irqtype = irqd_get_trigger_type(irqtype_data); + QIIB_DBG("%s: irqtype = %d\n", __func__, irqtype); + + key = "label"; + ret = of_property_read_string(node, key, &subsys_name); + if (ret) { + QIIB_ERR("%s: missing key: %s\n", __func__, key); + goto missing_key; + } + QIIB_DBG("%s: %s = %s\n", __func__, key, subsys_name); + + if (irqtype & IRQF_TRIGGER_HIGH) { + key = "qcom,rx-irq-clr-mask"; + ret = of_property_read_u32(node, key, &devp->irq_mask); + if (ret) { + QIIB_ERR("%s: missing key: %s\n", __func__, key); + ret = -ENODEV; + goto missing_key; + } + QIIB_DBG("%s: %s = %d\n", __func__, key, devp->irq_mask); + + key = "qcom,rx-irq-clr"; + ret = of_property_read_u32_array(node, key, irq_clear, + ARRAY_SIZE(irq_clear)); + if (ret) { + QIIB_ERR("%s: missing key: %s\n", __func__, key); + ret = -ENODEV; + goto missing_key; + } + + devp->rx_irq_reset_reg = ioremap_nocache(irq_clear[0], + irq_clear[1]); + if (!devp->rx_irq_reset_reg) { + QIIB_ERR("%s: unable to map rx reset reg\n", __func__); + ret = -ENOMEM; + goto missing_key; + } + } + + devp->dev_name = dev_name; + devp->ssr_name = subsys_name; + devp->nb.notifier_call = qiib_restart_notifier_cb; + + devp->notifier_handle = subsys_notif_register_notifier(devp->ssr_name, + &devp->nb); + if (IS_ERR_OR_NULL(devp->notifier_handle)) { + QIIB_ERR("%s: Could not register SSR notifier cb\n", __func__); + ret = -EINVAL; + goto ssr_reg_fail; + } + + ret = request_irq(devp->irq_line, qiib_irq_handler, irqtype, + devp->dev_name, devp); + if (ret < 0) { + QIIB_ERR("%s: request_irq() failed on %d\n", __func__, + devp->irq_line); + goto req_irq_fail; + } + + return ret; + +req_irq_fail: + subsys_notif_unregister_notifier(devp->notifier_handle, &devp->nb); +ssr_reg_fail: + if (devp->rx_irq_reset_reg) { + iounmap(devp->rx_irq_reset_reg); + devp->rx_irq_reset_reg = NULL; + } +missing_key: + return ret; +} + +/** + * qiib_cleanup - cleanup all the resources + * + * This function remove all the memory and unregister + * the char device region. + */ +static void qiib_cleanup(void) +{ + struct qiib_dev *devp; + struct qiib_dev *index; + + mutex_lock(&qiib_info->list_lock); + list_for_each_entry_safe(devp, index, &qiib_info->list, dev_list) { + cdev_del(&devp->cdev); + list_del(&devp->dev_list); + device_destroy(qiib_info->classp, + MKDEV(MAJOR(qiib_info->dev_num), devp->i)); + if (devp->notifier_handle) + subsys_notif_unregister_notifier(devp->notifier_handle, + &devp->nb); + kfree(devp); + } + mutex_unlock(&qiib_info->list_lock); + + if (!IS_ERR_OR_NULL(qiib_info->classp)) + class_destroy(qiib_info->classp); + + unregister_chrdev_region(MAJOR(qiib_info->dev_num), qiib_info->nports); +} + +/** + * qiib_alloc_chrdev_region() - allocate the char device region + * + * This function allocate memory for qiib character-device region and + * create the class. + */ +static int qiib_alloc_chrdev_region(void) +{ + int ret; + + ret = alloc_chrdev_region(&qiib_info->dev_num, + 0, + qiib_info->nports, + DEVICE_NAME); + if (IS_ERR_VALUE((unsigned long)ret)) { + QIIB_ERR("%s: alloc_chrdev_region() failed ret:%i\n", + __func__, ret); + return ret; + } + + qiib_info->classp = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(qiib_info->classp)) { + QIIB_ERR("%s: class_create() failed ENOMEM\n", __func__); + ret = -ENOMEM; + unregister_chrdev_region(MAJOR(qiib_info->dev_num), + qiib_info->nports); + return ret; + } + + return 0; +} + +static int qsee_ipc_irq_bridge_probe(struct platform_device *pdev) +{ + int ret; + struct device_node *node; + struct qiib_dev *devp; + int i = 0; + + qiib_info->nports = of_get_available_child_count(pdev->dev.of_node); + if (!qiib_info->nports) { + QIIB_ERR("%s:Fail nports = %d\n", __func__, qiib_info->nports); + return -EINVAL; + } + + ret = qiib_alloc_chrdev_region(); + if (ret) { + QIIB_ERR("%s: chrdev_region allocation failed ret:%i\n", + __func__, ret); + return ret; + } + + for_each_available_child_of_node(pdev->dev.of_node, node) { + devp = kzalloc(sizeof(*devp), GFP_KERNEL); + if (IS_ERR_OR_NULL(devp)) { + QIIB_ERR("%s:Allocation failed id:%d\n", __func__, i); + ret = -ENOMEM; + goto error; + } + + ret = qiib_parse_node(node, devp); + if (ret) { + QIIB_ERR("%s:qiib_parse_node failed %d\n", __func__, i); + kfree(devp); + goto error; + } + + ret = qiib_add_device(devp, i); + if (ret < 0) { + QIIB_ERR("%s: add [%s] device failed ret=%d\n", + __func__, devp->dev_name, ret); + kfree(devp); + goto error; + } + i++; + } + + QIIB_DBG("%s: Driver Initialized.\n", __func__); + return 0; + +error: + qiib_cleanup(); + return ret; +} + +static int qsee_ipc_irq_bridge_remove(struct platform_device *pdev) +{ + qiib_cleanup(); + return 0; +} + +static const struct of_device_id qsee_ipc_irq_bridge_match_table[] = { + { .compatible = "qcom,qsee-ipc-irq-bridge" }, + {}, +}; + +static struct platform_driver qsee_ipc_irq_bridge_driver = { + .probe = qsee_ipc_irq_bridge_probe, + .remove = qsee_ipc_irq_bridge_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = qsee_ipc_irq_bridge_match_table, + }, +}; + +static int __init qsee_ipc_irq_bridge_init(void) +{ + int ret; + + ret = qiib_driver_data_init(); + if (ret) { + QIIB_ERR("%s: driver data init failed %d\n", + __func__, ret); + return ret; + } + + ret = platform_driver_register(&qsee_ipc_irq_bridge_driver); + if (ret) { + QIIB_ERR("%s: platform driver register failed %d\n", + __func__, ret); + return ret; + } + + return 0; +} +module_init(qsee_ipc_irq_bridge_init); + +static void __exit qsee_ipc_irq_bridge_exit(void) +{ + platform_driver_unregister(&qsee_ipc_irq_bridge_driver); + qiib_driver_data_deinit(); +} +module_exit(qsee_ipc_irq_bridge_exit); +MODULE_DESCRIPTION("QSEE IPC interrupt bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c index ec4ff4959fb644593ded909d7dbebc3fd5b672db..51749d3479f9228ea25cd6414d7a5e05746743e7 100644 --- a/drivers/soc/qcom/ramdump.c +++ b/drivers/soc/qcom/ramdump.c @@ -38,6 +38,8 @@ static DEFINE_MUTEX(rd_minor_mutex); static DEFINE_IDA(rd_minor_id); static bool ramdump_devnode_inited; #define RAMDUMP_WAIT_MSECS 120000 +#define MAX_STRTBL_SIZE 512 +#define MAX_NAME_LENGTH 16 struct ramdump_device { char name[256]; @@ -447,12 +449,126 @@ static int _do_ramdump(void *handle, struct ramdump_segment *segments, } +static inline unsigned int set_section_name(const char *name, + struct elfhdr *ehdr) +{ + char *strtab = elf_str_table(ehdr); + static int strtable_idx = 1; + int idx, ret = 0; + + idx = strtable_idx; + if ((strtab == NULL) || (name == NULL)) + return 0; + + ret = idx; + idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH); + strtable_idx = idx + 1; + + return ret; +} + +static int _do_minidump(void *handle, struct ramdump_segment *segments, + int nsegments) +{ + int ret, i; + struct ramdump_device *rd_dev = (struct ramdump_device *)handle; + struct elfhdr *ehdr; + struct elf_shdr *shdr; + unsigned long offset, strtbl_off; + + if (!rd_dev->consumer_present) { + pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name); + return -EPIPE; + } + + rd_dev->segments = segments; + rd_dev->nsegments = nsegments; + + rd_dev->elfcore_size = sizeof(*ehdr) + + (sizeof(*shdr) * (nsegments + 2)) + MAX_STRTBL_SIZE; + ehdr = kzalloc(rd_dev->elfcore_size, GFP_KERNEL); + rd_dev->elfcore_buf = (char *)ehdr; + if (!rd_dev->elfcore_buf) + return -ENOMEM; + + memcpy(ehdr->e_ident, ELFMAG, SELFMAG); + ehdr->e_ident[EI_CLASS] = ELF_CLASS; + ehdr->e_ident[EI_DATA] = ELF_DATA; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELF_OSABI; + ehdr->e_type = ET_CORE; + ehdr->e_machine = ELF_ARCH; + ehdr->e_version = EV_CURRENT; + ehdr->e_ehsize = sizeof(*ehdr); + ehdr->e_shoff = sizeof(*ehdr); + ehdr->e_shentsize = sizeof(*shdr); + ehdr->e_shstrndx = 1; + + + offset = rd_dev->elfcore_size; + shdr = (struct elf_shdr *)(ehdr + 1); + strtbl_off = sizeof(*ehdr) + sizeof(*shdr) * (nsegments + 2); + shdr++; + shdr->sh_type = SHT_STRTAB; + shdr->sh_offset = (elf_addr_t)strtbl_off; + shdr->sh_size = MAX_STRTBL_SIZE; + shdr->sh_entsize = 0; + shdr->sh_flags = 0; + shdr->sh_name = set_section_name("STR_TBL", ehdr); + shdr++; + + for (i = 0; i < nsegments; i++, shdr++) { + /* Update elf header */ + shdr->sh_type = SHT_PROGBITS; + shdr->sh_name = set_section_name(segments[i].name, ehdr); + shdr->sh_addr = (elf_addr_t)segments[i].address; + shdr->sh_size = segments[i].size; + shdr->sh_flags = SHF_WRITE; + shdr->sh_offset = offset; + shdr->sh_entsize = 0; + offset += shdr->sh_size; + } + ehdr->e_shnum = nsegments + 2; + + rd_dev->data_ready = 1; + rd_dev->ramdump_status = -1; + + reinit_completion(&rd_dev->ramdump_complete); + + /* Tell userspace that the data is ready */ + wake_up(&rd_dev->dump_wait_q); + + /* Wait (with a timeout) to let the ramdump complete */ + ret = wait_for_completion_timeout(&rd_dev->ramdump_complete, + msecs_to_jiffies(RAMDUMP_WAIT_MSECS)); + + if (!ret) { + pr_err("Ramdump(%s): Timed out waiting for userspace.\n", + rd_dev->name); + ret = -EPIPE; + } else { + ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE; + } + + rd_dev->data_ready = 0; + rd_dev->elfcore_size = 0; + kfree(rd_dev->elfcore_buf); + rd_dev->elfcore_buf = NULL; + return ret; +} + int do_ramdump(void *handle, struct ramdump_segment *segments, int nsegments) { return _do_ramdump(handle, segments, nsegments, false); } EXPORT_SYMBOL(do_ramdump); +int do_minidump(void *handle, struct ramdump_segment *segments, int nsegments) +{ + return _do_minidump(handle, segments, nsegments); +} +EXPORT_SYMBOL(do_minidump); + int do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments) { diff --git a/drivers/soc/qcom/rpm-smd-debug.c b/drivers/soc/qcom/rpm-smd-debug.c new file mode 100644 index 0000000000000000000000000000000000000000..6ae9f088e52d5919e1e7287c392276aa416aa84b --- /dev/null +++ b/drivers/soc/qcom/rpm-smd-debug.c @@ -0,0 +1,151 @@ +/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "rpm-smd-debug: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MSG_BUFFER 350 +#define MAX_KEY_VALUE_PAIRS 20 + +static struct dentry *rpm_debugfs_dir; + +static u32 string_to_uint(const u8 *str) +{ + int i, len; + u32 output = 0; + + len = strnlen(str, sizeof(u32)); + for (i = 0; i < len; i++) + output |= str[i] << (i * 8); + + return output; +} + +static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer, + size_t count, loff_t *position) +{ + char buf[MAX_MSG_BUFFER], rsc_type_str[6] = {}, rpm_set[8] = {}, + key_str[6] = {}; + int i, pos = -1, set = -1, nelems = -1; + char *cmp; + uint32_t rsc_type = 0, rsc_id = 0, key = 0, data = 0; + struct msm_rpm_request *req; + + count = min(count, sizeof(buf) - 1); + if (copy_from_user(&buf, user_buffer, count)) + return -EFAULT; + buf[count] = '\0'; + cmp = strstrip(buf); + + if (sscanf(cmp, "%7s %5s %u %d %n", rpm_set, rsc_type_str, + &rsc_id, &nelems, &pos) != 4) { + pr_err("Invalid number of arguments passed\n"); + goto err; + } + + if (strlen(rpm_set) > 6 || strlen(rsc_type_str) > 4) { + pr_err("Invalid value of set or resource type\n"); + goto err; + } + + if (!strcmp(rpm_set, "active")) + set = 0; + else if (!strcmp(rpm_set, "sleep")) + set = 1; + + rsc_type = string_to_uint(rsc_type_str); + + if (set < 0 || nelems < 0) { + pr_err("Invalid value of set or nelems\n"); + goto err; + } + if (nelems > MAX_KEY_VALUE_PAIRS) { + pr_err("Exceeded max no of key-value entries\n"); + goto err; + } + + req = msm_rpm_create_request(set, rsc_type, rsc_id, nelems); + if (!req) + return -ENOMEM; + + for (i = 0; i < nelems; i++) { + cmp += pos; + if (sscanf(cmp, "%5s %n", key_str, &pos) != 1) { + pr_err("Invalid number of arguments passed\n"); + goto err; + } + + if (strlen(key_str) > 4) { + pr_err("Key value cannot be more than 4 charecters"); + goto err; + } + key = string_to_uint(key_str); + if (!key) { + pr_err("Key values entered incorrectly\n"); + goto err; + } + + cmp += pos; + if (sscanf(cmp, "%u %n", &data, &pos) != 1) { + pr_err("Invalid number of arguments passed\n"); + goto err; + } + + if (msm_rpm_add_kvp_data(req, key, + (void *)&data, sizeof(data))) + goto err_request; + } + + if (msm_rpm_wait_for_ack(msm_rpm_send_request(req))) + pr_err("Sending the RPM message failed\n"); + +err_request: + msm_rpm_free_request(req); +err: + return count; +} + +static const struct file_operations rsc_ops = { + .write = rsc_ops_write, +}; + +static int __init rpm_smd_debugfs_init(void) +{ + rpm_debugfs_dir = debugfs_create_dir("rpm_send_msg", NULL); + if (!rpm_debugfs_dir) + return -ENOMEM; + + if (!debugfs_create_file("message", 0200, rpm_debugfs_dir, NULL, + &rsc_ops)) + return -ENOMEM; + + return 0; +} +late_initcall(rpm_smd_debugfs_init); + +static void __exit rpm_smd_debugfs_exit(void) +{ + debugfs_remove_recursive(rpm_debugfs_dir); +} +module_exit(rpm_smd_debugfs_exit); + +MODULE_DESCRIPTION("RPM SMD Debug Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c new file mode 100644 index 0000000000000000000000000000000000000000..c04916cfbd7740b60bf2d0d7c6a30012cf8113e7 --- /dev/null +++ b/drivers/soc/qcom/rpm-smd.c @@ -0,0 +1,2165 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +/* Debug Definitions */ +enum { + MSM_RPM_LOG_REQUEST_PRETTY = BIT(0), + MSM_RPM_LOG_REQUEST_RAW = BIT(1), + MSM_RPM_LOG_REQUEST_SHOW_MSG_ID = BIT(2), +}; + +static int msm_rpm_debug_mask; +module_param_named( + debug_mask, msm_rpm_debug_mask, int, 0644 +); + +struct msm_rpm_driver_data { + const char *ch_name; + uint32_t ch_type; + struct smd_channel *ch_info; + struct work_struct work; + spinlock_t smd_lock_write; + spinlock_t smd_lock_read; + struct completion smd_open; +}; + +struct glink_apps_rpm_data { + const char *name; + const char *edge; + const char *xprt; + void *glink_handle; + struct glink_link_info *link_info; + struct glink_open_config *open_cfg; + struct work_struct work; +}; + +static bool glink_enabled; +static struct glink_apps_rpm_data *glink_data; + +#define DEFAULT_BUFFER_SIZE 256 +#define DEBUG_PRINT_BUFFER_SIZE 512 +#define MAX_SLEEP_BUFFER 128 +#define GFP_FLAG(noirq) (noirq ? GFP_ATOMIC : GFP_NOIO) +#define INV_RSC "resource does not exist" +#define ERR "err\0" +#define MAX_ERR_BUFFER_SIZE 128 +#define MAX_WAIT_ON_ACK 24 +#define INIT_ERROR 1 +#define V1_PROTOCOL_VERSION 0x31726576 /* rev1 */ +#define V0_PROTOCOL_VERSION 0 /* rev0 */ +#define RPM_MSG_TYPE_OFFSET 16 +#define RPM_MSG_TYPE_SIZE 8 +#define RPM_SET_TYPE_OFFSET 28 +#define RPM_SET_TYPE_SIZE 4 +#define RPM_REQ_LEN_OFFSET 0 +#define RPM_REQ_LEN_SIZE 16 +#define RPM_MSG_VERSION_OFFSET 24 +#define RPM_MSG_VERSION_SIZE 8 +#define RPM_MSG_VERSION 1 +#define RPM_MSG_SET_OFFSET 28 +#define RPM_MSG_SET_SIZE 4 +#define RPM_RSC_ID_OFFSET 16 +#define RPM_RSC_ID_SIZE 12 +#define RPM_DATA_LEN_OFFSET 0 +#define RPM_DATA_LEN_SIZE 16 +#define RPM_HDR_SIZE ((rpm_msg_fmt_ver == RPM_MSG_V0_FMT) ?\ + sizeof(struct rpm_v0_hdr) : sizeof(struct rpm_v1_hdr)) +#define CLEAR_FIELD(offset, size) (~GENMASK(offset + size - 1, offset)) + +static ATOMIC_NOTIFIER_HEAD(msm_rpm_sleep_notifier); +static bool standalone; +static int probe_status = -EPROBE_DEFER; +static int msm_rpm_read_smd_data(char *buf); +static void msm_rpm_process_ack(uint32_t msg_id, int errno); + +int msm_rpm_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&msm_rpm_sleep_notifier, nb); +} + +int msm_rpm_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&msm_rpm_sleep_notifier, nb); +} + +static struct workqueue_struct *msm_rpm_smd_wq; + +enum { + MSM_RPM_MSG_REQUEST_TYPE = 0, + MSM_RPM_MSG_TYPE_NR, +}; + +static const uint32_t msm_rpm_request_service_v1[MSM_RPM_MSG_TYPE_NR] = { + 0x716572, /* 'req\0' */ +}; + +enum { + RPM_V1_REQUEST_SERVICE, + RPM_V1_SYSTEMDB_SERVICE, + RPM_V1_COMMAND_SERVICE, + RPM_V1_ACK_SERVICE, + RPM_V1_NACK_SERVICE, +} msm_rpm_request_service_v2; + +struct rpm_v0_hdr { + uint32_t service_type; + uint32_t request_len; +}; + +struct rpm_v1_hdr { + uint32_t request_hdr; +}; + +struct rpm_message_header_v0 { + struct rpm_v0_hdr hdr; + uint32_t msg_id; + enum msm_rpm_set set; + uint32_t resource_type; + uint32_t resource_id; + uint32_t data_len; +}; + +struct rpm_message_header_v1 { + struct rpm_v1_hdr hdr; + uint32_t msg_id; + uint32_t resource_type; + uint32_t request_details; +}; + +struct msm_rpm_ack_msg_v0 { + uint32_t req; + uint32_t req_len; + uint32_t rsc_id; + uint32_t msg_len; + uint32_t id_ack; +}; + +struct msm_rpm_ack_msg_v1 { + uint32_t request_hdr; + uint32_t id_ack; +}; + +struct kvp { + unsigned int k; + unsigned int s; +}; + +struct msm_rpm_kvp_data { + uint32_t key; + uint32_t nbytes; /* number of bytes */ + uint8_t *value; + bool valid; +}; + +struct slp_buf { + struct rb_node node; + char ubuf[MAX_SLEEP_BUFFER]; + char *buf; + bool valid; +}; + +enum rpm_msg_fmts { + RPM_MSG_V0_FMT, + RPM_MSG_V1_FMT +}; + +static uint32_t rpm_msg_fmt_ver; +module_param_named( + rpm_msg_fmt_ver, rpm_msg_fmt_ver, uint, 0444 +); + +static struct rb_root tr_root = RB_ROOT; +static int (*msm_rpm_send_buffer)(char *buf, uint32_t size, bool noirq); +static int msm_rpm_send_smd_buffer(char *buf, uint32_t size, bool noirq); +static int msm_rpm_glink_send_buffer(char *buf, uint32_t size, bool noirq); +static uint32_t msm_rpm_get_next_msg_id(void); + +static inline uint32_t get_offset_value(uint32_t val, uint32_t offset, + uint32_t size) +{ + return (((val) & GENMASK(offset + size - 1, offset)) + >> offset); +} + +static inline void change_offset_value(uint32_t *val, uint32_t offset, + uint32_t size, int32_t val1) +{ + uint32_t member = *val; + uint32_t offset_val = get_offset_value(member, offset, size); + uint32_t mask = (1 << size) - 1; + + offset_val += val1; + *val &= CLEAR_FIELD(offset, size); + *val |= ((offset_val & mask) << offset); +} + +static inline void set_offset_value(uint32_t *val, uint32_t offset, + uint32_t size, uint32_t val1) +{ + uint32_t mask = (1 << size) - 1; + + *val &= CLEAR_FIELD(offset, size); + *val |= ((val1 & mask) << offset); +} +static uint32_t get_msg_id(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->msg_id; + + return ((struct rpm_message_header_v1 *)buf)->msg_id; + +} + +static uint32_t get_ack_msg_id(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct msm_rpm_ack_msg_v0 *)buf)->id_ack; + + return ((struct msm_rpm_ack_msg_v1 *)buf)->id_ack; + +} + +static uint32_t get_rsc_type(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->resource_type; + + return ((struct rpm_message_header_v1 *)buf)->resource_type; + +} + +static uint32_t get_set_type(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->set; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_SET_TYPE_OFFSET, + RPM_SET_TYPE_SIZE); +} + +static uint32_t get_data_len(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->data_len; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_DATA_LEN_OFFSET, + RPM_DATA_LEN_SIZE); +} + +static uint32_t get_rsc_id(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->resource_id; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_RSC_ID_OFFSET, + RPM_RSC_ID_SIZE); +} + +static uint32_t get_ack_req_len(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct msm_rpm_ack_msg_v0 *)buf)->req_len; + + return get_offset_value(((struct msm_rpm_ack_msg_v1 *)buf)-> + request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE); +} + +static uint32_t get_ack_msg_type(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct msm_rpm_ack_msg_v0 *)buf)->req; + + return get_offset_value(((struct msm_rpm_ack_msg_v1 *)buf)-> + request_hdr, RPM_MSG_TYPE_OFFSET, + RPM_MSG_TYPE_SIZE); +} + +static uint32_t get_req_len(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return ((struct rpm_message_header_v0 *)buf)->hdr.request_len; + + return get_offset_value(((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE); +} + +static void set_msg_ver(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver) { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_MSG_VERSION_OFFSET, + RPM_MSG_VERSION_SIZE, val); + } else { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_MSG_VERSION_OFFSET, + RPM_MSG_VERSION_SIZE, 0); + } +} + +static void set_req_len(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) { + ((struct rpm_message_header_v0 *)buf)->hdr.request_len = val; + } else { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE, val); + } +} + +static void change_req_len(char *buf, int32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) { + ((struct rpm_message_header_v0 *)buf)->hdr.request_len += val; + } else { + change_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_REQ_LEN_OFFSET, + RPM_REQ_LEN_SIZE, val); + } +} + +static void set_msg_type(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) { + ((struct rpm_message_header_v0 *)buf)->hdr.service_type = + msm_rpm_request_service_v1[val]; + } else { + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + hdr.request_hdr, RPM_MSG_TYPE_OFFSET, + RPM_MSG_TYPE_SIZE, RPM_V1_REQUEST_SERVICE); + } +} + +static void set_rsc_id(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->resource_id = val; + else + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_RSC_ID_OFFSET, + RPM_RSC_ID_SIZE, val); +} + +static void set_data_len(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->data_len = val; + else + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_DATA_LEN_OFFSET, + RPM_DATA_LEN_SIZE, val); +} +static void change_data_len(char *buf, int32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->data_len += val; + else + change_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_DATA_LEN_OFFSET, + RPM_DATA_LEN_SIZE, val); +} + +static void set_set_type(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->set = val; + else + set_offset_value(&((struct rpm_message_header_v1 *)buf)-> + request_details, RPM_SET_TYPE_OFFSET, + RPM_SET_TYPE_SIZE, val); +} +static void set_msg_id(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->msg_id = val; + else + ((struct rpm_message_header_v1 *)buf)->msg_id = val; + +} + +static void set_rsc_type(char *buf, uint32_t val) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + ((struct rpm_message_header_v0 *)buf)->resource_type = val; + else + ((struct rpm_message_header_v1 *)buf)->resource_type = val; +} + +static inline int get_buf_len(char *buf) +{ + return get_req_len(buf) + RPM_HDR_SIZE; +} + +static inline struct kvp *get_first_kvp(char *buf) +{ + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + return (struct kvp *)(buf + + sizeof(struct rpm_message_header_v0)); + else + return (struct kvp *)(buf + + sizeof(struct rpm_message_header_v1)); +} + +static inline struct kvp *get_next_kvp(struct kvp *k) +{ + return (struct kvp *)((void *)k + sizeof(*k) + k->s); +} + +static inline void *get_data(struct kvp *k) +{ + return (void *)k + sizeof(*k); +} + + +static void delete_kvp(char *buf, struct kvp *d) +{ + struct kvp *n; + int dec; + uint32_t size; + + n = get_next_kvp(d); + dec = (void *)n - (void *)d; + size = get_data_len(buf) - + ((void *)n - (void *)get_first_kvp(buf)); + + memcpy((void *)d, (void *)n, size); + + change_data_len(buf, -dec); + change_req_len(buf, -dec); +} + +static inline void update_kvp_data(struct kvp *dest, struct kvp *src) +{ + memcpy(get_data(dest), get_data(src), src->s); +} + +static void add_kvp(char *buf, struct kvp *n) +{ + int32_t inc = sizeof(*n) + n->s; + + if (get_req_len(buf) + inc > MAX_SLEEP_BUFFER) { + WARN_ON(get_req_len(buf) + inc > MAX_SLEEP_BUFFER); + return; + } + + memcpy(buf + get_buf_len(buf), n, inc); + + change_data_len(buf, inc); + change_req_len(buf, inc); +} + +static struct slp_buf *tr_search(struct rb_root *root, char *slp) +{ + unsigned int type = get_rsc_type(slp); + unsigned int id = get_rsc_id(slp); + struct rb_node *node = root->rb_node; + + while (node) { + struct slp_buf *cur = rb_entry(node, struct slp_buf, node); + unsigned int ctype = get_rsc_type(cur->buf); + unsigned int cid = get_rsc_id(cur->buf); + + if (type < ctype) + node = node->rb_left; + else if (type > ctype) + node = node->rb_right; + else if (id < cid) + node = node->rb_left; + else if (id > cid) + node = node->rb_right; + else + return cur; + } + return NULL; +} + +static int tr_insert(struct rb_root *root, struct slp_buf *slp) +{ + unsigned int type = get_rsc_type(slp->buf); + unsigned int id = get_rsc_id(slp->buf); + struct rb_node **node = &(root->rb_node), *parent = NULL; + + while (*node) { + struct slp_buf *curr = rb_entry(*node, struct slp_buf, node); + unsigned int ctype = get_rsc_type(curr->buf); + unsigned int cid = get_rsc_id(curr->buf); + + parent = *node; + + if (type < ctype) + node = &((*node)->rb_left); + else if (type > ctype) + node = &((*node)->rb_right); + else if (id < cid) + node = &((*node)->rb_left); + else if (id > cid) + node = &((*node)->rb_right); + else + return -EINVAL; + } + + rb_link_node(&slp->node, parent, node); + rb_insert_color(&slp->node, root); + slp->valid = true; + return 0; +} + +#define for_each_kvp(buf, k) \ + for (k = (struct kvp *)get_first_kvp(buf); \ + ((void *)k - (void *)get_first_kvp(buf)) < \ + get_data_len(buf);\ + k = get_next_kvp(k)) + + +static void tr_update(struct slp_buf *s, char *buf) +{ + struct kvp *e, *n; + + for_each_kvp(buf, n) { + bool found = false; + + for_each_kvp(s->buf, e) { + if (n->k == e->k) { + found = true; + if (n->s == e->s) { + void *e_data = get_data(e); + void *n_data = get_data(n); + + if (memcmp(e_data, n_data, n->s)) { + update_kvp_data(e, n); + s->valid = true; + } + } else { + delete_kvp(s->buf, e); + add_kvp(s->buf, n); + s->valid = true; + } + break; + } + + } + if (!found) { + add_kvp(s->buf, n); + s->valid = true; + } + } +} +static atomic_t msm_rpm_msg_id = ATOMIC_INIT(0); + +struct msm_rpm_request { + uint8_t *client_buf; + struct msm_rpm_kvp_data *kvp; + uint32_t num_elements; + uint32_t write_idx; + uint8_t *buf; + uint32_t numbytes; +}; + +/* + * Data related to message acknowledgment + */ + +LIST_HEAD(msm_rpm_wait_list); + +struct msm_rpm_wait_data { + struct list_head list; + uint32_t msg_id; + bool ack_recd; + int errno; + struct completion ack; + bool delete_on_ack; +}; +DEFINE_SPINLOCK(msm_rpm_list_lock); + + + +LIST_HEAD(msm_rpm_ack_list); + +static struct tasklet_struct data_tasklet; + +static inline uint32_t msm_rpm_get_msg_id_from_ack(uint8_t *buf) +{ + return get_ack_msg_id(buf); +} + +static inline int msm_rpm_get_error_from_ack(uint8_t *buf) +{ + uint8_t *tmp; + uint32_t req_len = get_ack_req_len(buf); + uint32_t msg_type = get_ack_msg_type(buf); + int rc = -ENODEV; + uint32_t err; + uint32_t ack_msg_size = rpm_msg_fmt_ver ? + sizeof(struct msm_rpm_ack_msg_v1) : + sizeof(struct msm_rpm_ack_msg_v0); + + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT && + msg_type == RPM_V1_ACK_SERVICE) { + return 0; + } else if (rpm_msg_fmt_ver && msg_type == RPM_V1_NACK_SERVICE) { + err = *(uint32_t *)(buf + sizeof(struct msm_rpm_ack_msg_v1)); + return err; + } + + req_len -= ack_msg_size; + req_len += 2 * sizeof(uint32_t); + if (!req_len) + return 0; + + pr_err("%s:rpm returned error or nack req_len: %d id_ack: %d\n", + __func__, req_len, get_ack_msg_id(buf)); + + tmp = buf + ack_msg_size; + + if (memcmp(tmp, ERR, sizeof(uint32_t))) { + pr_err("%s rpm returned error\n", __func__); + WARN_ON(1); + } + + tmp += 2 * sizeof(uint32_t); + + if (!(memcmp(tmp, INV_RSC, min_t(uint32_t, req_len, + sizeof(INV_RSC))-1))) { + pr_err("%s(): RPM NACK Unsupported resource\n", __func__); + rc = -EINVAL; + } else { + pr_err("%s(): RPM NACK Invalid header\n", __func__); + } + + return rc; +} + +int msm_rpm_smd_buffer_request(struct msm_rpm_request *cdata, + uint32_t size, gfp_t flag) +{ + struct slp_buf *slp; + static DEFINE_SPINLOCK(slp_buffer_lock); + unsigned long flags; + char *buf; + + buf = cdata->buf; + + if (size > MAX_SLEEP_BUFFER) + return -ENOMEM; + + spin_lock_irqsave(&slp_buffer_lock, flags); + slp = tr_search(&tr_root, buf); + + if (!slp) { + slp = kzalloc(sizeof(struct slp_buf), GFP_ATOMIC); + if (!slp) { + spin_unlock_irqrestore(&slp_buffer_lock, flags); + return -ENOMEM; + } + slp->buf = PTR_ALIGN(&slp->ubuf[0], sizeof(u32)); + memcpy(slp->buf, buf, size); + if (tr_insert(&tr_root, slp)) + pr_err("Error updating sleep request\n"); + } else { + /* handle unsent requests */ + tr_update(slp, buf); + } + trace_rpm_smd_sleep_set(get_msg_id(cdata->client_buf), + get_rsc_type(cdata->client_buf), + get_req_len(cdata->client_buf)); + + spin_unlock_irqrestore(&slp_buffer_lock, flags); + + return 0; +} + +static struct msm_rpm_driver_data msm_rpm_data = { + .smd_open = COMPLETION_INITIALIZER(msm_rpm_data.smd_open), +}; + +static int msm_rpm_glink_rx_poll(void *glink_handle) +{ + int ret; + + ret = glink_rpm_rx_poll(glink_handle); + if (ret >= 0) + /* + * Sleep for 50us at a time before checking + * for packet availability. The 50us is based + * on the the time rpm could take to process + * and send an ack for the sleep set request. + */ + udelay(50); + else + pr_err("Not receieve an ACK from RPM. ret = %d\n", ret); + + return ret; +} + +/* + * Returns + * = 0 on successful reads + * > 0 on successful reads with no further data + * standard Linux error codes on failure. + */ +static int msm_rpm_read_sleep_ack(void) +{ + int ret; + char buf[MAX_ERR_BUFFER_SIZE] = {0}; + + if (glink_enabled) + ret = msm_rpm_glink_rx_poll(glink_data->glink_handle); + else { + ret = msm_rpm_read_smd_data(buf); + if (!ret) + ret = smd_is_pkt_avail(msm_rpm_data.ch_info); + } + return ret; +} + +static int msm_rpm_flush_requests(bool print) +{ + struct rb_node *t; + int ret; + int count = 0; + + for (t = rb_first(&tr_root); t; t = rb_next(t)) { + + struct slp_buf *s = rb_entry(t, struct slp_buf, node); + unsigned int type = get_rsc_type(s->buf); + unsigned int id = get_rsc_id(s->buf); + + if (!s->valid) + continue; + + set_msg_id(s->buf, msm_rpm_get_next_msg_id()); + + if (!glink_enabled) + ret = msm_rpm_send_smd_buffer(s->buf, + get_buf_len(s->buf), true); + else + ret = msm_rpm_glink_send_buffer(s->buf, + get_buf_len(s->buf), true); + + WARN_ON(ret != get_buf_len(s->buf)); + trace_rpm_smd_send_sleep_set(get_msg_id(s->buf), type, id); + + s->valid = false; + count++; + + /* + * RPM acks need to be handled here if we have sent 24 + * messages such that we do not overrun SMD buffer. Since + * we expect only sleep sets at this point (RPM PC would be + * disallowed if we had pending active requests), we need not + * process these sleep set acks. + */ + if (count >= MAX_WAIT_ON_ACK) { + int ret = msm_rpm_read_sleep_ack(); + + if (ret >= 0) + count--; + else + return ret; + } + } + return 0; +} + +static void msm_rpm_notify_sleep_chain(char *buf, + struct msm_rpm_kvp_data *kvp) +{ + struct msm_rpm_notifier_data notif; + + notif.rsc_type = get_rsc_type(buf); + notif.rsc_id = get_req_len(buf); + notif.key = kvp->key; + notif.size = kvp->nbytes; + notif.value = kvp->value; + atomic_notifier_call_chain(&msm_rpm_sleep_notifier, 0, ¬if); +} + +static int msm_rpm_add_kvp_data_common(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size, bool noirq) +{ + uint32_t i; + uint32_t data_size, msg_size; + + if (probe_status) + return probe_status; + + if (!handle || !data) { + pr_err("%s(): Invalid handle/data\n", __func__); + return -EINVAL; + } + + if (size < 0) + return -EINVAL; + + data_size = ALIGN(size, SZ_4); + msg_size = data_size + 8; + + for (i = 0; i < handle->write_idx; i++) { + if (handle->kvp[i].key != key) + continue; + if (handle->kvp[i].nbytes != data_size) { + kfree(handle->kvp[i].value); + handle->kvp[i].value = NULL; + } else { + if (!memcmp(handle->kvp[i].value, data, data_size)) + return 0; + } + break; + } + + if (i >= handle->num_elements) { + pr_err("Number of resources exceeds max allocated\n"); + return -ENOMEM; + } + + if (i == handle->write_idx) + handle->write_idx++; + + if (!handle->kvp[i].value) { + handle->kvp[i].value = kzalloc(data_size, GFP_FLAG(noirq)); + + if (!handle->kvp[i].value) + return -ENOMEM; + } else { + /* We enter the else case, if a key already exists but the + * data doesn't match. In which case, we should zero the data + * out. + */ + memset(handle->kvp[i].value, 0, data_size); + } + + if (!handle->kvp[i].valid) + change_data_len(handle->client_buf, msg_size); + else + change_data_len(handle->client_buf, + (data_size - handle->kvp[i].nbytes)); + + handle->kvp[i].nbytes = data_size; + handle->kvp[i].key = key; + memcpy(handle->kvp[i].value, data, size); + handle->kvp[i].valid = true; + + return 0; + +} + +static struct msm_rpm_request *msm_rpm_create_request_common( + enum msm_rpm_set set, uint32_t rsc_type, uint32_t rsc_id, + int num_elements, bool noirq) +{ + struct msm_rpm_request *cdata; + uint32_t buf_size; + + if (probe_status) + return ERR_PTR(probe_status); + + cdata = kzalloc(sizeof(struct msm_rpm_request), + GFP_FLAG(noirq)); + + if (!cdata) { + pr_err("Cannot allocate memory for client data\n"); + goto cdata_alloc_fail; + } + + if (rpm_msg_fmt_ver == RPM_MSG_V0_FMT) + buf_size = sizeof(struct rpm_message_header_v0); + else + buf_size = sizeof(struct rpm_message_header_v1); + + cdata->client_buf = kzalloc(buf_size, GFP_FLAG(noirq)); + + if (!cdata->client_buf) + goto client_buf_alloc_fail; + + set_set_type(cdata->client_buf, set); + set_rsc_type(cdata->client_buf, rsc_type); + set_rsc_id(cdata->client_buf, rsc_id); + + cdata->num_elements = num_elements; + cdata->write_idx = 0; + + cdata->kvp = kcalloc(num_elements, sizeof(struct msm_rpm_kvp_data), + GFP_FLAG(noirq)); + + if (!cdata->kvp) { + pr_warn("%s(): Cannot allocate memory for key value data\n", + __func__); + goto kvp_alloc_fail; + } + + cdata->buf = kzalloc(DEFAULT_BUFFER_SIZE, GFP_FLAG(noirq)); + + if (!cdata->buf) + goto buf_alloc_fail; + + cdata->numbytes = DEFAULT_BUFFER_SIZE; + return cdata; + +buf_alloc_fail: + kfree(cdata->kvp); +kvp_alloc_fail: + kfree(cdata->client_buf); +client_buf_alloc_fail: + kfree(cdata); +cdata_alloc_fail: + return NULL; + +} + +void msm_rpm_free_request(struct msm_rpm_request *handle) +{ + int i; + + if (!handle) + return; + for (i = 0; i < handle->num_elements; i++) + kfree(handle->kvp[i].value); + kfree(handle->kvp); + kfree(handle->client_buf); + kfree(handle->buf); + kfree(handle); +} +EXPORT_SYMBOL(msm_rpm_free_request); + +struct msm_rpm_request *msm_rpm_create_request( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return msm_rpm_create_request_common(set, rsc_type, rsc_id, + num_elements, false); +} +EXPORT_SYMBOL(msm_rpm_create_request); + +struct msm_rpm_request *msm_rpm_create_request_noirq( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return msm_rpm_create_request_common(set, rsc_type, rsc_id, + num_elements, true); +} +EXPORT_SYMBOL(msm_rpm_create_request_noirq); + +int msm_rpm_add_kvp_data(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size) +{ + return msm_rpm_add_kvp_data_common(handle, key, data, size, false); + +} +EXPORT_SYMBOL(msm_rpm_add_kvp_data); + +int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size) +{ + return msm_rpm_add_kvp_data_common(handle, key, data, size, true); +} +EXPORT_SYMBOL(msm_rpm_add_kvp_data_noirq); + +/* Runs in interrupt context */ +static void msm_rpm_notify(void *data, unsigned int event) +{ + struct msm_rpm_driver_data *pdata = (struct msm_rpm_driver_data *)data; + + WARN_ON(!pdata); + + if (!(pdata->ch_info)) + return; + + switch (event) { + case SMD_EVENT_DATA: + tasklet_schedule(&data_tasklet); + trace_rpm_smd_interrupt_notify("interrupt notification"); + break; + case SMD_EVENT_OPEN: + complete(&pdata->smd_open); + break; + case SMD_EVENT_CLOSE: + case SMD_EVENT_STATUS: + case SMD_EVENT_REOPEN_READY: + break; + default: + pr_info("Unknown SMD event\n"); + + } +} + +bool msm_rpm_waiting_for_ack(void) +{ + bool ret; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + ret = list_empty(&msm_rpm_wait_list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + + return !ret; +} + +static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id) +{ + struct list_head *ptr; + struct msm_rpm_wait_data *elem = NULL; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + + list_for_each(ptr, &msm_rpm_wait_list) { + elem = list_entry(ptr, struct msm_rpm_wait_data, list); + if (elem && (elem->msg_id == msg_id)) + break; + elem = NULL; + } + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + return elem; +} + +static uint32_t msm_rpm_get_next_msg_id(void) +{ + uint32_t id; + + /* + * A message id of 0 is used by the driver to indicate a error + * condition. The RPM driver uses a id of 1 to indicate unsent data + * when the data sent over hasn't been modified. This isn't a error + * scenario and wait for ack returns a success when the message id is 1. + */ + + do { + id = atomic_inc_return(&msm_rpm_msg_id); + } while ((id == 0) || (id == 1) || msm_rpm_get_entry_from_msg_id(id)); + + return id; +} + +static int msm_rpm_add_wait_list(uint32_t msg_id, bool delete_on_ack) +{ + unsigned long flags; + struct msm_rpm_wait_data *data = + kzalloc(sizeof(struct msm_rpm_wait_data), GFP_ATOMIC); + + if (!data) + return -ENOMEM; + + init_completion(&data->ack); + data->ack_recd = false; + data->msg_id = msg_id; + data->errno = INIT_ERROR; + data->delete_on_ack = delete_on_ack; + spin_lock_irqsave(&msm_rpm_list_lock, flags); + if (delete_on_ack) + list_add_tail(&data->list, &msm_rpm_wait_list); + else + list_add(&data->list, &msm_rpm_wait_list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + + return 0; +} + +static void msm_rpm_free_list_entry(struct msm_rpm_wait_data *elem) +{ + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + list_del(&elem->list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + kfree(elem); +} + +static void msm_rpm_process_ack(uint32_t msg_id, int errno) +{ + struct list_head *ptr, *next; + struct msm_rpm_wait_data *elem = NULL; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + + list_for_each_safe(ptr, next, &msm_rpm_wait_list) { + elem = list_entry(ptr, struct msm_rpm_wait_data, list); + if (elem->msg_id == msg_id) { + elem->errno = errno; + elem->ack_recd = true; + complete(&elem->ack); + if (elem->delete_on_ack) { + list_del(&elem->list); + kfree(elem); + } + break; + } + } + /* Special case where the sleep driver doesn't + * wait for ACKs. This would decrease the latency involved with + * entering RPM assisted power collapse. + */ + if (!elem) + trace_rpm_smd_ack_recvd(0, msg_id, 0xDEADBEEF); + + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); +} + +struct msm_rpm_kvp_packet { + uint32_t id; + uint32_t len; + uint32_t val; +}; + +static int msm_rpm_read_smd_data(char *buf) +{ + int pkt_sz; + int bytes_read = 0; + + pkt_sz = smd_cur_packet_size(msm_rpm_data.ch_info); + + if (!pkt_sz) + return -EAGAIN; + + if (pkt_sz > MAX_ERR_BUFFER_SIZE) { + pr_err("rpm_smd pkt_sz is greater than max size\n"); + goto error; + } + + if (pkt_sz != smd_read_avail(msm_rpm_data.ch_info)) + return -EAGAIN; + + do { + int len; + + len = smd_read(msm_rpm_data.ch_info, buf + bytes_read, pkt_sz); + pkt_sz -= len; + bytes_read += len; + + } while (pkt_sz > 0); + + if (pkt_sz < 0) { + pr_err("rpm_smd pkt_sz is less than zero\n"); + goto error; + } + return 0; +error: + WARN_ON(1); + + return 0; +} + +static void data_fn_tasklet(unsigned long data) +{ + uint32_t msg_id; + int errno; + char buf[MAX_ERR_BUFFER_SIZE] = {0}; + + spin_lock(&msm_rpm_data.smd_lock_read); + while (smd_is_pkt_avail(msm_rpm_data.ch_info)) { + if (msm_rpm_read_smd_data(buf)) + break; + msg_id = msm_rpm_get_msg_id_from_ack(buf); + errno = msm_rpm_get_error_from_ack(buf); + trace_rpm_smd_ack_recvd(0, msg_id, errno); + msm_rpm_process_ack(msg_id, errno); + } + spin_unlock(&msm_rpm_data.smd_lock_read); +} + +static void msm_rpm_log_request(struct msm_rpm_request *cdata) +{ + char buf[DEBUG_PRINT_BUFFER_SIZE]; + size_t buflen = DEBUG_PRINT_BUFFER_SIZE; + char name[5]; + u32 value; + uint32_t i; + int j, prev_valid; + int valid_count = 0; + int pos = 0; + uint32_t res_type, rsc_id; + + name[4] = 0; + + for (i = 0; i < cdata->write_idx; i++) + if (cdata->kvp[i].valid) + valid_count++; + + pos += scnprintf(buf + pos, buflen - pos, "%sRPM req: ", KERN_INFO); + if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_SHOW_MSG_ID) + pos += scnprintf(buf + pos, buflen - pos, "msg_id=%u, ", + get_msg_id(cdata->client_buf)); + pos += scnprintf(buf + pos, buflen - pos, "s=%s", + (get_set_type(cdata->client_buf) == + MSM_RPM_CTX_ACTIVE_SET ? "act" : "slp")); + + res_type = get_rsc_type(cdata->client_buf); + rsc_id = get_rsc_id(cdata->client_buf); + if ((msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) + && (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_RAW)) { + /* Both pretty and raw formatting */ + memcpy(name, &res_type, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, + ", rsc_type=0x%08X (%s), rsc_id=%u; ", + res_type, name, rsc_id); + + for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) { + if (!cdata->kvp[i].valid) + continue; + + memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, + "[key=0x%08X (%s), value=%s", + cdata->kvp[i].key, name, + (cdata->kvp[i].nbytes ? "0x" : "null")); + + for (j = 0; j < cdata->kvp[i].nbytes; j++) + pos += scnprintf(buf + pos, buflen - pos, + "%02X ", + cdata->kvp[i].value[j]); + + if (cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, buflen - pos, "("); + + for (j = 0; j < cdata->kvp[i].nbytes; j += 4) { + value = 0; + memcpy(&value, &cdata->kvp[i].value[j], + min_t(uint32_t, sizeof(uint32_t), + cdata->kvp[i].nbytes - j)); + pos += scnprintf(buf + pos, buflen - pos, "%u", + value); + if (j + 4 < cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, + buflen - pos, " "); + } + if (cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, buflen - pos, ")"); + pos += scnprintf(buf + pos, buflen - pos, "]"); + if (prev_valid + 1 < valid_count) + pos += scnprintf(buf + pos, buflen - pos, ", "); + prev_valid++; + } + } else if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) { + /* Pretty formatting only */ + memcpy(name, &res_type, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, " %s %u; ", name, + rsc_id); + + for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) { + if (!cdata->kvp[i].valid) + continue; + + memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t)); + pos += scnprintf(buf + pos, buflen - pos, "%s=%s", + name, (cdata->kvp[i].nbytes ? "" : "null")); + + for (j = 0; j < cdata->kvp[i].nbytes; j += 4) { + value = 0; + memcpy(&value, &cdata->kvp[i].value[j], + min_t(uint32_t, sizeof(uint32_t), + cdata->kvp[i].nbytes - j)); + pos += scnprintf(buf + pos, buflen - pos, "%u", + value); + + if (j + 4 < cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, + buflen - pos, " "); + } + if (prev_valid + 1 < valid_count) + pos += scnprintf(buf + pos, buflen - pos, ", "); + prev_valid++; + } + } else { + /* Raw formatting only */ + pos += scnprintf(buf + pos, buflen - pos, + ", rsc_type=0x%08X, rsc_id=%u; ", res_type, rsc_id); + + for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) { + if (!cdata->kvp[i].valid) + continue; + + pos += scnprintf(buf + pos, buflen - pos, + "[key=0x%08X, value=%s", + cdata->kvp[i].key, + (cdata->kvp[i].nbytes ? "0x" : "null")); + for (j = 0; j < cdata->kvp[i].nbytes; j++) { + pos += scnprintf(buf + pos, buflen - pos, + "%02X", + cdata->kvp[i].value[j]); + if (j + 1 < cdata->kvp[i].nbytes) + pos += scnprintf(buf + pos, + buflen - pos, " "); + } + pos += scnprintf(buf + pos, buflen - pos, "]"); + if (prev_valid + 1 < valid_count) + pos += scnprintf(buf + pos, buflen - pos, ", "); + prev_valid++; + } + } + + pos += scnprintf(buf + pos, buflen - pos, "\n"); + printk(buf); +} + +static int msm_rpm_send_smd_buffer(char *buf, uint32_t size, bool noirq) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags); + ret = smd_write_avail(msm_rpm_data.ch_info); + + while ((ret = smd_write_avail(msm_rpm_data.ch_info)) < size) { + if (ret < 0) + break; + if (!noirq) { + spin_unlock_irqrestore( + &msm_rpm_data.smd_lock_write, flags); + cpu_relax(); + spin_lock_irqsave( + &msm_rpm_data.smd_lock_write, flags); + } else + udelay(5); + } + + if (ret < 0) { + pr_err("SMD not initialized\n"); + spin_unlock_irqrestore( + &msm_rpm_data.smd_lock_write, flags); + return ret; + } + + ret = smd_write(msm_rpm_data.ch_info, buf, size); + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags); + return ret; +} + +static int msm_rpm_glink_send_buffer(char *buf, uint32_t size, bool noirq) +{ + int ret; + unsigned long flags; + int timeout = 50; + + spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags); + do { + ret = glink_tx(glink_data->glink_handle, buf, buf, + size, GLINK_TX_SINGLE_THREADED); + if (ret == -EBUSY || ret == -ENOSPC) { + if (!noirq) { + spin_unlock_irqrestore( + &msm_rpm_data.smd_lock_write, flags); + cpu_relax(); + spin_lock_irqsave( + &msm_rpm_data.smd_lock_write, flags); + } else { + udelay(5); + } + timeout--; + } else { + ret = 0; + } + } while (ret && timeout); + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags); + + if (!timeout) + return 0; + else + return size; +} + +static int msm_rpm_send_data(struct msm_rpm_request *cdata, + int msg_type, bool noirq, bool noack) +{ + uint8_t *tmpbuff; + int ret; + uint32_t i; + uint32_t msg_size; + int msg_hdr_sz, req_hdr_sz; + uint32_t data_len = get_data_len(cdata->client_buf); + uint32_t set = get_set_type(cdata->client_buf); + uint32_t msg_id; + + if (probe_status) + return probe_status; + + if (!data_len) + return 1; + + msg_hdr_sz = rpm_msg_fmt_ver ? sizeof(struct rpm_message_header_v1) : + sizeof(struct rpm_message_header_v0); + + req_hdr_sz = RPM_HDR_SIZE; + set_msg_type(cdata->client_buf, msg_type); + + set_req_len(cdata->client_buf, data_len + msg_hdr_sz - req_hdr_sz); + msg_size = get_req_len(cdata->client_buf) + req_hdr_sz; + + /* populate data_len */ + if (msg_size > cdata->numbytes) { + kfree(cdata->buf); + cdata->numbytes = msg_size; + cdata->buf = kzalloc(msg_size, GFP_FLAG(noirq)); + } + + if (!cdata->buf) { + pr_err("Failed malloc\n"); + return 0; + } + + tmpbuff = cdata->buf; + + tmpbuff += msg_hdr_sz; + for (i = 0; (i < cdata->write_idx); i++) { + /* Sanity check */ + WARN_ON((tmpbuff - cdata->buf) > cdata->numbytes); + + if (!cdata->kvp[i].valid) + continue; + + memcpy(tmpbuff, &cdata->kvp[i].key, sizeof(uint32_t)); + tmpbuff += sizeof(uint32_t); + + memcpy(tmpbuff, &cdata->kvp[i].nbytes, sizeof(uint32_t)); + tmpbuff += sizeof(uint32_t); + + memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes); + tmpbuff += cdata->kvp[i].nbytes; + + if (set == MSM_RPM_CTX_SLEEP_SET) + msm_rpm_notify_sleep_chain(cdata->client_buf, + &cdata->kvp[i]); + + } + + memcpy(cdata->buf, cdata->client_buf, msg_hdr_sz); + if ((set == MSM_RPM_CTX_SLEEP_SET) && + !msm_rpm_smd_buffer_request(cdata, msg_size, + GFP_FLAG(noirq))) + return 1; + + msg_id = msm_rpm_get_next_msg_id(); + /* Set the version bit for new protocol */ + set_msg_ver(cdata->buf, rpm_msg_fmt_ver); + set_msg_id(cdata->buf, msg_id); + set_msg_id(cdata->client_buf, msg_id); + + if (msm_rpm_debug_mask + & (MSM_RPM_LOG_REQUEST_PRETTY | MSM_RPM_LOG_REQUEST_RAW)) + msm_rpm_log_request(cdata); + + if (standalone) { + for (i = 0; (i < cdata->write_idx); i++) + cdata->kvp[i].valid = false; + + set_data_len(cdata->client_buf, 0); + ret = msg_id; + return ret; + } + + msm_rpm_add_wait_list(msg_id, noack); + + ret = msm_rpm_send_buffer(&cdata->buf[0], msg_size, noirq); + + if (ret == msg_size) { + for (i = 0; (i < cdata->write_idx); i++) + cdata->kvp[i].valid = false; + set_data_len(cdata->client_buf, 0); + ret = msg_id; + trace_rpm_smd_send_active_set(msg_id, + get_rsc_type(cdata->client_buf), + get_rsc_id(cdata->client_buf)); + } else if (ret < msg_size) { + struct msm_rpm_wait_data *rc; + + ret = 0; + pr_err("Failed to write data msg_size:%d ret:%d msg_id:%d\n", + msg_size, ret, msg_id); + rc = msm_rpm_get_entry_from_msg_id(msg_id); + if (rc) + msm_rpm_free_list_entry(rc); + } + return ret; +} + +static int _msm_rpm_send_request(struct msm_rpm_request *handle, bool noack) +{ + int ret; + static DEFINE_MUTEX(send_mtx); + + mutex_lock(&send_mtx); + ret = msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false, noack); + mutex_unlock(&send_mtx); + + return ret; +} + +int msm_rpm_send_request(struct msm_rpm_request *handle) +{ + return _msm_rpm_send_request(handle, false); +} +EXPORT_SYMBOL(msm_rpm_send_request); + +int msm_rpm_send_request_noirq(struct msm_rpm_request *handle) +{ + return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, true, false); +} +EXPORT_SYMBOL(msm_rpm_send_request_noirq); + +void *msm_rpm_send_request_noack(struct msm_rpm_request *handle) +{ + int ret; + + ret = _msm_rpm_send_request(handle, true); + + return ret < 0 ? ERR_PTR(ret) : NULL; +} +EXPORT_SYMBOL(msm_rpm_send_request_noack); + +int msm_rpm_wait_for_ack(uint32_t msg_id) +{ + struct msm_rpm_wait_data *elem; + int rc = 0; + + if (!msg_id) { + pr_err("Invalid msg id\n"); + return -ENOMEM; + } + + if (msg_id == 1) + return rc; + + if (standalone) + return rc; + + elem = msm_rpm_get_entry_from_msg_id(msg_id); + if (!elem) + return rc; + + wait_for_completion(&elem->ack); + trace_rpm_smd_ack_recvd(0, msg_id, 0xDEADFEED); + + rc = elem->errno; + msm_rpm_free_list_entry(elem); + + return rc; +} +EXPORT_SYMBOL(msm_rpm_wait_for_ack); + +static void msm_rpm_smd_read_data_noirq(uint32_t msg_id) +{ + uint32_t id = 0; + + while (id != msg_id) { + if (smd_is_pkt_avail(msm_rpm_data.ch_info)) { + int errno; + char buf[MAX_ERR_BUFFER_SIZE] = {}; + + msm_rpm_read_smd_data(buf); + id = msm_rpm_get_msg_id_from_ack(buf); + errno = msm_rpm_get_error_from_ack(buf); + trace_rpm_smd_ack_recvd(1, msg_id, errno); + msm_rpm_process_ack(id, errno); + } + } +} + +static void msm_rpm_glink_read_data_noirq(struct msm_rpm_wait_data *elem) +{ + int ret; + + /* Use rx_poll method to read the message from RPM */ + while (elem->errno) { + ret = glink_rpm_rx_poll(glink_data->glink_handle); + if (ret >= 0) { + /* + * We might have receieve the notification. + * Now we have to check whether the notification + * received is what we are interested? + * Wait for few usec to get the notification + * before re-trying the poll again. + */ + udelay(50); + } else { + pr_err("rx poll return error = %d\n", ret); + } + } +} + +int msm_rpm_wait_for_ack_noirq(uint32_t msg_id) +{ + struct msm_rpm_wait_data *elem; + unsigned long flags; + int rc = 0; + + if (!msg_id) { + pr_err("Invalid msg id\n"); + return -ENOMEM; + } + + if (msg_id == 1) + return 0; + + if (standalone) + return 0; + + spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags); + + elem = msm_rpm_get_entry_from_msg_id(msg_id); + + if (!elem) + /* Should this be a bug + * Is it ok for another thread to read the msg? + */ + goto wait_ack_cleanup; + + if (elem->errno != INIT_ERROR) { + rc = elem->errno; + msm_rpm_free_list_entry(elem); + goto wait_ack_cleanup; + } + + if (!glink_enabled) + msm_rpm_smd_read_data_noirq(msg_id); + else + msm_rpm_glink_read_data_noirq(elem); + + rc = elem->errno; + + msm_rpm_free_list_entry(elem); +wait_ack_cleanup: + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags); + + if (!glink_enabled) + if (smd_is_pkt_avail(msm_rpm_data.ch_info)) + tasklet_schedule(&data_tasklet); + return rc; +} +EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq); + +void *msm_rpm_send_message_noack(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + int i, rc; + struct msm_rpm_request *req = + msm_rpm_create_request_common(set, rsc_type, rsc_id, nelems, + false); + + if (IS_ERR(req)) + return req; + + if (!req) + return ERR_PTR(ENOMEM); + + for (i = 0; i < nelems; i++) { + rc = msm_rpm_add_kvp_data(req, kvp[i].key, + kvp[i].data, kvp[i].length); + if (rc) + goto bail; + } + + rc = PTR_ERR(msm_rpm_send_request_noack(req)); +bail: + msm_rpm_free_request(req); + return rc < 0 ? ERR_PTR(rc) : NULL; +} +EXPORT_SYMBOL(msm_rpm_send_message_noack); + +int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + int i, rc; + struct msm_rpm_request *req = + msm_rpm_create_request(set, rsc_type, rsc_id, nelems); + + if (IS_ERR(req)) + return PTR_ERR(req); + + if (!req) + return -ENOMEM; + + for (i = 0; i < nelems; i++) { + rc = msm_rpm_add_kvp_data(req, kvp[i].key, + kvp[i].data, kvp[i].length); + if (rc) + goto bail; + } + + rc = msm_rpm_wait_for_ack(msm_rpm_send_request(req)); +bail: + msm_rpm_free_request(req); + return rc; +} +EXPORT_SYMBOL(msm_rpm_send_message); + +int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + int i, rc; + struct msm_rpm_request *req = + msm_rpm_create_request_noirq(set, rsc_type, rsc_id, nelems); + + if (IS_ERR(req)) + return PTR_ERR(req); + + if (!req) + return -ENOMEM; + + for (i = 0; i < nelems; i++) { + rc = msm_rpm_add_kvp_data_noirq(req, kvp[i].key, + kvp[i].data, kvp[i].length); + if (rc) + goto bail; + } + + rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(req)); +bail: + msm_rpm_free_request(req); + return rc; +} +EXPORT_SYMBOL(msm_rpm_send_message_noirq); + +/** + * During power collapse, the rpm driver disables the SMD interrupts to make + * sure that the interrupt doesn't wakes us from sleep. + */ +int msm_rpm_enter_sleep(bool print, const struct cpumask *cpumask) +{ + int ret = 0; + + if (standalone) + return 0; + + if (!glink_enabled) + ret = smd_mask_receive_interrupt(msm_rpm_data.ch_info, + true, cpumask); + else + ret = glink_rpm_mask_rx_interrupt(glink_data->glink_handle, + true, (void *)cpumask); + + if (!ret) { + ret = msm_rpm_flush_requests(print); + + if (ret) { + if (!glink_enabled) + smd_mask_receive_interrupt( + msm_rpm_data.ch_info, false, NULL); + else + glink_rpm_mask_rx_interrupt( + glink_data->glink_handle, false, NULL); + } + } + return ret; +} +EXPORT_SYMBOL(msm_rpm_enter_sleep); + +/** + * When the system resumes from power collapse, the SMD interrupt disabled by + * enter function has to reenabled to continue processing SMD message. + */ +void msm_rpm_exit_sleep(void) +{ + int ret; + + if (standalone) + return; + + do { + ret = msm_rpm_read_sleep_ack(); + } while (ret > 0); + + if (!glink_enabled) + smd_mask_receive_interrupt(msm_rpm_data.ch_info, false, NULL); + else + glink_rpm_mask_rx_interrupt(glink_data->glink_handle, + false, NULL); +} +EXPORT_SYMBOL(msm_rpm_exit_sleep); + +/* + * Whenever there is a data from RPM, notify_rx will be called. + * This function is invoked either interrupt OR polling context. + */ +static void msm_rpm_trans_notify_rx(void *handle, const void *priv, + const void *pkt_priv, const void *ptr, size_t size) +{ + uint32_t msg_id; + int errno; + char buf[MAX_ERR_BUFFER_SIZE] = {0}; + struct msm_rpm_wait_data *elem; + static DEFINE_SPINLOCK(rx_notify_lock); + unsigned long flags; + + if (!size) + return; + + WARN_ON(size > MAX_ERR_BUFFER_SIZE); + + spin_lock_irqsave(&rx_notify_lock, flags); + memcpy(buf, ptr, size); + msg_id = msm_rpm_get_msg_id_from_ack(buf); + errno = msm_rpm_get_error_from_ack(buf); + elem = msm_rpm_get_entry_from_msg_id(msg_id); + + /* + * It is applicable for sleep set requests + * Sleep set requests are not added to the + * wait queue list. Without this check we + * run into NULL pointer deferrence issue. + */ + if (!elem) { + spin_unlock_irqrestore(&rx_notify_lock, flags); + glink_rx_done(handle, ptr, 0); + return; + } + + msm_rpm_process_ack(msg_id, errno); + spin_unlock_irqrestore(&rx_notify_lock, flags); + + glink_rx_done(handle, ptr, 0); +} + +static void msm_rpm_trans_notify_state(void *handle, const void *priv, + unsigned int event) +{ + switch (event) { + case GLINK_CONNECTED: + glink_data->glink_handle = handle; + + if (IS_ERR_OR_NULL(glink_data->glink_handle)) { + pr_err("glink_handle %d\n", + (int)PTR_ERR(glink_data->glink_handle)); + WARN_ON(1); + } + + /* + * Do not allow clients to send data to RPM until glink + * is fully open. + */ + probe_status = 0; + pr_info("glink config params: transport=%s, edge=%s, name=%s\n", + glink_data->xprt, + glink_data->edge, + glink_data->name); + break; + default: + pr_err("Unrecognized event %d\n", event); + break; + }; +} + +static void msm_rpm_trans_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ +} + +static void msm_rpm_glink_open_work(struct work_struct *work) +{ + pr_debug("Opening glink channel\n"); + glink_data->glink_handle = glink_open(glink_data->open_cfg); + + if (IS_ERR_OR_NULL(glink_data->glink_handle)) { + pr_err("Error: glink_open failed %d\n", + (int)PTR_ERR(glink_data->glink_handle)); + WARN_ON(1); + } +} + +static void msm_rpm_glink_notifier_cb(struct glink_link_state_cb_info *cb_info, + void *priv) +{ + struct glink_open_config *open_config; + static bool first = true; + + if (!cb_info) { + pr_err("Missing callback data\n"); + return; + } + + switch (cb_info->link_state) { + case GLINK_LINK_STATE_UP: + if (first) + first = false; + else + break; + open_config = kzalloc(sizeof(*open_config), GFP_KERNEL); + if (!open_config) { + pr_err("Could not allocate memory\n"); + break; + } + + glink_data->open_cfg = open_config; + pr_debug("glink link state up cb receieved\n"); + INIT_WORK(&glink_data->work, msm_rpm_glink_open_work); + + open_config->priv = glink_data; + open_config->name = glink_data->name; + open_config->edge = glink_data->edge; + open_config->notify_rx = msm_rpm_trans_notify_rx; + open_config->notify_tx_done = msm_rpm_trans_notify_tx_done; + open_config->notify_state = msm_rpm_trans_notify_state; + schedule_work(&glink_data->work); + break; + default: + pr_err("Unrecognised state = %d\n", cb_info->link_state); + break; + }; +} + +static int msm_rpm_glink_dt_parse(struct platform_device *pdev, + struct glink_apps_rpm_data *glink_data) +{ + char *key = NULL; + int ret; + + if (of_device_is_compatible(pdev->dev.of_node, "qcom,rpm-glink")) { + glink_enabled = true; + } else { + pr_warn("qcom,rpm-glink compatible not matches\n"); + ret = -EINVAL; + return ret; + } + + key = "qcom,glink-edge"; + ret = of_property_read_string(pdev->dev.of_node, key, + &glink_data->edge); + if (ret) { + pr_err("Failed to read node: %s, key=%s\n", + pdev->dev.of_node->full_name, key); + return ret; + } + + key = "rpm-channel-name"; + ret = of_property_read_string(pdev->dev.of_node, key, + &glink_data->name); + if (ret) + pr_err("%s(): Failed to read node: %s, key=%s\n", __func__, + pdev->dev.of_node->full_name, key); + + return ret; +} + +static int msm_rpm_glink_link_setup(struct glink_apps_rpm_data *glink_data, + struct platform_device *pdev) +{ + struct glink_link_info *link_info; + void *link_state_cb_handle; + struct device *dev = &pdev->dev; + int ret = 0; + + link_info = devm_kzalloc(dev, sizeof(struct glink_link_info), + GFP_KERNEL); + if (!link_info) { + ret = -ENOMEM; + return ret; + } + + glink_data->link_info = link_info; + + /* + * Setup link info parameters + */ + link_info->edge = glink_data->edge; + link_info->glink_link_state_notif_cb = + msm_rpm_glink_notifier_cb; + link_state_cb_handle = glink_register_link_state_cb(link_info, NULL); + if (IS_ERR_OR_NULL(link_state_cb_handle)) { + pr_err("Could not register cb\n"); + ret = PTR_ERR(link_state_cb_handle); + return ret; + } + + spin_lock_init(&msm_rpm_data.smd_lock_read); + spin_lock_init(&msm_rpm_data.smd_lock_write); + + return ret; +} + +static int msm_rpm_dev_glink_probe(struct platform_device *pdev) +{ + int ret = -ENOMEM; + struct device *dev = &pdev->dev; + + glink_data = devm_kzalloc(dev, sizeof(*glink_data), GFP_KERNEL); + if (!glink_data) + return ret; + + ret = msm_rpm_glink_dt_parse(pdev, glink_data); + if (ret < 0) { + devm_kfree(dev, glink_data); + return ret; + } + + ret = msm_rpm_glink_link_setup(glink_data, pdev); + if (ret < 0) { + /* + * If the glink setup fails there is no + * fall back mechanism to SMD. + */ + pr_err("GLINK setup fail ret = %d\n", ret); + WARN_ON(1); + } + + return ret; +} + +static int msm_rpm_dev_probe(struct platform_device *pdev) +{ + char *key = NULL; + int ret = 0; + void __iomem *reg_base; + uint32_t version = V0_PROTOCOL_VERSION; /* set to default v0 format */ + + /* + * Check for standalone support + */ + key = "rpm-standalone"; + standalone = of_property_read_bool(pdev->dev.of_node, key); + if (standalone) { + probe_status = ret; + goto skip_init; + } + + reg_base = of_iomap(pdev->dev.of_node, 0); + + if (reg_base) { + version = readq_relaxed(reg_base); + iounmap(reg_base); + } + + if (version == V1_PROTOCOL_VERSION) + rpm_msg_fmt_ver = RPM_MSG_V1_FMT; + + pr_debug("RPM-SMD running version %d/n", rpm_msg_fmt_ver); + + ret = msm_rpm_dev_glink_probe(pdev); + if (!ret) { + pr_info("APSS-RPM communication over GLINK\n"); + msm_rpm_send_buffer = msm_rpm_glink_send_buffer; + of_platform_populate(pdev->dev.of_node, NULL, NULL, + &pdev->dev); + return ret; + } + msm_rpm_send_buffer = msm_rpm_send_smd_buffer; + + key = "rpm-channel-name"; + ret = of_property_read_string(pdev->dev.of_node, key, + &msm_rpm_data.ch_name); + if (ret) { + pr_err("%s(): Failed to read node: %s, key=%s\n", __func__, + pdev->dev.of_node->full_name, key); + goto fail; + } + + key = "rpm-channel-type"; + ret = of_property_read_u32(pdev->dev.of_node, key, + &msm_rpm_data.ch_type); + if (ret) { + pr_err("%s(): Failed to read node: %s, key=%s\n", __func__, + pdev->dev.of_node->full_name, key); + goto fail; + } + + 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); + if (ret) { + if (ret != -EPROBE_DEFER) { + pr_err("%s: Cannot open RPM channel %s %d\n", + __func__, msm_rpm_data.ch_name, + msm_rpm_data.ch_type); + } + goto fail; + } + + spin_lock_init(&msm_rpm_data.smd_lock_write); + spin_lock_init(&msm_rpm_data.smd_lock_read); + tasklet_init(&data_tasklet, data_fn_tasklet, 0); + + wait_for_completion(&msm_rpm_data.smd_open); + + smd_disable_read_intr(msm_rpm_data.ch_info); + + msm_rpm_smd_wq = alloc_workqueue("rpm-smd", + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!msm_rpm_smd_wq) { + pr_err("%s: Unable to alloc rpm-smd workqueue\n", __func__); + ret = -EINVAL; + goto fail; + } + queue_work(msm_rpm_smd_wq, &msm_rpm_data.work); + + probe_status = ret; +skip_init: + of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + + if (standalone) + pr_info("RPM running in standalone mode\n"); +fail: + return probe_status; +} + +static const struct of_device_id msm_rpm_match_table[] = { + {.compatible = "qcom,rpm-smd"}, + {.compatible = "qcom,rpm-glink"}, + {}, +}; + +static struct platform_driver msm_rpm_device_driver = { + .probe = msm_rpm_dev_probe, + .driver = { + .name = "rpm-smd", + .owner = THIS_MODULE, + .of_match_table = msm_rpm_match_table, + }, +}; + +int __init msm_rpm_driver_init(void) +{ + static bool registered; + + if (registered) + return 0; + registered = true; + + return platform_driver_register(&msm_rpm_device_driver); +} +EXPORT_SYMBOL(msm_rpm_driver_init); +arch_initcall(msm_rpm_driver_init); diff --git a/drivers/soc/qcom/rpmh_master_stat.c b/drivers/soc/qcom/rpmh_master_stat.c new file mode 100644 index 0000000000000000000000000000000000000000..283e367cf439a58729ae07b9a94288137e62249b --- /dev/null +++ b/drivers/soc/qcom/rpmh_master_stat.c @@ -0,0 +1,289 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rpmh_master_stat.h" + +#define UNIT_DIST 0x14 +#define REG_VALID 0x0 +#define REG_DATA_LO 0x4 +#define REG_DATA_HI 0x8 + +#define GET_ADDR(REG, UNIT_NO) (REG + (UNIT_DIST * UNIT_NO)) + +enum master_smem_id { + MPSS = 605, + ADSP, + CDSP, + SLPI, + GPU, + DISPLAY, +}; + +enum master_pid { + PID_APSS = 0, + PID_MPSS = 1, + PID_ADSP = 2, + PID_SLPI = 3, + PID_CDSP = 5, + PID_GPU = PID_APSS, + PID_DISPLAY = PID_APSS, +}; + +enum profile_data { + POWER_DOWN_START, + POWER_UP_END, + POWER_DOWN_END, + POWER_UP_START, + NUM_UNIT, +}; + +struct msm_rpmh_master_data { + char *master_name; + enum master_smem_id smem_id; + enum master_pid pid; +}; + +static const struct msm_rpmh_master_data rpmh_masters[] = { + {"MPSS", MPSS, PID_MPSS}, + {"ADSP", ADSP, PID_ADSP}, + {"CDSP", CDSP, PID_CDSP}, + {"SLPI", SLPI, PID_SLPI}, + {"GPU", GPU, PID_GPU}, + {"DISPLAY", DISPLAY, PID_DISPLAY}, +}; + +struct msm_rpmh_master_stats { + uint32_t version_id; + uint32_t counts; + uint64_t last_entered; + uint64_t last_exited; + uint64_t accumulated_duration; +}; + +struct msm_rpmh_profile_unit { + uint64_t value; + uint64_t valid; +}; + +struct rpmh_master_stats_prv_data { + struct kobj_attribute ka; + struct kobject *kobj; +}; + +static struct msm_rpmh_master_stats apss_master_stats; +static void __iomem *rpmh_unit_base; + +static DEFINE_MUTEX(rpmh_stats_mutex); + +static ssize_t msm_rpmh_master_stats_print_data(char *prvbuf, ssize_t length, + struct msm_rpmh_master_stats *record, + const char *name) +{ + uint64_t accumulated_duration = record->accumulated_duration; + /* + * If a master is in sleep when reading the sleep stats from SMEM + * adjust the accumulated sleep duration to show actual sleep time. + * This ensures that the displayed stats are real when used for + * the purpose of computing battery utilization. + */ + if (record->last_entered > record->last_exited) + accumulated_duration += + (arch_counter_get_cntvct() + - record->last_entered); + + return snprintf(prvbuf, length, "%s\n\tVersion:0x%x\n" + "\tSleep Count:0x%x\n" + "\tSleep Last Entered At:0x%llx\n" + "\tSleep Last Exited At:0x%llx\n" + "\tSleep Accumulated Duration:0x%llx\n\n", + name, record->version_id, record->counts, + record->last_entered, record->last_exited, + accumulated_duration); +} + +static ssize_t msm_rpmh_master_stats_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + ssize_t length; + int i = 0; + size_t size = 0; + struct msm_rpmh_master_stats *record = NULL; + + mutex_lock(&rpmh_stats_mutex); + + /* First Read APSS master stats */ + + length = msm_rpmh_master_stats_print_data(buf, PAGE_SIZE, + &apss_master_stats, "APSS"); + + /* Read SMEM data written by other masters */ + + for (i = 0; i < ARRAY_SIZE(rpmh_masters); i++) { + record = (struct msm_rpmh_master_stats *) qcom_smem_get( + rpmh_masters[i].pid, + rpmh_masters[i].smem_id, &size); + if (!IS_ERR_OR_NULL(record) && (PAGE_SIZE - length > 0)) + length += msm_rpmh_master_stats_print_data( + buf + length, PAGE_SIZE - length, + record, + rpmh_masters[i].master_name); + } + + mutex_unlock(&rpmh_stats_mutex); + + return length; +} + +static inline void msm_rpmh_apss_master_stats_update( + struct msm_rpmh_profile_unit *profile_unit) +{ + apss_master_stats.counts++; + apss_master_stats.last_entered = profile_unit[POWER_DOWN_END].value; + apss_master_stats.last_exited = profile_unit[POWER_UP_START].value; + apss_master_stats.accumulated_duration += + (apss_master_stats.last_exited + - apss_master_stats.last_entered); +} + +void msm_rpmh_master_stats_update(void) +{ + int i; + struct msm_rpmh_profile_unit profile_unit[NUM_UNIT]; + + if (!rpmh_unit_base) + return; + + for (i = POWER_DOWN_END; i < NUM_UNIT; i++) { + profile_unit[i].valid = readl_relaxed(rpmh_unit_base + + GET_ADDR(REG_VALID, i)); + + /* + * Do not update APSS stats if valid bit is not set. + * It means APSS did not execute cx-off sequence. + * This can be due to fall through at some point. + */ + + if (!(profile_unit[i].valid & BIT(REG_VALID))) + return; + + profile_unit[i].value = readl_relaxed(rpmh_unit_base + + GET_ADDR(REG_DATA_LO, i)); + profile_unit[i].value |= ((uint64_t) + readl_relaxed(rpmh_unit_base + + GET_ADDR(REG_DATA_HI, i)) << 32); + } + msm_rpmh_apss_master_stats_update(profile_unit); +} +EXPORT_SYMBOL(msm_rpmh_master_stats_update); + +static int msm_rpmh_master_stats_probe(struct platform_device *pdev) +{ + struct rpmh_master_stats_prv_data *prvdata = NULL; + struct kobject *rpmh_master_stats_kobj = NULL; + int ret = -ENOMEM; + + if (!pdev) + return -EINVAL; + + prvdata = devm_kzalloc(&pdev->dev, sizeof(*prvdata), GFP_KERNEL); + if (!prvdata) + return ret; + + rpmh_master_stats_kobj = kobject_create_and_add( + "rpmh_stats", + power_kobj); + if (!rpmh_master_stats_kobj) + return ret; + + prvdata->kobj = rpmh_master_stats_kobj; + + sysfs_attr_init(&prvdata->ka.attr); + prvdata->ka.attr.mode = 0444; + prvdata->ka.attr.name = "master_stats"; + prvdata->ka.show = msm_rpmh_master_stats_show; + prvdata->ka.store = NULL; + + ret = sysfs_create_file(prvdata->kobj, &prvdata->ka.attr); + if (ret) { + pr_err("sysfs_create_file failed\n"); + goto fail_sysfs; + } + + rpmh_unit_base = of_iomap(pdev->dev.of_node, 0); + if (!rpmh_unit_base) { + pr_err("Failed to get rpmh_unit_base\n"); + ret = -ENOMEM; + goto fail_iomap; + } + + apss_master_stats.version_id = 0x1; + platform_set_drvdata(pdev, prvdata); + return ret; + +fail_iomap: + sysfs_remove_file(prvdata->kobj, &prvdata->ka.attr); +fail_sysfs: + kobject_put(prvdata->kobj); + return ret; +} + +static int msm_rpmh_master_stats_remove(struct platform_device *pdev) +{ + struct rpmh_master_stats_prv_data *prvdata; + + if (!pdev) + return -EINVAL; + + prvdata = (struct rpmh_master_stats_prv_data *) + platform_get_drvdata(pdev); + + sysfs_remove_file(prvdata->kobj, &prvdata->ka.attr); + kobject_put(prvdata->kobj); + platform_set_drvdata(pdev, NULL); + iounmap(rpmh_unit_base); + + return 0; +} + +static const struct of_device_id rpmh_master_table[] = { + {.compatible = "qcom,rpmh-master-stats-v1"}, + {}, +}; + +static struct platform_driver msm_rpmh_master_stats_driver = { + .probe = msm_rpmh_master_stats_probe, + .remove = msm_rpmh_master_stats_remove, + .driver = { + .name = "msm_rpmh_master_stats", + .of_match_table = rpmh_master_table, + }, +}; + +module_platform_driver(msm_rpmh_master_stats_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM RPMH Master Statistics driver"); +MODULE_ALIAS("platform:msm_rpmh_master_stat_log"); diff --git a/drivers/soc/qcom/rpmh_master_stat.h b/drivers/soc/qcom/rpmh_master_stat.h new file mode 100644 index 0000000000000000000000000000000000000000..c3fe7dcff97d3c7402cc345854f4c376ab5e0dcd --- /dev/null +++ b/drivers/soc/qcom/rpmh_master_stat.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 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. + * + */ + +#if defined(CONFIG_QTI_RPMH_API) && defined(CONFIG_QTI_RPM_STATS_LOG) + +void msm_rpmh_master_stats_update(void); + +#else + +static inline void msm_rpmh_master_stats_update(void) {} + +#endif diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c index e82861ee563b67dc7e3e9abe9416337c678e0a77..d8e82c0c8f675af927439ac613f8dd528d7588d0 100644 --- a/drivers/soc/qcom/scm.c +++ b/drivers/soc/qcom/scm.c @@ -80,6 +80,7 @@ DEFINE_MUTEX(scm_lmh_lock); #define R3_STR "r3" #define R4_STR "r4" #define R5_STR "r5" +#define R6_STR "r6" #endif @@ -277,7 +278,7 @@ static enum scm_interface_version { /* This will be set to specify SMC32 or SMC64 */ static u32 scm_version_mask; -static bool is_scm_armv8(void) +bool is_scm_armv8(void) { int ret; u64 ret1, x0; @@ -588,6 +589,56 @@ int scm_is_call_available(u32 svc_id, u32 cmd_id) } EXPORT_SYMBOL(scm_is_call_available); +#define GET_FEAT_VERSION_CMD 3 +int scm_get_feat_version(u32 feat) +{ + struct scm_desc desc = {0}; + int ret; + + ret = scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD); + if (ret <= 0) + return 0; + + desc.args[0] = feat; + desc.arginfo = SCM_ARGS(1); + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, GET_FEAT_VERSION_CMD), + &desc); + if (!ret) + return desc.ret[0]; + + return 0; +} +EXPORT_SYMBOL(scm_get_feat_version); + +#define RESTORE_SEC_CFG 2 +int scm_restore_sec_cfg(u32 device_id, u32 spare, int *scm_ret) +{ + struct scm_desc desc = {0}; + int ret; + struct restore_sec_cfg { + u32 device_id; + u32 spare; + } cfg; + + cfg.device_id = device_id; + cfg.spare = spare; + + if (IS_ERR_OR_NULL(scm_ret)) + return -EINVAL; + + desc.args[0] = device_id; + desc.args[1] = spare; + desc.arginfo = SCM_ARGS(2); + + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP, RESTORE_SEC_CFG), &desc); + if (ret) + return ret; + + *scm_ret = desc.ret[0]; + return 0; +} +EXPORT_SYMBOL(scm_restore_sec_cfg); + /* * SCM call command ID to check secure mode * Return zero for secure device. @@ -618,3 +669,44 @@ bool scm_is_secure_device(void) return false; } EXPORT_SYMBOL(scm_is_secure_device); + +/* + * SCM call command ID to protect kernel memory + * in Hyp Stage 2 page tables. + * Return zero for success. + * Return non-zero for failure. + */ +#define TZ_RTIC_ENABLE_MEM_PROTECTION 0x4 +#if IS_ENABLED(CONFIG_QCOM_QHEE_ENABLE_MEM_PROTECTION) +int scm_enable_mem_protection(void) +{ + struct scm_desc desc = {0}; + int ret = 0, resp; + + desc.args[0] = 0; + desc.arginfo = 0; + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_RTIC, + TZ_RTIC_ENABLE_MEM_PROTECTION), + &desc); + resp = desc.ret[0]; + + if (ret == -1) { + pr_err("%s: SCM call not supported\n", __func__); + return ret; + } else if (ret || resp) { + pr_err("%s: SCM call failed\n", __func__); + if (ret) + return ret; + else + return resp; + } + + return resp; +} +#else +inline int scm_enable_mem_protection(void) +{ + return 0; +} +#endif +EXPORT_SYMBOL(scm_enable_mem_protection); diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c new file mode 100644 index 0000000000000000000000000000000000000000..28bb85bd3870621218004458d4470777f26e3653 --- /dev/null +++ b/drivers/soc/qcom/smcinvoke.c @@ -0,0 +1,575 @@ +/* + * SMC Invoke driver + * + * Copyright (c) 2016-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "smcinvoke_object.h" +#include "../../misc/qseecom_kernel.h" + +#define SMCINVOKE_DEV "smcinvoke" +#define SMCINVOKE_TZ_PARAM_ID 0x224 +#define SMCINVOKE_TZ_CMD 0x32000600 +#define SMCINVOKE_TZ_ROOT_OBJ 1 +#define SMCINVOKE_TZ_MIN_BUF_SIZE 4096 +#define SMCINVOKE_ARGS_ALIGN_SIZE (sizeof(uint64_t)) +#define SMCINVOKE_TZ_OBJ_NULL 0 + +#define FOR_ARGS(ndxvar, counts, section) \ + for (ndxvar = object_counts_index_##section(counts); \ + ndxvar < (object_counts_index_##section(counts) \ + + object_counts_num_##section(counts)); \ + ++ndxvar) + +static long smcinvoke_ioctl(struct file *, unsigned int, unsigned long); +static int smcinvoke_open(struct inode *, struct file *); +static int smcinvoke_release(struct inode *, struct file *); + +static const struct file_operations smcinvoke_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = smcinvoke_ioctl, + .compat_ioctl = smcinvoke_ioctl, + .open = smcinvoke_open, + .release = smcinvoke_release, +}; + +struct smcinvoke_buf_hdr { + uint32_t offset; + uint32_t size; +}; + +union smcinvoke_tz_args { + struct smcinvoke_buf_hdr b; + uint32_t tzhandle; +}; +struct smcinvoke_msg_hdr { + uint32_t tzhandle; + uint32_t op; + uint32_t counts; +}; + +struct smcinvoke_tzobj_context { + uint32_t tzhandle; +}; + +static dev_t smcinvoke_device_no; +struct cdev smcinvoke_cdev; +struct class *driver_class; +struct device *class_dev; + +/* + * size_add saturates at SIZE_MAX. If integer overflow is detected, + * this function would return SIZE_MAX otherwise normal a+b is returned. + */ +static inline size_t size_add(size_t a, size_t b) +{ + return (b > (SIZE_MAX - a)) ? SIZE_MAX : a + b; +} + +/* + * pad_size is used along with size_align to define a buffer overflow + * protected version of ALIGN + */ +static inline size_t pad_size(size_t a, size_t b) +{ + return (~a + 1) % b; +} + +/* + * size_align saturates at SIZE_MAX. If integer overflow is detected, this + * function would return SIZE_MAX otherwise next aligned size is returned. + */ +static inline size_t size_align(size_t a, size_t b) +{ + return size_add(a, pad_size(a, b)); +} + +/* + * This function retrieves file pointer corresponding to FD provided. It stores + * retrived file pointer until IOCTL call is concluded. Once call is completed, + * all stored file pointers are released. file pointers are stored to prevent + * other threads from releasing that FD while IOCTL is in progress. + */ +static int get_tzhandle_from_fd(int64_t fd, struct file **filp, + uint32_t *tzhandle) +{ + int ret = -EBADF; + struct file *tmp_filp = NULL; + struct smcinvoke_tzobj_context *tzobj = NULL; + + if (fd == SMCINVOKE_USERSPACE_OBJ_NULL) { + *tzhandle = SMCINVOKE_TZ_OBJ_NULL; + ret = 0; + goto out; + } else if (fd < SMCINVOKE_USERSPACE_OBJ_NULL) { + goto out; + } + + tmp_filp = fget(fd); + if (!tmp_filp) + goto out; + + /* Verify if filp is smcinvoke device's file pointer */ + if (!tmp_filp->f_op || !tmp_filp->private_data || + (tmp_filp->f_op != &smcinvoke_fops)) { + fput(tmp_filp); + goto out; + } + + tzobj = tmp_filp->private_data; + *tzhandle = tzobj->tzhandle; + *filp = tmp_filp; + ret = 0; +out: + return ret; +} + +static int get_fd_from_tzhandle(uint32_t tzhandle, int64_t *fd) +{ + int unused_fd = -1, ret = -1; + struct file *f = NULL; + struct smcinvoke_tzobj_context *cxt = NULL; + + if (tzhandle == SMCINVOKE_TZ_OBJ_NULL) { + *fd = SMCINVOKE_USERSPACE_OBJ_NULL; + ret = 0; + goto out; + } + + cxt = kzalloc(sizeof(*cxt), GFP_KERNEL); + if (!cxt) { + ret = -ENOMEM; + goto out; + } + unused_fd = get_unused_fd_flags(O_RDWR); + if (unused_fd < 0) + goto out; + + f = anon_inode_getfile(SMCINVOKE_DEV, &smcinvoke_fops, cxt, O_RDWR); + if (IS_ERR(f)) + goto out; + + *fd = unused_fd; + fd_install(*fd, f); + ((struct smcinvoke_tzobj_context *) + (f->private_data))->tzhandle = tzhandle; + return 0; +out: + if (unused_fd >= 0) + put_unused_fd(unused_fd); + kfree(cxt); + + return ret; +} + +static int prepare_send_scm_msg(const uint8_t *in_buf, size_t in_buf_len, + const uint8_t *out_buf, size_t out_buf_len, + int32_t *smcinvoke_result) +{ + int ret = 0; + struct scm_desc desc = {0}; + size_t inbuf_flush_size = (1UL << get_order(in_buf_len)) * PAGE_SIZE; + size_t outbuf_flush_size = (1UL << get_order(out_buf_len)) * PAGE_SIZE; + + desc.arginfo = SMCINVOKE_TZ_PARAM_ID; + desc.args[0] = (uint64_t)virt_to_phys(in_buf); + desc.args[1] = inbuf_flush_size; + desc.args[2] = (uint64_t)virt_to_phys(out_buf); + desc.args[3] = outbuf_flush_size; + + dmac_flush_range(in_buf, in_buf + inbuf_flush_size); + dmac_flush_range(out_buf, out_buf + outbuf_flush_size); + + ret = scm_call2(SMCINVOKE_TZ_CMD, &desc); + + /* process listener request */ + if (!ret && (desc.ret[0] == QSEOS_RESULT_INCOMPLETE || + desc.ret[0] == QSEOS_RESULT_BLOCKED_ON_LISTENER)) + ret = qseecom_process_listener_from_smcinvoke(&desc); + + *smcinvoke_result = (int32_t)desc.ret[1]; + if (ret || desc.ret[1] || desc.ret[2] || desc.ret[0]) + pr_err("SCM call failed with ret val = %d %d %d %d\n", + ret, (int)desc.ret[0], + (int)desc.ret[1], (int)desc.ret[2]); + + dmac_inv_range(in_buf, in_buf + inbuf_flush_size); + dmac_inv_range(out_buf, out_buf + outbuf_flush_size); + return ret; +} + +static int marshal_out(void *buf, uint32_t buf_size, + struct smcinvoke_cmd_req *req, + union smcinvoke_arg *args_buf) +{ + int ret = -EINVAL, i = 0; + union smcinvoke_tz_args *tz_args = NULL; + size_t offset = sizeof(struct smcinvoke_msg_hdr) + + object_counts_total(req->counts) * + sizeof(union smcinvoke_tz_args); + + if (offset > buf_size) + goto out; + + tz_args = (union smcinvoke_tz_args *) + (buf + sizeof(struct smcinvoke_msg_hdr)); + + tz_args += object_counts_num_BI(req->counts); + + FOR_ARGS(i, req->counts, BO) { + args_buf[i].b.size = tz_args->b.size; + if ((buf_size - tz_args->b.offset < tz_args->b.size) || + tz_args->b.offset > buf_size) { + pr_err("%s: buffer overflow detected\n", __func__); + goto out; + } + if (copy_to_user((void __user *)(uintptr_t)(args_buf[i].b.addr), + (uint8_t *)(buf) + tz_args->b.offset, + tz_args->b.size)) { + pr_err("Error %d copying ctxt to user\n", ret); + goto out; + } + tz_args++; + } + tz_args += object_counts_num_OI(req->counts); + + FOR_ARGS(i, req->counts, OO) { + /* + * create a new FD and assign to output object's + * context + */ + ret = get_fd_from_tzhandle(tz_args->tzhandle, + &(args_buf[i].o.fd)); + if (ret) + goto out; + tz_args++; + } + ret = 0; +out: + return ret; +} + +/* + * SMC expects arguments in following format + * --------------------------------------------------------------------------- + * | cxt | op | counts | ptr|size |ptr|size...|ORef|ORef|...| rest of payload | + * --------------------------------------------------------------------------- + * cxt: target, op: operation, counts: total arguments + * offset: offset is from beginning of buffer i.e. cxt + * size: size is 8 bytes aligned value + */ +static size_t compute_in_msg_size(const struct smcinvoke_cmd_req *req, + const union smcinvoke_arg *args_buf) +{ + uint32_t i = 0; + + size_t total_size = sizeof(struct smcinvoke_msg_hdr) + + object_counts_total(req->counts) * + sizeof(union smcinvoke_tz_args); + + /* Computed total_size should be 8 bytes aligned from start of buf */ + total_size = ALIGN(total_size, SMCINVOKE_ARGS_ALIGN_SIZE); + + /* each buffer has to be 8 bytes aligned */ + while (i < object_counts_num_buffers(req->counts)) + total_size = size_add(total_size, + size_align(args_buf[i++].b.size, SMCINVOKE_ARGS_ALIGN_SIZE)); + + /* Since we're using get_free_pages, no need for explicit PAGE align */ + return total_size; +} + +static int marshal_in(const struct smcinvoke_cmd_req *req, + const union smcinvoke_arg *args_buf, uint32_t tzhandle, + uint8_t *buf, size_t buf_size, struct file **arr_filp) +{ + int ret = -EINVAL, i = 0; + union smcinvoke_tz_args *tz_args = NULL; + struct smcinvoke_msg_hdr msg_hdr = {tzhandle, req->op, req->counts}; + uint32_t offset = sizeof(struct smcinvoke_msg_hdr) + + sizeof(union smcinvoke_tz_args) * + object_counts_total(req->counts); + + if (buf_size < offset) + goto out; + + *(struct smcinvoke_msg_hdr *)buf = msg_hdr; + tz_args = (union smcinvoke_tz_args *) + (buf + sizeof(struct smcinvoke_msg_hdr)); + + FOR_ARGS(i, req->counts, BI) { + offset = size_align(offset, SMCINVOKE_ARGS_ALIGN_SIZE); + if ((offset > buf_size) || + (args_buf[i].b.size > (buf_size - offset))) + goto out; + + tz_args->b.offset = offset; + tz_args->b.size = args_buf[i].b.size; + tz_args++; + + if (copy_from_user(buf+offset, + (void __user *)(uintptr_t)(args_buf[i].b.addr), + args_buf[i].b.size)) + goto out; + + offset += args_buf[i].b.size; + } + FOR_ARGS(i, req->counts, BO) { + offset = size_align(offset, SMCINVOKE_ARGS_ALIGN_SIZE); + if ((offset > buf_size) || + (args_buf[i].b.size > (buf_size - offset))) + goto out; + + tz_args->b.offset = offset; + tz_args->b.size = args_buf[i].b.size; + tz_args++; + + offset += args_buf[i].b.size; + } + FOR_ARGS(i, req->counts, OI) { + if (get_tzhandle_from_fd(args_buf[i].o.fd, + &arr_filp[i], &(tz_args->tzhandle))) + goto out; + tz_args++; + } + ret = 0; +out: + return ret; +} + +long smcinvoke_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = -1, i = 0, nr_args = 0; + struct smcinvoke_cmd_req req = {0}; + void *in_msg = NULL; + size_t inmsg_size = 0; + void *out_msg = NULL; + union smcinvoke_arg *args_buf = NULL; + struct file *filp_to_release[object_counts_max_OO] = {NULL}; + struct smcinvoke_tzobj_context *tzobj = filp->private_data; + + switch (cmd) { + case SMCINVOKE_IOCTL_INVOKE_REQ: + if (_IOC_SIZE(cmd) != sizeof(req)) { + ret = -EINVAL; + goto out; + } + ret = copy_from_user(&req, (void __user *)arg, sizeof(req)); + if (ret) { + ret = -EFAULT; + goto out; + } + + nr_args = object_counts_num_buffers(req.counts) + + object_counts_num_objects(req.counts); + + if (req.argsize != sizeof(union smcinvoke_arg)) { + ret = -EINVAL; + goto out; + } + + if (nr_args) { + + args_buf = kzalloc(nr_args * req.argsize, GFP_KERNEL); + if (!args_buf) { + ret = -ENOMEM; + goto out; + } + + ret = copy_from_user(args_buf, + (void __user *)(uintptr_t)(req.args), + nr_args * req.argsize); + + if (ret) { + ret = -EFAULT; + goto out; + } + } + + inmsg_size = compute_in_msg_size(&req, args_buf); + in_msg = (void *)__get_free_pages(GFP_KERNEL, + get_order(inmsg_size)); + if (!in_msg) { + ret = -ENOMEM; + goto out; + } + + out_msg = (void *)__get_free_page(GFP_KERNEL); + if (!out_msg) { + ret = -ENOMEM; + goto out; + } + + ret = marshal_in(&req, args_buf, tzobj->tzhandle, in_msg, + inmsg_size, filp_to_release); + if (ret) + goto out; + + ret = prepare_send_scm_msg(in_msg, inmsg_size, out_msg, + SMCINVOKE_TZ_MIN_BUF_SIZE, &req.result); + if (ret) + goto out; + + /* + * if invoke op results in an err, no need to marshal_out and + * copy args buf to user space + */ + if (!req.result) { + ret = marshal_out(in_msg, inmsg_size, &req, args_buf); + + ret |= copy_to_user( + (void __user *)(uintptr_t)(req.args), + args_buf, nr_args * req.argsize); + } + ret |= copy_to_user((void __user *)arg, &req, sizeof(req)); + if (ret) + goto out; + + break; + default: + ret = -ENOIOCTLCMD; + break; + } +out: + free_page((long)out_msg); + free_pages((long)in_msg, get_order(inmsg_size)); + kfree(args_buf); + for (i = 0; i < object_counts_max_OO; i++) { + if (filp_to_release[i]) + fput(filp_to_release[i]); + } + + return ret; +} + +static int smcinvoke_open(struct inode *nodp, struct file *filp) +{ + struct smcinvoke_tzobj_context *tzcxt = NULL; + + tzcxt = kzalloc(sizeof(*tzcxt), GFP_KERNEL); + if (!tzcxt) + return -ENOMEM; + + tzcxt->tzhandle = SMCINVOKE_TZ_ROOT_OBJ; + filp->private_data = tzcxt; + + return 0; +} + +static int smcinvoke_release(struct inode *nodp, struct file *filp) +{ + int ret = 0, smcinvoke_result = 0; + uint8_t *in_buf = NULL; + uint8_t *out_buf = NULL; + struct smcinvoke_msg_hdr hdr = {0}; + struct smcinvoke_tzobj_context *tzobj = filp->private_data; + uint32_t tzhandle = tzobj->tzhandle; + + /* Root object is special in sense it is indestructible */ + if (!tzhandle || tzhandle == SMCINVOKE_TZ_ROOT_OBJ) + goto out; + + in_buf = (uint8_t *)__get_free_page(GFP_KERNEL); + out_buf = (uint8_t *)__get_free_page(GFP_KERNEL); + if (!in_buf || !out_buf) + goto out; + + hdr.tzhandle = tzhandle; + hdr.op = object_op_RELEASE; + hdr.counts = 0; + *(struct smcinvoke_msg_hdr *)in_buf = hdr; + + ret = prepare_send_scm_msg(in_buf, SMCINVOKE_TZ_MIN_BUF_SIZE, + out_buf, SMCINVOKE_TZ_MIN_BUF_SIZE, &smcinvoke_result); +out: + kfree(filp->private_data); + free_page((long)in_buf); + free_page((long)out_buf); + + return ret; +} + +static int __init smcinvoke_init(void) +{ + unsigned int baseminor = 0; + unsigned int count = 1; + int rc = 0; + + rc = alloc_chrdev_region(&smcinvoke_device_no, baseminor, count, + SMCINVOKE_DEV); + if (rc < 0) { + pr_err("chrdev_region failed %d for %s\n", rc, SMCINVOKE_DEV); + return rc; + } + driver_class = class_create(THIS_MODULE, SMCINVOKE_DEV); + if (IS_ERR(driver_class)) { + rc = -ENOMEM; + pr_err("class_create failed %d\n", rc); + goto exit_unreg_chrdev_region; + } + class_dev = device_create(driver_class, NULL, smcinvoke_device_no, + NULL, SMCINVOKE_DEV); + if (!class_dev) { + pr_err("class_device_create failed %d\n", rc); + rc = -ENOMEM; + goto exit_destroy_class; + } + + cdev_init(&smcinvoke_cdev, &smcinvoke_fops); + smcinvoke_cdev.owner = THIS_MODULE; + + rc = cdev_add(&smcinvoke_cdev, MKDEV(MAJOR(smcinvoke_device_no), 0), + count); + if (rc < 0) { + pr_err("cdev_add failed %d for %s\n", rc, SMCINVOKE_DEV); + goto exit_destroy_device; + } + return 0; + +exit_destroy_device: + device_destroy(driver_class, smcinvoke_device_no); +exit_destroy_class: + class_destroy(driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(smcinvoke_device_no, count); + + return rc; +} + +static void __exit smcinvoke_exit(void) +{ + int count = 1; + + cdev_del(&smcinvoke_cdev); + device_destroy(driver_class, smcinvoke_device_no); + class_destroy(driver_class); + unregister_chrdev_region(smcinvoke_device_no, count); +} +device_initcall(smcinvoke_init); +module_exit(smcinvoke_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SMC Invoke driver"); diff --git a/drivers/soc/qcom/smcinvoke_object.h b/drivers/soc/qcom/smcinvoke_object.h new file mode 100644 index 0000000000000000000000000000000000000000..2761f87e14f550dd25d36d3c96562ec34ac59ab7 --- /dev/null +++ b/drivers/soc/qcom/smcinvoke_object.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2016-2018, 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 __SMCINVOKE_OBJECT_H +#define __SMCINVOKE_OBJECT_H + +#include + +#define object_op_METHOD_MASK ((uint32_t)0x0000FFFFu) +#define object_op_RELEASE (object_op_METHOD_MASK - 0) +#define object_op_RETAIN (object_op_METHOD_MASK - 1) + +#define object_counts_max_BI 0xF +#define object_counts_max_BO 0xF +#define object_counts_max_OI 0xF +#define object_counts_max_OO 0xF + +/* unpack counts */ + +#define object_counts_num_BI(k) ((size_t) (((k) >> 0) & object_counts_max_BI)) +#define object_counts_num_BO(k) ((size_t) (((k) >> 4) & object_counts_max_BO)) +#define object_counts_num_OI(k) ((size_t) (((k) >> 8) & object_counts_max_OI)) +#define object_counts_num_OO(k) ((size_t) (((k) >> 12) & object_counts_max_OO)) +#define object_counts_num_buffers(k) \ + (object_counts_num_BI(k) + object_counts_num_BO(k)) + +#define object_counts_num_objects(k) \ + (object_counts_num_OI(k) + object_counts_num_OO(k)) + +/* Indices into args[] */ + +#define object_counts_index_BI(k) 0 +#define object_counts_index_BO(k) \ + (object_counts_index_BI(k) + object_counts_num_BI(k)) +#define object_counts_index_OI(k) \ + (object_counts_index_BO(k) + object_counts_num_BO(k)) +#define object_counts_index_OO(k) \ + (object_counts_index_OI(k) + object_counts_num_OI(k)) +#define object_counts_total(k) \ + (object_counts_index_OO(k) + object_counts_num_OO(k)) + + +#endif /* __SMCINVOKE_OBJECT_H */ diff --git a/drivers/soc/qcom/smp2p_sleepstate.c b/drivers/soc/qcom/smp2p_sleepstate.c new file mode 100644 index 0000000000000000000000000000000000000000..d2b8733a4b7f19663ee0591ae0d84751a49972a0 --- /dev/null +++ b/drivers/soc/qcom/smp2p_sleepstate.c @@ -0,0 +1,112 @@ +/* Copyright (c) 2014-2018, 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 +#include +#include +#include +#include +#include +#include +#include "smp2p_private.h" + +#define SET_DELAY (2 * HZ) +#define PROC_AWAKE_ID 12 /* 12th bit */ +static int slst_gpio_base_id; + +/** + * sleepstate_pm_notifier() - PM notifier callback function. + * @nb: Pointer to the notifier block. + * @event: Suspend state event from PM module. + * @unused: Null pointer from PM module. + * + * This function is register as callback function to get notifications + * from the PM module on the system suspend state. + */ +static int sleepstate_pm_notifier(struct notifier_block *nb, + unsigned long event, void *unused) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 0); + usleep_range(10000, 10500); /* Tuned based on SMP2P latencies */ + msm_ipc_router_set_ws_allowed(true); + break; + + case PM_POST_SUSPEND: + gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 1); + msm_ipc_router_set_ws_allowed(false); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block sleepstate_pm_nb = { + .notifier_call = sleepstate_pm_notifier, + .priority = INT_MAX, +}; + +static int smp2p_sleepstate_probe(struct platform_device *pdev) +{ + int ret; + struct device_node *node = pdev->dev.of_node; + + slst_gpio_base_id = of_get_gpio(node, 0); + if (slst_gpio_base_id == -EPROBE_DEFER) { + return slst_gpio_base_id; + } else if (slst_gpio_base_id < 0) { + SMP2P_ERR("%s: Error to get gpio %d\n", + __func__, slst_gpio_base_id); + return slst_gpio_base_id; + } + + + gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 1); + + ret = register_pm_notifier(&sleepstate_pm_nb); + if (ret) + SMP2P_ERR("%s: power state notif error %d\n", __func__, ret); + + return 0; +} + +static const struct of_device_id msm_smp2p_slst_match_table[] = { + {.compatible = "qcom,smp2pgpio_sleepstate_3_out"}, + {.compatible = "qcom,smp2pgpio-sleepstate-out"}, + {}, +}; + +static struct platform_driver smp2p_sleepstate_driver = { + .probe = smp2p_sleepstate_probe, + .driver = { + .name = "smp2p_sleepstate", + .owner = THIS_MODULE, + .of_match_table = msm_smp2p_slst_match_table, + }, +}; + +static int __init smp2p_sleepstate_init(void) +{ + int ret; + + ret = platform_driver_register(&smp2p_sleepstate_driver); + if (ret) { + SMP2P_ERR("%s: smp2p_sleepstate_driver register failed %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +module_init(smp2p_sleepstate_init); +MODULE_DESCRIPTION("SMP2P SLEEP STATE"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index b4132861a773e14fb4248e2188cd08773321609b..d5f1cf0972afb2bdc70164947d91fe3d21d2efc1 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -307,11 +307,14 @@ static struct msm_soc_info cpu_of_id[] = { /* sm8150 ID */ [339] = {MSM_CPU_SM8150, "SM8150"}, + /* sa8150 ID */ + [362] = {MSM_CPU_SA8150, "SA8150"}, + /* sdmshrike ID */ [340] = {MSM_CPU_SDMSHRIKE, "SDMSHRIKE"}, - /* sdm640 ID */ - [355] = {MSM_CPU_SDM640, "SDM640"}, + /* sm6150 ID */ + [355] = {MSM_CPU_SM6150, "SM6150"}, /* qcs405 ID */ [352] = {MSM_CPU_QCS405, "QCS405"}, @@ -1171,13 +1174,17 @@ static void * __init setup_dummy_socinfo(void) dummy_socinfo.id = 339; strlcpy(dummy_socinfo.build_id, "sm8150 - ", sizeof(dummy_socinfo.build_id)); + } else if (early_machine_is_sa8150()) { + dummy_socinfo.id = 362; + strlcpy(dummy_socinfo.build_id, "sa8150 - ", + sizeof(dummy_socinfo.build_id)); } else if (early_machine_is_sdmshrike()) { dummy_socinfo.id = 340; strlcpy(dummy_socinfo.build_id, "sdmshrike - ", sizeof(dummy_socinfo.build_id)); - } else if (early_machine_is_sdm640()) { + } else if (early_machine_is_sm6150()) { dummy_socinfo.id = 355; - strlcpy(dummy_socinfo.build_id, "sdm640 - ", + strlcpy(dummy_socinfo.build_id, "sm6150 - ", sizeof(dummy_socinfo.build_id)); } else if (early_machine_is_qcs405()) { dummy_socinfo.id = 352; diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c new file mode 100644 index 0000000000000000000000000000000000000000..cb531e5f31ad288f8e135a50c9922a20451267f0 --- /dev/null +++ b/drivers/soc/qcom/spcom.c @@ -0,0 +1,2120 @@ +/* + * Copyright (c) 2015-2018, 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. + */ + +/* + * Secure-Processor-Communication (SPCOM). + * + * This driver provides communication to Secure Processor (SP) + * over RPMSG framework. + * + * It provides interface to userspace spcomlib. + * + * Userspace application shall use spcomlib for communication with SP. Userspace + * application can be either client or server. spcomlib shall use write() file + * operation to send data, and read() file operation to read data. + * + * This driver uses RPMSG with glink-spss as a transport layer. + * This driver exposes "/dev/" file node for each rpmsg logical + * channel. + * This driver exposes "/dev/spcom" file node for some debug/control command. + * The predefined channel "/dev/sp_kernel" is used for loading SP application + * from HLOS. + * This driver exposes "/dev/sp_ssr" file node to allow user space poll for SSR. + * After the remote SP App is loaded, this driver exposes a new file node + * "/dev/" for the matching HLOS App to use. + * The access to predefined file nodes and dynamically allocated file nodes is + * restricted by using unix group and SELinux. + * + * No message routing is used, but using the rpmsg/G-Link "multiplexing" feature + * to use a dedicated logical channel for HLOS and SP Application-Pair. + * + * Each HLOS/SP Application can be either Client or Server or both, + * Messaging is allways point-to-point between 2 HLOS<=>SP applications. + * Each channel exclusevly used by single Client or Server. + * + * User Space Request & Response are synchronous. + * read() & write() operations are blocking until completed or terminated. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include /* min() */ +#include /* MODULE_LICENSE */ +#include /* class_create() */ +#include /* kzalloc() */ +#include /* file_operations */ +#include /* cdev_add() */ +#include /* EINVAL, ETIMEDOUT */ +#include /* pr_err() */ +#include /* BIT(x) */ +#include /* wait_for_completion_timeout() */ +#include /* POLLOUT */ +#include +#include /* of_property_count_strings() */ +#include +#include /* msleep() */ +#include +#include +#include +#include +#include +#include +#include + +/** + * Request buffer size. + * Any large data (multiply of 4KB) is provided by temp buffer in DDR. + * Request shall provide the temp buffer physical address (align to 4KB). + * Maximum request/response size of 268 is used to accommodate APDU size. + * From kernel spcom driver perspective a PAGE_SIZE of 4K + * is the actual maximum size for a single read/write file operation. + */ +#define SPCOM_MAX_RESPONSE_SIZE 268 + +/* SPCOM driver name */ +#define DEVICE_NAME "spcom" + +/* maximum ION buffers should be >= SPCOM_MAX_CHANNELS */ +#define SPCOM_MAX_ION_BUF_PER_CH (SPCOM_MAX_CHANNELS + 4) + +/* maximum ION buffer per send request/response command */ +#define SPCOM_MAX_ION_BUF_PER_CMD SPCOM_MAX_ION_BUF + +/* Maximum command size */ +#define SPCOM_MAX_COMMAND_SIZE (PAGE_SIZE) + +/* Maximum input size */ +#define SPCOM_MAX_READ_SIZE (PAGE_SIZE) + +/* Current Process ID */ +#define current_pid() ((u32)(current->pid)) + +/* + * After both sides get CONNECTED, + * there is a race between one side queueing rx buffer and the other side + * trying to call glink_tx() , this race is only on the 1st tx. + * Do tx retry with some delay to allow the other side to queue rx buffer. + */ +#define TX_RETRY_DELAY_MSEC 100 + +/* SPCOM_MAX_REQUEST_SIZE-or-SPCOM_MAX_RESPONSE_SIZE + header */ +#define SPCOM_RX_BUF_SIZE 300 + +/* + * Initial transaction id, use non-zero nonce for debug. + * Incremented by client on request, and copied back by server on response. + */ +#define INITIAL_TXN_ID 0x12345678 + +/** + * struct spcom_msg_hdr - Request/Response message header between HLOS and SP. + * + * This header is proceeding any request specific parameters. + * The transaction id is used to match request with response. + * Note: rpmsg API provides the rx/tx data size, so user payload size is + * calculated by reducing the header size. + */ +struct spcom_msg_hdr { + uint32_t reserved; /* for future use */ + uint32_t txn_id; /* transaction id */ + char buf[0]; /* Variable buffer size, must be last field */ +} __packed; + +/** + * struct spcom_client - Client handle + */ +struct spcom_client { + struct spcom_channel *ch; +}; + +/** + * struct spcom_server - Server handle + */ +struct spcom_server { + struct spcom_channel *ch; +}; + +/** + * struct spcom_channel - channel context + */ +struct spcom_channel { + char name[SPCOM_CHANNEL_NAME_SIZE]; + struct mutex lock; + uint32_t txn_id; /* incrementing nonce per client request */ + bool is_server; /* for txn_id and response_timeout_msec */ + uint32_t response_timeout_msec; /* for client only */ + + /* char dev */ + struct cdev *cdev; + struct device *dev; + struct device_attribute attr; + + /* rpmsg */ + struct rpmsg_driver *rpdrv; + struct rpmsg_device *rpdev; + + /* Events notification */ + struct completion rx_done; + struct completion connect; + + /* + * Only one client or server per channel. + * Only one rx/tx transaction at a time (request + response). + */ + bool is_busy; + + u32 pid; /* debug only to find user space application */ + + /* abort flags */ + bool rpmsg_abort; + + /* rx data info */ + size_t actual_rx_size; /* actual data size received */ + void *rpmsg_rx_buf; + + /* shared buffer lock/unlock support */ + int dmabuf_fd_table[SPCOM_MAX_ION_BUF_PER_CH]; + struct dma_buf *dmabuf_handle_table[SPCOM_MAX_ION_BUF_PER_CH]; +}; + +/** + * struct rx_buff_list - holds rx rpmsg data, before it will be consumed + * by spcom_signal_rx_done worker, item per rx packet + */ +struct rx_buff_list { + struct list_head list; + + void *rpmsg_rx_buf; + int rx_buf_size; + struct spcom_channel *ch; +}; + +/** + * struct spcom_device - device state structure. + */ +struct spcom_device { + char predefined_ch_name[SPCOM_MAX_CHANNELS][SPCOM_CHANNEL_NAME_SIZE]; + + /* char device info */ + struct cdev cdev; + dev_t device_no; + struct class *driver_class; + struct device *class_dev; + struct platform_device *pdev; + + /* rpmsg channels */ + struct spcom_channel channels[SPCOM_MAX_CHANNELS]; + atomic_t chdev_count; + + struct completion rpmsg_state_change; + atomic_t rpmsg_dev_count; + + /* rx data path */ + struct list_head rx_list_head; + spinlock_t rx_lock; +}; + +/* Device Driver State */ +static struct spcom_device *spcom_dev; + +/* static functions declaration */ +static int spcom_create_channel_chardev(const char *name); +static struct spcom_channel *spcom_find_channel_by_name(const char *name); +static int spcom_register_rpmsg_drv(struct spcom_channel *ch); +static int spcom_unregister_rpmsg_drv(struct spcom_channel *ch); + +/** + * spcom_is_channel_open() - channel is open on this side. + * + * Channel is fully connected, when rpmsg driver is registered and + * rpmsg device probed + */ +static inline bool spcom_is_channel_open(struct spcom_channel *ch) +{ + return ch->rpdrv != NULL; +} + +/** + * spcom_is_channel_connected() - channel is fully connected by both sides. + */ +static inline bool spcom_is_channel_connected(struct spcom_channel *ch) +{ + /* Channel must be open before it gets connected */ + if (!spcom_is_channel_open(ch)) + return false; + + return ch->rpdev != NULL; +} + +/** + * spcom_create_predefined_channels_chardev() - expose predefined channels to + * user space. + * + * Predefined channels list is provided by device tree. Typically, it is for + * known servers on remote side that are not loaded by the HLOS + */ +static int spcom_create_predefined_channels_chardev(void) +{ + int i; + int ret; + static bool is_predefined_created; + + if (is_predefined_created) + return 0; + + for (i = 0; i < SPCOM_MAX_CHANNELS; i++) { + const char *name = spcom_dev->predefined_ch_name[i]; + + if (name[0] == 0) + break; + ret = spcom_create_channel_chardev(name); + if (ret) { + pr_err("failed to create chardev [%s], ret [%d].\n", + name, ret); + return -EFAULT; + } + } + + is_predefined_created = true; + + return 0; +} + +/*======================================================================*/ +/* UTILITIES */ +/*======================================================================*/ + +/** + * spcom_init_channel() - initialize channel state. + * + * @ch: channel state struct pointer + * @name: channel name + */ +static int spcom_init_channel(struct spcom_channel *ch, const char *name) +{ + if (!ch || !name || !name[0]) { + pr_err("invalid parameters.\n"); + return -EINVAL; + } + + strlcpy(ch->name, name, SPCOM_CHANNEL_NAME_SIZE); + + init_completion(&ch->rx_done); + init_completion(&ch->connect); + + mutex_init(&ch->lock); + ch->rpdrv = NULL; + ch->rpdev = NULL; + ch->actual_rx_size = 0; + ch->is_busy = false; + ch->txn_id = INITIAL_TXN_ID; /* use non-zero nonce for debug */ + ch->pid = 0; + ch->rpmsg_abort = false; + ch->rpmsg_rx_buf = NULL; + + return 0; +} + +/** + * spcom_find_channel_by_name() - find a channel by name. + * + * @name: channel name + * + * Return: a channel state struct. + */ +static struct spcom_channel *spcom_find_channel_by_name(const char *name) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(spcom_dev->channels); i++) { + struct spcom_channel *ch = &spcom_dev->channels[i]; + + if (strcmp(ch->name, name) == 0) + return ch; + } + + return NULL; +} + +/** + * spcom_rx() - wait for received data until timeout, unless pending rx data is + * already ready + * + * @ch: channel state struct pointer + * @buf: buffer pointer + * @size: buffer size + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_rx(struct spcom_channel *ch, + void *buf, + uint32_t size, + uint32_t timeout_msec) +{ + unsigned long jiffies = msecs_to_jiffies(timeout_msec); + long timeleft = 1; + int ret; + + mutex_lock(&ch->lock); + + /* check for already pending data */ + if (!ch->actual_rx_size) { + reinit_completion(&ch->rx_done); + + mutex_unlock(&ch->lock); /* unlock while waiting */ + /* wait for rx response */ + pr_debug("wait for rx done, timeout_msec=%d\n", timeout_msec); + if (timeout_msec) + timeleft = wait_for_completion_timeout(&ch->rx_done, + jiffies); + else + wait_for_completion(&ch->rx_done); + + mutex_lock(&ch->lock); + if (timeout_msec && timeleft == 0) { + pr_err("rx_done timeout expired %d ms\n", timeout_msec); + ret = -ETIMEDOUT; + goto exit_err; + } else if (ch->rpmsg_abort) { + pr_warn("rpmsg channel is closing\n"); + ret = -ERESTART; + goto exit_err; + } else if (ch->actual_rx_size) { + pr_debug("actual_rx_size is [%zu]\n", + ch->actual_rx_size); + } else { + pr_err("actual_rx_size is zero.\n"); + ret = -EFAULT; + goto exit_err; + } + } else { + pr_debug("pending data size [%zu], requested size [%zu]\n", + ch->actual_rx_size, size); + } + if (!ch->rpmsg_rx_buf) { + pr_err("invalid rpmsg_rx_buf.\n"); + ret = -ENOMEM; + goto exit_err; + } + + size = min_t(size_t, ch->actual_rx_size, size); + memcpy(buf, ch->rpmsg_rx_buf, size); + + pr_debug("copy size [%d].\n", (int) size); + + memset(ch->rpmsg_rx_buf, 0, ch->actual_rx_size); + kfree((void *)ch->rpmsg_rx_buf); + ch->rpmsg_rx_buf = NULL; + ch->actual_rx_size = 0; + + mutex_unlock(&ch->lock); + + return size; +exit_err: + mutex_unlock(&ch->lock); + return ret; +} + +/** + * spcom_get_next_request_size() - get request size. + * already ready + * + * @ch: channel state struct pointer + * + * Server needs the size of the next request to allocate a request buffer. + * Initially used intent-request, however this complicated the remote side, + * so both sides are not using glink_tx() with INTENT_REQ anymore. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_get_next_request_size(struct spcom_channel *ch) +{ + int size = -1; + + /* NOTE: Remote clients might not be connected yet.*/ + mutex_lock(&ch->lock); + reinit_completion(&ch->rx_done); + + /* check if already got it via callback */ + if (ch->actual_rx_size) { + pr_debug("next-req-size already ready ch [%s] size [%zu]\n", + ch->name, ch->actual_rx_size); + goto exit_ready; + } + mutex_unlock(&ch->lock); /* unlock while waiting */ + + pr_debug("Wait for Rx Done, ch [%s].\n", ch->name); + wait_for_completion(&ch->rx_done); + + mutex_lock(&ch->lock); /* re-lock after waiting */ + + if (ch->actual_rx_size == 0) { + pr_err("invalid rx size [%zu] ch [%s]\n", + ch->actual_rx_size, ch->name); + goto exit_error; + } + +exit_ready: + /* actual_rx_size not exeeds SPCOM_RX_BUF_SIZE*/ + size = (int)ch->actual_rx_size; + if (size > sizeof(struct spcom_msg_hdr)) { + size -= sizeof(struct spcom_msg_hdr); + } else { + pr_err("rx size [%d] too small.\n", size); + goto exit_error; + } + + mutex_unlock(&ch->lock); + return size; + +exit_error: + mutex_unlock(&ch->lock); + return -EFAULT; + + +} + +/*======================================================================*/ +/* USER SPACE commands handling */ +/*======================================================================*/ + +/** + * spcom_handle_create_channel_command() - Handle Create Channel command from + * user space. + * + * @cmd_buf: command buffer. + * @cmd_size: command buffer size. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_create_channel_command(void *cmd_buf, int cmd_size) +{ + int ret = 0; + struct spcom_user_create_channel_command *cmd = cmd_buf; + const char *ch_name; + const size_t maxlen = sizeof(cmd->ch_name); + + if (cmd_size != sizeof(*cmd)) { + pr_err("cmd_size [%d] , expected [%d].\n", + (int) cmd_size, (int) sizeof(*cmd)); + return -EINVAL; + } + + ch_name = cmd->ch_name; + if (strnlen(cmd->ch_name, maxlen) == maxlen) { + pr_err("channel name is not NULL terminated\n"); + return -EINVAL; + } + + pr_debug("ch_name [%s].\n", ch_name); + + ret = spcom_create_channel_chardev(ch_name); + + return ret; +} + +/** + * spcom_handle_restart_sp_command() - Handle Restart SP command from + * user space. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_restart_sp_command(void) +{ + void *subsystem_get_retval = NULL; + + pr_err("restart - PIL FW loading process initiated\n"); + + subsystem_get_retval = subsystem_get("spss"); + if (!subsystem_get_retval) { + pr_err("restart - unable to trigger PIL process for FW loading\n"); + return -EINVAL; + } + + pr_err("restart - PIL FW loading process is complete\n"); + return 0; +} + +/** + * spcom_handle_send_command() - Handle send request/response from user space. + * + * @buf: command buffer. + * @buf_size: command buffer size. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_send_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + int ret = 0; + struct spcom_send_command *cmd = cmd_buf; + uint32_t buf_size; + void *buf; + struct spcom_msg_hdr *hdr; + void *tx_buf; + int tx_buf_size; + uint32_t timeout_msec; + int time_msec = 0; + + pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size); + + /* + * check that cmd buf size is at least struct size, + * to allow access to struct fields. + */ + if (size < sizeof(*cmd)) { + pr_err("ch [%s] invalid cmd buf.\n", + ch->name); + return -EINVAL; + } + + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + + /* parse command buffer */ + buf = &cmd->buf; + buf_size = cmd->buf_size; + timeout_msec = cmd->timeout_msec; + + /* Check param validity */ + if (buf_size > SPCOM_MAX_RESPONSE_SIZE) { + pr_err("ch [%s] invalid buf size [%d].\n", + ch->name, buf_size); + return -EINVAL; + } + if (size != sizeof(*cmd) + buf_size) { + pr_err("ch [%s] invalid cmd size [%d].\n", + ch->name, size); + return -EINVAL; + } + + /* Allocate Buffers*/ + tx_buf_size = sizeof(*hdr) + buf_size; + tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + /* Prepare Tx Buf */ + hdr = tx_buf; + + mutex_lock(&ch->lock); + /* Header */ + hdr->txn_id = ch->txn_id; + if (!ch->is_server) { + ch->txn_id++; /* client sets the request txn_id */ + ch->response_timeout_msec = timeout_msec; + } + + /* user buf */ + memcpy(hdr->buf, buf, buf_size); + + time_msec = 0; + do { + if (ch->rpmsg_abort) { + pr_err("ch [%s] aborted\n", ch->name); + ret = -ECANCELED; + break; + } + /* may fail when RX intent not queued by SP */ + ret = rpmsg_trysend(ch->rpdev->ept, tx_buf, tx_buf_size); + time_msec += TX_RETRY_DELAY_MSEC; + mutex_unlock(&ch->lock); + msleep(TX_RETRY_DELAY_MSEC); + mutex_lock(&ch->lock); + } while (ret == -EAGAIN && time_msec < timeout_msec); + mutex_unlock(&ch->lock); + + kfree(tx_buf); + return ret; +} + +/** + * modify_ion_addr() - replace the ION buffer virtual address with physical + * address in a request or response buffer. + * + * @buf: buffer to modify + * @buf_size: buffer size + * @ion_info: ION buffer info such as FD and offset in buffer. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int modify_ion_addr(void *buf, + uint32_t buf_size, + struct spcom_ion_info ion_info) +{ + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *sg = NULL; + dma_addr_t phy_addr = 0; + int fd, ret = 0; + uint32_t buf_offset; + char *ptr = (char *)buf; + + fd = ion_info.fd; + buf_offset = ion_info.buf_offset; + ptr += buf_offset; + + if (fd < 0) { + pr_err("invalid fd [%d].\n", fd); + return -ENODEV; + } + + if (buf_size < sizeof(uint64_t)) { + pr_err("buf size too small [%d].\n", buf_size); + return -ENODEV; + } + + if (buf_offset % sizeof(uint64_t)) + pr_debug("offset [%d] is NOT 64-bit aligned.\n", buf_offset); + else + pr_debug("offset [%d] is 64-bit aligned.\n", buf_offset); + + if (buf_offset > buf_size - sizeof(uint64_t)) { + pr_err("invalid buf_offset [%d].\n", buf_offset); + return -ENODEV; + } + + dma_buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dma_buf)) { + pr_err("fail to get dma buf handle.\n"); + return -EINVAL; + } + pr_debug("dma_buf handle ok.\n"); + attach = dma_buf_attach(dma_buf, &spcom_dev->pdev->dev); + if (IS_ERR_OR_NULL(attach)) { + ret = PTR_ERR(attach); + pr_err("fail to attach dma buf %d\n", ret); + dma_buf_put(dma_buf); + goto mem_map_table_failed; + } + + sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sg)) { + ret = PTR_ERR(sg); + pr_err("fail to get sg table of dma buf %d\n", ret); + goto mem_map_table_failed; + } + if (sg->sgl) { + phy_addr = sg->sgl->dma_address; + } else { + pr_err("sgl is NULL\n"); + ret = -ENOMEM; + goto mem_map_sg_failed; + } + + /* Set the physical address at the buffer offset */ + pr_debug("ion phys addr = [0x%lx].\n", (long int) phy_addr); + memcpy(ptr, &phy_addr, sizeof(phy_addr)); + +mem_map_sg_failed: + dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); +mem_map_table_failed: + dma_buf_detach(dma_buf, attach); + dma_buf_put(dma_buf); + + return ret; +} + +/** + * spcom_handle_send_modified_command() - send a request/response with ION + * buffer address. Modify the request/response by replacing the ION buffer + * virtual address with the physical address. + * + * @ch: channel pointer + * @cmd_buf: User space command buffer + * @size: size of user command buffer + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_send_modified_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + int ret = 0; + struct spcom_user_send_modified_command *cmd = cmd_buf; + uint32_t buf_size; + void *buf; + struct spcom_msg_hdr *hdr; + void *tx_buf; + int tx_buf_size; + struct spcom_ion_info ion_info[SPCOM_MAX_ION_BUF_PER_CMD]; + int i; + uint32_t timeout_msec; + int time_msec = 0; + + pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size); + + /* + * check that cmd buf size is at least struct size, + * to allow access to struct fields. + */ + if (size < sizeof(*cmd)) { + pr_err("ch [%s] invalid cmd buf.\n", + ch->name); + return -EINVAL; + } + + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + + /* parse command buffer */ + buf = &cmd->buf; + buf_size = cmd->buf_size; + timeout_msec = cmd->timeout_msec; + memcpy(ion_info, cmd->ion_info, sizeof(ion_info)); + + /* Check param validity */ + if (buf_size > SPCOM_MAX_RESPONSE_SIZE) { + pr_err("ch [%s] invalid buf size [%d].\n", + ch->name, buf_size); + return -EINVAL; + } + if (size != sizeof(*cmd) + buf_size) { + pr_err("ch [%s] invalid cmd size [%d].\n", + ch->name, size); + return -EINVAL; + } + + /* Allocate Buffers*/ + tx_buf_size = sizeof(*hdr) + buf_size; + tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + /* Prepare Tx Buf */ + hdr = tx_buf; + + mutex_lock(&ch->lock); + /* Header */ + hdr->txn_id = ch->txn_id; + if (!ch->is_server) { + ch->txn_id++; /* client sets the request txn_id */ + ch->response_timeout_msec = timeout_msec; + } + + /* user buf */ + memcpy(hdr->buf, buf, buf_size); + + for (i = 0 ; i < ARRAY_SIZE(ion_info) ; i++) { + if (ion_info[i].fd >= 0) { + ret = modify_ion_addr(hdr->buf, buf_size, ion_info[i]); + if (ret < 0) { + mutex_unlock(&ch->lock); + pr_err("modify_ion_addr() error [%d].\n", ret); + memset(tx_buf, 0, tx_buf_size); + kfree(tx_buf); + return -EFAULT; + } + } + } + + time_msec = 0; + do { + if (ch->rpmsg_abort) { + pr_err("ch [%s] aborted\n", ch->name); + ret = -ECANCELED; + break; + } + /* may fail when RX intent not queued by SP */ + ret = rpmsg_trysend(ch->rpdev->ept, tx_buf, tx_buf_size); + time_msec += TX_RETRY_DELAY_MSEC; + mutex_unlock(&ch->lock); + msleep(TX_RETRY_DELAY_MSEC); + mutex_lock(&ch->lock); + } while (ret == -EAGAIN && time_msec < timeout_msec); + mutex_unlock(&ch->lock); + memset(tx_buf, 0, tx_buf_size); + kfree(tx_buf); + return ret; +} + + +/** + * spcom_handle_lock_ion_buf_command() - Lock an shared buffer. + * + * Lock an shared buffer, prevent it from being free if the userspace App crash, + * while it is used by the remote subsystem. + */ +static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + struct spcom_user_command *cmd = cmd_buf; + int fd; + int i; + struct dma_buf *dma_buf; + + if (size != sizeof(*cmd)) { + pr_err("cmd size [%d] , expected [%d].\n", + (int) size, (int) sizeof(*cmd)); + return -EINVAL; + } + + if (cmd->arg > (unsigned int)INT_MAX) { + pr_err("int overflow [%ld]\n", cmd->arg); + return -EINVAL; + } + fd = cmd->arg; + + dma_buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dma_buf)) { + pr_err("fail to get dma buf handle.\n"); + return -EINVAL; + } + pr_debug("dma_buf referenced ok.\n"); + + /* shared buf lock doesn't involve any rx/tx data to SP. */ + mutex_lock(&ch->lock); + + /* Check if this shared buffer is already locked */ + for (i = 0 ; i < ARRAY_SIZE(ch->dmabuf_handle_table) ; i++) { + if (ch->dmabuf_handle_table[i] == dma_buf) { + pr_err("fd [%d] shared buf is already locked.\n", fd); + /* decrement back the ref count */ + mutex_unlock(&ch->lock); + dma_buf_put(dma_buf); + return -EINVAL; + } + } + + /* Store the dma_buf handle */ + for (i = 0 ; i < ARRAY_SIZE(ch->dmabuf_handle_table) ; i++) { + if (ch->dmabuf_handle_table[i] == NULL) { + ch->dmabuf_handle_table[i] = dma_buf; + ch->dmabuf_fd_table[i] = fd; + pr_debug("ch [%s] locked ion buf #%d, fd [%d].\n", + ch->name, i, fd); + mutex_unlock(&ch->lock); + return 0; + } + } + + mutex_unlock(&ch->lock); + /* decrement back the ref count */ + dma_buf_put(dma_buf); + pr_err("no free entry to store ion handle of fd [%d].\n", fd); + + return -EFAULT; +} + +/** + * spcom_handle_unlock_ion_buf_command() - Unlock an ION buffer. + * + * Unlock an ION buffer, let it be free, when it is no longer being used by + * the remote subsystem. + */ +static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch, + void *cmd_buf, int size) +{ + int i; + struct spcom_user_command *cmd = cmd_buf; + int fd; + bool found = false; + + if (size != sizeof(*cmd)) { + pr_err("cmd size [%d], expected [%d]\n", + (int)size, (int)sizeof(*cmd)); + return -EINVAL; + } + if (cmd->arg > (unsigned int)INT_MAX) { + pr_err("int overflow [%ld]\n", cmd->arg); + return -EINVAL; + } + fd = cmd->arg; + + pr_debug("Unlock ion buf ch [%s] fd [%d].\n", ch->name, fd); + /* shared buf unlock doesn't involve any rx/tx data to SP. */ + mutex_lock(&ch->lock); + if (fd == (int) SPCOM_ION_FD_UNLOCK_ALL) { + pr_debug("unlocked ALL ion buf ch [%s].\n", ch->name); + found = true; + /* unlock all buf */ + for (i = 0; i < ARRAY_SIZE(ch->dmabuf_handle_table); i++) { + if (ch->dmabuf_handle_table[i] != NULL) { + pr_debug("unlocked ion buf #%d fd [%d].\n", + i, ch->dmabuf_fd_table[i]); + dma_buf_put(ch->dmabuf_handle_table[i]); + ch->dmabuf_handle_table[i] = NULL; + ch->dmabuf_fd_table[i] = -1; + } + } + } else { + /* unlock specific buf */ + for (i = 0 ; i < ARRAY_SIZE(ch->dmabuf_handle_table) ; i++) { + if (!ch->dmabuf_handle_table[i]) + continue; + if (ch->dmabuf_fd_table[i] == fd) { + pr_debug("unlocked ion buf #%d fd [%d].\n", + i, ch->dmabuf_fd_table[i]); + dma_buf_put(ch->dmabuf_handle_table[i]); + ch->dmabuf_handle_table[i] = NULL; + ch->dmabuf_fd_table[i] = -1; + found = true; + break; + } + } + } + mutex_unlock(&ch->lock); + + if (!found) { + pr_err("ch [%s] fd [%d] was not found.\n", ch->name, fd); + return -ENODEV; + } + + return 0; +} + +/** + * spcom_handle_write() - Handle user space write commands. + * + * @buf: command buffer. + * @buf_size: command buffer size. + * + * Return: 0 on successful operation, negative value otherwise. + */ +static int spcom_handle_write(struct spcom_channel *ch, + void *buf, + int buf_size) +{ + int ret = 0; + struct spcom_user_command *cmd = NULL; + int cmd_id = 0; + + /* Minimal command should have command-id and argument */ + if (buf_size < sizeof(struct spcom_user_command)) { + pr_err("Command buffer size [%d] too small\n", buf_size); + return -EINVAL; + } + + cmd = (struct spcom_user_command *)buf; + cmd_id = (int) cmd->cmd_id; + + pr_debug("cmd_id [0x%x]\n", cmd_id); + + if (!ch && cmd_id != SPCOM_CMD_CREATE_CHANNEL + && cmd_id != SPCOM_CMD_RESTART_SP) { + pr_err("channel context is null\n"); + return -EINVAL; + } + + switch (cmd_id) { + case SPCOM_CMD_SEND: + ret = spcom_handle_send_command(ch, buf, buf_size); + break; + case SPCOM_CMD_SEND_MODIFIED: + ret = spcom_handle_send_modified_command(ch, buf, buf_size); + break; + case SPCOM_CMD_LOCK_ION_BUF: + ret = spcom_handle_lock_ion_buf_command(ch, buf, buf_size); + break; + case SPCOM_CMD_UNLOCK_ION_BUF: + ret = spcom_handle_unlock_ion_buf_command(ch, buf, buf_size); + break; + case SPCOM_CMD_CREATE_CHANNEL: + ret = spcom_handle_create_channel_command(buf, buf_size); + break; + case SPCOM_CMD_RESTART_SP: + ret = spcom_handle_restart_sp_command(); + break; + default: + pr_err("Invalid Command Id [0x%x].\n", (int) cmd->cmd_id); + ret = -EINVAL; + } + + return ret; +} + +/** + * spcom_handle_get_req_size() - Handle user space get request size command + * + * @ch: channel handle + * @buf: command buffer. + * @size: command buffer size. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_handle_get_req_size(struct spcom_channel *ch, + void *buf, + uint32_t size) +{ + int ret = -1; + uint32_t next_req_size = 0; + + if (size < sizeof(next_req_size)) { + pr_err("buf size [%d] too small.\n", (int) size); + return -EINVAL; + } + + ret = spcom_get_next_request_size(ch); + if (ret < 0) + return ret; + next_req_size = (uint32_t) ret; + + memcpy(buf, &next_req_size, sizeof(next_req_size)); + pr_debug("next_req_size [%d].\n", next_req_size); + + return sizeof(next_req_size); /* can't exceed user buffer size */ +} + +/** + * spcom_handle_read_req_resp() - Handle user space get request/response command + * + * @ch: channel handle + * @buf: command buffer. + * @size: command buffer size. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_handle_read_req_resp(struct spcom_channel *ch, + void *buf, + uint32_t size) +{ + int ret; + struct spcom_msg_hdr *hdr; + void *rx_buf; + int rx_buf_size; + uint32_t timeout_msec = 0; /* client only */ + + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + + /* Check param validity */ + if (size > SPCOM_MAX_RESPONSE_SIZE) { + pr_err("ch [%s] invalid size [%d].\n", + ch->name, size); + return -EINVAL; + } + + /* Allocate Buffers*/ + rx_buf_size = sizeof(*hdr) + size; + rx_buf = kzalloc(rx_buf_size, GFP_KERNEL); + if (!rx_buf) + return -ENOMEM; + + /* + * client response timeout depends on the request + * handling time on the remote side . + */ + if (!ch->is_server) { + timeout_msec = ch->response_timeout_msec; + pr_debug("response_timeout_msec = %d.\n", (int) timeout_msec); + } + + ret = spcom_rx(ch, rx_buf, rx_buf_size, timeout_msec); + if (ret < 0) { + pr_err("rx error %d.\n", ret); + goto exit_err; + } else { + size = ret; /* actual_rx_size */ + } + + hdr = rx_buf; + + if (ch->is_server) { + ch->txn_id = hdr->txn_id; + pr_debug("request txn_id [0x%x].\n", ch->txn_id); + } + + /* copy data to user without the header */ + if (size > sizeof(*hdr)) { + size -= sizeof(*hdr); + memcpy(buf, hdr->buf, size); + } else { + pr_err("rx size [%d] too small.\n", size); + ret = -EFAULT; + goto exit_err; + } + + kfree(rx_buf); + return size; +exit_err: + kfree(rx_buf); + return ret; +} + +/** + * spcom_handle_read() - Handle user space read request/response or + * request-size command + * + * @ch: channel handle + * @buf: command buffer. + * @size: command buffer size. + * + * A special size SPCOM_GET_NEXT_REQUEST_SIZE, which is bigger than the max + * response/request tells the kernel that user space only need the size. + * + * Return: size in bytes on success, negative value on failure. + */ +static int spcom_handle_read(struct spcom_channel *ch, + void *buf, + uint32_t size) +{ + int ret = -1; + + if (size == SPCOM_GET_NEXT_REQUEST_SIZE) { + pr_debug("get next request size, ch [%s].\n", ch->name); + ch->is_server = true; + ret = spcom_handle_get_req_size(ch, buf, size); + } else { + pr_debug("get request/response, ch [%s].\n", ch->name); + ret = spcom_handle_read_req_resp(ch, buf, size); + } + + pr_debug("ch [%s] , size = %d.\n", ch->name, size); + + return ret; +} + +/*======================================================================*/ +/* CHAR DEVICE USER SPACE INTERFACE */ +/*======================================================================*/ + +/** + * file_to_filename() - get the filename from file pointer. + * + * @filp: file pointer + * + * it is used for debug prints. + * + * Return: filename string or "unknown". + */ +static char *file_to_filename(struct file *filp) +{ + struct dentry *dentry = NULL; + char *filename = NULL; + + if (!filp || !filp->f_path.dentry) + return "unknown"; + + dentry = filp->f_path.dentry; + filename = dentry->d_iname; + + return filename; +} + +/** + * spcom_device_open() - handle channel file open() from user space. + * + * @filp: file pointer + * + * The file name (without path) is the channel name. + * Register rpmsg driver matching with channel name. + * Store the channel context in the file private date pointer for future + * read/write/close operations. + */ +static int spcom_device_open(struct inode *inode, struct file *filp) +{ + struct spcom_channel *ch; + int ret; + const char *name = file_to_filename(filp); + u32 pid = current_pid(); + + pr_debug("open file [%s].\n", name); + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (strcmp(name, DEVICE_NAME) == 0) { + pr_debug("root dir skipped.\n"); + return 0; + } + + if (strcmp(name, "sp_ssr") == 0) { + pr_debug("sp_ssr dev node skipped.\n"); + return 0; + } + + ch = spcom_find_channel_by_name(name); + if (!ch) { + pr_err("channel %s doesn't exist, load App first.\n", name); + return -ENODEV; + } + + mutex_lock(&ch->lock); + if (!spcom_is_channel_open(ch)) { + reinit_completion(&ch->connect); + /* channel was closed need to register drv again */ + ret = spcom_register_rpmsg_drv(ch); + if (ret < 0) { + pr_err("register rpmsg driver failed %d\n", ret); + mutex_unlock(&ch->lock); + return ret; + } + } + /* only one client/server may use the channel */ + if (ch->is_busy) { + pr_err("channel [%s] is BUSY, already in use by pid [%d].\n", + name, ch->pid); + mutex_unlock(&ch->lock); + return -EBUSY; + } + + ch->is_busy = true; + ch->pid = pid; + ch->txn_id = INITIAL_TXN_ID; + mutex_unlock(&ch->lock); + + filp->private_data = ch; + return 0; +} + +/** + * spcom_device_release() - handle channel file close() from user space. + * + * @filp: file pointer + * + * The file name (without path) is the channel name. + * Open the relevant glink channel. + * Store the channel context in the file private + * date pointer for future read/write/close + * operations. + */ +static int spcom_device_release(struct inode *inode, struct file *filp) +{ + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + int ret = 0; + + pr_err("close file [%s].\n", name); + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (strcmp(name, DEVICE_NAME) == 0) { + pr_debug("root dir skipped.\n"); + return 0; + } + + if (strcmp(name, "sp_ssr") == 0) { + pr_debug("sp_ssr dev node skipped.\n"); + return 0; + } + + ch = filp->private_data; + if (!ch) { + pr_debug("ch is NULL, file name %s.\n", file_to_filename(filp)); + return -ENODEV; + } + + mutex_lock(&ch->lock); + /* channel might be already closed or disconnected */ + if (!spcom_is_channel_open(ch)) { + pr_debug("ch [%s] already closed.\n", name); + mutex_unlock(&ch->lock); + return 0; + } + + ch->is_busy = false; + ch->pid = 0; + ch->txn_id = INITIAL_TXN_ID; /* use non-zero nonce for debug */ + mutex_unlock(&ch->lock); + filp->private_data = NULL; + + return ret; +} + +/** + * spcom_device_write() - handle channel file write() from user space. + * + * @filp: file pointer + * + * Return: On Success - same size as number of bytes to write. + * On Failure - negative value. + */ +static ssize_t spcom_device_write(struct file *filp, + const char __user *user_buff, + size_t size, loff_t *f_pos) +{ + int ret; + char *buf; + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + int buf_size = 0; + + if (!user_buff || !f_pos || !filp) { + pr_err("invalid null parameters.\n"); + return -EINVAL; + } + + if (*f_pos != 0) { + pr_err("offset should be zero, no sparse buffer.\n"); + return -EINVAL; + } + + if (!name) { + pr_err("name is NULL\n"); + return -EINVAL; + } + pr_debug("write file [%s] size [%d] pos [%d].\n", + name, (int) size, (int) *f_pos); + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + ch = filp->private_data; + if (!ch) { + if (strcmp(name, DEVICE_NAME) != 0) { + pr_err("invalid ch pointer, command not allowed.\n"); + return -EINVAL; + } + pr_debug("control device - no channel context.\n"); + } else { + /* Check if remote side connect */ + if (!spcom_is_channel_connected(ch)) { + pr_err("ch [%s] remote side not connect.\n", ch->name); + return -ENOTCONN; + } + } + + if (size > SPCOM_MAX_COMMAND_SIZE) { + pr_err("size [%d] > max size [%d].\n", + (int) size, (int) SPCOM_MAX_COMMAND_SIZE); + return -EINVAL; + } + buf_size = size; /* explicit casting size_t to int */ + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + ret = copy_from_user(buf, user_buff, size); + if (ret) { + pr_err("Unable to copy from user (err %d).\n", ret); + kfree(buf); + return -EFAULT; + } + + ret = spcom_handle_write(ch, buf, buf_size); + if (ret) { + pr_err("handle command error [%d].\n", ret); + kfree(buf); + return -EFAULT; + } + + kfree(buf); + + return size; +} + +/** + * spcom_device_read() - handle channel file read() from user space. + * + * @filp: file pointer + * + * Return: number of bytes to read on success, negative value on + * failure. + */ +static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, + size_t size, loff_t *f_pos) +{ + int ret = 0; + int actual_size = 0; + char *buf; + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + uint32_t buf_size = 0; + + pr_debug("read file [%s], size = %d bytes.\n", name, (int) size); + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (!user_buff || !f_pos || + (size == 0) || (size > SPCOM_MAX_READ_SIZE)) { + pr_err("invalid parameters.\n"); + return -EINVAL; + } + buf_size = size; /* explicit casting size_t to uint32_t */ + + ch = filp->private_data; + + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } + + if (!spcom_is_channel_open(ch)) { + pr_err("ch is not open, file [%s].\n", name); + return -EINVAL; + } + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + ret = spcom_handle_read(ch, buf, buf_size); + if (ret < 0) { + pr_err("read error [%d].\n", ret); + kfree(buf); + return ret; + } + actual_size = ret; + if ((actual_size == 0) || (actual_size > size)) { + pr_err("invalid actual_size [%d].\n", actual_size); + kfree(buf); + return -EFAULT; + } + + ret = copy_to_user(user_buff, buf, actual_size); + if (ret) { + pr_err("Unable to copy to user, err = %d.\n", ret); + kfree(buf); + return -EFAULT; + } + + kfree(buf); + pr_debug("ch [%s] ret [%d].\n", name, (int) actual_size); + + return actual_size; +} + +/** + * spcom_device_poll() - handle channel file poll() from user space. + * + * @filp: file pointer + * + * This allows user space to wait/check for channel connection, + * or wait for SSR event. + * + * Return: event bitmask on success, set POLLERR on failure. + */ +static unsigned int spcom_device_poll(struct file *filp, + struct poll_table_struct *poll_table) +{ + /* + * when user call with timeout -1 for blocking mode, + * any bit must be set in response + */ + unsigned int ret = SPCOM_POLL_READY_FLAG; + unsigned long mask; + struct spcom_channel *ch; + const char *name = file_to_filename(filp); + bool wait = false; + bool done = false; + /* Event types always implicitly polled for */ + unsigned long reserved = POLLERR | POLLHUP | POLLNVAL; + int ready = 0; + + if (strcmp(name, "unknown") == 0) { + pr_err("name is unknown\n"); + return -EINVAL; + } + + if (!poll_table) { + pr_err("invalid parameters.\n"); + return -EINVAL; + } + + ch = filp->private_data; + mask = poll_requested_events(poll_table); + + pr_debug("== ch [%s] mask [0x%x] ==.\n", name, (int) mask); + + /* user space API has poll use "short" and not "long" */ + mask &= 0x0000FFFF; + + wait = mask & SPCOM_POLL_WAIT_FLAG; + if (wait) + pr_debug("ch [%s] wait for event flag is ON.\n", name); + + // mask will be used in output, clean input bits + mask &= (unsigned long)~SPCOM_POLL_WAIT_FLAG; + mask &= (unsigned long)~SPCOM_POLL_READY_FLAG; + mask &= (unsigned long)~reserved; + + switch (mask) { + case SPCOM_POLL_LINK_STATE: + pr_debug("ch [%s] SPCOM_POLL_LINK_STATE.\n", name); + if (wait) { + reinit_completion(&spcom_dev->rpmsg_state_change); + ready = wait_for_completion_interruptible( + &spcom_dev->rpmsg_state_change); + pr_debug("ch [%s] poll LINK_STATE signaled.\n", name); + } + done = atomic_read(&spcom_dev->rpmsg_dev_count) > 0; + break; + case SPCOM_POLL_CH_CONNECT: + /* + * ch is not expected to be NULL since user must call open() + * to get FD before it can call poll(). + * open() will fail if no ch related to the char-device. + */ + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return POLLERR; + } + pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name); + if (wait) { + reinit_completion(&ch->connect); + ready = wait_for_completion_interruptible(&ch->connect); + pr_debug("ch [%s] poll CH_CONNECT signaled.\n", name); + } + mutex_lock(&ch->lock); + done = completion_done(&ch->connect); + mutex_unlock(&ch->lock); + break; + default: + pr_err("ch [%s] poll, invalid mask [0x%x].\n", + name, (int) mask); + ret = POLLERR; + break; + } + + if (ready < 0) { /* wait was interrupted */ + pr_debug("ch [%s] poll interrupted, ret [%d].\n", name, ready); + ret = POLLERR | SPCOM_POLL_READY_FLAG | mask; + } + if (done) + ret |= mask; + + pr_debug("ch [%s] poll, mask = 0x%x, ret=0x%x.\n", + name, (int) mask, ret); + + return ret; +} + +/* file operation supported from user space */ +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = spcom_device_read, + .poll = spcom_device_poll, + .write = spcom_device_write, + .open = spcom_device_open, + .release = spcom_device_release, +}; + +/** + * spcom_create_channel_chardev() - Create a channel char-dev node file + * for user space interface + */ +static int spcom_create_channel_chardev(const char *name) +{ + int ret; + struct device *dev; + struct spcom_channel *ch; + dev_t devt; + struct class *cls = spcom_dev->driver_class; + struct device *parent = spcom_dev->class_dev; + void *priv; + struct cdev *cdev; + + pr_debug("Add channel [%s].\n", name); + + ch = spcom_find_channel_by_name(name); + if (ch) { + pr_err("channel [%s] already exist.\n", name); + return -EINVAL; + } + + ch = spcom_find_channel_by_name(""); /* find reserved channel */ + if (!ch) { + pr_err("no free channel.\n"); + return -ENODEV; + } + + ret = spcom_init_channel(ch, name); + if (ret < 0) { + pr_err("can't init channel %d\n", ret); + return ret; + } + + ret = spcom_register_rpmsg_drv(ch); + if (ret < 0) { + pr_err("register rpmsg driver failed %d\n", ret); + goto exit_destroy_channel; + } + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) { + ret = -ENOMEM; + goto exit_unregister_drv; + } + + devt = spcom_dev->device_no + atomic_read(&spcom_dev->chdev_count); + priv = ch; + dev = device_create(cls, parent, devt, priv, name); + if (IS_ERR(dev)) { + pr_err("device_create failed.\n"); + ret = -ENODEV; + goto exit_free_cdev; + } + + cdev_init(cdev, &fops); + cdev->owner = THIS_MODULE; + + ret = cdev_add(cdev, devt, 1); + if (ret < 0) { + pr_err("cdev_add failed %d\n", ret); + ret = -ENODEV; + goto exit_destroy_device; + } + atomic_inc(&spcom_dev->chdev_count); + mutex_lock(&ch->lock); + ch->cdev = cdev; + ch->dev = dev; + mutex_unlock(&ch->lock); + + return 0; + +exit_destroy_device: + device_destroy(spcom_dev->driver_class, devt); +exit_free_cdev: + kfree(cdev); +exit_unregister_drv: + ret = spcom_unregister_rpmsg_drv(ch); + if (ret != 0) + pr_err("can't unregister rpmsg drv\n", ret); +exit_destroy_channel: + // empty channel leaves free slot for next time + memset(ch->name, 0, SPCOM_CHANNEL_NAME_SIZE); + mutex_unlock(&ch->lock); + return -EFAULT; +} + +static int __init spcom_register_chardev(void) +{ + int ret; + unsigned int baseminor = 0; + unsigned int count = 1; + void *priv = spcom_dev; + + ret = alloc_chrdev_region(&spcom_dev->device_no, baseminor, count, + DEVICE_NAME); + if (ret < 0) { + pr_err("alloc_chrdev_region failed %d\n", ret); + return ret; + } + + spcom_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(spcom_dev->driver_class)) { + ret = -ENOMEM; + pr_err("class_create failed %d\n", ret); + goto exit_unreg_chrdev_region; + } + + spcom_dev->class_dev = device_create(spcom_dev->driver_class, NULL, + spcom_dev->device_no, priv, + DEVICE_NAME); + + if (IS_ERR(spcom_dev->class_dev)) { + pr_err("class_device_create failed %d\n", ret); + ret = -ENOMEM; + goto exit_destroy_class; + } + + cdev_init(&spcom_dev->cdev, &fops); + spcom_dev->cdev.owner = THIS_MODULE; + + ret = cdev_add(&spcom_dev->cdev, + MKDEV(MAJOR(spcom_dev->device_no), 0), + SPCOM_MAX_CHANNELS); + if (ret < 0) { + pr_err("cdev_add failed %d\n", ret); + goto exit_destroy_device; + } + + pr_debug("char device created.\n"); + + return 0; + +exit_destroy_device: + device_destroy(spcom_dev->driver_class, spcom_dev->device_no); +exit_destroy_class: + class_destroy(spcom_dev->driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(spcom_dev->device_no, 1); + return ret; +} + +static void spcom_unregister_chrdev(void) +{ + cdev_del(&spcom_dev->cdev); + device_destroy(spcom_dev->driver_class, spcom_dev->device_no); + class_destroy(spcom_dev->driver_class); + unregister_chrdev_region(spcom_dev->device_no, + atomic_read(&spcom_dev->chdev_count)); + +} + +static int spcom_parse_dt(struct device_node *np) +{ + int ret; + const char *propname = "qcom,spcom-ch-names"; + int num_ch; + int i; + const char *name; + + num_ch = of_property_count_strings(np, propname); + if (num_ch < 0) { + pr_err("wrong format of predefined channels definition [%d].\n", + num_ch); + return num_ch; + } + if (num_ch > ARRAY_SIZE(spcom_dev->predefined_ch_name)) { + pr_err("too many predefined channels [%d].\n", num_ch); + return -EINVAL; + } + + pr_debug("num of predefined channels [%d].\n", num_ch); + for (i = 0; i < num_ch; i++) { + ret = of_property_read_string_index(np, propname, i, &name); + if (ret) { + pr_err("failed to read DT channel [%d] name .\n", i); + return -EFAULT; + } + strlcpy(spcom_dev->predefined_ch_name[i], + name, + sizeof(spcom_dev->predefined_ch_name[i])); + + pr_debug("found ch [%s].\n", name); + } + + return num_ch; +} + +/* + * the function is running on system workqueue context, + * processes delayed (by rpmsg rx callback) packets: + * each paket belong to its destination spcom channel ch + */ +static void spcom_signal_rx_done(struct work_struct *ignored) +{ + struct spcom_channel *ch; + struct rx_buff_list *rx_item; + unsigned long flags; + + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + while (!list_empty(&spcom_dev->rx_list_head)) { + /* detach last entry */ + rx_item = list_last_entry(&spcom_dev->rx_list_head, + struct rx_buff_list, list); + list_del(&rx_item->list); + spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); + + if (!rx_item) { + pr_err("empty entry in pending rx list\n"); + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + continue; + } + ch = rx_item->ch; + mutex_lock(&ch->lock); + if (ch->rpmsg_abort) { + if (ch->rpmsg_rx_buf) { + pr_debug("ch [%s] rx aborted free %d bytes\n", + ch->actual_rx_size); + kfree(ch->rpmsg_rx_buf); + ch->actual_rx_size = 0; + } + goto rx_aborted; + } + if (ch->rpmsg_rx_buf) { + pr_err("ch [%s] previous buffer not consumed %d bytes\n", + ch->name, ch->actual_rx_size); + goto rx_aborted; + } + ch->rpmsg_rx_buf = rx_item->rpmsg_rx_buf; + ch->actual_rx_size = rx_item->rx_buf_size; + complete_all(&ch->rx_done); + mutex_unlock(&ch->lock); + + kfree(rx_item); + + /* lock for the next list entry */ + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + } + spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); + return; +rx_aborted: + mutex_unlock(&ch->lock); + kfree(rx_item->rpmsg_rx_buf); + kfree(rx_item); +} + +static int spcom_rpdev_cb(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 src) +{ + struct spcom_channel *ch; + static DECLARE_WORK(rpmsg_rx_consumer, spcom_signal_rx_done); + struct rx_buff_list *rx_item; + unsigned long flags; + + if (!rpdev || !data) { + pr_err("rpdev or data is NULL\n"); + return -EINVAL; + } + pr_debug("incoming msg from %s\n", rpdev->id.name); + ch = dev_get_drvdata(&rpdev->dev); + if (!ch) { + pr_err("%s: invalid ch\n"); + return -EINVAL; + } + if (len > SPCOM_RX_BUF_SIZE || len <= 0) { + pr_err("got msg size %d, max allowed %d\n", + len, SPCOM_RX_BUF_SIZE); + return -EINVAL; + } + + rx_item = kzalloc(sizeof(*rx_item), GFP_ATOMIC); + if (!rx_item) + return -ENOMEM; + + rx_item->rpmsg_rx_buf = kzalloc(len, GFP_ATOMIC); + if (!rx_item->rpmsg_rx_buf) { + kfree(rx_item); + return -ENOMEM; + } + memcpy(rx_item->rpmsg_rx_buf, data, len); + rx_item->rx_buf_size = len; + rx_item->ch = ch; + + spin_lock_irqsave(&spcom_dev->rx_lock, flags); + list_add(&rx_item->list, &spcom_dev->rx_list_head); + spin_unlock_irqrestore(&spcom_dev->rx_lock, flags); + pr_err("signaling rx item for %s, received %d bytes\n", + rpdev->id.name, len); + + schedule_work(&rpmsg_rx_consumer); + return 0; +} + +static int spcom_rpdev_probe(struct rpmsg_device *rpdev) +{ + const char *name; + struct spcom_channel *ch; + + if (!rpdev) { + pr_err("rpdev is NULL\n"); + return -EINVAL; + } + name = rpdev->id.name; + pr_debug("new channel %s rpmsg_device arrived\n", name); + ch = spcom_find_channel_by_name(name); + if (!ch) { + pr_err("channel %s not found\n", name); + return -ENODEV; + } + mutex_lock(&ch->lock); + ch->rpdev = rpdev; + ch->rpmsg_abort = false; + complete_all(&ch->connect); + mutex_unlock(&ch->lock); + + dev_set_drvdata(&rpdev->dev, ch); + + /* used to evaluate underlying transport link up/down */ + atomic_inc(&spcom_dev->rpmsg_dev_count); + if (atomic_read(&spcom_dev->rpmsg_dev_count) == 1) + complete_all(&spcom_dev->rpmsg_state_change); + + return 0; +} + +static void spcom_rpdev_remove(struct rpmsg_device *rpdev) +{ + struct spcom_channel *ch; + + if (!rpdev) { + pr_err("rpdev is NULL\n"); + return; + } + + ch = dev_get_drvdata(&rpdev->dev); + if (!ch) { + pr_err("channel %s not found\n", rpdev->id.name); + return; + } + mutex_lock(&ch->lock); + ch->rpdev = NULL; + ch->rpmsg_abort = true; + complete_all(&ch->rx_done); + mutex_unlock(&ch->lock); + + /* used to evaluate underlying transport link up/down */ + if (atomic_dec_and_test(&spcom_dev->rpmsg_dev_count)) + complete_all(&spcom_dev->rpmsg_state_change); + + dev_info(&rpdev->dev, "rpmsg device %s removed\n", rpdev->id.name); +} + +/* register rpmsg driver to match with channel ch_name */ +static int spcom_register_rpmsg_drv(struct spcom_channel *ch) +{ + struct rpmsg_driver *rpdrv; + struct rpmsg_device_id *match; + char *drv_name; + int ret; + + if (ch->rpdrv) { + pr_err("ch:%s, rpmsg driver %s already registered\n", ch->name, + ch->rpdrv->id_table->name); + return -ENODEV; + } + + rpdrv = kzalloc(sizeof(*rpdrv), GFP_KERNEL); + if (!rpdrv) + return -ENOMEM; + + /* zalloc array of two to NULL terminate the match list */ + match = kzalloc(2 * sizeof(*match), GFP_KERNEL); + if (!match) { + kfree(rpdrv); + return -ENOMEM; + } + snprintf(match->name, RPMSG_NAME_SIZE, "%s", ch->name); + + drv_name = kasprintf(GFP_KERNEL, "%s_%s", "spcom_rpmsg_drv", ch->name); + if (!drv_name) { + pr_err("can't allocate drv_name for %s\n", ch->name); + kfree(rpdrv); + kfree(match); + return -ENOMEM; + } + + rpdrv->probe = spcom_rpdev_probe; + rpdrv->remove = spcom_rpdev_remove; + rpdrv->callback = spcom_rpdev_cb; + rpdrv->id_table = match; + rpdrv->drv.name = drv_name; + ret = register_rpmsg_driver(rpdrv); + if (ret) { + pr_err("can't register rpmsg_driver for %s\n", ch->name); + kfree(rpdrv); + kfree(match); + kfree(drv_name); + return ret; + } + mutex_lock(&ch->lock); + ch->rpdrv = rpdrv; + ch->rpmsg_abort = false; + mutex_unlock(&ch->lock); + + return 0; +} + +static int spcom_unregister_rpmsg_drv(struct spcom_channel *ch) +{ + if (!ch->rpdrv) + return -ENODEV; + unregister_rpmsg_driver(ch->rpdrv); + + mutex_lock(&ch->lock); + kfree(ch->rpdrv->drv.name); + kfree((void *)ch->rpdrv->id_table); + kfree(ch->rpdrv); + ch->rpdrv = NULL; + ch->rpmsg_abort = true; /* will unblock spcom_rx() */ + mutex_unlock(&ch->lock); + return 0; +} + +static int spcom_probe(struct platform_device *pdev) +{ + int ret; + struct spcom_device *dev = NULL; + struct device_node *np; + + if (!pdev) { + pr_err("invalid pdev.\n"); + return -ENODEV; + } + + np = pdev->dev.of_node; + if (!np) { + pr_err("invalid DT node.\n"); + return -EINVAL; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + + spcom_dev = dev; + spcom_dev->pdev = pdev; + /* start counting exposed channel char devices from 1 */ + atomic_set(&spcom_dev->chdev_count, 1); + init_completion(&spcom_dev->rpmsg_state_change); + atomic_set(&spcom_dev->rpmsg_dev_count, 0); + + INIT_LIST_HEAD(&spcom_dev->rx_list_head); + spin_lock_init(&spcom_dev->rx_lock); + + ret = spcom_register_chardev(); + if (ret) { + pr_err("create character device failed.\n"); + goto fail_reg_chardev; + } + + ret = spcom_parse_dt(np); + if (ret < 0) + goto fail_reg_chardev; + + ret = spcom_create_predefined_channels_chardev(); + if (ret < 0) { + pr_err("create character device failed.\n"); + goto fail_reg_chardev; + } + pr_debug("Driver Initialization ok.\n"); + return 0; + +fail_reg_chardev: + pr_err("failed to init driver\n"); + spcom_unregister_chrdev(); + kfree(dev); + spcom_dev = NULL; + + return -ENODEV; +} + +static const struct of_device_id spcom_match_table[] = { + { .compatible = "qcom,spcom", }, + { }, +}; + +static struct platform_driver spcom_driver = { + .probe = spcom_probe, + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spcom_match_table), + }, +}; + +static int __init spcom_init(void) +{ + int ret; + + pr_info("spcom driver version 2.1 23-April-2018.\n"); + + ret = platform_driver_register(&spcom_driver); + if (ret) + pr_err("spcom_driver register failed %d\n", ret); + + return ret; +} +module_init(spcom_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Secure Processor Communication"); diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index 3330d772c684c308073f042abf589b641e84eb6e..2d5c8526bb3d2ae434a2a71153a9018144efa924 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2018, 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 @@ -707,14 +707,14 @@ static int subsystem_powerup(struct subsys_device *dev, void *data) return 0; } -static int __find_subsys(struct device *dev, void *data) +static int __find_subsys_device(struct device *dev, void *data) { struct subsys_device *subsys = to_subsys(dev); return !strcmp(subsys->desc->name, data); } -static struct subsys_device *find_subsys(const char *str) +struct subsys_device *find_subsys_device(const char *str) { struct device *dev; @@ -722,9 +722,10 @@ static struct subsys_device *find_subsys(const char *str) return NULL; dev = bus_find_device(&subsys_bus_type, NULL, (void *)str, - __find_subsys); + __find_subsys_device); return dev ? to_subsys(dev) : NULL; } +EXPORT_SYMBOL(find_subsys_device); static int subsys_start(struct subsys_device *subsys) { @@ -796,7 +797,7 @@ int subsystem_set_fwname(const char *name, const char *fw_name) if (!fw_name) return -EINVAL; - subsys = find_subsys(name); + subsys = find_subsys_device(name); if (!subsys) return -EINVAL; @@ -813,10 +814,10 @@ int wait_for_shutdown_ack(struct subsys_desc *desc) int ret; struct subsys_device *dev; - if (!desc || !desc->shutdown_ack_irq) + if (!desc) return 0; - dev = find_subsys(desc->name); + dev = find_subsys_device(desc->name); if (!dev) return 0; @@ -842,7 +843,7 @@ void *__subsystem_get(const char *name, const char *fw_name) if (!name) return NULL; - subsys = retval = find_subsys(name); + subsys = retval = find_subsys_device(name); if (!subsys) return ERR_PTR(-ENODEV); if (!try_module_get(subsys->owner)) { @@ -942,7 +943,7 @@ void subsystem_put(void *subsystem) } mutex_unlock(&track->lock); - subsys_d = find_subsys(subsys->desc->depends_on); + subsys_d = find_subsys_device(subsys->desc->depends_on); if (subsys_d) { subsystem_put(subsys_d); put_device(&subsys_d->dev); @@ -1156,7 +1157,7 @@ EXPORT_SYMBOL(subsystem_restart_dev); int subsystem_restart(const char *name) { int ret; - struct subsys_device *dev = find_subsys(name); + struct subsys_device *dev = find_subsys_device(name); if (!dev) return -ENODEV; @@ -1169,7 +1170,7 @@ EXPORT_SYMBOL(subsystem_restart); int subsystem_crashed(const char *name) { - struct subsys_device *dev = find_subsys(name); + struct subsys_device *dev = find_subsys_device(name); struct subsys_tracking *track; if (!dev) diff --git a/drivers/soc/qcom/sysmon-qmi.c b/drivers/soc/qcom/sysmon-qmi.c index 4462e92e90a277a5cd49fff33dd34d6b16a08747..cb603b4d053c1fbf5b6698a404690fc137ce65f6 100644 --- a/drivers/soc/qcom/sysmon-qmi.c +++ b/drivers/soc/qcom/sysmon-qmi.c @@ -71,7 +71,6 @@ struct sysmon_qmi_data { struct notifier_block notifier; void *notif_handle; bool legacy_version; - struct completion ind_recv; struct sockaddr_qrtr ssctl; struct list_head list; }; @@ -99,8 +98,13 @@ static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, struct sysmon_qmi_data *qmi_data = container_of(qmi, struct sysmon_qmi_data, clnt_handle); + struct subsys_device *subsys_dev = find_subsys_device(qmi_data->name); pr_info("%s: Indication received from subsystem\n", qmi_data->name); - complete(&qmi_data->ind_recv); + if (subsys_dev) + complete_shutdown_ack(subsys_dev); + else + pr_err("Failed to find subsystem: %s for indication\n", + qmi_data->name); } static struct qmi_msg_handler qmi_indication_handler[] = { @@ -400,8 +404,6 @@ int sysmon_send_shutdown(struct subsys_desc *dest_desc) if (!data->connected) return -EAGAIN; - reinit_completion(&data->ind_recv); - ret = qmi_txn_init(&data->clnt_handle, &txn, qmi_ssctl_shutdown_resp_msg_ei, &resp); @@ -428,11 +430,11 @@ int sysmon_send_shutdown(struct subsys_desc *dest_desc) if (ret < 0) { pr_err("SYSMON QMI txn wait failed to dest %s, ret - %d\n", dest_ss, ret); - goto out; } /* Check the response */ - if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) { + if (ret != -ETIMEDOUT && QMI_RESP_BIT_SHIFT(resp.resp.result) != + QMI_RESULT_SUCCESS_V01) { pr_err("SYSMON QMI request failed 0x%x\n", QMI_RESP_BIT_SHIFT(resp.resp.error)); ret = -EREMOTEIO; @@ -440,21 +442,13 @@ int sysmon_send_shutdown(struct subsys_desc *dest_desc) } shutdown_ack_ret = wait_for_shutdown_ack(dest_desc); - if (shutdown_ack_ret < 0) { - pr_err("shutdown_ack SMP2P bit for %s not set\n", data->name); - if (!data->ind_recv.done) { - pr_err("QMI shutdown indication not received\n"); - ret = shutdown_ack_ret; - } + if (shutdown_ack_ret > 0) { + ret = 0; goto out; - } else if (shutdown_ack_ret > 0) - goto out; - - if (!wait_for_completion_timeout(&data->ind_recv, - msecs_to_jiffies(SHUTDOWN_TIMEOUT))) { - pr_err("Timed out waiting for shutdown indication from %s\n", - data->name); - ret = -ETIMEDOUT; + } else if (shutdown_ack_ret < 0) { + pr_err("shutdown acknowledgment not received for %s\n", + data->name); + ret = shutdown_ack_ret; } out: return ret; @@ -651,8 +645,6 @@ int sysmon_notifier_register(struct subsys_desc *desc) goto add_list; } - init_completion(&data->ind_recv); - rc = qmi_handle_init(&data->clnt_handle, QMI_SSCTL_RESP_MSG_LENGTH, &ssctl_ops, qmi_indication_handler); diff --git a/drivers/soc/qcom/system_pm.c b/drivers/soc/qcom/system_pm.c index 6164b445f1f4cd9d3ac2511d0e4786e09d7915b5..f2ca7c823c4a0d82f335580f6f1ce18648255683 100644 --- a/drivers/soc/qcom/system_pm.c +++ b/drivers/soc/qcom/system_pm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -13,11 +13,10 @@ #include #include - #include -#include - #include +#include +#include "rpmh_master_stat.h" #define PDC_TIME_VALID_SHIFT 31 #define PDC_TIME_UPPER_MASK 0xFFFFFF @@ -35,7 +34,7 @@ static int setup_wakeup(uint32_t lo, uint32_t hi) return rpmh_write_control(rpmh_client, cmd, ARRAY_SIZE(cmd)); } -int system_sleep_update_wakeup(void) +static int system_sleep_update_wakeup(bool from_idle) { uint32_t lo = ~0U, hi = ~0U; @@ -44,16 +43,14 @@ int system_sleep_update_wakeup(void) return setup_wakeup(lo, hi); } -EXPORT_SYMBOL(system_sleep_update_wakeup); /** * system_sleep_allowed() - Returns if its okay to enter system low power modes */ -bool system_sleep_allowed(void) +static bool system_sleep_allowed(void) { return (rpmh_ctrlr_idle(rpmh_client) == 0); } -EXPORT_SYMBOL(system_sleep_allowed); /** * system_sleep_enter() - Activties done when entering system low power modes @@ -61,22 +58,25 @@ EXPORT_SYMBOL(system_sleep_allowed); * Returns 0 for success or error values from writing the sleep/wake values to * the hardware block. */ -int system_sleep_enter(void) +static int system_sleep_enter(struct cpumask *mask) { - if (IS_ERR_OR_NULL(rpmh_client)) - return -EFAULT; - return rpmh_flush(rpmh_client); } -EXPORT_SYMBOL(system_sleep_enter); /** * system_sleep_exit() - Activities done when exiting system low power modes */ -void system_sleep_exit(void) +static void system_sleep_exit(void) { + msm_rpmh_master_stats_update(); } -EXPORT_SYMBOL(system_sleep_exit); + +static struct system_pm_ops pm_ops = { + .enter = system_sleep_enter, + .exit = system_sleep_exit, + .update_wakeup = system_sleep_update_wakeup, + .sleep_allowed = system_sleep_allowed, +}; static int sys_pm_probe(struct platform_device *pdev) { @@ -84,7 +84,7 @@ static int sys_pm_probe(struct platform_device *pdev) if (IS_ERR_OR_NULL(rpmh_client)) return PTR_ERR(rpmh_client); - return 0; + return register_system_pm_ops(&pm_ops); } static const struct of_device_id sys_pm_drv_match[] = { diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index cae57fb531288c11e1ef542dd83e952ace004414..01e99873ae3cf2a2f3ffb4fd4a1cf8d6e3d71a9b 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -556,6 +557,8 @@ static void configure_bark_dump(struct msm_watchdog_data *wdog_dd) cpu_data[cpu].addr = virt_to_phys(cpu_buf + cpu * MAX_CPU_CTX_SIZE); cpu_data[cpu].len = MAX_CPU_CTX_SIZE; + snprintf(cpu_data[cpu].name, sizeof(cpu_data[cpu].name), + "KCPU_CTX%d", cpu); dump_entry.id = MSM_DUMP_DATA_CPU_CTX + cpu; dump_entry.addr = virt_to_phys(&cpu_data[cpu]); ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, @@ -603,6 +606,8 @@ static void configure_scandump(struct msm_watchdog_data *wdog_dd) cpu_data->addr = dump_addr; cpu_data->len = MAX_CPU_SCANDUMP_SIZE; + snprintf(cpu_data->name, sizeof(cpu_data->name), + "KSCANDUMP%d", cpu); dump_entry.id = MSM_DUMP_DATA_SCANDUMP_PER_CPU + cpu; dump_entry.addr = virt_to_phys(cpu_data); ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, @@ -806,6 +811,7 @@ static int msm_watchdog_probe(struct platform_device *pdev) { int ret; struct msm_watchdog_data *wdog_dd; + struct md_region md_entry; if (!pdev->dev.of_node || !enable) return -ENODEV; @@ -827,6 +833,15 @@ static int msm_watchdog_probe(struct platform_device *pdev) goto err; } init_watchdog_data(wdog_dd); + + /* Add wdog info to minidump table */ + strlcpy(md_entry.name, "KWDOGDATA", sizeof(md_entry.name)); + md_entry.virt_addr = (uintptr_t)wdog_dd; + md_entry.phys_addr = virt_to_phys(wdog_dd); + md_entry.size = sizeof(*wdog_dd); + if (msm_minidump_add_region(&md_entry)) + pr_info("Failed to add Watchdog data in Minidump\n"); + return 0; err: kzfree(wdog_dd); diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index fe3fa1e8517a1b5b83a258e875f48a30fcf29267..4903f15177cfd814a3c832781db53e285bb57482 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -624,6 +624,11 @@ static int a3700_spi_transfer_one(struct spi_master *master, a3700_spi_header_set(a3700_spi); if (xfer->rx_buf) { + /* Clear WFIFO, since it's last 2 bytes are shifted out during + * a read operation + */ + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, 0); + /* Set read data length */ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, a3700_spi->buf_len); diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 66947097102370d0f54ba2559abddab1ac812a5d..047875861df10a2f14be3fd705c462d3a43c040d 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1489,6 +1489,11 @@ static void atmel_spi_init(struct atmel_spi *as) { spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ + + /* It is recommended to enable FIFOs first thing after reset */ + if (as->fifo_size) + spi_writel(as, CR, SPI_BIT(FIFOEN)); + if (as->caps.has_wdrbt) { spi_writel(as, MR, SPI_BIT(WDRBT) | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); @@ -1499,9 +1504,6 @@ static void atmel_spi_init(struct atmel_spi *as) if (as->use_pdc) spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); - - if (as->fifo_size) - spi_writel(as, CR, SPI_BIT(FIFOEN)); } static int atmel_spi_probe(struct platform_device *pdev) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 092a5fc85b9a4e59ccf4d9c83ca319eaa866fd7d..2770fbd4ce49ff1eb211255f5489f1f9536b5f95 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -797,11 +797,21 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, goto stop_dma; } - /* wait for tx fifo to be emptied / rx fifo to be filled */ + /* wait for tx/rx DMA completion */ ret = sh_msiof_wait_for_completion(p); if (ret) goto stop_reset; + if (!rx) { + reinit_completion(&p->done); + sh_msiof_write(p, IER, IER_TEOFE); + + /* wait for tx fifo to be emptied */ + ret = sh_msiof_wait_for_completion(p); + if (ret) + goto stop_reset; + } + /* clear status bits */ sh_msiof_reset_str(p); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3ff0ee88c467a58faa9c6a1544fd620e6852b250..84dfef4bd6ae6b20090581b9a42fe5d59b62bedd 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -779,8 +779,14 @@ static int spi_map_buf(struct spi_controller *ctlr, struct device *dev, for (i = 0; i < sgs; i++) { if (vmalloced_buf || kmap_buf) { - min = min_t(size_t, - len, desc_len - offset_in_page(buf)); + /* + * Next scatterlist entry size is the minimum between + * the desc_len and the remaining buffer length that + * fits in a page. + */ + min = min_t(size_t, desc_len, + min_t(size_t, len, + PAGE_SIZE - offset_in_page(buf))); if (vmalloced_buf) vm_page = vmalloc_to_page(buf); else @@ -2252,12 +2258,6 @@ void spi_unregister_controller(struct spi_controller *ctlr) mutex_lock(&board_lock); found = idr_find(&spi_master_idr, id); mutex_unlock(&board_lock); - if (found != ctlr) { - dev_dbg(&ctlr->dev, - "attempting to delete unregistered controller [%s]\n", - dev_name(&ctlr->dev)); - return; - } if (ctlr->queued) { if (spi_destroy_queue(ctlr)) dev_err(&ctlr->dev, "queue remove failed\n"); @@ -2270,7 +2270,8 @@ void spi_unregister_controller(struct spi_controller *ctlr) device_unregister(&ctlr->dev); /* free bus id */ mutex_lock(&board_lock); - idr_remove(&spi_master_idr, id); + if (found == ctlr) + idr_remove(&spi_master_idr, id); mutex_unlock(&board_lock); } EXPORT_SYMBOL_GPL(spi_unregister_controller); diff --git a/drivers/spmi/spmi-pmic-arb-debug.c b/drivers/spmi/spmi-pmic-arb-debug.c index c5a31a9d84ebe5fee157b249e9d3e5fb9a527e20..2c90bef1224fa0c746ef81bc2eab7c1321eb7a6c 100644 --- a/drivers/spmi/spmi-pmic-arb-debug.c +++ b/drivers/spmi/spmi-pmic-arb-debug.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -69,6 +70,7 @@ enum pmic_arb_cmd_op_code { struct spmi_pmic_arb_debug { void __iomem *addr; raw_spinlock_t lock; + struct clk *clock; }; static inline void pmic_arb_debug_write(struct spmi_pmic_arb_debug *pa, @@ -181,6 +183,12 @@ static int pmic_arb_debug_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; + rc = clk_prepare_enable(pa->clock); + if (rc) { + pr_err("%s: failed to enable core clock, rc=%d\n", + __func__, rc); + return rc; + } raw_spin_lock_irqsave(&pa->lock, flags); rc = pmic_arb_debug_issue_command(ctrl, opc, sid, addr, len); @@ -192,6 +200,7 @@ static int pmic_arb_debug_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, buf[i] = pmic_arb_debug_read(pa, PMIC_ARB_DEBUG_RDATA(i)); done: raw_spin_unlock_irqrestore(&pa->lock, flags); + clk_disable_unprepare(pa->clock); return rc; } @@ -221,6 +230,12 @@ static int pmic_arb_debug_write_cmd(struct spmi_controller *ctrl, u8 opc, else return -EINVAL; + rc = clk_prepare_enable(pa->clock); + if (rc) { + pr_err("%s: failed to enable core clock, rc=%d\n", + __func__, rc); + return rc; + } raw_spin_lock_irqsave(&pa->lock, flags); /* Write data to FIFO */ @@ -230,6 +245,7 @@ static int pmic_arb_debug_write_cmd(struct spmi_controller *ctrl, u8 opc, rc = pmic_arb_debug_issue_command(ctrl, opc, sid, addr, len); raw_spin_unlock_irqrestore(&pa->lock, flags); + clk_disable_unprepare(pa->clock); return rc; } @@ -293,6 +309,17 @@ static int spmi_pmic_arb_debug_probe(struct platform_device *pdev) goto err_put_ctrl; } + if (of_find_property(pdev->dev.of_node, "clock-names", NULL)) { + pa->clock = devm_clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(pa->clock)) { + rc = PTR_ERR(pa->clock); + if (rc != -EPROBE_DEFER) + dev_err(&pdev->dev, "unable to request core clock, rc=%d\n", + rc); + goto err_put_ctrl; + } + } + platform_set_drvdata(pdev, ctrl); raw_spin_lock_init(&pa->lock); diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 0a28c7616d539ca67926db2aa91329b1a34711b4..4630dc85634e6aaaa06e492c0996e8863b8e48b2 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -14,6 +14,15 @@ config ASHMEM It is, in theory, a good memory allocator for low-memory devices, because it can discard shared memory units when under memory pressure. +config ANDROID_VSOC + tristate "Android Virtual SoC support" + default n + depends on PCI_MSI + ---help--- + This option adds support for the Virtual SoC driver needed to boot + a 'cuttlefish' Android image inside QEmu. The driver interacts with + a QEmu ivshmem device. If built as a module, it will be called vsoc. + source "drivers/staging/android/ion/Kconfig" source "drivers/staging/android/fiq_debugger/Kconfig" diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 9b9b297d7c0ea6d0cee687dbf0894c5a6d98debc..2638b4a23df4cddd74fd10207d9632e155f4d7a1 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -4,3 +4,4 @@ obj-y += ion/ obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger/ obj-$(CONFIG_ASHMEM) += ashmem.o +obj-$(CONFIG_ANDROID_VSOC) += vsoc.o diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO index 5f14247392bf430dd1df0de1399b52cee83a1dc9..2ea6f97b8f0ff2cea4c4a2ac84d8d79ae2797216 100644 --- a/drivers/staging/android/TODO +++ b/drivers/staging/android/TODO @@ -12,5 +12,15 @@ ion/ - Split /dev/ion up into multiple nodes (e.g. /dev/ion/heap0) - Better test framework (integration with VGEM was suggested) +vsoc.c, uapi/vsoc_shm.h + - The current driver uses the same wait queue for all of the futexes in a + region. This will cause false wakeups in regions with a large number of + waiting threads. We should eventually use multiple queues and select the + queue based on the region. + - Add debugfs support for examining the permissions of regions. + - Use ioremap_wc instead of ioremap_nocache. + - Remove VSOC_WAIT_FOR_INCOMING_INTERRUPT ioctl. This functionality has been + superseded by the futex and is there for legacy reasons. + Please send patches to Greg Kroah-Hartman and Cc: Arve HjønnevÃ¥g and Riley Andrews diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 295611223d538c65b5a50f99b7146eb5f3022fe8..f71d430d169e69b7fb8860b03af05c69b39fad85 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -168,6 +168,7 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, mutex_lock(&dev->buffer_lock); ion_buffer_add(dev, buffer); mutex_unlock(&dev->buffer_lock); + atomic_long_add(len, &heap->total_allocated); return buffer; err1: @@ -196,6 +197,7 @@ static void _ion_buffer_destroy(struct ion_buffer *buffer) rb_erase(&buffer->node, &dev->buffers); mutex_unlock(&dev->buffer_lock); + atomic_long_sub(buffer->size, &buffer->heap->total_allocated); if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) ion_heap_freelist_add(heap, buffer); else @@ -254,7 +256,8 @@ static struct sg_table *dup_sg_table(struct sg_table *table) new_sg = new_table->sgl; for_each_sg(table->sgl, sg, table->nents, i) { memcpy(new_sg, sg, sizeof(*sg)); - new_sg->dma_address = 0; + sg_dma_address(new_sg) = 0; + sg_dma_len(new_sg) = 0; new_sg = sg_next(new_sg); } @@ -311,8 +314,8 @@ static void ion_dma_buf_detatch(struct dma_buf *dmabuf, struct ion_dma_buf_attachment *a = attachment->priv; struct ion_buffer *buffer = dmabuf->priv; - free_duped_table(a->table); mutex_lock(&buffer->lock); + free_duped_table(a->table); list_del(&a->list); mutex_unlock(&buffer->lock); @@ -335,6 +338,7 @@ static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, !hlos_accessible_buffer(buffer)) map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; + mutex_lock(&buffer->lock); if (map_attrs & DMA_ATTR_SKIP_CPU_SYNC) trace_ion_dma_map_cmo_skip(attachment->dev, attachment->dmabuf->name, @@ -364,6 +368,7 @@ static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, return ERR_PTR(-ENOMEM); a->dma_mapped = true; + mutex_unlock(&buffer->lock); return table; } @@ -380,6 +385,7 @@ static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, !hlos_accessible_buffer(buffer)) map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; + mutex_lock(&buffer->lock); if (map_attrs & DMA_ATTR_SKIP_CPU_SYNC) trace_ion_dma_unmap_cmo_skip(attachment->dev, attachment->dmabuf->name, @@ -404,6 +410,7 @@ static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, dma_unmap_sg_attrs(attachment->dev, table->sgl, table->nents, direction, map_attrs); a->dma_mapped = false; + mutex_unlock(&buffer->lock); } void ion_pages_sync_for_device(struct device *dev, struct page *page, @@ -519,18 +526,35 @@ static void ion_dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) { } -static void ion_sgl_sync_range(struct device *dev, struct scatterlist *sgl, - unsigned int nents, unsigned long offset, - unsigned long length, - enum dma_data_direction dir, bool for_cpu) +static int ion_sgl_sync_range(struct device *dev, struct scatterlist *sgl, + unsigned int nents, unsigned long offset, + unsigned long length, + enum dma_data_direction dir, bool for_cpu) { int i; struct scatterlist *sg; unsigned int len = 0; + dma_addr_t sg_dma_addr; + + for_each_sg(sgl, sg, nents, i) { + if (sg_dma_len(sg) == 0) + break; + + if (i > 0) { + pr_warn("Partial cmo only supported with 1 segment\n" + "is dma_set_max_seg_size being set on dev:%s\n", + dev_name(dev)); + return -EINVAL; + } + } + for_each_sg(sgl, sg, nents, i) { unsigned int sg_offset, sg_left, size = 0; + if (i == 0) + sg_dma_addr = sg_dma_address(sg); + len += sg->length; if (len <= offset) continue; @@ -540,32 +564,42 @@ static void ion_sgl_sync_range(struct device *dev, struct scatterlist *sgl, size = (length < sg_left) ? length : sg_left; if (for_cpu) - dma_sync_single_range_for_cpu(dev, sg->dma_address, + dma_sync_single_range_for_cpu(dev, sg_dma_addr, sg_offset, size, dir); else - dma_sync_single_range_for_device(dev, sg->dma_address, + dma_sync_single_range_for_device(dev, sg_dma_addr, sg_offset, size, dir); offset += size; length -= size; + sg_dma_addr += sg->length; if (length == 0) break; } + + return 0; } -static void ion_sgl_sync_mapped(struct device *dev, struct scatterlist *sgl, - unsigned int nents, struct list_head *vmas, - enum dma_data_direction dir, bool for_cpu) +static int ion_sgl_sync_mapped(struct device *dev, struct scatterlist *sgl, + unsigned int nents, struct list_head *vmas, + enum dma_data_direction dir, bool for_cpu) { struct ion_vma_list *vma_list; + int ret = 0; list_for_each_entry(vma_list, vmas, list) { struct vm_area_struct *vma = vma_list->vma; - ion_sgl_sync_range(dev, sgl, nents, vma->vm_pgoff * PAGE_SIZE, - vma->vm_end - vma->vm_start, dir, for_cpu); + ret = ion_sgl_sync_range(dev, sgl, nents, + vma->vm_pgoff * PAGE_SIZE, + vma->vm_end - vma->vm_start, dir, + for_cpu); + if (ret) + break; } + + return ret; } static int __ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, @@ -608,23 +642,31 @@ static int __ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, struct device *dev = buffer->heap->priv; struct sg_table *table = buffer->sg_table; - trace_ion_begin_cpu_access_cmo_apply(dev, dmabuf->name, - true, true, direction, - sync_only_mapped); - if (sync_only_mapped) - ion_sgl_sync_mapped(dev, table->sgl, - table->nents, &buffer->vmas, - direction, true); + ret = ion_sgl_sync_mapped(dev, table->sgl, + table->nents, &buffer->vmas, + direction, true); else dma_sync_sg_for_cpu(dev, table->sgl, table->nents, direction); + if (!ret) + trace_ion_begin_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); + else + trace_ion_begin_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); mutex_unlock(&buffer->lock); goto out; } list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; + if (!a->dma_mapped) { trace_ion_begin_cpu_access_notmapped(a->dev, dmabuf->name, @@ -634,17 +676,29 @@ static int __ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, continue; } - trace_ion_begin_cpu_access_cmo_apply(a->dev, dmabuf->name, - true, true, direction, - sync_only_mapped); - if (sync_only_mapped) - ion_sgl_sync_mapped(a->dev, a->table->sgl, - a->table->nents, &buffer->vmas, - direction, true); + tmp = ion_sgl_sync_mapped(a->dev, a->table->sgl, + a->table->nents, + &buffer->vmas, + direction, true); else dma_sync_sg_for_cpu(a->dev, a->table->sgl, a->table->nents, direction); + + if (!tmp) { + trace_ion_begin_cpu_access_cmo_apply(a->dev, + dmabuf->name, + true, true, + direction, + sync_only_mapped); + } else { + trace_ion_begin_cpu_access_cmo_skip(a->dev, + dmabuf->name, true, + true, direction, + sync_only_mapped); + ret = tmp; + } + } mutex_unlock(&buffer->lock); @@ -687,22 +741,30 @@ static int __ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, struct device *dev = buffer->heap->priv; struct sg_table *table = buffer->sg_table; - trace_ion_end_cpu_access_cmo_apply(dev, dmabuf->name, - true, true, direction, - sync_only_mapped); - if (sync_only_mapped) - ion_sgl_sync_mapped(dev, table->sgl, - table->nents, &buffer->vmas, - direction, false); + ret = ion_sgl_sync_mapped(dev, table->sgl, + table->nents, &buffer->vmas, + direction, false); else dma_sync_sg_for_device(dev, table->sgl, table->nents, direction); + + if (!ret) + trace_ion_end_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); + else + trace_ion_end_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, direction, + sync_only_mapped); mutex_unlock(&buffer->lock); goto out; } list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; + if (!a->dma_mapped) { trace_ion_end_cpu_access_notmapped(a->dev, dmabuf->name, @@ -712,17 +774,26 @@ static int __ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, continue; } - trace_ion_end_cpu_access_cmo_apply(a->dev, dmabuf->name, - true, true, direction, - sync_only_mapped); - if (sync_only_mapped) - ion_sgl_sync_mapped(a->dev, a->table->sgl, - a->table->nents, &buffer->vmas, - direction, false); + tmp = ion_sgl_sync_mapped(a->dev, a->table->sgl, + a->table->nents, + &buffer->vmas, direction, + false); else dma_sync_sg_for_device(a->dev, a->table->sgl, a->table->nents, direction); + + if (!tmp) { + trace_ion_end_cpu_access_cmo_apply(a->dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); + } else { + trace_ion_end_cpu_access_cmo_skip(a->dev, dmabuf->name, + true, true, direction, + sync_only_mapped); + ret = tmp; + } } mutex_unlock(&buffer->lock); @@ -794,18 +865,24 @@ static int ion_dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf, struct device *dev = buffer->heap->priv; struct sg_table *table = buffer->sg_table; - trace_ion_begin_cpu_access_cmo_apply(dev, dmabuf->name, - true, true, dir, - false); - - ion_sgl_sync_range(dev, table->sgl, table->nents, - offset, len, dir, true); + ret = ion_sgl_sync_range(dev, table->sgl, table->nents, + offset, len, dir, true); + if (!ret) + trace_ion_begin_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, dir, + false); + else + trace_ion_begin_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, dir, + false); mutex_unlock(&buffer->lock); goto out; } list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; + if (!a->dma_mapped) { trace_ion_begin_cpu_access_notmapped(a->dev, dmabuf->name, @@ -815,12 +892,22 @@ static int ion_dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf, continue; } - trace_ion_begin_cpu_access_cmo_apply(a->dev, dmabuf->name, - true, true, dir, - false); + tmp = ion_sgl_sync_range(a->dev, a->table->sgl, a->table->nents, + offset, len, dir, true); + + if (!tmp) { + trace_ion_begin_cpu_access_cmo_apply(a->dev, + dmabuf->name, + true, true, dir, + false); + } else { + trace_ion_begin_cpu_access_cmo_skip(a->dev, + dmabuf->name, + true, true, dir, + false); + ret = tmp; + } - ion_sgl_sync_range(a->dev, a->table->sgl, a->table->nents, - offset, len, dir, true); } mutex_unlock(&buffer->lock); @@ -864,18 +951,25 @@ static int ion_dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf, struct device *dev = buffer->heap->priv; struct sg_table *table = buffer->sg_table; - trace_ion_end_cpu_access_cmo_apply(dev, dmabuf->name, - true, true, direction, - false); + ret = ion_sgl_sync_range(dev, table->sgl, table->nents, + offset, len, direction, false); - ion_sgl_sync_range(dev, table->sgl, table->nents, - offset, len, direction, false); + if (!ret) + trace_ion_end_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, + direction, false); + else + trace_ion_end_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, + direction, false); mutex_unlock(&buffer->lock); goto out; } list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; + if (!a->dma_mapped) { trace_ion_end_cpu_access_notmapped(a->dev, dmabuf->name, @@ -885,13 +979,20 @@ static int ion_dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf, continue; } - trace_ion_end_cpu_access_cmo_apply(a->dev, dmabuf->name, - true, true, direction, - false); + tmp = ion_sgl_sync_range(a->dev, a->table->sgl, a->table->nents, + offset, len, direction, false); - ion_sgl_sync_range(a->dev, a->table->sgl, a->table->nents, - offset, len, direction, false); + if (!tmp) { + trace_ion_end_cpu_access_cmo_apply(a->dev, dmabuf->name, + true, true, + direction, false); + } else { + trace_ion_end_cpu_access_cmo_skip(a->dev, dmabuf->name, + true, true, direction, + false); + ret = tmp; + } } mutex_unlock(&buffer->lock); diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h index aa099fdcaa257578463cac43cde32a4761070620..c8f84887bb6ba24bcbddd466922a11733d8b3552 100644 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -240,6 +240,7 @@ struct ion_heap { spinlock_t free_lock; wait_queue_head_t waitqueue; struct task_struct *task; + atomic_long_t total_allocated; int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *); }; diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index 0ddc663678c4be8342589c39c253f91f906c8184..b1efb3958a871e16c11bfbb2b9aaa9519abceaf9 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -48,6 +48,9 @@ static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) list_add_tail(&page->lru, &pool->low_items); pool->low_count++; } + + mod_node_page_state(page_pgdat(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, + (1 << (PAGE_SHIFT + pool->order))); mutex_unlock(&pool->mutex); return 0; } @@ -67,6 +70,8 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) } list_del(&page->lru); + mod_node_page_state(page_pgdat(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, + -(1 << (PAGE_SHIFT + pool->order))); return page; } diff --git a/drivers/staging/android/ion/ion_system_heap.h b/drivers/staging/android/ion/ion_system_heap.h index a9f12103d254d27bf4411259b775c0d0459d0546..6a913d62c9b5b296f955c368e367b1f3364de2a5 100644 --- a/drivers/staging/android/ion/ion_system_heap.h +++ b/drivers/staging/android/ion/ion_system_heap.h @@ -16,7 +16,11 @@ #define _ION_SYSTEM_HEAP_H #ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS -static const unsigned int orders[] = {9, 8, 4, 0}; +#if defined(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) +static const unsigned int orders[] = {8, 4, 0}; +#else +static const unsigned int orders[] = {9, 4, 0}; +#endif #else static const unsigned int orders[] = {0}; #endif diff --git a/drivers/staging/android/uapi/vsoc_shm.h b/drivers/staging/android/uapi/vsoc_shm.h new file mode 100644 index 0000000000000000000000000000000000000000..741b1387c25b730a96a00ec8b881749f7272097e --- /dev/null +++ b/drivers/staging/android/uapi/vsoc_shm.h @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2017 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 _UAPI_LINUX_VSOC_SHM_H +#define _UAPI_LINUX_VSOC_SHM_H + +#include + +/** + * A permission is a token that permits a receiver to read and/or write an area + * of memory within a Vsoc region. + * + * An fd_scoped permission grants both read and write access, and can be + * attached to a file description (see open(2)). + * Ownership of the area can then be shared by passing a file descriptor + * among processes. + * + * begin_offset and end_offset define the area of memory that is controlled by + * the permission. owner_offset points to a word, also in shared memory, that + * controls ownership of the area. + * + * ownership of the region expires when the associated file description is + * released. + * + * At most one permission can be attached to each file description. + * + * This is useful when implementing HALs like gralloc that scope and pass + * ownership of shared resources via file descriptors. + * + * The caller is responsibe for doing any fencing. + * + * The calling process will normally identify a currently free area of + * memory. It will construct a proposed fd_scoped_permission_arg structure: + * + * begin_offset and end_offset describe the area being claimed + * + * owner_offset points to the location in shared memory that indicates the + * owner of the area. + * + * owned_value is the value that will be stored in owner_offset iff the + * permission can be granted. It must be different than VSOC_REGION_FREE. + * + * Two fd_scoped_permission structures are compatible if they vary only by + * their owned_value fields. + * + * The driver ensures that, for any group of simultaneous callers proposing + * compatible fd_scoped_permissions, it will accept exactly one of the + * propopsals. The other callers will get a failure with errno of EAGAIN. + * + * A process receiving a file descriptor can identify the region being + * granted using the VSOC_GET_FD_SCOPED_PERMISSION ioctl. + */ +struct fd_scoped_permission { + __u32 begin_offset; + __u32 end_offset; + __u32 owner_offset; + __u32 owned_value; +}; + +/* + * This value represents a free area of memory. The driver expects to see this + * value at owner_offset when creating a permission otherwise it will not do it, + * and will write this value back once the permission is no longer needed. + */ +#define VSOC_REGION_FREE ((__u32)0) + +/** + * ioctl argument for VSOC_CREATE_FD_SCOPE_PERMISSION + */ +struct fd_scoped_permission_arg { + struct fd_scoped_permission perm; + __s32 managed_region_fd; +}; + +#define VSOC_NODE_FREE ((__u32)0) + +/* + * Describes a signal table in shared memory. Each non-zero entry in the + * table indicates that the receiver should signal the futex at the given + * offset. Offsets are relative to the region, not the shared memory window. + * + * interrupt_signalled_offset is used to reliably signal interrupts across the + * vmm boundary. There are two roles: transmitter and receiver. For example, + * in the host_to_guest_signal_table the host is the transmitter and the + * guest is the receiver. The protocol is as follows: + * + * 1. The transmitter should convert the offset of the futex to an offset + * in the signal table [0, (1 << num_nodes_lg2)) + * The transmitter can choose any appropriate hashing algorithm, including + * hash = futex_offset & ((1 << num_nodes_lg2) - 1) + * + * 3. The transmitter should atomically compare and swap futex_offset with 0 + * at hash. There are 3 possible outcomes + * a. The swap fails because the futex_offset is already in the table. + * The transmitter should stop. + * b. Some other offset is in the table. This is a hash collision. The + * transmitter should move to another table slot and try again. One + * possible algorithm: + * hash = (hash + 1) & ((1 << num_nodes_lg2) - 1) + * c. The swap worked. Continue below. + * + * 3. The transmitter atomically swaps 1 with the value at the + * interrupt_signalled_offset. There are two outcomes: + * a. The prior value was 1. In this case an interrupt has already been + * posted. The transmitter is done. + * b. The prior value was 0, indicating that the receiver may be sleeping. + * The transmitter will issue an interrupt. + * + * 4. On waking the receiver immediately exchanges a 0 with the + * interrupt_signalled_offset. If it receives a 0 then this a spurious + * interrupt. That may occasionally happen in the current protocol, but + * should be rare. + * + * 5. The receiver scans the signal table by atomicaly exchanging 0 at each + * location. If a non-zero offset is returned from the exchange the + * receiver wakes all sleepers at the given offset: + * futex((int*)(region_base + old_value), FUTEX_WAKE, MAX_INT); + * + * 6. The receiver thread then does a conditional wait, waking immediately + * if the value at interrupt_signalled_offset is non-zero. This catches cases + * here additional signals were posted while the table was being scanned. + * On the guest the wait is handled via the VSOC_WAIT_FOR_INCOMING_INTERRUPT + * ioctl. + */ +struct vsoc_signal_table_layout { + /* log_2(Number of signal table entries) */ + __u32 num_nodes_lg2; + /* + * Offset to the first signal table entry relative to the start of the + * region + */ + __u32 futex_uaddr_table_offset; + /* + * Offset to an atomic_t / atomic uint32_t. A non-zero value indicates + * that one or more offsets are currently posted in the table. + * semi-unique access to an entry in the table + */ + __u32 interrupt_signalled_offset; +}; + +#define VSOC_REGION_WHOLE ((__s32)0) +#define VSOC_DEVICE_NAME_SZ 16 + +/** + * Each HAL would (usually) talk to a single device region + * Mulitple entities care about these regions: + * - The ivshmem_server will populate the regions in shared memory + * - The guest kernel will read the region, create minor device nodes, and + * allow interested parties to register for FUTEX_WAKE events in the region + * - HALs will access via the minor device nodes published by the guest kernel + * - Host side processes will access the region via the ivshmem_server: + * 1. Pass name to ivshmem_server at a UNIX socket + * 2. ivshmemserver will reply with 2 fds: + * - host->guest doorbell fd + * - guest->host doorbell fd + * - fd for the shared memory region + * - region offset + * 3. Start a futex receiver thread on the doorbell fd pointed at the + * signal_nodes + */ +struct vsoc_device_region { + __u16 current_version; + __u16 min_compatible_version; + __u32 region_begin_offset; + __u32 region_end_offset; + __u32 offset_of_region_data; + struct vsoc_signal_table_layout guest_to_host_signal_table; + struct vsoc_signal_table_layout host_to_guest_signal_table; + /* Name of the device. Must always be terminated with a '\0', so + * the longest supported device name is 15 characters. + */ + char device_name[VSOC_DEVICE_NAME_SZ]; + /* There are two ways that permissions to access regions are handled: + * - When subdivided_by is VSOC_REGION_WHOLE, any process that can + * open the device node for the region gains complete access to it. + * - When subdivided is set processes that open the region cannot + * access it. Access to a sub-region must be established by invoking + * the VSOC_CREATE_FD_SCOPE_PERMISSION ioctl on the region + * referenced in subdivided_by, providing a fileinstance + * (represented by a fd) opened on this region. + */ + __u32 managed_by; +}; + +/* + * The vsoc layout descriptor. + * The first 4K should be reserved for the shm header and region descriptors. + * The regions should be page aligned. + */ + +struct vsoc_shm_layout_descriptor { + __u16 major_version; + __u16 minor_version; + + /* size of the shm. This may be redundant but nice to have */ + __u32 size; + + /* number of shared memory regions */ + __u32 region_count; + + /* The offset to the start of region descriptors */ + __u32 vsoc_region_desc_offset; +}; + +/* + * This specifies the current version that should be stored in + * vsoc_shm_layout_descriptor.major_version and + * vsoc_shm_layout_descriptor.minor_version. + * It should be updated only if the vsoc_device_region and + * vsoc_shm_layout_descriptor structures have changed. + * Versioning within each region is transferred + * via the min_compatible_version and current_version fields in + * vsoc_device_region. The driver does not consult these fields: they are left + * for the HALs and host processes and will change independently of the layout + * version. + */ +#define CURRENT_VSOC_LAYOUT_MAJOR_VERSION 2 +#define CURRENT_VSOC_LAYOUT_MINOR_VERSION 0 + +#define VSOC_CREATE_FD_SCOPED_PERMISSION \ + _IOW(0xF5, 0, struct fd_scoped_permission) +#define VSOC_GET_FD_SCOPED_PERMISSION _IOR(0xF5, 1, struct fd_scoped_permission) + +/* + * This is used to signal the host to scan the guest_to_host_signal_table + * for new futexes to wake. This sends an interrupt if one is not already + * in flight. + */ +#define VSOC_MAYBE_SEND_INTERRUPT_TO_HOST _IO(0xF5, 2) + +/* + * When this returns the guest will scan host_to_guest_signal_table to + * check for new futexes to wake. + */ +/* TODO(ghartman): Consider moving this to the bottom half */ +#define VSOC_WAIT_FOR_INCOMING_INTERRUPT _IO(0xF5, 3) + +/* + * Guest HALs will use this to retrieve the region description after + * opening their device node. + */ +#define VSOC_DESCRIBE_REGION _IOR(0xF5, 4, struct vsoc_device_region) + +/* + * Wake any threads that may be waiting for a host interrupt on this region. + * This is mostly used during shutdown. + */ +#define VSOC_SELF_INTERRUPT _IO(0xF5, 5) + +/* + * This is used to signal the host to scan the guest_to_host_signal_table + * for new futexes to wake. This sends an interrupt unconditionally. + */ +#define VSOC_SEND_INTERRUPT_TO_HOST _IO(0xF5, 6) + +enum wait_types { + VSOC_WAIT_UNDEFINED = 0, + VSOC_WAIT_IF_EQUAL = 1, + VSOC_WAIT_IF_EQUAL_TIMEOUT = 2 +}; + +/* + * Wait for a condition to be true + * + * Note, this is sized and aligned so the 32 bit and 64 bit layouts are + * identical. + */ +struct vsoc_cond_wait { + /* Input: Offset of the 32 bit word to check */ + __u32 offset; + /* Input: Value that will be compared with the offset */ + __u32 value; + /* Monotonic time to wake at in seconds */ + __u64 wake_time_sec; + /* Input: Monotonic time to wait in nanoseconds */ + __u32 wake_time_nsec; + /* Input: Type of wait */ + __u32 wait_type; + /* Output: Number of times the thread woke before returning. */ + __u32 wakes; + /* Ensure that we're 8-byte aligned and 8 byte length for 32/64 bit + * compatibility. + */ + __u32 reserved_1; +}; + +#define VSOC_COND_WAIT _IOWR(0xF5, 7, struct vsoc_cond_wait) + +/* Wake any local threads waiting at the offset given in arg */ +#define VSOC_COND_WAKE _IO(0xF5, 8) + +#endif /* _UAPI_LINUX_VSOC_SHM_H */ diff --git a/drivers/staging/android/vsoc.c b/drivers/staging/android/vsoc.c new file mode 100644 index 0000000000000000000000000000000000000000..587c66d709b97e759901aa6909d1ecdb1c178224 --- /dev/null +++ b/drivers/staging/android/vsoc.c @@ -0,0 +1,1169 @@ +/* + * drivers/android/staging/vsoc.c + * + * Android Virtual System on a Chip (VSoC) driver + * + * Copyright (C) 2017 Google, Inc. + * + * Author: ghartman@google.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * + * Based on drivers/char/kvm_ivshmem.c - driver for KVM Inter-VM shared memory + * Copyright 2009 Cam Macdonell + * + * Based on cirrusfb.c and 8139cp.c: + * Copyright 1999-2001 Jeff Garzik + * Copyright 2001-2004 Jeff Garzik + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "uapi/vsoc_shm.h" + +#define VSOC_DEV_NAME "vsoc" + +/* + * Description of the ivshmem-doorbell PCI device used by QEmu. These + * constants follow docs/specs/ivshmem-spec.txt, which can be found in + * the QEmu repository. This was last reconciled with the version that + * came out with 2.8 + */ + +/* + * These constants are determined KVM Inter-VM shared memory device + * register offsets + */ +enum { + INTR_MASK = 0x00, /* Interrupt Mask */ + INTR_STATUS = 0x04, /* Interrupt Status */ + IV_POSITION = 0x08, /* VM ID */ + DOORBELL = 0x0c, /* Doorbell */ +}; + +static const int REGISTER_BAR; /* Equal to 0 */ +static const int MAX_REGISTER_BAR_LEN = 0x100; +/* + * The MSI-x BAR is not used directly. + * + * static const int MSI_X_BAR = 1; + */ +static const int SHARED_MEMORY_BAR = 2; + +struct vsoc_region_data { + char name[VSOC_DEVICE_NAME_SZ + 1]; + wait_queue_head_t interrupt_wait_queue; + /* TODO(b/73664181): Use multiple futex wait queues */ + wait_queue_head_t futex_wait_queue; + /* Flag indicating that an interrupt has been signalled by the host. */ + atomic_t *incoming_signalled; + /* Flag indicating the guest has signalled the host. */ + atomic_t *outgoing_signalled; + int irq_requested; + int device_created; +}; + +struct vsoc_device { + /* Kernel virtual address of REGISTER_BAR. */ + void __iomem *regs; + /* Physical address of SHARED_MEMORY_BAR. */ + phys_addr_t shm_phys_start; + /* Kernel virtual address of SHARED_MEMORY_BAR. */ + void *kernel_mapped_shm; + /* Size of the entire shared memory window in bytes. */ + size_t shm_size; + /* + * Pointer to the virtual address of the shared memory layout structure. + * This is probably identical to kernel_mapped_shm, but saving this + * here saves a lot of annoying casts. + */ + struct vsoc_shm_layout_descriptor *layout; + /* + * Points to a table of region descriptors in the kernel's virtual + * address space. Calculated from + * vsoc_shm_layout_descriptor.vsoc_region_desc_offset + */ + struct vsoc_device_region *regions; + /* Head of a list of permissions that have been granted. */ + struct list_head permissions; + struct pci_dev *dev; + /* Per-region (and therefore per-interrupt) information. */ + struct vsoc_region_data *regions_data; + /* + * Table of msi-x entries. This has to be separated from struct + * vsoc_region_data because the kernel deals with them as an array. + */ + struct msix_entry *msix_entries; + /* + * Flags that indicate what we've initialzied. These are used to do an + * orderly cleanup of the device. + */ + char enabled_device; + char requested_regions; + char cdev_added; + char class_added; + char msix_enabled; + /* Mutex that protectes the permission list */ + struct mutex mtx; + /* Major number assigned by the kernel */ + int major; + + struct cdev cdev; + struct class *class; +}; + +static struct vsoc_device vsoc_dev; + +/* + * TODO(ghartman): Add a /sys filesystem entry that summarizes the permissions. + */ + +struct fd_scoped_permission_node { + struct fd_scoped_permission permission; + struct list_head list; +}; + +struct vsoc_private_data { + struct fd_scoped_permission_node *fd_scoped_permission_node; +}; + +static long vsoc_ioctl(struct file *, unsigned int, unsigned long); +static int vsoc_mmap(struct file *, struct vm_area_struct *); +static int vsoc_open(struct inode *, struct file *); +static int vsoc_release(struct inode *, struct file *); +static ssize_t vsoc_read(struct file *, char *, size_t, loff_t *); +static ssize_t vsoc_write(struct file *, const char *, size_t, loff_t *); +static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin); +static int do_create_fd_scoped_permission( + struct vsoc_device_region *region_p, + struct fd_scoped_permission_node *np, + struct fd_scoped_permission_arg *__user arg); +static void do_destroy_fd_scoped_permission( + struct vsoc_device_region *owner_region_p, + struct fd_scoped_permission *perm); +static long do_vsoc_describe_region(struct file *, + struct vsoc_device_region __user *); +static ssize_t vsoc_get_area(struct file *filp, __u32 *perm_off); + +/** + * Validate arguments on entry points to the driver. + */ +inline int vsoc_validate_inode(struct inode *inode) +{ + if (iminor(inode) >= vsoc_dev.layout->region_count) { + dev_err(&vsoc_dev.dev->dev, + "describe_region: invalid region %d\n", iminor(inode)); + return -ENODEV; + } + return 0; +} + +inline int vsoc_validate_filep(struct file *filp) +{ + int ret = vsoc_validate_inode(file_inode(filp)); + + if (ret) + return ret; + if (!filp->private_data) { + dev_err(&vsoc_dev.dev->dev, + "No private data on fd, region %d\n", + iminor(file_inode(filp))); + return -EBADFD; + } + return 0; +} + +/* Converts from shared memory offset to virtual address */ +static inline void *shm_off_to_virtual_addr(__u32 offset) +{ + return vsoc_dev.kernel_mapped_shm + offset; +} + +/* Converts from shared memory offset to physical address */ +static inline phys_addr_t shm_off_to_phys_addr(__u32 offset) +{ + return vsoc_dev.shm_phys_start + offset; +} + +/** + * Convenience functions to obtain the region from the inode or file. + * Dangerous to call before validating the inode/file. + */ +static inline struct vsoc_device_region *vsoc_region_from_inode( + struct inode *inode) +{ + return &vsoc_dev.regions[iminor(inode)]; +} + +static inline struct vsoc_device_region *vsoc_region_from_filep( + struct file *inode) +{ + return vsoc_region_from_inode(file_inode(inode)); +} + +static inline uint32_t vsoc_device_region_size(struct vsoc_device_region *r) +{ + return r->region_end_offset - r->region_begin_offset; +} + +static const struct file_operations vsoc_ops = { + .owner = THIS_MODULE, + .open = vsoc_open, + .mmap = vsoc_mmap, + .read = vsoc_read, + .unlocked_ioctl = vsoc_ioctl, + .compat_ioctl = vsoc_ioctl, + .write = vsoc_write, + .llseek = vsoc_lseek, + .release = vsoc_release, +}; + +static struct pci_device_id vsoc_id_table[] = { + {0x1af4, 0x1110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0}, +}; + +MODULE_DEVICE_TABLE(pci, vsoc_id_table); + +static void vsoc_remove_device(struct pci_dev *pdev); +static int vsoc_probe_device(struct pci_dev *pdev, + const struct pci_device_id *ent); + +static struct pci_driver vsoc_pci_driver = { + .name = "vsoc", + .id_table = vsoc_id_table, + .probe = vsoc_probe_device, + .remove = vsoc_remove_device, +}; + +static int do_create_fd_scoped_permission( + struct vsoc_device_region *region_p, + struct fd_scoped_permission_node *np, + struct fd_scoped_permission_arg *__user arg) +{ + struct file *managed_filp; + s32 managed_fd; + atomic_t *owner_ptr = NULL; + struct vsoc_device_region *managed_region_p; + + if (copy_from_user(&np->permission, &arg->perm, sizeof(*np)) || + copy_from_user(&managed_fd, + &arg->managed_region_fd, sizeof(managed_fd))) { + return -EFAULT; + } + managed_filp = fdget(managed_fd).file; + /* Check that it's a valid fd, */ + if (!managed_filp || vsoc_validate_filep(managed_filp)) + return -EPERM; + /* EEXIST if the given fd already has a permission. */ + if (((struct vsoc_private_data *)managed_filp->private_data)-> + fd_scoped_permission_node) + return -EEXIST; + managed_region_p = vsoc_region_from_filep(managed_filp); + /* Check that the provided region is managed by this one */ + if (&vsoc_dev.regions[managed_region_p->managed_by] != region_p) + return -EPERM; + /* The area must be well formed and have non-zero size */ + if (np->permission.begin_offset >= np->permission.end_offset) + return -EINVAL; + /* The area must fit in the memory window */ + if (np->permission.end_offset > + vsoc_device_region_size(managed_region_p)) + return -ERANGE; + /* The area must be in the region data section */ + if (np->permission.begin_offset < + managed_region_p->offset_of_region_data) + return -ERANGE; + /* The area must be page aligned */ + if (!PAGE_ALIGNED(np->permission.begin_offset) || + !PAGE_ALIGNED(np->permission.end_offset)) + return -EINVAL; + /* Owner offset must be naturally aligned in the window */ + if (np->permission.owner_offset & + (sizeof(np->permission.owner_offset) - 1)) + return -EINVAL; + /* The owner flag must reside in the owner memory */ + if (np->permission.owner_offset + sizeof(np->permission.owner_offset) > + vsoc_device_region_size(region_p)) + return -ERANGE; + /* The owner flag must reside in the data section */ + if (np->permission.owner_offset < region_p->offset_of_region_data) + return -EINVAL; + /* The owner value must change to claim the memory */ + if (np->permission.owned_value == VSOC_REGION_FREE) + return -EINVAL; + owner_ptr = + (atomic_t *)shm_off_to_virtual_addr(region_p->region_begin_offset + + np->permission.owner_offset); + /* We've already verified that this is in the shared memory window, so + * it should be safe to write to this address. + */ + if (atomic_cmpxchg(owner_ptr, + VSOC_REGION_FREE, + np->permission.owned_value) != VSOC_REGION_FREE) { + return -EBUSY; + } + ((struct vsoc_private_data *)managed_filp->private_data)-> + fd_scoped_permission_node = np; + /* The file offset needs to be adjusted if the calling + * process did any read/write operations on the fd + * before creating the permission. + */ + if (managed_filp->f_pos) { + if (managed_filp->f_pos > np->permission.end_offset) { + /* If the offset is beyond the permission end, set it + * to the end. + */ + managed_filp->f_pos = np->permission.end_offset; + } else { + /* If the offset is within the permission interval + * keep it there otherwise reset it to zero. + */ + if (managed_filp->f_pos < np->permission.begin_offset) { + managed_filp->f_pos = 0; + } else { + managed_filp->f_pos -= + np->permission.begin_offset; + } + } + } + return 0; +} + +static void do_destroy_fd_scoped_permission_node( + struct vsoc_device_region *owner_region_p, + struct fd_scoped_permission_node *node) +{ + if (node) { + do_destroy_fd_scoped_permission(owner_region_p, + &node->permission); + mutex_lock(&vsoc_dev.mtx); + list_del(&node->list); + mutex_unlock(&vsoc_dev.mtx); + kfree(node); + } +} + +static void do_destroy_fd_scoped_permission( + struct vsoc_device_region *owner_region_p, + struct fd_scoped_permission *perm) +{ + atomic_t *owner_ptr = NULL; + int prev = 0; + + if (!perm) + return; + owner_ptr = (atomic_t *)shm_off_to_virtual_addr( + owner_region_p->region_begin_offset + perm->owner_offset); + prev = atomic_xchg(owner_ptr, VSOC_REGION_FREE); + if (prev != perm->owned_value) + dev_err(&vsoc_dev.dev->dev, + "%x-%x: owner (%s) %x: expected to be %x was %x", + perm->begin_offset, perm->end_offset, + owner_region_p->device_name, perm->owner_offset, + perm->owned_value, prev); +} + +static long do_vsoc_describe_region(struct file *filp, + struct vsoc_device_region __user *dest) +{ + struct vsoc_device_region *region_p; + int retval = vsoc_validate_filep(filp); + + if (retval) + return retval; + region_p = vsoc_region_from_filep(filp); + if (copy_to_user(dest, region_p, sizeof(*region_p))) + return -EFAULT; + return 0; +} + +/** + * Implements the inner logic of cond_wait. Copies to and from userspace are + * done in the helper function below. + */ +static int handle_vsoc_cond_wait(struct file *filp, struct vsoc_cond_wait *arg) +{ + DEFINE_WAIT(wait); + u32 region_number = iminor(file_inode(filp)); + struct vsoc_region_data *data = vsoc_dev.regions_data + region_number; + struct hrtimer_sleeper timeout, *to = NULL; + int ret = 0; + struct vsoc_device_region *region_p = vsoc_region_from_filep(filp); + atomic_t *address = NULL; + struct timespec ts; + + /* Ensure that the offset is aligned */ + if (arg->offset & (sizeof(uint32_t) - 1)) + return -EADDRNOTAVAIL; + /* Ensure that the offset is within shared memory */ + if (((uint64_t)arg->offset) + region_p->region_begin_offset + + sizeof(uint32_t) > region_p->region_end_offset) + return -E2BIG; + address = shm_off_to_virtual_addr(region_p->region_begin_offset + + arg->offset); + + /* Ensure that the type of wait is valid */ + switch (arg->wait_type) { + case VSOC_WAIT_IF_EQUAL: + break; + case VSOC_WAIT_IF_EQUAL_TIMEOUT: + to = &timeout; + break; + default: + return -EINVAL; + } + + if (to) { + /* Copy the user-supplied timesec into the kernel structure. + * We do things this way to flatten differences between 32 bit + * and 64 bit timespecs. + */ + ts.tv_sec = arg->wake_time_sec; + ts.tv_nsec = arg->wake_time_nsec; + + if (!timespec_valid(&ts)) + return -EINVAL; + hrtimer_init_on_stack(&to->timer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS); + hrtimer_set_expires_range_ns(&to->timer, timespec_to_ktime(ts), + current->timer_slack_ns); + + hrtimer_init_sleeper(to, current); + } + + while (1) { + prepare_to_wait(&data->futex_wait_queue, &wait, + TASK_INTERRUPTIBLE); + /* + * Check the sentinel value after prepare_to_wait. If the value + * changes after this check the writer will call signal, + * changing the task state from INTERRUPTIBLE to RUNNING. That + * will ensure that schedule() will eventually schedule this + * task. + */ + if (atomic_read(address) != arg->value) { + ret = 0; + break; + } + if (to) { + hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS); + if (likely(to->task)) + freezable_schedule(); + hrtimer_cancel(&to->timer); + if (!to->task) { + ret = -ETIMEDOUT; + break; + } + } else { + freezable_schedule(); + } + /* Count the number of times that we woke up. This is useful + * for unit testing. + */ + ++arg->wakes; + if (signal_pending(current)) { + ret = -EINTR; + break; + } + } + finish_wait(&data->futex_wait_queue, &wait); + if (to) + destroy_hrtimer_on_stack(&to->timer); + return ret; +} + +/** + * Handles the details of copying from/to userspace to ensure that the copies + * happen on all of the return paths of cond_wait. + */ +static int do_vsoc_cond_wait(struct file *filp, + struct vsoc_cond_wait __user *untrusted_in) +{ + struct vsoc_cond_wait arg; + int rval = 0; + + if (copy_from_user(&arg, untrusted_in, sizeof(arg))) + return -EFAULT; + /* wakes is an out parameter. Initialize it to something sensible. */ + arg.wakes = 0; + rval = handle_vsoc_cond_wait(filp, &arg); + if (copy_to_user(untrusted_in, &arg, sizeof(arg))) + return -EFAULT; + return rval; +} + +static int do_vsoc_cond_wake(struct file *filp, uint32_t offset) +{ + struct vsoc_device_region *region_p = vsoc_region_from_filep(filp); + u32 region_number = iminor(file_inode(filp)); + struct vsoc_region_data *data = vsoc_dev.regions_data + region_number; + /* Ensure that the offset is aligned */ + if (offset & (sizeof(uint32_t) - 1)) + return -EADDRNOTAVAIL; + /* Ensure that the offset is within shared memory */ + if (((uint64_t)offset) + region_p->region_begin_offset + + sizeof(uint32_t) > region_p->region_end_offset) + return -E2BIG; + /* + * TODO(b/73664181): Use multiple futex wait queues. + * We need to wake every sleeper when the condition changes. Typically + * only a single thread will be waiting on the condition, but there + * are exceptions. The worst case is about 10 threads. + */ + wake_up_interruptible_all(&data->futex_wait_queue); + return 0; +} + +static long vsoc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int rv = 0; + struct vsoc_device_region *region_p; + u32 reg_num; + struct vsoc_region_data *reg_data; + int retval = vsoc_validate_filep(filp); + + if (retval) + return retval; + region_p = vsoc_region_from_filep(filp); + reg_num = iminor(file_inode(filp)); + reg_data = vsoc_dev.regions_data + reg_num; + switch (cmd) { + case VSOC_CREATE_FD_SCOPED_PERMISSION: + { + struct fd_scoped_permission_node *node = NULL; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + /* We can't allocate memory for the permission */ + if (!node) + return -ENOMEM; + INIT_LIST_HEAD(&node->list); + rv = do_create_fd_scoped_permission( + region_p, + node, + (struct fd_scoped_permission_arg __user *)arg); + if (!rv) { + mutex_lock(&vsoc_dev.mtx); + list_add(&node->list, &vsoc_dev.permissions); + mutex_unlock(&vsoc_dev.mtx); + } else { + kfree(node); + return rv; + } + } + break; + + case VSOC_GET_FD_SCOPED_PERMISSION: + { + struct fd_scoped_permission_node *node = + ((struct vsoc_private_data *)filp->private_data)-> + fd_scoped_permission_node; + if (!node) + return -ENOENT; + if (copy_to_user + ((struct fd_scoped_permission __user *)arg, + &node->permission, sizeof(node->permission))) + return -EFAULT; + } + break; + + case VSOC_MAYBE_SEND_INTERRUPT_TO_HOST: + if (!atomic_xchg( + reg_data->outgoing_signalled, + 1)) { + writel(reg_num, vsoc_dev.regs + DOORBELL); + return 0; + } else { + return -EBUSY; + } + break; + + case VSOC_SEND_INTERRUPT_TO_HOST: + writel(reg_num, vsoc_dev.regs + DOORBELL); + return 0; + + case VSOC_WAIT_FOR_INCOMING_INTERRUPT: + wait_event_interruptible( + reg_data->interrupt_wait_queue, + (atomic_read(reg_data->incoming_signalled) != 0)); + break; + + case VSOC_DESCRIBE_REGION: + return do_vsoc_describe_region( + filp, + (struct vsoc_device_region __user *)arg); + + case VSOC_SELF_INTERRUPT: + atomic_set(reg_data->incoming_signalled, 1); + wake_up_interruptible(®_data->interrupt_wait_queue); + break; + + case VSOC_COND_WAIT: + return do_vsoc_cond_wait(filp, + (struct vsoc_cond_wait __user *)arg); + case VSOC_COND_WAKE: + return do_vsoc_cond_wake(filp, arg); + + default: + return -EINVAL; + } + return 0; +} + +static ssize_t vsoc_read(struct file *filp, char *buffer, size_t len, + loff_t *poffset) +{ + __u32 area_off; + void *area_p; + ssize_t area_len; + int retval = vsoc_validate_filep(filp); + + if (retval) + return retval; + area_len = vsoc_get_area(filp, &area_off); + area_p = shm_off_to_virtual_addr(area_off); + area_p += *poffset; + area_len -= *poffset; + if (area_len <= 0) + return 0; + if (area_len < len) + len = area_len; + if (copy_to_user(buffer, area_p, len)) + return -EFAULT; + *poffset += len; + return len; +} + +static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin) +{ + ssize_t area_len = 0; + int retval = vsoc_validate_filep(filp); + + if (retval) + return retval; + area_len = vsoc_get_area(filp, NULL); + switch (origin) { + case SEEK_SET: + break; + + case SEEK_CUR: + if (offset > 0 && offset + filp->f_pos < 0) + return -EOVERFLOW; + offset += filp->f_pos; + break; + + case SEEK_END: + if (offset > 0 && offset + area_len < 0) + return -EOVERFLOW; + offset += area_len; + break; + + case SEEK_DATA: + if (offset >= area_len) + return -EINVAL; + if (offset < 0) + offset = 0; + break; + + case SEEK_HOLE: + /* Next hole is always the end of the region, unless offset is + * beyond that + */ + if (offset < area_len) + offset = area_len; + break; + + default: + return -EINVAL; + } + + if (offset < 0 || offset > area_len) + return -EINVAL; + filp->f_pos = offset; + + return offset; +} + +static ssize_t vsoc_write(struct file *filp, const char *buffer, + size_t len, loff_t *poffset) +{ + __u32 area_off; + void *area_p; + ssize_t area_len; + int retval = vsoc_validate_filep(filp); + + if (retval) + return retval; + area_len = vsoc_get_area(filp, &area_off); + area_p = shm_off_to_virtual_addr(area_off); + area_p += *poffset; + area_len -= *poffset; + if (area_len <= 0) + return 0; + if (area_len < len) + len = area_len; + if (copy_from_user(area_p, buffer, len)) + return -EFAULT; + *poffset += len; + return len; +} + +static irqreturn_t vsoc_interrupt(int irq, void *region_data_v) +{ + struct vsoc_region_data *region_data = + (struct vsoc_region_data *)region_data_v; + int reg_num = region_data - vsoc_dev.regions_data; + + if (unlikely(!region_data)) + return IRQ_NONE; + + if (unlikely(reg_num < 0 || + reg_num >= vsoc_dev.layout->region_count)) { + dev_err(&vsoc_dev.dev->dev, + "invalid irq @%p reg_num=0x%04x\n", + region_data, reg_num); + return IRQ_NONE; + } + if (unlikely(vsoc_dev.regions_data + reg_num != region_data)) { + dev_err(&vsoc_dev.dev->dev, + "irq not aligned @%p reg_num=0x%04x\n", + region_data, reg_num); + return IRQ_NONE; + } + wake_up_interruptible(®ion_data->interrupt_wait_queue); + return IRQ_HANDLED; +} + +static int vsoc_probe_device(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int result; + int i; + resource_size_t reg_size; + dev_t devt; + + vsoc_dev.dev = pdev; + result = pci_enable_device(pdev); + if (result) { + dev_err(&pdev->dev, + "pci_enable_device failed %s: error %d\n", + pci_name(pdev), result); + return result; + } + vsoc_dev.enabled_device = 1; + result = pci_request_regions(pdev, "vsoc"); + if (result < 0) { + dev_err(&pdev->dev, "pci_request_regions failed\n"); + vsoc_remove_device(pdev); + return -EBUSY; + } + vsoc_dev.requested_regions = 1; + /* Set up the control registers in BAR 0 */ + reg_size = pci_resource_len(pdev, REGISTER_BAR); + if (reg_size > MAX_REGISTER_BAR_LEN) + vsoc_dev.regs = + pci_iomap(pdev, REGISTER_BAR, MAX_REGISTER_BAR_LEN); + else + vsoc_dev.regs = pci_iomap(pdev, REGISTER_BAR, reg_size); + + if (!vsoc_dev.regs) { + dev_err(&pdev->dev, + "cannot ioremap registers of size %zu\n", + (size_t)reg_size); + vsoc_remove_device(pdev); + return -EBUSY; + } + + /* Map the shared memory in BAR 2 */ + vsoc_dev.shm_phys_start = pci_resource_start(pdev, SHARED_MEMORY_BAR); + vsoc_dev.shm_size = pci_resource_len(pdev, SHARED_MEMORY_BAR); + + dev_info(&pdev->dev, "shared memory @ DMA %p size=0x%zx\n", + (void *)vsoc_dev.shm_phys_start, vsoc_dev.shm_size); + /* TODO(ghartman): ioremap_wc should work here */ + vsoc_dev.kernel_mapped_shm = ioremap_nocache( + vsoc_dev.shm_phys_start, vsoc_dev.shm_size); + if (!vsoc_dev.kernel_mapped_shm) { + dev_err(&vsoc_dev.dev->dev, "cannot iomap region\n"); + vsoc_remove_device(pdev); + return -EBUSY; + } + + vsoc_dev.layout = + (struct vsoc_shm_layout_descriptor *)vsoc_dev.kernel_mapped_shm; + dev_info(&pdev->dev, "major_version: %d\n", + vsoc_dev.layout->major_version); + dev_info(&pdev->dev, "minor_version: %d\n", + vsoc_dev.layout->minor_version); + dev_info(&pdev->dev, "size: 0x%x\n", vsoc_dev.layout->size); + dev_info(&pdev->dev, "regions: %d\n", vsoc_dev.layout->region_count); + if (vsoc_dev.layout->major_version != + CURRENT_VSOC_LAYOUT_MAJOR_VERSION) { + dev_err(&vsoc_dev.dev->dev, + "driver supports only major_version %d\n", + CURRENT_VSOC_LAYOUT_MAJOR_VERSION); + vsoc_remove_device(pdev); + return -EBUSY; + } + result = alloc_chrdev_region(&devt, 0, vsoc_dev.layout->region_count, + VSOC_DEV_NAME); + if (result) { + dev_err(&vsoc_dev.dev->dev, "alloc_chrdev_region failed\n"); + vsoc_remove_device(pdev); + return -EBUSY; + } + vsoc_dev.major = MAJOR(devt); + cdev_init(&vsoc_dev.cdev, &vsoc_ops); + vsoc_dev.cdev.owner = THIS_MODULE; + result = cdev_add(&vsoc_dev.cdev, devt, vsoc_dev.layout->region_count); + if (result) { + dev_err(&vsoc_dev.dev->dev, "cdev_add error\n"); + vsoc_remove_device(pdev); + return -EBUSY; + } + vsoc_dev.cdev_added = 1; + vsoc_dev.class = class_create(THIS_MODULE, VSOC_DEV_NAME); + if (IS_ERR(vsoc_dev.class)) { + dev_err(&vsoc_dev.dev->dev, "class_create failed\n"); + vsoc_remove_device(pdev); + return PTR_ERR(vsoc_dev.class); + } + vsoc_dev.class_added = 1; + vsoc_dev.regions = (struct vsoc_device_region *) + (vsoc_dev.kernel_mapped_shm + + vsoc_dev.layout->vsoc_region_desc_offset); + vsoc_dev.msix_entries = kcalloc( + vsoc_dev.layout->region_count, + sizeof(vsoc_dev.msix_entries[0]), GFP_KERNEL); + if (!vsoc_dev.msix_entries) { + dev_err(&vsoc_dev.dev->dev, + "unable to allocate msix_entries\n"); + vsoc_remove_device(pdev); + return -ENOSPC; + } + vsoc_dev.regions_data = kcalloc( + vsoc_dev.layout->region_count, + sizeof(vsoc_dev.regions_data[0]), GFP_KERNEL); + if (!vsoc_dev.regions_data) { + dev_err(&vsoc_dev.dev->dev, + "unable to allocate regions' data\n"); + vsoc_remove_device(pdev); + return -ENOSPC; + } + for (i = 0; i < vsoc_dev.layout->region_count; ++i) + vsoc_dev.msix_entries[i].entry = i; + + result = pci_enable_msix_exact(vsoc_dev.dev, vsoc_dev.msix_entries, + vsoc_dev.layout->region_count); + if (result) { + dev_info(&pdev->dev, "pci_enable_msix failed: %d\n", result); + vsoc_remove_device(pdev); + return -ENOSPC; + } + /* Check that all regions are well formed */ + for (i = 0; i < vsoc_dev.layout->region_count; ++i) { + const struct vsoc_device_region *region = vsoc_dev.regions + i; + + if (!PAGE_ALIGNED(region->region_begin_offset) || + !PAGE_ALIGNED(region->region_end_offset)) { + dev_err(&vsoc_dev.dev->dev, + "region %d not aligned (%x:%x)", i, + region->region_begin_offset, + region->region_end_offset); + vsoc_remove_device(pdev); + return -EFAULT; + } + if (region->region_begin_offset >= region->region_end_offset || + region->region_end_offset > vsoc_dev.shm_size) { + dev_err(&vsoc_dev.dev->dev, + "region %d offsets are wrong: %x %x %zx", + i, region->region_begin_offset, + region->region_end_offset, vsoc_dev.shm_size); + vsoc_remove_device(pdev); + return -EFAULT; + } + if (region->managed_by >= vsoc_dev.layout->region_count) { + dev_err(&vsoc_dev.dev->dev, + "region %d has invalid owner: %u", + i, region->managed_by); + vsoc_remove_device(pdev); + return -EFAULT; + } + } + vsoc_dev.msix_enabled = 1; + for (i = 0; i < vsoc_dev.layout->region_count; ++i) { + const struct vsoc_device_region *region = vsoc_dev.regions + i; + size_t name_sz = sizeof(vsoc_dev.regions_data[i].name) - 1; + const struct vsoc_signal_table_layout *h_to_g_signal_table = + ®ion->host_to_guest_signal_table; + const struct vsoc_signal_table_layout *g_to_h_signal_table = + ®ion->guest_to_host_signal_table; + + vsoc_dev.regions_data[i].name[name_sz] = '\0'; + memcpy(vsoc_dev.regions_data[i].name, region->device_name, + name_sz); + dev_info(&pdev->dev, "region %d name=%s\n", + i, vsoc_dev.regions_data[i].name); + init_waitqueue_head( + &vsoc_dev.regions_data[i].interrupt_wait_queue); + init_waitqueue_head(&vsoc_dev.regions_data[i].futex_wait_queue); + vsoc_dev.regions_data[i].incoming_signalled = + vsoc_dev.kernel_mapped_shm + + region->region_begin_offset + + h_to_g_signal_table->interrupt_signalled_offset; + vsoc_dev.regions_data[i].outgoing_signalled = + vsoc_dev.kernel_mapped_shm + + region->region_begin_offset + + g_to_h_signal_table->interrupt_signalled_offset; + + result = request_irq( + vsoc_dev.msix_entries[i].vector, + vsoc_interrupt, 0, + vsoc_dev.regions_data[i].name, + vsoc_dev.regions_data + i); + if (result) { + dev_info(&pdev->dev, + "request_irq failed irq=%d vector=%d\n", + i, vsoc_dev.msix_entries[i].vector); + vsoc_remove_device(pdev); + return -ENOSPC; + } + vsoc_dev.regions_data[i].irq_requested = 1; + if (!device_create(vsoc_dev.class, NULL, + MKDEV(vsoc_dev.major, i), + NULL, vsoc_dev.regions_data[i].name)) { + dev_err(&vsoc_dev.dev->dev, "device_create failed\n"); + vsoc_remove_device(pdev); + return -EBUSY; + } + vsoc_dev.regions_data[i].device_created = 1; + } + return 0; +} + +/* + * This should undo all of the allocations in the probe function in reverse + * order. + * + * Notes: + * + * The device may have been partially initialized, so double check + * that the allocations happened. + * + * This function may be called multiple times, so mark resources as freed + * as they are deallocated. + */ +static void vsoc_remove_device(struct pci_dev *pdev) +{ + int i; + /* + * pdev is the first thing to be set on probe and the last thing + * to be cleared here. If it's NULL then there is no cleanup. + */ + if (!pdev || !vsoc_dev.dev) + return; + dev_info(&pdev->dev, "remove_device\n"); + if (vsoc_dev.regions_data) { + for (i = 0; i < vsoc_dev.layout->region_count; ++i) { + if (vsoc_dev.regions_data[i].device_created) { + device_destroy(vsoc_dev.class, + MKDEV(vsoc_dev.major, i)); + vsoc_dev.regions_data[i].device_created = 0; + } + if (vsoc_dev.regions_data[i].irq_requested) + free_irq(vsoc_dev.msix_entries[i].vector, NULL); + vsoc_dev.regions_data[i].irq_requested = 0; + } + kfree(vsoc_dev.regions_data); + vsoc_dev.regions_data = 0; + } + if (vsoc_dev.msix_enabled) { + pci_disable_msix(pdev); + vsoc_dev.msix_enabled = 0; + } + kfree(vsoc_dev.msix_entries); + vsoc_dev.msix_entries = 0; + vsoc_dev.regions = 0; + if (vsoc_dev.class_added) { + class_destroy(vsoc_dev.class); + vsoc_dev.class_added = 0; + } + if (vsoc_dev.cdev_added) { + cdev_del(&vsoc_dev.cdev); + vsoc_dev.cdev_added = 0; + } + if (vsoc_dev.major && vsoc_dev.layout) { + unregister_chrdev_region(MKDEV(vsoc_dev.major, 0), + vsoc_dev.layout->region_count); + vsoc_dev.major = 0; + } + vsoc_dev.layout = 0; + if (vsoc_dev.kernel_mapped_shm) { + pci_iounmap(pdev, vsoc_dev.kernel_mapped_shm); + vsoc_dev.kernel_mapped_shm = 0; + } + if (vsoc_dev.regs) { + pci_iounmap(pdev, vsoc_dev.regs); + vsoc_dev.regs = 0; + } + if (vsoc_dev.requested_regions) { + pci_release_regions(pdev); + vsoc_dev.requested_regions = 0; + } + if (vsoc_dev.enabled_device) { + pci_disable_device(pdev); + vsoc_dev.enabled_device = 0; + } + /* Do this last: it indicates that the device is not initialized. */ + vsoc_dev.dev = NULL; +} + +static void __exit vsoc_cleanup_module(void) +{ + vsoc_remove_device(vsoc_dev.dev); + pci_unregister_driver(&vsoc_pci_driver); +} + +static int __init vsoc_init_module(void) +{ + int err = -ENOMEM; + + INIT_LIST_HEAD(&vsoc_dev.permissions); + mutex_init(&vsoc_dev.mtx); + + err = pci_register_driver(&vsoc_pci_driver); + if (err < 0) + return err; + return 0; +} + +static int vsoc_open(struct inode *inode, struct file *filp) +{ + /* Can't use vsoc_validate_filep because filp is still incomplete */ + int ret = vsoc_validate_inode(inode); + + if (ret) + return ret; + filp->private_data = + kzalloc(sizeof(struct vsoc_private_data), GFP_KERNEL); + if (!filp->private_data) + return -ENOMEM; + return 0; +} + +static int vsoc_release(struct inode *inode, struct file *filp) +{ + struct vsoc_private_data *private_data = NULL; + struct fd_scoped_permission_node *node = NULL; + struct vsoc_device_region *owner_region_p = NULL; + int retval = vsoc_validate_filep(filp); + + if (retval) + return retval; + private_data = (struct vsoc_private_data *)filp->private_data; + if (!private_data) + return 0; + + node = private_data->fd_scoped_permission_node; + if (node) { + owner_region_p = vsoc_region_from_inode(inode); + if (owner_region_p->managed_by != VSOC_REGION_WHOLE) { + owner_region_p = + &vsoc_dev.regions[owner_region_p->managed_by]; + } + do_destroy_fd_scoped_permission_node(owner_region_p, node); + private_data->fd_scoped_permission_node = NULL; + } + kfree(private_data); + filp->private_data = NULL; + + return 0; +} + +/* + * Returns the device relative offset and length of the area specified by the + * fd scoped permission. If there is no fd scoped permission set, a default + * permission covering the entire region is assumed, unless the region is owned + * by another one, in which case the default is a permission with zero size. + */ +static ssize_t vsoc_get_area(struct file *filp, __u32 *area_offset) +{ + __u32 off = 0; + ssize_t length = 0; + struct vsoc_device_region *region_p; + struct fd_scoped_permission *perm; + + region_p = vsoc_region_from_filep(filp); + off = region_p->region_begin_offset; + perm = &((struct vsoc_private_data *)filp->private_data)-> + fd_scoped_permission_node->permission; + if (perm) { + off += perm->begin_offset; + length = perm->end_offset - perm->begin_offset; + } else if (region_p->managed_by == VSOC_REGION_WHOLE) { + /* No permission set and the regions is not owned by another, + * default to full region access. + */ + length = vsoc_device_region_size(region_p); + } else { + /* return zero length, access is denied. */ + length = 0; + } + if (area_offset) + *area_offset = off; + return length; +} + +static int vsoc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long len = vma->vm_end - vma->vm_start; + __u32 area_off; + phys_addr_t mem_off; + ssize_t area_len; + int retval = vsoc_validate_filep(filp); + + if (retval) + return retval; + area_len = vsoc_get_area(filp, &area_off); + /* Add the requested offset */ + area_off += (vma->vm_pgoff << PAGE_SHIFT); + area_len -= (vma->vm_pgoff << PAGE_SHIFT); + if (area_len < len) + return -EINVAL; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + mem_off = shm_off_to_phys_addr(area_off); + if (io_remap_pfn_range(vma, vma->vm_start, mem_off >> PAGE_SHIFT, + len, vma->vm_page_prot)) + return -EAGAIN; + return 0; +} + +module_init(vsoc_init_module); +module_exit(vsoc_cleanup_module); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Hartman "); +MODULE_DESCRIPTION("VSoC interpretation of QEmu's ivshmem device"); +MODULE_VERSION("1.0"); diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index 398347fedc476b3f186cbc38f4eb76271874b216..2cac160993bbd82f12246951e7db109b5fc043ac 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -1284,6 +1284,8 @@ static void ack_a_interrupt(struct comedi_device *dev, unsigned short a_status) ack |= NISTC_INTA_ACK_AI_START; if (a_status & NISTC_AI_STATUS1_STOP) ack |= NISTC_INTA_ACK_AI_STOP; + if (a_status & NISTC_AI_STATUS1_OVER) + ack |= NISTC_INTA_ACK_AI_ERR; if (ack) ni_stc_writew(dev, ack, NISTC_INTA_ACK_REG); } diff --git a/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c b/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c index 123e4af584081115bec627e49c57c3e315cb4e43..50260cb5056d9553c951ef4ca21640bd9e5681f6 100644 --- a/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c +++ b/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c @@ -75,6 +75,8 @@ int __init its_fsl_mc_msi_init(void) for (np = of_find_matching_node(NULL, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { + if (!of_device_is_available(np)) + continue; if (!of_property_read_bool(np, "msi-controller")) continue; diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c index 2da051c0d251cb6ba173b863fc33d54a2cb8a05a..a4bb93b440a51aa471e45a671086ed377a984e76 100644 --- a/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c +++ b/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c @@ -528,19 +528,20 @@ EXPORT_SYMBOL(cfs_cpt_spread_node); int cfs_cpt_current(struct cfs_cpt_table *cptab, int remap) { - int cpu = smp_processor_id(); - int cpt = cptab->ctb_cpu2cpt[cpu]; + int cpu; + int cpt; - if (cpt < 0) { - if (!remap) - return cpt; + preempt_disable(); + cpu = smp_processor_id(); + cpt = cptab->ctb_cpu2cpt[cpu]; + if (cpt < 0 && remap) { /* don't return negative value for safety of upper layer, * instead we shadow the unknown cpu to a valid partition ID */ cpt = cpu % cptab->ctb_nparts; } - + preempt_enable(); return cpt; } EXPORT_SYMBOL(cfs_cpt_current); diff --git a/drivers/staging/lustre/lustre/mdc/mdc_request.c b/drivers/staging/lustre/lustre/mdc/mdc_request.c index 6ef8ddec4ab691c47fea1d9386689601a5b52310..8c97acd792113dd59b1344bc196abd50290a04da 100644 --- a/drivers/staging/lustre/lustre/mdc/mdc_request.c +++ b/drivers/staging/lustre/lustre/mdc/mdc_request.c @@ -1121,9 +1121,9 @@ struct readpage_param { * in PAGE_SIZE (if PAGE_SIZE greater than LU_PAGE_SIZE), and the * lu_dirpage for this integrated page will be adjusted. **/ -static int mdc_read_page_remote(void *data, struct page *page0) +static int mdc_read_page_remote(struct file *data, struct page *page0) { - struct readpage_param *rp = data; + struct readpage_param *rp = (struct readpage_param *)data; struct page **page_pool; struct page *page; struct lu_dirpage *dp; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c index d8cfed358d55e15f34a377666e59a4faa8b7d339..f1d8cc5a27304e09bbe3ec605af3740a02117b25 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c @@ -1285,7 +1285,10 @@ const struct v4l2_file_operations atomisp_fops = { .mmap = atomisp_mmap, .unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT + /* + * There are problems with this code. Disable this for now. .compat_ioctl32 = atomisp_compat_ioctl32, + */ #endif .poll = atomisp_poll, }; @@ -1297,7 +1300,10 @@ const struct v4l2_file_operations atomisp_file_fops = { .mmap = atomisp_file_mmap, .unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT + /* + * There are problems with this code. Disable this for now. .compat_ioctl32 = atomisp_compat_ioctl32, + */ #endif .poll = atomisp_poll, }; diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 71af13bd0ebd179b61057254610b6d00eb187980..e35e1b2160e37e007b10a317284bbb8c27a068c3 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -288,7 +288,7 @@ static void release_ir_tx(struct kref *ref) struct IR_tx *tx = container_of(ref, struct IR_tx, ref); struct IR *ir = tx->ir; - ir->l.features &= ~LIRC_CAN_SEND_LIRCCODE; + ir->l.features &= ~LIRC_CAN_SEND_PULSE; /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */ ir->tx = NULL; kfree(tx); @@ -1228,6 +1228,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) dev_dbg(ir->l.dev, "%s result = %s\n", __func__, ret ? "POLLIN|POLLRDNORM" : "none"); + put_ir_rx(rx, false); return ret; } @@ -1267,14 +1268,14 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) if (!(features & LIRC_CAN_SEND_MASK)) return -ENOTTY; - result = put_user(LIRC_MODE_LIRCCODE, uptr); + result = put_user(LIRC_MODE_PULSE, uptr); break; case LIRC_SET_SEND_MODE: if (!(features & LIRC_CAN_SEND_MASK)) return -ENOTTY; result = get_user(mode, uptr); - if (!result && mode != LIRC_MODE_LIRCCODE) + if (!result && mode != LIRC_MODE_PULSE) return -EINVAL; break; default: @@ -1512,7 +1513,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) kref_init(&tx->ref); ir->tx = tx; - ir->l.features |= LIRC_CAN_SEND_LIRCCODE; + ir->l.features |= LIRC_CAN_SEND_PULSE; mutex_init(&tx->client_lock); tx->c = client; tx->need_boot = 1; diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 942d094269fba5db66ff7e791dcfaab1c6acec15..c4a5fb6f038fcf7567fa8a7bef2fadffb1fc5e25 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -796,6 +796,13 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) int ret; DEFINE_WAIT(__wait); + /* + * Don't leave commands partially setup because the unmap + * thread might need the blocks to make forward progress. + */ + tcmu_cmd_free_data(tcmu_cmd, tcmu_cmd->dbi_cur); + tcmu_cmd_reset_dbi_cur(tcmu_cmd); + prepare_to_wait(&udev->wait_cmdr, &__wait, TASK_INTERRUPTIBLE); pr_debug("sleeping for ring space\n"); diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 4798b4b1fd77f309e19cad3f627b728858b22b35..41c6154ae856c9d75fd20ba371f50c8f4a6346e1 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -601,6 +601,9 @@ static int imx_thermal_probe(struct platform_device *pdev) regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); + data->irq_enabled = true; + data->mode = THERMAL_DEVICE_ENABLED; + ret = devm_request_threaded_irq(&pdev->dev, data->irq, imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread, 0, "imx_thermal", data); @@ -613,9 +616,6 @@ static int imx_thermal_probe(struct platform_device *pdev) return ret; } - data->irq_enabled = true; - data->mode = THERMAL_DEVICE_ENABLED; - return 0; } diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 8ee38f55c7f36c5e244711bb45bc3171b4d32ba1..43b90fd577e49d86852760b0b917e0660a473c88 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -319,17 +319,21 @@ static int int3400_thermal_probe(struct platform_device *pdev) result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); if (result) - goto free_zone; + goto free_rel_misc; result = acpi_install_notify_handler( priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, (void *)priv); if (result) - goto free_zone; + goto free_sysfs; return 0; -free_zone: +free_sysfs: + sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); +free_rel_misc: + if (!priv->rel_misc_dev_res) + acpi_thermal_rel_misc_device_remove(priv->adev->handle); thermal_zone_device_unregister(priv->thermal); free_art_trt: kfree(priv->trts); diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index f304f798f01fc5e8a620b72a76747ca6ff03988b..dfbb3befbc998e35e6dab9b5df2e2be2da81fd17 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -81,6 +81,9 @@ static const struct of_device_id tsens_table[] = { { .compatible = "qcom,sdm630-tsens", .data = &data_tsens23xx, }, + { .compatible = "qcom,sm6150-tsens", + .data = &data_tsens23xx, + }, { .compatible = "qcom,sdm845-tsens", .data = &data_tsens24xx, }, diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c index b4d3116cfdafe81767b2b1c91fcab4a034f29041..3055f9a12a17087cfee105400b2cca3e68cbb166 100644 --- a/drivers/thermal/power_allocator.c +++ b/drivers/thermal/power_allocator.c @@ -523,6 +523,7 @@ static void allow_maximum_power(struct thermal_zone_device *tz) struct thermal_instance *instance; struct power_allocator_params *params = tz->governor_data; + mutex_lock(&tz->lock); list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if ((instance->trip != params->trip_max_desired_temperature) || (!cdev_is_power_actor(instance->cdev))) @@ -534,6 +535,7 @@ static void allow_maximum_power(struct thermal_zone_device *tz) mutex_unlock(&instance->cdev->lock); thermal_cdev_update(instance->cdev); } + mutex_unlock(&tz->lock); } /** diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index 5dd8869180ba328f6dcb35501b794cc824e3329e..f0385436f105c49d9fd41f1d0b75f52a34cfa3a2 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -93,3 +93,13 @@ config QTI_BCL_SOC_DRIVER threshold and notify the thermal framework. If you want this support, you should say Y here. + +config QTI_ADC_TM + tristate "Qualcomm Technologies Inc. Thermal Monitor ADC Driver" + depends on SPMI && THERMAL + depends on QCOM_SPMI_ADC5 + help + This enables the thermal Sysfs driver for the ADC thermal monitoring + device. It shows up in Sysfs as a thermal zone with multiple trip points. + Thermal client sets threshold temperature for both warm and cool + and gets updated when a threshold is reached. diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index aa6535bb0cda2d13fff09b7aa84dae53702832ac..e7e88d95d2b293a715fc28179274b493183b93ab 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_QTI_QMI_COOLING_DEVICE) += thermal_mitigation_device_service_v01.o obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o +obj-$(CONFIG_QTI_ADC_TM) += adc-tm.o adc-tm-common.o adc-tm5.o diff --git a/drivers/thermal/qcom/adc-tm-common.c b/drivers/thermal/qcom/adc-tm-common.c new file mode 100644 index 0000000000000000000000000000000000000000..583bd4ec0e0e04e613fb26e53f22e7823e551e11 --- /dev/null +++ b/drivers/thermal/qcom/adc-tm-common.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018, 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 +#include "adc-tm.h" + +/* + * Voltage to temperature table for NTCG104EF104 thermistor with + * 1.875V reference and 100k pull-up. + */ +static const struct adc_tm_map_pt adcmap_100k_104ef_104fb_1875_vref[] = { + { 1831, -40000 }, + { 1814, -35000 }, + { 1791, -30000 }, + { 1761, -25000 }, + { 1723, -20000 }, + { 1675, -15000 }, + { 1616, -10000 }, + { 1545, -5000 }, + { 1463, 0 }, + { 1370, 5000 }, + { 1268, 10000 }, + { 1160, 15000 }, + { 1049, 20000 }, + { 937, 25000 }, + { 828, 30000 }, + { 726, 35000 }, + { 630, 40000 }, + { 544, 45000 }, + { 467, 50000 }, + { 399, 55000 }, + { 340, 60000 }, + { 290, 65000 }, + { 247, 70000 }, + { 209, 75000 }, + { 179, 80000 }, + { 153, 85000 }, + { 130, 90000 }, + { 112, 95000 }, + { 96, 100000 }, + { 82, 105000 }, + { 71, 110000 }, + { 62, 115000 }, + { 53, 120000 }, + { 46, 125000 }, +}; + +static void adc_tm_map_temp_voltage(const struct adc_tm_map_pt *pts, + size_t tablesize, int input, int64_t *output) +{ + bool descending = true; + unsigned int i = 0; + + /* Check if table is descending or ascending */ + if (tablesize > 1) { + if (pts[0].y < pts[1].y) + descending = 0; + } + + while (i < tablesize) { + if (descending && (pts[i].y < input)) { + /* + * Table entry is less than measured value. + * Table is descending, stop. + */ + break; + } else if (!descending && (pts[i].y > input)) { + /* + * Table entry is greater than measured value. + * Table is ascending, stop. + */ + break; + } + i++; + } + + if (i == 0) { + *output = pts[0].x; + } else if (i == tablesize) { + *output = pts[tablesize-1].x; + } else { + /* + * Result is between search_index and search_index-1. + * Interpolate linearly. + */ + *output = (((int32_t) ((pts[i].x - pts[i-1].x) * + (input - pts[i-1].y)) / + (pts[i].y - pts[i-1].y)) + + pts[i-1].x); + } +} + +void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, + const struct adc_tm_data *data) +{ + uint32_t adc_hc_vdd_ref_mv = 1875; + + /* High temperature maps to lower threshold voltage */ + adc_tm_map_temp_voltage( + adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + param->high_thr_temp, ¶m->low_thr_voltage); + + param->low_thr_voltage *= data->full_scale_code_volt; + param->low_thr_voltage = div64_s64(param->low_thr_voltage, + adc_hc_vdd_ref_mv); + + /* Low temperature maps to higher threshold voltage */ + adc_tm_map_temp_voltage( + adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + param->low_thr_temp, ¶m->high_thr_voltage); + + param->high_thr_voltage *= data->full_scale_code_volt; + param->high_thr_voltage = div64_s64(param->high_thr_voltage, + adc_hc_vdd_ref_mv); + +} +EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k); + +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/qcom/adc-tm.c b/drivers/thermal/qcom/adc-tm.c new file mode 100644 index 0000000000000000000000000000000000000000..f2a800ba20b8d82d886f472003fc6733348d62dc --- /dev/null +++ b/drivers/thermal/qcom/adc-tm.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "adc-tm.h" + +LIST_HEAD(adc_tm_device_list); + +static int adc_tm_get_temp(void *data, int *temp) +{ + struct adc_tm_sensor *s = data; + struct adc_tm_chip *adc_tm = s->chip; + + return adc_tm->ops->get_temp(s, temp); +} + +static int adc_tm_set_trip_temp(void *data, int low_temp, int high_temp) +{ + struct adc_tm_sensor *s = data; + struct adc_tm_chip *adc_tm = s->chip; + + if (adc_tm->ops->set_trips) + return adc_tm->ops->set_trips(s, low_temp, high_temp); + + return 0; +} + +static int adc_tm_register_interrupts(struct adc_tm_chip *adc_tm) +{ + if (adc_tm->ops->interrupts_reg) + return adc_tm->ops->interrupts_reg(adc_tm); + + return 0; +} + +static int adc_tm_init(struct adc_tm_chip *adc_tm, uint32_t dt_chans) +{ + if (adc_tm->ops->init) + return adc_tm->ops->init(adc_tm, dt_chans); + + return 0; +} + +static struct thermal_zone_of_device_ops adc_tm_ops = { + .get_temp = adc_tm_get_temp, + .set_trips = adc_tm_set_trip_temp, +}; + +static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num) +{ + unsigned int i; + struct thermal_zone_device *tzd; + + for (i = 0; i < dt_chan_num; i++) { + adc_tm->sensor[i].chip = adc_tm; + tzd = devm_thermal_zone_of_sensor_register(adc_tm->dev, + adc_tm->sensor[i].adc_ch, + &adc_tm->sensor[i], + &adc_tm_ops); + if (IS_ERR(tzd)) { + pr_err("Error registering TZ zone:%d for dt_ch:%d\n", + PTR_ERR(tzd), adc_tm->sensor[i].adc_ch); + continue; + } + adc_tm->sensor[i].tzd = tzd; + } + + return 0; +} + +static int adc_tm_avg_samples_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value > ADC_TM_AVG_SAMPLES_MAX) + return -EINVAL; + + return __ffs64(value); +} + +static int adc_tm_hw_settle_time_from_dt(u32 value, + const unsigned int *hw_settle) +{ + unsigned int i; + + for (i = 0; i < ADC_TM_HW_SETTLE_SAMPLES_MAX; i++) { + if (value == hw_settle[i]) + return i; + } + + return -EINVAL; +} + +static int adc_tm_decimation_from_dt(u32 value, const unsigned int *decimation) +{ + unsigned int i; + + for (i = 0; i < ADC_TM_DECIMATION_SAMPLES_MAX; i++) { + if (value == decimation[i]) + return i; + } + + return -EINVAL; +} + +static const struct of_device_id adc_tm_match_table[] = { + { + .compatible = "qcom,adc-tm5", + .data = &data_adc_tm5, + }, + {} +}; + +static int adc_tm_get_dt_data(struct platform_device *pdev, + struct adc_tm_chip *adc_tm, + struct iio_channel *chan, + uint32_t dt_chan_num) +{ + struct device_node *child, *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + const struct of_device_id *id; + const struct adc_tm_data *data; + int ret, idx = 0; + + if (!node) + return -EINVAL; + + id = of_match_node(adc_tm_match_table, node); + if (id) + data = id->data; + else + data = &data_adc_tm5; + adc_tm->data = data; + adc_tm->ops = data->ops; + + ret = of_property_read_u32(node, "qcom,decimation", + &adc_tm->prop.decimation); + if (!ret) { + ret = adc_tm_decimation_from_dt(adc_tm->prop.decimation, + data->decimation); + if (ret < 0) { + dev_err(dev, "Invalid decimation value\n"); + return ret; + } + adc_tm->prop.decimation = ret; + } else { + adc_tm->prop.decimation = ADC_TM_DECIMATION_DEFAULT; + } + + ret = of_property_read_u32(node, "qcom,avg-samples", + &adc_tm->prop.fast_avg_samples); + if (!ret) { + ret = adc_tm_avg_samples_from_dt(adc_tm->prop.fast_avg_samples); + if (ret < 0) { + dev_err(dev, "Invalid fast average with%d\n", ret); + return -EINVAL; + } + } else { + adc_tm->prop.fast_avg_samples = ADC_TM_DEF_AVG_SAMPLES; + } + + adc_tm->prop.timer1 = ADC_TM_TIMER1; + adc_tm->prop.timer2 = ADC_TM_TIMER2; + adc_tm->prop.timer3 = ADC_TM_TIMER3; + + for_each_child_of_node(node, child) { + int channel_num, i = 0; + int calib_type = 0, ret, hw_settle_time = 0; + struct iio_channel *chan_adc; + + ret = of_property_read_u32(child, "reg", &channel_num); + if (ret) { + dev_err(dev, "Invalid channel num\n"); + return -EINVAL; + } + + ret = of_property_read_u32(child, "qcom,hw-settle-time", + &hw_settle_time); + if (!ret) { + ret = adc_tm_hw_settle_time_from_dt(hw_settle_time, + data->hw_settle); + if (ret < 0) { + pr_err("Invalid channel hw settle time property\n"); + return ret; + } + hw_settle_time = ret; + } else { + hw_settle_time = ADC_TM_DEF_HW_SETTLE_TIME; + } + + if (of_property_read_bool(child, "qcom,ratiometric")) + calib_type = ADC_RATIO_CAL; + else + calib_type = ADC_ABS_CAL; + + /* Individual channel properties */ + adc_tm->sensor[idx].adc_ch = channel_num; + adc_tm->sensor[idx].cal_sel = calib_type; + /* Default to 1 second timer select */ + adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2; + adc_tm->sensor[idx].hw_settle_time = hw_settle_time; + while (i < dt_chan_num) { + chan_adc = &chan[i]; + if (chan_adc->channel->channel == channel_num) + adc_tm->sensor[idx].adc = chan_adc; + i++; + } + idx++; + } + + return 0; +} + +static int adc_tm_probe(struct platform_device *pdev) +{ + struct device_node *child, *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct adc_tm_chip *adc_tm; + struct regmap *regmap; + struct iio_channel *channels; + int ret, dt_chan_num = 0, indio_chan_count = 0; + u32 reg; + + if (!node) + return -EINVAL; + + for_each_child_of_node(node, child) + dt_chan_num++; + + if (!dt_chan_num) { + dev_err(dev, "No channel listing\n"); + return -EINVAL; + } + + channels = iio_channel_get_all(dev); + if (IS_ERR(channels)) + return PTR_ERR(channels); + + while (channels[indio_chan_count].indio_dev) + indio_chan_count++; + + if (indio_chan_count != dt_chan_num) { + dev_err(dev, "VADC IIO channel missing in main node\n"); + return -EINVAL; + } + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", ®); + if (ret < 0) + return ret; + + adc_tm = devm_kzalloc(&pdev->dev, + sizeof(struct adc_tm_chip) + (dt_chan_num * + (sizeof(struct adc_tm_sensor))), GFP_KERNEL); + if (!adc_tm) + return -ENOMEM; + + adc_tm->regmap = regmap; + adc_tm->dev = dev; + adc_tm->base = reg; + adc_tm->dt_channels = dt_chan_num; + + ret = adc_tm_get_dt_data(pdev, adc_tm, channels, dt_chan_num); + if (ret) { + dev_err(dev, "adc-tm get dt data failed\n"); + return ret; + } + + ret = adc_tm_init(adc_tm, dt_chan_num); + if (ret) { + dev_err(dev, "adc-tm init failed\n"); + return ret; + } + + ret = adc_tm_register_tzd(adc_tm, dt_chan_num); + if (ret) { + dev_err(dev, "adc-tm failed to register with of thermal\n"); + return ret; + } + + ret = adc_tm_register_interrupts(adc_tm); + if (ret) { + pr_err("adc-tm register interrupts failed:%d\n", ret); + return ret; + } + + list_add_tail(&adc_tm->list, &adc_tm_device_list); + platform_set_drvdata(pdev, adc_tm); + + return 0; +} + +static int adc_tm_remove(struct platform_device *pdev) +{ + struct adc_tm_chip *adc_tm = platform_get_drvdata(pdev); + + if (adc_tm->ops->shutdown) + adc_tm->ops->shutdown(adc_tm); + + return 0; +} + +static struct platform_driver adc_tm_driver = { + .driver = { + .name = "qcom,adc-tm", + .of_match_table = adc_tm_match_table, + }, + .probe = adc_tm_probe, + .remove = adc_tm_remove, +}; +module_platform_driver(adc_tm_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/qcom/adc-tm.h b/drivers/thermal/qcom/adc-tm.h new file mode 100644 index 0000000000000000000000000000000000000000..5ab40e9a26f60e88ca5762474ae31792dc0bbfbd --- /dev/null +++ b/drivers/thermal/qcom/adc-tm.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __QCOM_ADC_TM_H__ +#define __QCOM_ADC_TM_H__ + +#include +#include +#include +#include +#include +#include + +struct adc_tm_chip; + +#define ADC_TM_DECIMATION_DEFAULT 840 +#define ADC_TM_DECIMATION_SAMPLES_MAX 3 +#define ADC_TM_DEF_AVG_SAMPLES 0 /* 1 sample */ +#define ADC_TM_DEF_HW_SETTLE_TIME 0 /* 15 us */ +#define ADC_TM_HW_SETTLE_SAMPLES_MAX 16 +#define ADC_TM_AVG_SAMPLES_MAX 16 +#define ADC_TM_TIMER1 3 /* 3.9ms */ +#define ADC_TM_TIMER2 10 /* 1 second */ +#define ADC_TM_TIMER3 4 /* 4 second */ + +enum adc_cal_method { + ADC_NO_CAL = 0, + ADC_RATIO_CAL = 1, + ADC_ABS_CAL = 2, + ADC_CAL_SEL_NONE, +}; + +enum adc_cal_val { + ADC_TIMER_CAL = 0, + ADC_NEW_CAL, + ADC_CAL_VAL_NONE, +}; + +enum adc_timer_select { + ADC_TIMER_SEL_1 = 0, + ADC_TIMER_SEL_2, + ADC_TIMER_SEL_3, + ADC_TIMER_SEL_NONE, +}; + +struct adc_tm_sensor { + struct adc_tm_chip *chip; + struct thermal_zone_device *tzd; + enum adc_cal_val cal_val; + enum adc_cal_method cal_sel; + unsigned int hw_settle_time; + unsigned int adc_ch; + unsigned int btm_ch; + unsigned int prescaling; + unsigned int timer_select; + struct iio_channel *adc; +}; + +struct adc_tm_cmn_prop { + unsigned int decimation; + unsigned int fast_avg_samples; + unsigned int timer1; + unsigned int timer2; + unsigned int timer3; +}; + +struct adc_tm_ops { + int (*get_temp)(struct adc_tm_sensor *, int *); + int (*init)(struct adc_tm_chip *, uint32_t); + int (*set_trips)(struct adc_tm_sensor *, int, int); + int (*interrupts_reg)(struct adc_tm_chip *); + int (*shutdown)(struct adc_tm_chip *); +}; + +struct adc_tm_chip { + struct device *dev; + struct list_head list; + struct regmap *regmap; + u16 base; + struct adc_tm_cmn_prop prop; + spinlock_t adc_tm_lock; + const struct adc_tm_ops *ops; + const struct adc_tm_data *data; + unsigned int dt_channels; + struct adc_tm_sensor sensor[0]; +}; + +struct adc_tm_data { + const struct adc_tm_ops *ops; + const u32 full_scale_code_volt; + unsigned int *decimation; + unsigned int *hw_settle; +}; + +extern const struct adc_tm_data data_adc_tm5; +/** + * Channel index for the corresponding index to adc_tm_channel_select + */ +enum adc_tm_channel_num { + ADC_TM_CHAN0 = 0, + ADC_TM_CHAN1, + ADC_TM_CHAN2, + ADC_TM_CHAN3, + ADC_TM_CHAN4, + ADC_TM_CHAN5, + ADC_TM_CHAN6, + ADC_TM_CHAN7, + ADC_TM_CHAN_NONE +}; + +/** + * Channel selection registers for each of the configurable measurements + * Channels allotment is set at device config for a channel. + */ +enum adc_tm_channel_sel { + ADC_TM_M0_ADC_CH_SEL_CTL = 0x60, + ADC_TM_M1_ADC_CH_SEL_CTL = 0x68, + ADC_TM_M2_ADC_CH_SEL_CTL = 0x70, + ADC_TM_M3_ADC_CH_SEL_CTL = 0x78, + ADC_TM_M4_ADC_CH_SEL_CTL = 0x80, + ADC_TM_M5_ADC_CH_SEL_CTL = 0x88, + ADC_TM_M6_ADC_CH_SEL_CTL = 0x90, + ADC_TM_M7_ADC_CH_SEL_CTL = 0x98, + ADC_TM_CH_SELECT_NONE +}; + +/** + * enum adc_tm_fast_avg_ctl - Provides ability to obtain single result + * from the ADC that is an average of multiple measurement + * samples. Select number of samples for use in fast + * average mode (i.e. 2 ^ value). + * %ADC_FAST_AVG_SAMPLE_1: 0x0 = 1 + * %ADC_FAST_AVG_SAMPLE_2: 0x1 = 2 + * %ADC_FAST_AVG_SAMPLE_4: 0x2 = 4 + * %ADC_FAST_AVG_SAMPLE_8: 0x3 = 8 + * %ADC_FAST_AVG_SAMPLE_16: 0x4 = 16 + */ +enum qpnp_adc_fast_avg_ctl { + ADC_FAST_AVG_SAMPLE_1 = 0, + ADC_FAST_AVG_SAMPLE_2, + ADC_FAST_AVG_SAMPLE_4, + ADC_FAST_AVG_SAMPLE_8, + ADC_FAST_AVG_SAMPLE_16, + ADC_FAST_AVG_SAMPLE_NONE, +}; + +struct adc_tm_trip_reg_type { + enum adc_tm_channel_sel btm_amux_ch; + uint16_t low_thr_lsb_addr; + uint16_t low_thr_msb_addr; + uint16_t high_thr_lsb_addr; + uint16_t high_thr_msb_addr; + u8 multi_meas_en; + u8 low_thr_int_chan_en; + u8 high_thr_int_chan_en; + u8 meas_interval_ctl; +}; + +/** + * struct adc_tm_config - Represent ADC Thermal Monitor configuration. + * @channel: ADC channel for which thermal monitoring is requested. + * @adc_code: The pre-calibrated digital output of a given ADC releative to the + * ADC reference. + * @high_thr_temp: Temperature at which high threshold notification is required. + * @low_thr_temp: Temperature at which low threshold notification is required. + * @low_thr_voltage : Low threshold voltage ADC code used for reverse + * calibration. + * @high_thr_voltage: High threshold voltage ADC code used for reverse + * calibration. + */ +struct adc_tm_config { + int channel; + int adc_code; + int high_thr_temp; + int low_thr_temp; + int64_t high_thr_voltage; + int64_t low_thr_voltage; +}; + +/** + * struct adc_map_pt - Map the graph representation for ADC channel + * @x: Represent the ADC digitized code. + * @y: Represent the physical data which can be temperature, voltage, + * resistance. + */ +struct adc_tm_map_pt { + int32_t x; + int32_t y; +}; + +/** + * struct adc_linear_graph - Represent ADC characteristics. + * @dy: numerator slope to calculate the gain. + * @dx: denominator slope to calculate the gain. + * @gnd: A/D word of the ground reference used for the channel. + * + * Each ADC device has different offset and gain parameters which are + * computed to calibrate the device. + */ +struct adc_tm_linear_graph { + s32 dy; + s32 dx; + s32 gnd; +}; + +void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, + const struct adc_tm_data *data); + +#endif /* __QCOM_ADC_TM_H__ */ diff --git a/drivers/thermal/qcom/adc-tm5.c b/drivers/thermal/qcom/adc-tm5.c new file mode 100644 index 0000000000000000000000000000000000000000..3f44d8188beb2ba44109a1e4035c2994339683f0 --- /dev/null +++ b/drivers/thermal/qcom/adc-tm5.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adc-tm.h" +#include "../thermal_core.h" + +#define ADC_TM_STATUS2 0x09 +#define ADC_TM_STATUS_LOW 0x0a +#define ADC_TM_STATUS_HIGH 0x0b +#define ADC_TM_NUM_BTM 0x0f + +#define ADC_TM_ADC_DIG_PARAM 0x42 +#define ADC_TM_FAST_AVG_CTL 0x43 +#define ADC_TM_FAST_AVG_EN BIT(7) + +#define ADC_TM_MEAS_INTERVAL_CTL 0x44 +#define ADC_TM_MEAS_INTERVAL_CTL2 0x45 + +#define ADC_TM_MEAS_INTERVAL_CTL2_SHIFT 0x4 +#define ADC_TM_MEAS_INTERVAL_CTL2_MASK 0xf0 +#define ADC_TM_MEAS_INTERVAL_CTL3_MASK 0xf + +#define ADC_TM_EN_CTL1 0x46 +#define ADC_TM_EN BIT(7) +#define ADC_TM_CONV_REQ 0x47 +#define ADC_TM_CONV_REQ_EN BIT(7) + +#define ADC_TM_Mn_ADC_CH_SEL_CTL(n) ((n * 8) + 0x60) +#define ADC_TM_Mn_LOW_THR0(n) ((n * 8) + 0x61) +#define ADC_TM_Mn_LOW_THR1(n) ((n * 8) + 0x62) +#define ADC_TM_Mn_HIGH_THR0(n) ((n * 8) + 0x63) +#define ADC_TM_Mn_HIGH_THR1(n) ((n * 8) + 0x64) +#define ADC_TM_Mn_MEAS_INTERVAL_CTL(n) ((n * 8) + 0x65) +#define ADC_TM_Mn_CTL(n) ((n * 8) + 0x66) +#define ADC_TM_CTL_HW_SETTLE_DELAY_MASK 0xf +#define ADC_TM_CTL_CAL_SEL 0x30 +#define ADC_TM_CTL_CAL_SEL_MASK_SHIFT 4 +#define ADC_TM_CTL_CAL_VAL 0x40 + +#define ADC_TM_Mn_EN(n) ((n * 8) + 0x67) +#define ADC_TM_Mn_MEAS_EN BIT(7) +#define ADC_TM_Mn_HIGH_THR_INT_EN BIT(1) +#define ADC_TM_Mn_LOW_THR_INT_EN BIT(0) +#define ADC_TM_LOWER_MASK(n) ((n) & 0x000000ff) +#define ADC_TM_UPPER_MASK(n) (((n) & 0xffffff00) >> 8) + +static struct adc_tm_trip_reg_type adc_tm_ch_data[] = { + [ADC_TM_CHAN0] = {ADC_TM_M0_ADC_CH_SEL_CTL}, + [ADC_TM_CHAN1] = {ADC_TM_M1_ADC_CH_SEL_CTL}, + [ADC_TM_CHAN2] = {ADC_TM_M2_ADC_CH_SEL_CTL}, + [ADC_TM_CHAN3] = {ADC_TM_M3_ADC_CH_SEL_CTL}, + [ADC_TM_CHAN4] = {ADC_TM_M4_ADC_CH_SEL_CTL}, + [ADC_TM_CHAN5] = {ADC_TM_M5_ADC_CH_SEL_CTL}, + [ADC_TM_CHAN6] = {ADC_TM_M6_ADC_CH_SEL_CTL}, + [ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL}, +}; + +static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp) +{ + int ret, milli_celsius; + + if (!sensor || !sensor->adc) + return -EINVAL; + + ret = iio_read_channel_processed(sensor->adc, &milli_celsius); + if (ret < 0) + return ret; + + *temp = milli_celsius; + + return 0; +} + +static int32_t adc_tm5_read_reg(struct adc_tm_chip *chip, + int16_t reg, u8 *data, int len) +{ + int ret; + + ret = regmap_bulk_read(chip->regmap, (chip->base + reg), data, len); + if (ret < 0) + pr_err("adc-tm read reg %d failed with %d\n", reg, ret); + + return ret; +} + +static int32_t adc_tm5_write_reg(struct adc_tm_chip *chip, + int16_t reg, u8 *data, int len) +{ + int ret; + + ret = regmap_bulk_write(chip->regmap, (chip->base + reg), data, len); + if (ret < 0) + pr_err("adc-tm write reg %d failed with %d\n", reg, ret); + + return ret; +} + +static int32_t adc_tm5_reg_update(struct adc_tm_chip *chip, + uint16_t addr, u8 mask, bool state) +{ + u8 reg_value = 0; + int ret; + + ret = adc_tm5_read_reg(chip, addr, ®_value, 1); + if (ret < 0) { + pr_err("read failed for addr:0x%x\n", addr); + return ret; + } + + reg_value = reg_value & ~mask; + if (state) + reg_value |= mask; + + pr_debug("state:%d, reg:0x%x with bits:0x%x and mask:0x%x\n", + state, addr, reg_value, ~mask); + ret = adc_tm5_write_reg(chip, addr, ®_value, 1); + if (ret < 0) { + pr_err("write failed for addr:%x\n", addr); + return ret; + } + + return ret; +} + +static int32_t adc_tm5_get_btm_idx(struct adc_tm_chip *chip, + uint32_t btm_chan, uint32_t *btm_chan_idx) +{ + int i; + + for (i = 0; i < ADC_TM_CHAN_NONE; i++) { + if (adc_tm_ch_data[i].btm_amux_ch == btm_chan) { + *btm_chan_idx = i; + return 0; + } + } + + return -EINVAL; +} + +static int32_t adc_tm5_enable(struct adc_tm_chip *chip) +{ + int rc = 0; + u8 data = 0; + + data = ADC_TM_EN; + rc = adc_tm5_write_reg(chip, ADC_TM_EN_CTL1, &data, 1); + if (rc < 0) { + pr_err("adc-tm enable failed\n"); + return rc; + } + + data = ADC_TM_CONV_REQ_EN; + rc = adc_tm5_write_reg(chip, ADC_TM_CONV_REQ, &data, 1); + if (rc < 0) { + pr_err("adc-tm request conversion failed\n"); + return rc; + } + + return rc; +} + +static int adc_tm5_configure(struct adc_tm_sensor *sensor, + uint32_t btm_chan_idx) +{ + struct adc_tm_chip *chip = sensor->chip; + u8 buf[8], cal_sel; + int ret = 0; + + ret = adc_tm5_read_reg(chip, + ADC_TM_Mn_ADC_CH_SEL_CTL(btm_chan_idx), buf, 8); + if (ret < 0) { + pr_err("adc-tm block read failed with %d\n", ret); + return ret; + } + + /* Update ADC channel select */ + buf[0] = sensor->adc_ch; + + /* Update timer select */ + buf[5] = sensor->timer_select; + + /* Set calibration select, hw_settle delay */ + cal_sel = (u8) (sensor->cal_sel << ADC_TM_CTL_CAL_SEL_MASK_SHIFT); + buf[6] &= (u8) ~ADC_TM_CTL_HW_SETTLE_DELAY_MASK; + buf[6] |= (u8) sensor->hw_settle_time; + buf[6] &= (u8) ~ADC_TM_CTL_CAL_SEL; + buf[6] |= (u8) cal_sel; + + buf[7] |= ADC_TM_Mn_MEAS_EN; + + ret = adc_tm5_write_reg(chip, + ADC_TM_Mn_ADC_CH_SEL_CTL(btm_chan_idx), buf, 8); + if (ret < 0) { + pr_err("adc-tm block write failed with %d\n", ret); + return ret; + } + + return 0; +} + +static int adc_tm5_set_mode(struct adc_tm_sensor *sensor, + enum thermal_device_mode mode) +{ + struct adc_tm_chip *chip = sensor->chip; + int ret = 0; + uint32_t btm_chan_idx = 0; + + ret = adc_tm5_get_btm_idx(chip, sensor->btm_ch, &btm_chan_idx); + if (ret < 0) { + pr_err("Invalid btm channel idx with %d\n", ret); + return ret; + } + + if (mode == THERMAL_DEVICE_ENABLED) { + ret = adc_tm5_configure(sensor, btm_chan_idx); + if (ret < 0) { + pr_err("Error during adc-tm configure:%d\n", ret); + return ret; + } + + ret = adc_tm5_enable(chip); + if (ret < 0) + pr_err("Error enabling adc-tm with %d\n", ret); + + } else if (mode == THERMAL_DEVICE_DISABLED) { + ret = adc_tm5_reg_update(chip, + ADC_TM_Mn_EN(btm_chan_idx), + ADC_TM_Mn_MEAS_EN, false); + if (ret < 0) + pr_err("Disable failed for ch:%d\n", btm_chan_idx); + } + + return ret; +} + +static int adc_tm5_activate_trip_type(struct adc_tm_sensor *adc_tm, + int trip, enum thermal_device_mode mode) +{ + struct adc_tm_chip *chip = adc_tm->chip; + int ret = 0; + bool state = false; + uint32_t btm_chan_idx = 0, btm_chan = 0; + + if (mode == THERMAL_DEVICE_ENABLED) + state = true; + + btm_chan = adc_tm->btm_ch; + ret = adc_tm5_get_btm_idx(chip, btm_chan, &btm_chan_idx); + if (ret < 0) { + pr_err("Invalid btm channel idx\n"); + return ret; + } + + switch (trip) { + case THERMAL_TRIP_CONFIGURABLE_HI: + /* low_thr (lower voltage) for higher temp */ + ret = adc_tm5_reg_update(chip, + ADC_TM_Mn_EN(btm_chan_idx), + ADC_TM_Mn_LOW_THR_INT_EN, state); + if (ret) + pr_err("channel:%x failed\n", btm_chan); + break; + case THERMAL_TRIP_CONFIGURABLE_LOW: + /* high_thr (higher voltage) for cooler temp */ + ret = adc_tm5_reg_update(chip, + ADC_TM_Mn_EN(btm_chan_idx), + ADC_TM_Mn_HIGH_THR_INT_EN, state); + if (ret) + pr_err("channel:%x failed\n", btm_chan); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int adc_tm5_set_trip_temp(struct adc_tm_sensor *sensor, + int low_temp, int high_temp) +{ + struct adc_tm_chip *chip; + struct adc_tm_config tm_config; + u8 trip_low_thr[2], trip_high_thr[2]; + uint16_t reg_low_thr_lsb, reg_high_thr_lsb; + int ret; + uint32_t btm_chan = 0, btm_chan_idx = 0, mask = 0; + unsigned long flags; + + if (!sensor) + return -EINVAL; + + pr_debug("%s:low_temp(mdegC):%d, high_temp(mdegC):%d\n", __func__, + low_temp, high_temp); + + chip = sensor->chip; + tm_config.channel = sensor->adc_ch; + tm_config.high_thr_temp = tm_config.low_thr_temp = 0; + if (high_temp != INT_MAX) + tm_config.high_thr_temp = high_temp; + if (low_temp != INT_MIN) + tm_config.low_thr_temp = low_temp; + + if ((high_temp == INT_MAX) && (low_temp == INT_MIN)) { + pr_err("No trips to set\n"); + return -EINVAL; + } + + pr_debug("requested a low temp- %d and high temp- %d\n", + tm_config.low_thr_temp, tm_config.high_thr_temp); + adc_tm_scale_therm_voltage_100k(&tm_config, chip->data); + + /* Cool temperature corresponds to high voltage threshold */ + mask = lower_32_bits(tm_config.high_thr_voltage); + trip_high_thr[0] = ADC_TM_LOWER_MASK(mask); + trip_high_thr[1] = ADC_TM_UPPER_MASK(mask); + /* Warm temperature corresponds to low voltage threshold */ + mask = lower_32_bits(tm_config.low_thr_voltage); + trip_low_thr[0] = ADC_TM_LOWER_MASK(mask); + trip_low_thr[1] = ADC_TM_UPPER_MASK(mask); + + pr_debug("high_thr:0x%llx, low_thr:0x%llx\n", + tm_config.high_thr_voltage, tm_config.low_thr_voltage); + + btm_chan = sensor->btm_ch; + ret = adc_tm5_get_btm_idx(chip, btm_chan, &btm_chan_idx); + if (ret < 0) { + pr_err("Invalid btm channel idx\n"); + return ret; + } + + spin_lock_irqsave(&chip->adc_tm_lock, flags); + + reg_low_thr_lsb = ADC_TM_Mn_LOW_THR0(btm_chan_idx); + reg_high_thr_lsb = ADC_TM_Mn_HIGH_THR0(btm_chan_idx); + + if (high_temp != INT_MAX) { + ret = adc_tm5_write_reg(chip, reg_low_thr_lsb, + trip_low_thr, 2); + if (ret) { + pr_err("Warm set threshold err\n"); + goto fail; + } + + ret = adc_tm5_activate_trip_type(sensor, + THERMAL_TRIP_CONFIGURABLE_HI, + THERMAL_DEVICE_ENABLED); + if (ret) { + pr_err("adc-tm warm activation failed\n"); + goto fail; + } + } else { + ret = adc_tm5_activate_trip_type(sensor, + THERMAL_TRIP_CONFIGURABLE_HI, + THERMAL_DEVICE_DISABLED); + if (ret) { + pr_err("adc-tm warm deactivation failed\n"); + goto fail; + } + } + + if (low_temp != INT_MIN) { + ret = adc_tm5_write_reg(chip, reg_high_thr_lsb, + trip_high_thr, 2); + if (ret) { + pr_err("adc-tm cool temp set threshold err\n"); + goto fail; + } + + ret = adc_tm5_activate_trip_type(sensor, + THERMAL_TRIP_CONFIGURABLE_LOW, + THERMAL_DEVICE_ENABLED); + if (ret) { + pr_err("adc-tm cool activation failed\n"); + goto fail; + } + } else { + ret = adc_tm5_activate_trip_type(sensor, + THERMAL_TRIP_CONFIGURABLE_LOW, + THERMAL_DEVICE_DISABLED); + if (ret) { + pr_err("adc-tm cool deactivation failed\n"); + goto fail; + } + } + + if ((high_temp != INT_MAX) || (low_temp != INT_MIN)) { + ret = adc_tm5_set_mode(sensor, THERMAL_DEVICE_ENABLED); + if (ret) + pr_err("sensor enabled failed\n"); + } else { + ret = adc_tm5_set_mode(sensor, THERMAL_DEVICE_DISABLED); + if (ret) + pr_err("sensor disable failed\n"); + } + +fail: + spin_unlock_irqrestore(&chip->adc_tm_lock, flags); + + return ret; +} + +static irqreturn_t adc_tm5_handler(int irq, void *data) +{ + struct adc_tm_chip *chip = data; + u8 status_low, status_high, ctl; + int ret, i = 0; + unsigned long flags; + + ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1); + if (ret < 0) { + pr_err("adc-tm-tm read status low failed with %d\n", ret); + return IRQ_HANDLED; + } + + ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_HIGH, &status_high, 1); + if (ret < 0) { + pr_err("adc-tm-tm read status high failed with %d\n", ret); + return IRQ_HANDLED; + } + + while (i < chip->dt_channels) { + bool upper_set = false, lower_set = false; + int temp; + + if (IS_ERR(chip->sensor[i].tzd)) + continue; + + ret = adc_tm5_get_temp(&chip->sensor[i], &temp); + if (ret < 0) + continue; + + spin_lock_irqsave(&chip->adc_tm_lock, flags); + + ret = adc_tm5_read_reg(chip, ADC_TM_Mn_EN(i), &ctl, 1); + if (ret) { + pr_err("ctl read failed with %d\n", ret); + goto fail; + } + + if ((status_low & 0x1) && (ctl & ADC_TM_Mn_MEAS_EN) + && (ctl & ADC_TM_Mn_LOW_THR_INT_EN)) + lower_set = true; + + if ((status_high & 0x1) && (ctl & ADC_TM_Mn_MEAS_EN) && + (ctl & ADC_TM_Mn_HIGH_THR_INT_EN)) + upper_set = true; +fail: + status_low >>= 1; + status_high >>= 1; + spin_unlock_irqrestore(&chip->adc_tm_lock, flags); + if (upper_set || lower_set) { + /* + * Expected behavior is while notifying of_thermal, + * thermal core will call set_trips with new thresholds + * and activate/disable the appropriate trips. + */ + pr_debug("notifying of_thermal\n"); + of_thermal_handle_trip(chip->sensor[i].tzd); + } + i++; + } + return IRQ_HANDLED; +} + +static int adc_tm5_register_interrupts(struct adc_tm_chip *chip) +{ + struct platform_device *pdev; + int ret, irq; + + if (!chip) + return -EINVAL; + + pdev = to_platform_device(chip->dev); + + irq = platform_get_irq_byname(pdev, "thr-int-en"); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq %s\n", + "thr-int-en"); + return irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + adc_tm5_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "thr-int-en", chip); + if (ret) { + dev_err(&pdev->dev, "failed to get irq %s\n", + "thr-int-en"); + return ret; + } + + enable_irq_wake(irq); + + return ret; +} + +static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans) +{ + u8 buf[4], channels_available, meas_int_timer_2_3 = 0; + int ret, i; + + ret = adc_tm5_read_reg(chip, ADC_TM_NUM_BTM, &channels_available, 1); + if (ret < 0) { + pr_err("read failed for BTM channels\n"); + return ret; + } + + if (dt_chans > channels_available) { + pr_err("Number of nodes greater than channels supported:%d\n", + channels_available); + return -EINVAL; + } + + ret = adc_tm5_read_reg(chip, + ADC_TM_ADC_DIG_PARAM, buf, 4); + if (ret < 0) { + pr_err("adc-tm block read failed with %d\n", ret); + return ret; + } + + /* Select decimation */ + buf[0] = chip->prop.decimation; + + /* Select number of samples in fast average mode */ + buf[1] = chip->prop.fast_avg_samples | ADC_TM_FAST_AVG_EN; + + /* Select timer1 */ + buf[2] = chip->prop.timer1; + + /* Select timer2 and timer3 */ + meas_int_timer_2_3 |= chip->prop.timer2 << + ADC_TM_MEAS_INTERVAL_CTL2_SHIFT; + meas_int_timer_2_3 |= chip->prop.timer3; + buf[3] = meas_int_timer_2_3; + + ret = adc_tm5_write_reg(chip, + ADC_TM_ADC_DIG_PARAM, buf, 4); + if (ret < 0) + pr_err("adc-tm block write failed with %d\n", ret); + + spin_lock_init(&chip->adc_tm_lock); + + for (i = 0; i < dt_chans; i++) + chip->sensor[i].btm_ch = adc_tm_ch_data[i].btm_amux_ch; + + return ret; +} + +static const struct adc_tm_ops ops_adc_tm5 = { + .init = adc_tm5_init, + .set_trips = adc_tm5_set_trip_temp, + .interrupts_reg = adc_tm5_register_interrupts, + .get_temp = adc_tm5_get_temp, +}; + +const struct adc_tm_data data_adc_tm5 = { + .ops = &ops_adc_tm5, + .full_scale_code_volt = 0x70e4, + .decimation = (unsigned int []) {250, 420, 840}, + .hw_settle = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; diff --git a/drivers/thermal/qcom/msm_lmh_dcvs.c b/drivers/thermal/qcom/msm_lmh_dcvs.c index 267d27906d1ac99216e679cfb9c946f732d6f553..68d374b5c255d2677905611a7e6430eac7c6b58f 100644 --- a/drivers/thermal/qcom/msm_lmh_dcvs.c +++ b/drivers/thermal/qcom/msm_lmh_dcvs.c @@ -88,15 +88,15 @@ struct limits_dcvs_hw { void *int_clr_reg; void *min_freq_reg; cpumask_t core_map; + cpumask_t online_mask; struct delayed_work freq_poll_work; - unsigned long max_freq; - unsigned long min_freq; + unsigned long max_freq[NR_CPUS]; + unsigned long min_freq[NR_CPUS]; unsigned long hw_freq_limit; struct device_attribute lmh_freq_attr; struct list_head list; bool is_irq_enabled; struct mutex access_lock; - struct mutex cdev_reg_lock; struct __limits_cdev_data *cdev_data; uint32_t cdev_registered; struct regulator *isens_reg[2]; @@ -106,63 +106,80 @@ struct limits_dcvs_hw { LIST_HEAD(lmh_dcvs_hw_list); DEFINE_MUTEX(lmh_dcvs_list_access); -static int limits_dcvs_get_freq_limits(uint32_t cpu, unsigned long *max_freq, - unsigned long *min_freq) +static void limits_dcvs_get_freq_limits(struct limits_dcvs_hw *hw) { unsigned long freq_ceil = UINT_MAX, freq_floor = 0; struct device *cpu_dev = NULL; - int ret = 0; - - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("Error in get CPU%d device\n", cpu); - return -ENODEV; - } + uint32_t cpu, idx = 0; - dev_pm_opp_find_freq_floor(cpu_dev, &freq_ceil); - dev_pm_opp_find_freq_ceil(cpu_dev, &freq_floor); + for_each_cpu(cpu, &hw->core_map) { + freq_ceil = UINT_MAX; + freq_floor = 0; + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("Error in get CPU%d device\n", cpu); + idx++; + continue; + } - *max_freq = freq_ceil / 1000; - *min_freq = freq_floor / 1000; + dev_pm_opp_find_freq_floor(cpu_dev, &freq_ceil); + dev_pm_opp_find_freq_ceil(cpu_dev, &freq_floor); - return ret; + hw->max_freq[idx] = freq_ceil / 1000; + hw->min_freq[idx] = freq_floor / 1000; + idx++; + } } static unsigned long limits_mitigation_notify(struct limits_dcvs_hw *hw) { - uint32_t val = 0; + uint32_t val = 0, max_cpu_ct = 0, max_cpu_limit = 0, idx = 0, cpu = 0; struct device *cpu_dev = NULL; unsigned long freq_val, max_limit = 0; struct dev_pm_opp *opp_entry; val = readl_relaxed(hw->osm_hw_reg); dcvsh_get_frequency(val, max_limit); - cpu_dev = get_cpu_device(cpumask_first(&hw->core_map)); - if (!cpu_dev) { - pr_err("Error in get CPU%d device\n", - cpumask_first(&hw->core_map)); - goto notify_exit; - } + for_each_cpu(cpu, &hw->core_map) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("Error in get CPU%d device\n", + cpumask_first(&hw->core_map)); + goto notify_exit; + } - pr_debug("CPU:%d max value read:%lu\n", + pr_debug("CPU:%d max value read:%lu\n", cpumask_first(&hw->core_map), max_limit); - freq_val = FREQ_KHZ_TO_HZ(max_limit); - opp_entry = dev_pm_opp_find_freq_floor(cpu_dev, &freq_val); - /* - * Hardware mitigation frequency can be lower than the lowest - * possible CPU frequency. In that case freq floor call will - * fail with -ERANGE and we need to match to the lowest - * frequency using freq_ceil. - */ - if (IS_ERR(opp_entry) && PTR_ERR(opp_entry) == -ERANGE) { - opp_entry = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_val); - if (IS_ERR(opp_entry)) - dev_err(cpu_dev, "frequency:%lu. opp error:%ld\n", + freq_val = FREQ_KHZ_TO_HZ(max_limit); + opp_entry = dev_pm_opp_find_freq_floor(cpu_dev, &freq_val); + /* + * Hardware mitigation frequency can be lower than the lowest + * possible CPU frequency. In that case freq floor call will + * fail with -ERANGE and we need to match to the lowest + * frequency using freq_ceil. + */ + if (IS_ERR(opp_entry) && PTR_ERR(opp_entry) == -ERANGE) { + opp_entry = dev_pm_opp_find_freq_ceil(cpu_dev, + &freq_val); + if (IS_ERR(opp_entry)) + dev_err(cpu_dev, + "frequency:%lu. opp error:%ld\n", freq_val, PTR_ERR(opp_entry)); + } + if (FREQ_HZ_TO_KHZ(freq_val) == hw->max_freq[idx]) { + max_cpu_ct++; + if (max_cpu_limit < hw->max_freq[idx]) + max_cpu_limit = hw->max_freq[idx]; + idx++; + continue; + } + max_limit = FREQ_HZ_TO_KHZ(freq_val); + break; } - max_limit = FREQ_HZ_TO_KHZ(freq_val); + if (max_cpu_ct == cpumask_weight(&hw->core_map)) + max_limit = max_cpu_limit; sched_update_cpu_freq_min_max(&hw->core_map, 0, max_limit); pr_debug("CPU:%d max limit:%lu\n", cpumask_first(&hw->core_map), max_limit); @@ -179,13 +196,18 @@ static void limits_dcvs_poll(struct work_struct *work) struct limits_dcvs_hw *hw = container_of(work, struct limits_dcvs_hw, freq_poll_work.work); + int cpu_ct = 0, cpu = 0, idx = 0; mutex_lock(&hw->access_lock); - if (hw->max_freq == U32_MAX) - limits_dcvs_get_freq_limits(cpumask_first(&hw->core_map), - &hw->max_freq, &hw->min_freq); + if (hw->max_freq[0] == U32_MAX) + limits_dcvs_get_freq_limits(hw); max_limit = limits_mitigation_notify(hw); - if (max_limit >= hw->max_freq) { + for_each_cpu(cpu, &hw->core_map) { + if (max_limit >= hw->max_freq[idx]) + cpu_ct++; + idx++; + } + if (cpu_ct >= cpumask_weight(&hw->core_map)) { writel_relaxed(0xFF, hw->int_clr_reg); hw->is_irq_enabled = true; enable_irq(hw->irq_num); @@ -313,14 +335,10 @@ static struct limits_dcvs_hw *get_dcvsh_hw_from_cpu(int cpu) { struct limits_dcvs_hw *hw; - mutex_lock(&lmh_dcvs_list_access); list_for_each_entry(hw, &lmh_dcvs_hw_list, list) { - if (cpumask_test_cpu(cpu, &hw->core_map)) { - mutex_unlock(&lmh_dcvs_list_access); + if (cpumask_test_cpu(cpu, &hw->core_map)) return hw; - } } - mutex_unlock(&lmh_dcvs_list_access); return NULL; } @@ -343,7 +361,7 @@ static int lmh_set_max_limit(int cpu, u32 freq) * the CPU to use the boost frequencies. */ hw->cdev_data[idx].max_freq = - (freq == hw->max_freq) ? U32_MAX : freq; + (freq == hw->max_freq[idx]) ? U32_MAX : freq; if (max_freq > hw->cdev_data[idx].max_freq) max_freq = hw->cdev_data[idx].max_freq; idx++; @@ -360,8 +378,7 @@ static int lmh_set_max_limit(int cpu, u32 freq) static int lmh_set_min_limit(int cpu, u32 freq) { struct limits_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu); - int cpu_idx, idx = 0; - u32 min_freq = 0; + int cpu_idx, idx = 0, cpu_ct = 0; if (!hw) return -EINVAL; @@ -370,11 +387,11 @@ static int lmh_set_min_limit(int cpu, u32 freq) for_each_cpu(cpu_idx, &hw->core_map) { if (cpu_idx == cpu) hw->cdev_data[idx].min_freq = freq; - if (min_freq < hw->cdev_data[idx].min_freq) - min_freq = hw->cdev_data[idx].min_freq; + if (hw->cdev_data[idx].min_freq <= hw->min_freq[idx]) + cpu_ct++; idx++; } - if (min_freq != hw->min_freq) + if (cpu_ct < cpumask_weight(&hw->core_map)) writel_relaxed(0x01, hw->min_freq_reg); else writel_relaxed(0x00, hw->min_freq_reg); @@ -389,38 +406,42 @@ static struct cpu_cooling_ops cd_ops = { static void register_cooling_device(struct work_struct *work) { - struct limits_dcvs_hw *hw = container_of(work, struct limits_dcvs_hw, - cdev_register_work); + struct limits_dcvs_hw *hw; unsigned int cpu = 0, idx = 0; - mutex_lock(&hw->cdev_reg_lock); - if (hw->max_freq == U32_MAX) - limits_dcvs_get_freq_limits(cpumask_first(&hw->core_map), - &hw->max_freq, &hw->min_freq); - - for_each_cpu(cpu, &hw->core_map) { - cpumask_t cpu_mask = { CPU_BITS_NONE }; + mutex_lock(&lmh_dcvs_list_access); + list_for_each_entry(hw, &lmh_dcvs_hw_list, list) { + if (hw->max_freq[0] == U32_MAX) + limits_dcvs_get_freq_limits(hw); - if (hw->cdev_data[idx].cdev) { - idx++; + if (cpumask_weight(&hw->online_mask) == 0) continue; + idx = 0; + for_each_cpu(cpu, &hw->core_map) { + cpumask_t cpu_mask = { CPU_BITS_NONE }; + + if (hw->cdev_data[idx].cdev) { + idx++; + continue; + } + cpumask_set_cpu(cpu, &cpu_mask); + hw->cdev_data[idx].max_freq = U32_MAX; + hw->cdev_data[idx].min_freq = 0; + hw->cdev_data[idx].cdev = + cpufreq_platform_cooling_register( + &cpu_mask, &cd_ops); + if (IS_ERR_OR_NULL(hw->cdev_data[idx].cdev)) { + pr_err("CPU:%u cdev register error:%ld\n", + cpu, PTR_ERR(hw->cdev_data[idx].cdev)); + hw->cdev_data[idx].cdev = NULL; + } else { + pr_debug("CPU:%u cdev registered\n", cpu); + hw->cdev_registered++; + } + idx++; } - cpumask_set_cpu(cpu, &cpu_mask); - hw->cdev_data[idx].max_freq = U32_MAX; - hw->cdev_data[idx].min_freq = 0; - hw->cdev_data[idx].cdev = cpufreq_platform_cooling_register( - &cpu_mask, &cd_ops); - if (IS_ERR_OR_NULL(hw->cdev_data[idx].cdev)) { - pr_err("CPU:%u cooling device register error:%ld\n", - cpu, PTR_ERR(hw->cdev_data[idx].cdev)); - hw->cdev_data[idx].cdev = NULL; - } else { - pr_debug("CPU:%u cooling device registered\n", cpu); - hw->cdev_registered++; - } - idx++; } - mutex_unlock(&hw->cdev_reg_lock); + mutex_unlock(&lmh_dcvs_list_access); } static int limits_cpu_online(unsigned int online_cpu) @@ -429,6 +450,7 @@ static int limits_cpu_online(unsigned int online_cpu) if (!hw) return 0; + cpumask_set_cpu(online_cpu, &hw->online_mask); if (hw->cdev_registered != cpumask_weight(&hw->core_map)) queue_work(system_highpri_wq, &hw->cdev_register_work); @@ -542,11 +564,14 @@ static int limits_dcvs_probe(struct platform_device *pdev) return -ENOMEM; cpumask_copy(&hw->core_map, &mask); + cpumask_clear(&hw->online_mask); hw->cdev_registered = 0; for_each_cpu(cpu, &hw->core_map) { hw->cdev_data[idx].cdev = NULL; hw->cdev_data[idx].max_freq = U32_MAX; hw->cdev_data[idx].min_freq = 0; + hw->max_freq[idx] = U32_MAX; + hw->min_freq[idx] = 0; idx++; } ret = of_property_read_u32(dn, "qcom,affinity", &affinity); @@ -586,8 +611,7 @@ static int limits_dcvs_probe(struct platform_device *pdev) */ hw->temp_limits[LIMITS_TRIP_HI] = INT_MAX; hw->temp_limits[LIMITS_TRIP_ARM] = 0; - hw->hw_freq_limit = hw->max_freq = U32_MAX; - hw->min_freq = 0; + hw->hw_freq_limit = U32_MAX; snprintf(hw->sensor_name, sizeof(hw->sensor_name), "limits_sensor-%02d", affinity); tzdev = thermal_zone_of_sensor_register(&pdev->dev, 0, hw, @@ -603,7 +627,6 @@ static int limits_dcvs_probe(struct platform_device *pdev) } mutex_init(&hw->access_lock); - mutex_init(&hw->cdev_reg_lock); INIT_WORK(&hw->cdev_register_work, register_cooling_device); INIT_DEFERRABLE_WORK(&hw->freq_poll_work, limits_dcvs_poll); hw->osm_hw_reg = devm_ioremap(&pdev->dev, request_reg, 0x4); @@ -640,7 +663,7 @@ static int limits_dcvs_probe(struct platform_device *pdev) probe_exit: mutex_lock(&lmh_dcvs_list_access); INIT_LIST_HEAD(&hw->list); - list_add(&hw->list, &lmh_dcvs_hw_list); + list_add_tail(&hw->list, &lmh_dcvs_hw_list); mutex_unlock(&lmh_dcvs_list_access); lmh_debug_register(pdev); diff --git a/drivers/thermal/qcom/qti_virtual_sensor.c b/drivers/thermal/qcom/qti_virtual_sensor.c index 903d8066667ff02be9e9667e191e90555d19c3a6..31fcfde67a0bc2c0605a55c5db237db4acd93bc2 100644 --- a/drivers/thermal/qcom/qti_virtual_sensor.c +++ b/drivers/thermal/qcom/qti_virtual_sensor.c @@ -110,6 +110,16 @@ static const struct virtual_sensor_data qti_virtual_sensors[] = { "gpuss-1-usr"}, .logic = VIRT_MAXIMUM, }, + { + .virt_zone_name = "cpuss-max-step", + .num_sensors = 5, + .sensor_names = {"cpuss-0-usr", + "cpuss-1-usr", + "cpuss-2-usr", + "cpuss-3-usr", + "mhm-usr"}, + .logic = VIRT_MAXIMUM, + }, }; int qti_virtual_sensor_register(struct device *dev) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f6a855c0b516eecc2bd87de53e23a641cda54095..4b1d34d67db42284ec3abae8eaa9f483fdada040 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -724,7 +724,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, * based on the MACRO determine the default state to use or the * offset from the max_state. */ - if (upper > (THERMAL_MAX_LIMIT - max_state)) { + if (upper >= (THERMAL_MAX_LIMIT - max_state)) { /* upper default max_state */ if (upper == THERMAL_NO_LIMIT) upper = max_state; @@ -732,7 +732,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, upper = max_state - (THERMAL_MAX_LIMIT - upper); } - if (lower > (THERMAL_MAX_LIMIT - max_state)) { + if (lower >= (THERMAL_MAX_LIMIT - max_state)) { /* lower default 0 */ if (lower == THERMAL_NO_LIMIT) lower = 0; diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 6161537568081c840fed12f7af98f784484a1a34..3fe10afce2ec07917c2a19510ad3de213ec712af 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -812,7 +812,6 @@ thermal_cooling_device_cur_state_store(struct device *dev, { struct thermal_cooling_device *cdev = to_cooling_device(dev); unsigned long state; - int result; if (sscanf(buf, "%ld\n", &state) != 1) return -EINVAL; @@ -820,9 +819,13 @@ thermal_cooling_device_cur_state_store(struct device *dev, if ((long)state < 0) return -EINVAL; - result = cdev->ops->set_cur_state(cdev, state); - if (result) - return result; + mutex_lock(&cdev->lock); + cdev->sysfs_cur_state_req = state; + + cdev->updated = false; + mutex_unlock(&cdev->lock); + thermal_cdev_update(cdev); + return count; } diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h index c9e08a2811822abdcc0161f1bdcbfa4f627f6b97..0c696d3d5a266facfd9c4fb7f01595f421b31780 100644 --- a/drivers/thermal/tsens.h +++ b/drivers/thermal/tsens.h @@ -131,6 +131,8 @@ struct tsens_data { u32 wd_bark_mask; bool mtc; bool valid_status_check; + u32 ver_major; + u32 ver_minor; }; struct tsens_mtc_sysfs { diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c index 02322afaf89c6acddb1b6ad697de7b5b7b909066..914e2ced7755c0d2c63058cae1b329bf3aa497de 100644 --- a/drivers/thermal/tsens1xxx.c +++ b/drivers/thermal/tsens1xxx.c @@ -351,8 +351,8 @@ static irqreturn_t tsens_irq_thread(int irq, void *data) th_temp = code_to_degc((threshold & TSENS_UPPER_THRESHOLD_MASK) >> TSENS_UPPER_THRESHOLD_SHIFT, - tm->sensor); - if (th_temp > temp) { + (tm->sensor + i)); + if (th_temp > (temp/TSENS_SCALE_MILLIDEG)) { pr_debug("Re-arm high threshold\n"); rc = tsens_tz_activate_trip_type( &tm->sensor[i], @@ -373,8 +373,8 @@ static irqreturn_t tsens_irq_thread(int irq, void *data) tm->tsens_tm_addr + addr_offset)); th_temp = code_to_degc((threshold & TSENS_LOWER_THRESHOLD_MASK), - tm->sensor); - if (th_temp < temp) { + (tm->sensor + i)); + if (th_temp < (temp/TSENS_SCALE_MILLIDEG)) { pr_debug("Re-arm Low threshold\n"); rc = tsens_tz_activate_trip_type( &tm->sensor[i], diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c index 55420bf5e422544634b842f7b34ea098ab7322e7..6584000ae94bac2e255804043fa5d2047a06a9f2 100644 --- a/drivers/thermal/tsens2xxx.c +++ b/drivers/thermal/tsens2xxx.c @@ -59,6 +59,7 @@ #define TSENS_TM_SCALE_DECI_MILLIDEG 100 #define TSENS_DEBUG_WDOG_TRIGGER_COUNT 5 #define TSENS_TM_WATCHDOG_LOG(n) ((n) + 0x13c) +#define TSENS_TM_WATCHDOG_LOG_v23(n) ((n) + 0x170) #define TSENS_EN BIT(0) #define TSENS_CTRL_SENSOR_EN_MASK(n) ((n >> 3) & 0xffff) #define TSENS_TM_TRDY(n) ((n) + 0xe4) @@ -321,7 +322,10 @@ static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data) TSENS_TM_SN_CRITICAL_THRESHOLD(tm->tsens_tm_addr); wd_critical_addr = TSENS_TM_CRITICAL_INT_STATUS(tm->tsens_tm_addr); - wd_log_addr = TSENS_TM_WATCHDOG_LOG(tm->tsens_tm_addr); + if (tm->ctrl_data->ver_major == 2 && tm->ctrl_data->ver_minor == 3) + wd_log_addr = TSENS_TM_WATCHDOG_LOG_v23(tm->tsens_tm_addr); + else + wd_log_addr = TSENS_TM_WATCHDOG_LOG(tm->tsens_tm_addr); if (tm->ctrl_data->wd_bark) { wd_mask = readl_relaxed(wd_critical_addr); @@ -643,6 +647,8 @@ const struct tsens_data data_tsens23xx = { .wd_bark_mask = 1, .ops = &ops_tsens2xxx, .mtc = false, + .ver_major = 2, + .ver_minor = 3, }; const struct tsens_data data_tsens24xx = { diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 53250fc057e1f355b490286ca545fcc2b8d4fb54..91830b1bdcaf74bc42a423a4a45cdfa04e1ce6af 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -539,14 +539,14 @@ static bool icm_ar_is_supported(struct tb *tb) static int icm_ar_get_mode(struct tb *tb) { struct tb_nhi *nhi = tb->nhi; - int retries = 5; + int retries = 60; u32 val; do { val = ioread32(nhi->iobase + REG_FW_STS); if (val & REG_FW_STS_NVM_AUTH_DONE) break; - msleep(30); + msleep(50); } while (--retries); if (!retries) { @@ -720,6 +720,9 @@ static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) struct icm *icm = tb_priv(tb); u32 val; + if (!icm->upstream_port) + return -ENODEV; + /* Put ARC to wait for CIO reset event to happen */ val = ioread32(nhi->iobase + REG_FW_STS); val |= REG_FW_STS_CIO_RESET_REQ; @@ -859,6 +862,9 @@ static int icm_firmware_init(struct tb *tb) break; default: + if (ret < 0) + return ret; + tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); return -ENODEV; } diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 05af126a243597500549255b55cdde8d37bc652a..16c607075ede41ec4c8f1bba2e46920454e837a9 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -846,6 +846,7 @@ static const struct dev_pm_ops nhi_pm_ops = { * we just disable hotplug, the * pci-tunnels stay alive. */ + .thaw_noirq = nhi_resume_noirq, .restore_noirq = nhi_resume_noirq, .suspend = nhi_suspend, .freeze = nhi_suspend, diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 3953d17202a87f239b2a9209d030db87366fd30c..8bd1371099808975546fc301be2ccbdb2400f4de 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -716,6 +716,13 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) if (sw->authorized) goto unlock; + /* + * Make sure there is no PCIe rescan ongoing when a new PCIe + * tunnel is created. Otherwise the PCIe rescan code might find + * the new tunnel too early. + */ + pci_lock_rescan_remove(); + switch (val) { /* Approve switch */ case 1: @@ -735,6 +742,8 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) break; } + pci_unlock_rescan_remove(); + if (!ret) { sw->authorized = val; /* Notify status change to the userspace */ diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 0a3c9665e015492f7fbeae5c33e1900961b817c6..f46bd1af7a10b290bddeeda3aba53fd8a0acd665 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -133,6 +133,9 @@ struct gsm_dlci { struct mutex mutex; /* Link layer */ + int mode; +#define DLCI_MODE_ABM 0 /* Normal Asynchronous Balanced Mode */ +#define DLCI_MODE_ADM 1 /* Asynchronous Disconnected Mode */ spinlock_t lock; /* Protects the internal state */ struct timer_list t1; /* Retransmit timer for SABM and UA */ int retries; @@ -1376,7 +1379,13 @@ static struct gsm_control *gsm_control_send(struct gsm_mux *gsm, ctrl->data = data; ctrl->len = clen; gsm->pending_cmd = ctrl; - gsm->cretries = gsm->n2; + + /* If DLCI0 is in ADM mode skip retries, it won't respond */ + if (gsm->dlci[0]->mode == DLCI_MODE_ADM) + gsm->cretries = 1; + else + gsm->cretries = gsm->n2; + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); gsm_control_transmit(gsm, ctrl); spin_unlock_irqrestore(&gsm->control_lock, flags); @@ -1463,6 +1472,10 @@ static void gsm_dlci_open(struct gsm_dlci *dlci) * in which case an opening port goes back to closed and a closing port * is simply put into closed state (any further frames from the other * end will get a DM response) + * + * Some control dlci can stay in ADM mode with other dlci working just + * fine. In that case we can just keep the control dlci open after the + * DLCI_OPENING retries time out. */ static void gsm_dlci_t1(unsigned long data) @@ -1476,8 +1489,16 @@ static void gsm_dlci_t1(unsigned long data) if (dlci->retries) { gsm_command(dlci->gsm, dlci->addr, SABM|PF); mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); - } else + } else if (!dlci->addr && gsm->control == (DM | PF)) { + if (debug & 8) + pr_info("DLCI %d opening in ADM mode.\n", + dlci->addr); + dlci->mode = DLCI_MODE_ADM; + gsm_dlci_open(dlci); + } else { gsm_dlci_close(dlci); + } + break; case DLCI_CLOSING: dlci->retries--; @@ -1495,8 +1516,8 @@ static void gsm_dlci_t1(unsigned long data) * @dlci: DLCI to open * * Commence opening a DLCI from the Linux side. We issue SABM messages - * to the modem which should then reply with a UA, at which point we - * will move into open state. Opening is done asynchronously with retry + * to the modem which should then reply with a UA or ADM, at which point + * we will move into open state. Opening is done asynchronously with retry * running off timers and the responses. */ @@ -2864,11 +2885,22 @@ static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk) static int gsm_carrier_raised(struct tty_port *port) { struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + struct gsm_mux *gsm = dlci->gsm; + /* Not yet open so no carrier info */ if (dlci->state != DLCI_OPEN) return 0; if (debug & 2) return 1; + + /* + * Basic mode with control channel in ADM mode may not respond + * to CMD_MSC at all and modem_rx is empty. + */ + if (gsm->encoding == 0 && gsm->dlci[0]->mode == DLCI_MODE_ADM && + !dlci->modem_rx) + return 1; + return dlci->modem_rx & TIOCM_CD; } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index faf50df816224feb643d75f79c75aa2800297474..1c70541a146724fcfcea8bda926d9679e7c64d10 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2182,6 +2182,12 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, } if (tty_hung_up_p(file)) break; + /* + * Abort readers for ttys which never actually + * get hung up. See __tty_hangup(). + */ + if (test_bit(TTY_HUPPING, &tty->flags)) + break; if (!timeout) break; if (file->f_flags & O_NONBLOCK) { diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index c55624703fdf04a74fc167b702a50212204e4c62..e0aa5f03004cc6c1754684716835acbd1f7f19b9 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -37,6 +37,7 @@ #define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 #define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 +#define UART_EXAR_INT0 0x80 #define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ #define UART_EXAR_FCTR 0x08 /* Feature Control Register */ @@ -124,6 +125,7 @@ struct exar8250_board { struct exar8250 { unsigned int nr; struct exar8250_board *board; + void __iomem *virt; int line[0]; }; @@ -134,12 +136,9 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, const struct exar8250_board *board = priv->board; unsigned int bar = 0; - if (!pcim_iomap_table(pcidev)[bar] && !pcim_iomap(pcidev, bar, 0)) - return -ENOMEM; - port->port.iotype = UPIO_MEM; port->port.mapbase = pci_resource_start(pcidev, bar) + offset; - port->port.membase = pcim_iomap_table(pcidev)[bar] + offset; + port->port.membase = priv->virt + offset; port->port.regshift = board->reg_shift; return 0; @@ -423,6 +422,25 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev) port->port.private_data = NULL; } +/* + * These Exar UARTs have an extra interrupt indicator that could fire for a + * few interrupts that are not presented/cleared through IIR. One of which is + * a wakeup interrupt when coming out of sleep. These interrupts are only + * cleared by reading global INT0 or INT1 registers as interrupts are + * associated with channel 0. The INT[3:0] registers _are_ accessible from each + * channel's address space, but for the sake of bus efficiency we register a + * dedicated handler at the PCI device level to handle them. + */ +static irqreturn_t exar_misc_handler(int irq, void *data) +{ + struct exar8250 *priv = data; + + /* Clear all PCI interrupts by reading INT0. No effect on IIR */ + ioread8(priv->virt + UART_EXAR_INT0); + + return IRQ_HANDLED; +} + static int exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -451,6 +469,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) return -ENOMEM; priv->board = board; + priv->virt = pcim_iomap(pcidev, bar, 0); + if (!priv->virt) + return -ENOMEM; pci_set_master(pcidev); @@ -464,6 +485,11 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) uart.port.irq = pci_irq_vector(pcidev, 0); uart.port.dev = &pcidev->dev; + rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler, + IRQF_SHARED, "exar_uart", priv); + if (rc) + return rc; + for (i = 0; i < nr_ports && i < maxnr; i++) { rc = board->setup(priv, pcidev, &uart, i); if (rc) { diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 951680640ad5b82705401f31491d7899730e5a06..3613a6aabfb37db7abf529bcb034d300b579d7b8 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -321,6 +321,7 @@ static const struct of_device_id of_platform_serial_table[] = { { .compatible = "mrvl,mmp-uart", .data = (void *)PORT_XSCALE, }, { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 8dcfd4978a036c7f72820cfccdd3c4c958fc8fba..e32c51d549c3d3a7e4edc9cbb3421d9ff1285b21 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -51,6 +51,10 @@ #define UART_EXAR_SLEEP 0x8b /* Sleep mode */ #define UART_EXAR_DVID 0x8d /* Device identification */ +/* Nuvoton NPCM timeout register */ +#define UART_NPCM_TOR 7 +#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */ + /* * Debugging. */ @@ -297,6 +301,15 @@ static const struct serial8250_config uart_config[] = { UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, .flags = UART_CAP_FIFO, }, + [PORT_NPCM] = { + .name = "Nuvoton 16550", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -445,7 +458,6 @@ static void io_serial_out(struct uart_port *p, int offset, int value) } static int serial8250_default_handle_irq(struct uart_port *port); -static int exar_handle_irq(struct uart_port *port); static void set_io_from_upio(struct uart_port *p) { @@ -1890,26 +1902,6 @@ static int serial8250_default_handle_irq(struct uart_port *port) return ret; } -/* - * These Exar UARTs have an extra interrupt indicator that could - * fire for a few unimplemented interrupts. One of which is a - * wakeup event when coming out of sleep. Put this here just - * to be on the safe side that these interrupts don't go unhandled. - */ -static int exar_handle_irq(struct uart_port *port) -{ - unsigned int iir = serial_port_in(port, UART_IIR); - int ret = 0; - - if (((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) && - serial_port_in(port, UART_EXAR_INT0) != 0) - ret = 1; - - ret |= serial8250_handle_irq(port, iir); - - return ret; -} - /* * Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP * have a programmable TX threshold that triggers the THRE interrupt in @@ -2168,6 +2160,15 @@ int serial8250_do_startup(struct uart_port *port) UART_DA830_PWREMU_MGMT_FREE); } + if (port->type == PORT_NPCM) { + /* + * Nuvoton calls the scratch register 'UART_TOR' (timeout + * register). Enable it, and set TIOC (timeout interrupt + * comparator) to be 0x20 for correct operation. + */ + serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20); + } + #ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the @@ -2490,6 +2491,15 @@ static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up, return quot_16 >> 4; } +/* Nuvoton NPCM UARTs have a custom divisor calculation */ +static unsigned int npcm_get_divisor(struct uart_8250_port *up, + unsigned int baud) +{ + struct uart_port *port = &up->port; + + return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; +} + static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud, unsigned int *frac) @@ -2510,6 +2520,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, quot = 0x8002; else if (up->port.type == PORT_XR17V35X) quot = xr17v35x_get_divisor(up, baud, frac); + else if (up->port.type == PORT_NPCM) + quot = npcm_get_divisor(up, baud); else quot = uart_get_divisor(port, baud); @@ -3074,11 +3086,6 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (port->type == PORT_UNKNOWN) serial8250_release_std_resource(up); - /* Fixme: probably not the best place for this */ - if ((port->type == PORT_XR17V35X) || - (port->type == PORT_XR17D15X)) - port->handle_irq = exar_handle_irq; - register_dev_spec_attr_grp(up); up->fcr = uart_config[up->port.type].fcr; } diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 17dba0af5ee9a29e54bdde68d61e7dcbcb098c20..ac667b47f19970c451712280c2a6d7fbbe7a5ff2 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -172,7 +172,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match) */ int __init setup_earlycon(char *buf) { - const struct earlycon_id *match; + const struct earlycon_id **p_match; if (!buf || !buf[0]) return -EINVAL; @@ -180,7 +180,9 @@ int __init setup_earlycon(char *buf) if (early_con.flags & CON_ENABLED) return -EALREADY; - for (match = __earlycon_table; match < __earlycon_table_end; match++) { + for (p_match = __earlycon_table; p_match < __earlycon_table_end; + p_match++) { + const struct earlycon_id *match = *p_match; size_t len = strlen(match->name); if (strncmp(buf, match->name, len)) diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index a260cde743e272657673474d9612c0bf663d3995..5532c440bf61be00193101ecc2e15e5e63e17400 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -245,7 +245,8 @@ static void kgdboc_put_char(u8 chr) kgdb_tty_line, chr); } -static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) +static int param_set_kgdboc_var(const char *kmessage, + const struct kernel_param *kp) { int len = strlen(kmessage); diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 93b1d95386490189b25c148e751c613b0c51bbf0..4346ed2c1c4a181fc2ca9a4500c65f52d595632b 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 7e77bd2118ad6c1b8dd0cd600fb2075e66564ff4..562d31073f9a2c588c62de7f0d64c570bf9fcf62 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -585,6 +585,14 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) return; } + /* + * Some console devices aren't actually hung up for technical and + * historical reasons, which can lead to indefinite interruptible + * sleep in n_tty_read(). The following explicitly tells + * n_tty_read() to abort readers. + */ + set_bit(TTY_HUPPING, &tty->flags); + /* inuse_filps is protected by the single tty lock, this really needs to change if we want to flush the workqueue with the lock held */ @@ -639,6 +647,7 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) * from the ldisc side, which is now guaranteed. */ set_bit(TTY_HUPPED, &tty->flags); + clear_bit(TTY_HUPPING, &tty->flags); tty_unlock(tty); if (f) @@ -2806,7 +2815,10 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) kref_init(&tty->kref); tty->magic = TTY_MAGIC; - tty_ldisc_init(tty); + if (tty_ldisc_init(tty)) { + kfree(tty); + return NULL; + } tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 7c895684c3efd1892913f31a9d093d90d1238f33..ca656ef8de648c7fcca3757ba981f04528f6966c 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -175,12 +175,11 @@ static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) return ERR_CAST(ldops); } - ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); - if (ld == NULL) { - put_ldops(ldops); - return ERR_PTR(-ENOMEM); - } - + /* + * There is no way to handle allocation failure of only 16 bytes. + * Let's simplify error handling and save more memory. + */ + ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL | __GFP_NOFAIL); ld->ops = ldops; ld->tty = tty; @@ -526,19 +525,16 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld) static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) { /* There is an outstanding reference here so this is safe */ - old = tty_ldisc_get(tty, old->ops->num); - WARN_ON(IS_ERR(old)); - tty->ldisc = old; - tty_set_termios_ldisc(tty, old->ops->num); - if (tty_ldisc_open(tty, old) < 0) { - tty_ldisc_put(old); + if (tty_ldisc_failto(tty, old->ops->num) < 0) { + const char *name = tty_name(tty); + + pr_warn("Falling back ldisc for %s.\n", name); /* The traditional behaviour is to fall back to N_TTY, we want to avoid falling back to N_NULL unless we have no choice to avoid the risk of breaking anything */ if (tty_ldisc_failto(tty, N_TTY) < 0 && tty_ldisc_failto(tty, N_NULL) < 0) - panic("Couldn't open N_NULL ldisc for %s.", - tty_name(tty)); + panic("Couldn't open N_NULL ldisc for %s.", name); } } @@ -823,12 +819,13 @@ EXPORT_SYMBOL_GPL(tty_ldisc_release); * the tty structure is not completely set up when this call is made. */ -void tty_ldisc_init(struct tty_struct *tty) +int tty_ldisc_init(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); if (IS_ERR(ld)) - panic("n_tty: init_tty"); + return PTR_ERR(ld); tty->ldisc = ld; + return 0; } /** diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 5a5813d01cf8f18bca50c37689d1f8974eac7003..de67abbda921fd44cf874162f097dc150db954be 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1352,6 +1352,11 @@ static void csi_m(struct vc_data *vc) case 3: vc->vc_italic = 1; break; + case 21: + /* + * No console drivers support double underline, so + * convert it to a single underline. + */ case 4: vc->vc_underline = 1; break; @@ -1387,7 +1392,6 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 1; vc->vc_toggle_meta = 1; break; - case 21: case 22: vc->vc_intensity = 1; break; diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index 48d5327d38d420d84f49d52aa9a904d2ee174d80..fe5cdda80b2ca3b2837e714f5e3f326300e6a397 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -124,6 +124,13 @@ hv_uio_probe(struct hv_device *dev, if (ret) goto fail; + /* Communicating with host has to be via shared memory not hypercall */ + if (!dev->channel->offermsg.monitor_allocated) { + dev_err(&dev->device, "vmbus channel requires hypercall\n"); + ret = -ENOTSUPP; + goto fail_close; + } + dev->channel->inbound.ring_buffer->interrupt_mask = 1; set_channel_read_mode(dev->channel, HV_CALL_DIRECT); diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index bd3e0c5a6db25e7a162d922c6508de1ad0b68025..212289c55b6f2d3eb1b2563983f612e0738f8633 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -210,8 +210,13 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) if (!udev->parent) rc = hcd_bus_suspend(udev, msg); - /* Non-root devices don't need to do anything for FREEZE or PRETHAW */ - else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW) + /* + * Non-root USB2 devices don't need to do anything for FREEZE + * or PRETHAW. USB3 devices don't support global suspend and + * needs to be selectively suspended. + */ + else if ((msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW) + && (udev->speed < USB_SPEED_SUPER)) rc = 0; else rc = usb_port_suspend(udev, msg); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 3baf532f7a0e282843be36297a945421aa2f5ca0..d41be10f20520d20331ae5331a78c96dfe4f9139 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2440,6 +2440,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) { + pm_wakeup_event(&hcd->self.root_hub->dev, 0); set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); queue_work(pm_wq, &hcd->wakeup_work); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a0e117abc7f4a5bac348d99f50def80f0b4022e7..b0eb86530af9f1f6878d683e4e8329cd64729971 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -661,12 +661,17 @@ void usb_wakeup_notification(struct usb_device *hdev, unsigned int portnum) { struct usb_hub *hub; + struct usb_port *port_dev; if (!hdev) return; hub = usb_hub_to_struct_hub(hdev); if (hub) { + port_dev = hub->ports[portnum - 1]; + if (port_dev && port_dev->child) + pm_wakeup_event(&port_dev->child->dev, 0); + set_bit(portnum, hub->wakeup_bits); kick_hub_wq(hub); } @@ -3426,8 +3431,11 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); - if (status == 0 && !port_is_suspended(hub, portstatus)) + if (status == 0 && !port_is_suspended(hub, portstatus)) { + if (portchange & USB_PORT_STAT_C_SUSPEND) + pm_wakeup_event(&udev->dev, 0); goto SuspendCleared; + } /* see 7.1.7.7; affects power usage, but not budgeting */ if (hub_is_superspeed(hub->hdev)) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 4f1c6f8d4352b7ee1c99106471bcbb1f33d3894c..40ce175655e656c3d48c42facadf4cd4578aada0 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -45,6 +45,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, + /* HP v222w 16GB Mini USB Drive */ + { USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index c2631145f404943fc148b747c4620e0a5165153d..9bd60ec83ac6d39dafd27061600423c56cc33b68 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3277,7 +3277,6 @@ static void dwc2_conn_id_status_change(struct work_struct *work) dwc2_core_init(hsotg, false); dwc2_enable_global_interrupts(hsotg); spin_lock_irqsave(&hsotg->lock, flags); - dwc2_hsotg_disconnect(hsotg); dwc2_hsotg_core_init_disconnected(hsotg, false); spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_hsotg_core_connect(hsotg); @@ -3296,8 +3295,12 @@ static void dwc2_conn_id_status_change(struct work_struct *work) if (count > 250) dev_err(hsotg->dev, "Connection id status change timed out\n"); - hsotg->op_state = OTG_STATE_A_HOST; + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_hsotg_disconnect(hsotg); + spin_unlock_irqrestore(&hsotg->lock, flags); + + hsotg->op_state = OTG_STATE_A_HOST; /* Initialize the Core for Host mode */ dwc2_core_init(hsotg, false); dwc2_enable_global_interrupts(hsotg); diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9198554e8755fba85b5631be8fa64f21581fec35..10a3ff124290b071d6609b98eac594e24c5e8049 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -167,12 +167,8 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) int retries = 1000; int ret; - /* Reset PHYs */ + /* Reset and initialize PHYs */ usb_phy_reset(dwc->usb2_phy); - - if (dwc->maximum_speed == USB_SPEED_SUPER) - usb_phy_reset(dwc->usb3_phy); - ret = usb_phy_init(dwc->usb2_phy); if (ret) { pr_err("%s: usb_phy_init(dwc->usb2_phy) returned %d\n", @@ -180,9 +176,10 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) return ret; } - if (dwc->maximum_speed == USB_SPEED_HIGH) + if (dwc->maximum_speed <= USB_SPEED_HIGH) goto generic_phy_init; + usb_phy_reset(dwc->usb3_phy); ret = usb_phy_init(dwc->usb3_phy); if (ret == -EBUSY) { /* @@ -546,6 +543,10 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_del_phy_power_chg_quirk) reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; + if (dwc->ssp_u3_u0_quirk) + reg |= (DWC3_GUSB3PIPECTL_UX_EXIT_PX | + DWC3_GUSB3PIPECTL_P3EXSIGP2); + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); @@ -723,6 +724,9 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) if (dwc->revision < DWC3_REVISION_190A) reg |= DWC3_GCTL_U2RSTECN; + if (dwc->disable_clk_gating) + reg |= DWC3_GCTL_DSBLCLKGTNG; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); } @@ -762,7 +766,7 @@ int dwc3_core_init(struct dwc3 *dwc) /* Handle USB2.0-only core configuration */ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { - if (dwc->maximum_speed == USB_SPEED_SUPER) + if (dwc->maximum_speed >= USB_SPEED_SUPER) dwc->maximum_speed = USB_SPEED_HIGH; } @@ -1039,6 +1043,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->tx_de_emphasis_quirk = device_property_read_bool(dev, "snps,tx_de_emphasis_quirk"); + dwc->ssp_u3_u0_quirk = device_property_read_bool(dev, + "snps,ssp-u3-u0-quirk"); device_property_read_u8(dev, "snps,tx_de_emphasis", &tx_de_emphasis); device_property_read_string(dev, "snps,hsphy_interface", @@ -1049,6 +1055,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,bus-suspend-enable"); dwc->usb3_u1u2_disable = device_property_read_bool(dev, "snps,usb3-u1u2-disable"); + dwc->disable_clk_gating = device_property_read_bool(dev, + "snps,disable-clk-gating"); dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4c41a33c0b3589ece969aecea8cb5de620865c7d..64e0fd0b33cf654fd70dccc4267dc1f972ca023a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -253,6 +253,7 @@ #define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1) #define DWC3_GUSB3PIPECTL_DEPOCHANGE BIT(18) #define DWC3_GUSB3PIPECTL_SUSPHY BIT(17) +#define DWC3_GUSB3PIPECTL_P3EXSIGP2 BIT(10) #define DWC3_GUSB3PIPECTL_LFPSFILT BIT(9) #define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8) #define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3) @@ -1121,8 +1122,10 @@ struct dwc3 { unsigned dis_tx_ipgap_linecheck_quirk:1; unsigned tx_de_emphasis_quirk:1; + unsigned ssp_u3_u0_quirk:1; unsigned tx_de_emphasis:2; unsigned err_evt_seen:1; + unsigned disable_clk_gating:1; unsigned enable_bus_suspend:1; unsigned usb3_u1u2_disable:1; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 1626f46bd8eca516f1cf422673546e6d56b2ea4e..85142b7e5d7ed6bd842d397ccb61fb513252b09c 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -275,9 +275,6 @@ struct dwc3_msm { #define USB_SSPHY_1P8_VOL_MAX 1800000 /* uV */ #define USB_SSPHY_1P8_HPM_LOAD 23000 /* uA */ -#define DSTS_CONNECTSPD_SS 0x4 - - static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc); static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA); static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned int event); @@ -392,7 +389,7 @@ static bool dwc3_msm_is_ss_rhport_connected(struct dwc3_msm *mdwc) for (i = 0; i < num_ports; i++) { reg = dwc3_msm_read_reg(mdwc->base, USB3_PORTSC + i*0x10); - if ((reg & PORT_CONNECT) && DEV_SUPERSPEED(reg)) + if ((reg & PORT_CONNECT) && DEV_SUPERSPEED_ANY(reg)) return true; } @@ -409,7 +406,7 @@ static bool dwc3_msm_is_host_superspeed(struct dwc3_msm *mdwc) for (i = 0; i < num_ports; i++) { reg = dwc3_msm_read_reg(mdwc->base, USB3_PORTSC + i*0x10); - if ((reg & PORT_PE) && DEV_SUPERSPEED(reg)) + if ((reg & PORT_PE) && DEV_SUPERSPEED_ANY(reg)) return true; } @@ -421,7 +418,11 @@ static inline bool dwc3_msm_is_dev_superspeed(struct dwc3_msm *mdwc) u8 speed; speed = dwc3_msm_read_reg(mdwc->base, DWC3_DSTS) & DWC3_DSTS_CONNECTSPD; - return !!(speed & DSTS_CONNECTSPD_SS); + if ((speed & DWC3_DSTS_SUPERSPEED) || + (speed & DWC3_DSTS_SUPERSPEED_PLUS)) + return true; + + return false; } static inline bool dwc3_msm_is_superspeed(struct dwc3_msm *mdwc) @@ -1209,7 +1210,7 @@ static void gsi_configure_ep(struct usb_ep *ep, struct usb_gsi_request *request) | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)); /* Burst size is only needed in SuperSpeed mode */ - if (dwc->gadget.speed == USB_SPEED_SUPER) { + if (dwc->gadget.speed >= USB_SPEED_SUPER) { u32 burst = dep->endpoint.maxburst - 1; params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst); @@ -2112,6 +2113,60 @@ static void configure_nonpdc_usb_interrupt(struct dwc3_msm *mdwc, } } +enum bus_vote { + BUS_VOTE_INVALID, + BUS_VOTE_SUSPEND, + BUS_VOTE_NOMINAL, + BUS_VOTE_SVS +}; + +static int dwc3_msm_update_bus_bw(struct dwc3_msm *mdwc, enum bus_vote bv) +{ + int ret = 0; + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + + if (!mdwc->bus_perf_client) + return 0; + + dbg_event(0xFF, "bus_vote_start", bv); + + switch (bv) { + case BUS_VOTE_SVS: + /* On some platforms SVS does not have separate vote. Vote for + * nominal if svs usecase does not exist + */ + if (mdwc->bus_scale_table->num_usecases == 2) + goto nominal_vote; + + /* index starts from zero */ + ret = msm_bus_scale_client_update_request( + mdwc->bus_perf_client, 2); + if (ret) + dev_err(mdwc->dev, "bus bw voting failed %d\n", ret); + break; + case BUS_VOTE_NOMINAL: +nominal_vote: + ret = msm_bus_scale_client_update_request( + mdwc->bus_perf_client, 1); + if (ret) + dev_err(mdwc->dev, "bus bw voting failed %d\n", ret); + break; + case BUS_VOTE_SUSPEND: + ret = msm_bus_scale_client_update_request( + mdwc->bus_perf_client, 0); + if (ret) + dev_err(mdwc->dev, "bus bw voting failed %d\n", ret); + break; + default: + dev_err(mdwc->dev, "Unsupported bus vote:%d\n", bv); + ret = -EINVAL; + } + + dbg_event(0xFF, "bus_vote_end", bv); + + return ret; + +} static int dwc3_msm_suspend(struct dwc3_msm *mdwc) { int ret; @@ -2194,7 +2249,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc) usb_phy_set_suspend(mdwc->hs_phy, 1); /* Suspend SS PHY */ - if (dwc->maximum_speed == USB_SPEED_SUPER) { + if (dwc->maximum_speed >= USB_SPEED_SUPER) { /* indicate phy about SS mode */ if (dwc3_msm_is_superspeed(mdwc)) mdwc->ss_phy->flags |= DEVICE_IN_SS_MODE; @@ -2243,15 +2298,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc) } } - /* Remove bus voting */ - if (mdwc->bus_perf_client) { - dbg_event(0xFF, "bus_devote_start", 0); - ret = msm_bus_scale_client_update_request( - mdwc->bus_perf_client, 0); - dbg_event(0xFF, "bus_devote_finish", 0); - if (ret) - dev_err(mdwc->dev, "bus bw unvoting failed %d\n", ret); - } + dwc3_msm_update_bus_bw(mdwc, BUS_VOTE_SUSPEND); /* * release wakeup source with timeout to defer system suspend to @@ -2309,15 +2356,10 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) pm_stay_awake(mdwc->dev); - /* Enable bus voting */ - if (mdwc->bus_perf_client) { - dbg_event(0xFF, "bus_vote_start", 1); - ret = msm_bus_scale_client_update_request( - mdwc->bus_perf_client, 1); - dbg_event(0xFF, "bus_vote_finish", 1); - if (ret) - dev_err(mdwc->dev, "bus bw voting failed %d\n", ret); - } + if (mdwc->in_host_mode && mdwc->max_rh_port_speed == USB_SPEED_HIGH) + dwc3_msm_update_bus_bw(mdwc, BUS_VOTE_SVS); + else + dwc3_msm_update_bus_bw(mdwc, BUS_VOTE_NOMINAL); /* Vote for TCXO while waking up USB HSPHY */ ret = clk_prepare_enable(mdwc->xo_clk); @@ -2369,7 +2411,7 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) clk_prepare_enable(mdwc->bus_aggr_clk); /* Resume SS PHY */ - if (dwc->maximum_speed == USB_SPEED_SUPER && + if (dwc->maximum_speed >= USB_SPEED_SUPER && mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) { mdwc->ss_phy->flags &= ~(PHY_LANE_A | PHY_LANE_B); if (mdwc->typec_orientation == ORIENTATION_CC1) @@ -2512,27 +2554,26 @@ static void dwc3_resume_work(struct work_struct *w) dev_dbg(mdwc->dev, "%s: dwc3 resume work\n", __func__); - if (mdwc->vbus_active && !mdwc->in_restart) { + if (mdwc->extcon && mdwc->vbus_active && !mdwc->in_restart) { extcon_id = EXTCON_USB; edev = mdwc->extcon[mdwc->ext_idx].edev; - } else if (mdwc->id_state == DWC3_ID_GROUND) { + } else if (mdwc->extcon && mdwc->id_state == DWC3_ID_GROUND) { extcon_id = EXTCON_USB_HOST; edev = mdwc->extcon[mdwc->ext_idx].edev; } /* Check speed and Type-C polarity values in order to configure PHY */ if (edev && extcon_get_state(edev, extcon_id)) { - ret = extcon_get_property(edev, extcon_id, - EXTCON_PROP_USB_SS, &val); - /* Use default dwc->maximum_speed if speed isn't reported */ - if (!ret) - dwc->maximum_speed = (val.intval == 0) ? - USB_SPEED_HIGH : USB_SPEED_SUPER; - if (dwc->maximum_speed > dwc->max_hw_supp_speed) dwc->maximum_speed = dwc->max_hw_supp_speed; + ret = extcon_get_property(edev, extcon_id, + EXTCON_PROP_USB_SS, &val); + + if (!ret && val.intval == 0) + dwc->maximum_speed = USB_SPEED_HIGH; + if (mdwc->override_usb_speed) { dwc->maximum_speed = mdwc->override_usb_speed; dwc->gadget.max_speed = dwc->maximum_speed; @@ -3110,7 +3151,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node, *dwc3_node; struct device *dev = &pdev->dev; - union power_supply_propval pval = {0}; struct dwc3_msm *mdwc; struct dwc3 *dwc; struct resource *res; @@ -3361,26 +3401,12 @@ static int dwc3_msm_probe(struct platform_device *pdev) } mutex_init(&mdwc->suspend_resume_mutex); - mdwc->usb_psy = power_supply_get_by_name("usb"); - if (!mdwc->usb_psy) { - dev_warn(mdwc->dev, "Could not get usb power_supply\n"); - pval.intval = -EINVAL; - } else { - power_supply_get_property(mdwc->usb_psy, - POWER_SUPPLY_PROP_PRESENT, &pval); - } ret = dwc3_msm_extcon_register(mdwc); if (ret) - goto put_psy; + goto put_dwc3; - if (!pval.intval) { - /* USB cable is not connected */ - schedule_delayed_work(&mdwc->sm_work, 0); - } else { - if (!mdwc->vbus_active && mdwc->id_state && pval.intval > 0) - dev_info(mdwc->dev, "charger detection in progress\n"); - } + schedule_delayed_work(&mdwc->sm_work, 0); device_create_file(&pdev->dev, &dev_attr_mode); device_create_file(&pdev->dev, &dev_attr_speed); @@ -3395,10 +3421,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) return 0; -put_psy: - if (mdwc->usb_psy) - power_supply_put(mdwc->usb_psy); - put_dwc3: if (mdwc->bus_perf_client) msm_bus_scale_unregister_client(mdwc->bus_perf_client); @@ -3499,18 +3521,10 @@ static int dwc3_msm_host_notifier(struct notifier_block *nb, struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, host_nb); struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); struct usb_device *udev = ptr; - union power_supply_propval pval; - unsigned int max_power; if (event != USB_DEVICE_ADD && event != USB_DEVICE_REMOVE) return NOTIFY_DONE; - if (!mdwc->usb_psy) { - mdwc->usb_psy = power_supply_get_by_name("usb"); - if (!mdwc->usb_psy) - return NOTIFY_DONE; - } - /* * For direct-attach devices, new udev is direct child of root hub * i.e. dwc -> xhci -> root_hub -> udev @@ -3530,31 +3544,17 @@ static int dwc3_msm_host_notifier(struct notifier_block *nb, "set hs core clk rate %ld\n", mdwc->core_clk_rate_hs); mdwc->max_rh_port_speed = USB_SPEED_HIGH; + dwc3_msm_update_bus_bw(mdwc, BUS_VOTE_SVS); } else { mdwc->max_rh_port_speed = USB_SPEED_SUPER; } - - if (udev->speed >= USB_SPEED_SUPER) - max_power = udev->actconfig->desc.bMaxPower * 8; - else - max_power = udev->actconfig->desc.bMaxPower * 2; - dev_dbg(mdwc->dev, "%s configured bMaxPower:%d (mA)\n", - dev_name(&udev->dev), max_power); - - /* inform PMIC of max power so it can optimize boost */ - pval.intval = max_power * 1000; - power_supply_set_property(mdwc->usb_psy, - POWER_SUPPLY_PROP_BOOST_CURRENT, &pval); } else { - pval.intval = 0; - power_supply_set_property(mdwc->usb_psy, - POWER_SUPPLY_PROP_BOOST_CURRENT, &pval); - /* set rate back to default core clk rate */ clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate); dev_dbg(mdwc->dev, "set core clk rate %ld\n", mdwc->core_clk_rate); mdwc->max_rh_port_speed = USB_SPEED_UNKNOWN; + dwc3_msm_update_bus_bw(mdwc, BUS_VOTE_NOMINAL); } } @@ -3637,7 +3637,7 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) dev_dbg(mdwc->dev, "%s: turn on host\n", __func__); mdwc->hs_phy->flags |= PHY_HOST_MODE; - if (dwc->maximum_speed == USB_SPEED_SUPER) { + if (dwc->maximum_speed >= USB_SPEED_SUPER) { mdwc->ss_phy->flags |= PHY_HOST_MODE; usb_phy_notify_connect(mdwc->ss_phy, USB_SPEED_SUPER); @@ -3753,7 +3753,7 @@ static void dwc3_override_vbus_status(struct dwc3_msm *mdwc, bool vbus_present) vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL : 0); /* Update only if Super Speed is supported */ - if (dwc->maximum_speed == USB_SPEED_SUPER) { + if (dwc->maximum_speed >= USB_SPEED_SUPER) { /* Update VBUS Valid from SSPHY to controller */ dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, LANE0_PWR_PRESENT, @@ -3843,10 +3843,14 @@ static int dwc3_restart_usb_host_mode(struct notifier_block *nb, usb_speed = (event == 0 ? USB_SPEED_HIGH : USB_SPEED_SUPER); if (dwc->maximum_speed == usb_speed) - goto err; + return 0; dbg_event(0xFF, "fw_restarthost", 0); flush_delayed_work(&mdwc->sm_work); + + if (!mdwc->in_host_mode) + goto err; + dbg_event(0xFF, "stop_host_mode", dwc->maximum_speed); ret = dwc3_otg_start_host(mdwc, 0); if (ret) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 54343fbd85eef112f38283ee0376f911a2bc2cb8..bc5e91d4fac87c80d184fccd24267467666d0dd3 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -212,7 +212,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res)); if (ret) { dev_err(dev, "couldn't add resources to dwc3 device\n"); - return ret; + goto err; } dwc->pci = pci; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 59710e1383b008c236395e97a10099d2c4cc730a..925194ace52ca79cccb491f3b71b54fbea730232 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -275,18 +275,8 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc, struct dwc3_ep *dep) return 0; } -/** - * dwc3_gadget_giveback - call struct usb_request's ->complete callback - * @dep: The endpoint to whom the request belongs to - * @req: The request we're giving back - * @status: completion code for the request - * - * Must be called with controller's lock held and interrupts disabled. This - * function will unmap @req and call its ->complete() callback to notify upper - * layers that it has completed. - */ -void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, - int status) +void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, + struct dwc3_request *req, int status) { struct dwc3 *dwc = dep->dwc; @@ -299,11 +289,28 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, if (req->trb) usb_gadget_unmap_request_by_dev(dwc->sysdev, - &req->request, req->direction); + &req->request, req->direction); req->trb = NULL; - trace_dwc3_gadget_giveback(req); +} + +/** + * dwc3_gadget_giveback - call struct usb_request's ->complete callback + * @dep: The endpoint to whom the request belongs to + * @req: The request we're giving back + * @status: completion code for the request + * + * Must be called with controller's lock held and interrupts disabled. This + * function will unmap @req and call its ->complete() callback to notify upper + * layers that it has completed. + */ +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + dwc3_gadget_del_and_unmap_request(dep, req, status); spin_unlock(&dwc->lock); usb_gadget_giveback_request(&dep->endpoint, &req->request); @@ -1381,7 +1388,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) if (req->trb) memset(req->trb, 0, sizeof(struct dwc3_trb)); dep->queued_requests--; - dwc3_gadget_giveback(dep, req, ret); + dwc3_gadget_del_and_unmap_request(dep, req, ret); return ret; } @@ -1599,10 +1606,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, * consideration so we don't mess up our TRB ring * pointers. */ - wait_event_lock_irq(dep->wait_end_transfer, - !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), - dwc->lock); - if (!r->trb) goto out0; @@ -1677,9 +1680,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) unsigned transfer_in_flight; unsigned started; - if (dep->flags & DWC3_EP_STALL) - return 0; - if (dep->number > 1) trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); else @@ -1701,8 +1701,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) else dep->flags |= DWC3_EP_STALL; } else { - if (!(dep->flags & DWC3_EP_STALL)) - return 0; ret = dwc3_send_clear_stall_ep_cmd(dep); if (ret) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index bf2d0ce80c99af5c4b113135e62a553d1b919311..085f6f4f3552be00ae74084763dc19558f7bd89e 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -14,11 +14,16 @@ #include #include +#ifdef CONFIG_USB_F_NCM +#include +#endif + #ifdef CONFIG_USB_CONFIGFS_F_ACC extern int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl); void acc_disconnect(void); #endif + static struct class *android_class; static struct device *android_device; static int index; @@ -1499,6 +1504,18 @@ static int android_setup(struct usb_gadget *gadget, } } +#ifdef CONFIG_USB_F_NCM + if (value < 0) + value = ncm_ctrlrequest(cdev, c); + + /* + * for mirror link command case, if it already been handled, + * do not pass to composite_setup + */ + if (value == 0) + return value; +#endif + #ifdef CONFIG_USB_CONFIGFS_F_ACC if (value < 0) value = acc_ctrlrequest(cdev, c); @@ -1563,7 +1580,7 @@ static const struct usb_gadget_driver configfs_driver_template = { .suspend = composite_suspend, .resume = composite_resume, - .max_speed = USB_SPEED_SUPER, + .max_speed = USB_SPEED_SUPER_PLUS, .driver = { .owner = THIS_MODULE, .name = "configfs-gadget", diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c index 08b24137f815059b6af9dd17e290caa5467092f8..2e1d9beac545f78130632cdb15700a052c836368 100644 --- a/drivers/usb/gadget/function/f_ccid.c +++ b/drivers/usb/gadget/function/f_ccid.c @@ -638,7 +638,7 @@ static int ccid_function_bind(struct usb_configuration *c, ccid_fs_notify_desc.bEndpointAddress; ret = usb_assign_descriptors(f, ccid_fs_descs, ccid_hs_descs, - ccid_ss_descs, NULL); + ccid_ss_descs, ccid_ss_descs); if (ret) goto ep_auto_out_fail; diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c index 18402cb548e42b9b7ee2acf157b7c11a432ab378..fbd79142c8a21ab8920da148858513035271ff14 100644 --- a/drivers/usb/gadget/function/f_cdev.c +++ b/drivers/usb/gadget/function/f_cdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2011, 2013-2018, The Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 license terms, * and distributes only under these terms. * @@ -793,7 +793,7 @@ static int usb_cser_bind(struct usb_configuration *c, struct usb_function *f) } status = usb_assign_descriptors(f, cser_fs_function, cser_hs_function, - cser_ss_function, NULL); + cser_ss_function, cser_ss_function); if (status) goto fail; diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c index 78669fffbe49dea792614089189ae935c7866a7a..02de6c31739acab7d3050c14e02835c9d431a986 100644 --- a/drivers/usb/gadget/function/f_diag.c +++ b/drivers/usb/gadget/function/f_diag.c @@ -2,7 +2,7 @@ * Diag Function Device - Route ARM9 and ARM11 DIAG messages * between HOST and DEVICE. * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * Author: Brian Swetland * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -331,6 +331,8 @@ struct usb_diag_ch *usb_diag_open(const char *name, void *priv, struct usb_diag_ch *ch; unsigned long flags; int found = 0; + bool connected = false; + struct diag_context *dev; spin_lock_irqsave(&ch_lock, flags); /* Check if we already have a channel with this name */ @@ -358,6 +360,16 @@ struct usb_diag_ch *usb_diag_open(const char *name, void *priv, spin_unlock_irqrestore(&ch_lock, flags); } + if (ch->priv_usb) { + dev = ch->priv_usb; + spin_lock_irqsave(&dev->lock, flags); + connected = dev->configured; + spin_unlock_irqrestore(&dev->lock, flags); + } + + if (ch->notify && connected) + ch->notify(priv, USB_DIAG_CONNECT, NULL); + return ch; } EXPORT_SYMBOL(usb_diag_open); @@ -728,12 +740,7 @@ static void diag_function_unbind(struct usb_configuration *c, struct diag_context *ctxt = func_to_diag(f); unsigned long flags; - if (gadget_is_superspeed(c->cdev->gadget)) - usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - - usb_free_descriptors(f->fs_descriptors); + usb_free_all_descriptors(f); /* * Channel priv_usb may point to other diag function. @@ -773,36 +780,20 @@ static int diag_function_bind(struct usb_configuration *c, ctxt->out = ep; ep->driver_data = ctxt; - status = -ENOMEM; - /* copy descriptors, and track endpoint copies */ - f->fs_descriptors = usb_copy_descriptors(fs_diag_desc); - if (!f->fs_descriptors) + hs_bulk_in_desc.bEndpointAddress = + fs_bulk_in_desc.bEndpointAddress; + hs_bulk_out_desc.bEndpointAddress = + fs_bulk_out_desc.bEndpointAddress; + ss_bulk_in_desc.bEndpointAddress = + fs_bulk_in_desc.bEndpointAddress; + ss_bulk_out_desc.bEndpointAddress = + fs_bulk_out_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, fs_diag_desc, hs_diag_desc, + ss_diag_desc, ss_diag_desc); + if (status) goto fail; - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_bulk_in_desc.bEndpointAddress = - fs_bulk_in_desc.bEndpointAddress; - hs_bulk_out_desc.bEndpointAddress = - fs_bulk_out_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(hs_diag_desc); - if (!f->hs_descriptors) - goto fail; - } - - if (gadget_is_superspeed(c->cdev->gadget)) { - ss_bulk_in_desc.bEndpointAddress = - fs_bulk_in_desc.bEndpointAddress; - ss_bulk_out_desc.bEndpointAddress = - fs_bulk_out_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(ss_diag_desc); - if (!f->ss_descriptors) - goto fail; - } - /* Allow only first diag channel to update pid and serial no */ if (ctxt == list_first_entry(&diag_dev_list, struct diag_context, list_item)) @@ -810,12 +801,6 @@ static int diag_function_bind(struct usb_configuration *c, return 0; fail: - if (f->ss_descriptors) - usb_free_descriptors(f->ss_descriptors); - if (f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - if (f->fs_descriptors) - usb_free_descriptors(f->fs_descriptors); if (ctxt->out) ctxt->out->driver_data = NULL; if (ctxt->in) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 0ecbbd4465092cf101efabfbd2678ed73d658362..705d196657951596c26717e35bff440999e14702 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3331,6 +3331,7 @@ static int _ffs_func_bind(struct usb_configuration *c, ret = ss_len; goto error; } + func->function.ssp_descriptors = func->function.ss_descriptors; } else { ss_len = 0; } @@ -3545,6 +3546,11 @@ static bool ffs_func_req_match(struct usb_function *f, { struct ffs_function *func = ffs_func_from_usb(f); + if (!test_bit(FFS_FL_BOUND, &func->ffs->flags)) { + ffs_log("ffs function do not bind yet.\n"); + return false; + } + if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP)) return false; @@ -3791,6 +3797,7 @@ static void ffs_func_unbind(struct usb_configuration *c, func->function.fs_descriptors = NULL; func->function.hs_descriptors = NULL; func->function.ss_descriptors = NULL; + func->function.ssp_descriptors = NULL; func->interfaces_nums = NULL; ffs_event_add(ffs, FUNCTIONFS_UNBIND); diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 3ef9018b0fb152b25ba672b21138fab67f1a239e..e8b96378a50d185a3c00d1ea5db9425fd32c35ee 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -2466,59 +2466,35 @@ static int gsi_update_function_bind_params(struct f_gsi *gsi, spin_lock_init(&gsi->d_port.evt_q.q_lock); gsi->d_port.evt_q.head = gsi->d_port.evt_q.tail = MAXQUEUELEN - 1; - /* copy descriptors, and track endpoint copies */ - f->fs_descriptors = usb_copy_descriptors(info->fs_desc_hdr); - if (!gsi->function.fs_descriptors) - goto fail; - - /* support all relevant hardware speeds... we expect that when - * hardware is dual speed, all bulk-capable endpoints work at - * both speeds - */ - if (gadget_is_dualspeed(cdev->gadget)) { - if (info->fs_in_desc) - info->hs_in_desc->bEndpointAddress = - info->fs_in_desc->bEndpointAddress; - if (info->fs_out_desc) - info->hs_out_desc->bEndpointAddress = - info->fs_out_desc->bEndpointAddress; - if (info->fs_notify_desc) - info->hs_notify_desc->bEndpointAddress = - info->fs_notify_desc->bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(info->hs_desc_hdr); - if (!f->hs_descriptors) - goto fail; + if (info->fs_in_desc) { + info->hs_in_desc->bEndpointAddress = + info->fs_in_desc->bEndpointAddress; + info->ss_in_desc->bEndpointAddress = + info->fs_in_desc->bEndpointAddress; } - if (gadget_is_superspeed(cdev->gadget)) { - if (info->fs_in_desc) - info->ss_in_desc->bEndpointAddress = - info->fs_in_desc->bEndpointAddress; - - if (info->fs_out_desc) - info->ss_out_desc->bEndpointAddress = - info->fs_out_desc->bEndpointAddress; - if (info->fs_notify_desc) - info->ss_notify_desc->bEndpointAddress = - info->fs_notify_desc->bEndpointAddress; + if (info->fs_out_desc) { + info->hs_out_desc->bEndpointAddress = + info->fs_out_desc->bEndpointAddress; + info->ss_out_desc->bEndpointAddress = + info->fs_out_desc->bEndpointAddress; + } - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(info->ss_desc_hdr); - if (!f->ss_descriptors) - goto fail; + if (info->fs_notify_desc) { + info->hs_notify_desc->bEndpointAddress = + info->fs_notify_desc->bEndpointAddress; + info->ss_notify_desc->bEndpointAddress = + info->fs_notify_desc->bEndpointAddress; } + status = usb_assign_descriptors(f, info->fs_desc_hdr, info->hs_desc_hdr, + info->ss_desc_hdr, info->ss_desc_hdr); + if (status) + goto fail; + return 0; fail: - if (gadget_is_superspeed(cdev->gadget) && f->ss_descriptors) - usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(cdev->gadget) && f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - if (f->fs_descriptors) - usb_free_descriptors(f->fs_descriptors); if (gsi->c_port.notify_req) { kfree(gsi->c_port.notify_req->buf); usb_ep_free_request(gsi->c_port.notify, gsi->c_port.notify_req); @@ -2930,16 +2906,11 @@ static void gsi_unbind(struct usb_configuration *c, struct usb_function *f) if (gsi->prot_id == IPA_USB_MBIM) mbim_gsi_ext_config_desc.function.subCompatibleID[0] = 0; - if (gadget_is_superspeed(c->cdev->gadget)) { - usb_free_descriptors(f->ss_descriptors); - f->ss_descriptors = NULL; - } - if (gadget_is_dualspeed(c->cdev->gadget)) { - usb_free_descriptors(f->hs_descriptors); - f->hs_descriptors = NULL; - } - usb_free_descriptors(f->fs_descriptors); + usb_free_all_descriptors(f); + f->hs_descriptors = NULL; f->fs_descriptors = NULL; + f->ss_descriptors = NULL; + f->ssp_descriptors = NULL; if (gsi->c_port.notify) { kfree(gsi->c_port.notify_req->buf); diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 7d2e5e6e6ba2ee868bff352001471145b9d7f19b..f0a8e66e7a9cb7174de6f429bc5818c5f287d590 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -405,7 +405,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (err) { ERROR(midi, "%s: couldn't enqueue request: %d\n", midi->out_ep->name, err); - free_ep_req(midi->out_ep, req); + if (req->buf != NULL) + free_ep_req(midi->out_ep, req); return err; } } diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 892069269fa9f2b02539561b51f7ab252393f816..96af5de4ee7821103425e054232268c94387e00c 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1539,10 +1539,12 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, dev->function.fs_descriptors = fs_mtp_descs; dev->function.hs_descriptors = hs_mtp_descs; dev->function.ss_descriptors = ss_mtp_descs; + dev->function.ssp_descriptors = ss_mtp_descs; } else { dev->function.fs_descriptors = fs_ptp_descs; dev->function.hs_descriptors = hs_ptp_descs; dev->function.ss_descriptors = ss_ptp_descs; + dev->function.ssp_descriptors = ss_ptp_descs; } dev->function.bind = mtp_function_bind; dev->function.unbind = mtp_function_unbind; diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 45b334ceaf2e399987661652219f6af7032b139c..d0faf68a648ce97d426dd351a64c93fbc7131ecb 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1422,17 +1422,39 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) */ if (!ncm_opts->bound) { mutex_lock(&ncm_opts->lock); + ncm_opts->net = gether_setup_default(); + if (IS_ERR(ncm_opts->net)) { + status = PTR_ERR(ncm_opts->net); + mutex_unlock(&ncm_opts->lock); + goto error; + } gether_set_gadget(ncm_opts->net, cdev->gadget); status = gether_register_netdev(ncm_opts->net); mutex_unlock(&ncm_opts->lock); - if (status) - return status; + if (status) { + free_netdev(ncm_opts->net); + goto error; + } ncm_opts->bound = true; } + + /* export host's Ethernet address in CDC format */ + status = gether_get_host_addr_cdc(ncm_opts->net, ncm->ethaddr, + sizeof(ncm->ethaddr)); + if (status < 12) { /* strlen("01234567890a") */ + ERROR(cdev, "%s: failed to get host eth addr, err %d\n", + __func__, status); + status = -EINVAL; + goto netdev_cleanup; + } + ncm->port.ioport = netdev_priv(ncm_opts->net); + us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); - if (IS_ERR(us)) - return PTR_ERR(us); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto netdev_cleanup; + } ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; @@ -1504,7 +1526,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) fs_ncm_notify_desc.bEndpointAddress; status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, - ncm_ss_function, NULL); + ncm_ss_function, ncm_ss_function); if (status) goto fail; @@ -1533,7 +1555,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); } +netdev_cleanup: + gether_cleanup(netdev_priv(ncm_opts->net)); +error: ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; @@ -1574,15 +1599,60 @@ static struct config_item_type ncm_func_type = { .ct_owner = THIS_MODULE, }; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + +struct ncm_setup_desc { + struct work_struct work; + struct device *device; + uint8_t major; // Mirror Link major version + uint8_t minor; // Mirror Link minor version +}; + +static struct ncm_setup_desc *_ncm_setup_desc; + +#define MIRROR_LINK_STRING_LENGTH_MAX 32 +static void ncm_setup_work(struct work_struct *data) +{ + char mirror_link_string[MIRROR_LINK_STRING_LENGTH_MAX]; + char *envp[2] = { mirror_link_string, NULL }; + + snprintf(mirror_link_string, MIRROR_LINK_STRING_LENGTH_MAX, + "MirrorLink=V%d.%d", + _ncm_setup_desc->major, _ncm_setup_desc->minor); + kobject_uevent_env(&_ncm_setup_desc->device->kobj, KOBJ_CHANGE, envp); +} + +int ncm_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + int value = -EOPNOTSUPP; + + if (ctrl->bRequestType == 0x40 && ctrl->bRequest == 0xF0) { + _ncm_setup_desc->minor = (uint8_t)(ctrl->wValue >> 8); + _ncm_setup_desc->major = (uint8_t)(ctrl->wValue & 0xFF); + schedule_work(&_ncm_setup_desc->work); + value = 0; + } + + return value; +} +#endif + static void ncm_free_inst(struct usb_function_instance *f) { struct f_ncm_opts *opts; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + /* release _ncm_setup_desc related resource */ + device_destroy(_ncm_setup_desc->device->class, + _ncm_setup_desc->device->devt); + cancel_work(&_ncm_setup_desc->work); + kfree(_ncm_setup_desc); +#endif + opts = container_of(f, struct f_ncm_opts, func_inst); if (opts->bound) gether_cleanup(netdev_priv(opts->net)); - else - free_netdev(opts->net); kfree(opts); } @@ -1595,15 +1665,17 @@ static struct usb_function_instance *ncm_alloc_inst(void) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); opts->func_inst.free_func_inst = ncm_free_inst; - opts->net = gether_setup_default(); - if (IS_ERR(opts->net)) { - struct net_device *net = opts->net; - kfree(opts); - return ERR_CAST(net); - } config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + _ncm_setup_desc = kzalloc(sizeof(*_ncm_setup_desc), GFP_KERNEL); + if (!_ncm_setup_desc) + return ERR_PTR(-ENOMEM); + INIT_WORK(&_ncm_setup_desc->work, ncm_setup_work); + _ncm_setup_desc->device = create_function_device("f_ncm"); +#endif + return &opts->func_inst; } @@ -1623,6 +1695,8 @@ static void ncm_free(struct usb_function *f) static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ncm *ncm = func_to_ncm(f); + struct f_ncm_opts *opts = container_of(f->fi, struct f_ncm_opts, + func_inst); DBG(c->cdev, "ncm unbind\n"); @@ -1634,13 +1708,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); + + gether_cleanup(netdev_priv(opts->net)); + opts->bound = false; } static struct usb_function *ncm_alloc(struct usb_function_instance *fi) { struct f_ncm *ncm; struct f_ncm_opts *opts; - int status; /* allocate and initialize one new instance */ ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); @@ -1650,20 +1726,9 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) opts = container_of(fi, struct f_ncm_opts, func_inst); mutex_lock(&opts->lock); opts->refcnt++; - - /* export host's Ethernet address in CDC format */ - status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, - sizeof(ncm->ethaddr)); - if (status < 12) { /* strlen("01234567890a") */ - kfree(ncm); - mutex_unlock(&opts->lock); - return ERR_PTR(-EINVAL); - } ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; - spin_lock_init(&ncm->lock); ncm_reset_values(ncm); - ncm->port.ioport = netdev_priv(opts->net); mutex_unlock(&opts->lock); ncm->port.is_fixed = true; ncm->port.supports_multi_frame = true; diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 63e127163d4928334e6e83c90d17e660439674c4..994ccd9e2f54d9d6336d2ff53b88b03538b58150 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -183,15 +183,28 @@ struct usb_qdss_opts *to_fi_usb_qdss_opts(struct usb_function_instance *fi) } /*----------------------------------------------------------------------*/ -static void qdss_ctrl_write_complete(struct usb_ep *ep, +static void qdss_write_complete(struct usb_ep *ep, struct usb_request *req) { struct f_qdss *qdss = ep->driver_data; struct qdss_request *d_req = req->context; + struct usb_ep *in; + struct list_head *list_pool; + enum qdss_state state; unsigned long flags; pr_debug("%s\n", __func__); + if (qdss->debug_inface_enabled) { + in = qdss->port.ctrl_in; + list_pool = &qdss->ctrl_write_pool; + state = USB_QDSS_CTRL_WRITE_DONE; + } else { + in = qdss->port.data; + list_pool = &qdss->data_write_pool; + state = USB_QDSS_DATA_WRITE_DONE; + } + if (!req->status) { /* send zlp */ if ((req->length >= ep->maxpacket) && @@ -199,13 +212,13 @@ static void qdss_ctrl_write_complete(struct usb_ep *ep, req->length = 0; d_req->actual = req->actual; d_req->status = req->status; - if (!usb_ep_queue(qdss->port.ctrl_in, req, GFP_ATOMIC)) + if (!usb_ep_queue(in, req, GFP_ATOMIC)) return; } } spin_lock_irqsave(&qdss->lock, flags); - list_add_tail(&req->list, &qdss->ctrl_write_pool); + list_add_tail(&req->list, list_pool); if (req->length != 0) { d_req->actual = req->actual; d_req->status = req->status; @@ -213,8 +226,7 @@ static void qdss_ctrl_write_complete(struct usb_ep *ep, spin_unlock_irqrestore(&qdss->lock, flags); if (qdss->ch.notify) - qdss->ch.notify(qdss->ch.priv, USB_QDSS_CTRL_WRITE_DONE, d_req, - NULL); + qdss->ch.notify(qdss->ch.priv, state, d_req, NULL); } static void qdss_ctrl_read_complete(struct usb_ep *ep, @@ -252,6 +264,12 @@ void usb_qdss_free_req(struct usb_qdss_ch *ch) return; } + list_for_each_safe(act, tmp, &qdss->data_write_pool) { + req = list_entry(act, struct usb_request, list); + list_del(&req->list); + usb_ep_free_request(qdss->port.data, req); + } + list_for_each_safe(act, tmp, &qdss->ctrl_write_pool) { req = list_entry(act, struct usb_request, list); list_del(&req->list); @@ -271,23 +289,41 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf, { struct f_qdss *qdss = ch->priv_usb; struct usb_request *req; + struct usb_ep *in; + struct list_head *list_pool; int i; pr_debug("%s\n", __func__); - if (no_write_buf <= 0 || no_read_buf <= 0 || !qdss) { + if (!qdss) { + pr_err("%s: %s closed\n", __func__, ch->name); + return -ENODEV; + } + + if ((qdss->debug_inface_enabled && + (no_write_buf <= 0 || no_read_buf <= 0)) || + (!qdss->debug_inface_enabled && + (no_write_buf <= 0 || no_read_buf))) { pr_err("%s: missing params\n", __func__); return -ENODEV; } + if (qdss->debug_inface_enabled) { + in = qdss->port.ctrl_in; + list_pool = &qdss->ctrl_write_pool; + } else { + in = qdss->port.data; + list_pool = &qdss->data_write_pool; + } + for (i = 0; i < no_write_buf; i++) { - req = usb_ep_alloc_request(qdss->port.ctrl_in, GFP_ATOMIC); + req = usb_ep_alloc_request(in, GFP_ATOMIC); if (!req) { pr_err("%s: ctrl_in allocation err\n", __func__); goto fail; } - req->complete = qdss_ctrl_write_complete; - list_add_tail(&req->list, &qdss->ctrl_write_pool); + req->complete = qdss_write_complete; + list_add_tail(&req->list, list_pool); } for (i = 0; i < no_read_buf; i++) { @@ -326,11 +362,7 @@ static void clear_desc(struct usb_gadget *gadget, struct usb_function *f) { pr_debug("%s\n", __func__); - if (gadget_is_superspeed(gadget) && f->ss_descriptors) - usb_free_descriptors(f->ss_descriptors); - - if (gadget_is_dualspeed(gadget) && f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); + usb_free_all_descriptors(f); } static int qdss_bind(struct usb_configuration *c, struct usb_function *f) @@ -338,7 +370,7 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f) struct usb_gadget *gadget = c->cdev->gadget; struct f_qdss *qdss = func_to_qdss(f); struct usb_ep *ep; - int iface, id; + int iface, id, ret; pr_debug("%s\n", __func__); @@ -378,6 +410,10 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f) qdss_ctrl_intf_desc.iInterface = id; } + /* for non-accelerated path keep tx fifo size 1k */ + if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) + qdss_data_ep_comp_desc.bMaxBurst = 0; + ep = usb_ep_autoconfig_ss(gadget, &qdss_ss_data_desc, &qdss_data_ep_comp_desc); if (!ep) { @@ -415,28 +451,18 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f) qdss_ss_ctrl_in_desc.bEndpointAddress; qdss_hs_ctrl_out_desc.bEndpointAddress = qdss_ss_ctrl_out_desc.bEndpointAddress; - f->hs_descriptors = usb_copy_descriptors(qdss_hs_desc); - } else - f->hs_descriptors = usb_copy_descriptors( - qdss_hs_data_only_desc); - if (!f->hs_descriptors) { - pr_err("%s: usb_copy_descriptors error\n", __func__); - goto fail; } - /* update ss descriptors */ - if (gadget_is_superspeed(gadget)) { - if (qdss->debug_inface_enabled) - f->ss_descriptors = - usb_copy_descriptors(qdss_ss_desc); - else - f->ss_descriptors = - usb_copy_descriptors(qdss_ss_data_only_desc); - if (!f->ss_descriptors) { - pr_err("%s: usb_copy_descriptors error\n", __func__); - goto fail; - } - } + if (qdss->debug_inface_enabled) + ret = usb_assign_descriptors(f, qdss_hs_desc, qdss_hs_desc, + qdss_ss_desc, qdss_ss_desc); + else + ret = usb_assign_descriptors(f, qdss_hs_data_only_desc, + qdss_hs_data_only_desc, qdss_ss_data_only_desc, + qdss_ss_data_only_desc); + + if (ret) + goto fail; return 0; fail: @@ -491,21 +517,20 @@ static void usb_qdss_disconnect_work(struct work_struct *work) qdss = container_of(work, struct f_qdss, disconnect_w); pr_debug("%s\n", __func__); - /* - * Uninitialized init data i.e. ep specific operation. - * Notify qdss to cancel all active transfers. - */ - if (qdss->ch.app_conn) { + + /* Notify qdss to cancel all active transfers */ + if (qdss->ch.notify) + qdss->ch.notify(qdss->ch.priv, + USB_QDSS_DISCONNECT, + NULL, + NULL); + + /* Uninitialized init data i.e. ep specific operation */ + if (qdss->ch.app_conn && !strcmp(qdss->ch.name, USB_QDSS_CH_MSM)) { status = uninit_data(qdss->port.data); if (status) pr_err("%s: uninit_data error\n", __func__); - if (qdss->ch.notify) - qdss->ch.notify(qdss->ch.priv, - USB_QDSS_DISCONNECT, - NULL, - NULL); - status = set_qdss_data_connection(qdss, 0); if (status) pr_err("qdss_disconnect error"); @@ -562,15 +587,16 @@ static void usb_qdss_connect_work(struct work_struct *work) } pr_debug("%s\n", __func__); + + if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) + goto notify; + status = set_qdss_data_connection(qdss, 1); if (status) { pr_err("set_qdss_data_connection error(%d)", status); return; } - if (qdss->ch.notify) - qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT, - NULL, &qdss->ch); spin_lock_irqsave(&qdss->lock, flags); req = qdss->endless_req; spin_unlock_irqrestore(&qdss->lock, flags); @@ -578,8 +604,15 @@ static void usb_qdss_connect_work(struct work_struct *work) return; status = usb_ep_queue(qdss->port.data, req, GFP_ATOMIC); - if (status) + if (status) { pr_err("%s: usb_ep_queue error (%d)\n", __func__, status); + return; + } + +notify: + if (qdss->ch.notify) + qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT, + NULL, &qdss->ch); } static int qdss_set_alt(struct usb_function *f, unsigned int intf, @@ -596,9 +629,9 @@ static int qdss_set_alt(struct usb_function *f, unsigned int intf, if (alt != 0) goto fail1; - if (gadget->speed != USB_SPEED_SUPER && - gadget->speed != USB_SPEED_HIGH) { - pr_err("%s: qdss supportes HS or SS only\n", __func__); + if (gadget->speed < USB_SPEED_HIGH) { + pr_err("%s: qdss doesn't support USB full or low speed\n", + __func__); ret = -EINVAL; goto fail1; } @@ -722,6 +755,7 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name) spin_lock_init(&qdss->lock); INIT_LIST_HEAD(&qdss->ctrl_read_pool); INIT_LIST_HEAD(&qdss->ctrl_write_pool); + INIT_LIST_HEAD(&qdss->data_write_pool); INIT_WORK(&qdss->connect_w, usb_qdss_connect_work); INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work); @@ -817,6 +851,50 @@ int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) } EXPORT_SYMBOL(usb_qdss_ctrl_write); +int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) +{ + struct f_qdss *qdss = ch->priv_usb; + unsigned long flags; + struct usb_request *req = NULL; + + pr_debug("usb_qdss_ctrl_write\n"); + + if (!qdss) + return -ENODEV; + + spin_lock_irqsave(&qdss->lock, flags); + + if (qdss->usb_connected == 0) { + spin_unlock_irqrestore(&qdss->lock, flags); + return -EIO; + } + + if (list_empty(&qdss->data_write_pool)) { + pr_err("error: usb_qdss_data_write list is empty\n"); + spin_unlock_irqrestore(&qdss->lock, flags); + return -EAGAIN; + } + + req = list_first_entry(&qdss->data_write_pool, struct usb_request, + list); + list_del(&req->list); + spin_unlock_irqrestore(&qdss->lock, flags); + + req->buf = d_req->buf; + req->length = d_req->length; + req->context = d_req; + if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) { + spin_lock_irqsave(&qdss->lock, flags); + list_add_tail(&req->list, &qdss->data_write_pool); + spin_unlock_irqrestore(&qdss->lock, flags); + pr_err("qdss usb_ep_queue failed\n"); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(usb_qdss_write); + struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv, void (*notify)(void *priv, unsigned int event, struct qdss_request *d_req, struct usb_qdss_ch *)) @@ -874,7 +952,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch) pr_debug("%s\n", __func__); spin_lock_irqsave(&qdss_lock, flags); - if (!qdss || !qdss->usb_connected) { + ch->priv_usb = NULL; + if (!qdss || !qdss->usb_connected || + !strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) { ch->app_conn = 0; spin_unlock_irqrestore(&qdss_lock, flags); return; diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h index 72edb9094ebacb6de808feaf1eaf30bd2cd656d5..57c76f887618db796842dbff97f075cfc5a0717b 100644 --- a/drivers/usb/gadget/function/f_qdss.h +++ b/drivers/usb/gadget/function/f_qdss.h @@ -59,6 +59,10 @@ struct f_qdss { struct usb_qdss_ch ch; struct list_head ctrl_read_pool; struct list_head ctrl_write_pool; + + /* for mdm channel SW path */ + struct list_head data_write_pool; + struct work_struct connect_w; struct work_struct disconnect_w; spinlock_t lock; diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index ce0f3a78ca13945299adab1be1faf5c95ab825ad..b4541e2009633b5725cff05a04713adb25503402 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -33,4 +33,8 @@ struct f_ncm_opts { int refcnt; }; +extern struct device *create_function_device(char *name); +int ncm_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl); + #endif /* U_NCM_H */ diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h index 7d53a4773d1af5a303e292234239769b97878d2f..2f03334c68741fc1bedcb4590fc61ff2d456db62 100644 --- a/drivers/usb/gadget/u_f.h +++ b/drivers/usb/gadget/u_f.h @@ -64,7 +64,9 @@ struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len); /* Frees a usb_request previously allocated by alloc_ep_req() */ static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req) { + WARN_ON(req->buf == NULL); kfree(req->buf); + req->buf = NULL; usb_ep_free_request(ep, req); } diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index e6849b739845f329c831954f12f2818a6a4a587a..28cc75b425eb4e303f33bf6b8a0ede9f273ae756 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -249,6 +249,9 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request); * arranges to poll once per interval, and the gadget driver usually will * have queued some data to transfer at that time. * + * Note that @req's ->complete() callback must never be called from + * within usb_ep_queue() as that can create deadlock situations. + * * Returns zero, or a negative error code. Endpoints that are not enabled * report errors; errors will also be * reported when the usb peripheral is disconnected. diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 3fb57cf8abb8726f4e754a2b0cf9a4bc61e179da..d79ab0d85924d9c3dd5e13f1342a510193381eed 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -134,7 +134,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) xhci->quirks |= XHCI_AMD_PLL_FIX; - if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x43bb) + if (pdev->vendor == PCI_VENDOR_ID_AMD && + (pdev->device == 0x15e0 || + pdev->device == 0x15e1 || + pdev->device == 0x43bb)) xhci->quirks |= XHCI_SUSPEND_DELAY; if (pdev->vendor == PCI_VENDOR_ID_AMD) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 69195ea2886dbbcb521181c55c7c79f8b9df28a4..5847a891d0c19052ec1541d364392faed5055818 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -540,7 +540,6 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); static struct platform_driver usb_xhci_driver = { .probe = xhci_plat_probe, .remove = xhci_plat_remove, - .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-hcd", .pm = &xhci_plat_pm_ops, diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index f019d80ca9e40451a8a3bbca0671b95935a7798f..dc45cfc8eb1016787dde88c5a60889bcff161245 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -1216,7 +1216,7 @@ sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot, /* Interface routine */ static int sisusbcon_font_set(struct vc_data *c, struct console_font *font, - unsigned flags) + unsigned int flags) { struct sisusb_usb_data *sisusb; unsigned charcount = font->charcount; @@ -1337,29 +1337,65 @@ static void sisusbdummycon_init(struct vc_data *vc, int init) vc_resize(vc, 80, 25); } -static int sisusbdummycon_dummy(void) +static void sisusbdummycon_deinit(struct vc_data *vc) { } +static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx, + int height, int width) { } +static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos, + int xpos) { } +static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos) { } +static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { } + +static bool sisusbdummycon_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int lines) { - return 0; + return false; } -#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy +static int sisusbdummycon_switch(struct vc_data *vc) +{ + return 0; +} + +static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch) +{ + return 0; +} + +static int sisusbdummycon_font_set(struct vc_data *vc, + struct console_font *font, + unsigned int flags) +{ + return 0; +} + +static int sisusbdummycon_font_default(struct vc_data *vc, + struct console_font *font, char *name) +{ + return 0; +} + +static int sisusbdummycon_font_copy(struct vc_data *vc, int con) +{ + return 0; +} static const struct consw sisusb_dummy_con = { .owner = THIS_MODULE, .con_startup = sisusbdummycon_startup, .con_init = sisusbdummycon_init, - .con_deinit = SISUSBCONDUMMY, - .con_clear = SISUSBCONDUMMY, - .con_putc = SISUSBCONDUMMY, - .con_putcs = SISUSBCONDUMMY, - .con_cursor = SISUSBCONDUMMY, - .con_scroll = SISUSBCONDUMMY, - .con_switch = SISUSBCONDUMMY, - .con_blank = SISUSBCONDUMMY, - .con_font_set = SISUSBCONDUMMY, - .con_font_get = SISUSBCONDUMMY, - .con_font_default = SISUSBCONDUMMY, - .con_font_copy = SISUSBCONDUMMY, + .con_deinit = sisusbdummycon_deinit, + .con_clear = sisusbdummycon_clear, + .con_putc = sisusbdummycon_putc, + .con_putcs = sisusbdummycon_putcs, + .con_cursor = sisusbdummycon_cursor, + .con_scroll = sisusbdummycon_scroll, + .con_switch = sisusbdummycon_switch, + .con_blank = sisusbdummycon_blank, + .con_font_set = sisusbdummycon_font_set, + .con_font_default = sisusbdummycon_font_default, + .con_font_copy = sisusbdummycon_font_copy, }; int diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index ff5a1a8989d5a5ad5ad599463d6d34533cf13f09..ff17e94ef465cd86cef3e6e1970d3710d9717a5e 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1780,6 +1780,7 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) int vbus; u8 devctl; + pm_runtime_get_sync(dev); spin_lock_irqsave(&musb->lock, flags); val = musb->a_wait_bcon; vbus = musb_platform_get_vbus_status(musb); @@ -1793,6 +1794,7 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) vbus = 0; } spin_unlock_irqrestore(&musb->lock, flags); + pm_runtime_put_sync(dev); return sprintf(buf, "Vbus %s, timeout %lu msec\n", vbus ? "on" : "off", val); @@ -2496,11 +2498,11 @@ static int musb_remove(struct platform_device *pdev) musb_disable_interrupts(musb); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); spin_unlock_irqrestore(&musb->lock, flags); + musb_platform_exit(musb); pm_runtime_dont_use_autosuspend(musb->controller); pm_runtime_put_sync(musb->controller); pm_runtime_disable(musb->controller); - musb_platform_exit(musb); musb_phy_callback = NULL; if (musb->dma_controller) musb_dma_controller_destroy(musb->dma_controller); @@ -2733,7 +2735,8 @@ static int musb_resume(struct device *dev) if ((devctl & mask) != (musb->context.devctl & mask)) musb->port1_status = 0; - musb_start(musb); + musb_enable_interrupts(musb); + musb_platform_enable(musb); spin_lock_irqsave(&musb->lock, flags); error = musb_run_resume_work(musb); diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 844a309fe895f04fc3ffa2c0a4748b738906c9bc..e85b9c2a4910808405544e6df27a25fb52bd4d82 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -114,15 +114,19 @@ static int service_tx_status_request( } is_in = epnum & USB_DIR_IN; - if (is_in) { - epnum &= 0x0f; + epnum &= 0x0f; + if (epnum >= MUSB_C_NUM_EPS) { + handled = -EINVAL; + break; + } + + if (is_in) ep = &musb->endpoints[epnum].ep_in; - } else { + else ep = &musb->endpoints[epnum].ep_out; - } regs = musb->endpoints[epnum].regs; - if (epnum >= MUSB_C_NUM_EPS || !ep->desc) { + if (!ep->desc) { handled = -EINVAL; break; } diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index ad0c84629484466eafe996ad2d3b3d889b9b0a73..497cad2d78197589bdeee9860b3e392a0edd3518 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -564,10 +564,13 @@ static int usbpd_release_ss_lane(struct usbpd *pd, goto err_exit; } - ret = extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, 0); - if (ret) { - usbpd_err(&pd->dev, "err(%d) for releasing ss lane", ret); - goto err_exit; + if (pd->peer_usb_comm) { + ret = extcon_blocking_sync(pd->extcon, EXTCON_USB_HOST, 0); + if (ret) { + usbpd_err(&pd->dev, "err(%d) for releasing ss lane", + ret); + goto err_exit; + } } pd->ss_lane_svid = hdlr->svid; @@ -755,6 +758,7 @@ static int pd_eval_src_caps(struct usbpd *pd) { int i; union power_supply_propval val; + bool pps_found = false; u32 first_pdo = pd->received_pdos[0]; if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) { @@ -774,10 +778,8 @@ static int pd_eval_src_caps(struct usbpd *pd) if (pd->peer_usb_comm && pd->current_dr == DR_UFP && !pd->pd_connected) start_usb_peripheral(pd); - if (pd->spec_rev == USBPD_REV_30 && !rev3_sink_only) { - bool pps_found = false; - - /* downgrade to 2.0 if no PPS */ + /* Check for PPS APDOs */ + if (pd->spec_rev == USBPD_REV_30) { for (i = 1; i < PD_MAX_DATA_OBJ; i++) { if ((PD_SRC_PDO_TYPE(pd->received_pdos[i]) == PD_SRC_PDO_TYPE_AUGMENTED) && @@ -786,10 +788,18 @@ static int pd_eval_src_caps(struct usbpd *pd) break; } } - if (!pps_found) + + /* downgrade to 2.0 if no PPS */ + if (!pps_found && !rev3_sink_only) pd->spec_rev = USBPD_REV_20; } + val.intval = pps_found ? + POWER_SUPPLY_PD_PPS_ACTIVE : + POWER_SUPPLY_PD_ACTIVE; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + /* Select the first PDO (vSafe5V) immediately. */ pd_select_pdo(pd, 1, 0, 0); @@ -1270,6 +1280,18 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) dual_role_instance_changed(pd->dual_role); break; + case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: + val.intval = pd->in_pr_swap = true; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + pd->in_explicit_contract = false; + + /* lock in current mode */ + set_power_role(pd, pd->current_pr); + + kick_sm(pd, SRC_TRANSITION_TIME); + break; + case PE_SRC_HARD_RESET: case PE_SNK_HARD_RESET: /* are we still connected? */ @@ -1416,6 +1438,13 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: + val.intval = pd->in_pr_swap = true; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + + /* lock in current mode */ + set_power_role(pd, pd->current_pr); + val.intval = pd->requested_current = 0; /* suspend charging */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); @@ -1813,6 +1842,7 @@ static void reset_vdm_state(struct usbpd *pd) pd->num_svids = 0; kfree(pd->vdm_tx); pd->vdm_tx = NULL; + pd->ss_lane_svid = 0x0; } static void dr_swap(struct usbpd *pd) @@ -2125,7 +2155,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_dbg(&pd->dev, "Src CapsCounter exceeded, disabling PD\n"); usbpd_set_state(pd, PE_SRC_DISABLED); - val.intval = 0; + val.intval = POWER_SUPPLY_PD_INACTIVE; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); @@ -2145,7 +2175,7 @@ static void usbpd_sm(struct work_struct *w) pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT; kick_sm(pd, SENDER_RESPONSE_TIME); - val.intval = 1; + val.intval = POWER_SUPPLY_PD_ACTIVE; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); break; @@ -2192,9 +2222,6 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) { - /* lock in current mode */ - set_power_role(pd, pd->current_pr); - /* we'll happily accept Src->Sink requests anytime */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { @@ -2203,8 +2230,7 @@ static void usbpd_sm(struct work_struct *w) break; } - pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; - kick_sm(pd, SRC_TRANSITION_TIME); + usbpd_set_state(pd, PE_PRS_SRC_SNK_TRANSITION_TO_OFF); break; } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); @@ -2337,10 +2363,6 @@ static void usbpd_sm(struct work_struct *w) pd->src_cap_id++; usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); - - val.intval = 1; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_ACTIVE, &val); } else if (pd->hard_reset_count < 3) { usbpd_set_state(pd, PE_SNK_HARD_RESET); } else { @@ -2351,7 +2373,7 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); - val.intval = 0; + val.intval = POWER_SUPPLY_PD_INACTIVE; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); } @@ -2483,9 +2505,6 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); } else if (IS_CTRL(rx_msg, MSG_PR_SWAP) && pd->spec_rev == USBPD_REV_20) { - /* lock in current mode */ - set_power_role(pd, pd->current_pr); - /* TODO: should we Reject in certain circumstances? */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { @@ -2494,10 +2513,6 @@ static void usbpd_sm(struct work_struct *w) break; } - pd->in_pr_swap = true; - val.intval = 1; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP) && @@ -2750,17 +2765,10 @@ static void usbpd_sm(struct work_struct *w) break; } - pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; - kick_sm(pd, SRC_TRANSITION_TIME); + usbpd_set_state(pd, PE_PRS_SRC_SNK_TRANSITION_TO_OFF); break; case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: - pd->in_pr_swap = true; - val.intval = 1; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PR_SWAP, &val); - pd->in_explicit_contract = false; - if (pd->vbus_enabled) { regulator_disable(pd->vbus); pd->vbus_enabled = false; @@ -2798,10 +2806,6 @@ static void usbpd_sm(struct work_struct *w) break; } - pd->in_pr_swap = true; - val.intval = 1; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; @@ -3572,7 +3576,7 @@ static ssize_t rdo_h_show(struct device *dev, struct device_attribute *attr, { struct usbpd *pd = dev_get_drvdata(dev); int pos = PD_RDO_OBJ_POS(pd->rdo); - int type = PD_SRC_PDO_TYPE(pd->received_pdos[pos]); + int type = PD_SRC_PDO_TYPE(pd->received_pdos[pos - 1]); int len; len = scnprintf(buf, PAGE_SIZE, "Request Data Object\n" diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index c66b93664d54344a3e8e50a9e5cdca1745e9039f..c508e2d7104b231097a6a12d5a47c94f9d352581 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -62,6 +62,7 @@ config USB_SERIAL_SIMPLE - Fundamental Software dongle. - Google USB serial devices - HP4x calculators + - Libtransistor USB console - a number of Motorola phones - Motorola Tetra devices - Novatel Wireless GPS receivers diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index aed182d24d23468fec2d3ebc2705c867652e7ba9..d0f00274d16c7f915d613a428b478a4528da2970 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -158,6 +158,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ + { USB_DEVICE(0x155A, 0x1006) }, /* ELDAT Easywave RX09 */ { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */ { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */ { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ @@ -216,6 +217,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ { USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */ { USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */ + { USB_DEVICE(0x3923, 0x7A0B) }, /* National Instruments USB Serial Console */ { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index d038e543c2462953c15f662323b21ffa83d787d6..385f2ae3be24af225433824223d45d35d8b48bfb 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -773,6 +773,7 @@ static const struct usb_device_id id_table_combined[] = { .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) }, + { USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S03_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_59_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_57A_PID) }, @@ -935,6 +936,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_FHE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, @@ -1900,7 +1902,8 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial) return ftdi_jtag_probe(serial); if (udev->product && - (!strcmp(udev->product, "BeagleBone/XDS100V2") || + (!strcmp(udev->product, "Arrow USB Blaster") || + !strcmp(udev->product, "BeagleBone/XDS100V2") || !strcmp(udev->product, "SNAP Connect E10"))) return ftdi_jtag_probe(serial); diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 8b4ecd2bd297b84d5a86c9b02cd5c38d3a6e86a8..975d02666c5a0cf93cd647d8667f988e589c8171 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -923,6 +923,9 @@ /* * RT Systems programming cables for various ham radios */ +/* This device uses the VID of FTDI */ +#define RTSYSTEMS_USB_VX8_PID 0x9e50 /* USB-VX8 USB to 7 pin modular plug for Yaesu VX-8 radio */ + #define RTSYSTEMS_VID 0x2100 /* Vendor ID */ #define RTSYSTEMS_USB_S03_PID 0x9001 /* RTS-03 USB to Serial Adapter */ #define RTSYSTEMS_USB_59_PID 0x9e50 /* USB-59 USB to 8 pin plug */ @@ -1441,6 +1444,12 @@ */ #define FTDI_CINTERION_MC55I_PID 0xA951 +/* + * Product: FirmwareHubEmulator + * Manufacturer: Harman Becker Automotive Systems + */ +#define FTDI_FHE_PID 0xA9A0 + /* * Product: Comet Caller ID decoder * Manufacturer: Crucible Technologies diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index 6aa7ff2c1cf74b953dc5669e08500b43ccb9e115..2674da40d9cd78d31c702a2b9b597639b02982a8 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -66,6 +66,11 @@ DEVICE(flashloader, FLASHLOADER_IDS); 0x01) } DEVICE(google, GOOGLE_IDS); +/* Libtransistor USB console */ +#define LIBTRANSISTOR_IDS() \ + { USB_DEVICE(0x1209, 0x8b00) } +DEVICE(libtransistor, LIBTRANSISTOR_IDS); + /* ViVOpay USB Serial Driver */ #define VIVOPAY_IDS() \ { USB_DEVICE(0x1d5f, 0x1004) } /* ViVOpay 8800 */ @@ -113,6 +118,7 @@ static struct usb_serial_driver * const serial_drivers[] = { &funsoft_device, &flashloader_device, &google_device, + &libtransistor_device, &vivopay_device, &moto_modem_device, &motorola_tetra_device, @@ -129,6 +135,7 @@ static const struct usb_device_id id_table[] = { FUNSOFT_IDS(), FLASHLOADER_IDS(), GOOGLE_IDS(), + LIBTRANSISTOR_IDS(), VIVOPAY_IDS(), MOTO_IDS(), MOTOROLA_TETRA_IDS(), diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 714c5bcedf2b99b914f3f05a3068e88d47c3d0fb..dd24c5c1534dcb2ec12a95e2e96ea1cd2bedd118 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -31,7 +31,7 @@ * difficult to estimate the time it takes for the system to process the command * before it is actually passed to the PPM. */ -#define UCSI_TIMEOUT_MS 1000 +#define UCSI_TIMEOUT_MS 5000 /* * UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index 6968c906fa2916440d956e49bce362ff5a6f4912..b59a253a8479dbbb97a5604e7402b87f57a3cd71 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -200,7 +200,12 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf, if (!bid) return -ENODEV; + /* device_attach() callers should hold parent lock for USB */ + if (bid->udev->dev.parent) + device_lock(bid->udev->dev.parent); ret = device_attach(&bid->udev->dev); + if (bid->udev->dev.parent) + device_unlock(bid->udev->dev.parent); if (ret < 0) { dev_err(&bid->udev->dev, "rebind failed\n"); return ret; diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 33737b612b1fdc0d49710cbb89876ca20bff7280..c81c44c13a5673872805e8bd09e603b73c169dca 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -257,7 +257,7 @@ enum usbip_side { #define VUDC_EVENT_ERROR_USB (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) #define VUDC_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) -#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE) +#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE) #define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) #define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) #define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c index f1635662c2992a8c531d1cd53dd0e14e9f79ef1a..f8f7f3803a99c8ce551e7b935400f0f884891c67 100644 --- a/drivers/usb/usbip/usbip_event.c +++ b/drivers/usb/usbip/usbip_event.c @@ -105,10 +105,6 @@ static void event_handler(struct work_struct *work) unset_event(ud, USBIP_EH_UNUSABLE); } - /* Stop the error handler. */ - if (ud->event & USBIP_EH_BYE) - usbip_dbg_eh("removed %p\n", ud); - wake_up(&ud->eh_waitq); } } diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 89858aeed6476060e19baa962c740eac8ee808e6..05aa1ba351b6d828b890459d7eb67a0c27b6292d 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -368,6 +368,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh(" ClearHubFeature\n"); break; case ClearPortFeature: + if (rhport < 0) + goto error; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed == HCD_USB3) { @@ -525,11 +527,16 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } + if (rhport < 0) + goto error; + vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND; break; case USB_PORT_FEAT_POWER: usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_POWER\n"); + if (rhport < 0) + goto error; if (hcd->speed == HCD_USB3) vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER; else @@ -538,6 +545,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_BH_PORT_RESET: usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n"); + if (rhport < 0) + goto error; /* Applicable only for USB3.0 hub */ if (hcd->speed != HCD_USB3) { pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " @@ -548,6 +557,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_RESET: usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_RESET\n"); + if (rhport < 0) + goto error; /* if it's already enabled, disable */ if (hcd->speed == HCD_USB3) { vhci_hcd->port_status[rhport] = 0; @@ -568,6 +579,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, default: usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", wValue); + if (rhport < 0) + goto error; if (hcd->speed == HCD_USB3) { if ((vhci_hcd->port_status[rhport] & USB_SS_PORT_STAT_POWER) != 0) { diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 91335e6de88a24ca12cc5e8c465fce91f9382e15..115a36f6f40398b75dfa75b61b50e81f98bc5a63 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -808,6 +808,7 @@ static int vfio_exp_config_write(struct vfio_pci_device *vdev, int pos, { __le16 *ctrl = (__le16 *)(vdev->vconfig + pos - offset + PCI_EXP_DEVCTL); + int readrq = le16_to_cpu(*ctrl) & PCI_EXP_DEVCTL_READRQ; count = vfio_default_config_write(vdev, pos, count, perm, offset, val); if (count < 0) @@ -833,6 +834,27 @@ static int vfio_exp_config_write(struct vfio_pci_device *vdev, int pos, pci_try_reset_function(vdev->pdev); } + /* + * MPS is virtualized to the user, writes do not change the physical + * register since determining a proper MPS value requires a system wide + * device view. The MRRS is largely independent of MPS, but since the + * user does not have that system-wide view, they might set a safe, but + * inefficiently low value. Here we allow writes through to hardware, + * but we set the floor to the physical device MPS setting, so that + * we can at least use full TLPs, as defined by the MPS value. + * + * NB, if any devices actually depend on an artificially low MRRS + * setting, this will need to be revisited, perhaps with a quirk + * though pcie_set_readrq(). + */ + if (readrq != (le16_to_cpu(*ctrl) & PCI_EXP_DEVCTL_READRQ)) { + readrq = 128 << + ((le16_to_cpu(*ctrl) & PCI_EXP_DEVCTL_READRQ) >> 12); + readrq = max(readrq, pcie_get_mps(vdev->pdev)); + + pcie_set_readrq(vdev->pdev, readrq); + } + return count; } @@ -851,11 +873,12 @@ static int __init init_pci_cap_exp_perm(struct perm_bits *perm) * Allow writes to device control fields, except devctl_phantom, * which could confuse IOMMU, MPS, which can break communication * with other physical devices, and the ARI bit in devctl2, which - * is set at probe time. FLR gets virtualized via our writefn. + * is set at probe time. FLR and MRRS get virtualized via our + * writefn. */ p_setw(perm, PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_PAYLOAD, - ~PCI_EXP_DEVCTL_PHANTOM); + PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_PAYLOAD | + PCI_EXP_DEVCTL_READRQ, ~PCI_EXP_DEVCTL_PHANTOM); p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI); return 0; } diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 082891dffd9d573cc671cf12011d6c660df41da1..b0d606b2d06c34e8df9f4d78914ec995ec6a2d95 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -622,7 +622,7 @@ static int vhost_net_rx_peek_head_len(struct vhost_net *net, struct sock *sk) if (!len && vq->busyloop_timeout) { /* Both tx vq and rx socket were polled here */ - mutex_lock(&vq->mutex); + mutex_lock_nested(&vq->mutex, 1); vhost_disable_notify(&net->dev, vq); preempt_disable(); @@ -755,7 +755,7 @@ static void handle_rx(struct vhost_net *net) struct iov_iter fixup; __virtio16 num_buffers; - mutex_lock(&vq->mutex); + mutex_lock_nested(&vq->mutex, 0); sock = vq->private_data; if (!sock) goto out; diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index a827c1a684a9ab7e3ba215a044be363ba2b08c47..8e3ca44007664ac1bc56e91f00e15f0ee9ffffdb 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -213,8 +213,7 @@ int vhost_poll_start(struct vhost_poll *poll, struct file *file) if (mask) vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask); if (mask & POLLERR) { - if (poll->wqh) - remove_wait_queue(poll->wqh, &poll->wait); + vhost_poll_stop(poll); ret = -EINVAL; } @@ -757,7 +756,7 @@ static int vhost_copy_to_user(struct vhost_virtqueue *vq, void __user *to, struct iov_iter t; void __user *uaddr = vhost_vq_meta_fetch(vq, (u64)(uintptr_t)to, size, - VHOST_ADDR_DESC); + VHOST_ADDR_USED); if (uaddr) return __copy_to_user(uaddr, from, size); @@ -1253,14 +1252,14 @@ static int vq_log_access_ok(struct vhost_virtqueue *vq, /* Caller should have vq mutex and device mutex */ int vhost_vq_access_ok(struct vhost_virtqueue *vq) { - if (vq->iotlb) { - /* When device IOTLB was used, the access validation - * will be validated during prefetching. - */ + if (!vq_log_access_ok(vq, vq->log_base)) + return 0; + + /* Access validation occurs at prefetch time with IOTLB */ + if (vq->iotlb) return 1; - } - return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used) && - vq_log_access_ok(vq, vq->log_base); + + return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used); } EXPORT_SYMBOL_GPL(vhost_vq_access_ok); diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index d7c239ea3d09f5f47aefca8cd4d9157608f250f3..f5574060f9c82dcb1802059c8bd229aa5f3f122c 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -177,7 +177,7 @@ static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data) struct spi_message msg; struct spi_transfer xfer = { .len = 1, - .cs_change = 1, + .cs_change = 0, .tx_buf = lcd->buf, }; diff --git a/drivers/video/backlight/qcom-spmi-wled.c b/drivers/video/backlight/qcom-spmi-wled.c index 77944f62a230321a54a18d4f64f1f3c0b698b222..08e0da8743a632bff4c6416d873d2438f1f1e465 100644 --- a/drivers/video/backlight/qcom-spmi-wled.c +++ b/drivers/video/backlight/qcom-spmi-wled.c @@ -1514,6 +1514,7 @@ static int wled_probe(struct platform_device *pdev) static const struct of_device_id wled_match_table[] = { { .compatible = "qcom,pmi8998-spmi-wled", .data = &version_table[0] }, { .compatible = "qcom,pm855l-spmi-wled", .data = &version_table[2] }, + { .compatible = "qcom,pm6150l-spmi-wled", .data = &version_table[2] }, { }, }; diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index eab1f842f9c01c1fa74d944e72ec7c6547da12cc..e4bd63e9db6bda265fe9fea5a7f3073bc5661478 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -369,7 +369,7 @@ static int tdo24m_probe(struct spi_device *spi) spi_message_init(m); - x->cs_change = 1; + x->cs_change = 0; x->tx_buf = &lcd->buf[0]; spi_message_add_tail(x, m); diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 6a41ea92737a30590ac1bb5d9bd7e93f683bb4d8..4dc5ee8debeba2fee5f02807f0b8b454f7363ffd 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -49,7 +49,7 @@ static int tosa_tg_send(struct spi_device *spi, int adrs, uint8_t data) struct spi_message msg; struct spi_transfer xfer = { .len = 1, - .cs_change = 1, + .cs_change = 0, .tx_buf = buf, }; diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index b90ef96e43d65a3ea18f74fdc6c9c9d702214595..f2eafe2ed98066866d6006f05f5019ead3fdae34 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -41,12 +41,47 @@ static void dummycon_init(struct vc_data *vc, int init) vc_resize(vc, DUMMY_COLUMNS, DUMMY_ROWS); } -static int dummycon_dummy(void) +static void dummycon_deinit(struct vc_data *vc) { } +static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height, + int width) { } +static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { } +static void dummycon_putcs(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos) { } +static void dummycon_cursor(struct vc_data *vc, int mode) { } + +static bool dummycon_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int lines) +{ + return false; +} + +static int dummycon_switch(struct vc_data *vc) { - return 0; + return 0; } -#define DUMMY (void *)dummycon_dummy +static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch) +{ + return 0; +} + +static int dummycon_font_set(struct vc_data *vc, struct console_font *font, + unsigned int flags) +{ + return 0; +} + +static int dummycon_font_default(struct vc_data *vc, + struct console_font *font, char *name) +{ + return 0; +} + +static int dummycon_font_copy(struct vc_data *vc, int con) +{ + return 0; +} /* * The console `switch' structure for the dummy console @@ -55,19 +90,19 @@ static int dummycon_dummy(void) */ const struct consw dummy_con = { - .owner = THIS_MODULE, - .con_startup = dummycon_startup, - .con_init = dummycon_init, - .con_deinit = DUMMY, - .con_clear = DUMMY, - .con_putc = DUMMY, - .con_putcs = DUMMY, - .con_cursor = DUMMY, - .con_scroll = DUMMY, - .con_switch = DUMMY, - .con_blank = DUMMY, - .con_font_set = DUMMY, - .con_font_default = DUMMY, - .con_font_copy = DUMMY, + .owner = THIS_MODULE, + .con_startup = dummycon_startup, + .con_init = dummycon_init, + .con_deinit = dummycon_deinit, + .con_clear = dummycon_clear, + .con_putc = dummycon_putc, + .con_putcs = dummycon_putcs, + .con_cursor = dummycon_cursor, + .con_scroll = dummycon_scroll, + .con_switch = dummycon_switch, + .con_blank = dummycon_blank, + .con_font_set = dummycon_font_set, + .con_font_default = dummycon_font_default, + .con_font_copy = dummycon_font_copy, }; EXPORT_SYMBOL_GPL(dummy_con); diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 42d02a206059b55b198c797d5c8816dbe7c0a872..7f2526b43b3364665df9350bb80d4f1eb168a6c4 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -673,12 +673,12 @@ static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b, return true; } -static int newport_dummy(struct vc_data *c) +static int newport_set_origin(struct vc_data *vc) { return 0; } -#define DUMMY (void *) newport_dummy +static void newport_save_screen(struct vc_data *vc) { } const struct consw newport_con = { .owner = THIS_MODULE, @@ -694,8 +694,8 @@ const struct consw newport_con = { .con_blank = newport_blank, .con_font_set = newport_font_set, .con_font_default = newport_font_default, - .con_set_origin = DUMMY, - .con_save_screen = DUMMY + .con_set_origin = newport_set_origin, + .con_save_screen = newport_save_screen }; static int newport_probe(struct gio_device *dev, diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index a17ba1465815f919444a83676517dc0f7e2bba16..f09e17b60e45ef611f51e2e21528432731fac8f0 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1272,7 +1272,8 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight) return 0; } -static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags) +static int vgacon_font_set(struct vc_data *c, struct console_font *font, + unsigned int flags) { unsigned charcount = font->charcount; int rc; @@ -1407,21 +1408,20 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, * The console `switch' structure for the VGA based console */ -static int vgacon_dummy(struct vc_data *c) -{ - return 0; -} - -#define DUMMY (void *) vgacon_dummy +static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height, + int width) { } +static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { } +static void vgacon_putcs(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos) { } const struct consw vga_con = { .owner = THIS_MODULE, .con_startup = vgacon_startup, .con_init = vgacon_init, .con_deinit = vgacon_deinit, - .con_clear = DUMMY, - .con_putc = DUMMY, - .con_putcs = DUMMY, + .con_clear = vgacon_clear, + .con_putc = vgacon_putc, + .con_putcs = vgacon_putcs, .con_cursor = vgacon_cursor, .con_scroll = vgacon_scroll, .con_switch = vgacon_switch, diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5e58f5ec0a28e449afa8813a652b1aa3469e0721..5dd7f6b3c45cb42e1e004b00da12b6648faf803a 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -2334,6 +2334,19 @@ config FB_PRE_INIT_FB Select this option if display contents should be inherited as set by the bootloader. +config FB_MSM + tristate "MSM Framebuffer support" + depends on FB && ARCH_QCOM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select SYNC_FILE + ---help--- + The MSM driver implements a frame buffer interface to + provide access to the display hardware and provide + a way for users to display graphics + on connected display panels. + config FB_MX3 tristate "MX3 Framebuffer support" depends on FB && MX3_IPU @@ -2454,6 +2467,7 @@ config FB_SIMPLE source "drivers/video/fbdev/omap/Kconfig" source "drivers/video/fbdev/omap2/Kconfig" source "drivers/video/fbdev/mmp/Kconfig" +source "drivers/video/fbdev/msm/Kconfig" config FB_SH_MOBILE_MERAM tristate "SuperH Mobile MERAM read ahead support" diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 8895536a20d648723197affff38d1bbf8fc140a8..653288d107c4beb960e94508615952bfce662e00 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -131,6 +131,11 @@ obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o obj-$(CONFIG_FB_OPENCORES) += ocfb.o obj-$(CONFIG_FB_SM712) += sm712fb.o +ifeq ($(CONFIG_FB_MSM),y) +obj-y += msm/ +else +obj-$(CONFIG_MSM_DBA) += msm/msm_dba/ +endif # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o obj-$(CONFIG_FB_VESA) += vesafb.o diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 04612f938bab1fc3eecf457d070fe501b8a1a3de..235d549d5958564d307fa5cfaa1ff267e4b50849 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2588,7 +2588,8 @@ static int fbcon_copy_font(struct vc_data *vc, int con) * is ever implemented. */ -static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags) +static int fbcon_set_font(struct vc_data *vc, struct console_font *font, + unsigned int flags) { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; unsigned charcount = font->charcount; diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index f741ba8df01b8e28f4331f988d2b04219bb103b6..8634175da1fbf6e386ddea9741a4cd587e3e89af 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1087,7 +1087,7 @@ fb_blank(struct fb_info *info, int blank) EXPORT_SYMBOL(fb_blank); static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) + unsigned long arg, struct file *file) { struct fb_ops *fb; struct fb_var_screeninfo var; @@ -1099,6 +1099,20 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, void __user *argp = (void __user *)arg; long ret = 0; + memset(&var, 0, sizeof(var)); + memset(&fix, 0, sizeof(fix)); + memset(&con2fb, 0, sizeof(con2fb)); + memset(&cmap_from, 0, sizeof(cmap_from)); + memset(&cmap, 0, sizeof(cmap)); + memset(&event, 0, sizeof(event)); + + memset(&var, 0, sizeof(var)); + memset(&fix, 0, sizeof(fix)); + memset(&con2fb, 0, sizeof(con2fb)); + memset(&cmap_from, 0, sizeof(cmap_from)); + memset(&cmap, 0, sizeof(cmap)); + memset(&event, 0, sizeof(event)); + switch (cmd) { case FBIOGET_VSCREENINFO: if (!lock_fb_info(info)) @@ -1217,7 +1231,9 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, if (!lock_fb_info(info)) return -ENODEV; fb = info->fbops; - if (fb->fb_ioctl) + if (fb->fb_ioctl_v2) + ret = fb->fb_ioctl_v2(info, cmd, arg, file); + else if (fb->fb_ioctl) ret = fb->fb_ioctl(info, cmd, arg); else ret = -ENOTTY; @@ -1232,7 +1248,7 @@ static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (!info) return -ENODEV; - return do_fb_ioctl(info, cmd, arg); + return do_fb_ioctl(info, cmd, arg, file); } #ifdef CONFIG_COMPAT @@ -1263,7 +1279,7 @@ struct fb_cmap32 { }; static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, - unsigned long arg) + unsigned long arg, struct file *file) { struct fb_cmap_user __user *cmap; struct fb_cmap32 __user *cmap32; @@ -1286,7 +1302,7 @@ static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, put_user(compat_ptr(data), &cmap->transp)) return -EFAULT; - err = do_fb_ioctl(info, cmd, (unsigned long) cmap); + err = do_fb_ioctl(info, cmd, (unsigned long) cmap, file); if (!err) { if (copy_in_user(&cmap32->start, @@ -1331,7 +1347,7 @@ static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, } static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, - unsigned long arg) + unsigned long arg, struct file *file) { struct fb_fix_screeninfo fix; @@ -1360,20 +1376,22 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, case FBIOPUT_CON2FBMAP: arg = (unsigned long) compat_ptr(arg); case FBIOBLANK: - ret = do_fb_ioctl(info, cmd, arg); + ret = do_fb_ioctl(info, cmd, arg, file); break; case FBIOGET_FSCREENINFO: - ret = fb_get_fscreeninfo(info, cmd, arg); + ret = fb_get_fscreeninfo(info, cmd, arg, file); break; case FBIOGETCMAP: case FBIOPUTCMAP: - ret = fb_getput_cmap(info, cmd, arg); + ret = fb_getput_cmap(info, cmd, arg, file); break; default: - if (fb->fb_compat_ioctl) + if (fb->fb_compat_ioctl_v2) + ret = fb->fb_compat_ioctl_v2(info, cmd, arg, file); + else if (fb->fb_compat_ioctl) ret = fb->fb_compat_ioctl(info, cmd, arg); break; } diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..e8f902bc0e19b87ec14ba0f7e2410457d6f952dc --- /dev/null +++ b/drivers/video/fbdev/msm/Kconfig @@ -0,0 +1,137 @@ +source "drivers/video/fbdev/msm/msm_dba/Kconfig" + +if FB_MSM + +config FB_MSM_MDSS_COMMON + bool + +choice + prompt "MDP HW version" + default FB_MSM_MDP + +config FB_MSM_MDP + bool "MDP HW" + select FB_MSM_MDP_HW + ---help--- + The Mobile Display Processor (MDP) driver support devices which + contain MDP hardware block. + + Support for MSM MDP HW revision 2.2. + Say Y here if this is msm7201 variant platform. + +config FB_MSM_MDSS + bool "MDSS HW" + select SYNC_FILE + select FB_MSM_MDSS_COMMON + ---help--- + The Mobile Display Sub System (MDSS) driver supports devices which + contain MDSS hardware block. + + The MDSS driver implements frame buffer interface to provide access to + the display hardware and provide a way for users to display graphics + on connected display panels. + +config FB_MSM_MDP_NONE + bool "MDP HW None" + ---help--- + This is used for platforms without Mobile Display Sub System (MDSS). + mdm platform don't have MDSS hardware block. + + Say Y here if this is mdm platform. + +endchoice + +config FB_MSM_QPIC + bool + select FB_MSM_MDSS_COMMON + +config FB_MSM_QPIC_ILI_QVGA_PANEL + bool "Qpic MIPI ILI QVGA Panel" + select FB_MSM_QPIC + ---help--- + Support for MIPI ILI QVGA (240x320) panel ILI TECHNOLOGY 9341 + with on-chip full display RAM use parallel interface. + +config FB_MSM_QPIC_PANEL_DETECT + bool "Qpic Panel Detect" + select FB_MSM_QPIC_ILI_QVGA_PANEL + ---help--- + Support for Qpic panel auto detect. + +config FB_MSM_MDSS_WRITEBACK + bool "MDSS Writeback Panel" + ---help--- + The MDSS Writeback Panel provides support for routing the output of + MDSS frame buffer driver and MDP processing to memory. + +config FB_MSM_MDSS_HDMI_PANEL + bool "MDSS HDMI Tx Panel" + depends on FB_MSM_MDSS + select MSM_EXT_DISPLAY + default n + ---help--- + The MDSS HDMI Panel provides support for transmitting TMDS signals of + MDSS frame buffer data to connected hdmi compliant TVs, monitors etc. + +config FB_MSM_MDSS_HDMI_MHL_SII8334 + depends on FB_MSM_MDSS_HDMI_PANEL + bool 'MHL SII8334 support ' + default n + ---help--- + Support the HDMI to MHL conversion. + MHL (Mobile High-Definition Link) technology + uses USB connector to output HDMI content + +config FB_MSM_MDSS_MHL3 + depends on FB_MSM_MDSS_HDMI_PANEL + bool "MHL3 SII8620 Support" + default n + ---help--- + Support the SiliconImage 8620 MHL Tx transmitter that uses + USB connector to output HDMI content. Transmitter is an + i2c device acting as an HDMI to MHL bridge. Chip supports + MHL 3.0 standard. + +config FB_MSM_MDSS_DSI_CTRL_STATUS + tristate "DSI controller status check feature" + ---help--- + Check DSI controller status periodically (default period is 5 + seconds) by sending Bus-Turn-Around (BTA) command. If DSI controller + fails to acknowledge the BTA command, it sends PANEL_ALIVE=0 status + to HAL layer to reset the controller. + +config FB_MSM_MDSS_EDP_PANEL + depends on FB_MSM_MDSS + bool "MDSS eDP Panel" + ---help--- + The MDSS eDP Panel provides support for eDP host controller driver. + Which runs in Video mode only and is responsible for transmitting + frame buffer from host SOC to eDP display panel. + +config FB_MSM_MDSS_MDP3 + depends on FB_MSM_MDSS + bool "MDP3 display controller" + ---help--- + The MDP3 provides support for an older version display controller. + Included in latest display sub-system, known as MDSS. + +config FB_MSM_MDSS_XLOG_DEBUG + depends on FB_MSM_MDSS + bool "Enable MDSS debugging" + ---help--- + The MDSS debugging provides support to enable display debugging + features to: Dump MDSS registers during driver errors, panic + driver during fatal errors and enable some display-driver logging + into an internal buffer (this avoids logging overhead). + +config FB_MSM_MDSS_FRC_DEBUG + depends on DEBUG_FS && FB_MSM_MDSS + bool "Enable Video FRC debugging" + default n + ---help--- + The MDSS FRC debugging provides support to enable the deterministic + frame rate control (FRC) debugging features to: Collect video frame + statistics and check whether its output pattern matches expected + cadence. + +endif diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..81d4953828f099d1a65194dea02314c5d9c718f8 --- /dev/null +++ b/drivers/video/fbdev/msm/Makefile @@ -0,0 +1,74 @@ +ccflags-y += -I$(src) + +obj-$(CONFIG_FB_MSM_MDSS_MHL3) += mhl3/ +obj-$(CONFIG_MSM_DBA) += msm_dba/ + +mdss-mdp3-objs = mdp3.o mdp3_layer.o mdp3_dma.o mdp3_ctrl.o dsi_status_v2.o +mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o +obj-$(CONFIG_FB_MSM_MDSS_MDP3) += mdss-mdp3.o +ifeq ($(CONFIG_FB_MSM_MDSS_MDP3), y) +ccflags-y += -DTARGET_HW_MDSS_MDP3 +endif +mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o dsi_status_6g.o +mdss-mdp-objs += mdss_mdp_pp.o mdss_mdp_pp_debug.o mdss_mdp_pp_cache_config.o mdss_sync.o +mdss-mdp-objs += mdss_mdp_intf_video.o +mdss-mdp-objs += mdss_mdp_intf_cmd.o +mdss-mdp-objs += mdss_mdp_intf_writeback.o +mdss-mdp-objs += mdss_rotator.o +mdss-mdp-objs += mdss_mdp_overlay.o +mdss-mdp-objs += mdss_mdp_layer.o +mdss-mdp-objs += mdss_mdp_splash_logo.o +mdss-mdp-objs += mdss_mdp_cdm.o +mdss-mdp-objs += mdss_smmu.o +mdss-mdp-objs += mdss_mdp_wfd.o +mdss-mdp-objs += mdss_io_util.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss_mdp_debug.o + +mdss-mdp-objs += mdss_mdp_pp_v1_7.o +mdss-mdp-objs += mdss_mdp_pp_v3.o +mdss-mdp-objs += mdss_mdp_pp_common.o + +ifeq ($(CONFIG_FB_MSM_MDSS),y) +obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o +endif + +ifeq ($(CONFIG_FB_MSM_MDSS_FRC_DEBUG),y) +obj-$(CONFIG_DEBUG_FS) += mdss_debug_frc.o +endif + +mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o +mdss-dsi-objs += mdss_dsi_panel.o +mdss-dsi-objs += msm_mdss_io_8974.o +mdss-dsi-objs += mdss_dsi_phy.o +mdss-dsi-objs += mdss_dsi_phy_12nm.o +mdss-dsi-objs += mdss_dsi_clk.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o + +ifneq ($(CONFIG_FB_MSM_MDSS_MDP3), y) +obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss_cec_core.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss_dba_utils.o +obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp.o +obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp_aux.o + +obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o +obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_panel.o +obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp.o +obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp2p2.o +obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_cec.o +obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_audio.o +obj-$(CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334) += mhl_sii8334.o mhl_msc.o +ccflags-y += -DTARGET_HW_MDSS_HDMI +endif + +obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o + +mdss-qpic-objs := mdss_qpic.o mdss_fb.o mdss_qpic_panel.o mdss_sync.o +obj-$(CONFIG_FB_MSM_QPIC) += mdss-qpic.o +obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o + +obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o +obj-$(CONFIG_COMPAT) += mdss_compat_utils.o diff --git a/drivers/video/fbdev/msm/dsi_host_v2.c b/drivers/video/fbdev/msm/dsi_host_v2.c new file mode 100644 index 0000000000000000000000000000000000000000..58927a6d26c69859a6dd6db53bd014c2cc5744ef --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_host_v2.c @@ -0,0 +1,1889 @@ +/* Copyright (c) 2012-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dsi_v2.h" +#include "dsi_io_v2.h" +#include "dsi_host_v2.h" +#include "mdss_debug.h" +#include "mdp3.h" + +#define DSI_POLL_SLEEP_US 1000 +#define DSI_POLL_TIMEOUT_US 16000 +#define DSI_ESC_CLK_RATE 19200000 +#define DSI_DMA_CMD_TIMEOUT_MS 200 +#define VSYNC_PERIOD 17 +#define DSI_MAX_PKT_SIZE 10 +#define DSI_SHORT_PKT_DATA_SIZE 2 +#define DSI_MAX_BYTES_TO_READ 16 + +struct dsi_host_v2_private { + unsigned char *dsi_base; + size_t dsi_reg_size; + struct device dis_dev; + int clk_count; + int dsi_on; + + void (*debug_enable_clk)(int on); +}; + +static struct dsi_host_v2_private *dsi_host_private; +static int msm_dsi_clk_ctrl(struct mdss_panel_data *pdata, int enable); + +int msm_dsi_init(void) +{ + if (!dsi_host_private) { + dsi_host_private = kzalloc(sizeof(struct dsi_host_v2_private), + GFP_KERNEL); + if (!dsi_host_private) + return -ENOMEM; + + } + + return 0; +} + +void msm_dsi_deinit(void) +{ + kfree(dsi_host_private); + dsi_host_private = NULL; +} + +void msm_dsi_ack_err_status(unsigned char *ctrl_base) +{ + u32 status; + + status = MIPI_INP(ctrl_base + DSI_ACK_ERR_STATUS); + + if (status) { + MIPI_OUTP(ctrl_base + DSI_ACK_ERR_STATUS, status); + + /* Writing of an extra 0 needed to clear error bits */ + MIPI_OUTP(ctrl_base + DSI_ACK_ERR_STATUS, 0); + pr_err("%s: status=%x\n", __func__, status); + } +} + +void msm_dsi_timeout_status(unsigned char *ctrl_base) +{ + u32 status; + + status = MIPI_INP(ctrl_base + DSI_TIMEOUT_STATUS); + if (status & 0x0111) { + MIPI_OUTP(ctrl_base + DSI_TIMEOUT_STATUS, status); + pr_err("%s: status=%x\n", __func__, status); + } +} + +void msm_dsi_dln0_phy_err(unsigned char *ctrl_base) +{ + u32 status; + + status = MIPI_INP(ctrl_base + DSI_DLN0_PHY_ERR); + + if (status & 0x011111) { + MIPI_OUTP(ctrl_base + DSI_DLN0_PHY_ERR, status); + pr_err("%s: status=%x\n", __func__, status); + } +} + +void msm_dsi_fifo_status(unsigned char *ctrl_base) +{ + u32 status; + + status = MIPI_INP(ctrl_base + DSI_FIFO_STATUS); + + if (status & 0x44444489) { + MIPI_OUTP(ctrl_base + DSI_FIFO_STATUS, status); + pr_err("%s: status=%x\n", __func__, status); + } +} + +void msm_dsi_status(unsigned char *ctrl_base) +{ + u32 status; + + status = MIPI_INP(ctrl_base + DSI_STATUS); + + if (status & 0x80000000) { + MIPI_OUTP(ctrl_base + DSI_STATUS, status); + pr_err("%s: status=%x\n", __func__, status); + } +} + +void msm_dsi_error(unsigned char *ctrl_base) +{ + msm_dsi_ack_err_status(ctrl_base); + msm_dsi_timeout_status(ctrl_base); + msm_dsi_fifo_status(ctrl_base); + msm_dsi_status(ctrl_base); + msm_dsi_dln0_phy_err(ctrl_base); +} + +static void msm_dsi_set_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask) +{ + u32 intr_ctrl; + + intr_ctrl = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL); + intr_ctrl |= mask; + MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, intr_ctrl); +} + +static void msm_dsi_clear_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask) +{ + u32 intr_ctrl; + + intr_ctrl = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL); + intr_ctrl &= ~mask; + MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, intr_ctrl); +} + +static void msm_dsi_set_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&ctrl->irq_lock, flags); + if (ctrl->dsi_irq_mask & mask) { + spin_unlock_irqrestore(&ctrl->irq_lock, flags); + return; + } + if (ctrl->dsi_irq_mask == 0) { + ctrl->mdss_util->enable_irq(ctrl->dsi_hw); + pr_debug("%s: IRQ Enable, mask=%x term=%x\n", __func__, + (int)ctrl->dsi_irq_mask, (int)mask); + } + + msm_dsi_set_irq_mask(ctrl, mask); + ctrl->dsi_irq_mask |= mask; + spin_unlock_irqrestore(&ctrl->irq_lock, flags); +} + +static void msm_dsi_clear_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&ctrl->irq_lock, flags); + if (!(ctrl->dsi_irq_mask & mask)) { + spin_unlock_irqrestore(&ctrl->irq_lock, flags); + return; + } + ctrl->dsi_irq_mask &= ~mask; + if (ctrl->dsi_irq_mask == 0) { + ctrl->mdss_util->disable_irq(ctrl->dsi_hw); + pr_debug("%s: IRQ Disable, mask=%x term=%x\n", __func__, + (int)ctrl->dsi_irq_mask, (int)mask); + } + msm_dsi_clear_irq_mask(ctrl, mask); + spin_unlock_irqrestore(&ctrl->irq_lock, flags); +} + +irqreturn_t msm_dsi_isr_handler(int irq, void *ptr) +{ + u32 isr; + + struct mdss_dsi_ctrl_pdata *ctrl = + (struct mdss_dsi_ctrl_pdata *)ptr; + + spin_lock(&ctrl->mdp_lock); + + if (ctrl->dsi_irq_mask == 0) { + spin_unlock(&ctrl->mdp_lock); + return IRQ_HANDLED; + } + + isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL); + MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr); + + pr_debug("%s: isr=%x", __func__, isr); + + if (isr & DSI_INTR_ERROR) { + pr_err("%s: isr=%x %x", __func__, isr, (int)DSI_INTR_ERROR); + msm_dsi_error(dsi_host_private->dsi_base); + } + + if (isr & DSI_INTR_VIDEO_DONE) + complete(&ctrl->video_comp); + + if (isr & DSI_INTR_CMD_DMA_DONE) + complete(&ctrl->dma_comp); + + if (isr & DSI_INTR_BTA_DONE) + complete(&ctrl->bta_comp); + + if (isr & DSI_INTR_CMD_MDP_DONE) + complete(&ctrl->mdp_comp); + + spin_unlock(&ctrl->mdp_lock); + + return IRQ_HANDLED; +} + +int msm_dsi_irq_init(struct device *dev, int irq_no, + struct mdss_dsi_ctrl_pdata *ctrl) +{ + int ret; + u32 isr; + struct mdss_hw *dsi_hw; + + msm_dsi_ahb_ctrl(1); + isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL); + isr &= ~DSI_INTR_ALL_MASK; + MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr); + msm_dsi_ahb_ctrl(0); + + ret = devm_request_irq(dev, irq_no, msm_dsi_isr_handler, + IRQF_DISABLED, "DSI", ctrl); + if (ret) { + pr_err("%s request_irq() failed!\n", __func__); + return ret; + } + + dsi_hw = kzalloc(sizeof(struct mdss_hw), GFP_KERNEL); + if (!dsi_hw) + return -ENOMEM; + + ctrl->dsi_hw = dsi_hw; + + dsi_hw->irq_info = kzalloc(sizeof(struct irq_info), GFP_KERNEL); + if (!dsi_hw->irq_info) { + kfree(dsi_hw); + pr_err("no mem to save irq info: kzalloc fail\n"); + return -ENOMEM; + } + + dsi_hw->hw_ndx = MDSS_HW_DSI0; + dsi_hw->irq_info->irq = irq_no; + dsi_hw->irq_info->irq_mask = 0; + dsi_hw->irq_info->irq_ena = false; + dsi_hw->irq_info->irq_buzy = false; + + ctrl->mdss_util->register_irq(ctrl->dsi_hw); + ctrl->mdss_util->disable_irq(ctrl->dsi_hw); + + return 0; +} + +static void msm_dsi_get_cmd_engine(struct mdss_dsi_ctrl_pdata *ctrl) +{ + unsigned char *ctrl_base = dsi_host_private->dsi_base; + u32 dsi_ctrl; + + if (ctrl->panel_mode == DSI_VIDEO_MODE) { + dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL); + MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl | 0x04); + } +} + +static void msm_dsi_release_cmd_engine(struct mdss_dsi_ctrl_pdata *ctrl) +{ + unsigned char *ctrl_base = dsi_host_private->dsi_base; + u32 dsi_ctrl; + + if (ctrl->panel_mode == DSI_VIDEO_MODE) { + dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL); + dsi_ctrl &= ~0x04; + MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl); + } +} + +static int msm_dsi_wait4mdp_done(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int rc; + unsigned long flag; + + spin_lock_irqsave(&ctrl->mdp_lock, flag); + reinit_completion(&ctrl->mdp_comp); + msm_dsi_set_irq(ctrl, DSI_INTR_CMD_MDP_DONE_MASK); + spin_unlock_irqrestore(&ctrl->mdp_lock, flag); + + rc = wait_for_completion_timeout(&ctrl->mdp_comp, + msecs_to_jiffies(VSYNC_PERIOD * 4)); + + if (rc == 0) { + pr_err("DSI wait 4 mdp done time out\n"); + rc = -ETIME; + } else if (!IS_ERR_VALUE(rc)) { + rc = 0; + } + + msm_dsi_clear_irq(ctrl, DSI_INTR_CMD_MDP_DONE_MASK); + + return rc; +} + +void msm_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int rc; + u32 dsi_status; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + + if (ctrl->panel_mode == DSI_VIDEO_MODE) + return; + + dsi_status = MIPI_INP(ctrl_base + DSI_STATUS); + if (dsi_status & 0x04) { + pr_debug("dsi command engine is busy\n"); + rc = msm_dsi_wait4mdp_done(ctrl); + if (rc) + pr_err("Timed out waiting for mdp done"); + } +} + +static int msm_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int rc; + unsigned long flag; + + spin_lock_irqsave(&ctrl->mdp_lock, flag); + reinit_completion(&ctrl->video_comp); + msm_dsi_set_irq(ctrl, DSI_INTR_VIDEO_DONE_MASK); + spin_unlock_irqrestore(&ctrl->mdp_lock, flag); + + rc = wait_for_completion_timeout(&ctrl->video_comp, + msecs_to_jiffies(VSYNC_PERIOD * 4)); + + if (rc == 0) { + pr_err("DSI wait 4 video done time out\n"); + rc = -ETIME; + } else if (!IS_ERR_VALUE(rc)) { + rc = 0; + } + + msm_dsi_clear_irq(ctrl, DSI_INTR_VIDEO_DONE_MASK); + + return rc; +} + +static int msm_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int rc = 0; + u32 dsi_status; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + + if (ctrl->panel_mode == DSI_CMD_MODE) + return rc; + + dsi_status = MIPI_INP(ctrl_base + DSI_STATUS); + if (dsi_status & 0x08) { + pr_debug("dsi command in video mode wait for active region\n"); + rc = msm_dsi_wait4video_done(ctrl); + /* delay 4-5 ms to skip BLLP */ + if (!rc) + usleep_range(4000, 5000); + } + return rc; +} + +void msm_dsi_host_init(struct mdss_panel_data *pdata) +{ + u32 dsi_ctrl, data; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mipi_panel_info *pinfo; + + pr_debug("%s\n", __func__); + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + pinfo = &pdata->panel_info.mipi; + + + if (pinfo->mode == DSI_VIDEO_MODE) { + data = 0; + if (pinfo->pulse_mode_hsa_he) + data |= BIT(28); + if (pinfo->hfp_power_stop) + data |= BIT(24); + if (pinfo->hbp_power_stop) + data |= BIT(20); + if (pinfo->hsa_power_stop) + data |= BIT(16); + if (pinfo->eof_bllp_power_stop) + data |= BIT(15); + if (pinfo->bllp_power_stop) + data |= BIT(12); + data |= ((pinfo->traffic_mode & 0x03) << 8); + data |= ((pinfo->dst_format & 0x03) << 4); /* 2 bits */ + data |= (pinfo->vc & 0x03); + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_CTRL, data); + + data = 0; + data |= ((pinfo->rgb_swap & 0x07) << 12); + if (pinfo->b_sel) + data |= BIT(8); + if (pinfo->g_sel) + data |= BIT(4); + if (pinfo->r_sel) + data |= BIT(0); + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_DATA_CTRL, data); + } else if (pinfo->mode == DSI_CMD_MODE) { + data = 0; + data |= ((pinfo->interleave_max & 0x0f) << 20); + data |= ((pinfo->rgb_swap & 0x07) << 16); + if (pinfo->b_sel) + data |= BIT(12); + if (pinfo->g_sel) + data |= BIT(8); + if (pinfo->r_sel) + data |= BIT(4); + data |= (pinfo->dst_format & 0x0f); /* 4 bits */ + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_CTRL, data); + + /* DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL */ + data = pinfo->wr_mem_continue & 0x0ff; + data <<= 8; + data |= (pinfo->wr_mem_start & 0x0ff); + if (pinfo->insert_dcs_cmd) + data |= BIT(16); + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL, + data); + } else + pr_err("%s: Unknown DSI mode=%d\n", __func__, pinfo->mode); + + dsi_ctrl = BIT(8) | BIT(2); /* clock enable & cmd mode */ + + if (pinfo->crc_check) + dsi_ctrl |= BIT(24); + if (pinfo->ecc_check) + dsi_ctrl |= BIT(20); + if (pinfo->data_lane3) + dsi_ctrl |= BIT(7); + if (pinfo->data_lane2) + dsi_ctrl |= BIT(6); + if (pinfo->data_lane1) + dsi_ctrl |= BIT(5); + if (pinfo->data_lane0) + dsi_ctrl |= BIT(4); + + /* from frame buffer, low power mode */ + /* DSI_COMMAND_MODE_DMA_CTRL */ + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, 0x14000000); + + data = 0; + if (pinfo->te_sel) + data |= BIT(31); + data |= pinfo->mdp_trigger << 4;/* cmd mdp trigger */ + data |= pinfo->dma_trigger; /* cmd dma trigger */ + data |= (pinfo->stream & 0x01) << 8; + MIPI_OUTP(ctrl_base + DSI_TRIG_CTRL, data); + + /* DSI_LAN_SWAP_CTRL */ + MIPI_OUTP(ctrl_base + DSI_LANE_SWAP_CTRL, ctrl_pdata->dlane_swap); + + /* clock out ctrl */ + data = pinfo->t_clk_post & 0x3f; /* 6 bits */ + data <<= 8; + data |= pinfo->t_clk_pre & 0x3f; /* 6 bits */ + /* DSI_CLKOUT_TIMING_CTRL */ + MIPI_OUTP(ctrl_base + DSI_CLKOUT_TIMING_CTRL, data); + + data = 0; + if (pinfo->rx_eot_ignore) + data |= BIT(4); + if (pinfo->tx_eot_append) + data |= BIT(0); + MIPI_OUTP(ctrl_base + DSI_EOT_PACKET_CTRL, data); + + + /* allow only ack-err-status to generate interrupt */ + /* DSI_ERR_INT_MASK0 */ + MIPI_OUTP(ctrl_base + DSI_ERR_INT_MASK0, 0x13ff3fe0); + + /* turn esc, byte, dsi, pclk, sclk, hclk on */ + MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0x23f); + + dsi_ctrl |= BIT(0); /* enable dsi */ + MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl); + + wmb(); /* ensure write is finished before progressing */ +} + +void dsi_set_tx_power_mode(int mode) +{ + u32 data; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + + data = MIPI_INP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL); + + if (mode == 0) + data &= ~BIT(26); + else + data |= BIT(26); + + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, data); +} + +void msm_dsi_sw_reset(void) +{ + u32 dsi_ctrl; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + + pr_debug("%s\n", __func__); + + dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL); + dsi_ctrl &= ~0x01; + MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl); + wmb(); /* ensure write is finished before progressing */ + + /* turn esc, byte, dsi, pclk, sclk, hclk on */ + MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0x23f); + wmb(); /* ensure write is finished before progressing */ + + MIPI_OUTP(ctrl_base + DSI_SOFT_RESET, 0x01); + wmb(); /* ensure write is finished before progressing */ + MIPI_OUTP(ctrl_base + DSI_SOFT_RESET, 0x00); + wmb(); /* ensure write is finished before progressing */ +} + +void msm_dsi_controller_cfg(int enable) +{ + u32 dsi_ctrl, status; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + + pr_debug("%s\n", __func__); + + /* Check for CMD_MODE_DMA_BUSY */ + if (readl_poll_timeout((ctrl_base + DSI_STATUS), + status, + ((status & 0x02) == 0), + DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US)) { + pr_err("%s: DSI status=%x failed\n", __func__, status); + pr_err("%s: Doing sw reset\n", __func__); + msm_dsi_sw_reset(); + } + + /* Check for x_HS_FIFO_EMPTY */ + if (readl_poll_timeout((ctrl_base + DSI_FIFO_STATUS), + status, + ((status & 0x11111000) == 0x11111000), + DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US)) + pr_err("%s: FIFO status=%x failed\n", __func__, status); + + /* Check for VIDEO_MODE_ENGINE_BUSY */ + if (readl_poll_timeout((ctrl_base + DSI_STATUS), + status, + ((status & 0x08) == 0), + DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US)) { + pr_err("%s: DSI status=%x\n", __func__, status); + pr_err("%s: Doing sw reset\n", __func__); + msm_dsi_sw_reset(); + } + + dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL); + if (enable) + dsi_ctrl |= 0x01; + else + dsi_ctrl &= ~0x01; + + MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl); + wmb(); /* ensure write is finished before progressing */ +} + +void msm_dsi_op_mode_config(int mode, struct mdss_panel_data *pdata) +{ + u32 dsi_ctrl; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + + pr_debug("%s\n", __func__); + + dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL); + + if (dsi_ctrl & DSI_VIDEO_MODE_EN) + dsi_ctrl &= ~(DSI_CMD_MODE_EN|DSI_EN); + else + dsi_ctrl &= ~(DSI_CMD_MODE_EN|DSI_VIDEO_MODE_EN|DSI_EN); + + if (mode == DSI_VIDEO_MODE) { + dsi_ctrl |= (DSI_VIDEO_MODE_EN|DSI_EN); + } else { + dsi_ctrl |= (DSI_CMD_MODE_EN|DSI_EN); + /* For Video mode panel, keep Video and Cmd mode ON */ + if (pdata->panel_info.type == MIPI_VIDEO_PANEL) + dsi_ctrl |= DSI_VIDEO_MODE_EN; + } + + pr_debug("%s: dsi_ctrl=%x\n", __func__, dsi_ctrl); + + MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl); + wmb(); /* ensure write is finished before progressing */ +} + +int msm_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_buf *tp) +{ + int len, rc; + unsigned long size, addr; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + unsigned long flag; + + len = ALIGN(tp->len, 4); + size = ALIGN(tp->len, SZ_4K); + + tp->dmap = dma_map_single(&dsi_host_private->dis_dev, tp->data, size, + DMA_TO_DEVICE); + if (dma_mapping_error(&dsi_host_private->dis_dev, tp->dmap)) { + pr_err("%s: dmap mapp failed\n", __func__); + return -ENOMEM; + } + + addr = tp->dmap; + + msm_dsi_get_cmd_engine(ctrl); + + spin_lock_irqsave(&ctrl->mdp_lock, flag); + reinit_completion(&ctrl->dma_comp); + msm_dsi_set_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK); + spin_unlock_irqrestore(&ctrl->mdp_lock, flag); + + MIPI_OUTP(ctrl_base + DSI_DMA_CMD_OFFSET, addr); + MIPI_OUTP(ctrl_base + DSI_DMA_CMD_LENGTH, len); + wmb(); /* ensure write is finished before progressing */ + + MIPI_OUTP(ctrl_base + DSI_CMD_MODE_DMA_SW_TRIGGER, 0x01); + wmb(); /* ensure write is finished before progressing */ + + rc = wait_for_completion_timeout(&ctrl->dma_comp, + msecs_to_jiffies(DSI_DMA_CMD_TIMEOUT_MS)); + if (rc == 0) { + pr_err("DSI command transaction time out\n"); + rc = -ETIME; + } else if (!IS_ERR_VALUE(rc)) { + rc = 0; + } + + dma_unmap_single(&dsi_host_private->dis_dev, tp->dmap, size, + DMA_TO_DEVICE); + tp->dmap = 0; + + msm_dsi_clear_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK); + + msm_dsi_release_cmd_engine(ctrl); + + return rc; +} + +int msm_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_buf *rp, int rlen) +{ + u32 *lp, data; + int i, off, cnt; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + + lp = (u32 *)rp->data; + cnt = rlen; + cnt += 3; + cnt >>= 2; + + if (cnt > 4) + cnt = 4; /* 4 x 32 bits registers only */ + + off = DSI_RDBK_DATA0; + off += ((cnt - 1) * 4); + + for (i = 0; i < cnt; i++) { + data = (u32)MIPI_INP(ctrl_base + off); + *lp++ = ntohl(data); /* to network byte order */ + pr_debug("%s: data = 0x%x and ntohl(data) = 0x%x\n", + __func__, data, ntohl(data)); + off -= 4; + rp->len += sizeof(*lp); + } + + return rlen; +} + +static int msm_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_cmd_desc *cmds, int cnt) +{ + struct dsi_buf *tp; + struct dsi_cmd_desc *cm; + struct dsi_ctrl_hdr *dchdr; + int len; + int rc = 0; + + tp = &ctrl->tx_buf; + mdss_dsi_buf_init(tp); + cm = cmds; + len = 0; + while (cnt--) { + dchdr = &cm->dchdr; + mdss_dsi_buf_reserve(tp, len); + len = mdss_dsi_cmd_dma_add(tp, cm); + if (!len) { + pr_err("%s: failed to add cmd = 0x%x\n", + __func__, cm->payload[0]); + rc = -EINVAL; + goto dsi_cmds_tx_err; + } + + if (dchdr->last) { + tp->data = tp->start; /* begin of buf */ + rc = msm_dsi_wait4video_eng_busy(ctrl); + if (rc) { + pr_err("%s: wait4video_eng failed\n", __func__); + goto dsi_cmds_tx_err; + + } + + rc = msm_dsi_cmd_dma_tx(ctrl, tp); + if (IS_ERR_VALUE(len)) { + pr_err("%s: failed to call cmd_dma_tx for cmd = 0x%x\n", + __func__, cmds->payload[0]); + goto dsi_cmds_tx_err; + } + + if (dchdr->wait) + usleep_range(dchdr->wait * 1000, + dchdr->wait * 1000); + + mdss_dsi_buf_init(tp); + len = 0; + } + cm++; + } + +dsi_cmds_tx_err: + return rc; +} + +static int msm_dsi_parse_rx_response(struct dsi_buf *rp) +{ + int rc = 0; + unsigned char cmd; + + cmd = rp->data[0]; + switch (cmd) { + case DTYPE_ACK_ERR_RESP: + pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__); + rc = -EINVAL; + break; + case DTYPE_GEN_READ1_RESP: + case DTYPE_DCS_READ1_RESP: + mdss_dsi_short_read1_resp(rp); + break; + case DTYPE_GEN_READ2_RESP: + case DTYPE_DCS_READ2_RESP: + mdss_dsi_short_read2_resp(rp); + break; + case DTYPE_GEN_LREAD_RESP: + case DTYPE_DCS_LREAD_RESP: + mdss_dsi_long_read_resp(rp); + break; + default: + rc = -EINVAL; + pr_warn("%s: Unknown cmd received\n", __func__); + break; + } + + return rc; +} + +/* MIPI_DSI_MRPS, Maximum Return Packet Size */ +static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */ + +static struct dsi_cmd_desc pkt_size_cmd = { + {DTYPE_MAX_PKTSIZE, 1, 0, 0, 0, sizeof(max_pktsize)}, + max_pktsize, +}; + +static int msm_dsi_set_max_packet_size(struct mdss_dsi_ctrl_pdata *ctrl, + int size) +{ + struct dsi_buf *tp; + int rc; + + tp = &ctrl->tx_buf; + mdss_dsi_buf_init(tp); + max_pktsize[0] = size; + + rc = mdss_dsi_cmd_dma_add(tp, &pkt_size_cmd); + if (!rc) { + pr_err("%s: failed to add max_pkt_size\n", __func__); + return -EINVAL; + } + + rc = msm_dsi_wait4video_eng_busy(ctrl); + if (rc) { + pr_err("%s: failed to wait4video_eng\n", __func__); + return rc; + } + + rc = msm_dsi_cmd_dma_tx(ctrl, tp); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: failed to tx max_pkt_size\n", __func__); + return rc; + } + pr_debug("%s: max_pkt_size=%d sent\n", __func__, size); + return rc; +} + +/* read data length is less than or equal to 10 bytes*/ +static int msm_dsi_cmds_rx_1(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_cmd_desc *cmds, int rlen) +{ + int rc; + struct dsi_buf *tp, *rp; + + tp = &ctrl->tx_buf; + rp = &ctrl->rx_buf; + mdss_dsi_buf_init(rp); + mdss_dsi_buf_init(tp); + + rc = mdss_dsi_cmd_dma_add(tp, cmds); + if (!rc) { + pr_err("%s: dsi_cmd_dma_add failed\n", __func__); + rc = -EINVAL; + goto dsi_cmds_rx_1_error; + } + + rc = msm_dsi_wait4video_eng_busy(ctrl); + if (rc) { + pr_err("%s: wait4video_eng failed\n", __func__); + goto dsi_cmds_rx_1_error; + } + + rc = msm_dsi_cmd_dma_tx(ctrl, tp); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: msm_dsi_cmd_dma_tx failed\n", __func__); + goto dsi_cmds_rx_1_error; + } + + if (rlen <= DSI_SHORT_PKT_DATA_SIZE) { + msm_dsi_cmd_dma_rx(ctrl, rp, rlen); + } else { + msm_dsi_cmd_dma_rx(ctrl, rp, rlen + DSI_HOST_HDR_SIZE); + rp->len = rlen + DSI_HOST_HDR_SIZE; + } + rc = msm_dsi_parse_rx_response(rp); + +dsi_cmds_rx_1_error: + if (rc) + rp->len = 0; + + return rc; +} + +/* read data length is more than 10 bytes, which requires multiple DSI read*/ +static int msm_dsi_cmds_rx_2(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_cmd_desc *cmds, int rlen) +{ + int rc; + struct dsi_buf *tp, *rp; + int pkt_size, data_bytes, total; + + tp = &ctrl->tx_buf; + rp = &ctrl->rx_buf; + mdss_dsi_buf_init(rp); + pkt_size = DSI_MAX_PKT_SIZE; + data_bytes = MDSS_DSI_LEN; + total = 0; + + while (true) { + rc = msm_dsi_set_max_packet_size(ctrl, pkt_size); + if (rc) + break; + + mdss_dsi_buf_init(tp); + rc = mdss_dsi_cmd_dma_add(tp, cmds); + if (!rc) { + pr_err("%s: dsi_cmd_dma_add failed\n", __func__); + rc = -EINVAL; + break; + } + rc = msm_dsi_wait4video_eng_busy(ctrl); + if (rc) { + pr_err("%s: wait4video_eng failed\n", __func__); + break; + } + + rc = msm_dsi_cmd_dma_tx(ctrl, tp); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: msm_dsi_cmd_dma_tx failed\n", __func__); + break; + } + + msm_dsi_cmd_dma_rx(ctrl, rp, DSI_MAX_BYTES_TO_READ); + + rp->data += DSI_MAX_BYTES_TO_READ - DSI_HOST_HDR_SIZE; + total += data_bytes; + if (total >= rlen) + break; + + data_bytes = DSI_MAX_BYTES_TO_READ - DSI_HOST_HDR_SIZE; + pkt_size += data_bytes; + } + + if (!rc) { + rp->data = rp->start; + rp->len = rlen + DSI_HOST_HDR_SIZE; + rc = msm_dsi_parse_rx_response(rp); + } + + if (rc) + rp->len = 0; + + return rc; +} + +int msm_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_cmd_desc *cmds, int rlen) +{ + int rc; + + if (rlen <= DSI_MAX_PKT_SIZE) + rc = msm_dsi_cmds_rx_1(ctrl, cmds, rlen); + else + rc = msm_dsi_cmds_rx_2(ctrl, cmds, rlen); + + return rc; +} + +void msm_dsi_cmdlist_tx(struct mdss_dsi_ctrl_pdata *ctrl, + struct dcs_cmd_req *req) +{ + int ret; + + ret = msm_dsi_cmds_tx(ctrl, req->cmds, req->cmds_cnt); + + if (req->cb) + req->cb(ret); +} + +void msm_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl, + struct dcs_cmd_req *req) +{ + struct dsi_buf *rp; + int len = 0; + + if (req->rbuf) { + rp = &ctrl->rx_buf; + len = msm_dsi_cmds_rx(ctrl, req->cmds, req->rlen); + memcpy(req->rbuf, rp->data, rp->len); + } else { + pr_err("%s: No rx buffer provided\n", __func__); + } + + if (req->cb) + req->cb(len); +} +int msm_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) +{ + struct dcs_cmd_req *req; + int dsi_on; + int ret = -EINVAL; + + mutex_lock(&ctrl->mutex); + dsi_on = dsi_host_private->dsi_on; + mutex_unlock(&ctrl->mutex); + if (!dsi_on) { + pr_err("try to send DSI commands while dsi is off\n"); + return ret; + } + + if (from_mdp) /* from mdp kickoff */ + mutex_lock(&ctrl->cmd_mutex); + req = mdss_dsi_cmdlist_get(ctrl, from_mdp); + + if (!req) { + mutex_unlock(&ctrl->cmd_mutex); + return ret; + } + /* + * mdss interrupt is generated in mdp core clock domain + * mdp clock need to be enabled to receive dsi interrupt + * also, axi bus bandwidth need since dsi controller will + * fetch dcs commands from axi bus + */ + mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P); + msm_dsi_clk_ctrl(&ctrl->panel_data, 1); + + if (0 == (req->flags & CMD_REQ_LP_MODE)) + dsi_set_tx_power_mode(0); + + if (req->flags & CMD_REQ_RX) + msm_dsi_cmdlist_rx(ctrl, req); + else + msm_dsi_cmdlist_tx(ctrl, req); + + if (0 == (req->flags & CMD_REQ_LP_MODE)) + dsi_set_tx_power_mode(1); + + msm_dsi_clk_ctrl(&ctrl->panel_data, 0); + mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P); + + if (from_mdp) /* from mdp kickoff */ + mutex_unlock(&ctrl->cmd_mutex); + return 0; +} + +static int msm_dsi_cal_clk_rate(struct mdss_panel_data *pdata, + u64 *bitclk_rate, + u32 *dsiclk_rate, + u32 *byteclk_rate, + u32 *pclk_rate) +{ + struct mdss_panel_info *pinfo; + struct mipi_panel_info *mipi; + u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height; + int lanes; + u64 clk_rate; + + pinfo = &pdata->panel_info; + mipi = &pdata->panel_info.mipi; + + hbp = pdata->panel_info.lcdc.h_back_porch; + hfp = pdata->panel_info.lcdc.h_front_porch; + vbp = pdata->panel_info.lcdc.v_back_porch; + vfp = pdata->panel_info.lcdc.v_front_porch; + hspw = pdata->panel_info.lcdc.h_pulse_width; + vspw = pdata->panel_info.lcdc.v_pulse_width; + width = pdata->panel_info.xres; + height = pdata->panel_info.yres; + + lanes = 0; + if (mipi->data_lane0) + lanes++; + if (mipi->data_lane1) + lanes++; + if (mipi->data_lane2) + lanes++; + if (mipi->data_lane3) + lanes++; + if (lanes == 0) + return -EINVAL; + + *bitclk_rate = (width + hbp + hfp + hspw) * (height + vbp + vfp + vspw); + *bitclk_rate *= mipi->frame_rate; + *bitclk_rate *= pdata->panel_info.bpp; + do_div(*bitclk_rate, lanes); + clk_rate = *bitclk_rate; + + do_div(clk_rate, 8U); + *byteclk_rate = (u32) clk_rate; + *dsiclk_rate = *byteclk_rate * lanes; + *pclk_rate = *byteclk_rate * lanes * 8 / pdata->panel_info.bpp; + + pr_debug("dsiclk_rate=%u, byteclk=%u, pck_=%u\n", + *dsiclk_rate, *byteclk_rate, *pclk_rate); + return 0; +} + +static int msm_dsi_on(struct mdss_panel_data *pdata) +{ + int ret = 0, i; + u64 clk_rate; + struct mdss_panel_info *pinfo; + struct mipi_panel_info *mipi; + u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height; + u32 ystride, bpp, data; + u32 dummy_xres, dummy_yres; + u64 bitclk_rate = 0 + u32 byteclk_rate = 0, pclk_rate = 0, dsiclk_rate = 0; + unsigned char *ctrl_base = dsi_host_private->dsi_base; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + pr_debug("%s\n", __func__); + + pinfo = &pdata->panel_info; + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + mutex_lock(&ctrl_pdata->mutex); + + + if (!pdata->panel_info.dynamic_switch_pending) { + for (i = 0; !ret && (i < DSI_MAX_PM); i++) { + ret = msm_mdss_enable_vreg( + ctrl_pdata->power_data[i].vreg_config, + ctrl_pdata->power_data[i].num_vreg, 1); + if (ret) { + pr_err("%s: failed to enable vregs for %s\n", + __func__, __mdss_dsi_pm_name(i)); + goto error_vreg; + } + } + } + + msm_dsi_ahb_ctrl(1); + msm_dsi_phy_sw_reset(dsi_host_private->dsi_base); + msm_dsi_phy_init(dsi_host_private->dsi_base, pdata); + + msm_dsi_cal_clk_rate(pdata, &bitclk_rate, &dsiclk_rate, + &byteclk_rate, &pclk_rate); + msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, dsiclk_rate, + byteclk_rate, pclk_rate); + msm_dsi_prepare_clocks(); + msm_dsi_clk_enable(); + + clk_rate = pdata->panel_info.clk_rate; + clk_rate = min(clk_rate, pdata->panel_info.clk_max); + + hbp = pdata->panel_info.lcdc.h_back_porch; + hfp = pdata->panel_info.lcdc.h_front_porch; + vbp = pdata->panel_info.lcdc.v_back_porch; + vfp = pdata->panel_info.lcdc.v_front_porch; + hspw = pdata->panel_info.lcdc.h_pulse_width; + vspw = pdata->panel_info.lcdc.v_pulse_width; + width = pdata->panel_info.xres; + height = pdata->panel_info.yres; + + mipi = &pdata->panel_info.mipi; + if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { + dummy_xres = pdata->panel_info.lcdc.xres_pad; + dummy_yres = pdata->panel_info.lcdc.yres_pad; + + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_ACTIVE_H, + ((hspw + hbp + width + dummy_xres) << 16 | + (hspw + hbp))); + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_ACTIVE_V, + ((vspw + vbp + height + dummy_yres) << 16 | + (vspw + vbp))); + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_TOTAL, + (vspw + vbp + height + dummy_yres + + vfp - 1) << 16 | (hspw + hbp + + width + dummy_xres + hfp - 1)); + + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_HSYNC, (hspw << 16)); + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_VSYNC, 0); + MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_VSYNC_VPOS, + (vspw << 16)); + + } else { /* command mode */ + if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888) + bpp = 3; + else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB666) + bpp = 3; + else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565) + bpp = 2; + else + bpp = 3; /* Default format set to RGB888 */ + + ystride = width * bpp + 1; + + data = (ystride << 16) | (mipi->vc << 8) | DTYPE_DCS_LWRITE; + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM0_CTRL, + data); + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM1_CTRL, + data); + + data = height << 16 | width; + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM1_TOTAL, + data); + MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM0_TOTAL, + data); + } + + msm_dsi_sw_reset(); + msm_dsi_host_init(pdata); + + if (mipi->force_clk_lane_hs) { + u32 tmp; + + tmp = MIPI_INP(ctrl_base + DSI_LANE_CTRL); + tmp |= (1<<28); + MIPI_OUTP(ctrl_base + DSI_LANE_CTRL, tmp); + wmb(); /* ensure write is finished before progressing */ + } + + msm_dsi_op_mode_config(mipi->mode, pdata); + + msm_dsi_set_irq(ctrl_pdata, DSI_INTR_ERROR_MASK); + dsi_host_private->clk_count = 1; + dsi_host_private->dsi_on = 1; + +error_vreg: + if (ret) { + for (; i >= 0; i--) + msm_mdss_enable_vreg( + ctrl_pdata->power_data[i].vreg_config, + ctrl_pdata->power_data[i].num_vreg, 0); + } + + mutex_unlock(&ctrl_pdata->mutex); + return ret; +} + +static int msm_dsi_off(struct mdss_panel_data *pdata) +{ + int ret = 0, i; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + ret = -EINVAL; + return ret; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s\n", __func__); + mutex_lock(&ctrl_pdata->mutex); + msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask); + msm_dsi_controller_cfg(0); + msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, 0, 0, 0); + msm_dsi_clk_disable(); + msm_dsi_unprepare_clocks(); + msm_dsi_phy_off(dsi_host_private->dsi_base); + msm_dsi_ahb_ctrl(0); + + if (!pdata->panel_info.dynamic_switch_pending) { + for (i = DSI_MAX_PM - 1; i >= 0; i--) { + ret = msm_mdss_enable_vreg( + ctrl_pdata->power_data[i].vreg_config, + ctrl_pdata->power_data[i].num_vreg, 0); + if (ret) + pr_err("%s: failed to disable vregs for %s\n", + __func__, __mdss_dsi_pm_name(i)); + } + } + dsi_host_private->clk_count = 0; + dsi_host_private->dsi_on = 0; + + mutex_unlock(&ctrl_pdata->mutex); + + return ret; +} + +static int msm_dsi_cont_on(struct mdss_panel_data *pdata) +{ + struct mdss_panel_info *pinfo; + int ret = 0, i; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + ret = -EINVAL; + return ret; + } + + + pr_debug("%s:\n", __func__); + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pinfo = &pdata->panel_info; + mutex_lock(&ctrl_pdata->mutex); + for (i = 0; !ret && (i < DSI_MAX_PM); i++) { + ret = msm_mdss_enable_vreg( + ctrl_pdata->power_data[i].vreg_config, + ctrl_pdata->power_data[i].num_vreg, 1); + if (ret) { + pr_err("%s: failed to enable vregs for %s\n", + __func__, __mdss_dsi_pm_name(i)); + goto error_vreg; + } + } + pinfo->panel_power_state = MDSS_PANEL_POWER_ON; + ret = mdss_dsi_panel_reset(pdata, 1); + if (ret) { + pr_err("%s: Panel reset failed\n", __func__); + mutex_unlock(&ctrl_pdata->mutex); + return ret; + } + + msm_dsi_ahb_ctrl(1); + msm_dsi_prepare_clocks(); + msm_dsi_clk_enable(); + msm_dsi_set_irq(ctrl_pdata, DSI_INTR_ERROR_MASK); + dsi_host_private->clk_count = 1; + dsi_host_private->dsi_on = 1; + +error_vreg: + if (ret) { + for (; i >= 0; i--) + msm_mdss_enable_vreg( + ctrl_pdata->power_data[i].vreg_config, + ctrl_pdata->power_data[i].num_vreg, 0); + } + + mutex_unlock(&ctrl_pdata->mutex); + return ret; +} + +static int msm_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct dcs_cmd_req cmdreq; + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = ctrl->status_cmds.cmds; + cmdreq.cmds_cnt = ctrl->status_cmds.cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT | CMD_REQ_RX; + cmdreq.rlen = 1; + cmdreq.cb = NULL; + cmdreq.rbuf = ctrl->status_buf.data; + + return mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + + +/** + * msm_dsi_reg_status_check() - Check dsi panel status through reg read + * @ctrl_pdata: pointer to the dsi controller structure + * + * This function can be used to check the panel status through reading the + * status register from the panel. + * + * Return: positive value if the panel is in good state, negative value or + * zero otherwise. + */ +int msm_dsi_reg_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + int ret = 0; + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return 0; + } + + pr_debug("%s: Checking Register status\n", __func__); + + msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 1); + + if (ctrl_pdata->status_cmds.link_state == DSI_HS_MODE) + dsi_set_tx_power_mode(0); + + ret = msm_dsi_read_status(ctrl_pdata); + + if (ctrl_pdata->status_cmds.link_state == DSI_HS_MODE) + dsi_set_tx_power_mode(1); + + if (ret == 0) { + if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 0)) { + pr_err("%s: Read back value from panel is incorrect\n", + __func__); + ret = -EINVAL; + } else { + ret = 1; + } + } else { + pr_err("%s: Read status register returned error\n", __func__); + } + + msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 0); + pr_debug("%s: Read register done with ret: %d\n", __func__, ret); + + return ret; +} + +/** + * msm_dsi_bta_status_check() - Check dsi panel status through bta check + * @ctrl_pdata: pointer to the dsi controller structure + * + * This function can be used to check status of the panel using bta check + * for the panel. + * + * Return: positive value if the panel is in good state, negative value or + * zero otherwise. + */ +static int msm_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + int ret = 0; + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return 0; + } + + mutex_lock(&ctrl_pdata->cmd_mutex); + msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 1); + msm_dsi_cmd_mdp_busy(ctrl_pdata); + msm_dsi_set_irq(ctrl_pdata, DSI_INTR_BTA_DONE_MASK); + reinit_completion(&ctrl_pdata->bta_comp); + + /* BTA trigger */ + MIPI_OUTP(dsi_host_private->dsi_base + DSI_CMD_MODE_BTA_SW_TRIGGER, + 0x01); + wmb(); /* ensure write is finished before progressing */ + ret = wait_for_completion_killable_timeout(&ctrl_pdata->bta_comp, + HZ/10); + msm_dsi_clear_irq(ctrl_pdata, DSI_INTR_BTA_DONE_MASK); + msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 0); + mutex_unlock(&ctrl_pdata->cmd_mutex); + + if (ret <= 0) + pr_err("%s: DSI BTA error: %i\n", __func__, __LINE__); + + pr_debug("%s: BTA done with ret: %d\n", __func__, ret); + return ret; +} + +static void msm_dsi_debug_enable_clock(int on) +{ + if (dsi_host_private->debug_enable_clk) + dsi_host_private->debug_enable_clk(on); + + if (on) + msm_dsi_ahb_ctrl(1); + else + msm_dsi_ahb_ctrl(0); +} + +static int msm_dsi_debug_init(void) +{ + int rc; + + if (!mdss_res) + return 0; + + dsi_host_private->debug_enable_clk = + mdss_res->debug_inf.debug_enable_clock; + + mdss_res->debug_inf.debug_enable_clock = msm_dsi_debug_enable_clock; + + + rc = mdss_debug_register_base("dsi0", + dsi_host_private->dsi_base, + dsi_host_private->dsi_reg_size, + NULL); + + return rc; +} + +static int dsi_get_panel_cfg(char *panel_cfg) +{ + int rc; + struct mdss_panel_cfg *pan_cfg = NULL; + + if (!panel_cfg) + return MDSS_PANEL_INTF_INVALID; + + pan_cfg = mdp3_panel_intf_type(MDSS_PANEL_INTF_DSI); + if (IS_ERR(pan_cfg)) { + panel_cfg[0] = 0; + return PTR_ERR(pan_cfg); + } else if (!pan_cfg) { + panel_cfg[0] = 0; + return 0; + } + + pr_debug("%s:%d: cfg:[%s]\n", __func__, __LINE__, + pan_cfg->arg_cfg); + rc = strlcpy(panel_cfg, pan_cfg->arg_cfg, + MDSS_MAX_PANEL_LEN); + return rc; +} + +static struct device_node *dsi_pref_prim_panel( + struct platform_device *pdev) +{ + struct device_node *dsi_pan_node = NULL; + + pr_debug("%s:%d: Select primary panel from dt\n", + __func__, __LINE__); + dsi_pan_node = of_parse_phandle(pdev->dev.of_node, + "qcom,dsi-pref-prim-pan", 0); + if (!dsi_pan_node) + pr_err("%s:can't find panel phandle\n", __func__); + + return dsi_pan_node; +} + +/** + * dsi_find_panel_of_node(): find device node of dsi panel + * @pdev: platform_device of the dsi ctrl node + * @panel_cfg: string containing intf specific config data + * + * Function finds the panel device node using the interface + * specific configuration data. This configuration data is + * could be derived from the result of bootloader's GCDB + * panel detection mechanism. If such config data doesn't + * exist then this panel returns the default panel configured + * in the device tree. + * + * returns pointer to panel node on success, NULL on error. + */ +static struct device_node *dsi_find_panel_of_node( + struct platform_device *pdev, char *panel_cfg) +{ + int l; + char *panel_name; + struct device_node *dsi_pan_node = NULL, *mdss_node = NULL; + + if (!panel_cfg) + return NULL; + + l = strlen(panel_cfg); + if (!l) { + /* no panel cfg chg, parse dt */ + pr_debug("%s:%d: no cmd line cfg present\n", + __func__, __LINE__); + dsi_pan_node = dsi_pref_prim_panel(pdev); + } else { + if (panel_cfg[0] != '0') { + pr_err("%s:%d:ctrl id=[%d] not supported\n", + __func__, __LINE__, panel_cfg[0]); + return NULL; + } + /* + * skip first two chars '' and + * ':' to get to the panel name + */ + panel_name = panel_cfg + 2; + pr_debug("%s:%d:%s:%s\n", __func__, __LINE__, + panel_cfg, panel_name); + + mdss_node = of_parse_phandle(pdev->dev.of_node, + "qcom,mdss-mdp", 0); + + if (!mdss_node) { + pr_err("%s: %d: mdss_node null\n", + __func__, __LINE__); + return NULL; + } + dsi_pan_node = of_find_node_by_name(mdss_node, + panel_name); + if (!dsi_pan_node) { + pr_err("%s: invalid pan node\n", + __func__); + dsi_pan_node = dsi_pref_prim_panel(pdev); + } + } + return dsi_pan_node; +} + +static int msm_dsi_clk_ctrl(struct mdss_panel_data *pdata, int enable) +{ + u32 bitclk_rate = 0, byteclk_rate = 0, pclk_rate = 0, dsiclk_rate = 0; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + pr_debug("%s:\n", __func__); + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + mutex_lock(&ctrl_pdata->mutex); + + if (enable) { + dsi_host_private->clk_count++; + if (dsi_host_private->clk_count == 1) { + msm_dsi_ahb_ctrl(1); + msm_dsi_cal_clk_rate(pdata, &bitclk_rate, &dsiclk_rate, + &byteclk_rate, &pclk_rate); + msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, dsiclk_rate, + byteclk_rate, pclk_rate); + msm_dsi_prepare_clocks(); + msm_dsi_clk_enable(); + } + } else { + dsi_host_private->clk_count--; + if (dsi_host_private->clk_count == 0) { + msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask); + msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, 0, 0, 0); + msm_dsi_clk_disable(); + msm_dsi_unprepare_clocks(); + msm_dsi_ahb_ctrl(0); + } + } + mutex_unlock(&ctrl_pdata->mutex); + return 0; +} + +void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) +{ + init_completion(&ctrl->dma_comp); + init_completion(&ctrl->mdp_comp); + init_completion(&ctrl->bta_comp); + init_completion(&ctrl->video_comp); + spin_lock_init(&ctrl->irq_lock); + spin_lock_init(&ctrl->mdp_lock); + mutex_init(&ctrl->mutex); + mutex_init(&ctrl->cmd_mutex); + complete(&ctrl->mdp_comp); + dsi_buf_alloc(&ctrl->tx_buf, SZ_4K); + dsi_buf_alloc(&ctrl->rx_buf, SZ_4K); + dsi_buf_alloc(&ctrl->status_buf, SZ_4K); + ctrl->cmdlist_commit = msm_dsi_cmdlist_commit; + ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode; + + if (ctrl->status_mode == ESD_REG) + ctrl->check_status = msm_dsi_reg_status_check; + else if (ctrl->status_mode == ESD_BTA) + ctrl->check_status = msm_dsi_bta_status_check; + + if (ctrl->status_mode == ESD_MAX) { + pr_err("%s: Using default BTA for ESD check\n", __func__); + ctrl->check_status = msm_dsi_bta_status_check; + } +} + +static void msm_dsi_parse_lane_swap(struct device_node *np, char *dlane_swap) +{ + const char *data; + + *dlane_swap = DSI_LANE_MAP_0123; + data = of_get_property(np, "qcom,lane-map", NULL); + if (data) { + if (!strcmp(data, "lane_map_3012")) + *dlane_swap = DSI_LANE_MAP_3012; + else if (!strcmp(data, "lane_map_2301")) + *dlane_swap = DSI_LANE_MAP_2301; + else if (!strcmp(data, "lane_map_1230")) + *dlane_swap = DSI_LANE_MAP_1230; + else if (!strcmp(data, "lane_map_0321")) + *dlane_swap = DSI_LANE_MAP_0321; + else if (!strcmp(data, "lane_map_1032")) + *dlane_swap = DSI_LANE_MAP_1032; + else if (!strcmp(data, "lane_map_2103")) + *dlane_swap = DSI_LANE_MAP_2103; + else if (!strcmp(data, "lane_map_3210")) + *dlane_swap = DSI_LANE_MAP_3210; + } +} + +static int msm_dsi_probe(struct platform_device *pdev) +{ + struct dsi_interface intf; + char panel_cfg[MDSS_MAX_PANEL_LEN]; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0; + struct device_node *dsi_pan_node = NULL; + bool cmd_cfg_cont_splash = false; + struct resource *mdss_dsi_mres; + int i; + + pr_debug("%s\n", __func__); + + rc = msm_dsi_init(); + if (rc) + return rc; + + if (!pdev->dev.of_node) { + pr_err("%s: Device node is not accessible\n", __func__); + rc = -ENODEV; + goto error_no_mem; + } + pdev->id = 0; + + ctrl_pdata = platform_get_drvdata(pdev); + if (!ctrl_pdata) { + ctrl_pdata = devm_kzalloc(&pdev->dev, + sizeof(struct mdss_dsi_ctrl_pdata), GFP_KERNEL); + if (!ctrl_pdata) { + rc = -ENOMEM; + goto error_no_mem; + } + platform_set_drvdata(pdev, ctrl_pdata); + } + + ctrl_pdata->mdss_util = mdss_get_util_intf(); + if (mdp3_res->mdss_util == NULL) { + pr_err("Failed to get mdss utility functions\n"); + return -ENODEV; + } + + mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mdss_dsi_mres) { + pr_err("%s:%d unable to get the MDSS reg resources", + __func__, __LINE__); + rc = -ENOMEM; + goto error_io_resource; + } else { + dsi_host_private->dsi_reg_size = resource_size(mdss_dsi_mres); + dsi_host_private->dsi_base = ioremap(mdss_dsi_mres->start, + dsi_host_private->dsi_reg_size); + if (!dsi_host_private->dsi_base) { + pr_err("%s:%d unable to remap dsi resources", + __func__, __LINE__); + rc = -ENOMEM; + goto error_io_resource; + } + } + + mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mdss_dsi_mres || mdss_dsi_mres->start == 0) { + pr_err("%s:%d unable to get the MDSS irq resources", + __func__, __LINE__); + rc = -ENODEV; + goto error_irq_resource; + } + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + goto error_platform_pop; + } + + /* DSI panels can be different between controllers */ + rc = dsi_get_panel_cfg(panel_cfg); + if (!rc) + /* dsi panel cfg not present */ + pr_warn("%s:%d:dsi specific cfg not present\n", + __func__, __LINE__); + + /* find panel device node */ + dsi_pan_node = dsi_find_panel_of_node(pdev, panel_cfg); + if (!dsi_pan_node) { + pr_err("%s: can't find panel node %s\n", __func__, + panel_cfg); + goto error_pan_node; + } + + cmd_cfg_cont_splash = mdp3_panel_get_boot_cfg() ? true : false; + + rc = mdss_dsi_panel_init(dsi_pan_node, ctrl_pdata, cmd_cfg_cont_splash); + if (rc) { + pr_err("%s: dsi panel init failed\n", __func__); + goto error_pan_node; + } + + rc = dsi_ctrl_config_init(pdev, ctrl_pdata); + if (rc) { + dev_err(&pdev->dev, "%s: failed to parse mdss dtsi rc=%d\n", + __func__, rc); + goto error_pan_node; + } + + msm_dsi_parse_lane_swap(pdev->dev.of_node, &(ctrl_pdata->dlane_swap)); + + for (i = 0; i < DSI_MAX_PM; i++) { + rc = msm_dsi_io_init(pdev, &(ctrl_pdata->power_data[i])); + if (rc) { + dev_err(&pdev->dev, "%s: failed to init IO for %s\n", + __func__, __mdss_dsi_pm_name(i)); + goto error_io_init; + } + } + + pr_debug("%s: Dsi Ctrl->0 initialized\n", __func__); + + dsi_host_private->dis_dev = pdev->dev; + intf.on = msm_dsi_on; + intf.off = msm_dsi_off; + intf.cont_on = msm_dsi_cont_on; + intf.clk_ctrl = msm_dsi_clk_ctrl; + intf.op_mode_config = msm_dsi_op_mode_config; + intf.index = 0; + intf.private = NULL; + dsi_register_interface(&intf); + + msm_dsi_debug_init(); + + msm_dsi_ctrl_init(ctrl_pdata); + + rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start, + ctrl_pdata); + if (rc) { + dev_err(&pdev->dev, "%s: failed to init irq, rc=%d\n", + __func__, rc); + goto error_irq_init; + } + + rc = dsi_panel_device_register_v2(pdev, ctrl_pdata); + if (rc) { + pr_err("%s: dsi panel dev reg failed\n", __func__); + goto error_device_register; + } + pr_debug("%s success\n", __func__); + return 0; +error_device_register: + kfree(ctrl_pdata->dsi_hw->irq_info); + kfree(ctrl_pdata->dsi_hw); +error_irq_init: + for (i = DSI_MAX_PM - 1; i >= 0; i--) + msm_dsi_io_deinit(pdev, &(ctrl_pdata->power_data[i])); +error_io_init: + dsi_ctrl_config_deinit(pdev, ctrl_pdata); +error_pan_node: + of_node_put(dsi_pan_node); +error_platform_pop: + msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask); +error_irq_resource: + if (dsi_host_private->dsi_base) { + iounmap(dsi_host_private->dsi_base); + dsi_host_private->dsi_base = NULL; + } +error_io_resource: + devm_kfree(&pdev->dev, ctrl_pdata); +error_no_mem: + msm_dsi_deinit(); + + return rc; +} + +static int msm_dsi_remove(struct platform_device *pdev) +{ + int i; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = platform_get_drvdata(pdev); + + if (!ctrl_pdata) { + pr_err("%s: no driver data\n", __func__); + return -ENODEV; + } + + msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask); + for (i = DSI_MAX_PM - 1; i >= 0; i--) + msm_dsi_io_deinit(pdev, &(ctrl_pdata->power_data[i])); + dsi_ctrl_config_deinit(pdev, ctrl_pdata); + iounmap(dsi_host_private->dsi_base); + dsi_host_private->dsi_base = NULL; + msm_dsi_deinit(); + devm_kfree(&pdev->dev, ctrl_pdata); + + return 0; +} + +static const struct of_device_id msm_dsi_v2_dt_match[] = { + {.compatible = "qcom,msm-dsi-v2"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_dsi_v2_dt_match); + +static struct platform_driver msm_dsi_v2_driver = { + .probe = msm_dsi_probe, + .remove = msm_dsi_remove, + .shutdown = NULL, + .driver = { + .name = "qcom,dsi-panel-v2", + .of_match_table = msm_dsi_v2_dt_match, + }, +}; + +static int msm_dsi_v2_register_driver(void) +{ + return platform_driver_register(&msm_dsi_v2_driver); +} + +static int __init msm_dsi_v2_driver_init(void) +{ + int ret; + + ret = msm_dsi_v2_register_driver(); + if (ret) { + pr_err("msm_dsi_v2_register_driver() failed!\n"); + return ret; + } + + return ret; +} +module_init(msm_dsi_v2_driver_init); + +static void __exit msm_dsi_v2_driver_cleanup(void) +{ + platform_driver_unregister(&msm_dsi_v2_driver); +} +module_exit(msm_dsi_v2_driver_cleanup); diff --git a/drivers/video/fbdev/msm/dsi_host_v2.h b/drivers/video/fbdev/msm/dsi_host_v2.h new file mode 100644 index 0000000000000000000000000000000000000000..d61bcf9ea7f209518ae6edb073b13c5079b66a99 --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_host_v2.h @@ -0,0 +1,178 @@ +/* Copyright (c) 2012-2014, 2018, 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 DSI_HOST_V2_H +#define DSI_HOST_V2_H + +#include + +#define DSI_INTR_ERROR_MASK BIT(25) +#define DSI_INTR_ERROR BIT(24) +#define DSI_INTR_BTA_DONE_MASK BIT(21) +#define DSI_INTR_BTA_DONE BIT(20) +#define DSI_INTR_VIDEO_DONE_MASK BIT(17) +#define DSI_INTR_VIDEO_DONE BIT(16) +#define DSI_INTR_CMD_MDP_DONE_MASK BIT(9) +#define DSI_INTR_CMD_MDP_DONE BIT(8) +#define DSI_INTR_CMD_DMA_DONE_MASK BIT(1) +#define DSI_INTR_CMD_DMA_DONE BIT(0) +#define DSI_INTR_ALL_MASK 0x2220202 + +#define DSI_BTA_TERM BIT(1) + +#define DSI_CTRL 0x0000 +#define DSI_STATUS 0x0004 +#define DSI_FIFO_STATUS 0x0008 +#define DSI_VIDEO_MODE_CTRL 0x000C +#define DSI_VIDEO_MODE_DATA_CTRL 0x001C +#define DSI_VIDEO_MODE_ACTIVE_H 0x0020 +#define DSI_VIDEO_MODE_ACTIVE_V 0x0024 +#define DSI_VIDEO_MODE_TOTAL 0x0028 +#define DSI_VIDEO_MODE_HSYNC 0x002C +#define DSI_VIDEO_MODE_VSYNC 0x0030 +#define DSI_VIDEO_MODE_VSYNC_VPOS 0x0034 +#define DSI_COMMAND_MODE_DMA_CTRL 0x0038 +#define DSI_COMMAND_MODE_MDP_CTRL 0x003C +#define DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL 0x0040 +#define DSI_DMA_CMD_OFFSET 0x0044 +#define DSI_DMA_CMD_LENGTH 0x0048 +#define DSI_DMA_FIFO_CTRL 0x004C +#define DSI_COMMAND_MODE_MDP_STREAM0_CTRL 0x0054 +#define DSI_COMMAND_MODE_MDP_STREAM0_TOTAL 0x0058 +#define DSI_COMMAND_MODE_MDP_STREAM1_CTRL 0x005C +#define DSI_COMMAND_MODE_MDP_STREAM1_TOTAL 0x0060 +#define DSI_ACK_ERR_STATUS 0x0064 +#define DSI_RDBK_DATA0 0x0068 +#define DSI_RDBK_DATA1 0x006C +#define DSI_RDBK_DATA2 0x0070 +#define DSI_RDBK_DATA3 0x0074 +#define DSI_RDBK_DATATYPE0 0x0078 +#define DSI_RDBK_DATATYPE1 0x007C +#define DSI_TRIG_CTRL 0x0080 +#define DSI_EXT_MUX 0x0084 +#define DSI_EXT_TE_PULSE_DETECT_CTRL 0x0088 +#define DSI_CMD_MODE_DMA_SW_TRIGGER 0x008C +#define DSI_CMD_MODE_MDP_SW_TRIGGER 0x0090 +#define DSI_CMD_MODE_BTA_SW_TRIGGER 0x0094 +#define DSI_RESET_SW_TRIGGER 0x0098 +#define DSI_LANE_CTRL 0x00A8 +#define DSI_LANE_SWAP_CTRL 0x00AC +#define DSI_DLN0_PHY_ERR 0x00B0 +#define DSI_TIMEOUT_STATUS 0x00BC +#define DSI_CLKOUT_TIMING_CTRL 0x00C0 +#define DSI_EOT_PACKET 0x00C4 +#define DSI_EOT_PACKET_CTRL 0x00C8 +#define DSI_ERR_INT_MASK0 0x0108 +#define DSI_INT_CTRL 0x010c +#define DSI_SOFT_RESET 0x0114 +#define DSI_CLK_CTRL 0x0118 +#define DSI_CLK_STATUS 0x011C +#define DSI_PHY_SW_RESET 0x0128 +#define DSI_COMMAND_MODE_MDP_IDLE_CTRL 0x0190 +#define DSI_VERSION 0x01F0 + +#define DSI_DSIPHY_PLL_CTRL_0 0x0200 +#define DSI_DSIPHY_PLL_CTRL_1 0x0204 +#define DSI_DSIPHY_PLL_CTRL_2 0x0208 +#define DSI_DSIPHY_PLL_CTRL_3 0x020C +#define DSI_DSIPHY_PLL_CTRL_4 0x0210 +#define DSI_DSIPHY_PLL_CTRL_5 0x0214 +#define DSI_DSIPHY_PLL_CTRL_6 0x0218 +#define DSI_DSIPHY_PLL_CTRL_7 0x021C +#define DSI_DSIPHY_PLL_CTRL_8 0x0220 +#define DSI_DSIPHY_PLL_CTRL_9 0x0224 +#define DSI_DSIPHY_PLL_CTRL_10 0x0228 +#define DSI_DSIPHY_PLL_CTRL_11 0x022C +#define DSI_DSIPHY_PLL_CTRL_12 0x0230 +#define DSI_DSIPHY_PLL_CTRL_13 0x0234 +#define DSI_DSIPHY_PLL_CTRL_14 0x0238 +#define DSI_DSIPHY_PLL_CTRL_15 0x023C +#define DSI_DSIPHY_PLL_CTRL_16 0x0240 +#define DSI_DSIPHY_PLL_CTRL_17 0x0244 +#define DSI_DSIPHY_PLL_CTRL_18 0x0248 +#define DSI_DSIPHY_PLL_CTRL_19 0x024C +#define DSI_DSIPHY_ANA_CTRL0 0x0260 +#define DSI_DSIPHY_ANA_CTRL1 0x0264 +#define DSI_DSIPHY_ANA_CTRL2 0x0268 +#define DSI_DSIPHY_ANA_CTRL3 0x026C +#define DSI_DSIPHY_ANA_CTRL4 0x0270 +#define DSI_DSIPHY_ANA_CTRL5 0x0274 +#define DSI_DSIPHY_ANA_CTRL6 0x0278 +#define DSI_DSIPHY_ANA_CTRL7 0x027C +#define DSI_DSIPHY_PLL_RDY 0x0280 +#define DSI_DSIPHY_PLL_ANA_STATUS0 0x0294 +#define DSI_DSIPHY_PLL_ANA_STATUS1 0x0298 +#define DSI_DSIPHY_PLL_ANA_STATUS2 0x029C +#define DSI_DSIPHY_LN0_CFG0 0x0300 +#define DSI_DSIPHY_LN0_CFG1 0x0304 +#define DSI_DSIPHY_LN0_CFG2 0x0308 +#define DSI_DSIPHY_LN1_CFG0 0x0340 +#define DSI_DSIPHY_LN1_CFG1 0x0344 +#define DSI_DSIPHY_LN1_CFG2 0x0348 +#define DSI_DSIPHY_LN2_CFG0 0x0380 +#define DSI_DSIPHY_LN2_CFG1 0x0384 +#define DSI_DSIPHY_LN2_CFG2 0x0388 +#define DSI_DSIPHY_LN3_CFG0 0x03C0 +#define DSI_DSIPHY_LN3_CFG1 0x03C4 +#define DSI_DSIPHY_LN3_CFG2 0x03C8 +#define DSI_DSIPHY_LNCK_CFG0 0x0400 +#define DSI_DSIPHY_LNCK_CFG1 0x0404 +#define DSI_DSIPHY_LNCK_CFG2 0x0408 +#define DSI_DSIPHY_TIMING_CTRL_0 0x0440 +#define DSI_DSIPHY_TIMING_CTRL_1 0x0444 +#define DSI_DSIPHY_TIMING_CTRL_2 0x0448 +#define DSI_DSIPHY_TIMING_CTRL_3 0x044C +#define DSI_DSIPHY_TIMING_CTRL_4 0x0450 +#define DSI_DSIPHY_TIMING_CTRL_5 0x0454 +#define DSI_DSIPHY_TIMING_CTRL_6 0x0458 +#define DSI_DSIPHY_TIMING_CTRL_7 0x045C +#define DSI_DSIPHY_TIMING_CTRL_8 0x0460 +#define DSI_DSIPHY_TIMING_CTRL_9 0x0464 +#define DSI_DSIPHY_TIMING_CTRL_10 0x0468 +#define DSI_DSIPHY_TIMING_CTRL_11 0x046C +#define DSI_DSIPHY_CTRL_0 0x0470 +#define DSI_DSIPHY_CTRL_1 0x0474 +#define DSI_DSIPHY_CTRL_2 0x0478 +#define DSI_DSIPHY_CTRL_3 0x047C +#define DSI_DSIPHY_STRENGTH_CTRL_0 0x0480 +#define DSI_DSIPHY_STRENGTH_CTRL_1 0x0484 +#define DSI_DSIPHY_STRENGTH_CTRL_2 0x0488 +#define DSI_DSIPHY_LDO_CNTRL 0x04B0 +#define DSI_DSIPHY_REGULATOR_CTRL_0 0x0500 +#define DSI_DSIPHY_REGULATOR_CTRL_1 0x0504 +#define DSI_DSIPHY_REGULATOR_CTRL_2 0x0508 +#define DSI_DSIPHY_REGULATOR_CTRL_3 0x050C +#define DSI_DSIPHY_REGULATOR_CTRL_4 0x0510 +#define DSI_DSIPHY_REGULATOR_TEST 0x0514 +#define DSI_DSIPHY_REGULATOR_CAL_PWR_CFG 0x0518 +#define DSI_DSIPHY_CAL_HW_TRIGGER 0x0528 +#define DSI_DSIPHY_CAL_SW_CFG0 0x052C +#define DSI_DSIPHY_CAL_SW_CFG1 0x0530 +#define DSI_DSIPHY_CAL_SW_CFG2 0x0534 +#define DSI_DSIPHY_CAL_HW_CFG0 0x0538 +#define DSI_DSIPHY_CAL_HW_CFG1 0x053C +#define DSI_DSIPHY_CAL_HW_CFG2 0x0540 +#define DSI_DSIPHY_CAL_HW_CFG3 0x0544 +#define DSI_DSIPHY_CAL_HW_CFG4 0x0548 +#define DSI_DSIPHY_REGULATOR_CAL_STATUS0 0x0550 +#define DSI_DSIPHY_BIST_CTRL0 0x048C +#define DSI_DSIPHY_BIST_CTRL1 0x0490 +#define DSI_DSIPHY_BIST_CTRL2 0x0494 +#define DSI_DSIPHY_BIST_CTRL3 0x0498 +#define DSI_DSIPHY_BIST_CTRL4 0x049C +#define DSI_DSIPHY_BIST_CTRL5 0x04A0 + +#define DSI_EN BIT(0) +#define DSI_VIDEO_MODE_EN BIT(1) +#define DSI_CMD_MODE_EN BIT(2) + +#endif /* DSI_HOST_V2_H */ diff --git a/drivers/video/fbdev/msm/dsi_io_v2.c b/drivers/video/fbdev/msm/dsi_io_v2.c new file mode 100644 index 0000000000000000000000000000000000000000..71c1d1d7c2a7dfab453acbeec3c4463a55e96aaa --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_io_v2.c @@ -0,0 +1,389 @@ +/* Copyright (c) 2013, 2018, 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 +#include +#include +#include +#include +#include +#include + + +#include "dsi_v2.h" +#include "dsi_io_v2.h" +#include "dsi_host_v2.h" + +struct msm_dsi_io_private { + struct clk *dsi_byte_clk; + struct clk *dsi_esc_clk; + struct clk *dsi_pixel_clk; + struct clk *dsi_ahb_clk; + struct clk *dsi_clk; + int msm_dsi_clk_on; + int msm_dsi_ahb_clk_on; +}; + +static struct msm_dsi_io_private *dsi_io_private; + +#define DSI_VDDA_VOLTAGE 1200000 + +void msm_dsi_ahb_ctrl(int enable) +{ + if (enable) { + dsi_io_private->msm_dsi_ahb_clk_on++; + if (dsi_io_private->msm_dsi_ahb_clk_on == 1) + clk_enable(dsi_io_private->dsi_ahb_clk); + } else { + dsi_io_private->msm_dsi_ahb_clk_on--; + if (dsi_io_private->msm_dsi_ahb_clk_on == 0) + clk_disable(dsi_io_private->dsi_ahb_clk); + } +} + +int msm_dsi_io_init(struct platform_device *pdev, struct mdss_module_power *mp) +{ + int rc; + + if (!dsi_io_private) { + dsi_io_private = kzalloc(sizeof(struct msm_dsi_io_private), + GFP_KERNEL); + if (!dsi_io_private) + return -ENOMEM; + } + + rc = msm_dsi_clk_init(pdev); + if (rc) { + pr_err("fail to initialize DSI clock\n"); + return rc; + } + + rc = msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, + mp->num_vreg, 1); + if (rc) { + pr_err("fail to initialize DSI regulator\n"); + return rc; + } + + return 0; +} + +void msm_dsi_io_deinit(struct platform_device *pdev, + struct mdss_module_power *mp) +{ + if (dsi_io_private) { + msm_dsi_clk_deinit(); + msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, + mp->num_vreg, 0); + kfree(dsi_io_private); + dsi_io_private = NULL; + } +} + +int msm_dsi_clk_init(struct platform_device *dev) +{ + int rc = 0; + + dsi_io_private->dsi_clk = clk_get(&dev->dev, "dsi_clk"); + if (IS_ERR(dsi_io_private->dsi_clk)) { + pr_err("can't find dsi core_clk\n"); + rc = PTR_ERR(dsi_io_private->dsi_clk); + dsi_io_private->dsi_clk = NULL; + return rc; + } + dsi_io_private->dsi_byte_clk = clk_get(&dev->dev, "byte_clk"); + if (IS_ERR(dsi_io_private->dsi_byte_clk)) { + pr_err("can't find dsi byte_clk\n"); + rc = PTR_ERR(dsi_io_private->dsi_byte_clk); + dsi_io_private->dsi_byte_clk = NULL; + return rc; + } + + dsi_io_private->dsi_esc_clk = clk_get(&dev->dev, "esc_clk"); + if (IS_ERR(dsi_io_private->dsi_esc_clk)) { + pr_err("can't find dsi esc_clk\n"); + rc = PTR_ERR(dsi_io_private->dsi_esc_clk); + dsi_io_private->dsi_esc_clk = NULL; + return rc; + } + + dsi_io_private->dsi_pixel_clk = clk_get(&dev->dev, "pixel_clk"); + if (IS_ERR(dsi_io_private->dsi_pixel_clk)) { + pr_err("can't find dsi pixel\n"); + rc = PTR_ERR(dsi_io_private->dsi_pixel_clk); + dsi_io_private->dsi_pixel_clk = NULL; + return rc; + } + + dsi_io_private->dsi_ahb_clk = clk_get(&dev->dev, "iface_clk"); + if (IS_ERR(dsi_io_private->dsi_ahb_clk)) { + pr_err("can't find dsi iface_clk\n"); + rc = PTR_ERR(dsi_io_private->dsi_ahb_clk); + dsi_io_private->dsi_ahb_clk = NULL; + return rc; + } + clk_prepare(dsi_io_private->dsi_ahb_clk); + + return 0; +} + +void msm_dsi_clk_deinit(void) +{ + if (dsi_io_private->dsi_clk) { + clk_put(dsi_io_private->dsi_clk); + dsi_io_private->dsi_clk = NULL; + } + if (dsi_io_private->dsi_byte_clk) { + clk_put(dsi_io_private->dsi_byte_clk); + dsi_io_private->dsi_byte_clk = NULL; + } + if (dsi_io_private->dsi_esc_clk) { + clk_put(dsi_io_private->dsi_esc_clk); + dsi_io_private->dsi_esc_clk = NULL; + } + if (dsi_io_private->dsi_pixel_clk) { + clk_put(dsi_io_private->dsi_pixel_clk); + dsi_io_private->dsi_pixel_clk = NULL; + } + if (dsi_io_private->dsi_ahb_clk) { + clk_unprepare(dsi_io_private->dsi_ahb_clk); + clk_put(dsi_io_private->dsi_ahb_clk); + dsi_io_private->dsi_ahb_clk = NULL; + } +} + +int msm_dsi_prepare_clocks(void) +{ + clk_prepare(dsi_io_private->dsi_clk); + clk_prepare(dsi_io_private->dsi_byte_clk); + clk_prepare(dsi_io_private->dsi_esc_clk); + clk_prepare(dsi_io_private->dsi_pixel_clk); + return 0; +} + +int msm_dsi_unprepare_clocks(void) +{ + clk_unprepare(dsi_io_private->dsi_clk); + clk_unprepare(dsi_io_private->dsi_esc_clk); + clk_unprepare(dsi_io_private->dsi_byte_clk); + clk_unprepare(dsi_io_private->dsi_pixel_clk); + return 0; +} + +int msm_dsi_clk_set_rate(unsigned long esc_rate, + unsigned long dsi_rate, + unsigned long byte_rate, + unsigned long pixel_rate) +{ + int rc; + + rc = clk_set_rate(dsi_io_private->dsi_clk, dsi_rate); + if (rc) { + pr_err("dsi_esc_clk - clk_set_rate failed =%d\n", rc); + return rc; + } + + rc = clk_set_rate(dsi_io_private->dsi_esc_clk, esc_rate); + if (rc) { + pr_err("dsi_esc_clk - clk_set_rate failed =%d\n", rc); + return rc; + } + + rc = clk_set_rate(dsi_io_private->dsi_byte_clk, byte_rate); + if (rc) { + pr_err("dsi_byte_clk - clk_set_rate faile = %dd\n", rc); + return rc; + } + + rc = clk_set_rate(dsi_io_private->dsi_pixel_clk, pixel_rate); + if (rc) { + pr_err("dsi_pixel_clk - clk_set_rate failed = %d\n", rc); + return rc; + } + return 0; +} + +int msm_dsi_clk_enable(void) +{ + if (dsi_io_private->msm_dsi_clk_on) { + pr_debug("dsi_clks on already\n"); + return 0; + } + + clk_enable(dsi_io_private->dsi_clk); + clk_enable(dsi_io_private->dsi_esc_clk); + clk_enable(dsi_io_private->dsi_byte_clk); + clk_enable(dsi_io_private->dsi_pixel_clk); + + dsi_io_private->msm_dsi_clk_on = 1; + return 0; +} + +int msm_dsi_clk_disable(void) +{ + if (dsi_io_private->msm_dsi_clk_on == 0) { + pr_debug("mdss_dsi_clks already OFF\n"); + return 0; + } + + clk_disable(dsi_io_private->dsi_clk); + clk_disable(dsi_io_private->dsi_byte_clk); + clk_disable(dsi_io_private->dsi_esc_clk); + clk_disable(dsi_io_private->dsi_pixel_clk); + + dsi_io_private->msm_dsi_clk_on = 0; + return 0; +} + +static void msm_dsi_phy_strength_init(unsigned char *ctrl_base, + struct mdss_dsi_phy_ctrl *pd) +{ + MIPI_OUTP(ctrl_base + DSI_DSIPHY_STRENGTH_CTRL_0, pd->strength[0]); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_STRENGTH_CTRL_2, pd->strength[1]); +} + +static void msm_dsi_phy_ctrl_init(unsigned char *ctrl_base, + struct mdss_panel_data *pdata) +{ + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x5f); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_3, 0x10); +} + +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, 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]); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_3, pd->regulator[3]); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_4, pd->regulator[4]); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CAL_PWR_CFG, + pd->regulator[5]); + +} + +static int msm_dsi_phy_calibration(unsigned char *ctrl_base) +{ + int i = 0, term_cnt = 5000, ret = 0, cal_busy; + + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_SW_CFG2, 0x0); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG1, 0x5a); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG3, 0x10); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG4, 0x01); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_CFG0, 0x01); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_TRIGGER, 0x01); + usleep_range(5000, 5100); /*per DSI controller spec*/ + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CAL_HW_TRIGGER, 0x00); + + cal_busy = MIPI_INP(ctrl_base + DSI_DSIPHY_REGULATOR_CAL_STATUS0); + while (cal_busy & 0x10) { + i++; + if (i > term_cnt) { + ret = -EINVAL; + pr_err("%s error\n", __func__); + break; + } + cal_busy = MIPI_INP(ctrl_base + + DSI_DSIPHY_REGULATOR_CAL_STATUS0); + } + + return ret; +} + +static void msm_dsi_phy_lane_init(unsigned char *ctrl_base, + struct mdss_dsi_phy_ctrl *pd) +{ + int ln, index; + + /*CFG0, CFG1, CFG2, TEST_DATAPATH, TEST_STR0, TEST_STR1*/ + for (ln = 0; ln < 5; ln++) { + unsigned char *off = ctrl_base + 0x0300 + (ln * 0x40); + + index = ln * 6; + + MIPI_OUTP(off, pd->lanecfg[index]); + MIPI_OUTP(off + 4, pd->lanecfg[index + 1]); + MIPI_OUTP(off + 8, pd->lanecfg[index + 2]); + MIPI_OUTP(off + 12, pd->lanecfg[index + 3]); + MIPI_OUTP(off + 20, pd->lanecfg[index + 4]); + MIPI_OUTP(off + 24, pd->lanecfg[index + 5]); + } + wmb(); /* ensure write is finished before progressing */ +} + +static void msm_dsi_phy_timing_init(unsigned char *ctrl_base, + struct mdss_dsi_phy_ctrl *pd) +{ + int i, off = DSI_DSIPHY_TIMING_CTRL_0; + + for (i = 0; i < 12; i++) { + MIPI_OUTP(ctrl_base + off, pd->timing[i]); + off += 4; + } + wmb(); /* ensure write is finished before progressing */ +} + +static void msm_dsi_phy_bist_init(unsigned char *ctrl_base, + struct mdss_dsi_phy_ctrl *pd) +{ + MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL4, pd->bistctrl[4]); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL1, pd->bistctrl[1]); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL0, pd->bistctrl[0]); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_BIST_CTRL4, 0); + wmb(); /* ensure write is finished before progressing */ +} + +int msm_dsi_phy_init(unsigned char *ctrl_base, + struct mdss_panel_data *pdata) +{ + struct mdss_dsi_phy_ctrl *pd; + + pd = &(pdata->panel_info.mipi.dsi_phy_db); + + msm_dsi_phy_strength_init(ctrl_base, pd); + + msm_dsi_phy_ctrl_init(ctrl_base, pdata); + + msm_dsi_phy_regulator_init(ctrl_base, pd); + + msm_dsi_phy_calibration(ctrl_base); + + msm_dsi_phy_lane_init(ctrl_base, pd); + + msm_dsi_phy_timing_init(ctrl_base, pd); + + msm_dsi_phy_bist_init(ctrl_base, pd); + + return 0; +} + +void msm_dsi_phy_sw_reset(unsigned char *ctrl_base) +{ + /* start phy sw reset */ + MIPI_OUTP(ctrl_base + DSI_PHY_SW_RESET, 0x0001); + udelay(1000); /*per DSI controller spec*/ + wmb(); /* ensure write is finished before progressing */ + /* end phy sw reset */ + MIPI_OUTP(ctrl_base + DSI_PHY_SW_RESET, 0x0000); + udelay(100); /*per DSI controller spec*/ + wmb(); /* ensure write is finished before progressing */ +} + +void msm_dsi_phy_off(unsigned char *ctrl_base) +{ + MIPI_OUTP(ctrl_base + DSI_DSIPHY_PLL_CTRL_5, 0x05f); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_REGULATOR_CTRL_0, 0x02); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_0, 0x00); + MIPI_OUTP(ctrl_base + DSI_DSIPHY_CTRL_1, 0x7f); + MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0); +} diff --git a/drivers/video/fbdev/msm/dsi_io_v2.h b/drivers/video/fbdev/msm/dsi_io_v2.h new file mode 100644 index 0000000000000000000000000000000000000000..d0227ecc7b810a06c147daf6d578e441d05c7389 --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_io_v2.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2013, 2018, 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 DSI_IO_V2_H +#define DSI_IO_V2_H + +#include "mdss_panel.h" + +void msm_dsi_ahb_ctrl(int enable); + +int msm_dsi_io_init(struct platform_device *dev, + struct mdss_module_power *mp); + +void msm_dsi_io_deinit(struct platform_device *dev, + struct mdss_module_power *mp); + +int msm_dsi_clk_init(struct platform_device *dev); + +void msm_dsi_clk_deinit(void); + +int msm_dsi_prepare_clocks(void); + +int msm_dsi_unprepare_clocks(void); + +int msm_dsi_clk_set_rate(unsigned long esc_rate, + unsigned long dsi_rate, + unsigned long byte_rate, + unsigned long pixel_rate); + +int msm_dsi_clk_enable(void); + +int msm_dsi_clk_disable(void); + +int msm_dsi_phy_init(unsigned char *ctrl_base, + struct mdss_panel_data *pdata); + +void msm_dsi_phy_sw_reset(unsigned char *ctrl_base); + +void msm_dsi_phy_off(unsigned char *ctrl_base); +#endif /* DSI_IO_V2_H */ diff --git a/drivers/video/fbdev/msm/dsi_status_6g.c b/drivers/video/fbdev/msm/dsi_status_6g.c new file mode 100644 index 0000000000000000000000000000000000000000..8a329026602cc82586ad8787d7f730cd69e31f04 --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_status_6g.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2013-2018, 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 +#include +#include +#include +#include + +#include "mdss_dsi.h" +#include "mdss_mdp.h" +#include "mdss_debug.h" + +/* + * mdss_check_te_status() - Check the status of panel for TE based ESD. + * @ctrl_pdata : dsi controller data + * @pstatus_data : dsi status data + * @interval : duration in milliseconds for panel TE wait + * + * This function is called when the TE signal from the panel doesn't arrive + * after 'interval' milliseconds. If the TE IRQ is not ready, the workqueue + * gets re-scheduled. Otherwise, report the panel to be dead due to ESD attack. + */ +static bool mdss_check_te_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata, + struct dsi_status_data *pstatus_data, uint32_t interval) +{ + bool ret; + + atomic_set(&ctrl_pdata->te_irq_ready, 0); + reinit_completion(&ctrl_pdata->te_irq_comp); + enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + /* Define TE interrupt timeout value as 3x(1/fps) */ + ret = wait_for_completion_timeout(&ctrl_pdata->te_irq_comp, + msecs_to_jiffies(interval)); + disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + pr_debug("%s: Panel TE check done with ret = %d\n", __func__, ret); + return ret; +} + +/* + * mdss_check_dsi_ctrl_status() - Check MDP5 DSI controller status periodically. + * @work : dsi controller status data + * @interval : duration in milliseconds to schedule work queue + * + * This function calls check_status API on DSI controller to send the BTA + * command. If DSI controller fails to acknowledge the BTA command, it sends + * the PANEL_ALIVE=0 status to HAL layer. + */ +void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval) +{ + struct dsi_status_data *pstatus_data = NULL; + struct mdss_panel_data *pdata = NULL; + struct mipi_panel_info *mipi = NULL; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_overlay_private *mdp5_data = NULL; + struct mdss_mdp_ctl *ctl = NULL; + int ret = 0; + + pstatus_data = container_of(to_delayed_work(work), + struct dsi_status_data, check_status); + if (!pstatus_data || !(pstatus_data->mfd)) { + pr_err("%s: mfd not available\n", __func__); + return; + } + + pdata = dev_get_platdata(&pstatus_data->mfd->pdev->dev); + if (!pdata) { + pr_err("%s: Panel data not available\n", __func__); + return; + } + mipi = &pdata->panel_info.mipi; + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (!ctrl_pdata || (!ctrl_pdata->check_status && + (ctrl_pdata->status_mode != ESD_TE))) { + pr_err("%s: DSI ctrl or status_check callback not available\n", + __func__); + return; + } + + if (!pdata->panel_info.esd_rdy) { + pr_debug("%s: unblank not complete, reschedule check status\n", + __func__); + schedule_delayed_work(&pstatus_data->check_status, + msecs_to_jiffies(interval)); + return; + } + + mdp5_data = mfd_to_mdp5_data(pstatus_data->mfd); + ctl = mfd_to_ctl(pstatus_data->mfd); + + if (!ctl) { + pr_err("%s: Display is off\n", __func__); + return; + } + + if (ctrl_pdata->status_mode == ESD_TE) { + uint32_t fps = mdss_panel_get_framerate(&pdata->panel_info, + FPS_RESOLUTION_HZ); + uint32_t timeout = ((1000 / fps) + 1) * + MDSS_STATUS_TE_WAIT_MAX; + + if (mdss_check_te_status(ctrl_pdata, pstatus_data, timeout)) + goto sim; + else + goto status_dead; + } + + /* + * TODO: Because mdss_dsi_cmd_mdp_busy has made sure DMA to + * be idle in mdss_dsi_cmdlist_commit, it is not necessary + * to acquire ov_lock in case of video mode. Removing this + * lock to fix issues so that ESD thread would not block other + * overlay operations. Need refine this lock for command mode + * + * If Burst mode is enabled then we dont have to acquire ov_lock as + * command and data arbitration is possible in h/w + */ + + if ((mipi->mode == DSI_CMD_MODE) && !ctrl_pdata->burst_mode_enabled) + mutex_lock(&mdp5_data->ov_lock); + mutex_lock(&ctl->offlock); + + if (mdss_panel_is_power_off(pstatus_data->mfd->panel_power_state) || + pstatus_data->mfd->shutdown_pending) { + mutex_unlock(&ctl->offlock); + if ((mipi->mode == DSI_CMD_MODE) && + !ctrl_pdata->burst_mode_enabled) + mutex_unlock(&mdp5_data->ov_lock); + pr_err("%s: DSI turning off, avoiding panel status check\n", + __func__); + return; + } + + /* + * For the command mode panels, we return pan display + * IOCTL on vsync interrupt. So, after vsync interrupt comes + * and when DMA_P is in progress, if the panel stops responding + * and if we trigger BTA before DMA_P finishes, then the DSI + * FIFO will not be cleared since the DSI data bus control + * doesn't come back to the host after BTA. This may cause the + * display reset not to be proper. Hence, wait for DMA_P done + * for command mode panels before triggering BTA. + */ + if (ctl->ops.wait_pingpong && !ctrl_pdata->burst_mode_enabled) + ctl->ops.wait_pingpong(ctl, NULL); + + pr_debug("%s: DSI ctrl wait for ping pong done\n", __func__); + MDSS_XLOG(mipi->mode); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + ret = ctrl_pdata->check_status(ctrl_pdata); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + + mutex_unlock(&ctl->offlock); + if ((mipi->mode == DSI_CMD_MODE) && !ctrl_pdata->burst_mode_enabled) + mutex_unlock(&mdp5_data->ov_lock); + + if (pstatus_data->mfd->panel_power_state == MDSS_PANEL_POWER_ON) { + if (ret > 0) + schedule_delayed_work(&pstatus_data->check_status, + msecs_to_jiffies(interval)); + else + goto status_dead; + } +sim: + if (pdata->panel_info.panel_force_dead) { + pr_debug("force_dead=%d\n", pdata->panel_info.panel_force_dead); + pdata->panel_info.panel_force_dead--; + if (!pdata->panel_info.panel_force_dead) + goto status_dead; + } + + return; + +status_dead: + mdss_fb_report_panel_dead(pstatus_data->mfd); +} diff --git a/drivers/video/fbdev/msm/dsi_status_v2.c b/drivers/video/fbdev/msm/dsi_status_v2.c new file mode 100644 index 0000000000000000000000000000000000000000..35b09849f3c88eabcecbe803ae0db1d3dd8f5325 --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_status_v2.c @@ -0,0 +1,167 @@ +/* Copyright (c) 2013-2015, 2017-2018, 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 +#include +#include +#include +#include +#include + +#include "mdss_dsi.h" +#include "mdp3_ctrl.h" + +/* + * mdp3_check_te_status() - Check the status of panel for TE based ESD. + * @ctrl_pdata : dsi controller data + * @pstatus_data : dsi status data + * @interval : duration in milliseconds for panel TE wait + * + * This function waits for TE signal from the panel for a maximum + * duration of 3 vsyncs. If timeout occurs, report the panel to be + * dead due to ESD attack. + * NOTE: The TE IRQ handling is linked to the ESD thread scheduling, + * i.e. rate of TE IRQs firing is bound by the ESD interval. + */ +static int mdp3_check_te_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata, + struct dsi_status_data *pstatus_data, uint32_t interval) +{ + int ret; + + pr_debug("%s: Checking panel TE status\n", __func__); + + atomic_set(&ctrl_pdata->te_irq_ready, 0); + reinit_completion(&ctrl_pdata->te_irq_comp); + enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + + ret = wait_for_completion_timeout(&ctrl_pdata->te_irq_comp, + msecs_to_jiffies(interval)); + + disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + pr_debug("%s: Panel TE check done with ret = %d\n", __func__, ret); + + return ret; +} + +/* + * mdp3_check_dsi_ctrl_status() - Check MDP3 DSI controller status periodically. + * @work : dsi controller status data + * @interval : duration in milliseconds to schedule work queue + * + * This function calls check_status API on DSI controller to send the BTA + * command. If DSI controller fails to acknowledge the BTA command, it sends + * the PANEL_ALIVE=0 status to HAL layer. + */ +void mdp3_check_dsi_ctrl_status(struct work_struct *work, + uint32_t interval) +{ + struct dsi_status_data *pdsi_status = NULL; + struct mdss_panel_data *pdata = NULL; + struct mipi_panel_info *mipi = NULL; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdp3_session_data *mdp3_session = NULL; + int ret = 0; + + pdsi_status = container_of(to_delayed_work(work), + struct dsi_status_data, check_status); + + if (!pdsi_status || !(pdsi_status->mfd)) { + pr_err("%s: mfd not available\n", __func__); + return; + } + + pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev); + if (!pdata) { + pr_err("%s: Panel data not available\n", __func__); + return; + } + + mipi = &pdata->panel_info.mipi; + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (!ctrl_pdata || (!ctrl_pdata->check_status && + (ctrl_pdata->status_mode != ESD_TE))) { + pr_err("%s: DSI ctrl or status_check callback not available\n", + __func__); + return; + } + + if (!pdata->panel_info.esd_rdy) { + pr_err("%s: unblank not complete, reschedule check status\n", + __func__); + schedule_delayed_work(&pdsi_status->check_status, + msecs_to_jiffies(interval)); + return; + } + + mdp3_session = pdsi_status->mfd->mdp.private1; + if (!mdp3_session) { + pr_err("%s: Display is off\n", __func__); + return; + } + + if (mdp3_session->in_splash_screen) { + schedule_delayed_work(&pdsi_status->check_status, + msecs_to_jiffies(interval)); + pr_debug("%s: cont splash is on\n", __func__); + return; + } + + if (mipi->mode == DSI_CMD_MODE && + mipi->hw_vsync_mode && + mdss_dsi_is_te_based_esd(ctrl_pdata)) { + uint32_t fps = mdss_panel_get_framerate(&pdata->panel_info, + FPS_RESOLUTION_HZ); + uint32_t timeout = ((1000 / fps) + 1) * + MDSS_STATUS_TE_WAIT_MAX; + + if (mdp3_check_te_status(ctrl_pdata, pdsi_status, timeout) > 0) + goto sim; + goto status_dead; + } + + mutex_lock(&mdp3_session->lock); + if (!mdp3_session->status) { + pr_debug("%s: display off already\n", __func__); + mutex_unlock(&mdp3_session->lock); + return; + } + + if (mdp3_session->wait_for_dma_done) + ret = mdp3_session->wait_for_dma_done(mdp3_session); + mutex_unlock(&mdp3_session->lock); + + if (!ret) + ret = ctrl_pdata->check_status(ctrl_pdata); + else + pr_err("%s: wait_for_dma_done error\n", __func__); + + if (mdss_fb_is_power_on_interactive(pdsi_status->mfd)) { + if (ret > 0) + schedule_delayed_work(&pdsi_status->check_status, + msecs_to_jiffies(interval)); + else + goto status_dead; + } +sim: + if (pdata->panel_info.panel_force_dead) { + pr_debug("force_dead=%d\n", pdata->panel_info.panel_force_dead); + pdata->panel_info.panel_force_dead--; + if (!pdata->panel_info.panel_force_dead) + goto status_dead; + } + return; + +status_dead: + mdss_fb_report_panel_dead(pdsi_status->mfd); +} + diff --git a/drivers/video/fbdev/msm/dsi_v2.c b/drivers/video/fbdev/msm/dsi_v2.c new file mode 100644 index 0000000000000000000000000000000000000000..bfd29416cd1b06bd1877defd77f5e0adae1813d8 --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_v2.c @@ -0,0 +1,616 @@ +/* Copyright (c) 2012-2015, 2017-2018, 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 +#include +#include +#include +#include +#include +#include + +#include "dsi_v2.h" + +static struct dsi_interface dsi_intf; + +static int dsi_off(struct mdss_panel_data *pdata) +{ + int rc = 0; + + pr_debug("turn off dsi controller\n"); + if (dsi_intf.off) + rc = dsi_intf.off(pdata); + + if (rc) { + pr_err("mdss_dsi_off DSI failed %d\n", rc); + return rc; + } + return rc; +} + +static int dsi_on(struct mdss_panel_data *pdata) +{ + int rc = 0; + + pr_debug("%s DSI controller on\n", __func__); + if (dsi_intf.on) + rc = dsi_intf.on(pdata); + + if (rc) { + pr_err("mdss_dsi_on DSI failed %d\n", rc); + return rc; + } + return rc; +} + +static int dsi_update_pconfig(struct mdss_panel_data *pdata, + int mode) +{ + int ret = 0; + struct mdss_panel_info *pinfo = &pdata->panel_info; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + if (!pdata) + return -ENODEV; + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (mode == DSI_CMD_MODE) { + pinfo->mipi.mode = DSI_CMD_MODE; + pinfo->type = MIPI_CMD_PANEL; + pinfo->mipi.vsync_enable = 1; + pinfo->mipi.hw_vsync_mode = 1; + } else { + pinfo->mipi.mode = DSI_VIDEO_MODE; + pinfo->type = MIPI_VIDEO_PANEL; + pinfo->mipi.vsync_enable = 0; + pinfo->mipi.hw_vsync_mode = 0; + } + + ctrl_pdata->panel_mode = pinfo->mipi.mode; + mdss_panel_get_dst_fmt(pinfo->bpp, pinfo->mipi.mode, + pinfo->mipi.pixel_packing, &(pinfo->mipi.dst_format)); + pinfo->cont_splash_enabled = 0; + + return ret; +} + +static int dsi_panel_handler(struct mdss_panel_data *pdata, int enable) +{ + int rc = 0; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + pr_debug("%s enable=%d\n", __func__, enable); + if (!pdata) + return -ENODEV; + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (enable && + (pdata->panel_info.panel_power_state == MDSS_PANEL_POWER_OFF)) { + if (!pdata->panel_info.dynamic_switch_pending) { + mdss_dsi_panel_reset(pdata, 1); + rc = ctrl_pdata->on(pdata); + if (rc) + pr_err("%s panel on failed %d\n", __func__, rc); + } + pdata->panel_info.panel_power_state = MDSS_PANEL_POWER_ON; + if (pdata->panel_info.type == MIPI_CMD_PANEL) + mdss_dsi_set_tear_on(ctrl_pdata); + } else if (!enable && + (pdata->panel_info.panel_power_state == MDSS_PANEL_POWER_ON)) { + msm_dsi_sw_reset(); + if (dsi_intf.op_mode_config) + dsi_intf.op_mode_config(DSI_CMD_MODE, pdata); + if (pdata->panel_info.dynamic_switch_pending) { + pr_info("%s: switching to %s mode\n", __func__, + (pdata->panel_info.mipi.mode ? "video" : "command")); + if (pdata->panel_info.type == MIPI_CMD_PANEL) { + ctrl_pdata->switch_mode(pdata, DSI_VIDEO_MODE); + } else if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { + ctrl_pdata->switch_mode(pdata, DSI_CMD_MODE); + mdss_dsi_set_tear_off(ctrl_pdata); + } + } + pdata->panel_info.panel_power_state = MDSS_PANEL_POWER_OFF; + if (!pdata->panel_info.dynamic_switch_pending) { + rc = ctrl_pdata->off(pdata); + mdss_dsi_panel_reset(pdata, 0); + } + } + return rc; +} + +static int dsi_splash_on(struct mdss_panel_data *pdata) +{ + int rc = 0; + + pr_debug("%s:\n", __func__); + + if (dsi_intf.cont_on) + rc = dsi_intf.cont_on(pdata); + + if (rc) { + pr_err("mdss_dsi_on DSI failed %d\n", rc); + return rc; + } + return rc; +} + +static int dsi_clk_ctrl(struct mdss_panel_data *pdata, int enable) +{ + int rc = 0; + + pr_debug("%s:\n", __func__); + + if (dsi_intf.clk_ctrl) + rc = dsi_intf.clk_ctrl(pdata, enable); + + return rc; +} + +static int dsi_event_handler(struct mdss_panel_data *pdata, + int event, void *arg) +{ + int rc = 0; + + if (!pdata) { + pr_err("%s: Invalid input data\n", __func__); + return -ENODEV; + } + + switch (event) { + case MDSS_EVENT_UNBLANK: + rc = dsi_on(pdata); + break; + 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; + case MDSS_EVENT_CONT_SPLASH_BEGIN: + rc = dsi_splash_on(pdata); + break; + case MDSS_EVENT_PANEL_CLK_CTRL: + rc = dsi_clk_ctrl(pdata, + (int)(((struct dsi_panel_clk_ctrl *)arg)->state)); + break; + case MDSS_EVENT_DSI_UPDATE_PANEL_DATA: + rc = dsi_update_pconfig(pdata, (int)(unsigned long) arg); + break; + default: + pr_debug("%s: unhandled event=%d\n", __func__, event); + break; + } + return rc; +} + +static int dsi_parse_gpio(struct platform_device *pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct device_node *np = pdev->dev.of_node; + + ctrl_pdata->disp_en_gpio = of_get_named_gpio(np, + "qcom,platform-enable-gpio", 0); + + if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) + pr_err("%s:%d, Disp_en gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->rst_gpio = of_get_named_gpio(np, + "qcom,platform-reset-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->rst_gpio)) + pr_err("%s:%d, reset gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->mode_gpio = -1; + if (ctrl_pdata->panel_data.panel_info.mode_gpio_state != + MODE_GPIO_NOT_VALID) { + ctrl_pdata->mode_gpio = of_get_named_gpio(np, + "qcom,platform-mode-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->mode_gpio)) + pr_info("%s:%d, reset gpio not specified\n", + __func__, __LINE__); + } + + ctrl_pdata->bklt_en_gpio = of_get_named_gpio(np, + "qcom,platform-bklight-en-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio)) + pr_err("%s:%d, bklt_en gpio not specified\n", + __func__, __LINE__); + + return 0; +} + +static void mdss_dsi_put_dt_vreg_data(struct device *dev, + struct mdss_module_power *module_power) +{ + if (!module_power) { + pr_err("%s: invalid input\n", __func__); + return; + } + + if (module_power->vreg_config) { + devm_kfree(dev, module_power->vreg_config); + module_power->vreg_config = NULL; + } + module_power->num_vreg = 0; +} + +static int mdss_dsi_get_dt_vreg_data(struct device *dev, + struct mdss_module_power *mp, enum dsi_pm_type module) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *of_node = NULL, *supply_node = NULL; + const char *pm_supply_name = NULL; + struct device_node *supply_root_node = NULL; + + if (!dev || !mp) { + pr_err("%s: invalid input\n", __func__); + rc = -EINVAL; + return rc; + } + + of_node = dev->of_node; + + mp->num_vreg = 0; + pm_supply_name = __mdss_dsi_pm_supply_node_name(module); + supply_root_node = of_get_child_by_name(of_node, pm_supply_name); + if (!supply_root_node) { + pr_err("no supply entry present\n"); + goto novreg; + } + + for_each_child_of_node(supply_root_node, supply_node) { + mp->num_vreg++; + } + + if (mp->num_vreg == 0) { + pr_debug("%s: no vreg\n", __func__); + goto novreg; + } else { + pr_debug("%s: vreg found. count=%d\n", __func__, mp->num_vreg); + } + + mp->vreg_config = devm_kzalloc(dev, sizeof(struct mdss_vreg) * + mp->num_vreg, GFP_KERNEL); + if (!mp->vreg_config) { + rc = -ENOMEM; + goto error; + } + + for_each_child_of_node(supply_root_node, supply_node) { + const char *st = NULL; + /* vreg-name */ + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err("%s: error reading name. rc=%d\n", + __func__, rc); + goto error; + } + snprintf(mp->vreg_config[i].vreg_name, + ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st); + /* vreg-min-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err("%s: error reading min volt. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].min_voltage = tmp; + + /* vreg-max-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err("%s: error reading max volt. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].max_voltage = tmp; + + /* enable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err("%s: error reading enable load. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].load[DSS_REG_MODE_ENABLE] = tmp; + + /* disable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err("%s: error reading disable load. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] = tmp; + + /* ulp-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-ulp-load", &tmp); + if (rc) + pr_warn("%s: error reading ulp load. rc=%d\n", + __func__, rc); + + mp->vreg_config[i].load[DSS_REG_MODE_ULP] = (!rc ? tmp : + mp->vreg_config[i].load[DSS_REG_MODE_ENABLE]); + + /* pre-sleep */ + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-on-sleep", &tmp); + if (rc) { + pr_debug("%s: error reading supply pre sleep value. rc=%d\n", + __func__, rc); + rc = 0; + } else { + mp->vreg_config[i].pre_on_sleep = tmp; + } + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-off-sleep", &tmp); + if (rc) { + pr_debug("%s: error reading supply pre sleep value. rc=%d\n", + __func__, rc); + rc = 0; + } else { + mp->vreg_config[i].pre_off_sleep = tmp; + } + + /* post-sleep */ + rc = of_property_read_u32(supply_node, + "qcom,supply-post-on-sleep", &tmp); + if (rc) { + pr_debug("%s: error reading supply post sleep value. rc=%d\n", + __func__, rc); + rc = 0; + } else { + mp->vreg_config[i].post_on_sleep = tmp; + } + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-off-sleep", &tmp); + if (rc) { + pr_debug("%s: error reading supply post sleep value. rc=%d\n", + __func__, rc); + rc = 0; + } else { + mp->vreg_config[i].post_off_sleep = tmp; + } + + pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, ulp=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%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].load[DSS_REG_MODE_ENABLE] + mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] + mp->vreg_config[i].load[DSS_REG_MODE_ULP] + mp->vreg_config[i].pre_on_sleep, + mp->vreg_config[i].post_on_sleep, + mp->vreg_config[i].pre_off_sleep, + mp->vreg_config[i].post_off_sleep + ); + ++i; + } + + return rc; + +error: + if (mp->vreg_config) { + devm_kfree(dev, mp->vreg_config); + mp->vreg_config = NULL; + } +novreg: + mp->num_vreg = 0; + + return rc; +} + +static int dsi_parse_phy(struct platform_device *pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct device_node *np = pdev->dev.of_node; + int i, len; + const char *data; + struct mdss_dsi_phy_ctrl *phy_db + = &(ctrl_pdata->panel_data.panel_info.mipi.dsi_phy_db); + + data = of_get_property(np, "qcom,platform-regulator-settings", &len); + if ((!data) || (len != 6)) { + pr_err("%s:%d, Unable to read Phy regulator settings", + __func__, __LINE__); + return -EINVAL; + } + for (i = 0; i < len; i++) + phy_db->regulator[i] = data[i]; + + data = of_get_property(np, "qcom,platform-strength-ctrl", &len); + if ((!data) || (len != 2)) { + pr_err("%s:%d, Unable to read Phy Strength ctrl settings", + __func__, __LINE__); + return -EINVAL; + } + phy_db->strength[0] = data[0]; + phy_db->strength[1] = data[1]; + + data = of_get_property(np, "qcom,platform-bist-ctrl", &len); + if ((!data) || (len != 6)) { + pr_err("%s:%d, Unable to read Phy Bist Ctrl settings", + __func__, __LINE__); + return -EINVAL; + } + for (i = 0; i < len; i++) + phy_db->bistctrl[i] = data[i]; + + data = of_get_property(np, "qcom,platform-lane-config", &len); + if ((!data) || (len != 30)) { + pr_err("%s:%d, Unable to read Phy lane configure settings", + __func__, __LINE__); + return -EINVAL; + } + for (i = 0; i < len; i++) + phy_db->lanecfg[i] = data[i]; + + return 0; +} + +void dsi_ctrl_config_deinit(struct platform_device *pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + int i; + + for (i = DSI_MAX_PM - 1; i >= 0; i--) { + mdss_dsi_put_dt_vreg_data(&pdev->dev, + &ctrl_pdata->power_data[i]); + } +} + +int dsi_ctrl_config_init(struct platform_device *pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + int rc = 0, i; + + for (i = 0; i < DSI_MAX_PM; i++) { + rc = mdss_dsi_get_dt_vreg_data(&pdev->dev, + &ctrl_pdata->power_data[i], i); + if (rc) { + DEV_ERR("%s: '%s' get_dt_vreg_data failed.rc=%d\n", + __func__, __mdss_dsi_pm_name(i), rc); + return rc; + } + } + + rc = dsi_parse_gpio(pdev, ctrl_pdata); + if (rc) { + pr_err("fail to parse panel GPIOs\n"); + return rc; + } + + rc = dsi_parse_phy(pdev, ctrl_pdata); + if (rc) { + pr_err("fail to parse DSI PHY settings\n"); + return rc; + } + + return 0; +} +int dsi_panel_device_register_v2(struct platform_device *dev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mipi_panel_info *mipi; + int rc; + u8 lanes = 0, bpp; + u32 h_period, v_period; + struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + + h_period = ((pinfo->lcdc.h_pulse_width) + + (pinfo->lcdc.h_back_porch) + + (pinfo->xres) + + (pinfo->lcdc.h_front_porch)); + + v_period = ((pinfo->lcdc.v_pulse_width) + + (pinfo->lcdc.v_back_porch) + + (pinfo->yres) + + (pinfo->lcdc.v_front_porch)); + + mipi = &pinfo->mipi; + + pinfo->type = + ((mipi->mode == DSI_VIDEO_MODE) + ? MIPI_VIDEO_PANEL : MIPI_CMD_PANEL); + + if (mipi->data_lane3) + lanes += 1; + if (mipi->data_lane2) + lanes += 1; + if (mipi->data_lane1) + lanes += 1; + if (mipi->data_lane0) + lanes += 1; + + if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB888) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB666_LOOSE)) + bpp = 3; + else if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565) + || (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB565)) + bpp = 2; + else + bpp = 3; /* Default format set to RGB888 */ + + if (pinfo->type == MIPI_VIDEO_PANEL && + !pinfo->clk_rate) { + h_period += pinfo->lcdc.xres_pad; + v_period += pinfo->lcdc.yres_pad; + + if (lanes > 0) { + pinfo->clk_rate = + ((h_period * v_period * (mipi->frame_rate) * bpp * 8) + / lanes); + } else { + pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); + pinfo->clk_rate = + (h_period * v_period + * (mipi->frame_rate) * bpp * 8); + } + } + + ctrl_pdata->panel_data.event_handler = dsi_event_handler; + + /* + * register in mdp driver + */ + rc = mdss_register_panel(dev, &(ctrl_pdata->panel_data)); + if (rc) { + dev_err(&dev->dev, "unable to register MIPI DSI panel\n"); + return rc; + } + + pr_debug("%s: Panal data initialized\n", __func__); + return 0; +} + +void dsi_register_interface(struct dsi_interface *intf) +{ + dsi_intf = *intf; +} + +int dsi_buf_alloc(struct dsi_buf *dp, int size) +{ + dp->start = kzalloc(size, GFP_KERNEL); + if (!dp->start) + return -ENOMEM; + + dp->end = dp->start + size; + dp->size = size; + + if ((int)dp->start & 0x07) { + pr_err("%s: buf NOT 8 bytes aligned\n", __func__); + return -EINVAL; + } + + dp->data = dp->start; + dp->len = 0; + return 0; +} + diff --git a/drivers/video/fbdev/msm/dsi_v2.h b/drivers/video/fbdev/msm/dsi_v2.h new file mode 100644 index 0000000000000000000000000000000000000000..2f6f4043cddcf98a0d7e45c5a11b88a59740a82b --- /dev/null +++ b/drivers/video/fbdev/msm/dsi_v2.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2012-2014, 2018, 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 DSI_V2_H +#define DSI_V2_H + +#include +#include + +#include "mdss_dsi.h" +#include "mdss_panel.h" + +#define DSI_BUF_SIZE 1024 +#define DSI_MRPS 0x04 /* Maximum Return Packet Size */ + +struct dsi_interface { + int (*on)(struct mdss_panel_data *pdata); + int (*off)(struct mdss_panel_data *pdata); + int (*cont_on)(struct mdss_panel_data *pdata); + int (*clk_ctrl)(struct mdss_panel_data *pdata, int enable); + void (*op_mode_config)(int mode, struct mdss_panel_data *pdata); + int index; + void *private; +}; + +int dsi_panel_device_register_v2(struct platform_device *pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata); + +void dsi_register_interface(struct dsi_interface *intf); + +int dsi_buf_alloc(struct dsi_buf *dp, int size); + +void dsi_set_tx_power_mode(int mode); + +void dsi_ctrl_config_deinit(struct platform_device *pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata); + +int dsi_ctrl_config_init(struct platform_device *pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata); + +struct mdss_panel_cfg *mdp3_panel_intf_type(int intf_val); + +int mdp3_panel_get_boot_cfg(void); + +void msm_dsi_sw_reset(void); +#endif /* DSI_V2_H */ diff --git a/drivers/video/fbdev/msm/mdp3.c b/drivers/video/fbdev/msm/mdp3.c new file mode 100644 index 0000000000000000000000000000000000000000..f85880dd8d76656c199f02f42cf87b36a5d6d611 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3.c @@ -0,0 +1,2661 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "mdp3.h" +#include "mdss_fb.h" +#include "mdp3_hwio.h" +#include "mdp3_ctrl.h" +#include "mdp3_ppp.h" +#include "mdss_debug.h" +#include "mdss_smmu.h" +#include "mdss.h" + +#ifndef EXPORT_COMPAT +#define EXPORT_COMPAT(x) +#endif + +#define AUTOSUSPEND_TIMEOUT_MS 100 +#define MISR_POLL_SLEEP 2000 +#define MISR_POLL_TIMEOUT 32000 +#define MDP3_REG_CAPTURED_DSI_PCLK_MASK 1 + +#define MDP_CORE_HW_VERSION 0x03050306 +struct mdp3_hw_resource *mdp3_res; + +#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ + { \ + .src = MSM_BUS_MASTER_MDP_PORT0, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ab = (ab_val), \ + .ib = (ib_val), \ + } + +#define SET_BIT(value, bit_num) \ +{ \ + value[bit_num >> 3] |= (1 << (bit_num & 7)); \ +} + +#define MAX_BPP_SUPPORTED 4 + +static struct msm_bus_vectors mdp_bus_vectors[] = { + MDP_BUS_VECTOR_ENTRY(0, 0), + MDP_BUS_VECTOR_ENTRY(SZ_128M, SZ_256M), + MDP_BUS_VECTOR_ENTRY(SZ_256M, SZ_512M), +}; +static struct msm_bus_paths + mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)]; +static struct msm_bus_scale_pdata mdp_bus_scale_table = { + .usecase = mdp_bus_usecases, + .num_usecases = ARRAY_SIZE(mdp_bus_usecases), + .name = "mdp3", +}; + +struct mdp3_bus_handle_map mdp3_bus_handle[MDP3_BUS_HANDLE_MAX] = { + [MDP3_BUS_HANDLE] = { + .bus_vector = mdp_bus_vectors, + .usecases = mdp_bus_usecases, + .scale_pdata = &mdp_bus_scale_table, + .current_bus_idx = 0, + .handle = 0, + }, +}; + +static struct mdss_panel_intf pan_types[] = { + {"dsi", MDSS_PANEL_INTF_DSI}, +}; +static char mdss_mdp3_panel[MDSS_MAX_PANEL_LEN]; + +struct mdp3_iommu_domain_map mdp3_iommu_domains[MDP3_IOMMU_DOMAIN_MAX] = { + [MDP3_IOMMU_DOMAIN_UNSECURE] = { + .domain_type = MDP3_IOMMU_DOMAIN_UNSECURE, + .client_name = "mdp_ns", + .npartitions = 1, + .domain_idx = MDP3_IOMMU_DOMAIN_UNSECURE, + }, + [MDP3_IOMMU_DOMAIN_SECURE] = { + .domain_type = MDP3_IOMMU_DOMAIN_SECURE, + .client_name = "mdp_secure", + .npartitions = 1, + .domain_idx = MDP3_IOMMU_DOMAIN_SECURE, + }, +}; + +static irqreturn_t mdp3_irq_handler(int irq, void *ptr) +{ + int i = 0; + struct mdp3_hw_resource *mdata = (struct mdp3_hw_resource *)ptr; + u32 mdp_interrupt = 0; + u32 mdp_status = 0; + + spin_lock(&mdata->irq_lock); + if (!mdata->irq_mask) { + pr_err("spurious interrupt\n"); + spin_unlock(&mdata->irq_lock); + return IRQ_HANDLED; + } + mdp_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS); + mdp_interrupt = mdp_status; + pr_debug("%s irq=%d\n", __func__, mdp_interrupt); + + mdp_interrupt &= mdata->irq_mask; + + while (mdp_interrupt && i < MDP3_MAX_INTR) { + if ((mdp_interrupt & 0x1) && mdata->callbacks[i].cb) + mdata->callbacks[i].cb(i, mdata->callbacks[i].data); + mdp_interrupt = mdp_interrupt >> 1; + i++; + } + MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_status); + + spin_unlock(&mdata->irq_lock); + + return IRQ_HANDLED; +} + +void mdp3_irq_enable(int type) +{ + unsigned long flag; + + pr_debug("%s type=%d\n", __func__, type); + spin_lock_irqsave(&mdp3_res->irq_lock, flag); + if (mdp3_res->irq_ref_count[type] > 0) { + pr_debug("interrupt %d already enabled\n", type); + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); + return; + } + + mdp3_res->irq_mask |= BIT(type); + MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask); + + mdp3_res->irq_ref_count[type] += 1; + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); +} + +void mdp3_irq_disable(int type) +{ + unsigned long flag; + + spin_lock_irqsave(&mdp3_res->irq_lock, flag); + mdp3_irq_disable_nosync(type); + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); +} + +void mdp3_irq_disable_nosync(int type) +{ + if (mdp3_res->irq_ref_count[type] <= 0) { + pr_debug("interrupt %d not enabled\n", type); + return; + } + mdp3_res->irq_ref_count[type] -= 1; + 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); + } +} + +int mdp3_set_intr_callback(u32 type, struct mdp3_intr_cb *cb) +{ + unsigned long flag; + + pr_debug("interrupt %d callback\n", type); + spin_lock_irqsave(&mdp3_res->irq_lock, flag); + if (cb) + mdp3_res->callbacks[type] = *cb; + else + mdp3_res->callbacks[type].cb = NULL; + + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); + return 0; +} + +void mdp3_irq_register(void) +{ + unsigned long flag; + struct mdss_hw *mdp3_hw; + + pr_debug("%s\n", __func__); + mdp3_hw = &mdp3_res->mdp3_hw; + spin_lock_irqsave(&mdp3_res->irq_lock, flag); + mdp3_res->irq_ref_cnt++; + if (mdp3_res->irq_ref_cnt == 1) { + MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask); + mdp3_res->mdss_util->enable_irq(&mdp3_res->mdp3_hw); + } + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); +} + +void mdp3_irq_deregister(void) +{ + unsigned long flag; + bool irq_enabled = true; + struct mdss_hw *mdp3_hw; + + pr_debug("%s\n", __func__); + mdp3_hw = &mdp3_res->mdp3_hw; + 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_res->irq_ref_cnt--; + /* This can happen if suspend is called first */ + if (mdp3_res->irq_ref_cnt < 0) { + irq_enabled = false; + mdp3_res->irq_ref_cnt = 0; + } + if (mdp3_res->irq_ref_cnt == 0 && irq_enabled) + mdp3_res->mdss_util->disable_irq_nosync(&mdp3_res->mdp3_hw); + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); +} + +void mdp3_irq_suspend(void) +{ + unsigned long flag; + bool irq_enabled = true; + struct mdss_hw *mdp3_hw; + + pr_debug("%s\n", __func__); + mdp3_hw = &mdp3_res->mdp3_hw; + spin_lock_irqsave(&mdp3_res->irq_lock, flag); + mdp3_res->irq_ref_cnt--; + if (mdp3_res->irq_ref_cnt < 0) { + irq_enabled = false; + mdp3_res->irq_ref_cnt = 0; + } + if (mdp3_res->irq_ref_cnt == 0 && irq_enabled) { + MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0); + mdp3_res->mdss_util->disable_irq_nosync(&mdp3_res->mdp3_hw); + } + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); +} + +static int mdp3_bus_scale_register(void) +{ + int i, j; + + if (!mdp3_res->bus_handle) { + pr_err("No bus handle\n"); + return -EINVAL; + } + for (i = 0; i < MDP3_BUS_HANDLE_MAX; i++) { + struct mdp3_bus_handle_map *bus_handle = + &mdp3_res->bus_handle[i]; + + if (!bus_handle->handle) { + int j; + struct msm_bus_scale_pdata *bus_pdata = + bus_handle->scale_pdata; + + for (j = 0; j < bus_pdata->num_usecases; j++) { + bus_handle->usecases[j].num_paths = 1; + bus_handle->usecases[j].vectors = + &bus_handle->bus_vector[j]; + } + + bus_handle->handle = + msm_bus_scale_register_client(bus_pdata); + if (!bus_handle->handle) { + pr_err("not able to get bus scale i=%d\n", i); + return -ENOMEM; + } + pr_debug("register bus_hdl=%x\n", + bus_handle->handle); + } + + for (j = 0; j < MDP3_CLIENT_MAX; j++) { + bus_handle->ab[j] = 0; + bus_handle->ib[j] = 0; + } + } + return 0; +} + +static void mdp3_bus_scale_unregister(void) +{ + int i; + + if (!mdp3_res->bus_handle) + return; + + for (i = 0; i < MDP3_BUS_HANDLE_MAX; i++) { + pr_debug("unregister index=%d bus_handle=%x\n", + i, mdp3_res->bus_handle[i].handle); + if (mdp3_res->bus_handle[i].handle) { + msm_bus_scale_unregister_client( + mdp3_res->bus_handle[i].handle); + mdp3_res->bus_handle[i].handle = 0; + } + } +} + +int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota) +{ + struct mdp3_bus_handle_map *bus_handle; + int cur_bus_idx; + int bus_idx; + int client_idx; + u64 total_ib = 0, total_ab = 0; + int i, rc; + + client_idx = MDP3_BUS_HANDLE; + + bus_handle = &mdp3_res->bus_handle[client_idx]; + cur_bus_idx = bus_handle->current_bus_idx; + + if (bus_handle->handle < 1) { + pr_err("invalid bus handle %d\n", bus_handle->handle); + return -EINVAL; + } + + bus_handle->ab[client] = ab_quota; + bus_handle->ib[client] = ib_quota; + + for (i = 0; i < MDP3_CLIENT_MAX; i++) { + total_ab += bus_handle->ab[i]; + total_ib += bus_handle->ib[i]; + } + + if ((total_ab | total_ib) == 0) { + bus_idx = 0; + } else { + int num_cases = bus_handle->scale_pdata->num_usecases; + struct msm_bus_vectors *vect = NULL; + + bus_idx = (cur_bus_idx % (num_cases - 1)) + 1; + + /* aligning to avoid performing updates for small changes */ + total_ab = ALIGN(total_ab, SZ_64M); + total_ib = ALIGN(total_ib, SZ_64M); + + vect = bus_handle->scale_pdata->usecase[cur_bus_idx].vectors; + if ((total_ab == vect->ab) && (total_ib == vect->ib)) { + pr_debug("skip bus scaling, no change in vectors\n"); + return 0; + } + + vect = bus_handle->scale_pdata->usecase[bus_idx].vectors; + vect->ab = total_ab; + vect->ib = total_ib; + + pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx, + vect->ab, vect->ib); + } + bus_handle->current_bus_idx = bus_idx; + rc = msm_bus_scale_client_update_request(bus_handle->handle, bus_idx); + + if (!rc && ab_quota != 0 && ib_quota != 0) { + bus_handle->restore_ab[client] = ab_quota; + bus_handle->restore_ib[client] = ib_quota; + } + + return rc; +} + +static int mdp3_clk_update(u32 clk_idx, u32 enable) +{ + int ret = 0; + struct clk *clk; + int count = 0; + + if (clk_idx >= MDP3_MAX_CLK || !mdp3_res->clocks[clk_idx]) + return -ENODEV; + + clk = mdp3_res->clocks[clk_idx]; + + if (enable) + mdp3_res->clock_ref_count[clk_idx]++; + else + mdp3_res->clock_ref_count[clk_idx]--; + + count = mdp3_res->clock_ref_count[clk_idx]; + if (count == 1 && enable) { + pr_debug("clk=%d en=%d\n", clk_idx, enable); + ret = clk_prepare(clk); + if (ret) { + pr_err("%s: Failed to prepare clock %d", + __func__, clk_idx); + mdp3_res->clock_ref_count[clk_idx]--; + return ret; + } + if (clk_idx == MDP3_CLK_MDP_CORE) + MDSS_XLOG(enable); + ret = clk_enable(clk); + if (ret) + pr_err("%s: clock enable failed %d\n", __func__, + clk_idx); + } else if (count == 0) { + pr_debug("clk=%d disable\n", clk_idx); + if (clk_idx == MDP3_CLK_MDP_CORE) + MDSS_XLOG(enable); + clk_disable(clk); + clk_unprepare(clk); + ret = 0; + } else if (count < 0) { + pr_err("clk=%d count=%d\n", clk_idx, count); + ret = -EINVAL; + } + return ret; +} + + + +int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate, + int client) +{ + int ret = 0; + unsigned long rounded_rate; + struct clk *clk = mdp3_res->clocks[clk_type]; + + if (clk) { + mutex_lock(&mdp3_res->res_mutex); + rounded_rate = clk_round_rate(clk, clk_rate); + if (IS_ERR_VALUE(rounded_rate)) { + pr_err("unable to round rate err=%ld\n", rounded_rate); + mutex_unlock(&mdp3_res->res_mutex); + return -EINVAL; + } + if (clk_type == MDP3_CLK_MDP_SRC) { + if (client == MDP3_CLIENT_DMA_P) { + mdp3_res->dma_core_clk_request = rounded_rate; + } else if (client == MDP3_CLIENT_PPP) { + mdp3_res->ppp_core_clk_request = rounded_rate; + } else { + pr_err("unrecognized client=%d\n", client); + mutex_unlock(&mdp3_res->res_mutex); + return -EINVAL; + } + rounded_rate = max(mdp3_res->dma_core_clk_request, + mdp3_res->ppp_core_clk_request); + } + if (rounded_rate != clk_get_rate(clk)) { + ret = clk_set_rate(clk, rounded_rate); + if (ret) + pr_err("clk_set_rate failed ret=%d\n", ret); + else + pr_debug("mdp clk rate=%lu, client = %d\n", + rounded_rate, client); + } + mutex_unlock(&mdp3_res->res_mutex); + } else { + pr_err("mdp src clk not setup properly\n"); + ret = -EINVAL; + } + return ret; +} + +unsigned long mdp3_get_clk_rate(u32 clk_idx) +{ + unsigned long clk_rate = 0; + struct clk *clk; + + if (clk_idx >= MDP3_MAX_CLK) + return -ENODEV; + + clk = mdp3_res->clocks[clk_idx]; + + if (clk) { + mutex_lock(&mdp3_res->res_mutex); + clk_rate = clk_get_rate(clk); + mutex_unlock(&mdp3_res->res_mutex); + } + return clk_rate; +} + +static int mdp3_clk_register(char *clk_name, int clk_idx) +{ + struct clk *tmp; + + if (clk_idx >= MDP3_MAX_CLK) { + pr_err("invalid clk index %d\n", clk_idx); + return -EINVAL; + } + + tmp = devm_clk_get(&mdp3_res->pdev->dev, clk_name); + if (IS_ERR(tmp)) { + pr_err("unable to get clk: %s\n", clk_name); + return PTR_ERR(tmp); + } + + mdp3_res->clocks[clk_idx] = tmp; + + return 0; +} + +static int mdp3_clk_setup(void) +{ + int rc; + + rc = mdp3_clk_register("iface_clk", MDP3_CLK_AHB); + if (rc) + return rc; + + rc = mdp3_clk_register("bus_clk", MDP3_CLK_AXI); + if (rc) + return rc; + + rc = mdp3_clk_register("core_clk_src", MDP3_CLK_MDP_SRC); + if (rc) + return rc; + + rc = mdp3_clk_register("core_clk", MDP3_CLK_MDP_CORE); + if (rc) + return rc; + + rc = mdp3_clk_register("vsync_clk", MDP3_CLK_VSYNC); + if (rc) + return rc; + + rc = mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, MDP_CORE_CLK_RATE_SVS, + MDP3_CLIENT_DMA_P); + if (rc) + pr_err("%s: Error setting max clock during probe\n", __func__); + return rc; +} + +static void mdp3_clk_remove(void) +{ + if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_AHB])) + clk_put(mdp3_res->clocks[MDP3_CLK_AHB]); + + if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_AXI])) + clk_put(mdp3_res->clocks[MDP3_CLK_AXI]); + + if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_MDP_SRC])) + clk_put(mdp3_res->clocks[MDP3_CLK_MDP_SRC]); + + if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_MDP_CORE])) + clk_put(mdp3_res->clocks[MDP3_CLK_MDP_CORE]); + + if (!IS_ERR_OR_NULL(mdp3_res->clocks[MDP3_CLK_VSYNC])) + clk_put(mdp3_res->clocks[MDP3_CLK_VSYNC]); + +} + +u64 mdp3_clk_round_off(u64 clk_rate) +{ + u64 clk_round_off = 0; + + if (clk_rate <= MDP_CORE_CLK_RATE_SVS) + clk_round_off = MDP_CORE_CLK_RATE_SVS; + else if (clk_rate <= MDP_CORE_CLK_RATE_SUPER_SVS) + clk_round_off = MDP_CORE_CLK_RATE_SUPER_SVS; + else + clk_round_off = MDP_CORE_CLK_RATE_MAX; + + pr_debug("clk = %llu rounded to = %llu\n", + clk_rate, clk_round_off); + return clk_round_off; +} + +int mdp3_clk_enable(int enable, int dsi_clk) +{ + int rc = 0; + int changed = 0; + + pr_debug("MDP CLKS %s\n", (enable ? "Enable" : "Disable")); + + mutex_lock(&mdp3_res->res_mutex); + + if (enable) { + if (mdp3_res->clk_ena == 0) + changed++; + mdp3_res->clk_ena++; + } else { + if (mdp3_res->clk_ena) { + mdp3_res->clk_ena--; + if (mdp3_res->clk_ena == 0) + changed++; + } else { + pr_err("Can not be turned off\n"); + } + } + pr_debug("%s: clk_ena=%d changed=%d enable=%d\n", + __func__, mdp3_res->clk_ena, changed, enable); + + if (changed) { + if (enable) + pm_runtime_get_sync(&mdp3_res->pdev->dev); + + rc = mdp3_clk_update(MDP3_CLK_AHB, enable); + rc |= mdp3_clk_update(MDP3_CLK_AXI, enable); + rc |= mdp3_clk_update(MDP3_CLK_MDP_SRC, enable); + rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, enable); + rc |= mdp3_clk_update(MDP3_CLK_VSYNC, enable); + + if (!enable) { + pm_runtime_mark_last_busy(&mdp3_res->pdev->dev); + pm_runtime_put_autosuspend(&mdp3_res->pdev->dev); + } + } + + mutex_unlock(&mdp3_res->res_mutex); + return rc; +} + +void mdp3_bus_bw_iommu_enable(int enable, int client) +{ + struct mdp3_bus_handle_map *bus_handle; + int client_idx; + u64 ab = 0, ib = 0; + int ref_cnt; + + client_idx = MDP3_BUS_HANDLE; + + bus_handle = &mdp3_res->bus_handle[client_idx]; + if (bus_handle->handle < 1) { + pr_err("invalid bus handle %d\n", bus_handle->handle); + return; + } + mutex_lock(&mdp3_res->res_mutex); + if (enable) + bus_handle->ref_cnt++; + else + if (bus_handle->ref_cnt) + bus_handle->ref_cnt--; + ref_cnt = bus_handle->ref_cnt; + mutex_unlock(&mdp3_res->res_mutex); + + if (enable) { + if (mdp3_res->allow_iommu_update) + mdp3_iommu_enable(client); + if (ref_cnt == 1) { + pm_runtime_get_sync(&mdp3_res->pdev->dev); + ab = bus_handle->restore_ab[client]; + ib = bus_handle->restore_ib[client]; + mdp3_bus_scale_set_quota(client, ab, ib); + } + } else { + if (ref_cnt == 0) { + mdp3_bus_scale_set_quota(client, 0, 0); + pm_runtime_mark_last_busy(&mdp3_res->pdev->dev); + pm_runtime_put_autosuspend(&mdp3_res->pdev->dev); + } + mdp3_iommu_disable(client); + } + + if (ref_cnt < 0) { + pr_err("Ref count < 0, bus client=%d, ref_cnt=%d", + client_idx, ref_cnt); + } +} + +void mdp3_calc_dma_res(struct mdss_panel_info *panel_info, u64 *clk_rate, + u64 *ab, u64 *ib, uint32_t bpp) +{ + u32 vtotal = mdss_panel_get_vtotal(panel_info); + u32 htotal = mdss_panel_get_htotal(panel_info, 0); + u64 clk = htotal * vtotal * panel_info->mipi.frame_rate; + + pr_debug("clk_rate for dma = %llu, bpp = %d\n", clk, bpp); + if (clk_rate) + *clk_rate = mdp3_clk_round_off(clk); + + /* ab and ib vote should be same for honest voting */ + if (ab || ib) { + *ab = clk * bpp; + *ib = *ab; + } +} + +int mdp3_res_update(int enable, int dsi_clk, int client) +{ + int rc = 0; + + if (enable) { + rc = mdp3_clk_enable(enable, dsi_clk); + if (rc < 0) { + pr_err("mdp3_clk_enable failed, enable=%d, dsi_clk=%d\n", + enable, dsi_clk); + goto done; + } + mdp3_irq_register(); + mdp3_bus_bw_iommu_enable(enable, client); + } else { + mdp3_bus_bw_iommu_enable(enable, client); + mdp3_irq_suspend(); + rc = mdp3_clk_enable(enable, dsi_clk); + if (rc < 0) { + pr_err("mdp3_clk_enable failed, enable=%d, dsi_clk=%d\n", + enable, dsi_clk); + goto done; + } + } + +done: + return rc; +} + +int mdp3_get_mdp_dsi_clk(void) +{ + int rc; + + mutex_lock(&mdp3_res->res_mutex); + rc = mdp3_clk_update(MDP3_CLK_DSI, 1); + mutex_unlock(&mdp3_res->res_mutex); + return rc; +} + +int mdp3_put_mdp_dsi_clk(void) +{ + int rc; + + mutex_lock(&mdp3_res->res_mutex); + rc = mdp3_clk_update(MDP3_CLK_DSI, 0); + mutex_unlock(&mdp3_res->res_mutex); + return rc; +} + +static int mdp3_irq_setup(void) +{ + int ret; + struct mdss_hw *mdp3_hw; + + mdp3_hw = &mdp3_res->mdp3_hw; + ret = devm_request_irq(&mdp3_res->pdev->dev, + mdp3_hw->irq_info->irq, + mdp3_irq_handler, + 0, "MDP", mdp3_res); + if (ret) { + pr_err("mdp request_irq() failed!\n"); + return ret; + } + disable_irq_nosync(mdp3_hw->irq_info->irq); + mdp3_res->irq_registered = true; + return 0; +} + +static int mdp3_get_iommu_domain(u32 type) +{ + if (type >= MDSS_IOMMU_MAX_DOMAIN) + return -EINVAL; + + if (!mdp3_res) + return -ENODEV; + + return mdp3_res->domains[type].domain_idx; +} + +static int mdp3_check_version(void) +{ + int rc; + + rc = mdp3_clk_enable(1, 0); + if (rc) { + pr_err("fail to turn on MDP core clks\n"); + return rc; + } + + mdp3_res->mdp_rev = MDP3_REG_READ(MDP3_REG_HW_VERSION); + + if (mdp3_res->mdp_rev != MDP_CORE_HW_VERSION) { + pr_err("mdp_hw_revision=%x mismatch\n", mdp3_res->mdp_rev); + rc = -ENODEV; + } + + rc = mdp3_clk_enable(0, 0); + if (rc) + pr_err("fail to turn off MDP core clks\n"); + + return rc; +} + +static int mdp3_hw_init(void) +{ + int i; + + for (i = MDP3_DMA_P; i < MDP3_DMA_MAX; i++) { + mdp3_res->dma[i].dma_sel = i; + mdp3_res->dma[i].capability = MDP3_DMA_CAP_ALL; + mdp3_res->dma[i].in_use = 0; + mdp3_res->dma[i].available = 1; + mdp3_res->dma[i].cc_vect_sel = 0; + mdp3_res->dma[i].lut_sts = 0; + mdp3_res->dma[i].hist_cmap = NULL; + mdp3_res->dma[i].gc_cmap = NULL; + mutex_init(&mdp3_res->dma[i].pp_lock); + } + mdp3_res->dma[MDP3_DMA_S].capability = MDP3_DMA_CAP_DITHER; + mdp3_res->dma[MDP3_DMA_E].available = 0; + + for (i = MDP3_DMA_OUTPUT_SEL_AHB; i < MDP3_DMA_OUTPUT_SEL_MAX; i++) { + mdp3_res->intf[i].cfg.type = i; + mdp3_res->intf[i].active = 0; + mdp3_res->intf[i].in_use = 0; + mdp3_res->intf[i].available = 1; + } + mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_AHB].available = 0; + mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_LCDC].available = 0; + mdp3_res->smart_blit_en = SMART_BLIT_RGB_EN | SMART_BLIT_YUV_EN; + mdp3_res->solid_fill_vote_en = false; + return 0; +} + +int mdp3_dynamic_clock_gating_ctrl(int enable) +{ + int rc = 0; + int cgc_cfg = 0; + /*Disable dynamic auto clock gating*/ + pr_debug("%s Status %s\n", __func__, (enable ? "ON":"OFF")); + rc = mdp3_clk_enable(1, 0); + if (rc) { + pr_err("fail to turn on MDP core clks\n"); + return rc; + } + cgc_cfg = MDP3_REG_READ(MDP3_REG_CGC_EN); + if (enable) { + cgc_cfg |= (BIT(10)); + cgc_cfg |= (BIT(18)); + MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc_cfg); + VBIF_REG_WRITE(MDP3_VBIF_REG_FORCE_EN, 0x0); + } else { + cgc_cfg &= ~(BIT(10)); + cgc_cfg &= ~(BIT(18)); + MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc_cfg); + VBIF_REG_WRITE(MDP3_VBIF_REG_FORCE_EN, 0x3); + } + + rc = mdp3_clk_enable(0, 0); + if (rc) + pr_err("fail to turn off MDP core clks\n"); + + return rc; +} + +/** + * mdp3_get_panic_lut_cfg() - calculate panic and robust lut mask + * @panel_width: Panel width + * + * DMA buffer has 16 fill levels. Which needs to configured as safe + * and panic levels based on panel resolutions. + * No. of fill levels used = ((panel active width * 8) / 512). + * Roundoff the fill levels if needed. + * half of the total fill levels used will be treated as panic levels. + * Roundoff panic levels if total used fill levels are odd. + * + * Sample calculation for 720p display: + * Fill levels used = (720 * 8) / 512 = 12.5 after round off 13. + * panic levels = 13 / 2 = 6.5 after roundoff 7. + * Panic mask = 0x3FFF (2 bits per level) + * Robust mask = 0xFF80 (1 bit per level) + */ +u64 mdp3_get_panic_lut_cfg(u32 panel_width) +{ + u32 fill_levels = (((panel_width * 8) / 512) + 1); + u32 panic_mask = 0; + u32 robust_mask = 0; + u32 i = 0; + u64 panic_config = 0; + u32 panic_levels = 0; + + panic_levels = fill_levels / 2; + if (fill_levels % 2) + panic_levels++; + + for (i = 0; i < panic_levels; i++) { + panic_mask |= (BIT((i * 2) + 1) | BIT(i * 2)); + robust_mask |= BIT(i); + } + panic_config = ~robust_mask; + panic_config = panic_config << 32; + panic_config |= panic_mask; + return panic_config; +} + +int mdp3_enable_panic_ctrl(void) +{ + int rc = 0; + + if (MDP3_REG_READ(MDP3_PANIC_ROBUST_CTRL) == 0) { + pr_err("%s: Enable Panic Control\n", __func__); + MDP3_REG_WRITE(MDP3_PANIC_ROBUST_CTRL, BIT(0)); + } + return rc; +} + +int mdp3_qos_remapper_setup(struct mdss_panel_data *panel) +{ + int rc = 0; + u64 panic_config = mdp3_get_panic_lut_cfg(panel->panel_info.xres); + + rc = mdp3_clk_update(MDP3_CLK_AHB, 1); + rc |= mdp3_clk_update(MDP3_CLK_AXI, 1); + rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, 1); + if (rc) { + pr_err("fail to turn on MDP core clks\n"); + return rc; + } + + if (!panel) + return -EINVAL; + /* Program MDP QOS Remapper */ + MDP3_REG_WRITE(MDP3_DMA_P_QOS_REMAPPER, 0x1A9); + MDP3_REG_WRITE(MDP3_DMA_P_WATERMARK_0, 0x0); + MDP3_REG_WRITE(MDP3_DMA_P_WATERMARK_1, 0x0); + MDP3_REG_WRITE(MDP3_DMA_P_WATERMARK_2, 0x0); + /* PANIC setting depends on panel width*/ + MDP3_REG_WRITE(MDP3_PANIC_LUT0, (panic_config & 0xFFFF)); + MDP3_REG_WRITE(MDP3_PANIC_LUT1, ((panic_config >> 16) & 0xFFFF)); + MDP3_REG_WRITE(MDP3_ROBUST_LUT, ((panic_config >> 32) & 0xFFFF)); + MDP3_REG_WRITE(MDP3_PANIC_ROBUST_CTRL, 0x1); + pr_debug("Panel width %d Panic Lut0 %x Lut1 %x Robust %x\n", + panel->panel_info.xres, + MDP3_REG_READ(MDP3_PANIC_LUT0), + MDP3_REG_READ(MDP3_PANIC_LUT1), + MDP3_REG_READ(MDP3_ROBUST_LUT)); + + rc = mdp3_clk_update(MDP3_CLK_AHB, 0); + rc |= mdp3_clk_update(MDP3_CLK_AXI, 0); + rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, 0); + if (rc) + pr_err("fail to turn off MDP core clks\n"); + return rc; +} + +static int mdp3_res_init(void) +{ + int rc = 0; + + rc = mdp3_irq_setup(); + if (rc) + return rc; + + rc = mdp3_clk_setup(); + if (rc) + return rc; + + mdp3_res->ion_client = msm_ion_client_create(mdp3_res->pdev->name); + if (IS_ERR_OR_NULL(mdp3_res->ion_client)) { + pr_err("msm_ion_client_create() return error (%pK)\n", + mdp3_res->ion_client); + mdp3_res->ion_client = NULL; + return -EINVAL; + } + mutex_init(&mdp3_res->iommu_lock); + + mdp3_res->domains = mdp3_iommu_domains; + mdp3_res->bus_handle = mdp3_bus_handle; + rc = mdp3_bus_scale_register(); + if (rc) { + pr_err("unable to register bus scaling\n"); + return rc; + } + + rc = mdp3_hw_init(); + + return rc; +} + +static void mdp3_res_deinit(void) +{ + struct mdss_hw *mdp3_hw; + int rc = 0; + + mdp3_hw = &mdp3_res->mdp3_hw; + mdp3_bus_scale_unregister(); + mutex_lock(&mdp3_res->iommu_lock); + if (mdp3_res->iommu_ref_cnt) { + mdp3_res->iommu_ref_cnt--; + if (mdp3_res->iommu_ref_cnt == 0) + rc = mdss_smmu_detach(mdss_res); + } else { + pr_err("iommu ref count %d\n", mdp3_res->iommu_ref_cnt); + } + mutex_unlock(&mdp3_res->iommu_lock); + + if (!IS_ERR_OR_NULL(mdp3_res->ion_client)) + ion_client_destroy(mdp3_res->ion_client); + + mdp3_clk_remove(); + + if (mdp3_res->irq_registered) + devm_free_irq(&mdp3_res->pdev->dev, + mdp3_hw->irq_info->irq, mdp3_res); +} + +static int mdp3_get_pan_intf(const char *pan_intf) +{ + int i, rc = MDSS_PANEL_INTF_INVALID; + + if (!pan_intf) + return rc; + + for (i = 0; i < ARRAY_SIZE(pan_types); i++) { + if (!strcmp(pan_intf, pan_types[i].name)) { + rc = pan_types[i].type; + break; + } + } + + return rc; +} + +static int mdp3_parse_dt_pan_intf(struct platform_device *pdev) +{ + int rc; + struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev); + const char *prim_intf = NULL; + + rc = of_property_read_string(pdev->dev.of_node, + "qcom,mdss-pref-prim-intf", &prim_intf); + if (rc) + return -ENODEV; + + rc = mdp3_get_pan_intf(prim_intf); + if (rc < 0) { + mdata->pan_cfg.pan_intf = MDSS_PANEL_INTF_INVALID; + } else { + mdata->pan_cfg.pan_intf = rc; + rc = 0; + } + return rc; +} + +static int mdp3_get_pan_cfg(struct mdss_panel_cfg *pan_cfg) +{ + char *t = NULL; + char pan_intf_str[MDSS_MAX_PANEL_LEN]; + int rc, i, panel_len; + char pan_name[MDSS_MAX_PANEL_LEN]; + + if (!pan_cfg) + return -EINVAL; + + if (mdss_mdp3_panel[0] == '0') { + pan_cfg->lk_cfg = false; + } else if (mdss_mdp3_panel[0] == '1') { + pan_cfg->lk_cfg = true; + } else { + /* read from dt */ + pan_cfg->lk_cfg = true; + pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID; + return -EINVAL; + } + + /* skip lk cfg and delimiter; ex: "0:" */ + strlcpy(pan_name, &mdss_mdp3_panel[2], MDSS_MAX_PANEL_LEN); + t = strnstr(pan_name, ":", MDSS_MAX_PANEL_LEN); + if (!t) { + pr_err("%s: pan_name=[%s] invalid\n", + __func__, pan_name); + pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID; + return -EINVAL; + } + + for (i = 0; ((pan_name + i) < t) && (i < 4); i++) + pan_intf_str[i] = *(pan_name + i); + pan_intf_str[i] = 0; + pr_debug("%s:%d panel intf %s\n", __func__, __LINE__, pan_intf_str); + /* point to the start of panel name */ + t = t + 1; + strlcpy(&pan_cfg->arg_cfg[0], t, sizeof(pan_cfg->arg_cfg)); + pr_debug("%s:%d: t=[%s] panel name=[%s]\n", __func__, __LINE__, + t, pan_cfg->arg_cfg); + + panel_len = strlen(pan_cfg->arg_cfg); + if (!panel_len) { + pr_err("%s: Panel name is invalid\n", __func__); + pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID; + return -EINVAL; + } + + rc = mdp3_get_pan_intf(pan_intf_str); + pan_cfg->pan_intf = (rc < 0) ? MDSS_PANEL_INTF_INVALID : rc; + return 0; +} + +static int mdp3_get_cmdline_config(struct platform_device *pdev) +{ + int rc, len = 0; + int *intf_type; + char *panel_name; + struct mdss_panel_cfg *pan_cfg; + struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev); + + mdata->pan_cfg.arg_cfg[MDSS_MAX_PANEL_LEN] = 0; + pan_cfg = &mdata->pan_cfg; + panel_name = &pan_cfg->arg_cfg[0]; + intf_type = &pan_cfg->pan_intf; + + /* reads from dt by default */ + pan_cfg->lk_cfg = true; + + len = strlen(mdss_mdp3_panel); + + if (len > 0) { + rc = mdp3_get_pan_cfg(pan_cfg); + if (!rc) { + pan_cfg->init_done = true; + return rc; + } + } + + rc = mdp3_parse_dt_pan_intf(pdev); + /* if pref pan intf is not present */ + if (rc) + pr_err("%s:unable to parse device tree for pan intf\n", + __func__); + else + pan_cfg->init_done = true; + + return rc; +} + + +int mdp3_irq_init(u32 irq_start) +{ + struct mdss_hw *mdp3_hw; + + mdp3_hw = &mdp3_res->mdp3_hw; + + mdp3_hw->irq_info = kzalloc(sizeof(struct irq_info), GFP_KERNEL); + if (!mdp3_hw->irq_info) + return -ENOMEM; + + mdp3_hw->hw_ndx = MDSS_HW_MDP; + mdp3_hw->irq_info->irq = irq_start; + mdp3_hw->irq_info->irq_mask = 0; + mdp3_hw->irq_info->irq_ena = false; + mdp3_hw->irq_info->irq_buzy = false; + + mdp3_res->mdss_util->register_irq(&mdp3_res->mdp3_hw); + return 0; +} + +static int mdp3_parse_dt(struct platform_device *pdev) +{ + struct resource *res; + struct property *prop = NULL; + bool panic_ctrl; + int rc; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdp_phys"); + if (!res) { + pr_err("unable to get MDP base address\n"); + return -EINVAL; + } + + mdp3_res->mdp_reg_size = resource_size(res); + mdp3_res->mdp_base = devm_ioremap(&pdev->dev, res->start, + mdp3_res->mdp_reg_size); + if (unlikely(!mdp3_res->mdp_base)) { + pr_err("unable to map MDP base\n"); + return -ENOMEM; + } + + pr_debug("MDP HW Base phy_Address=0x%x virt=0x%x\n", + (int) res->start, + (int) mdp3_res->mdp_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vbif_phys"); + if (!res) { + pr_err("unable to get VBIF base address\n"); + return -EINVAL; + } + + mdp3_res->vbif_reg_size = resource_size(res); + mdp3_res->vbif_base = devm_ioremap(&pdev->dev, res->start, + mdp3_res->vbif_reg_size); + if (unlikely(!mdp3_res->vbif_base)) { + pr_err("unable to map VBIF base\n"); + return -ENOMEM; + } + + pr_debug("VBIF HW Base phy_Address=0x%x virt=0x%x\n", + (int) res->start, + (int) mdp3_res->vbif_base); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + pr_err("unable to get MDSS irq\n"); + return -EINVAL; + } + rc = mdp3_irq_init(res->start); + if (rc) { + pr_err("%s: Error in irq initialization:rc=[%d]\n", + __func__, rc); + return rc; + } + + rc = mdp3_get_cmdline_config(pdev); + if (rc) { + pr_err("%s: Error in panel override:rc=[%d]\n", + __func__, rc); + kfree(mdp3_res->mdp3_hw.irq_info); + return rc; + } + + prop = of_find_property(pdev->dev.of_node, "batfet-supply", NULL); + mdp3_res->batfet_required = prop ? true : false; + + panic_ctrl = of_property_read_bool( + pdev->dev.of_node, "qcom,mdss-has-panic-ctrl"); + mdp3_res->dma[MDP3_DMA_P].has_panic_ctrl = panic_ctrl; + + mdp3_res->idle_pc_enabled = of_property_read_bool( + pdev->dev.of_node, "qcom,mdss-idle-power-collapse-enabled"); + + return 0; +} + +void msm_mdp3_cx_ctrl(int enable) +{ + int rc; + + if (!mdp3_res->vdd_cx) { + mdp3_res->vdd_cx = devm_regulator_get(&mdp3_res->pdev->dev, + "vdd-cx"); + if (IS_ERR_OR_NULL(mdp3_res->vdd_cx)) { + pr_debug("unable to get CX reg. rc=%d\n", + PTR_RET(mdp3_res->vdd_cx)); + mdp3_res->vdd_cx = NULL; + return; + } + } + + if (enable) { + rc = regulator_set_voltage( + mdp3_res->vdd_cx, + RPM_REGULATOR_CORNER_SVS_SOC, + RPM_REGULATOR_CORNER_SUPER_TURBO); + if (rc < 0) + goto vreg_set_voltage_fail; + + rc = regulator_enable(mdp3_res->vdd_cx); + if (rc) { + pr_err("Failed to enable regulator vdd_cx.\n"); + return; + } + } else { + rc = regulator_disable(mdp3_res->vdd_cx); + if (rc) { + pr_err("Failed to disable regulator vdd_cx.\n"); + return; + } + rc = regulator_set_voltage( + mdp3_res->vdd_cx, + RPM_REGULATOR_CORNER_NONE, + RPM_REGULATOR_CORNER_SUPER_TURBO); + if (rc < 0) + goto vreg_set_voltage_fail; + } + + return; +vreg_set_voltage_fail: + pr_err("Set vltg failed\n"); +} + +void mdp3_batfet_ctrl(int enable) +{ + int rc; + + if (!mdp3_res->batfet_required) + return; + + if (!mdp3_res->batfet) { + if (enable) { + mdp3_res->batfet = + devm_regulator_get(&mdp3_res->pdev->dev, + "batfet"); + if (IS_ERR_OR_NULL(mdp3_res->batfet)) { + pr_debug("unable to get batfet reg. rc=%d\n", + PTR_RET(mdp3_res->batfet)); + mdp3_res->batfet = NULL; + return; + } + } else { + pr_debug("Batfet regulator disable w/o enable\n"); + return; + } + } + + if (enable) + rc = regulator_enable(mdp3_res->batfet); + else + rc = regulator_disable(mdp3_res->batfet); + + if (rc < 0) + pr_err("%s: reg enable/disable failed", __func__); +} + +void mdp3_enable_regulator(int enable) +{ + mdp3_batfet_ctrl(enable); +} + +int mdp3_put_img(struct mdp3_img_data *data, int client) +{ + struct ion_client *iclient = mdp3_res->ion_client; + int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN_UNSECURE)->domain_idx; + int dir = DMA_BIDIRECTIONAL; + + if (data->flags & MDP_MEMORY_ID_TYPE_FB) { + pr_info("%s fb mem buf=0x%pa\n", __func__, &data->addr); + fdput(data->srcp_f); + memset(&data->srcp_f, 0, sizeof(struct fd)); + } else if (!IS_ERR_OR_NULL(data->srcp_dma_buf)) { + pr_debug("ion hdl = %pK buf=0x%pa\n", data->srcp_dma_buf, + &data->addr); + if (!iclient) { + pr_err("invalid ion client\n"); + return -ENOMEM; + } + if (data->mapped) { + if (client == MDP3_CLIENT_PPP || + client == MDP3_CLIENT_DMA_P) + mdss_smmu_unmap_dma_buf(data->tab_clone, + dom, dir, data->srcp_dma_buf); + else + mdss_smmu_unmap_dma_buf(data->srcp_table, + dom, dir, data->srcp_dma_buf); + data->mapped = false; + } + if (!data->skip_detach) { + dma_buf_unmap_attachment(data->srcp_attachment, + data->srcp_table, + mdss_smmu_dma_data_direction(dir)); + dma_buf_detach(data->srcp_dma_buf, + data->srcp_attachment); + dma_buf_put(data->srcp_dma_buf); + data->srcp_dma_buf = NULL; + } + } else { + return -EINVAL; + } + if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) { + kfree(data->tab_clone->sgl); + kfree(data->tab_clone); + } + return 0; +} + +int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client) +{ + struct fd f; + int ret = -EINVAL; + int fb_num; + struct ion_client *iclient = mdp3_res->ion_client; + int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN_UNSECURE)->domain_idx; + + data->flags = img->flags; + + if (img->flags & MDP_MEMORY_ID_TYPE_FB) { + f = fdget(img->memory_id); + if (f.file == NULL) { + pr_err("invalid framebuffer file (%d)\n", + img->memory_id); + return -EINVAL; + } + if (MAJOR(f.file->f_path.dentry->d_inode->i_rdev) == FB_MAJOR) { + fb_num = MINOR(f.file->f_path.dentry->d_inode->i_rdev); + ret = mdss_fb_get_phys_info(&data->addr, + &data->len, fb_num); + if (ret) { + pr_err("mdss_fb_get_phys_info() failed\n"); + fdput(f); + memset(&f, 0, sizeof(struct fd)); + } + } else { + pr_err("invalid FB_MAJOR\n"); + fdput(f); + ret = -EINVAL; + } + data->srcp_f = f; + if (!ret) + goto done; + } else if (iclient) { + data->srcp_dma_buf = dma_buf_get(img->memory_id); + if (IS_ERR(data->srcp_dma_buf)) { + pr_err("DMA : error on ion_import_fd\n"); + ret = PTR_ERR(data->srcp_dma_buf); + data->srcp_dma_buf = NULL; + return ret; + } + + data->srcp_attachment = + mdss_smmu_dma_buf_attach(data->srcp_dma_buf, + &mdp3_res->pdev->dev, dom); + if (IS_ERR(data->srcp_attachment)) { + ret = PTR_ERR(data->srcp_attachment); + goto err_put; + } + + data->srcp_table = + dma_buf_map_attachment(data->srcp_attachment, + mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL)); + if (IS_ERR(data->srcp_table)) { + ret = PTR_ERR(data->srcp_table); + goto err_detach; + } + + if (client == MDP3_CLIENT_PPP || + client == MDP3_CLIENT_DMA_P) { + data->tab_clone = + mdss_smmu_sg_table_clone(data->srcp_table, + GFP_KERNEL, true); + if (IS_ERR_OR_NULL(data->tab_clone)) { + if (!(data->tab_clone)) + ret = -EINVAL; + else + ret = PTR_ERR(data->tab_clone); + goto clone_err; + } + ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf, + data->tab_clone, dom, + &data->addr, &data->len, + DMA_BIDIRECTIONAL); + } else { + ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf, + data->srcp_table, dom, &data->addr, + &data->len, DMA_BIDIRECTIONAL); + } + + if (IS_ERR_VALUE(ret)) { + pr_err("smmu map dma buf failed: (%d)\n", ret); + goto err_unmap; + } + + data->mapped = true; + data->skip_detach = false; + } +done: + if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) { + data->addr += data->tab_clone->sgl->length; + data->len -= data->tab_clone->sgl->length; + } + if (!ret && (img->offset < data->len)) { + data->addr += img->offset; + data->len -= img->offset; + + pr_debug("mem=%d ihdl=%pK buf=0x%pa len=0x%lx\n", + img->memory_id, data->srcp_dma_buf, + &data->addr, data->len); + + } else { + mdp3_put_img(data, client); + return -EINVAL; + } + return ret; + +clone_err: + dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table, + mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL)); +err_detach: + dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment); +err_put: + dma_buf_put(data->srcp_dma_buf); + return ret; +err_unmap: + dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table, + mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL)); + dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment); + dma_buf_put(data->srcp_dma_buf); + + if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) { + kfree(data->tab_clone->sgl); + kfree(data->tab_clone); + } + return ret; + +} + +int mdp3_iommu_enable(int client) +{ + int rc = 0; + + mutex_lock(&mdp3_res->iommu_lock); + + if (mdp3_res->iommu_ref_cnt == 0) { + rc = mdss_smmu_attach(mdss_res); + if (rc) + rc = mdss_smmu_detach(mdss_res); + } + + if (!rc) + mdp3_res->iommu_ref_cnt++; + mutex_unlock(&mdp3_res->iommu_lock); + + pr_debug("client :%d total_ref_cnt: %d\n", + client, mdp3_res->iommu_ref_cnt); + return rc; +} + +int mdp3_iommu_disable(int client) +{ + int rc = 0; + + mutex_lock(&mdp3_res->iommu_lock); + if (mdp3_res->iommu_ref_cnt) { + mdp3_res->iommu_ref_cnt--; + + pr_debug("client :%d total_ref_cnt: %d\n", + client, mdp3_res->iommu_ref_cnt); + if (mdp3_res->iommu_ref_cnt == 0) + rc = mdss_smmu_detach(mdss_res); + } else { + pr_err("iommu ref count unbalanced for client %d\n", client); + } + mutex_unlock(&mdp3_res->iommu_lock); + + return rc; +} + +int mdp3_iommu_ctrl(int enable) +{ + int rc; + + if (mdp3_res->allow_iommu_update == false) + return 0; + + if (enable) + rc = mdp3_iommu_enable(MDP3_CLIENT_DSI); + else + rc = mdp3_iommu_disable(MDP3_CLIENT_DSI); + return rc; +} + +static int mdp3_init(struct msm_fb_data_type *mfd) +{ + int rc; + + rc = mdp3_ctrl_init(mfd); + if (rc) { + pr_err("mdp3 ctl init fail\n"); + return rc; + } + + rc = mdp3_ppp_res_init(mfd); + if (rc) + pr_err("mdp3 ppp res init fail\n"); + + return rc; +} + +u32 mdp3_fb_stride(u32 fb_index, u32 xres, int bpp) +{ + /* + * The adreno GPU hardware requires that the pitch be aligned to + * 32 pixels for color buffers, so for the cases where the GPU + * is writing directly to fb0, the framebuffer pitch + * also needs to be 32 pixel aligned + */ + + if (fb_index == 0) + return ALIGN(xres, 32) * bpp; + else + return xres * bpp; +} + +__ref int mdp3_parse_dt_splash(struct msm_fb_data_type *mfd) +{ + struct platform_device *pdev = mfd->pdev; + int len = 0, rc = 0; + u32 offsets[2]; + struct device_node *pnode, *child_node; + struct property *prop = NULL; + + mfd->splash_info.splash_logo_enabled = + of_property_read_bool(pdev->dev.of_node, + "qcom,mdss-fb-splash-logo-enabled"); + + prop = of_find_property(pdev->dev.of_node, "qcom,memblock-reserve", + &len); + if (!prop) { + pr_debug("Read memblock reserve settings for fb failed\n"); + pr_debug("Read cont-splash-memory settings\n"); + } + + if (len) { + len = len / sizeof(u32); + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,memblock-reserve", offsets, len); + if (rc) { + pr_err("error reading mem reserve settings for fb\n"); + rc = -EINVAL; + goto error; + } + } else { + child_node = of_get_child_by_name(pdev->dev.of_node, + "qcom,cont-splash-memory"); + if (!child_node) { + pr_err("splash mem child node is not present\n"); + rc = -EINVAL; + goto error; + } + + pnode = of_parse_phandle(child_node, "linux,contiguous-region", + 0); + if (pnode != NULL) { + const u32 *addr; + u64 size; + + addr = of_get_address(pnode, 0, &size, NULL); + if (!addr) { + pr_err("failed to parse the splash memory address\n"); + of_node_put(pnode); + rc = -EINVAL; + goto error; + } + offsets[0] = (u32) of_read_ulong(addr, 2); + offsets[1] = (u32) size; + of_node_put(pnode); + } else { + pr_err("mem reservation for splash screen fb not present\n"); + rc = -EINVAL; + goto error; + } + } + + if (!memblock_is_reserved(offsets[0])) { + pr_debug("failed to reserve memory for fb splash\n"); + rc = -EINVAL; + goto error; + } + + mdp3_res->splash_mem_addr = offsets[0]; + mdp3_res->splash_mem_size = offsets[1]; +error: + if (rc && mfd->panel_info->cont_splash_enabled) + pr_err("no rsvd mem found in DT for splash screen\n"); + else + rc = 0; + + return rc; +} + +void mdp3_release_splash_memory(struct msm_fb_data_type *mfd) +{ + /* Give back the reserved memory to the system */ + if (mdp3_res->splash_mem_addr) { + if ((mfd->panel.type == MIPI_VIDEO_PANEL) && + (mdp3_res->cont_splash_en)) { + mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE, + mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_size); + } + pr_debug("%s\n", __func__); + memblock_free(mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_size); + free_bootmem_late(mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_size); + mdp3_res->splash_mem_addr = 0; + } +} + +struct mdp3_dma *mdp3_get_dma_pipe(int capability) +{ + int i; + + for (i = MDP3_DMA_P; i < MDP3_DMA_MAX; i++) { + if (!mdp3_res->dma[i].in_use && mdp3_res->dma[i].available && + mdp3_res->dma[i].capability & capability) { + mdp3_res->dma[i].in_use = true; + return &mdp3_res->dma[i]; + } + } + return NULL; +} + +struct mdp3_intf *mdp3_get_display_intf(int type) +{ + int i; + + for (i = MDP3_DMA_OUTPUT_SEL_AHB; i < MDP3_DMA_OUTPUT_SEL_MAX; i++) { + if (!mdp3_res->intf[i].in_use && mdp3_res->intf[i].available && + mdp3_res->intf[i].cfg.type == type) { + mdp3_res->intf[i].in_use = true; + return &mdp3_res->intf[i]; + } + } + return NULL; +} + +static int mdp3_fb_mem_get_iommu_domain(void) +{ + if (!mdp3_res) + return -ENODEV; + return mdp3_res->domains[MDP3_IOMMU_DOMAIN_UNSECURE].domain_idx; +} + +int mdp3_get_cont_spash_en(void) +{ + return mdp3_res->cont_splash_en; +} + +static int mdp3_is_display_on(struct mdss_panel_data *pdata) +{ + int rc = 0; + u32 status; + + rc = mdp3_clk_enable(1, 0); + if (rc) { + pr_err("fail to turn on MDP core clks\n"); + return rc; + } + if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { + status = MDP3_REG_READ(MDP3_REG_DSI_VIDEO_EN); + rc = status & 0x1; + } else { + status = MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG); + status &= 0x180000; + rc = (status == 0x080000); + } + + mdp3_res->splash_mem_addr = MDP3_REG_READ(MDP3_REG_DMA_P_IBUF_ADDR); + + if (mdp3_clk_enable(0, 0)) + pr_err("fail to turn off MDP core clks\n"); + return rc; +} + +static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) +{ + struct mdss_panel_info *panel_info = &pdata->panel_info; + struct mdp3_bus_handle_map *bus_handle; + u64 ab = 0; + u64 ib = 0; + u64 mdp_clk_rate = 0; + int rc = 0; + + pr_debug("mdp3__continuous_splash_on\n"); + + bus_handle = &mdp3_res->bus_handle[MDP3_BUS_HANDLE]; + if (bus_handle->handle < 1) { + pr_err("invalid bus handle %d\n", bus_handle->handle); + return -EINVAL; + } + mdp3_calc_dma_res(panel_info, &mdp_clk_rate, &ab, &ib, panel_info->bpp); + + mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE, + MDP3_CLIENT_DMA_P); + mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, mdp_clk_rate, + MDP3_CLIENT_DMA_P); + + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib); + bus_handle->restore_ab[MDP3_CLIENT_DMA_P] = ab; + bus_handle->restore_ib[MDP3_CLIENT_DMA_P] = ib; + + rc = mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P); + if (rc) { + pr_err("fail to enable clk\n"); + return rc; + } + + rc = mdp3_ppp_init(); + if (rc) { + pr_err("ppp init failed\n"); + goto splash_on_err; + } + + if (panel_info->type == MIPI_VIDEO_PANEL) + mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_VIDEO].active = 1; + else + mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_CMD].active = 1; + + mdp3_enable_regulator(true); + mdp3_res->cont_splash_en = 1; + return 0; + +splash_on_err: + if (mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P)) + pr_err("%s: Unable to disable mdp3 clocks\n", __func__); + + return rc; +} + +static int mdp3_panel_register_done(struct mdss_panel_data *pdata) +{ + int rc = 0; + u64 ab = 0; u64 ib = 0; + u64 mdp_clk_rate = 0; + + /* Store max bandwidth supported in mdp res */ + mdp3_calc_dma_res(&pdata->panel_info, &mdp_clk_rate, &ab, &ib, + MAX_BPP_SUPPORTED); + do_div(ab, 1024); + mdp3_res->max_bw = ab+1; + + /* + * If idle pc feature is not enabled, then get a reference to the + * runtime device which will be released when device is turned off + */ + if (!mdp3_res->idle_pc_enabled || + pdata->panel_info.type != MIPI_CMD_PANEL) { + pm_runtime_get_sync(&mdp3_res->pdev->dev); + } + + if (pdata->panel_info.cont_splash_enabled) { + if (!mdp3_is_display_on(pdata)) { + pr_err("continuous splash, but bootloader is not\n"); + return 0; + } + rc = mdp3_continuous_splash_on(pdata); + } else { + if (mdp3_is_display_on(pdata)) { + pr_err("lk continuous splash, but kerenl not\n"); + rc = mdp3_continuous_splash_on(pdata); + } + } + /* + * We want to prevent iommu from being enabled if there is + * continue splash screen. This would have happened in + * res_update in continuous_splash_on without this flag. + */ + if (pdata->panel_info.cont_splash_enabled == false) + mdp3_res->allow_iommu_update = true; + + mdss_res->pdata = pdata; + return rc; +} + +/* mdp3_clear_irq() - Clear interrupt + * @ interrupt_mask : interrupt mask + * + * This function clear sync irq for command mode panel. + * When system is entering in idle screen state. + */ +void mdp3_clear_irq(u32 interrupt_mask) +{ + unsigned long flag; + u32 irq_status = 0; + + spin_lock_irqsave(&mdp3_res->irq_lock, flag); + irq_status = interrupt_mask & + MDP3_REG_READ(MDP3_REG_INTR_STATUS); + if (irq_status) + MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, irq_status); + spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); + +} + +/* mdp3_autorefresh_disable() - Disable Auto refresh + * @ panel_info : pointer to panel configuration structure + * + * This function disable Auto refresh block for command mode panel. + */ +int mdp3_autorefresh_disable(struct mdss_panel_info *panel_info) +{ + if ((panel_info->type == MIPI_CMD_PANEL) && + (MDP3_REG_READ(MDP3_REG_AUTOREFRESH_CONFIG_P))) + MDP3_REG_WRITE(MDP3_REG_AUTOREFRESH_CONFIG_P, 0); + return 0; +} + +int mdp3_splash_done(struct mdss_panel_info *panel_info) +{ + if (panel_info->cont_splash_enabled) { + pr_err("continuous splash is on and splash done called\n"); + return -EINVAL; + } + mdp3_res->allow_iommu_update = true; + return 0; +} + +static int mdp3_debug_dump_stats_show(struct seq_file *s, void *v) +{ + struct mdp3_hw_resource *res = (struct mdp3_hw_resource *)s->private; + + seq_printf(s, "underrun: %08u\n", res->underrun_cnt); + + return 0; +} +DEFINE_MDSS_DEBUGFS_SEQ_FOPS(mdp3_debug_dump_stats); + +static void mdp3_debug_enable_clock(int on) +{ + if (on) + mdp3_clk_enable(1, 0); + else + mdp3_clk_enable(0, 0); +} + +static int mdp3_debug_init(struct platform_device *pdev) +{ + int rc; + struct mdss_data_type *mdata; + struct mdss_debug_data *mdd; + + mdata = devm_kzalloc(&pdev->dev, sizeof(*mdata), GFP_KERNEL); + if (!mdata) + return -ENOMEM; + + mdss_res = mdata; + mutex_init(&mdata->reg_lock); + mutex_init(&mdata->reg_bus_lock); + mutex_init(&mdata->bus_lock); + INIT_LIST_HEAD(&mdata->reg_bus_clist); + atomic_set(&mdata->sd_client_count, 0); + atomic_set(&mdata->active_intf_cnt, 0); + mdss_res->mdss_util = mdp3_res->mdss_util; + + mdata->debug_inf.debug_enable_clock = mdp3_debug_enable_clock; + mdata->mdp_rev = mdp3_res->mdp_rev; + + rc = mdss_debugfs_init(mdata); + if (rc) + return rc; + + mdd = mdata->debug_inf.debug_data; + if (!mdd) + return -EINVAL; + + debugfs_create_file("stat", 0644, mdd->root, mdp3_res, + &mdp3_debug_dump_stats_fops); + + rc = mdss_debug_register_base(NULL, mdp3_res->mdp_base, + mdp3_res->mdp_reg_size, NULL); + + return rc; +} + +static void mdp3_debug_deinit(struct platform_device *pdev) +{ + if (mdss_res) { + mdss_debugfs_remove(mdss_res); + devm_kfree(&pdev->dev, mdss_res); + mdss_res = NULL; + } +} + +static void mdp3_dma_underrun_intr_handler(int type, void *arg) +{ + struct mdp3_dma *dma = &mdp3_res->dma[MDP3_DMA_P]; + + mdp3_res->underrun_cnt++; + pr_err_ratelimited("display underrun detected count=%d\n", + mdp3_res->underrun_cnt); + ATRACE_INT("mdp3_dma_underrun_intr_handler", mdp3_res->underrun_cnt); + + if (dma->ccs_config.ccs_enable && !dma->ccs_config.ccs_dirty) { + dma->ccs_config.ccs_dirty = true; + schedule_work(&dma->underrun_work); + } +} + +uint32_t ppp_formats_supported[] = { + MDP_RGB_565, + MDP_BGR_565, + MDP_RGB_888, + MDP_BGR_888, + MDP_XRGB_8888, + MDP_ARGB_8888, + MDP_RGBA_8888, + MDP_BGRA_8888, + MDP_RGBX_8888, + MDP_Y_CBCR_H2V1, + MDP_Y_CBCR_H2V2, + MDP_Y_CBCR_H2V2_ADRENO, + MDP_Y_CBCR_H2V2_VENUS, + MDP_Y_CRCB_H2V1, + MDP_Y_CRCB_H2V2, + MDP_YCRYCB_H2V1, + MDP_BGRX_8888, +}; + +uint32_t dma_formats_supported[] = { + MDP_RGB_565, + MDP_RGB_888, + MDP_XRGB_8888, +}; + +static void __mdp3_set_supported_formats(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ppp_formats_supported); i++) + SET_BIT(mdp3_res->ppp_formats, ppp_formats_supported[i]); + + for (i = 0; i < ARRAY_SIZE(dma_formats_supported); i++) + SET_BIT(mdp3_res->dma_formats, dma_formats_supported[i]); +} + +static void __update_format_supported_info(char *buf, int *cnt) +{ + int j; + size_t len = PAGE_SIZE; + int num_bytes = BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1); +#define SPRINT(fmt, ...) \ + (*cnt += scnprintf(buf + *cnt, len - *cnt, fmt, ##__VA_ARGS__)) + + SPRINT("ppp_input_fmts="); + for (j = 0; j < num_bytes; j++) + SPRINT("%d,", mdp3_res->ppp_formats[j]); + SPRINT("\ndma_output_fmts="); + for (j = 0; j < num_bytes; j++) + SPRINT("%d,", mdp3_res->dma_formats[j]); + SPRINT("\n"); +#undef SPRINT +} + +static ssize_t mdp3_show_capabilities(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t len = PAGE_SIZE; + int cnt = 0; + +#define SPRINT(fmt, ...) \ + (cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__)) + + SPRINT("dma_pipes=%d\n", 1); + SPRINT("mdp_version=3\n"); + SPRINT("hw_rev=%d\n", 305); + SPRINT("pipe_count:%d\n", 1); + SPRINT("pipe_num:%d pipe_type:dma pipe_ndx:%d rects:%d ", 0, 1, 1); + SPRINT("pipe_is_handoff:%d display_id:%d\n", 0, 0); + __update_format_supported_info(buf, &cnt); + SPRINT("rgb_pipes=%d\n", 0); + SPRINT("vig_pipes=%d\n", 0); + SPRINT("dma_pipes=%d\n", 1); + SPRINT("blending_stages=%d\n", 1); + SPRINT("cursor_pipes=%d\n", 0); + SPRINT("max_cursor_size=%d\n", 0); + SPRINT("smp_count=%d\n", 0); + SPRINT("smp_size=%d\n", 0); + SPRINT("smp_mb_per_pipe=%d\n", 0); + SPRINT("max_downscale_ratio=%d\n", PPP_DOWNSCALE_MAX); + SPRINT("max_upscale_ratio=%d\n", PPP_UPSCALE_MAX); + SPRINT("max_pipe_bw=%u\n", mdp3_res->max_bw); + SPRINT("max_bandwidth_low=%u\n", mdp3_res->max_bw); + SPRINT("max_bandwidth_high=%u\n", mdp3_res->max_bw); + SPRINT("max_mdp_clk=%u\n", MDP_CORE_CLK_RATE_MAX); + SPRINT("clk_fudge_factor=%u,%u\n", CLK_FUDGE_NUM, CLK_FUDGE_DEN); + SPRINT("features=has_ppp\n"); + +#undef SPRINT + + return cnt; +} + +static DEVICE_ATTR(caps, 0444, mdp3_show_capabilities, NULL); + +static ssize_t mdp3_store_smart_blit(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u32 data = -1; + ssize_t rc = 0; + + rc = kstrtoint(buf, 10, &data); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + mdp3_res->smart_blit_en = data; + pr_debug("mdp3 smart blit RGB %s YUV %s\n", + (mdp3_res->smart_blit_en & SMART_BLIT_RGB_EN) ? + "ENABLED" : "DISABLED", + (mdp3_res->smart_blit_en & SMART_BLIT_YUV_EN) ? + "ENABLED" : "DISABLED"); + return len; +} + +static ssize_t mdp3_show_smart_blit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + pr_debug("mdp3 smart blit RGB %s YUV %s\n", + (mdp3_res->smart_blit_en & SMART_BLIT_RGB_EN) ? + "ENABLED" : "DISABLED", + (mdp3_res->smart_blit_en & SMART_BLIT_YUV_EN) ? + "ENABLED" : "DISABLED"); + ret = snprintf(buf, PAGE_SIZE, "%d\n", mdp3_res->smart_blit_en); + return ret; +} + +static DEVICE_ATTR(smart_blit, 0664, + mdp3_show_smart_blit, mdp3_store_smart_blit); + +static struct attribute *mdp3_fs_attrs[] = { + &dev_attr_caps.attr, + &dev_attr_smart_blit.attr, + NULL +}; + +static struct attribute_group mdp3_fs_attr_group = { + .attrs = mdp3_fs_attrs +}; + +static int mdp3_register_sysfs(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + rc = sysfs_create_group(&dev->kobj, &mdp3_fs_attr_group); + + return rc; +} + +int mdp3_create_sysfs_link(struct device *dev) +{ + int rc; + + rc = sysfs_create_link_nowarn(&dev->kobj, + &mdp3_res->pdev->dev.kobj, "mdp"); + + return rc; +} + +int mdp3_misr_get(struct mdp_misr *misr_resp) +{ + int result = 0, ret = -1; + int crc = 0; + + pr_debug("%s CRC Capture on DSI\n", __func__); + switch (misr_resp->block_id) { + case DISPLAY_MISR_DSI0: + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_EN, 0); + /* Sleep for one vsync after DSI video engine is disabled */ + msleep(20); + /* Enable DSI_VIDEO_0 MISR Block */ + MDP3_REG_WRITE(MDP3_REG_MODE_DSI_PCLK, 0x20); + /* Reset MISR Block */ + MDP3_REG_WRITE(MDP3_REG_MISR_RESET_DSI_PCLK, 1); + /* Clear MISR capture done bit */ + MDP3_REG_WRITE(MDP3_REG_CAPTURED_DSI_PCLK, 0); + /* Enable MDP DSI interface */ + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_EN, 1); + ret = readl_poll_timeout(mdp3_res->mdp_base + + MDP3_REG_CAPTURED_DSI_PCLK, result, + result & MDP3_REG_CAPTURED_DSI_PCLK_MASK, + MISR_POLL_SLEEP, MISR_POLL_TIMEOUT); + MDP3_REG_WRITE(MDP3_REG_MODE_DSI_PCLK, 0); + if (ret == 0) { + /* Disable DSI MISR interface */ + MDP3_REG_WRITE(MDP3_REG_MODE_DSI_PCLK, 0x0); + crc = MDP3_REG_READ(MDP3_REG_MISR_CAPT_VAL_DSI_PCLK); + pr_debug("CRC Val %d\n", crc); + } else { + pr_err("CRC Read Timed Out\n"); + } + break; + + case DISPLAY_MISR_DSI_CMD: + /* Select DSI PCLK Domain */ + MDP3_REG_WRITE(MDP3_REG_SEL_CLK_OR_HCLK_TEST_BUS, 0x004); + /* Select Block id DSI_CMD */ + MDP3_REG_WRITE(MDP3_REG_MODE_DSI_PCLK, 0x10); + /* Reset MISR Block */ + MDP3_REG_WRITE(MDP3_REG_MISR_RESET_DSI_PCLK, 1); + /* Drive Data on Test Bus */ + MDP3_REG_WRITE(MDP3_REG_EXPORT_MISR_DSI_PCLK, 0); + /* Kikk off DMA_P */ + MDP3_REG_WRITE(MDP3_REG_DMA_P_START, 0x11); + /* Wait for DMA_P Done */ + ret = readl_poll_timeout(mdp3_res->mdp_base + + MDP3_REG_INTR_STATUS, result, + result & MDP3_INTR_DMA_P_DONE_BIT, + MISR_POLL_SLEEP, MISR_POLL_TIMEOUT); + if (ret == 0) { + crc = MDP3_REG_READ(MDP3_REG_MISR_CURR_VAL_DSI_PCLK); + pr_debug("CRC Val %d\n", crc); + } else { + pr_err("CRC Read Timed Out\n"); + } + break; + + default: + pr_err("%s CRC Capture not supported\n", __func__); + ret = -EINVAL; + break; + } + + misr_resp->crc_value[0] = crc; + pr_debug("%s, CRC Capture on DSI Param Block = 0x%x, CRC 0x%x\n", + __func__, misr_resp->block_id, misr_resp->crc_value[0]); + return ret; +} + +int mdp3_misr_set(struct mdp_misr *misr_req) +{ + int ret = 0; + + pr_debug("%s Parameters Block = %d Cframe Count = %d CRC = %d\n", + __func__, misr_req->block_id, misr_req->frame_count, + misr_req->crc_value[0]); + + switch (misr_req->block_id) { + case DISPLAY_MISR_DSI0: + pr_debug("In the case DISPLAY_MISR_DSI0\n"); + MDP3_REG_WRITE(MDP3_REG_SEL_CLK_OR_HCLK_TEST_BUS, 1); + MDP3_REG_WRITE(MDP3_REG_MODE_DSI_PCLK, 0x20); + MDP3_REG_WRITE(MDP3_REG_MISR_RESET_DSI_PCLK, 0x1); + break; + + case DISPLAY_MISR_DSI_CMD: + pr_debug("In the case DISPLAY_MISR_DSI_CMD\n"); + MDP3_REG_WRITE(MDP3_REG_SEL_CLK_OR_HCLK_TEST_BUS, 1); + MDP3_REG_WRITE(MDP3_REG_MODE_DSI_PCLK, 0x10); + MDP3_REG_WRITE(MDP3_REG_MISR_RESET_DSI_PCLK, 0x1); + break; + + default: + pr_err("%s CRC Capture not supported\n", __func__); + ret = -EINVAL; + break; + } + return ret; +} + +struct mdss_panel_cfg *mdp3_panel_intf_type(int intf_val) +{ + if (!mdp3_res || !mdp3_res->pan_cfg.init_done) + return ERR_PTR(-EPROBE_DEFER); + + if (mdp3_res->pan_cfg.pan_intf == intf_val) + return &mdp3_res->pan_cfg; + else + return NULL; +} +EXPORT_SYMBOL(mdp3_panel_intf_type); + +int mdp3_footswitch_ctrl(int enable) +{ + int rc = 0; + int active_cnt = 0; + + mutex_lock(&mdp3_res->fs_idle_pc_lock); + MDSS_XLOG(enable); + if (!mdp3_res->fs_ena && enable) { + rc = regulator_enable(mdp3_res->fs); + if (rc) { + pr_err("mdp footswitch ctrl enable failed\n"); + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + return -EINVAL; + } + pr_debug("mdp footswitch ctrl enable success\n"); + mdp3_enable_regulator(true); + mdp3_res->fs_ena = true; + } else if (!enable && mdp3_res->fs_ena) { + active_cnt = atomic_read(&mdp3_res->active_intf_cnt); + if (active_cnt != 0) { + /* + * Turning off GDSC while overlays are still + * active. + */ + mdp3_res->idle_pc = true; + pr_debug("idle pc. active overlays=%d\n", + active_cnt); + } + mdp3_enable_regulator(false); + rc = regulator_disable(mdp3_res->fs); + if (rc) { + pr_err("mdp footswitch ctrl disable failed\n"); + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + return -EINVAL; + } + mdp3_res->fs_ena = false; + pr_debug("mdp3 footswitch ctrl disable configured\n"); + } else { + pr_debug("mdp3 footswitch ctrl already configured\n"); + } + + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + return rc; +} + +int mdp3_panel_get_intf_status(u32 disp_num, u32 intf_type) +{ + int rc = 0, status = 0; + + if (intf_type != MDSS_PANEL_INTF_DSI) + return 0; + + rc = mdp3_clk_enable(1, 0); + if (rc) { + pr_err("fail to turn on MDP core clks\n"); + return rc; + } + + status = (MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG) & 0x180000); + /* DSI video mode or command mode */ + rc = (status == 0x180000) || (status == 0x080000); + + if (mdp3_clk_enable(0, 0)) + pr_err("fail to turn off MDP core clks\n"); + return rc; +} + +static int mdp3_probe(struct platform_device *pdev) +{ + int rc; + static struct msm_mdp_interface mdp3_interface = { + .init_fnc = mdp3_init, + .fb_mem_get_iommu_domain = mdp3_fb_mem_get_iommu_domain, + .panel_register_done = mdp3_panel_register_done, + .fb_stride = mdp3_fb_stride, + .check_dsi_status = mdp3_check_dsi_ctrl_status, + }; + + struct mdp3_intr_cb underrun_cb = { + .cb = mdp3_dma_underrun_intr_handler, + .data = NULL, + }; + + pr_debug("%s: START\n", __func__); + if (!pdev->dev.of_node) { + pr_err("MDP driver only supports device tree probe\n"); + return -ENOTSUPP; + } + + if (mdp3_res) { + pr_err("MDP already initialized\n"); + return -EINVAL; + } + + mdp3_res = devm_kzalloc(&pdev->dev, sizeof(struct mdp3_hw_resource), + GFP_KERNEL); + if (mdp3_res == NULL) + return -ENOMEM; + + pdev->id = 0; + mdp3_res->pdev = pdev; + mutex_init(&mdp3_res->res_mutex); + mutex_init(&mdp3_res->fs_idle_pc_lock); + spin_lock_init(&mdp3_res->irq_lock); + platform_set_drvdata(pdev, mdp3_res); + atomic_set(&mdp3_res->active_intf_cnt, 0); + mutex_init(&mdp3_res->reg_bus_lock); + INIT_LIST_HEAD(&mdp3_res->reg_bus_clist); + + mdp3_res->mdss_util = mdss_get_util_intf(); + if (mdp3_res->mdss_util == NULL) { + pr_err("Failed to get mdss utility functions\n"); + rc = -ENODEV; + goto get_util_fail; + } + mdp3_res->mdss_util->get_iommu_domain = mdp3_get_iommu_domain; + mdp3_res->mdss_util->iommu_attached = is_mdss_iommu_attached; + mdp3_res->mdss_util->iommu_ctrl = mdp3_iommu_ctrl; + mdp3_res->mdss_util->bus_scale_set_quota = mdp3_bus_scale_set_quota; + mdp3_res->mdss_util->panel_intf_type = mdp3_panel_intf_type; + mdp3_res->mdss_util->dyn_clk_gating_ctrl = + mdp3_dynamic_clock_gating_ctrl; + mdp3_res->mdss_util->panel_intf_type = mdp3_panel_intf_type; + mdp3_res->mdss_util->panel_intf_status = mdp3_panel_get_intf_status; + + if (mdp3_res->mdss_util->param_check(mdss_mdp3_panel)) { + mdp3_res->mdss_util->display_disabled = true; + mdp3_res->mdss_util->mdp_probe_done = true; + return 0; + } + + rc = mdp3_parse_dt(pdev); + if (rc) + goto probe_done; + + rc = mdp3_res_init(); + if (rc) { + pr_err("unable to initialize mdp3 resources\n"); + goto probe_done; + } + + mdp3_res->fs_ena = false; + mdp3_res->fs = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR_OR_NULL(mdp3_res->fs)) { + pr_err("unable to get mdss gdsc regulator\n"); + return -EINVAL; + } + + rc = mdp3_debug_init(pdev); + if (rc) { + pr_err("unable to initialize mdp debugging\n"); + goto probe_done; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT_MS); + if (mdp3_res->idle_pc_enabled) { + pr_debug("%s: Enabling autosuspend\n", __func__); + pm_runtime_use_autosuspend(&pdev->dev); + } + /* Enable PM runtime */ + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + if (!pm_runtime_enabled(&pdev->dev)) { + rc = mdp3_footswitch_ctrl(1); + if (rc) { + pr_err("unable to turn on FS\n"); + goto probe_done; + } + } + + rc = mdp3_check_version(); + if (rc) { + pr_err("mdp3 check version failed\n"); + goto probe_done; + } + rc = mdp3_register_sysfs(pdev); + if (rc) + pr_err("unable to register mdp sysfs nodes\n"); + + rc = mdss_fb_register_mdp_instance(&mdp3_interface); + if (rc) + pr_err("unable to register mdp instance\n"); + + rc = mdp3_set_intr_callback(MDP3_INTR_LCDC_UNDERFLOW, + &underrun_cb); + if (rc) + pr_err("unable to configure interrupt callback\n"); + + rc = mdss_smmu_init(mdss_res, &pdev->dev); + if (rc) + pr_err("mdss smmu init failed\n"); + + __mdp3_set_supported_formats(); + + mdp3_res->mdss_util->mdp_probe_done = true; + pr_debug("%s: END\n", __func__); + +probe_done: + if (IS_ERR_VALUE(rc)) + kfree(mdp3_res->mdp3_hw.irq_info); +get_util_fail: + if (IS_ERR_VALUE(rc)) { + mdp3_res_deinit(); + + if (mdp3_res->mdp_base) + devm_iounmap(&pdev->dev, mdp3_res->mdp_base); + + devm_kfree(&pdev->dev, mdp3_res); + mdp3_res = NULL; + + if (mdss_res) { + devm_kfree(&pdev->dev, mdss_res); + mdss_res = NULL; + } + } + + return rc; +} + +int mdp3_panel_get_boot_cfg(void) +{ + int rc; + + if (!mdp3_res || !mdp3_res->pan_cfg.init_done) + rc = -EPROBE_DEFER; + else if (mdp3_res->pan_cfg.lk_cfg) + rc = 1; + else + rc = 0; + return rc; +} + +static int mdp3_suspend_sub(void) +{ + mdp3_footswitch_ctrl(0); + return 0; +} + +static int mdp3_resume_sub(void) +{ + mdp3_footswitch_ctrl(1); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mdp3_pm_suspend(struct device *dev) +{ + dev_dbg(dev, "Display pm suspend\n"); + MDSS_XLOG(XLOG_FUNC_ENTRY); + return mdp3_suspend_sub(); +} + +static int mdp3_pm_resume(struct device *dev) +{ + dev_dbg(dev, "Display pm resume\n"); + + /* + * It is possible that the runtime status of the mdp device may + * have been active when the system was suspended. Reset the runtime + * status to suspended state after a complete system resume. + */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + MDSS_XLOG(XLOG_FUNC_ENTRY); + return mdp3_resume_sub(); +} +#endif + +#if defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP) +static int mdp3_suspend(struct platform_device *pdev, pm_message_t state) +{ + pr_debug("Display suspend\n"); + + MDSS_XLOG(XLOG_FUNC_ENTRY); + return mdp3_suspend_sub(); +} + +static int mdp3_resume(struct platform_device *pdev) +{ + pr_debug("Display resume\n"); + + MDSS_XLOG(XLOG_FUNC_ENTRY); + return mdp3_resume_sub(); +} +#else +#define mdp3_suspend NULL +#define mdp3_resume NULL +#endif + +#ifdef CONFIG_PM +static int mdp3_runtime_resume(struct device *dev) +{ + bool device_on = true; + + dev_dbg(dev, "Display pm runtime resume, active overlay cnt=%d\n", + atomic_read(&mdp3_res->active_intf_cnt)); + + /* do not resume panels when coming out of idle power collapse */ + if (!mdp3_res->idle_pc) + device_for_each_child(dev, &device_on, mdss_fb_suspres_panel); + + MDSS_XLOG(XLOG_FUNC_ENTRY); + mdp3_footswitch_ctrl(1); + + return 0; +} + +static int mdp3_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "Display pm runtime idle\n"); + + return 0; +} + +static int mdp3_runtime_suspend(struct device *dev) +{ + bool device_on = false; + + dev_dbg(dev, "Display pm runtime suspend, active overlay cnt=%d\n", + atomic_read(&mdp3_res->active_intf_cnt)); + + if (mdp3_res->clk_ena) { + pr_debug("Clk turned on...MDP suspend failed\n"); + return -EBUSY; + } + + MDSS_XLOG(XLOG_FUNC_ENTRY); + mdp3_footswitch_ctrl(0); + + /* do not suspend panels when going in to idle power collapse */ + if (!mdp3_res->idle_pc) + device_for_each_child(dev, &device_on, mdss_fb_suspres_panel); + + return 0; +} +#endif + +static const struct dev_pm_ops mdp3_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mdp3_pm_suspend, + mdp3_pm_resume) + SET_RUNTIME_PM_OPS(mdp3_runtime_suspend, + mdp3_runtime_resume, + mdp3_runtime_idle) +}; + + +static int mdp3_remove(struct platform_device *pdev) +{ + struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev); + + if (!mdata) + return -ENODEV; + pm_runtime_disable(&pdev->dev); + mdp3_bus_scale_unregister(); + mdp3_clk_remove(); + mdp3_debug_deinit(pdev); + return 0; +} + +static const struct of_device_id mdp3_dt_match[] = { + { .compatible = "qcom,mdss_mdp3",}, + {} +}; +MODULE_DEVICE_TABLE(of, mdp3_dt_match); +EXPORT_COMPAT("qcom,mdss_mdp3"); + +static struct platform_driver mdp3_driver = { + .probe = mdp3_probe, + .remove = mdp3_remove, + .suspend = mdp3_suspend, + .resume = mdp3_resume, + .shutdown = NULL, + .driver = { + .name = "mdp3", + .of_match_table = mdp3_dt_match, + .pm = &mdp3_pm_ops, + }, +}; + +static int __init mdp3_driver_init(void) +{ + int ret; + + ret = platform_driver_register(&mdp3_driver); + if (ret) { + pr_err("register mdp3 driver failed!\n"); + return ret; + } + + return 0; +} + +module_param_string(panel, mdss_mdp3_panel, MDSS_MAX_PANEL_LEN, 0600); +/* + * panel=:: + * where is "1"-lk/gcdb config or "0" non-lk/non-gcdb + * config; is dsi:0 + * is panel interface specific string + * Ex: This string is panel's device node name from DT + * for DSI interface + */ +MODULE_PARM_DESC(panel, "lk supplied panel selection string"); +module_init(mdp3_driver_init); diff --git a/drivers/video/fbdev/msm/mdp3.h b/drivers/video/fbdev/msm/mdp3.h new file mode 100644 index 0000000000000000000000000000000000000000..6fb39a73649dd5bc011829beda2bda836017fb89 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3.h @@ -0,0 +1,292 @@ +/* Copyright (c) 2013-2014, 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * 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 MDP3_H +#define MDP3_H + +#include +#include +#include +#include + +#include "mdss_dsi_clk.h" +#include "mdp3_dma.h" +#include "mdss_fb.h" +#include "mdss.h" + +#define MDP_VSYNC_CLK_RATE 19200000 +#define MDP_CORE_CLK_RATE_SVS 160000000 +#define MDP_CORE_CLK_RATE_SUPER_SVS 200000000 +#define MDP_CORE_CLK_RATE_MAX 307200000 + +#define CLK_FUDGE_NUM 12 +#define CLK_FUDGE_DEN 10 + +/* PPP cant work at SVS for panel res above qHD */ +#define SVS_MAX_PIXEL (540 * 960) + +#define KOFF_TIMEOUT_MS 84 +#define KOFF_TIMEOUT msecs_to_jiffies(KOFF_TIMEOUT_MS) +#define WAIT_DMA_TIMEOUT msecs_to_jiffies(84) + +/* + * MDP_DEINTERLACE & MDP_SHARPENING Flags are not valid for MDP3 + * so using them together for MDP_SMART_BLIT. + */ +#define MDP_SMART_BLIT 0xC0000000 + +#define BITS_PER_BYTE 8 +#define MDP_IMGTYPE_LIMIT1 0x100 +#define BITS_TO_BYTES(x) DIV_ROUND_UP(x, BITS_PER_BYTE) + +enum { + MDP3_CLK_AHB, + MDP3_CLK_AXI, + MDP3_CLK_MDP_SRC, + MDP3_CLK_MDP_CORE, + MDP3_CLK_VSYNC, + MDP3_CLK_DSI, + MDP3_MAX_CLK +}; + +enum { + MDP3_BUS_HANDLE, + MDP3_BUS_HANDLE_MAX, +}; + +enum { + MDP3_IOMMU_DOMAIN_UNSECURE, + MDP3_IOMMU_DOMAIN_SECURE, + MDP3_IOMMU_DOMAIN_MAX, +}; + +enum { + MDP3_IOMMU_CTX_MDP_0, + MDP3_IOMMU_CTX_MDP_1, + MDP3_IOMMU_CTX_MAX +}; + +/* Keep DSI entry in sync with mdss + * which is being used by DSI 6G + */ +enum { + MDP3_CLIENT_DMA_P, + MDP3_CLIENT_DSI = 1, + MDP3_CLIENT_PPP, + MDP3_CLIENT_IOMMU, + MDP3_CLIENT_MAX, +}; + +enum { + DI_PARTITION_NUM = 0, + DI_DOMAIN_NUM = 1, + DI_MAX, +}; + +struct mdp3_bus_handle_map { + struct msm_bus_vectors *bus_vector; + struct msm_bus_paths *usecases; + struct msm_bus_scale_pdata *scale_pdata; + int current_bus_idx; + int ref_cnt; + u64 restore_ab[MDP3_CLIENT_MAX]; + u64 restore_ib[MDP3_CLIENT_MAX]; + u64 ab[MDP3_CLIENT_MAX]; + u64 ib[MDP3_CLIENT_MAX]; + u32 handle; +}; + +struct mdp3_iommu_domain_map { + u32 domain_type; + char *client_name; + int npartitions; + int domain_idx; + struct iommu_domain *domain; +}; + +struct mdp3_iommu_ctx_map { + u32 ctx_type; + struct mdp3_iommu_domain_map *domain; + char *ctx_name; + struct device *ctx; + int attached; +}; + +struct mdp3_iommu_meta { + struct rb_node node; + struct ion_handle *handle; + struct rb_root iommu_maps; + struct kref ref; + struct sg_table *table; + struct dma_buf *dbuf; + int mapped_size; + unsigned long size; + dma_addr_t iova_addr; + unsigned long flags; +}; + +#define MDP3_MAX_INTR 28 + +struct mdp3_intr_cb { + void (*cb)(int type, void *); + void *data; +}; + +#define SMART_BLIT_RGB_EN 1 +#define SMART_BLIT_YUV_EN 2 + +struct mdp3_hw_resource { + struct platform_device *pdev; + u32 mdp_rev; + + struct mutex res_mutex; + + struct clk *clocks[MDP3_MAX_CLK]; + int clock_ref_count[MDP3_MAX_CLK]; + unsigned long dma_core_clk_request; + unsigned long ppp_core_clk_request; + struct mdss_hw mdp3_hw; + struct mdss_util_intf *mdss_util; + + char __iomem *mdp_base; + size_t mdp_reg_size; + + char __iomem *vbif_base; + size_t vbif_reg_size; + + struct mdp3_bus_handle_map *bus_handle; + + struct ion_client *ion_client; + struct mdp3_iommu_domain_map *domains; + struct mdp3_iommu_ctx_map *iommu_contexts; + unsigned int iommu_ref_cnt; + bool allow_iommu_update; + struct ion_handle *ion_handle; + struct mutex iommu_lock; + struct mutex fs_idle_pc_lock; + + struct mdp3_dma dma[MDP3_DMA_MAX]; + struct mdp3_intf intf[MDP3_DMA_OUTPUT_SEL_MAX]; + + struct rb_root iommu_root; + spinlock_t irq_lock; + u32 irq_ref_count[MDP3_MAX_INTR]; + u32 irq_mask; + int irq_ref_cnt; + struct mdp3_intr_cb callbacks[MDP3_MAX_INTR]; + u32 underrun_cnt; + + int irq_registered; + + unsigned long splash_mem_addr; + u32 splash_mem_size; + struct mdss_panel_cfg pan_cfg; + + int clk_prepare_count; + int cont_splash_en; + + bool batfet_required; + struct regulator *batfet; + struct regulator *vdd_cx; + struct regulator *fs; + bool fs_ena; + int clk_ena; + bool idle_pc_enabled; + bool idle_pc; + atomic_t active_intf_cnt; + u8 smart_blit_en; + bool solid_fill_vote_en; + struct list_head reg_bus_clist; + struct mutex reg_bus_lock; + + u32 max_bw; + + u8 ppp_formats[BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)]; + u8 dma_formats[BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)]; +}; + +struct mdp3_img_data { + dma_addr_t addr; + unsigned long len; + u32 offset; + u32 flags; + u32 padding; + int p_need; + struct ion_handle *srcp_ihdl; + u32 dir; + u32 domain; + bool mapped; + bool skip_detach; + struct fd srcp_f; + struct dma_buf *srcp_dma_buf; + struct dma_buf_attachment *srcp_attachment; + struct sg_table *srcp_table; + struct sg_table *tab_clone; +}; + +extern struct mdp3_hw_resource *mdp3_res; + +struct mdp3_dma *mdp3_get_dma_pipe(int capability); +struct mdp3_intf *mdp3_get_display_intf(int type); +void mdp3_irq_enable(int type); +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 dsi_clk); +int mdp3_res_update(int enable, int dsi_clk, int client); +int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota); +int mdp3_put_img(struct mdp3_img_data *data, int client); +int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, + int client); +int mdp3_iommu_enable(int client); +int mdp3_iommu_disable(int client); +int mdp3_iommu_is_attached(void); +void mdp3_free(struct msm_fb_data_type *mfd); +int mdp3_parse_dt_splash(struct msm_fb_data_type *mfd); +void mdp3_release_splash_memory(struct msm_fb_data_type *mfd); +int mdp3_create_sysfs_link(struct device *dev); +int mdp3_get_cont_spash_en(void); +int mdp3_get_mdp_dsi_clk(void); +int mdp3_put_mdp_dsi_clk(void); + +int mdp3_misr_set(struct mdp_misr *misr_req); +int mdp3_misr_get(struct mdp_misr *misr_resp); +void mdp3_enable_regulator(int enable); +void mdp3_check_dsi_ctrl_status(struct work_struct *work, + uint32_t interval); +int mdp3_dynamic_clock_gating_ctrl(int enable); +int mdp3_footswitch_ctrl(int enable); +int mdp3_qos_remapper_setup(struct mdss_panel_data *panel); +int mdp3_splash_done(struct mdss_panel_info *panel_info); +int mdp3_autorefresh_disable(struct mdss_panel_info *panel_info); +u64 mdp3_clk_round_off(u64 clk_rate); + +void mdp3_calc_dma_res(struct mdss_panel_info *panel_info, u64 *clk_rate, + u64 *ab, u64 *ib, uint32_t bpp); +void mdp3_clear_irq(u32 interrupt_mask); +int mdp3_enable_panic_ctrl(void); + +int mdp3_layer_pre_commit(struct msm_fb_data_type *mfd, + struct file *file, struct mdp_layer_commit_v1 *commit); +int mdp3_layer_atomic_validate(struct msm_fb_data_type *mfd, + struct file *file, struct mdp_layer_commit_v1 *commit); + +#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) +#define VBIF_REG_WRITE(off, val) writel_relaxed(val, mdp3_res->vbif_base + off) +#define VBIF_REG_READ(off) readl_relaxed(mdp3_res->vbif_base + off) + +#endif /* MDP3_H */ diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c new file mode 100644 index 0000000000000000000000000000000000000000..19c9a2bd4adba59a3b180c20bea9818b7197dbf1 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_ctrl.c @@ -0,0 +1,3024 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdp3_ctrl.h" +#include "mdp3.h" +#include "mdp3_ppp.h" +#include "mdss_smmu.h" +#include "mdss_sync.h" + +#define VSYNC_EXPIRE_TICK 4 + +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 int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable); +static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable); +static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd); +static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd, + struct mdp_rgb_lut_data *cfg); +static int mdp3_ctrl_lut_config(struct msm_fb_data_type *mfd, + struct mdp_rgb_lut_data *cfg); +static void mdp3_ctrl_pp_resume(struct msm_fb_data_type *mfd); + +u32 mdp_lut_inverse16[MDP_LUT_SIZE] = { +0, 65536, 32768, 21845, 16384, 13107, 10923, 9362, 8192, 7282, 6554, 5958, +5461, 5041, 4681, 4369, 4096, 3855, 3641, 3449, 3277, 3121, 2979, 2849, 2731, +2621, 2521, 2427, 2341, 2260, 2185, 2114, 2048, 1986, 1928, 1872, 1820, 1771, +1725, 1680, 1638, 1598, 1560, 1524, 1489, 1456, 1425, 1394, 1365, 1337, 1311, +1285, 1260, 1237, 1214, 1192, 1170, 1150, 1130, 1111, 1092, 1074, 1057, 1040, +1024, 1008, 993, 978, 964, 950, 936, 923, 910, 898, 886, 874, 862, 851, 840, +830, 819, 809, 799, 790, 780, 771, 762, 753, 745, 736, 728, 720, 712, 705, 697, +690, 683, 676, 669, 662, 655, 649, 643, 636, 630, 624, 618, 612, 607, 601, 596, +590, 585, 580, 575, 570, 565, 560, 555, 551, 546, 542, 537, 533, 529, 524, 520, +516, 512, 508, 504, 500, 496, 493, 489, 485, 482, 478, 475, 471, 468, 465, 462, +458, 455, 452, 449, 446, 443, 440, 437, 434, 431, 428, 426, 423, 420, 417, 415, +412, 410, 407, 405, 402, 400, 397, 395, 392, 390, 388, 386, 383, 381, 379, 377, +374, 372, 370, 368, 366, 364, 362, 360, 358, 356, 354, 352, 350, 349, 347, 345, +343, 341, 340, 338, 336, 334, 333, 331, 329, 328, 326, 324, 323, 321, 320, 318, +317, 315, 314, 312, 311, 309, 308, 306, 305, 303, 302, 301, 299, 298, 297, 295, +294, 293, 291, 290, 289, 287, 286, 285, 284, 282, 281, 280, 279, 278, 277, 275, +274, 273, 272, 271, 270, 269, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, +257}; + +static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq) +{ + bufq->count = 0; + bufq->push_idx = 0; + bufq->pop_idx = 0; +} + +void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq) +{ + int count = bufq->count; + + if (!count) + return; + + while (count-- && (bufq->pop_idx >= 0)) { + struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx]; + + bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE; + mdp3_put_img(data, MDP3_CLIENT_DMA_P); + } + bufq->count = 0; + bufq->push_idx = 0; + bufq->pop_idx = 0; +} + +int mdp3_bufq_push(struct mdp3_buffer_queue *bufq, + struct mdp3_img_data *data) +{ + if (bufq->count >= MDP3_MAX_BUF_QUEUE) { + pr_err("bufq full\n"); + return -EPERM; + } + + bufq->img_data[bufq->push_idx] = *data; + bufq->push_idx = (bufq->push_idx + 1) % MDP3_MAX_BUF_QUEUE; + bufq->count++; + return 0; +} + +static struct mdp3_img_data *mdp3_bufq_pop(struct mdp3_buffer_queue *bufq) +{ + struct mdp3_img_data *data; + + if (bufq->count == 0) + return NULL; + + data = &bufq->img_data[bufq->pop_idx]; + bufq->count--; + bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE; + return data; +} + +static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq) +{ + return bufq->count; +} + +void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses, + struct notifier_block *notifier) +{ + blocking_notifier_chain_register(&ses->notifier_head, notifier); +} + +void mdp3_ctrl_notifier_unregister(struct mdp3_session_data *ses, + struct notifier_block *notifier) +{ + blocking_notifier_chain_unregister(&ses->notifier_head, notifier); +} + +int mdp3_ctrl_notify(struct mdp3_session_data *ses, int event) +{ + return blocking_notifier_call_chain(&ses->notifier_head, event, ses); +} + +static void mdp3_dispatch_dma_done(struct kthread_work *work) +{ + struct mdp3_session_data *session; + int cnt = 0; + + pr_debug("%s\n", __func__); + session = container_of(work, struct mdp3_session_data, + dma_done_work); + if (!session) + return; + + cnt = atomic_read(&session->dma_done_cnt); + MDSS_XLOG(cnt); + while (cnt > 0) { + mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE); + atomic_dec(&session->dma_done_cnt); + cnt--; + } +} + +static void mdp3_dispatch_clk_off(struct work_struct *work) +{ + struct mdp3_session_data *session; + int rc; + bool dmap_busy; + int retry_count = 2; + + pr_debug("%s\n", __func__); + MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__); + session = container_of(work, struct mdp3_session_data, + clk_off_work); + if (!session) + return; + + mutex_lock(&session->lock); + if (session->vsync_enabled || + atomic_read(&session->vsync_countdown) > 0) { + mutex_unlock(&session->lock); + pr_debug("%s: Ignoring clk shut down\n", __func__); + MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__); + return; + } + + if (session->intf->active) { +retry_dma_done: + rc = wait_for_completion_timeout(&session->dma_completion, + WAIT_DMA_TIMEOUT); + if (rc <= 0) { + struct mdss_panel_data *panel; + + panel = session->panel; + pr_debug("cmd kickoff timed out (%d)\n", rc); + dmap_busy = session->dma->busy(); + if (dmap_busy) { + if (--retry_count) { + pr_err("dmap is busy, retry %d\n", + retry_count); + goto retry_dma_done; + } + pr_err("dmap is still busy, bug_on\n"); + WARN_ON(1); + } else { + pr_debug("dmap is not busy, continue\n"); + } + } + } + mdp3_ctrl_vsync_enable(session->mfd, 0); + mdp3_ctrl_clk_enable(session->mfd, 0); + MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__); + mutex_unlock(&session->lock); +} + +static void mdp3_vsync_retire_handle_vsync(void *arg) +{ + struct mdp3_session_data *mdp3_session; + + mdp3_session = (struct mdp3_session_data *)arg; + + if (!mdp3_session) { + pr_warn("Invalid handle for vsync\n"); + return; + } + + schedule_work(&mdp3_session->retire_work); +} + +static void mdp3_vsync_retire_signal(struct msm_fb_data_type *mfd, int val) +{ + struct mdp3_session_data *mdp3_session; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + + mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex); + if (mdp3_session->retire_cnt > 0) { + mdss_inc_timeline(mfd->mdp_sync_pt_data.timeline_retire, val); + mdp3_session->retire_cnt -= min(val, mdp3_session->retire_cnt); + } + mutex_unlock(&mfd->mdp_sync_pt_data.sync_mutex); +} + +static void mdp3_vsync_retire_work_handler(struct work_struct *work) +{ + struct mdp3_session_data *mdp3_session = + container_of(work, struct mdp3_session_data, retire_work); + + if (!mdp3_session) + return; + + mdp3_vsync_retire_signal(mdp3_session->mfd, 1); +} + +void mdp3_hist_intr_notify(struct mdp3_dma *dma) +{ + dma->hist_events++; + sysfs_notify_dirent(dma->hist_event_sd); + pr_debug("%s:: hist_events = %u\n", __func__, dma->hist_events); +} + +void vsync_notify_handler(void *arg) +{ + struct mdp3_session_data *session = (struct mdp3_session_data *)arg; + + session->vsync_time = ktime_get(); + MDSS_XLOG(ktime_to_ms(session->vsync_time)); + sysfs_notify_dirent(session->vsync_event_sd); +} + +void dma_done_notify_handler(void *arg) +{ + struct mdp3_session_data *session = (struct mdp3_session_data *)arg; + + atomic_inc(&session->dma_done_cnt); + kthread_queue_work(&session->worker, &session->dma_done_work); + complete_all(&session->dma_completion); +} + +void vsync_count_down(void *arg) +{ + struct mdp3_session_data *session = (struct mdp3_session_data *)arg; + + /* We are counting down to turn off clocks */ + if (atomic_read(&session->vsync_countdown) > 0) + atomic_dec(&session->vsync_countdown); + if (atomic_read(&session->vsync_countdown) == 0) + schedule_work(&session->clk_off_work); +} + +void mdp3_ctrl_reset_countdown(struct mdp3_session_data *session, + struct msm_fb_data_type *mfd) +{ + if (mdp3_ctrl_get_intf_type(mfd) == MDP3_DMA_OUTPUT_SEL_DSI_CMD) + atomic_set(&session->vsync_countdown, VSYNC_EXPIRE_TICK); +} + +static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable) +{ + struct mdp3_session_data *mdp3_session; + struct mdp3_notification vsync_client; + struct mdp3_notification *arg = NULL; + bool mod_vsync_timer = false; + + pr_debug("%s =%d\n", __func__, enable); + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma || + !mdp3_session->intf) + return -ENODEV; + + if (!mdp3_session->status) { + pr_debug("fb%d is not on yet", mfd->index); + return -EINVAL; + } + if (enable) { + vsync_client.handler = vsync_notify_handler; + vsync_client.arg = mdp3_session; + arg = &vsync_client; + } else if (atomic_read(&mdp3_session->vsync_countdown) > 0) { + /* + * Now that vsync is no longer needed we will + * shutdown dsi clocks as soon as cnt down == 0 + * for cmd mode panels + */ + vsync_client.handler = vsync_count_down; + vsync_client.arg = mdp3_session; + arg = &vsync_client; + enable = 1; + } + + if (enable) { + if (mdp3_session->status == 1 && + (mdp3_session->vsync_before_commit || + !mdp3_session->intf->active)) { + mod_vsync_timer = true; + } else if (!mdp3_session->clk_on) { + /* Enable clocks before enabling the vsync interrupt */ + mdp3_ctrl_reset_countdown(mdp3_session, mfd); + mdp3_ctrl_clk_enable(mfd, 1); + } + } + + mdp3_clk_enable(1, 0); + mdp3_session->dma->vsync_enable(mdp3_session->dma, arg); + mdp3_clk_enable(0, 0); + + /* + * Need to fake vsync whenever dsi interface is not + * active or when dsi clocks are currently off + */ + if (mod_vsync_timer) { + mod_timer(&mdp3_session->vsync_timer, + jiffies + msecs_to_jiffies(mdp3_session->vsync_period)); + } else if (!enable) { + del_timer(&mdp3_session->vsync_timer); + } + + return 0; +} + +void mdp3_vsync_timer_func(unsigned long arg) +{ + struct mdp3_session_data *session = (struct mdp3_session_data *)arg; + + if (session->status == 1 && (session->vsync_before_commit || + !session->intf->active)) { + pr_debug("%s trigger\n", __func__); + vsync_notify_handler(session); + mod_timer(&session->vsync_timer, + jiffies + msecs_to_jiffies(session->vsync_period)); + } +} + +static int mdp3_ctrl_async_blit_req(struct msm_fb_data_type *mfd, + void __user *p) +{ + struct mdp_async_blit_req_list req_list_header; + int rc, count; + void __user *p_req; + + if (copy_from_user(&req_list_header, p, sizeof(req_list_header))) + return -EFAULT; + p_req = p + sizeof(req_list_header); + count = req_list_header.count; + if (count < 0 || count >= MAX_BLIT_REQ) + return -EINVAL; + rc = mdp3_ppp_parse_req(p_req, &req_list_header, 1); + if (!rc) + rc = copy_to_user(p, &req_list_header, sizeof(req_list_header)); + return rc; +} + +static int mdp3_ctrl_blit_req(struct msm_fb_data_type *mfd, void __user *p) +{ + struct mdp_async_blit_req_list req_list_header; + int rc, count; + void __user *p_req; + + if (copy_from_user(&(req_list_header.count), p, + sizeof(struct mdp_blit_req_list))) + return -EFAULT; + p_req = p + sizeof(struct mdp_blit_req_list); + count = req_list_header.count; + if (count < 0 || count >= MAX_BLIT_REQ) + return -EINVAL; + req_list_header.sync.acq_fen_fd_cnt = 0; + rc = mdp3_ppp_parse_req(p_req, &req_list_header, 0); + return rc; +} + +static ssize_t mdp3_bl_show_event(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdp3_session_data *mdp3_session = NULL; + int ret; + + if (!mfd || !mfd->mdp.private1) + return -EAGAIN; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + ret = scnprintf(buf, PAGE_SIZE, "%d\n", mdp3_session->bl_events); + return ret; +} + +static ssize_t mdp3_hist_show_event(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdp3_session_data *mdp3_session = NULL; + struct mdp3_dma *dma = NULL; + int ret; + + if (!mfd || !mfd->mdp.private1) + return -EAGAIN; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + dma = (struct mdp3_dma *)mdp3_session->dma; + ret = scnprintf(buf, PAGE_SIZE, "%d\n", dma->hist_events); + return ret; +} + +static ssize_t mdp3_vsync_show_event(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdp3_session_data *mdp3_session = NULL; + u64 vsync_ticks; + int rc; + + if (!mfd || !mfd->mdp.private1) + return -EAGAIN; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + + vsync_ticks = ktime_to_ns(mdp3_session->vsync_time); + + pr_debug("fb%d vsync=%llu\n", mfd->index, vsync_ticks); + rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks); + return rc; +} + +static ssize_t mdp3_packpattern_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdp3_session_data *mdp3_session = NULL; + int rc; + u32 pattern = 0; + + if (!mfd || !mfd->mdp.private1) + return -EAGAIN; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + + pattern = mdp3_session->dma->output_config.pack_pattern; + + /* If pattern was found to be 0 then get pattern for fb imagetype */ + if (!pattern) + pattern = mdp3_ctrl_get_pack_pattern(mfd->fb_imgType); + + pr_debug("fb%d pack_pattern c= %d.", mfd->index, pattern); + rc = scnprintf(buf, PAGE_SIZE, "packpattern=%d\n", pattern); + return rc; +} + +static ssize_t mdp3_dyn_pu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + struct mdp3_session_data *mdp3_session = NULL; + int ret, state; + + if (!mfd || !mfd->mdp.private1) + return -EAGAIN; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + state = (mdp3_session->dyn_pu_state >= 0) ? + mdp3_session->dyn_pu_state : -1; + ret = scnprintf(buf, PAGE_SIZE, "%d", state); + return ret; +} + +static ssize_t mdp3_dyn_pu_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + struct mdp3_session_data *mdp3_session = NULL; + int ret, dyn_pu; + + if (!mfd || !mfd->mdp.private1) + return -EAGAIN; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + ret = kstrtoint(buf, 10, &dyn_pu); + if (ret) { + pr_err("Invalid input for partial update: ret = %d\n", ret); + return ret; + } + + mdp3_session->dyn_pu_state = dyn_pu; + sysfs_notify(&dev->kobj, NULL, "dyn_pu"); + return count; +} + +static DEVICE_ATTR(hist_event, 0444, mdp3_hist_show_event, NULL); +static DEVICE_ATTR(bl_event, 0444, mdp3_bl_show_event, NULL); +static DEVICE_ATTR(vsync_event, 0444, mdp3_vsync_show_event, NULL); +static DEVICE_ATTR(packpattern, 0444, mdp3_packpattern_show, NULL); +static DEVICE_ATTR(dyn_pu, 0664, mdp3_dyn_pu_show, + mdp3_dyn_pu_store); + +static struct attribute *generic_attrs[] = { + &dev_attr_packpattern.attr, + &dev_attr_dyn_pu.attr, + &dev_attr_hist_event.attr, + &dev_attr_bl_event.attr, + NULL, +}; + +static struct attribute *vsync_fs_attrs[] = { + &dev_attr_vsync_event.attr, + NULL, +}; + +static struct attribute_group vsync_fs_attr_group = { + .attrs = vsync_fs_attrs, +}; + +static struct attribute_group generic_attr_group = { + .attrs = generic_attrs, +}; + +static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable) +{ + struct mdp3_session_data *session; + struct mdss_panel_data *panel; + struct dsi_panel_clk_ctrl clk_ctrl; + int rc = 0; + + pr_debug("%s %d\n", __func__, enable); + + session = mfd->mdp.private1; + panel = session->panel; + + if (!panel->event_handler) + return 0; + + if ((enable && session->clk_on == 0) || + (!enable && session->clk_on == 1)) { + clk_ctrl.client = DSI_CLK_REQ_MDP_CLIENT; + clk_ctrl.state = enable; + rc = panel->event_handler(panel, + MDSS_EVENT_PANEL_CLK_CTRL, (void *)&clk_ctrl); + rc |= mdp3_res_update(enable, 1, MDP3_CLIENT_DMA_P); + } else { + pr_debug("enable = %d, clk_on=%d\n", enable, session->clk_on); + } + + session->clk_on = enable; + return rc; +} + +static int mdp3_ctrl_res_req_bus(struct msm_fb_data_type *mfd, int status) +{ + int rc = 0; + + if (status) { + u64 ab = 0; + u64 ib = 0; + + mdp3_calc_dma_res(mfd->panel_info, NULL, &ab, &ib, + ppp_bpp(mfd->fb_imgType)); + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib); + } else { + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0); + } + return rc; +} + +static int mdp3_ctrl_res_req_clk(struct msm_fb_data_type *mfd, int status) +{ + int rc = 0; + + if (status) { + u64 mdp_clk_rate = 0; + + mdp3_calc_dma_res(mfd->panel_info, &mdp_clk_rate, + NULL, NULL, 0); + + mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, mdp_clk_rate, + MDP3_CLIENT_DMA_P); + mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE, + MDP3_CLIENT_DMA_P); + + rc = mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P); + if (rc) { + pr_err("mdp3 clk enable fail\n"); + return rc; + } + } else { + rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P); + if (rc) + pr_err("mdp3 clk disable fail\n"); + } + return rc; +} + +static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd) +{ + int type; + + switch (mfd->panel.type) { + case MIPI_VIDEO_PANEL: + type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO; + break; + case MIPI_CMD_PANEL: + type = MDP3_DMA_OUTPUT_SEL_DSI_CMD; + break; + case LCDC_PANEL: + type = MDP3_DMA_OUTPUT_SEL_LCDC; + break; + default: + type = MDP3_DMA_OUTPUT_SEL_MAX; + } + return type; +} + +int mdp3_ctrl_get_source_format(u32 imgType) +{ + int format; + + switch (imgType) { + case MDP_RGB_565: + format = MDP3_DMA_IBUF_FORMAT_RGB565; + break; + case MDP_RGB_888: + format = MDP3_DMA_IBUF_FORMAT_RGB888; + break; + case MDP_ARGB_8888: + case MDP_RGBA_8888: + format = MDP3_DMA_IBUF_FORMAT_XRGB8888; + break; + default: + format = MDP3_DMA_IBUF_FORMAT_UNDEFINED; + } + return format; +} + +int mdp3_ctrl_get_pack_pattern(u32 imgType) +{ + int packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_RGB; + + if (imgType == MDP_RGBA_8888 || imgType == MDP_RGB_888) + packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_BGR; + return packPattern; +} + +static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd, + struct mdp3_intf *intf) +{ + int rc = 0; + struct mdp3_intf_cfg cfg; + struct mdp3_video_intf_cfg *video = &cfg.video; + struct mdss_panel_info *p = mfd->panel_info; + int h_back_porch = p->lcdc.h_back_porch; + int h_front_porch = p->lcdc.h_front_porch; + int w = p->xres; + int v_back_porch = p->lcdc.v_back_porch; + int v_front_porch = p->lcdc.v_front_porch; + int h = p->yres; + int h_sync_skew = p->lcdc.hsync_skew; + int h_pulse_width = p->lcdc.h_pulse_width; + int v_pulse_width = p->lcdc.v_pulse_width; + int hsync_period = h_front_porch + h_back_porch + w + h_pulse_width; + int vsync_period = v_front_porch + v_back_porch + h + v_pulse_width; + struct mdp3_session_data *mdp3_session; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + vsync_period *= hsync_period; + + cfg.type = mdp3_ctrl_get_intf_type(mfd); + if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO || + cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC) { + video->hsync_period = hsync_period; + video->hsync_pulse_width = h_pulse_width; + video->vsync_period = vsync_period; + video->vsync_pulse_width = v_pulse_width * hsync_period; + video->display_start_x = h_back_porch + h_pulse_width; + video->display_end_x = hsync_period - h_front_porch - 1; + video->display_start_y = + (v_back_porch + v_pulse_width) * hsync_period; + video->display_end_y = + vsync_period - v_front_porch * hsync_period - 1; + video->active_start_x = video->display_start_x; + video->active_end_x = video->display_end_x; + video->active_h_enable = true; + video->active_start_y = video->display_start_y; + video->active_end_y = video->display_end_y; + video->active_v_enable = true; + video->hsync_skew = h_sync_skew; + video->hsync_polarity = 1; + video->vsync_polarity = 1; + video->de_polarity = 1; + video->underflow_color = p->lcdc.underflow_clr; + } else if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + cfg.dsi_cmd.primary_dsi_cmd_id = 0; + cfg.dsi_cmd.secondary_dsi_cmd_id = 1; + cfg.dsi_cmd.dsi_cmd_tg_intf_sel = 0; + } else + return -EINVAL; + + if (!(mdp3_session->in_splash_screen)) { + if (intf->config) + rc = intf->config(intf, &cfg); + else + rc = -EINVAL; + } + return rc; +} + +static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, + struct mdp3_dma *dma) +{ + int rc; + struct mdss_panel_info *panel_info = mfd->panel_info; + struct fb_info *fbi = mfd->fbi; + struct fb_fix_screeninfo *fix; + struct fb_var_screeninfo *var; + struct mdp3_dma_output_config outputConfig; + struct mdp3_dma_source sourceConfig; + int frame_rate = mfd->panel_info->mipi.frame_rate; + int vbp, vfp, vspw; + int vtotal, vporch; + struct mdp3_notification dma_done_callback; + struct mdp3_tear_check te; + struct mdp3_session_data *mdp3_session; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + + vbp = panel_info->lcdc.v_back_porch; + vfp = panel_info->lcdc.v_front_porch; + vspw = panel_info->lcdc.v_pulse_width; + vporch = vbp + vfp + vspw; + vtotal = vporch + panel_info->yres; + + fix = &fbi->fix; + var = &fbi->var; + + sourceConfig.width = panel_info->xres; + sourceConfig.height = panel_info->yres; + sourceConfig.x = 0; + sourceConfig.y = 0; + sourceConfig.buf = mfd->iova; + sourceConfig.vporch = vporch; + sourceConfig.vsync_count = + MDP_VSYNC_CLK_RATE / (frame_rate * vtotal); + + outputConfig.dither_en = 0; + outputConfig.out_sel = mdp3_ctrl_get_intf_type(mfd); + outputConfig.bit_mask_polarity = 0; + outputConfig.color_components_flip = 0; + outputConfig.pack_align = MDP3_DMA_OUTPUT_PACK_ALIGN_LSB; + outputConfig.color_comp_out_bits = (MDP3_DMA_OUTPUT_COMP_BITS_8 << 4) | + (MDP3_DMA_OUTPUT_COMP_BITS_8 << 2)| + MDP3_DMA_OUTPUT_COMP_BITS_8; + + if (dma->update_src_cfg) { + /* configuration has been updated through PREPARE call */ + sourceConfig.format = dma->source_config.format; + sourceConfig.stride = dma->source_config.stride; + outputConfig.pack_pattern = dma->output_config.pack_pattern; + } else { + sourceConfig.format = + mdp3_ctrl_get_source_format(mfd->fb_imgType); + outputConfig.pack_pattern = + mdp3_ctrl_get_pack_pattern(mfd->fb_imgType); + sourceConfig.stride = fix->line_length; + } + + te.frame_rate = panel_info->mipi.frame_rate; + te.hw_vsync_mode = panel_info->mipi.hw_vsync_mode; + te.tear_check_en = panel_info->te.tear_check_en; + te.sync_cfg_height = panel_info->te.sync_cfg_height; + te.vsync_init_val = panel_info->te.vsync_init_val; + te.sync_threshold_start = panel_info->te.sync_threshold_start; + te.sync_threshold_continue = panel_info->te.sync_threshold_continue; + te.start_pos = panel_info->te.start_pos; + te.rd_ptr_irq = panel_info->te.rd_ptr_irq; + te.refx100 = panel_info->te.refx100; + + if (dma->dma_config) { + if (!panel_info->partial_update_enabled) { + dma->roi.w = sourceConfig.width; + dma->roi.h = sourceConfig.height; + dma->roi.x = sourceConfig.x; + dma->roi.y = sourceConfig.y; + } + rc = dma->dma_config(dma, &sourceConfig, &outputConfig, + mdp3_session->in_splash_screen); + } else { + pr_err("%s: dma config failed\n", __func__); + rc = -EINVAL; + } + + if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + if (dma->dma_sync_config) + rc = dma->dma_sync_config(dma, + &sourceConfig, &te); + else + rc = -EINVAL; + dma_done_callback.handler = dma_done_notify_handler; + dma_done_callback.arg = mfd->mdp.private1; + dma->dma_done_notifier(dma, &dma_done_callback); + } + + return rc; +} + +static int mdp3_ctrl_on(struct msm_fb_data_type *mfd) +{ + int rc = 0; + struct mdp3_session_data *mdp3_session; + struct mdss_panel_data *panel; + + pr_debug("%s\n", __func__); + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma || + !mdp3_session->intf) { + pr_err("%s no device\n", __func__); + return -ENODEV; + } + mutex_lock(&mdp3_session->lock); + + MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__, mfd->panel_power_state); + panel = mdp3_session->panel; + /* make sure DSI host is initialized properly */ + if (panel) { + pr_debug("%s : dsi host init, power state = %d Splash %d\n", + __func__, mfd->panel_power_state, + mdp3_session->in_splash_screen); + if (mdss_fb_is_power_on_lp(mfd) || + mdp3_session->in_splash_screen) { + /* Turn on panel so that it can exit low power mode */ + mdp3_clk_enable(1, 0); + rc = panel->event_handler(panel, + MDSS_EVENT_LINK_READY, NULL); + rc |= panel->event_handler(panel, + MDSS_EVENT_UNBLANK, NULL); + rc |= panel->event_handler(panel, + MDSS_EVENT_PANEL_ON, NULL); + if (mdss_fb_is_power_on_ulp(mfd)) + rc |= mdp3_enable_panic_ctrl(); + mdp3_clk_enable(0, 0); + } + } + + if (mdp3_session->status) { + pr_debug("fb%d is on already\n", mfd->index); + MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__, mfd->panel_power_state); + goto end; + } + + if (mdp3_session->intf->active) { + pr_debug("continuous splash screen, initialized already\n"); + mdp3_session->status = 1; + goto end; + } + + /* + * Get a reference to the runtime pm device. + * If idle pc feature is enabled, it will be released + * at end of this routine else, when device is turned off. + */ + pm_runtime_get_sync(&mdp3_res->pdev->dev); + + /* Increment the overlay active count */ + atomic_inc(&mdp3_res->active_intf_cnt); + mdp3_ctrl_notifier_register(mdp3_session, + &mdp3_session->mfd->mdp_sync_pt_data.notifier); + + /* request bus bandwidth before DSI DMA traffic */ + rc = mdp3_ctrl_res_req_bus(mfd, 1); + if (rc) { + pr_err("fail to request bus resource\n"); + goto on_error; + } + + rc = mdp3_dynamic_clock_gating_ctrl(0); + if (rc) { + pr_err("fail to disable dynamic clock gating\n"); + goto on_error; + } + mdp3_qos_remapper_setup(panel); + + rc = mdp3_ctrl_res_req_clk(mfd, 1); + if (rc) { + pr_err("fail to request mdp clk resource\n"); + goto on_error; + } + + if (panel->event_handler) { + rc = panel->event_handler(panel, MDSS_EVENT_LINK_READY, NULL); + rc |= panel->event_handler(panel, MDSS_EVENT_UNBLANK, NULL); + rc |= panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL); + if (panel->panel_info.type == MIPI_CMD_PANEL) { + struct dsi_panel_clk_ctrl clk_ctrl; + + clk_ctrl.state = MDSS_DSI_CLK_ON; + clk_ctrl.client = DSI_CLK_REQ_MDP_CLIENT; + rc |= panel->event_handler(panel, + MDSS_EVENT_PANEL_CLK_CTRL, + (void *)&clk_ctrl); + } + } + if (rc) { + pr_err("fail to turn on the panel\n"); + goto on_error; + } + + rc = mdp3_ctrl_dma_init(mfd, mdp3_session->dma); + if (rc) { + pr_err("dma init failed\n"); + goto on_error; + } + + rc = mdp3_ppp_init(); + if (rc) { + pr_err("ppp init failed\n"); + goto on_error; + } + + rc = mdp3_ctrl_intf_init(mfd, mdp3_session->intf); + if (rc) { + pr_err("display interface init failed\n"); + goto on_error; + } + mdp3_session->clk_on = 1; + + mdp3_session->first_commit = true; + if (mfd->panel_info->panel_dead) + mdp3_session->esd_recovery = true; + + mdp3_session->status = 1; + + mdp3_ctrl_pp_resume(mfd); + MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__, mfd->panel_power_state); +on_error: + if (rc || (mdp3_res->idle_pc_enabled && + (mfd->panel_info->type == MIPI_CMD_PANEL))) { + if (rc) { + pr_err("Failed to turn on fb%d\n", mfd->index); + atomic_dec(&mdp3_res->active_intf_cnt); + } + pm_runtime_put(&mdp3_res->pdev->dev); + } +end: + mutex_unlock(&mdp3_session->lock); + return rc; +} + +static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) +{ + int rc = 0; + bool intf_stopped = true; + struct mdp3_session_data *mdp3_session; + struct mdss_panel_data *panel; + + pr_debug("%s\n", __func__); + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma || + !mdp3_session->intf) { + pr_err("mdp3_ctrl_on no device"); + return -ENODEV; + } + + /* + * Keep a reference to the runtime pm until the overlay is turned + * off, and then release this last reference at the end. This will + * help in distinguishing between idle power collapse versus suspend + * power collapse + */ + pm_runtime_get_sync(&mdp3_res->pdev->dev); + + MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__, mdss_fb_is_power_on_ulp(mfd), + mfd->panel_power_state); + panel = mdp3_session->panel; + mutex_lock(&mdp3_session->lock); + + pr_debug("Requested power state = %d\n", mfd->panel_power_state); + if (mdss_fb_is_power_on_lp(mfd)) { + /* + * Transition to low power + * As display updates are expected in low power mode, + * keep the interface and clocks on. + */ + intf_stopped = false; + } else { + /* Transition to display off */ + if (!mdp3_session->status) { + pr_debug("fb%d is off already", mfd->index); + goto off_error; + } + if (panel && panel->set_backlight) + panel->set_backlight(panel, 0); + } + + /* + * While transitioning from interactive to low power, + * events need to be sent to the interface so that the + * panel can be configured in low power mode + */ + if (panel->event_handler) + rc = panel->event_handler(panel, MDSS_EVENT_BLANK, + (void *) (long int)mfd->panel_power_state); + if (rc) + pr_err("EVENT_BLANK error (%d)\n", rc); + + if (intf_stopped) { + if (!mdp3_session->clk_on) + mdp3_ctrl_clk_enable(mfd, 1); + /* PP related programming for ctrl off */ + mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P); + mutex_lock(&mdp3_session->dma->pp_lock); + mdp3_session->dma->ccs_config.ccs_dirty = false; + mdp3_session->dma->lut_config.lut_dirty = false; + mutex_unlock(&mdp3_session->dma->pp_lock); + + rc = mdp3_session->dma->stop(mdp3_session->dma, + mdp3_session->intf); + if (rc) + pr_debug("fail to stop the MDP3 dma\n"); + /* Wait to ensure TG to turn off */ + msleep(20); + mfd->panel_info->cont_splash_enabled = 0; + + /* Disable Auto refresh once continuous splash disabled */ + mdp3_autorefresh_disable(mfd->panel_info); + mdp3_splash_done(mfd->panel_info); + + mdp3_irq_deregister(); + } + + if (panel->event_handler) + rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, + (void *) (long int)mfd->panel_power_state); + if (rc) + pr_err("EVENT_PANEL_OFF error (%d)\n", rc); + + if (intf_stopped) { + if (mdp3_session->clk_on) { + pr_debug("%s stop clock\n", __func__); + if (panel->event_handler && + (panel->panel_info.type == MIPI_CMD_PANEL)) { + struct dsi_panel_clk_ctrl clk_ctrl; + + clk_ctrl.state = MDSS_DSI_CLK_OFF; + clk_ctrl.client = DSI_CLK_REQ_MDP_CLIENT; + rc |= panel->event_handler(panel, + MDSS_EVENT_PANEL_CLK_CTRL, + (void *)&clk_ctrl); + } + + rc = mdp3_dynamic_clock_gating_ctrl(1); + rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P); + if (rc) + pr_err("mdp clock resource release failed\n"); + } + + mdp3_ctrl_notifier_unregister(mdp3_session, + &mdp3_session->mfd->mdp_sync_pt_data.notifier); + + mdp3_session->vsync_enabled = 0; + atomic_set(&mdp3_session->vsync_countdown, 0); + atomic_set(&mdp3_session->dma_done_cnt, 0); + mdp3_session->clk_on = 0; + mdp3_session->in_splash_screen = 0; + mdp3_res->solid_fill_vote_en = false; + mdp3_session->status = 0; + if (atomic_dec_return(&mdp3_res->active_intf_cnt) != 0) { + pr_warn("active_intf_cnt unbalanced\n"); + atomic_set(&mdp3_res->active_intf_cnt, 0); + } + /* + * Release the pm runtime reference held when + * idle pc feature is not enabled + */ + if (!mdp3_res->idle_pc_enabled || + (mfd->panel_info->type != MIPI_CMD_PANEL)) { + rc = pm_runtime_put(&mdp3_res->pdev->dev); + if (rc) + pr_err("%s: pm_runtime_put failed (rc %d)\n", + __func__, rc); + } + mdp3_bufq_deinit(&mdp3_session->bufq_out); + if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) { + mdp3_session->overlay.id = MSMFB_NEW_REQUEST; + mdp3_bufq_deinit(&mdp3_session->bufq_in); + } + } + + if (mdss_fb_is_power_on_ulp(mfd) && + (mfd->panel.type == MIPI_CMD_PANEL)) { + pr_debug("%s: Disable MDP3 clocks in ULP\n", __func__); + if (!mdp3_session->clk_on) + mdp3_ctrl_clk_enable(mfd, 1); + /* + * STOP DMA transfer first and signal vsync notification + * Before releasing the resource in ULP state. + */ + rc = mdp3_session->dma->stop(mdp3_session->dma, + mdp3_session->intf); + if (rc) + pr_warn("fail to stop the MDP3 dma in ULP\n"); + /* Wait to ensure TG to turn off */ + msleep(20); + /* + * Handle ULP request initiated from fb_pm_suspend. + * For ULP panel power state disabling vsync and set + * vsync_count to zero and Turn off MDP3 clocks + */ + atomic_set(&mdp3_session->vsync_countdown, 0); + mdp3_session->vsync_enabled = 0; + mdp3_ctrl_vsync_enable(mdp3_session->mfd, 0); + mdp3_ctrl_clk_enable(mdp3_session->mfd, 0); + } +off_error: + MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__); + mutex_unlock(&mdp3_session->lock); + /* Release the last reference to the runtime device */ + pm_runtime_put(&mdp3_res->pdev->dev); + + return 0; +} + +int mdp3_ctrl_reset(struct msm_fb_data_type *mfd) +{ + int rc = 0; + struct mdp3_session_data *mdp3_session; + struct mdp3_dma *mdp3_dma; + struct mdss_panel_data *panel; + struct mdp3_notification vsync_client; + + pr_debug("%s\n", __func__); + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma || + !mdp3_session->intf) { + pr_err("%s no device\n", __func__); + return -ENODEV; + } + + panel = mdp3_session->panel; + mdp3_dma = mdp3_session->dma; + mutex_lock(&mdp3_session->lock); + pr_debug("%s idle_pc %s FS_EN %s\n", + __func__, + mdp3_res->idle_pc ? "True":"False", + mdp3_res->fs_ena ? "True":"False"); + if (mdp3_res->idle_pc) { + mdp3_clk_enable(1, 0); + mdp3_dynamic_clock_gating_ctrl(0); + mdp3_qos_remapper_setup(panel); + } + + /*Map the splash addr for VIDEO mode panel before smmu attach*/ + if ((mfd->panel.type == MIPI_VIDEO_PANEL) && + (mdp3_session->in_splash_screen)) { + rc = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE, + mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_size, + IOMMU_READ | IOMMU_NOEXEC); + } + + rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); + if (rc) { + pr_err("fail to attach dma iommu\n"); + if (mdp3_res->idle_pc) + mdp3_clk_enable(0, 0); + goto reset_error; + } + + vsync_client = mdp3_dma->vsync_client; + + mdp3_ctrl_intf_init(mfd, mdp3_session->intf); + mdp3_ctrl_dma_init(mfd, mdp3_dma); + mdp3_ppp_init(); + mdp3_ctrl_pp_resume(mfd); + if (vsync_client.handler) + mdp3_dma->vsync_enable(mdp3_dma, &vsync_client); + + if (!mdp3_res->idle_pc) { + mdp3_session->first_commit = true; + mfd->panel_info->cont_splash_enabled = 0; + mdp3_session->in_splash_screen = 0; + mdp3_splash_done(mfd->panel_info); + /* Disable Auto refresh */ + mdp3_autorefresh_disable(mfd->panel_info); + } else { + mdp3_res->idle_pc = false; + mdp3_clk_enable(0, 0); + mdp3_iommu_disable(MDP3_CLIENT_DMA_P); + } + +reset_error: + mutex_unlock(&mdp3_session->lock); + return rc; +} + +static int mdp3_overlay_get(struct msm_fb_data_type *mfd, + struct mdp_overlay *req) +{ + int rc = 0; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + + mutex_lock(&mdp3_session->lock); + + if (mdp3_session->overlay.id == req->id) + *req = mdp3_session->overlay; + else + rc = -EINVAL; + + mutex_unlock(&mdp3_session->lock); + + return rc; +} + +static int mdp3_overlay_set(struct msm_fb_data_type *mfd, + struct mdp_overlay *req) +{ + int rc = 0; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct mdp3_dma *dma = mdp3_session->dma; + struct fb_fix_screeninfo *fix; + struct fb_info *fbi = mfd->fbi; + int stride; + int format; + + fix = &fbi->fix; + stride = req->src.width * ppp_bpp(req->src.format); + format = mdp3_ctrl_get_source_format(req->src.format); + + + if (mdp3_session->overlay.id != req->id) + pr_err("overlay was not released, continue to recover\n"); + /* + * A change in overlay structure will always come with + * MSMFB_NEW_REQUEST for MDP3 + */ + if (req->id == MSMFB_NEW_REQUEST) { + mutex_lock(&mdp3_session->lock); + if (dma->source_config.stride != stride || + dma->source_config.format != format) { + dma->source_config.format = format; + dma->source_config.stride = stride; + dma->output_config.pack_pattern = + mdp3_ctrl_get_pack_pattern(req->src.format); + dma->update_src_cfg = true; + } + mdp3_session->overlay = *req; + mdp3_session->overlay.id = 1; + req->id = 1; + mutex_unlock(&mdp3_session->lock); + } + + return rc; +} + +static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx) +{ + int rc = 0; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct fb_info *fbi = mfd->fbi; + struct fb_fix_screeninfo *fix; + int format; + + fix = &fbi->fix; + format = mdp3_ctrl_get_source_format(mfd->fb_imgType); + 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); + } else { + rc = -EINVAL; + } + + mutex_unlock(&mdp3_session->lock); + + return rc; +} + +static int mdp3_overlay_queue_buffer(struct msm_fb_data_type *mfd, + struct msmfb_overlay_data *req) +{ + int rc; + bool is_panel_type_cmd = false; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct msmfb_data *img = &req->data; + struct mdp3_img_data data; + struct mdp3_dma *dma = mdp3_session->dma; + + memset(&data, 0, sizeof(struct mdp3_img_data)); + if (mfd->panel.type == MIPI_CMD_PANEL) + is_panel_type_cmd = true; + if (is_panel_type_cmd) { + rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); + if (rc) { + pr_err("fail to enable iommu\n"); + return rc; + } + } + rc = mdp3_get_img(img, &data, MDP3_CLIENT_DMA_P); + if (rc) { + pr_err("fail to get overlay buffer\n"); + goto err; + } + + if (data.len < dma->source_config.stride * dma->source_config.height) { + pr_err("buf size(0x%lx) is smaller than dma config(0x%x)\n", + data.len, (dma->source_config.stride * + dma->source_config.height)); + mdp3_put_img(&data, MDP3_CLIENT_DMA_P); + rc = -EINVAL; + goto err; + } + rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data); + if (rc) { + pr_err("fail to queue the overlay buffer, buffer drop\n"); + mdp3_put_img(&data, MDP3_CLIENT_DMA_P); + goto err; + } + rc = 0; +err: + if (is_panel_type_cmd) + mdp3_iommu_disable(MDP3_CLIENT_DMA_P); + return rc; +} + +static int mdp3_overlay_play(struct msm_fb_data_type *mfd, + struct msmfb_overlay_data *req) +{ + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + int rc = 0; + + pr_debug("%s req id=%x mem_id=%d\n", + __func__, req->id, req->data.memory_id); + + mutex_lock(&mdp3_session->lock); + + if (mdp3_session->overlay.id == MSMFB_NEW_REQUEST) { + pr_err("overlay play without overlay set first\n"); + mutex_unlock(&mdp3_session->lock); + return -EINVAL; + } + + if (mdss_fb_is_power_on(mfd)) + rc = mdp3_overlay_queue_buffer(mfd, req); + else + rc = -EPERM; + + mutex_unlock(&mdp3_session->lock); + + return rc; +} + +bool update_roi(struct mdp3_rect oldROI, struct mdp_rect newROI) +{ + return ((newROI.x != oldROI.x) || (newROI.y != oldROI.y) || + (newROI.w != oldROI.w) || (newROI.h != oldROI.h)); +} + +bool is_roi_valid(struct mdp3_dma_source source_config, struct mdp_rect roi) +{ + return (roi.w > 0) && (roi.h > 0) && + (roi.x >= source_config.x) && + ((roi.x + roi.w) <= source_config.width) && + (roi.y >= source_config.y) && + ((roi.y + roi.h) <= source_config.height); +} + +static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, + struct mdp_display_commit *cmt_data) +{ + struct mdp3_session_data *mdp3_session; + struct mdp3_img_data *data; + struct mdss_panel_info *panel_info; + int rc = 0; + static bool splash_done; + struct mdss_panel_data *panel; + + if (!mfd || !mfd->mdp.private1) + return -EINVAL; + + panel_info = mfd->panel_info; + mdp3_session = mfd->mdp.private1; + if (!mdp3_session || !mdp3_session->dma) + return -EINVAL; + + if (mdp3_bufq_count(&mdp3_session->bufq_in) == 0) { + pr_debug("no buffer in queue yet\n"); + return -EPERM; + } + + if (panel_info->partial_update_enabled && + is_roi_valid(mdp3_session->dma->source_config, + cmt_data->l_roi) && + update_roi(mdp3_session->dma->roi, cmt_data->l_roi)) { + mdp3_session->dma->roi.x = cmt_data->l_roi.x; + mdp3_session->dma->roi.y = cmt_data->l_roi.y; + mdp3_session->dma->roi.w = cmt_data->l_roi.w; + mdp3_session->dma->roi.h = cmt_data->l_roi.h; + mdp3_session->dma->update_src_cfg = true; + pr_debug("%s: ROI: x=%d y=%d w=%d h=%d\n", __func__, + mdp3_session->dma->roi.x, + mdp3_session->dma->roi.y, + mdp3_session->dma->roi.w, + mdp3_session->dma->roi.h); + } + + panel = mdp3_session->panel; + mutex_lock(&mdp3_res->fs_idle_pc_lock); + if (mdp3_session->in_splash_screen || + mdp3_res->idle_pc) { + pr_debug("%s: reset- in_splash = %d, idle_pc = %d", __func__, + mdp3_session->in_splash_screen, mdp3_res->idle_pc); + rc = mdp3_ctrl_reset(mfd); + if (rc) { + pr_err("fail to reset display\n"); + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + return -EINVAL; + } + } + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + + mutex_lock(&mdp3_session->lock); + + if (!mdp3_session->status) { + pr_err("%s, display off!\n", __func__); + mutex_unlock(&mdp3_session->lock); + return -EPERM; + } + + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN); + data = mdp3_bufq_pop(&mdp3_session->bufq_in); + if (data) { + mdp3_ctrl_reset_countdown(mdp3_session, mfd); + mdp3_ctrl_clk_enable(mfd, 1); + if (mdp3_session->dma->update_src_cfg && + panel_info->partial_update_enabled) { + panel->panel_info.roi.x = mdp3_session->dma->roi.x; + panel->panel_info.roi.y = mdp3_session->dma->roi.y; + panel->panel_info.roi.w = mdp3_session->dma->roi.w; + panel->panel_info.roi.h = mdp3_session->dma->roi.h; + rc = mdp3_session->dma->update(mdp3_session->dma, + (void *)(int)data->addr, + mdp3_session->intf, (void *)panel); + } else { + rc = mdp3_session->dma->update(mdp3_session->dma, + (void *)(int)data->addr, + mdp3_session->intf, NULL); + } + /* This is for the previous frame */ + if (rc < 0) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_TIMEOUT); + } else { + if (mdp3_ctrl_get_intf_type(mfd) == + MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_DONE); + } + } + mdp3_session->dma_active = 1; + init_completion(&mdp3_session->dma_completion); + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED); + mdp3_bufq_push(&mdp3_session->bufq_out, data); + } + + if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) { + mdp3_release_splash_memory(mfd); + data = mdp3_bufq_pop(&mdp3_session->bufq_out); + if (data) + mdp3_put_img(data, MDP3_CLIENT_DMA_P); + } + + if (mdp3_session->first_commit) { + /*wait to ensure frame is sent to panel*/ + if (panel_info->mipi.post_init_delay) + msleep(((1000 / panel_info->mipi.frame_rate) + 1) * + panel_info->mipi.post_init_delay); + else + msleep(1000 / panel_info->mipi.frame_rate); + mdp3_session->first_commit = false; + if (panel) + rc |= panel->event_handler(panel, + MDSS_EVENT_POST_PANEL_ON, NULL); + } + + mdp3_session->vsync_before_commit = 0; + if (!splash_done || mdp3_session->esd_recovery == true) { + if (panel && panel->set_backlight) + panel->set_backlight(panel, panel->panel_info.bl_max); + splash_done = true; + mdp3_session->esd_recovery = false; + } + + /* start vsync tick countdown for cmd mode if vsync isn't enabled */ + if (mfd->panel.type == MIPI_CMD_PANEL && !mdp3_session->vsync_enabled) + mdp3_ctrl_vsync_enable(mdp3_session->mfd, 0); + + mutex_unlock(&mdp3_session->lock); + + mdss_fb_update_notify_update(mfd); + + return 0; +} + +static int mdp3_map_pan_buff_immediate(struct msm_fb_data_type *mfd) +{ + int rc = 0; + unsigned long length; + dma_addr_t addr; + int domain = mfd->mdp.fb_mem_get_iommu_domain(); + + rc = mdss_smmu_map_dma_buf(mfd->fbmem_buf, mfd->fb_table, domain, + &addr, &length, DMA_BIDIRECTIONAL); + if (IS_ERR_VALUE(rc)) + goto err_unmap; + else + mfd->iova = addr; + + pr_debug("%s : smmu map dma buf VA: (%llx) MFD->iova %llx\n", + __func__, (u64) addr, (u64) mfd->iova); + return rc; + +err_unmap: + pr_err("smmu map dma buf failed: (%d)\n", rc); + dma_buf_unmap_attachment(mfd->fb_attachment, mfd->fb_table, + mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL)); + dma_buf_detach(mfd->fbmem_buf, mfd->fb_attachment); + dma_buf_put(mfd->fbmem_buf); + return rc; +} + +static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) +{ + struct fb_info *fbi; + struct mdp3_session_data *mdp3_session; + u32 offset; + int bpp; + struct mdss_panel_info *panel_info; + static bool splash_done; + struct mdss_panel_data *panel; + + int rc; + + pr_debug("%s\n", __func__); + if (!mfd || !mfd->mdp.private1) + return; + + panel_info = mfd->panel_info; + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + if (!mdp3_session || !mdp3_session->dma) + return; + + mutex_lock(&mdp3_res->fs_idle_pc_lock); + if (mdp3_session->in_splash_screen || + mdp3_res->idle_pc) { + pr_debug("%s: reset- in_splash = %d, idle_pc = %d", __func__, + mdp3_session->in_splash_screen, mdp3_res->idle_pc); + rc = mdp3_ctrl_reset(mfd); + if (rc) { + pr_err("fail to reset display\n"); + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + return; + } + } + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + + mutex_lock(&mdp3_session->lock); + + if (!mdp3_session->status) { + pr_err("%s, display off!\n", __func__); + goto pan_error; + } + + fbi = mfd->fbi; + + bpp = fbi->var.bits_per_pixel / 8; + offset = fbi->var.xoffset * bpp + + fbi->var.yoffset * fbi->fix.line_length; + + if (offset > fbi->fix.smem_len) { + pr_err("invalid fb offset=%u total length=%u\n", + offset, fbi->fix.smem_len); + goto pan_error; + } + + if (mfd->fbi->screen_base) { + mdp3_ctrl_reset_countdown(mdp3_session, mfd); + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN); + mdp3_ctrl_clk_enable(mfd, 1); + if (mdp3_session->first_commit) { + rc = mdp3_map_pan_buff_immediate(mfd); + if (IS_ERR_VALUE(rc)) + goto pan_error; + } + rc = mdp3_session->dma->update(mdp3_session->dma, + (void *)(int)(mfd->iova + offset), + mdp3_session->intf, NULL); + /* This is for the previous frame */ + if (rc < 0) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_TIMEOUT); + } else { + if (mdp3_ctrl_get_intf_type(mfd) == + MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_DONE); + } + } + mdp3_session->dma_active = 1; + init_completion(&mdp3_session->dma_completion); + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED); + } else { + pr_debug("%s no memory, stop interface", __func__); + mdp3_clk_enable(1, 0); + mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf); + mdp3_clk_enable(0, 0); + } + + panel = mdp3_session->panel; + if (mdp3_session->first_commit) { + /*wait to ensure frame is sent to panel*/ + if (panel_info->mipi.post_init_delay) + msleep(((1000 / panel_info->mipi.frame_rate) + 1) * + panel_info->mipi.post_init_delay); + else + msleep(1000 / panel_info->mipi.frame_rate); + mdp3_session->first_commit = false; + if (panel) + panel->event_handler(panel, MDSS_EVENT_POST_PANEL_ON, + NULL); + } + + mdp3_session->vsync_before_commit = 0; + if (!splash_done || mdp3_session->esd_recovery == true) { + if (panel && panel->set_backlight) + panel->set_backlight(panel, panel->panel_info.bl_max); + splash_done = true; + mdp3_session->esd_recovery = false; + } + + +pan_error: + mutex_unlock(&mdp3_session->lock); +} + +static int mdp3_set_metadata(struct msm_fb_data_type *mfd, + struct msmfb_metadata *metadata_ptr) +{ + int ret = 0; + + switch (metadata_ptr->op) { + case metadata_op_crc: + ret = mdp3_ctrl_res_req_clk(mfd, 1); + if (ret) { + pr_err("failed to turn on mdp clks\n"); + return ret; + } + ret = mdp3_misr_set(&metadata_ptr->data.misr_request); + ret = mdp3_ctrl_res_req_clk(mfd, 0); + if (ret) { + pr_err("failed to release mdp clks\n"); + return ret; + } + break; + default: + pr_warn("Unsupported request to MDP SET META IOCTL.\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static int mdp3_get_metadata(struct msm_fb_data_type *mfd, + struct msmfb_metadata *metadata) +{ + int ret = 0; + + switch (metadata->op) { + case metadata_op_frame_rate: + metadata->data.panel_frame_rate = + mfd->panel_info->mipi.frame_rate; + break; + case metadata_op_get_caps: + metadata->data.caps.mdp_rev = 305; + metadata->data.caps.rgb_pipes = 0; + metadata->data.caps.vig_pipes = 0; + metadata->data.caps.dma_pipes = 1; + break; + case metadata_op_crc: + ret = mdp3_ctrl_res_req_clk(mfd, 1); + if (ret) { + pr_err("failed to turn on mdp clks\n"); + return ret; + } + ret = mdp3_misr_get(&metadata->data.misr_request); + ret = mdp3_ctrl_res_req_clk(mfd, 0); + if (ret) { + pr_err("failed to release mdp clks\n"); + return ret; + } + break; + case metadata_op_get_ion_fd: + if (mfd->fb_ion_handle && mfd->fb_ion_client) { + get_dma_buf(mfd->fbmem_buf); + metadata->data.fbmem_ionfd = + ion_share_dma_buf_fd(mfd->fb_ion_client, + mfd->fb_ion_handle); + if (metadata->data.fbmem_ionfd < 0) { + dma_buf_put(mfd->fbmem_buf); + pr_err("fd allocation failed. fd = %d\n", + metadata->data.fbmem_ionfd); + } + } + break; + default: + pr_warn("Unsupported request to MDP GET META IOCTL.\n"); + ret = -EINVAL; + break; + } + return ret; +} + +int mdp3_validate_start_req(struct mdp_histogram_start_req *req) +{ + if (req->frame_cnt >= MDP_HISTOGRAM_FRAME_COUNT_MAX) { + pr_err("%s invalid req frame_cnt\n", __func__); + return -EINVAL; + } + if (req->bit_mask >= MDP_HISTOGRAM_BIT_MASK_MAX) { + pr_err("%s invalid req bit mask\n", __func__); + return -EINVAL; + } + if (req->block != MDP_BLOCK_DMA_P || + req->num_bins != MDP_HISTOGRAM_BIN_NUM) { + pr_err("mdp3_histogram_start invalid request\n"); + return -EINVAL; + } + return 0; +} + +int mdp3_validate_scale_config(struct mdp_bl_scale_data *data) +{ + if (data->scale > MDP_HISTOGRAM_BL_SCALE_MAX) { + pr_err("%s invalid bl_scale\n", __func__); + return -EINVAL; + } + if (data->min_lvl > MDP_HISTOGRAM_BL_LEVEL_MAX) { + pr_err("%s invalid bl_min_lvl\n", __func__); + return -EINVAL; + } + return 0; +} + +int mdp3_validate_csc_data(struct mdp_csc_cfg_data *data) +{ + int i; + bool mv_valid = false; + + for (i = 0; i < 9; i++) { + if (data->csc_data.csc_mv[i] >= + MDP_HISTOGRAM_CSC_MATRIX_MAX) + return -EINVAL; + if ((!mv_valid) && (data->csc_data.csc_mv[i] != 0)) + mv_valid = true; + } + if (!mv_valid) { + pr_err("%s: black screen data! csc_mv is all 0s\n", __func__); + return -EINVAL; + } + for (i = 0; i < 3; i++) { + if (data->csc_data.csc_pre_bv[i] >= + MDP_HISTOGRAM_CSC_VECTOR_MAX) + return -EINVAL; + if (data->csc_data.csc_post_bv[i] >= + MDP_HISTOGRAM_CSC_VECTOR_MAX) + return -EINVAL; + } + for (i = 0; i < 6; i++) { + if (data->csc_data.csc_pre_lv[i] >= + MDP_HISTOGRAM_CSC_VECTOR_MAX) + return -EINVAL; + if (data->csc_data.csc_post_lv[i] >= + MDP_HISTOGRAM_CSC_VECTOR_MAX) + return -EINVAL; + } + return 0; +} + +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; + + mutex_lock(&session->lock); + if (!session->status) { + mutex_unlock(&session->lock); + return -EPERM; + } + + pr_debug("%s\n", __func__); + + ret = mdp3_validate_start_req(req); + if (ret) { + mutex_unlock(&session->lock); + return ret; + } + + if (!session->dma->histo_op || + !session->dma->config_histo) { + pr_err("%s not supported\n", __func__); + mutex_unlock(&session->lock); + return -EINVAL; + } + + mutex_lock(&session->histo_lock); + + if (session->histo_status) { + pr_info("%s already started\n", __func__); + mutex_unlock(&session->histo_lock); + mutex_unlock(&session->lock); + return 0; + } + + mdp3_res_update(1, 0, MDP3_CLIENT_DMA_P); + ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_RESET); + if (ret) { + pr_err("%s reset error\n", __func__); + 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("%s error\n", __func__); + goto histogram_start_err; + } + + ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_START); + if (ret) { + pr_err("%s config error\n", __func__); + goto histogram_start_err; + } + + session->histo_status = 1; + +histogram_start_err: + mdp3_res_update(0, 0, MDP3_CLIENT_DMA_P); + mutex_unlock(&session->histo_lock); + mutex_unlock(&session->lock); + return ret; +} + +static int mdp3_histogram_stop(struct mdp3_session_data *session, + u32 block) +{ + int ret; + + pr_debug("%s\n", __func__); + + if (!session->dma->histo_op || block != MDP_BLOCK_DMA_P) { + pr_err("%s not supported\n", __func__); + return -EINVAL; + } + + mutex_lock(&session->histo_lock); + + if (!session->histo_status) { + pr_debug("%s already stopped!", __func__); + ret = 0; + goto histogram_stop_err; + } + + mdp3_clk_enable(1, 0); + ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_CANCEL); + mdp3_clk_enable(0, 0); + if (ret) + pr_err("%s error\n", __func__); + + 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; + + pr_debug("%s\n", __func__); + if (!session->dma->get_histo) { + pr_err("%s not supported\n", __func__); + return -EINVAL; + } + + mutex_lock(&session->histo_lock); + + if (!session->histo_status) { + pr_debug("%s not started\n", __func__); + mutex_unlock(&session->histo_lock); + return -EPROTO; + } + + mutex_unlock(&session->histo_lock); + + if (!session->clk_on) { + pr_debug("mdp/dsi clock off currently\n"); + return -EPERM; + } + + mdp3_clk_enable(1, 0); + ret = session->dma->get_histo(session->dma); + mdp3_clk_enable(0, 0); + if (ret) { + pr_debug("%s error = %d\n", __func__, 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; + } + + mutex_lock(&session->lock); + mutex_lock(&session->dma->pp_lock); + session->dma->cc_vect_sel = (session->dma->cc_vect_sel + 1) % 2; + + config.ccs_enable = 1; + config.ccs_sel = session->dma->cc_vect_sel; + config.pre_limit_sel = session->dma->cc_vect_sel; + config.post_limit_sel = session->dma->cc_vect_sel; + config.pre_bias_sel = session->dma->cc_vect_sel; + config.post_bias_sel = session->dma->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; + + /* cache one copy of setting for suspend/resume reconfiguring */ + session->dma->ccs_cache = *data; + + mdp3_clk_enable(1, 0); + ret = session->dma->config_ccs(session->dma, &config, &ccs); + mdp3_clk_enable(0, 0); + mutex_unlock(&session->dma->pp_lock); + 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 mdp_lut_cfg_data *lut; + 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_validate_scale_config(&mdp_pp.data.bl_scale_data); + if (ret) { + pr_err("%s: invalid scale config\n", __func__); + break; + } + ret = mdp3_bl_scale_config(mfd, (struct mdp_bl_scale_data *) + &mdp_pp.data.bl_scale_data); + break; + case mdp_op_csc_cfg: + /* Checking state of dyn_pu before programming CSC block */ + if (mdp3_session->dyn_pu_state) { + pr_debug("Partial update feature is enabled.\n"); + return -EPERM; + } + ret = mdp3_validate_csc_data(&(mdp_pp.data.csc_cfg_data)); + if (ret) { + pr_err("%s: invalid csc data\n", __func__); + break; + } + ret = mdp3_csc_config(mdp3_session, + &(mdp_pp.data.csc_cfg_data)); + break; + case mdp_op_lut_cfg: + lut = &mdp_pp.data.lut_cfg_data; + if (lut->lut_type != mdp_lut_rgb) { + pr_err("Lut type %d is not supported", lut->lut_type); + return -EINVAL; + } + if (lut->data.rgb_lut_data.flags & MDP_PP_OPS_READ) + ret = mdp3_ctrl_lut_read(mfd, + &(lut->data.rgb_lut_data)); + else + ret = mdp3_ctrl_lut_config(mfd, + &(lut->data.rgb_lut_data)); + if (ret) + pr_err("RGB LUT ioctl failed\n"); + else + ret = copy_to_user(argp, &mdp_pp, sizeof(mdp_pp)); + 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 = -ENOTSUPP; + 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_validate_lut_data(struct fb_cmap *cmap) +{ + u32 i = 0; + + if (!cmap || !cmap->red || !cmap->green || !cmap->blue) { + pr_err("Invalid arguments!\n"); + return -EINVAL; + } + + for (i = 0; i < MDP_LUT_SIZE; i++) { + if (cmap->red[i] > 0xFF || cmap->green[i] > 0xFF || + cmap->blue[i] > 0xFF) { + pr_err("LUT value over 255 (limit) at %d index\n", i); + return -EINVAL; + } + } + + return 0; +} + +static inline int mdp3_copy_lut_buffer(struct fb_cmap *dst, struct fb_cmap *src) +{ + if (!dst || !src || !dst->red || !dst->blue || !dst->green || + !src->red || !src->green || !src->blue) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + dst->start = src->start; + dst->len = src->len; + + memcpy(dst->red, src->red, MDP_LUT_SIZE * sizeof(u16)); + memcpy(dst->green, src->green, MDP_LUT_SIZE * sizeof(u16)); + memcpy(dst->blue, src->blue, MDP_LUT_SIZE * sizeof(u16)); + return 0; +} + +static int mdp3_alloc_lut_buffer(struct platform_device *pdev, void **cmap) +{ + struct fb_cmap *map; + + map = devm_kzalloc(&pdev->dev, sizeof(struct fb_cmap), GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + memset(map, 0, sizeof(struct fb_cmap)); + + map->red = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16), + GFP_KERNEL); + if (map->red == NULL) + goto exit_red; + + memset(map->red, 0, sizeof(u16) * MDP_LUT_SIZE); + + map->green = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16), + GFP_KERNEL); + if (map->green == NULL) + goto exit_green; + + memset(map->green, 0, sizeof(u16) * MDP_LUT_SIZE); + + map->blue = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16), + GFP_KERNEL); + if (map->blue == NULL) + goto exit_blue; + + memset(map->blue, 0, sizeof(u16) * MDP_LUT_SIZE); + + *cmap = map; + return 0; +exit_blue: + devm_kfree(&pdev->dev, map->green); +exit_green: + devm_kfree(&pdev->dev, map->red); +exit_red: + devm_kfree(&pdev->dev, map); + return -ENOMEM; +} + +static void mdp3_free_lut_buffer(struct platform_device *pdev, void **cmap) +{ + struct fb_cmap *map = (struct fb_cmap *)(*cmap); + + if (map == NULL) + return; + + devm_kfree(&pdev->dev, map->blue); + map->blue = NULL; + devm_kfree(&pdev->dev, map->green); + map->green = NULL; + devm_kfree(&pdev->dev, map->red); + map->red = NULL; + devm_kfree(&pdev->dev, map); + map = NULL; +} + +static int mdp3_lut_combine_gain(struct fb_cmap *cmap, struct mdp3_dma *dma) +{ + int i = 0; + u32 r = 0, g = 0, b = 0; + + if (!cmap || !dma || !dma->gc_cmap || !dma->hist_cmap || + !dma->gc_cmap->red || !dma->gc_cmap->green || + !dma->gc_cmap->blue || !dma->hist_cmap->red || + !dma->hist_cmap->green || !dma->hist_cmap->blue) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + for (i = 1; i < MDP_LUT_SIZE; i++) { + r = MIN(dma->gc_cmap->red[i] * dma->hist_cmap->red[i] * + mdp_lut_inverse16[i], 0xFF0000); + g = MIN(dma->gc_cmap->green[i] * dma->hist_cmap->green[i] * + mdp_lut_inverse16[i], 0xFF0000); + b = MIN(dma->gc_cmap->blue[i] * dma->hist_cmap->blue[i] * + mdp_lut_inverse16[i], 0xFF0000); + + cmap->red[i] = (r >> 16) & 0xFF; + cmap->green[i] = (g >> 16) & 0xFF; + cmap->blue[i] = (b >> 16) & 0xFF; + } + return 0; +} + +/* Called from within pp_lock and session lock locked context */ +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 *dma; + struct mdp3_dma_lut_config lut_config; + + dma = mdp3_session->dma; + + if (!dma->config_lut) { + pr_err("Config LUT not defined!\n"); + return -EINVAL; + } + + lut_config.lut_enable = 7; + lut_config.lut_sel = mdp3_session->lut_sel; + lut_config.lut_position = 1; + lut_config.lut_dirty = true; + + if (!mdp3_session->status) { + pr_err("display off!\n"); + return -EPERM; + } + + mdp3_clk_enable(1, 0); + rc = dma->config_lut(dma, &lut_config, cmap); + mdp3_clk_enable(0, 0); + if (rc) + pr_err("%s failed\n", __func__); + + mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2; + return rc; +} + +static int mdp3_ctrl_lut_config(struct msm_fb_data_type *mfd, + struct mdp_rgb_lut_data *cfg) +{ + int rc = 0; + bool data_validated = false; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct mdp3_dma *dma; + struct fb_cmap *cmap; + + dma = mdp3_session->dma; + + if ((cfg->cmap.start > MDP_LUT_SIZE) || + (cfg->cmap.len > MDP_LUT_SIZE) || + (cfg->cmap.start + cfg->cmap.len > MDP_LUT_SIZE)) { + pr_err("Invalid arguments.\n"); + return -EINVAL; + } + + rc = mdp3_alloc_lut_buffer(mfd->pdev, (void **) &cmap); + if (rc) { + pr_err("No memory\n"); + return -ENOMEM; + } + + mutex_lock(&mdp3_session->lock); + mutex_lock(&dma->pp_lock); + rc = copy_from_user(cmap->red + cfg->cmap.start, + cfg->cmap.red, sizeof(u16) * cfg->cmap.len); + rc |= copy_from_user(cmap->green + cfg->cmap.start, + cfg->cmap.green, sizeof(u16) * cfg->cmap.len); + rc |= copy_from_user(cmap->blue + cfg->cmap.start, + cfg->cmap.blue, sizeof(u16) * cfg->cmap.len); + if (rc) { + pr_err("Copying user data failed!\n"); + goto exit_err; + } + + switch (cfg->lut_type) { + case mdp_rgb_lut_gc: + if (cfg->flags & MDP_PP_OPS_DISABLE) { + if (dma->lut_sts & MDP3_LUT_GC_EN) + /* Free GC cmap cache since disabled */ + mdp3_free_lut_buffer(mfd->pdev, + (void **)&dma->gc_cmap); + dma->lut_sts &= ~MDP3_LUT_GC_EN; + } else if (!(dma->lut_sts & MDP3_LUT_GC_EN)) { + /* Check if values sent are valid */ + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid GC LUT data\n"); + goto exit_err; + } + data_validated = true; + + /* Allocate GC cmap cache to store values */ + rc = mdp3_alloc_lut_buffer(mfd->pdev, + (void **)&dma->gc_cmap); + if (rc) { + pr_err("GC LUT config failed\n"); + goto exit_err; + } + dma->lut_sts |= MDP3_LUT_GC_EN; + } + /* + * Copy the GC values from userspace to maintain the + * correct values user intended to program in cache. + * The values programmed in HW might factor in presence + * of other LUT modifying features hence can be + * different from these user given values. + */ + if (dma->lut_sts & MDP3_LUT_GC_EN) { + /* Validate LUT data if not yet validated */ + if (!data_validated) { + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid GC LUT data\n"); + goto exit_err; + } + } + rc = mdp3_copy_lut_buffer(dma->gc_cmap, cmap); + if (rc) { + pr_err("Could not store GC to cache\n"); + goto exit_err; + } + } + break; + case mdp_rgb_lut_hist: + if (cfg->flags & MDP_PP_OPS_DISABLE) { + if (dma->lut_sts & MDP3_LUT_HIST_EN) + /* Free HIST cmap cache since disabled */ + mdp3_free_lut_buffer(mfd->pdev, + (void **)&dma->hist_cmap); + dma->lut_sts &= ~MDP3_LUT_HIST_EN; + } else if (!(dma->lut_sts & MDP3_LUT_HIST_EN)) { + /* Check if values sent are valid */ + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid HIST LUT data\n"); + goto exit_err; + } + data_validated = true; + + /* Allocate HIST cmap cache to store values */ + rc = mdp3_alloc_lut_buffer(mfd->pdev, + (void **)&dma->hist_cmap); + if (rc) { + pr_err("HIST LUT config failed\n"); + goto exit_err; + } + dma->lut_sts |= MDP3_LUT_HIST_EN; + } + /* + * Copy the HIST LUT values from userspace to maintain + * correct values user intended to program in cache. + * The values programmed in HW might factor in presence + * of other LUT modifying features hence can be + * different from these user given values. + */ + if (dma->lut_sts & MDP3_LUT_HIST_EN) { + /* Validate LUT data if not yet validated */ + if (!data_validated) { + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid H LUT data\n"); + goto exit_err; + } + } + rc = mdp3_copy_lut_buffer(dma->hist_cmap, cmap); + if (rc) { + pr_err("Could not cache Hist LUT\n"); + goto exit_err; + } + } + break; + default: + pr_err("Invalid lut type: %u\n", cfg->lut_type); + rc = -EINVAL; + goto exit_err; + } + + /* + * In case both GC LUT and HIST LUT need to be programmed the gains + * of each the individual LUTs need to be applied onto a single LUT + * and applied in HW + */ + if ((dma->lut_sts & MDP3_LUT_HIST_EN) && + (dma->lut_sts & MDP3_LUT_GC_EN)) { + rc = mdp3_lut_combine_gain(cmap, dma); + if (rc) { + pr_err("Combining gains failed rc = %d\n", rc); + goto exit_err; + } + } + + rc = mdp3_ctrl_lut_update(mfd, cmap); + if (rc) + pr_err("Updating LUT failed! rc = %d\n", rc); +exit_err: + mutex_unlock(&dma->pp_lock); + mutex_unlock(&mdp3_session->lock); + mdp3_free_lut_buffer(mfd->pdev, (void **) &cmap); + return rc; +} + +static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd, + struct mdp_rgb_lut_data *cfg) +{ + int rc = 0; + struct fb_cmap *cmap; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct mdp3_dma *dma = mdp3_session->dma; + + switch (cfg->lut_type) { + case mdp_rgb_lut_gc: + if (!dma->gc_cmap) { + pr_err("GC not programmed\n"); + return -EPERM; + } + cmap = dma->gc_cmap; + break; + case mdp_rgb_lut_hist: + if (!dma->hist_cmap) { + pr_err("Hist LUT not programmed\n"); + return -EPERM; + } + cmap = dma->hist_cmap; + break; + default: + pr_err("Invalid lut type %u\n", cfg->lut_type); + return -EINVAL; + } + + cfg->cmap.start = cmap->start; + cfg->cmap.len = cmap->len; + + mutex_lock(&dma->pp_lock); + rc = copy_to_user(cfg->cmap.red, cmap->red, sizeof(u16) * + MDP_LUT_SIZE); + rc |= copy_to_user(cfg->cmap.green, cmap->green, sizeof(u16) * + MDP_LUT_SIZE); + rc |= copy_to_user(cfg->cmap.blue, cmap->blue, sizeof(u16) * + MDP_LUT_SIZE); + mutex_unlock(&dma->pp_lock); + return rc; +} + +/* Invoked from ctrl_on with session lock locked context */ +static void mdp3_ctrl_pp_resume(struct msm_fb_data_type *mfd) +{ + struct mdp3_session_data *mdp3_session; + struct mdp3_dma *dma; + struct fb_cmap *cmap; + int rc = 0; + + mdp3_session = mfd->mdp.private1; + dma = mdp3_session->dma; + + mutex_lock(&dma->pp_lock); + /* + * if dma->ccs_config.ccs_enable is set then DMA PP block was enabled + * via user space IOCTL. + * Then set dma->ccs_config.ccs_dirty flag + * Then PP block will be reconfigured when next kickoff comes. + */ + if (dma->ccs_config.ccs_enable) + dma->ccs_config.ccs_dirty = true; + + /* + * If gamma correction was enabled then we program the LUT registers + * with the last configuration data before suspend. If gamma correction + * is not enabled then we do not program anything. The LUT from + * histogram processing algorithms will program hardware based on new + * frame data if they are enabled. + */ + if (dma->lut_sts & MDP3_LUT_GC_EN) { + + rc = mdp3_alloc_lut_buffer(mfd->pdev, (void **)&cmap); + if (rc) { + pr_err("No memory for GC LUT, rc = %d\n", rc); + goto exit_err; + } + + if (dma->lut_sts & MDP3_LUT_HIST_EN) { + rc = mdp3_lut_combine_gain(cmap, dma); + if (rc) { + pr_err("Combining the gain failed rc=%d\n", rc); + goto exit_err; + } + } else { + rc = mdp3_copy_lut_buffer(cmap, dma->gc_cmap); + if (rc) { + pr_err("Updating GC failed rc = %d\n", rc); + goto exit_err; + } + } + + rc = mdp3_ctrl_lut_update(mfd, cmap); + if (rc) + pr_err("GC Lut update failed rc=%d\n", rc); +exit_err: + mdp3_free_lut_buffer(mfd->pdev, (void **)&cmap); + } + + mutex_unlock(&dma->pp_lock); +} + +static int mdp3_overlay_prepare(struct msm_fb_data_type *mfd, + struct mdp_overlay_list __user *user_ovlist) +{ + struct mdp_overlay_list ovlist; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct mdp_overlay *req_list; + struct mdp_overlay *req; + int rc; + + if (!mdp3_session) + return -ENODEV; + + req = &mdp3_session->req_overlay; + + if (copy_from_user(&ovlist, user_ovlist, sizeof(ovlist))) + return -EFAULT; + + if (ovlist.num_overlays != 1) { + pr_err("OV_PREPARE failed: only 1 overlay allowed\n"); + return -EINVAL; + } + + if (copy_from_user(&req_list, ovlist.overlay_list, + sizeof(struct mdp_overlay *))) + return -EFAULT; + + if (copy_from_user(req, req_list, sizeof(*req))) + return -EFAULT; + + rc = mdp3_overlay_set(mfd, req); + if (!IS_ERR_VALUE(rc)) { + if (copy_to_user(req_list, req, sizeof(*req))) + return -EFAULT; + } + + if (put_user(IS_ERR_VALUE(rc) ? 0 : 1, + &user_ovlist->processed_overlays)) + return -EFAULT; + + return rc; +} + +static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd, + u32 cmd, void __user *argp) +{ + int rc = -EINVAL; + struct mdp3_session_data *mdp3_session; + struct msmfb_metadata metadata; + struct mdp_overlay *req = NULL; + struct msmfb_overlay_data ov_data; + int val; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + if (!mdp3_session) + return -ENODEV; + + req = &mdp3_session->req_overlay; + + if (!mdp3_session->status && cmd != MSMFB_METADATA_GET && + cmd != MSMFB_HISTOGRAM_STOP && cmd != MSMFB_HISTOGRAM) { + pr_err("%s, display off!\n", __func__); + 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))) { + mutex_lock(&mdp3_session->lock); + mdp3_session->vsync_enabled = val; + rc = mdp3_ctrl_vsync_enable(mfd, val); + mutex_unlock(&mdp3_session->lock); + } else { + pr_err("MSMFB_OVERLAY_VSYNC_CTRL failed\n"); + rc = -EFAULT; + } + break; + case MSMFB_ASYNC_BLIT: + mutex_lock(&mdp3_res->fs_idle_pc_lock); + if (mdp3_session->in_splash_screen || mdp3_res->idle_pc) { + pr_debug("%s: reset- in_splash = %d, idle_pc = %d", + __func__, mdp3_session->in_splash_screen, + mdp3_res->idle_pc); + mdp3_ctrl_reset(mfd); + } + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + rc = mdp3_ctrl_async_blit_req(mfd, argp); + break; + case MSMFB_BLIT: + mutex_lock(&mdp3_res->fs_idle_pc_lock); + if (mdp3_session->in_splash_screen) + mdp3_ctrl_reset(mfd); + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + rc = mdp3_ctrl_blit_req(mfd, argp); + break; + case MSMFB_METADATA_GET: + rc = copy_from_user(&metadata, argp, sizeof(metadata)); + if (!rc) + rc = mdp3_get_metadata(mfd, &metadata); + if (!rc) + rc = copy_to_user(argp, &metadata, sizeof(metadata)); + if (rc) + pr_err("mdp3_get_metadata failed (%d)\n", rc); + break; + case MSMFB_METADATA_SET: + rc = copy_from_user(&metadata, argp, sizeof(metadata)); + if (!rc) + rc = mdp3_set_metadata(mfd, &metadata); + if (rc) + pr_err("mdp3_set_metadata failed (%d)\n", rc); + break; + case MSMFB_OVERLAY_GET: + rc = copy_from_user(req, argp, sizeof(*req)); + if (!rc) { + rc = mdp3_overlay_get(mfd, req); + + if (!IS_ERR_VALUE(rc)) + rc = copy_to_user(argp, req, sizeof(*req)); + } + if (rc) + pr_err("OVERLAY_GET failed (%d)\n", rc); + break; + case MSMFB_OVERLAY_SET: + rc = copy_from_user(req, argp, sizeof(*req)); + if (!rc) { + rc = mdp3_overlay_set(mfd, req); + + if (!IS_ERR_VALUE(rc)) + rc = copy_to_user(argp, req, sizeof(*req)); + } + if (rc) + pr_err("OVERLAY_SET failed (%d)\n", rc); + break; + case MSMFB_OVERLAY_UNSET: + if (!IS_ERR_VALUE(copy_from_user(&val, argp, sizeof(val)))) + rc = mdp3_overlay_unset(mfd, val); + break; + case MSMFB_OVERLAY_PLAY: + rc = copy_from_user(&ov_data, argp, sizeof(ov_data)); + mutex_lock(&mdp3_res->fs_idle_pc_lock); + if (mdp3_session->in_splash_screen) + mdp3_ctrl_reset(mfd); + mutex_unlock(&mdp3_res->fs_idle_pc_lock); + if (!rc) + rc = mdp3_overlay_play(mfd, &ov_data); + if (rc) + pr_err("OVERLAY_PLAY failed (%d)\n", rc); + break; + case MSMFB_OVERLAY_PREPARE: + rc = mdp3_overlay_prepare(mfd, argp); + break; + default: + break; + } + return rc; +} + +int mdp3_wait_for_dma_done(struct mdp3_session_data *session) +{ + int rc = 0; + + if (session->dma_active) { + rc = wait_for_completion_timeout(&session->dma_completion, + KOFF_TIMEOUT); + if (rc > 0) { + session->dma_active = 0; + rc = 0; + } else if (rc == 0) { + rc = -ETIME; + } + } + return rc; +} + +static int mdp3_update_panel_info(struct msm_fb_data_type *mfd, int mode, + int dest_ctrl) +{ + int ret = 0; + struct mdp3_session_data *mdp3_session; + struct mdss_panel_data *panel; + u32 intf_type = 0; + + if (!mfd || !mfd->mdp.private1) + return -EINVAL; + + mdp3_session = mfd->mdp.private1; + panel = mdp3_session->panel; + + if (!panel->event_handler) + return 0; + ret = panel->event_handler(panel, MDSS_EVENT_DSI_UPDATE_PANEL_DATA, + (void *)(unsigned long)mode); + if (ret) + pr_err("Dynamic switch to %s mode failed!\n", + mode ? "command" : "video"); + if (mode == 1) + mfd->panel.type = MIPI_CMD_PANEL; + else + mfd->panel.type = MIPI_VIDEO_PANEL; + + if (mfd->panel.type != MIPI_VIDEO_PANEL) + mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done; + + intf_type = mdp3_ctrl_get_intf_type(mfd); + mdp3_session->intf->cfg.type = intf_type; + mdp3_session->intf->available = 1; + mdp3_session->intf->in_use = 1; + mdp3_res->intf[intf_type].in_use = 1; + + mdp3_intf_init(mdp3_session->intf); + + mdp3_session->dma->output_config.out_sel = intf_type; + mdp3_session->status = mdp3_session->intf->active; + + return 0; +} + +static int mdp3_vsync_retire_setup(struct msm_fb_data_type *mfd) +{ + struct mdp3_session_data *mdp3_session; + struct mdp3_notification retire_client; + char name[24]; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + + snprintf(name, sizeof(name), "mdss_fb%d_retire", mfd->index); + mfd->mdp_sync_pt_data.timeline_retire = mdss_create_timeline(name); + if (mfd->mdp_sync_pt_data.timeline_retire == NULL) { + pr_err("cannot vsync create time line"); + return -ENOMEM; + } + + /* Add retire vsync handler */ + retire_client.handler = mdp3_vsync_retire_handle_vsync; + retire_client.arg = mdp3_session; + + if (mdp3_session->dma) + mdp3_session->dma->retire_client = retire_client; + + INIT_WORK(&mdp3_session->retire_work, mdp3_vsync_retire_work_handler); + + return 0; +} + +int mdp3_ctrl_init(struct msm_fb_data_type *mfd) +{ + struct device *dev = mfd->fbi->dev; + struct msm_mdp_interface *mdp3_interface = &mfd->mdp; + struct mdp3_session_data *mdp3_session = NULL; + u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO; + int rc; + int splash_mismatch = 0; + struct sched_param sched = { .sched_priority = 16 }; + + pr_info("%s\n", __func__); + rc = mdp3_parse_dt_splash(mfd); + if (rc) + splash_mismatch = 1; + + mdp3_interface->on_fnc = mdp3_ctrl_on; + mdp3_interface->off_fnc = mdp3_ctrl_off; + mdp3_interface->do_histogram = NULL; + mdp3_interface->cursor_update = NULL; + 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->pre_commit = mdp3_layer_pre_commit; + mdp3_interface->atomic_validate = mdp3_layer_atomic_validate; + mdp3_interface->lut_update = NULL; + mdp3_interface->configure_panel = mdp3_update_panel_info; + mdp3_interface->input_event_handler = NULL; + mdp3_interface->signal_retire_fence = NULL; + + mdp3_session = kzalloc(sizeof(struct mdp3_session_data), GFP_KERNEL); + if (!mdp3_session) + return -ENOMEM; + + mutex_init(&mdp3_session->lock); + INIT_WORK(&mdp3_session->clk_off_work, mdp3_dispatch_clk_off); + + kthread_init_worker(&mdp3_session->worker); + kthread_init_work(&mdp3_session->dma_done_work, mdp3_dispatch_dma_done); + + + mdp3_session->thread = kthread_run(kthread_worker_fn, + &mdp3_session->worker, + "mdp3_dispatch_dma_done"); + + if (IS_ERR(mdp3_session->thread)) { + pr_err("Can't initialize mdp3_dispatch_dma_done thread\n"); + rc = -ENODEV; + goto init_done; + } + + sched_setscheduler(mdp3_session->thread, SCHED_FIFO, &sched); + + atomic_set(&mdp3_session->vsync_countdown, 0); + mutex_init(&mdp3_session->histo_lock); + mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL); + if (!mdp3_session->dma) { + rc = -ENODEV; + goto init_done; + } + + rc = mdp3_dma_init(mdp3_session->dma); + if (rc) { + pr_err("fail to init dma\n"); + goto init_done; + } + + intf_type = mdp3_ctrl_get_intf_type(mfd); + mdp3_session->intf = mdp3_get_display_intf(intf_type); + if (!mdp3_session->intf) { + rc = -ENODEV; + goto init_done; + } + rc = mdp3_intf_init(mdp3_session->intf); + if (rc) { + pr_err("fail to init interface\n"); + goto init_done; + } + + mdp3_session->dma->output_config.out_sel = intf_type; + mdp3_session->mfd = mfd; + mdp3_session->panel = dev_get_platdata(&mfd->pdev->dev); + mdp3_session->status = mdp3_session->intf->active; + 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; + BLOCKING_INIT_NOTIFIER_HEAD(&mdp3_session->notifier_head); + + init_timer(&mdp3_session->vsync_timer); + mdp3_session->vsync_timer.function = mdp3_vsync_timer_func; + mdp3_session->vsync_timer.data = (u32)mdp3_session; + mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate; + mfd->mdp.private1 = mdp3_session; + init_completion(&mdp3_session->dma_completion); + if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) + mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done; + + rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group); + if (rc) { + pr_err("vsync sysfs group creation failed, ret=%d\n", rc); + goto init_done; + } + rc = sysfs_create_group(&dev->kobj, &generic_attr_group); + if (rc) { + pr_err("generic sysfs group creation failed, ret=%d\n", rc); + goto init_done; + } + + mdp3_session->vsync_event_sd = sysfs_get_dirent(dev->kobj.sd, + "vsync_event"); + if (!mdp3_session->vsync_event_sd) { + pr_err("vsync_event sysfs lookup failed\n"); + rc = -ENODEV; + goto init_done; + } + + mdp3_session->dma->hist_event_sd = sysfs_get_dirent(dev->kobj.sd, + "hist_event"); + if (!mdp3_session->dma->hist_event_sd) { + pr_err("hist_event sysfs lookup failed\n"); + rc = -ENODEV; + goto init_done; + } + + mdp3_session->bl_event_sd = sysfs_get_dirent(dev->kobj.sd, + "bl_event"); + if (!mdp3_session->bl_event_sd) { + pr_err("bl_event sysfs lookup failed\n"); + rc = -ENODEV; + goto init_done; + } + + rc = mdp3_create_sysfs_link(dev); + if (rc) + pr_warn("problem creating link to mdp sysfs\n"); + + /* Enable PM runtime */ + pm_runtime_set_suspended(&mdp3_res->pdev->dev); + pm_runtime_enable(&mdp3_res->pdev->dev); + + kobject_uevent(&dev->kobj, KOBJ_ADD); + pr_debug("vsync kobject_uevent(KOBJ_ADD)\n"); + + if (mdp3_get_cont_spash_en()) { + mdp3_session->clk_on = 1; + mdp3_session->in_splash_screen = 1; + mdp3_ctrl_notifier_register(mdp3_session, + &mdp3_session->mfd->mdp_sync_pt_data.notifier); + } + + /* + * Increment the overlay active count. + * This is needed to ensure that if idle power collapse kicks in + * right away, it would be handled correctly. + */ + atomic_inc(&mdp3_res->active_intf_cnt); + if (splash_mismatch) { + pr_err("splash memory mismatch, stop splash\n"); + mdp3_ctrl_off(mfd); + } + + mdp3_session->vsync_before_commit = true; + mdp3_session->dyn_pu_state = mfd->panel_info->partial_update_enabled; + + if (mfd->panel_info->mipi.dms_mode || + mfd->panel_info->type == MIPI_CMD_PANEL) { + rc = mdp3_vsync_retire_setup(mfd); + if (IS_ERR_VALUE(rc)) { + pr_err("unable to create vsync timeline\n"); + goto init_done; + } + } +init_done: + if (IS_ERR_VALUE(rc)) + kfree(mdp3_session); + + return rc; +} diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.h b/drivers/video/fbdev/msm/mdp3_ctrl.h new file mode 100644 index 0000000000000000000000000000000000000000..b7b667b69a9ae13740eef83c7289c21780696ca2 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_ctrl.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2013-2014, 2016-2018, 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 MDP3_CTRL_H +#define MDP3_CTRL_H + +#include +#include +#include +#include +#include + +#include "mdp3.h" +#include "mdp3_dma.h" +#include "mdss_fb.h" +#include "mdss_panel.h" + +#define MDP3_MAX_BUF_QUEUE 8 +#define MDP3_LUT_HIST_EN 0x001 +#define MDP3_LUT_GC_EN 0x002 + +struct mdp3_buffer_queue { + struct mdp3_img_data img_data[MDP3_MAX_BUF_QUEUE]; + int count; + int push_idx; + int pop_idx; +}; + +struct mdp3_session_data { + struct mutex lock; + int status; + struct mdp3_dma *dma; + struct mdss_panel_data *panel; + struct mdp3_intf *intf; + struct msm_fb_data_type *mfd; + ktime_t vsync_time; + struct timer_list vsync_timer; + int vsync_period; + struct kernfs_node *vsync_event_sd; + struct kernfs_node *bl_event_sd; + struct mdp_overlay overlay; + struct mdp_overlay req_overlay; + struct mdp3_buffer_queue bufq_in; + struct mdp3_buffer_queue bufq_out; + struct work_struct clk_off_work; + + struct kthread_work dma_done_work; + struct kthread_worker worker; + struct task_struct *thread; + + atomic_t dma_done_cnt; + int histo_status; + struct mutex histo_lock; + int lut_sel; + bool vsync_before_commit; + bool first_commit; + int clk_on; + struct blocking_notifier_head notifier_head; + + int vsync_enabled; + atomic_t vsync_countdown; /* Used to count down */ + bool in_splash_screen; + bool esd_recovery; + int dyn_pu_state; /* dynamic partial update status */ + u32 bl_events; + + bool dma_active; + struct completion dma_completion; + int (*wait_for_dma_done)(struct mdp3_session_data *session); + + /* For retire fence */ + struct mdss_timeline *vsync_timeline; + int retire_cnt; + struct work_struct retire_work; +}; + +void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq); +int mdp3_ctrl_init(struct msm_fb_data_type *mfd); +int mdp3_bufq_push(struct mdp3_buffer_queue *bufq, + struct mdp3_img_data *data); +int mdp3_ctrl_get_source_format(u32 imgType); +int mdp3_ctrl_get_pack_pattern(u32 imgType); +int mdp3_ctrl_reset(struct msm_fb_data_type *mfd); + +#endif /* MDP3_CTRL_H */ diff --git a/drivers/video/fbdev/msm/mdp3_dma.c b/drivers/video/fbdev/msm/mdp3_dma.c new file mode 100644 index 0000000000000000000000000000000000000000..8c36ad65694ba2d6b1ea347d914d6a57ad7c3b08 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_dma.c @@ -0,0 +1,1290 @@ +/* Copyright (c) 2013-2014, 2016-2018, 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 +#include + +#include "mdp3.h" +#include "mdp3_dma.h" +#include "mdp3_hwio.h" +#include "mdss_debug.h" + +#define DMA_STOP_POLL_SLEEP_US 1000 +#define DMA_STOP_POLL_TIMEOUT_US 200000 +#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) + +#define VSYNC_SELECT 0x024 +#define VSYNC_TOTAL_LINES_SHIFT 21 +#define VSYNC_COUNT_MASK 0x7ffff +#define VSYNC_THRESH_CONT_SHIFT 16 + +static void mdp3_vsync_intr_handler(int type, void *arg) +{ + struct mdp3_dma *dma = (struct mdp3_dma *)arg; + struct mdp3_notification vsync_client; + struct mdp3_notification retire_client; + unsigned int wait_for_next_vs; + + pr_debug("%s\n", __func__); + spin_lock(&dma->dma_lock); + vsync_client = dma->vsync_client; + retire_client = dma->retire_client; + wait_for_next_vs = !dma->vsync_status; + dma->vsync_status = 0; + if (wait_for_next_vs) + complete(&dma->vsync_comp); + spin_unlock(&dma->dma_lock); + if (vsync_client.handler) { + vsync_client.handler(vsync_client.arg); + } else { + if (wait_for_next_vs) + mdp3_irq_disable_nosync(type); + } + + if (retire_client.handler) + retire_client.handler(retire_client.arg); +} + +static void mdp3_dma_done_intr_handler(int type, void *arg) +{ + struct mdp3_dma *dma = (struct mdp3_dma *)arg; + struct mdp3_notification dma_client; + + pr_debug("%s\n", __func__); + spin_lock(&dma->dma_lock); + dma_client = dma->dma_notifier_client; + complete(&dma->dma_comp); + spin_unlock(&dma->dma_lock); + mdp3_irq_disable_nosync(type); + if (dma_client.handler) + dma_client.handler(dma_client.arg); +} + +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); + mdp3_hist_intr_notify(dma); + } + 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("%s type=%d\n", __func__, 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) + mdp3_irq_enable(MDP3_INTR_LCDC_START_OF_FRAME); + } else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC) { + irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE; + irq_bit += dma->dma_sel; + mdp3_irq_enable(irq_bit); + } + + if (type & MDP3_DMA_CALLBACK_TYPE_DMA_DONE) { + irq_bit = MDP3_INTR_DMA_P_DONE; + if (dma->dma_sel == MDP3_DMA_S) + irq_bit = MDP3_INTR_DMA_S_DONE; + mdp3_irq_enable(irq_bit); + } + } else { + pr_err("%s not supported interface\n", __func__); + } +} + +void mdp3_dma_callback_disable(struct mdp3_dma *dma, int type) +{ + int irq_bit; + + pr_debug("%s type=%d\n", __func__, 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) + mdp3_irq_disable(MDP3_INTR_LCDC_START_OF_FRAME); + } else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC) { + irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE; + irq_bit += dma->dma_sel; + mdp3_irq_disable(irq_bit); + /* + * Clear read pointer interrupt before disabling clocks. + * Else pending ISR handling will result in NOC error + * since the clock will be disable after this point. + */ + mdp3_clear_irq(irq_bit); + } + + if (type & MDP3_DMA_CALLBACK_TYPE_DMA_DONE) { + irq_bit = MDP3_INTR_DMA_P_DONE; + if (dma->dma_sel == MDP3_DMA_S) + irq_bit = MDP3_INTR_DMA_S_DONE; + mdp3_irq_disable(irq_bit); + } + } +} + +static int mdp3_dma_callback_setup(struct mdp3_dma *dma) +{ + int rc = 0; + struct mdp3_intr_cb vsync_cb = { + .cb = mdp3_vsync_intr_handler, + .data = dma, + }; + + struct mdp3_intr_cb dma_cb = { + .cb = mdp3_dma_done_intr_handler, + .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, + &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); + irq_bit = MDP3_INTR_DMA_P_DONE; + if (dma->dma_sel == MDP3_DMA_S) + irq_bit = MDP3_INTR_DMA_S_DONE; + rc |= mdp3_set_intr_callback(irq_bit, &dma_cb); + } else { + pr_err("%s not supported interface\n", __func__); + rc = -ENODEV; + } + + return rc; +} + +static void mdp3_dma_vsync_enable(struct mdp3_dma *dma, + struct mdp3_notification *vsync_client) +{ + unsigned long flag; + int updated = 0; + int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC; + + pr_debug("%s\n", __func__); + + spin_lock_irqsave(&dma->dma_lock, flag); + if (vsync_client) { + if (dma->vsync_client.handler != vsync_client->handler) { + dma->vsync_client = *vsync_client; + updated = 1; + } + } else { + if (dma->vsync_client.handler) { + dma->vsync_client.handler = NULL; + dma->vsync_client.arg = NULL; + updated = 1; + } + } + spin_unlock_irqrestore(&dma->dma_lock, flag); + + if (updated) { + if (vsync_client && vsync_client->handler) + mdp3_dma_callback_enable(dma, cb_type); + else + mdp3_dma_callback_disable(dma, cb_type); + } +} + +static void mdp3_dma_done_notifier(struct mdp3_dma *dma, + struct mdp3_notification *dma_client) +{ + unsigned long flag; + + spin_lock_irqsave(&dma->dma_lock, flag); + if (dma_client) { + dma->dma_notifier_client = *dma_client; + } else { + dma->dma_notifier_client.handler = NULL; + dma->dma_notifier_client.arg = NULL; + } + spin_unlock_irqrestore(&dma->dma_lock, flag); +} + +int mdp3_dma_sync_config(struct mdp3_dma *dma, + struct mdp3_dma_source *source_config, struct mdp3_tear_check *te) +{ + u32 vsync_clk_speed_hz, vclks_line, cfg; + int porch = source_config->vporch; + int height = source_config->height; + int total_lines = height + porch; + int dma_sel = dma->dma_sel; + + vsync_clk_speed_hz = MDP_VSYNC_CLK_RATE; + + cfg = total_lines << VSYNC_TOTAL_LINES_SHIFT; + total_lines *= te->frame_rate; + + vclks_line = (total_lines) ? vsync_clk_speed_hz / total_lines : 0; + + cfg |= BIT(19); + if (te->hw_vsync_mode) + cfg |= BIT(20); + + if (te->refx100) { + vclks_line = vclks_line * te->frame_rate * + 100 / te->refx100; + } else { + pr_warn("refx100 cannot be zero! Use 6000 as default\n"); + vclks_line = vclks_line * te->frame_rate * + 100 / 6000; + } + + cfg |= (vclks_line & VSYNC_COUNT_MASK); + + MDP3_REG_WRITE(MDP3_REG_SYNC_CONFIG_0 + dma_sel, cfg); + MDP3_REG_WRITE(MDP3_REG_VSYNC_SEL, VSYNC_SELECT); + MDP3_REG_WRITE(MDP3_REG_PRIMARY_VSYNC_INIT_VAL + dma_sel, + te->vsync_init_val); + MDP3_REG_WRITE(MDP3_REG_PRIMARY_RD_PTR_IRQ, te->rd_ptr_irq); + MDP3_REG_WRITE(MDP3_REG_SYNC_THRESH_0 + dma_sel, + ((te->sync_threshold_continue << VSYNC_THRESH_CONT_SHIFT) | + te->sync_threshold_start)); + MDP3_REG_WRITE(MDP3_REG_PRIMARY_START_P0S + dma_sel, te->start_pos); + MDP3_REG_WRITE(MDP3_REG_TEAR_CHECK_EN, te->tear_check_en); + return 0; +} + +static int mdp3_dmap_config(struct mdp3_dma *dma, + struct mdp3_dma_source *source_config, + struct mdp3_dma_output_config *output_config, + bool splash_screen_active) +{ + u32 dma_p_cfg_reg, dma_p_size, dma_p_out_xy; + + dma_p_cfg_reg = source_config->format << 25; + if (output_config->dither_en) + dma_p_cfg_reg |= BIT(24); + dma_p_cfg_reg |= output_config->out_sel << 19; + dma_p_cfg_reg |= output_config->bit_mask_polarity << 18; + dma_p_cfg_reg |= output_config->color_components_flip << 14; + dma_p_cfg_reg |= output_config->pack_pattern << 8; + dma_p_cfg_reg |= output_config->pack_align << 7; + dma_p_cfg_reg |= output_config->color_comp_out_bits; + + dma_p_size = source_config->width | (source_config->height << 16); + dma_p_out_xy = source_config->x | (source_config->y << 16); + if (!splash_screen_active) { + MDP3_REG_WRITE(MDP3_REG_DMA_P_CONFIG, dma_p_cfg_reg); + MDP3_REG_WRITE(MDP3_REG_DMA_P_SIZE, dma_p_size); + MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, + (u32)source_config->buf); + MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_Y_STRIDE, + source_config->stride); + MDP3_REG_WRITE(MDP3_REG_DMA_P_OUT_XY, dma_p_out_xy); + MDP3_REG_WRITE(MDP3_REG_DMA_P_FETCH_CFG, 0x40); + } + + dma->source_config = *source_config; + dma->output_config = *output_config; + + if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD) + mdp3_irq_enable(MDP3_INTR_LCDC_UNDERFLOW); + + mdp3_dma_callback_setup(dma); + return 0; +} + +static void mdp3_dmap_config_source(struct mdp3_dma *dma) +{ + struct mdp3_dma_source *source_config = &dma->source_config; + u32 dma_p_cfg_reg, dma_p_size; + + dma_p_cfg_reg = MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG); + dma_p_cfg_reg &= ~MDP3_DMA_IBUF_FORMAT_MASK; + dma_p_cfg_reg |= source_config->format << 25; + dma_p_cfg_reg &= ~MDP3_DMA_PACK_PATTERN_MASK; + dma_p_cfg_reg |= dma->output_config.pack_pattern << 8; + + dma_p_size = dma->roi.w | (dma->roi.h << 16); + + MDP3_REG_WRITE(MDP3_REG_DMA_P_CONFIG, dma_p_cfg_reg); + MDP3_REG_WRITE(MDP3_REG_DMA_P_SIZE, dma_p_size); + MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_Y_STRIDE, source_config->stride); +} + +static int mdp3_dmas_config(struct mdp3_dma *dma, + struct mdp3_dma_source *source_config, + struct mdp3_dma_output_config *output_config, + bool splash_screen_active) +{ + u32 dma_s_cfg_reg, dma_s_size, dma_s_out_xy; + + dma_s_cfg_reg = source_config->format << 25; + if (output_config->dither_en) + dma_s_cfg_reg |= BIT(24); + dma_s_cfg_reg |= output_config->out_sel << 19; + dma_s_cfg_reg |= output_config->bit_mask_polarity << 18; + dma_s_cfg_reg |= output_config->color_components_flip << 14; + dma_s_cfg_reg |= output_config->pack_pattern << 8; + dma_s_cfg_reg |= output_config->pack_align << 7; + dma_s_cfg_reg |= output_config->color_comp_out_bits; + + dma_s_size = source_config->width | (source_config->height << 16); + dma_s_out_xy = source_config->x | (source_config->y << 16); + + if (!splash_screen_active) { + MDP3_REG_WRITE(MDP3_REG_DMA_S_CONFIG, dma_s_cfg_reg); + MDP3_REG_WRITE(MDP3_REG_DMA_S_SIZE, dma_s_size); + MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_ADDR, + (u32)source_config->buf); + MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_Y_STRIDE, + source_config->stride); + MDP3_REG_WRITE(MDP3_REG_DMA_S_OUT_XY, dma_s_out_xy); + MDP3_REG_WRITE(MDP3_REG_SECONDARY_RD_PTR_IRQ, 0x10); + } + dma->source_config = *source_config; + dma->output_config = *output_config; + + mdp3_dma_callback_setup(dma); + return 0; +} + +static void mdp3_dmas_config_source(struct mdp3_dma *dma) +{ + struct mdp3_dma_source *source_config = &dma->source_config; + u32 dma_s_cfg_reg, dma_s_size; + + dma_s_cfg_reg = MDP3_REG_READ(MDP3_REG_DMA_S_CONFIG); + dma_s_cfg_reg &= ~MDP3_DMA_IBUF_FORMAT_MASK; + dma_s_cfg_reg |= source_config->format << 25; + + dma_s_size = source_config->width | (source_config->height << 16); + + MDP3_REG_WRITE(MDP3_REG_DMA_S_CONFIG, dma_s_cfg_reg); + MDP3_REG_WRITE(MDP3_REG_DMA_S_SIZE, dma_s_size); + MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_Y_STRIDE, source_config->stride); +} + +static int mdp3_dmap_cursor_config(struct mdp3_dma *dma, + struct mdp3_dma_cursor *cursor) +{ + u32 cursor_size, cursor_pos, blend_param, trans_mask; + + cursor_size = cursor->width | (cursor->height << 16); + cursor_pos = cursor->x | (cursor->y << 16); + trans_mask = 0; + if (cursor->blend_config.mode == MDP3_DMA_CURSOR_BLEND_CONSTANT_ALPHA) { + blend_param = cursor->blend_config.constant_alpha << 24; + } else if (cursor->blend_config.mode == + MDP3_DMA_CURSOR_BLEND_COLOR_KEYING) { + blend_param = cursor->blend_config.transparent_color; + trans_mask = cursor->blend_config.transparency_mask; + } else { + blend_param = 0; + } + + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_FORMAT, cursor->format); + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_SIZE, cursor_size); + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BUF_ADDR, (u32)cursor->buf); + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_POS, cursor_pos); + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BLEND_CONFIG, + cursor->blend_config.mode); + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BLEND_PARAM, blend_param); + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BLEND_TRANS_MASK, trans_mask); + dma->cursor = *cursor; + return 0; +} + +static int mdp3_dmap_ccs_config_internal(struct mdp3_dma *dma, + struct mdp3_dma_color_correct_config *config, + struct mdp3_dma_ccs *ccs) +{ + int i; + u32 addr; + + if (!ccs) + return -EINVAL; + + 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->mv[i]); + addr += 4; + } + + 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_bv[i]); + addr += 4; + } + + 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_bv[i]); + addr += 4; + } + + 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_lv[i]); + addr += 4; + } + + 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_lv[i]); + addr += 4; + } + } + return 0; +} + +static void mdp3_ccs_update(struct mdp3_dma *dma, bool from_kickoff) +{ + u32 cc_config; + bool ccs_updated = false, lut_updated = false; + struct mdp3_dma_ccs ccs; + + 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; + /* + * CCS dirty flag should be reset when call is made from frame + * kickoff, or else upon resume the flag would be dirty and LUT + * config could call this function thereby causing no register + * programming for CCS, which will cause screen to go dark + */ + if (from_kickoff) + dma->ccs_config.ccs_dirty = false; + ccs_updated = true; + } + + 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; + lut_updated = true; + } + + if (ccs_updated && from_kickoff) { + ccs.mv = dma->ccs_cache.csc_data.csc_mv; + ccs.pre_bv = dma->ccs_cache.csc_data.csc_pre_bv; + ccs.post_bv = dma->ccs_cache.csc_data.csc_post_bv; + ccs.pre_lv = dma->ccs_cache.csc_data.csc_pre_lv; + ccs.post_lv = dma->ccs_cache.csc_data.csc_post_lv; + mdp3_dmap_ccs_config_internal(dma, &dma->ccs_config, &ccs); + } + + if (lut_updated || ccs_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(); /* ensure write is finished before progressing */ + } +} + +static int mdp3_dmap_ccs_config(struct mdp3_dma *dma, + struct mdp3_dma_color_correct_config *config, + struct mdp3_dma_ccs *ccs) +{ + mdp3_dmap_ccs_config_internal(dma, config, ccs); + + dma->ccs_config = *config; + + if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD) + mdp3_ccs_update(dma, false); + + return 0; +} + +static int mdp3_dmap_lut_config(struct mdp3_dma *dma, + struct mdp3_dma_lut_config *config, + struct fb_cmap *cmap) +{ + u32 addr, color; + int i; + + if (config->lut_enable && cmap) { + addr = MDP3_REG_DMA_P_CSC_LUT1; + if (config->lut_sel) + addr = MDP3_REG_DMA_P_CSC_LUT2; + + for (i = 0; i < MDP_LUT_SIZE; i++) { + color = cmap->green[i] & 0xff; + color |= (cmap->red[i] & 0xff) << 8; + color |= (cmap->blue[i] & 0xff) << 16; + MDP3_REG_WRITE(addr, color); + addr += 4; + } + } + + dma->lut_config = *config; + + if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD) + mdp3_ccs_update(dma, false); + + return 0; +} + +static int mdp3_dmap_histo_config(struct mdp3_dma *dma, + struct mdp3_dma_histogram_config *histo_config) +{ + unsigned long flag; + u32 histo_bit_mask = 0, histo_control = 0; + 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) + histo_bit_mask = BIT(31); + histo_bit_mask |= histo_config->bit_mask; + + if (histo_config->auto_clear_en) + 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, 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; +} + +int dma_bpp(int format) +{ + int bpp; + + switch (format) { + case MDP3_DMA_IBUF_FORMAT_RGB888: + bpp = 3; + break; + case MDP3_DMA_IBUF_FORMAT_RGB565: + bpp = 2; + break; + case MDP3_DMA_IBUF_FORMAT_XRGB8888: + bpp = 4; + break; + default: + bpp = 0; + } + return bpp; +} + +static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf, + struct mdp3_intf *intf, void *data) +{ + unsigned long flag; + int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC; + struct mdss_panel_data *panel; + int rc = 0; + int retry_count = 2; + + ATRACE_BEGIN(__func__); + pr_debug("%s\n", __func__); + + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE; + if (intf->active) { + ATRACE_BEGIN("mdp3_wait_for_dma_comp"); +retry_dma_done: + rc = wait_for_completion_timeout(&dma->dma_comp, + KOFF_TIMEOUT); + if (rc <= 0 && --retry_count) { + int vsync_status; + + vsync_status = (1 << MDP3_INTR_DMA_P_DONE) & + MDP3_REG_READ(MDP3_REG_INTR_STATUS); + if (!vsync_status) { + pr_err("%s: cmd timeout retry cnt %d\n", + __func__, retry_count); + goto retry_dma_done; + } + rc = -1; + } + ATRACE_END("mdp3_wait_for_dma_comp"); + } + } + if (dma->update_src_cfg) { + if (dma->output_config.out_sel == + MDP3_DMA_OUTPUT_SEL_DSI_VIDEO && intf->active) + pr_err("configuring dma source while it is active\n"); + dma->dma_config_source(dma); + if (data) { + panel = (struct mdss_panel_data *)data; + if (panel->event_handler) { + panel->event_handler(panel, + MDSS_EVENT_ENABLE_PARTIAL_ROI, NULL); + panel->event_handler(panel, + MDSS_EVENT_DSI_STREAM_SIZE, NULL); + } + } + dma->update_src_cfg = false; + } + mutex_lock(&dma->pp_lock); + if (dma->ccs_config.ccs_dirty) + mdp3_ccs_update(dma, true); + mutex_unlock(&dma->pp_lock); + spin_lock_irqsave(&dma->dma_lock, flag); + MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)(buf + + dma->roi.y * dma->source_config.stride + + dma->roi.x * dma_bpp(dma->source_config.format))); + dma->source_config.buf = (int)buf; + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) + MDP3_REG_WRITE(MDP3_REG_DMA_P_START, 1); + + if (!intf->active) { + pr_debug("%s start interface\n", __func__); + intf->start(intf); + } + + mb(); /* make sure everything is written before enable */ + dma->vsync_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS) & + (1 << MDP3_INTR_LCDC_START_OF_FRAME); + init_completion(&dma->vsync_comp); + spin_unlock_irqrestore(&dma->dma_lock, flag); + + mdp3_dma_callback_enable(dma, cb_type); + pr_debug("%s wait for vsync_comp\n", __func__); + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) { + ATRACE_BEGIN("mdp3_wait_for_vsync_comp"); +retry_vsync: + rc = wait_for_completion_timeout(&dma->vsync_comp, + KOFF_TIMEOUT); + if (rc <= 0 && --retry_count) { + int vsync = MDP3_REG_READ(MDP3_REG_INTR_STATUS) & + (1 << MDP3_INTR_LCDC_START_OF_FRAME); + + if (!vsync) { + pr_err("%s trying again count = %d\n", + __func__, retry_count); + goto retry_vsync; + } + rc = -1; + } + ATRACE_END("mdp3_wait_for_vsync_comp"); + } + pr_debug("%s wait for vsync_comp out\n", __func__); + ATRACE_END(__func__); + return rc; +} + +static int mdp3_dmas_update(struct mdp3_dma *dma, void *buf, + struct mdp3_intf *intf, void *data) +{ + unsigned long flag; + int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC; + + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE; + if (intf->active) + wait_for_completion_killable(&dma->dma_comp); + } + + spin_lock_irqsave(&dma->dma_lock, flag); + MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_ADDR, (u32)buf); + dma->source_config.buf = (int)buf; + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) + MDP3_REG_WRITE(MDP3_REG_DMA_S_START, 1); + + if (!intf->active) { + pr_debug("%s start interface\n", __func__); + intf->start(intf); + } + + wmb(); /* ensure write is finished before progressing */ + init_completion(&dma->vsync_comp); + spin_unlock_irqrestore(&dma->dma_lock, flag); + + mdp3_dma_callback_enable(dma, cb_type); + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) + wait_for_completion_killable(&dma->vsync_comp); + return 0; +} + +static int mdp3_dmap_cursor_update(struct mdp3_dma *dma, int x, int y) +{ + u32 cursor_pos; + + cursor_pos = x | (y << 16); + MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_POS, cursor_pos); + dma->cursor.x = x; + dma->cursor.y = y; + return 0; +} + +static int mdp3_dmap_histo_get(struct mdp3_dma *dma) +{ + 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("%s invalid state %d\n", __func__, 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("%s time out\n", __func__); + ret = -ETIMEDOUT; + } else if (ret < 0) { + pr_err("%s interrupted\n", __func__); + } + + if (ret < 0) + return ret; + + if (dma->histo_state != MDP3_DMA_HISTO_STATE_READY) { + pr_debug("%s dma shut down\n", __func__); + return -EPERM; + } + + addr = MDP3_REG_DMA_P_HIST_R_DATA; + 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 < 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 < MDP_HISTOGRAM_BIN_NUM; i++) { + dma->histo_data.b_data[i] = MDP3_REG_READ(addr); + addr += 4; + } + + 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(); /* ensure write is finished before progressing */ + 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(); /* ensure write is finished before progressing */ + 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; + + spin_lock_irqsave(&dma->histo_lock, flag); + + init_completion(&dma->histo_comp); + + + 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(); /* ensure write is finished before progressing */ + 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("%s timed out\n", __func__); + ret = -ETIMEDOUT; + } else if (ret < 0) { + pr_err("%s interrupted\n", __func__); + } else { + ret = 0; + } + mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE); + + 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(); /* ensure write is finished before progressing */ + 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: + ret = mdp3_dmap_histo_start(dma); + break; + case MDP3_DMA_HISTO_OP_STOP: + case MDP3_DMA_HISTO_OP_CANCEL: + ret = mdp3_dmap_histo_stop(dma); + break; + case MDP3_DMA_HISTO_OP_RESET: + ret = mdp3_dmap_histo_reset(dma); + break; + default: + ret = -EINVAL; + } + return ret; +} + +bool mdp3_dmap_busy(void) +{ + u32 val; + + val = MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS); + pr_err("%s DMAP Status %s\n", __func__, + (val & MDP3_DMA_P_BUSY_BIT) ? "BUSY":"IDLE"); + return val & MDP3_DMA_P_BUSY_BIT; +} + +/* + * During underrun DMA_P registers are reset. Reprogramming CSC to prevent + * black screen + */ +static void mdp3_dmap_underrun_worker(struct work_struct *work) +{ + struct mdp3_dma *dma; + + dma = container_of(work, struct mdp3_dma, underrun_work); + mutex_lock(&dma->pp_lock); + if (dma->ccs_config.ccs_enable && dma->ccs_config.ccs_dirty) { + dma->cc_vect_sel = (dma->cc_vect_sel + 1) % 2; + dma->ccs_config.ccs_sel = dma->cc_vect_sel; + dma->ccs_config.pre_limit_sel = dma->cc_vect_sel; + dma->ccs_config.post_limit_sel = dma->cc_vect_sel; + dma->ccs_config.pre_bias_sel = dma->cc_vect_sel; + dma->ccs_config.post_bias_sel = dma->cc_vect_sel; + mdp3_ccs_update(dma, true); + } + mutex_unlock(&dma->pp_lock); +} + +static int mdp3_dma_start(struct mdp3_dma *dma, struct mdp3_intf *intf) +{ + unsigned long flag; + int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC; + u32 dma_start_offset = MDP3_REG_DMA_P_START; + + if (dma->dma_sel == MDP3_DMA_P) + dma_start_offset = MDP3_REG_DMA_P_START; + else if (dma->dma_sel == MDP3_DMA_S) + dma_start_offset = MDP3_REG_DMA_S_START; + else + return -EINVAL; + + spin_lock_irqsave(&dma->dma_lock, flag); + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + cb_type |= MDP3_DMA_CALLBACK_TYPE_DMA_DONE; + MDP3_REG_WRITE(dma_start_offset, 1); + } + + intf->start(intf); + wmb(); /* ensure write is finished before progressing */ + init_completion(&dma->vsync_comp); + spin_unlock_irqrestore(&dma->dma_lock, flag); + + if (dma->dma_sel == MDP3_DMA_P && dma->has_panic_ctrl) + MDP3_REG_WRITE(MDP3_PANIC_ROBUST_CTRL, BIT(0)); + + mdp3_dma_callback_enable(dma, cb_type); + pr_debug("%s wait for vsync in\n", __func__); + wait_for_completion_killable(&dma->vsync_comp); + pr_debug("%s wait for vsync out\n", __func__); + return 0; +} + +static int mdp3_dma_stop(struct mdp3_dma *dma, struct mdp3_intf *intf) +{ + int ret = 0; + u32 status, display_status_bit; + + if (dma->dma_sel == MDP3_DMA_P) + display_status_bit = BIT(6); + else if (dma->dma_sel == MDP3_DMA_S) + display_status_bit = BIT(7); + else + return -EINVAL; + + if (dma->dma_sel == MDP3_DMA_P && dma->has_panic_ctrl) + MDP3_REG_WRITE(MDP3_PANIC_ROBUST_CTRL, 0); + + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) + display_status_bit |= BIT(11); + + intf->stop(intf); + ret = readl_poll_timeout((mdp3_res->mdp_base + MDP3_REG_DISPLAY_STATUS), + status, + ((status & display_status_bit) == 0), + DMA_STOP_POLL_SLEEP_US, + DMA_STOP_POLL_TIMEOUT_US); + + mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_VSYNC | + MDP3_DMA_CALLBACK_TYPE_DMA_DONE); + mdp3_irq_disable(MDP3_INTR_LCDC_UNDERFLOW); + + MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0); + MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, 0xfffffff); + + init_completion(&dma->dma_comp); + dma->vsync_client.handler = NULL; + return ret; +} + +int mdp3_dma_init(struct mdp3_dma *dma) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + switch (dma->dma_sel) { + case MDP3_DMA_P: + dma->dma_config = mdp3_dmap_config; + dma->dma_sync_config = mdp3_dma_sync_config; + dma->dma_config_source = mdp3_dmap_config_source; + 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->vsync_enable = mdp3_dma_vsync_enable; + dma->dma_done_notifier = mdp3_dma_done_notifier; + dma->start = mdp3_dma_start; + dma->stop = mdp3_dma_stop; + dma->busy = mdp3_dmap_busy; + INIT_WORK(&dma->underrun_work, mdp3_dmap_underrun_worker); + break; + case MDP3_DMA_S: + dma->dma_config = mdp3_dmas_config; + dma->dma_sync_config = mdp3_dma_sync_config; + dma->dma_config_source = mdp3_dmas_config_source; + 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->vsync_enable = mdp3_dma_vsync_enable; + dma->start = mdp3_dma_start; + dma->stop = mdp3_dma_stop; + break; + case MDP3_DMA_E: + default: + ret = -ENODEV; + break; + } + + 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; + dma->update_src_cfg = false; + + memset(&dma->cursor, 0, sizeof(dma->cursor)); + memset(&dma->ccs_config, 0, sizeof(dma->ccs_config)); + memset(&dma->histogram_config, 0, sizeof(dma->histogram_config)); + + return ret; +} + +int lcdc_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg) +{ + u32 temp; + struct mdp3_video_intf_cfg *v = &cfg->video; + + temp = v->hsync_pulse_width | (v->hsync_period << 16); + MDP3_REG_WRITE(MDP3_REG_LCDC_HSYNC_CTL, temp); + MDP3_REG_WRITE(MDP3_REG_LCDC_VSYNC_PERIOD, v->vsync_period); + MDP3_REG_WRITE(MDP3_REG_LCDC_VSYNC_PULSE_WIDTH, v->vsync_pulse_width); + temp = v->display_start_x | (v->display_end_x << 16); + MDP3_REG_WRITE(MDP3_REG_LCDC_DISPLAY_HCTL, temp); + MDP3_REG_WRITE(MDP3_REG_LCDC_DISPLAY_V_START, v->display_start_y); + MDP3_REG_WRITE(MDP3_REG_LCDC_DISPLAY_V_END, v->display_end_y); + temp = v->active_start_x | (v->active_end_x); + if (v->active_h_enable) + temp |= BIT(31); + MDP3_REG_WRITE(MDP3_REG_LCDC_ACTIVE_HCTL, temp); + MDP3_REG_WRITE(MDP3_REG_LCDC_ACTIVE_V_START, v->active_start_y); + MDP3_REG_WRITE(MDP3_REG_LCDC_ACTIVE_V_END, v->active_end_y); + MDP3_REG_WRITE(MDP3_REG_LCDC_HSYNC_SKEW, v->hsync_skew); + temp = 0; + if (!v->hsync_polarity) + temp = BIT(0); + if (!v->vsync_polarity) + temp = BIT(1); + if (!v->de_polarity) + temp = BIT(2); + MDP3_REG_WRITE(MDP3_REG_LCDC_CTL_POLARITY, temp); + + return 0; +} + +int lcdc_start(struct mdp3_intf *intf) +{ + MDP3_REG_WRITE(MDP3_REG_LCDC_EN, BIT(0)); + wmb(); /* ensure write is finished before progressing */ + intf->active = true; + return 0; +} + +int lcdc_stop(struct mdp3_intf *intf) +{ + MDP3_REG_WRITE(MDP3_REG_LCDC_EN, 0); + wmb(); /* ensure write is finished before progressing */ + intf->active = false; + return 0; +} + +int dsi_video_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg) +{ + u32 temp; + struct mdp3_video_intf_cfg *v = &cfg->video; + + pr_debug("%s\n", __func__); + + temp = v->hsync_pulse_width | (v->hsync_period << 16); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_HSYNC_CTL, temp); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_VSYNC_PERIOD, v->vsync_period); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_VSYNC_PULSE_WIDTH, + v->vsync_pulse_width); + temp = v->display_start_x | (v->display_end_x << 16); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_DISPLAY_HCTL, temp); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_DISPLAY_V_START, v->display_start_y); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_DISPLAY_V_END, v->display_end_y); + temp = v->active_start_x | (v->active_end_x << 16); + if (v->active_h_enable) + temp |= BIT(31); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_ACTIVE_HCTL, temp); + + temp = v->active_start_y; + if (v->active_v_enable) + temp |= BIT(31); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_ACTIVE_V_START, temp); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_ACTIVE_V_END, v->active_end_y); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_HSYNC_SKEW, v->hsync_skew); + temp = 0; + if (!v->hsync_polarity) + temp |= BIT(0); + if (!v->vsync_polarity) + temp |= BIT(1); + if (!v->de_polarity) + temp |= BIT(2); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_CTL_POLARITY, temp); + + v->underflow_color |= 0x80000000; + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL, v->underflow_color); + + return 0; +} + +int dsi_video_start(struct mdp3_intf *intf) +{ + pr_debug("%s\n", __func__); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_EN, BIT(0)); + wmb(); /* ensure write is finished before progressing */ + intf->active = true; + return 0; +} + +int dsi_video_stop(struct mdp3_intf *intf) +{ + pr_debug("%s\n", __func__); + MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_EN, 0); + wmb(); /* ensure write is finished before progressing */ + intf->active = false; + return 0; +} + +int dsi_cmd_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg) +{ + u32 id_map = 0; + u32 trigger_en = 0; + + if (cfg->dsi_cmd.primary_dsi_cmd_id) + id_map = BIT(0); + if (cfg->dsi_cmd.secondary_dsi_cmd_id) + id_map = BIT(4); + + if (cfg->dsi_cmd.dsi_cmd_tg_intf_sel) + trigger_en = BIT(4); + + MDP3_REG_WRITE(MDP3_REG_DSI_CMD_MODE_ID_MAP, id_map); + MDP3_REG_WRITE(MDP3_REG_DSI_CMD_MODE_TRIGGER_EN, trigger_en); + + return 0; +} + +int dsi_cmd_start(struct mdp3_intf *intf) +{ + intf->active = true; + return 0; +} + +int dsi_cmd_stop(struct mdp3_intf *intf) +{ + intf->active = false; + return 0; +} + +int mdp3_intf_init(struct mdp3_intf *intf) +{ + switch (intf->cfg.type) { + case MDP3_DMA_OUTPUT_SEL_LCDC: + intf->config = lcdc_config; + intf->start = lcdc_start; + intf->stop = lcdc_stop; + break; + case MDP3_DMA_OUTPUT_SEL_DSI_VIDEO: + intf->config = dsi_video_config; + intf->start = dsi_video_start; + intf->stop = dsi_video_stop; + break; + case MDP3_DMA_OUTPUT_SEL_DSI_CMD: + intf->config = dsi_cmd_config; + intf->start = dsi_cmd_start; + intf->stop = dsi_cmd_stop; + break; + + default: + return -EINVAL; + } + return 0; +} diff --git a/drivers/video/fbdev/msm/mdp3_dma.h b/drivers/video/fbdev/msm/mdp3_dma.h new file mode 100644 index 0000000000000000000000000000000000000000..24caedb931f72253825d1bf6dd1b184ee20b8fb0 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_dma.h @@ -0,0 +1,395 @@ +/* Copyright (c) 2013-2014, 2016-2018, 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 MDP3_DMA_H +#define MDP3_DMA_H + +#include +#include +#include + +#define MDP_HISTOGRAM_BL_SCALE_MAX 1024 +#define MDP_HISTOGRAM_BL_LEVEL_MAX 255 +#define MDP_HISTOGRAM_FRAME_COUNT_MAX 0x20 +#define MDP_HISTOGRAM_BIT_MASK_MAX 0x4 +#define MDP_HISTOGRAM_CSC_MATRIX_MAX 0x2000 +#define MDP_HISTOGRAM_CSC_VECTOR_MAX 0x200 +#define MDP_HISTOGRAM_BIN_NUM 32 +#define MDP_LUT_SIZE 256 + +enum { + MDP3_DMA_P, + MDP3_DMA_S, + MDP3_DMA_E, + MDP3_DMA_MAX +}; + +enum { + MDP3_DMA_CAP_CURSOR = 0x1, + MDP3_DMA_CAP_COLOR_CORRECTION = 0x2, + MDP3_DMA_CAP_HISTOGRAM = 0x4, + MDP3_DMA_CAP_GAMMA_CORRECTION = 0x8, + MDP3_DMA_CAP_DITHER = 0x10, + MDP3_DMA_CAP_ALL = 0x1F +}; + +enum { + MDP3_DMA_OUTPUT_SEL_AHB, + MDP3_DMA_OUTPUT_SEL_DSI_CMD, + MDP3_DMA_OUTPUT_SEL_LCDC, + MDP3_DMA_OUTPUT_SEL_DSI_VIDEO, + MDP3_DMA_OUTPUT_SEL_MAX +}; + +enum { + MDP3_DMA_IBUF_FORMAT_RGB888, + MDP3_DMA_IBUF_FORMAT_RGB565, + MDP3_DMA_IBUF_FORMAT_XRGB8888, + MDP3_DMA_IBUF_FORMAT_UNDEFINED +}; + +enum { + MDP3_DMA_OUTPUT_PACK_PATTERN_RGB = 0x21, + MDP3_DMA_OUTPUT_PACK_PATTERN_RBG = 0x24, + MDP3_DMA_OUTPUT_PACK_PATTERN_BGR = 0x12, + MDP3_DMA_OUTPUT_PACK_PATTERN_BRG = 0x18, + MDP3_DMA_OUTPUT_PACK_PATTERN_GBR = 0x06, + MDP3_DMA_OUTPUT_PACK_PATTERN_GRB = 0x09, +}; + +enum { + MDP3_DMA_OUTPUT_PACK_ALIGN_LSB, + MDP3_DMA_OUTPUT_PACK_ALIGN_MSB +}; + +enum { + MDP3_DMA_OUTPUT_COMP_BITS_4, /*4 bits per color component*/ + MDP3_DMA_OUTPUT_COMP_BITS_5, + MDP3_DMA_OUTPUT_COMP_BITS_6, + MDP3_DMA_OUTPUT_COMP_BITS_8, +}; + +enum { + MDP3_DMA_CURSOR_FORMAT_ARGB888, +}; + +enum { + MDP3_DMA_COLOR_CORRECT_SET_1, + MDP3_DMA_COLOR_CORRECT_SET_2 +}; + +enum { + MDP3_DMA_LUT_POSITION_PRE, + MDP3_DMA_LUT_POSITION_POST +}; + +enum { + MDP3_DMA_LUT_DISABLE = 0x0, + MDP3_DMA_LUT_ENABLE_C0 = 0x01, + MDP3_DMA_LUT_ENABLE_C1 = 0x02, + MDP3_DMA_LUT_ENABLE_C2 = 0x04, + MDP3_DMA_LUT_ENABLE_ALL = 0x07, +}; + +enum { + MDP3_DMA_HISTOGRAM_BIT_MASK_NONE = 0X0, + MDP3_DMA_HISTOGRAM_BIT_MASK_ONE_MSB = 0x1, + MDP3_DMA_HISTOGRAM_BIT_MASK_TWO_MSB = 0x2, + MDP3_DMA_HISTOGRAM_BIT_MASK_THREE_MSB = 0x3 +}; + +enum { + MDP3_DMA_COLOR_FLIP_NONE, + MDP3_DMA_COLOR_FLIP_COMP1 = 0x1, + MDP3_DMA_COLOR_FLIP_COMP2 = 0x2, + MDP3_DMA_COLOR_FLIP_COMP3 = 0x4, +}; + +enum { + MDP3_DMA_CURSOR_BLEND_NONE = 0x0, + MDP3_DMA_CURSOR_BLEND_PER_PIXEL_ALPHA = 0x3, + MDP3_DMA_CURSOR_BLEND_CONSTANT_ALPHA = 0x5, + MDP3_DMA_CURSOR_BLEND_COLOR_KEYING = 0x9 +}; + +enum { + MDP3_DMA_HISTO_OP_START, + MDP3_DMA_HISTO_OP_STOP, + MDP3_DMA_HISTO_OP_CANCEL, + MDP3_DMA_HISTO_OP_RESET +}; + +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 { + u32 format; + int width; + int height; + int x; + int y; + dma_addr_t buf; + int stride; + int vsync_count; + int vporch; +}; + +struct mdp3_dma_output_config { + int dither_en; + u32 out_sel; + u32 bit_mask_polarity; + u32 color_components_flip; + u32 pack_pattern; + u32 pack_align; + u32 color_comp_out_bits; +}; + +struct mdp3_dma_cursor_blend_config { + u32 mode; + u32 transparent_color; /*color keying*/ + u32 transparency_mask; + u32 constant_alpha; +}; + +struct mdp3_dma_cursor { + int enable; /* enable cursor or not*/ + u32 format; + int width; + int height; + int x; + int y; + void *buf; + struct mdp3_dma_cursor_blend_config blend_config; +}; + +struct mdp3_dma_ccs { + 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_config { + int lut_enable; + u32 lut_sel; + u32 lut_position; + bool lut_dirty; +}; + +struct mdp3_dma_color_correct_config { + int ccs_enable; + u32 post_limit_sel; + u32 pre_limit_sel; + u32 post_bias_sel; + u32 pre_bias_sel; + u32 ccs_sel; + bool ccs_dirty; +}; + +struct mdp3_dma_histogram_config { + int frame_count; + u32 bit_mask_polarity; + u32 bit_mask; + int auto_clear_en; +}; + +struct mdp3_dma_histogram_data { + 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_notification { + void (*handler)(void *arg); + void *arg; +}; + +struct mdp3_tear_check { + int frame_rate; + bool hw_vsync_mode; + u32 tear_check_en; + u32 sync_cfg_height; + u32 vsync_init_val; + u32 sync_threshold_start; + u32 sync_threshold_continue; + u32 start_pos; + u32 rd_ptr_irq; + u32 refx100; +}; + +struct mdp3_rect { + u32 x; + u32 y; + u32 w; + u32 h; +}; + +struct mdp3_intf; + +struct mdp3_dma { + u32 dma_sel; + u32 capability; + int in_use; + int available; + + spinlock_t dma_lock; + spinlock_t histo_lock; + struct completion vsync_comp; + struct completion dma_comp; + struct completion histo_comp; + struct kernfs_node *hist_event_sd; + struct mdp3_notification vsync_client; + struct mdp3_notification dma_notifier_client; + struct mdp3_notification retire_client; + + struct mdp3_dma_output_config output_config; + struct mdp3_dma_source source_config; + + struct mdp3_dma_cursor cursor; + struct mdp3_dma_color_correct_config ccs_config; + struct mdp_csc_cfg_data ccs_cache; + int cc_vect_sel; + + struct work_struct underrun_work; + struct mutex pp_lock; + + struct mdp3_dma_lut_config lut_config; + struct mdp3_dma_histogram_config histogram_config; + int histo_state; + struct mdp3_dma_histogram_data histo_data; + unsigned int vsync_status; + bool update_src_cfg; + bool has_panic_ctrl; + struct mdp3_rect roi; + + u32 lut_sts; + u32 hist_events; + struct fb_cmap *gc_cmap; + struct fb_cmap *hist_cmap; + + bool (*busy)(void); + + int (*dma_config)(struct mdp3_dma *dma, + struct mdp3_dma_source *source_config, + struct mdp3_dma_output_config *output_config, + bool splash_screen_active); + + int (*dma_sync_config)(struct mdp3_dma *dma, struct mdp3_dma_source + *source_config, struct mdp3_tear_check *te); + + void (*dma_config_source)(struct mdp3_dma *dma); + + int (*start)(struct mdp3_dma *dma, struct mdp3_intf *intf); + + int (*stop)(struct mdp3_dma *dma, struct mdp3_intf *intf); + + int (*config_cursor)(struct mdp3_dma *dma, + struct mdp3_dma_cursor *cursor); + + int (*config_ccs)(struct mdp3_dma *dma, + struct mdp3_dma_color_correct_config *config, + struct mdp3_dma_ccs *ccs); + + int (*config_lut)(struct mdp3_dma *dma, + struct mdp3_dma_lut_config *config, + struct fb_cmap *cmap); + + int (*update)(struct mdp3_dma *dma, + void *buf, struct mdp3_intf *intf, void *data); + + int (*update_cursor)(struct mdp3_dma *dma, int x, int y); + + 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); + + void (*vsync_enable)(struct mdp3_dma *dma, + struct mdp3_notification *vsync_client); + + void (*retire_enable)(struct mdp3_dma *dma, + struct mdp3_notification *retire_client); + + void (*dma_done_notifier)(struct mdp3_dma *dma, + struct mdp3_notification *dma_client); +}; + +struct mdp3_video_intf_cfg { + int hsync_period; + int hsync_pulse_width; + int vsync_period; + int vsync_pulse_width; + int display_start_x; + int display_end_x; + int display_start_y; + int display_end_y; + int active_start_x; + int active_end_x; + int active_h_enable; + int active_start_y; + int active_end_y; + int active_v_enable; + int hsync_skew; + int hsync_polarity; + int vsync_polarity; + int de_polarity; + int underflow_color; +}; + +struct mdp3_dsi_cmd_intf_cfg { + int primary_dsi_cmd_id; + int secondary_dsi_cmd_id; + int dsi_cmd_tg_intf_sel; +}; + +struct mdp3_intf_cfg { + u32 type; + struct mdp3_video_intf_cfg video; + struct mdp3_dsi_cmd_intf_cfg dsi_cmd; +}; + +struct mdp3_intf { + struct mdp3_intf_cfg cfg; + int active; + int available; + int in_use; + int (*config)(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg); + int (*start)(struct mdp3_intf *intf); + int (*stop)(struct mdp3_intf *intf); +}; + +int mdp3_dma_init(struct mdp3_dma *dma); + +int mdp3_intf_init(struct mdp3_intf *intf); + +void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type); + +void mdp3_dma_callback_disable(struct mdp3_dma *dma, int type); + +void mdp3_hist_intr_notify(struct mdp3_dma *dma); +#endif /* MDP3_DMA_H */ diff --git a/drivers/video/fbdev/msm/mdp3_hwio.h b/drivers/video/fbdev/msm/mdp3_hwio.h new file mode 100644 index 0000000000000000000000000000000000000000..2e3d358cd19c4122d315b96e6e2d6d682c6c8757 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_hwio.h @@ -0,0 +1,361 @@ +/* Copyright (c) 2013-2014, 2016, 2018, 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 MDP3_HWIO_H +#define MDP3_HWIO_H + +#include + +/*synchronization*/ +#define MDP3_REG_SYNC_CONFIG_0 0x0300 +#define MDP3_REG_SYNC_CONFIG_1 0x0304 +#define MDP3_REG_SYNC_CONFIG_2 0x0308 +#define MDP3_REG_SYNC_STATUS_0 0x030c +#define MDP3_REG_SYNC_STATUS_1 0x0310 +#define MDP3_REG_SYNC_STATUS_2 0x0314 +#define MDP3_REG_PRIMARY_VSYNC_OUT_CTRL 0x0318 +#define MDP3_REG_SECONDARY_VSYNC_OUT_CTRL 0x031c +#define MDP3_REG_EXTERNAL_VSYNC_OUT_CTRL 0x0320 +#define MDP3_REG_VSYNC_SEL 0x0324 +#define MDP3_REG_PRIMARY_VSYNC_INIT_VAL 0x0328 +#define MDP3_REG_SECONDARY_VSYNC_INIT_VAL 0x032c +#define MDP3_REG_EXTERNAL_VSYNC_INIT_VAL 0x0330 +#define MDP3_REG_AUTOREFRESH_CONFIG_P 0x034C +#define MDP3_REG_SYNC_THRESH_0 0x0200 +#define MDP3_REG_SYNC_THRESH_1 0x0204 +#define MDP3_REG_SYNC_THRESH_2 0x0208 +#define MDP3_REG_TEAR_CHECK_EN 0x020C +#define MDP3_REG_PRIMARY_START_P0S 0x0210 +#define MDP3_REG_SECONDARY_START_POS 0x0214 +#define MDP3_REG_EXTERNAL_START_POS 0x0218 + +/*interrupt*/ +#define MDP3_REG_INTR_ENABLE 0x0020 +#define MDP3_REG_INTR_STATUS 0x0024 +#define MDP3_REG_INTR_CLEAR 0x0028 + +#define MDP3_REG_PRIMARY_RD_PTR_IRQ 0x021C +#define MDP3_REG_SECONDARY_RD_PTR_IRQ 0x0220 + +/*operation control*/ +#define MDP3_REG_DMA_P_START 0x0044 +#define MDP3_REG_DMA_S_START 0x0048 +#define MDP3_REG_DMA_E_START 0x004c + +#define MDP3_REG_DISPLAY_STATUS 0x0038 + +#define MDP3_REG_HW_VERSION 0x0070 +#define MDP3_REG_SW_RESET 0x0074 +#define MDP3_REG_SEL_CLK_OR_HCLK_TEST_BUS 0x007C + +/*EBI*/ +#define MDP3_REG_EBI2_LCD0 0x003c +#define MDP3_REG_EBI2_LCD0_YSTRIDE 0x0050 + +/*clock control*/ +#define MDP3_REG_CGC_EN 0x0100 +#define MDP3_VBIF_REG_FORCE_EN 0x0004 + +/* QOS Remapper */ +#define MDP3_DMA_P_QOS_REMAPPER 0x90090 +#define MDP3_DMA_P_WATERMARK_0 0x90094 +#define MDP3_DMA_P_WATERMARK_1 0x90098 +#define MDP3_DMA_P_WATERMARK_2 0x9009C +#define MDP3_PANIC_ROBUST_CTRL 0x900A0 +#define MDP3_PANIC_LUT0 0x900A4 +#define MDP3_PANIC_LUT1 0x900A8 +#define MDP3_ROBUST_LUT 0x900AC + +/*danger safe*/ +#define MDP3_PANIC_ROBUST_CTRL 0x900A0 + +/*DMA_P*/ +#define MDP3_REG_DMA_P_CONFIG 0x90000 +#define MDP3_REG_DMA_P_SIZE 0x90004 +#define MDP3_REG_DMA_P_IBUF_ADDR 0x90008 +#define MDP3_REG_DMA_P_IBUF_Y_STRIDE 0x9000C +#define MDP3_REG_DMA_P_PROFILE_EN 0x90020 +#define MDP3_REG_DMA_P_OUT_XY 0x90010 +#define MDP3_REG_DMA_P_CURSOR_FORMAT 0x90040 +#define MDP3_REG_DMA_P_CURSOR_SIZE 0x90044 +#define MDP3_REG_DMA_P_CURSOR_BUF_ADDR 0x90048 +#define MDP3_REG_DMA_P_CURSOR_POS 0x9004c +#define MDP3_REG_DMA_P_CURSOR_BLEND_CONFIG 0x90060 +#define MDP3_REG_DMA_P_CURSOR_BLEND_PARAM 0x90064 +#define MDP3_REG_DMA_P_CURSOR_BLEND_TRANS_MASK 0x90068 +#define MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG 0x90070 +#define MDP3_REG_DMA_P_CSC_BYPASS 0X93004 +#define MDP3_REG_DMA_P_CSC_MV1 0x93400 +#define MDP3_REG_DMA_P_CSC_MV2 0x93440 +#define MDP3_REG_DMA_P_CSC_PRE_BV1 0x93500 +#define MDP3_REG_DMA_P_CSC_PRE_BV2 0x93540 +#define MDP3_REG_DMA_P_CSC_POST_BV1 0x93580 +#define MDP3_REG_DMA_P_CSC_POST_BV2 0x935c0 +#define MDP3_REG_DMA_P_CSC_PRE_LV1 0x93600 +#define MDP3_REG_DMA_P_CSC_PRE_LV2 0x93640 +#define MDP3_REG_DMA_P_CSC_POST_LV1 0x93680 +#define MDP3_REG_DMA_P_CSC_POST_LV2 0x936c0 +#define MDP3_REG_DMA_P_CSC_LUT1 0x93800 +#define MDP3_REG_DMA_P_CSC_LUT2 0x93c00 +#define MDP3_REG_DMA_P_HIST_START 0x94000 +#define MDP3_REG_DMA_P_HIST_FRAME_CNT 0x94004 +#define MDP3_REG_DMA_P_HIST_BIT_MASK 0x94008 +#define MDP3_REG_DMA_P_HIST_RESET_SEQ_START 0x9400c +#define MDP3_REG_DMA_P_HIST_CONTROL 0x94010 +#define MDP3_REG_DMA_P_HIST_INTR_STATUS 0x94014 +#define MDP3_REG_DMA_P_HIST_INTR_CLEAR 0x94018 +#define MDP3_REG_DMA_P_HIST_INTR_ENABLE 0x9401c +#define MDP3_REG_DMA_P_HIST_STOP_REQ 0x94020 +#define MDP3_REG_DMA_P_HIST_CANCEL_REQ 0x94024 +#define MDP3_REG_DMA_P_HIST_EXTRA_INFO_0 0x94028 +#define MDP3_REG_DMA_P_HIST_EXTRA_INFO_1 0x9402c +#define MDP3_REG_DMA_P_HIST_R_DATA 0x94100 +#define MDP3_REG_DMA_P_HIST_G_DATA 0x94200 +#define MDP3_REG_DMA_P_HIST_B_DATA 0x94300 +#define MDP3_REG_DMA_P_FETCH_CFG 0x90074 +#define MDP3_REG_DMA_P_DCVS_CTRL 0x90080 +#define MDP3_REG_DMA_P_DCVS_STATUS 0x90084 + +/*DMA_S*/ +#define MDP3_REG_DMA_S_CONFIG 0xA0000 +#define MDP3_REG_DMA_S_SIZE 0xA0004 +#define MDP3_REG_DMA_S_IBUF_ADDR 0xA0008 +#define MDP3_REG_DMA_S_IBUF_Y_STRIDE 0xA000C +#define MDP3_REG_DMA_S_OUT_XY 0xA0010 + +/*DMA MASK*/ +#define MDP3_DMA_IBUF_FORMAT_MASK 0x06000000 +#define MDP3_DMA_PACK_PATTERN_MASK 0x00003f00 + +/*MISR*/ +#define MDP3_REG_MODE_CLK 0x000D0000 +#define MDP3_REG_MISR_RESET_CLK 0x000D0004 +#define MDP3_REG_EXPORT_MISR_CLK 0x000D0008 +#define MDP3_REG_MISR_CURR_VAL_CLK 0x000D000C +#define MDP3_REG_MODE_HCLK 0x000D0100 +#define MDP3_REG_MISR_RESET_HCLK 0x000D0104 +#define MDP3_REG_EXPORT_MISR_HCLK 0x000D0108 +#define MDP3_REG_MISR_CURR_VAL_HCLK 0x000D010C +#define MDP3_REG_MODE_DCLK 0x000D0200 +#define MDP3_REG_MISR_RESET_DCLK 0x000D0204 +#define MDP3_REG_EXPORT_MISR_DCLK 0x000D0208 +#define MDP3_REG_MISR_CURR_VAL_DCLK 0x000D020C +#define MDP3_REG_CAPTURED_DCLK 0x000D0210 +#define MDP3_REG_MISR_CAPT_VAL_DCLK 0x000D0214 +#define MDP3_REG_MODE_TVCLK 0x000D0300 +#define MDP3_REG_MISR_RESET_TVCLK 0x000D0304 +#define MDP3_REG_EXPORT_MISR_TVCLK 0x000D0308 +#define MDP3_REG_MISR_CURR_VAL_TVCLK 0x000D030C +#define MDP3_REG_CAPTURED_TVCLK 0x000D0310 +#define MDP3_REG_MISR_CAPT_VAL_TVCLK 0x000D0314 + +/* Select DSI operation type(CMD/VIDEO) */ +#define MDP3_REG_MODE_DSI_PCLK 0x000D0400 +#define MDP3_REG_MODE_DSI_PCLK_BLOCK_DSI_CMD 0x10 +#define MDP3_REG_MODE_DSI_PCLK_BLOCK_DSI_VIDEO1 0x20 +#define MDP3_REG_MODE_DSI_PCLK_BLOCK_DSI_VIDEO2 0x30 +/* RESET DSI MISR STATE */ +#define MDP3_REG_MISR_RESET_DSI_PCLK 0x000D0404 + +/* For reading MISR State(1) and driving data on test bus(0) */ +#define MDP3_REG_EXPORT_MISR_DSI_PCLK 0x000D0408 +/* Read MISR signature */ +#define MDP3_REG_MISR_CURR_VAL_DSI_PCLK 0x000D040C + +/* MISR status Bit0 (1) Capture Done */ +#define MDP3_REG_CAPTURED_DSI_PCLK 0x000D0410 +#define MDP3_REG_MISR_CAPT_VAL_DSI_PCLK 0x000D0414 +#define MDP3_REG_MISR_TESTBUS_CAPT_VAL 0x000D0600 + +/*interface*/ +#define MDP3_REG_LCDC_EN 0xE0000 +#define MDP3_REG_LCDC_HSYNC_CTL 0xE0004 +#define MDP3_REG_LCDC_VSYNC_PERIOD 0xE0008 +#define MDP3_REG_LCDC_VSYNC_PULSE_WIDTH 0xE000C +#define MDP3_REG_LCDC_DISPLAY_HCTL 0xE0010 +#define MDP3_REG_LCDC_DISPLAY_V_START 0xE0014 +#define MDP3_REG_LCDC_DISPLAY_V_END 0xE0018 +#define MDP3_REG_LCDC_ACTIVE_HCTL 0xE001C +#define MDP3_REG_LCDC_ACTIVE_V_START 0xE0020 +#define MDP3_REG_LCDC_ACTIVE_V_END 0xE0024 +#define MDP3_REG_LCDC_BORDER_COLOR 0xE0028 +#define MDP3_REG_LCDC_UNDERFLOW_CTL 0xE002C +#define MDP3_REG_LCDC_HSYNC_SKEW 0xE0030 +#define MDP3_REG_LCDC_TEST_CTL 0xE0034 +#define MDP3_REG_LCDC_CTL_POLARITY 0xE0038 +#define MDP3_REG_LCDC_TEST_COL_VAR1 0xE003C +#define MDP3_REG_LCDC_TEST_COL_VAR2 0xE0040 +#define MDP3_REG_LCDC_UFLOW_HIDING_CTL 0xE0044 +#define MDP3_REG_LCDC_LOST_PIXEL_CNT_VALUE 0xE0048 + +#define MDP3_REG_DSI_VIDEO_EN 0xF0000 +#define MDP3_REG_DSI_VIDEO_HSYNC_CTL 0xF0004 +#define MDP3_REG_DSI_VIDEO_VSYNC_PERIOD 0xF0008 +#define MDP3_REG_DSI_VIDEO_VSYNC_PULSE_WIDTH 0xF000C +#define MDP3_REG_DSI_VIDEO_DISPLAY_HCTL 0xF0010 +#define MDP3_REG_DSI_VIDEO_DISPLAY_V_START 0xF0014 +#define MDP3_REG_DSI_VIDEO_DISPLAY_V_END 0xF0018 +#define MDP3_REG_DSI_VIDEO_ACTIVE_HCTL 0xF001C +#define MDP3_REG_DSI_VIDEO_ACTIVE_V_START 0xF0020 +#define MDP3_REG_DSI_VIDEO_ACTIVE_V_END 0xF0024 +#define MDP3_REG_DSI_VIDEO_BORDER_COLOR 0xF0028 +#define MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL 0xF002C +#define MDP3_REG_DSI_VIDEO_HSYNC_SKEW 0xF0030 +#define MDP3_REG_DSI_VIDEO_TEST_CTL 0xF0034 +#define MDP3_REG_DSI_VIDEO_CTL_POLARITY 0xF0038 +#define MDP3_REG_DSI_VIDEO_TEST_COL_VAR1 0xF003C +#define MDP3_REG_DSI_VIDEO_TEST_COL_VAR2 0xF0040 +#define MDP3_REG_DSI_VIDEO_UFLOW_HIDING_CTL 0xF0044 +#define MDP3_REG_DSI_VIDEO_LOST_PIXEL_CNT_VALUE 0xF0048 + +#define MDP3_REG_DSI_CMD_MODE_ID_MAP 0xF1000 +#define MDP3_REG_DSI_CMD_MODE_TRIGGER_EN 0xF1004 + +#define MDP3_PPP_CSC_PFMVn(n) (0x40400 + (4 * (n))) +#define MDP3_PPP_CSC_PRMVn(n) (0x40440 + (4 * (n))) +#define MDP3_PPP_CSC_PBVn(n) (0x40500 + (4 * (n))) +#define MDP3_PPP_CSC_PLVn(n) (0x40580 + (4 * (n))) + +#define MDP3_PPP_CSC_SFMVn(n) (0x40480 + (4 * (n))) +#define MDP3_PPP_CSC_SRMVn(n) (0x404C0 + (4 * (n))) +#define MDP3_PPP_CSC_SBVn(n) (0x40540 + (4 * (n))) +#define MDP3_PPP_CSC_SLVn(n) (0x405C0 + (4 * (n))) + +#define MDP3_PPP_SCALE_PHASEX_INIT 0x1013C +#define MDP3_PPP_SCALE_PHASEY_INIT 0x10140 +#define MDP3_PPP_SCALE_PHASEX_STEP 0x10144 +#define MDP3_PPP_SCALE_PHASEY_STEP 0x10148 + +#define MDP3_PPP_OP_MODE 0x10138 + +#define MDP3_PPP_PRE_LUT 0x40800 +#define MDP3_PPP_POST_LUT 0x40C00 +#define MDP3_PPP_LUTn(n) ((4 * (n))) + +#define MDP3_PPP_BG_EDGE_REP 0x101BC +#define MDP3_PPP_SRC_EDGE_REP 0x101B8 + +#define MDP3_PPP_STRIDE_MASK 0x3FFF +#define MDP3_PPP_STRIDE1_OFFSET 16 + +#define MDP3_PPP_XY_MASK 0x0FFF +#define MDP3_PPP_XY_OFFSET 16 + +#define MDP3_PPP_SRC_SIZE 0x10108 +#define MDP3_PPP_SRCP0_ADDR 0x1010C +#define MDP3_PPP_SRCP1_ADDR 0x10110 +#define MDP3_PPP_SRCP3_ADDR 0x10118 +#define MDP3_PPP_SRC_YSTRIDE1_ADDR 0x1011C +#define MDP3_PPP_SRC_YSTRIDE2_ADDR 0x10120 +#define MDP3_PPP_SRC_FORMAT 0x10124 +#define MDP3_PPP_SRC_UNPACK_PATTERN1 0x10128 +#define MDP3_PPP_SRC_UNPACK_PATTERN2 0x1012C + +#define MDP3_PPP_OUT_FORMAT 0x10150 +#define MDP3_PPP_OUT_PACK_PATTERN1 0x10154 +#define MDP3_PPP_OUT_PACK_PATTERN2 0x10158 +#define MDP3_PPP_OUT_SIZE 0x10164 +#define MDP3_PPP_OUTP0_ADDR 0x10168 +#define MDP3_PPP_OUTP1_ADDR 0x1016C +#define MDP3_PPP_OUTP3_ADDR 0x10174 +#define MDP3_PPP_OUT_YSTRIDE1_ADDR 0x10178 +#define MDP3_PPP_OUT_YSTRIDE2_ADDR 0x1017C +#define MDP3_PPP_OUT_XY 0x1019C + +#define MDP3_PPP_BGP0_ADDR 0x101C0 +#define MDP3_PPP_BGP1_ADDR 0x101C4 +#define MDP3_PPP_BGP3_ADDR 0x101C8 +#define MDP3_PPP_BG_YSTRIDE1_ADDR 0x101CC +#define MDP3_PPP_BG_YSTRIDE2_ADDR 0x101D0 +#define MDP3_PPP_BG_FORMAT 0x101D4 +#define MDP3_PPP_BG_UNPACK_PATTERN1 0x101D8 +#define MDP3_PPP_BG_UNPACK_PATTERN2 0x101DC + +#define MDP3_TFETCH_SOLID_FILL 0x20004 +#define MDP3_TFETCH_FILL_COLOR 0x20040 + +#define MDP3_PPP_BLEND_PARAM 0x1014C + +#define MDP3_PPP_BLEND_BG_ALPHA_SEL 0x70010 + +#define MDP3_PPP_ACTIVE BIT(0) + +/*interrupt mask*/ + +#define MDP3_INTR_DP0_ROI_DONE_BIT BIT(0) +#define MDP3_INTR_DP1_ROI_DONE_BIT BIT(1) +#define MDP3_INTR_DMA_S_DONE_BIT BIT(2) +#define MDP3_INTR_DMA_E_DONE_BIT BIT(3) +#define MDP3_INTR_DP0_TERMINAL_FRAME_DONE_BIT BIT(4) +#define MDP3_INTR_DP1_TERMINAL_FRAME_DONE_BIT BIT(5) +#define MDP3_INTR_DMA_TV_DONE_BIT BIT(6) +#define MDP3_INTR_TV_ENCODER_UNDER_RUN_BIT BIT(7) +#define MDP3_INTR_SYNC_PRIMARY_LINE_BIT BIT(8) +#define MDP3_INTR_SYNC_SECONDARY_LINE_BIT BIT(9) +#define MDP3_INTR_SYNC_EXTERNAL_LINE_BIT BIT(10) +#define MDP3_INTR_DP0_FETCH_DONE_BIT BIT(11) +#define MDP3_INTR_DP1_FETCH_DONE_BIT BIT(12) +#define MDP3_INTR_TV_OUT_FRAME_START_BIT BIT(13) +#define MDP3_INTR_DMA_P_DONE_BIT BIT(14) +#define MDP3_INTR_LCDC_START_OF_FRAME_BIT BIT(15) +#define MDP3_INTR_LCDC_UNDERFLOW_BIT BIT(16) +#define MDP3_INTR_DMA_P_LINE_BIT BIT(17) +#define MDP3_INTR_DMA_S_LINE_BIT BIT(18) +#define MDP3_INTR_DMA_E_LINE_BIT BIT(19) +#define MDP3_INTR_DMA_P_HISTO_BIT BIT(20) +#define MDP3_INTR_DTV_OUT_DONE_BIT BIT(21) +#define MDP3_INTR_DTV_OUT_START_OF_FRAME_BIT BIT(22) +#define MDP3_INTR_DTV_OUT_UNDERFLOW_BIT BIT(23) +#define MDP3_INTR_DTV_OUT_LINE_BIT BIT(24) +#define MDP3_INTR_DMA_P_AUTO_FREFRESH_START_BIT BIT(25) +#define MDP3_INTR_DMA_S_AUTO_FREFRESH_START_BIT BIT(26) +#define MDP3_INTR_QPIC_EOF_ENABLE_BIT BIT(27) + +enum { + MDP3_INTR_DP0_ROI_DONE, + MDP3_INTR_DP1_ROI_DONE, + MDP3_INTR_DMA_S_DONE, + MDP3_INTR_DMA_E_DONE, + MDP3_INTR_DP0_TERMINAL_FRAME_DONE, + MDP3_INTR_DP1_TERMINAL_FRAME_DONE, + MDP3_INTR_DMA_TV_DONE, + MDP3_INTR_TV_ENCODER_UNDER_RUN, + MDP3_INTR_SYNC_PRIMARY_LINE, + MDP3_INTR_SYNC_SECONDARY_LINE, + MDP3_INTR_SYNC_EXTERNAL_LINE, + MDP3_INTR_DP0_FETCH_DONE, + MDP3_INTR_DP1_FETCH_DONE, + MDP3_INTR_TV_OUT_FRAME_START, + MDP3_INTR_DMA_P_DONE, + MDP3_INTR_LCDC_START_OF_FRAME, + MDP3_INTR_LCDC_UNDERFLOW, + MDP3_INTR_DMA_P_LINE, + MDP3_INTR_DMA_S_LINE, + MDP3_INTR_DMA_E_LINE, + MDP3_INTR_DMA_P_HISTO, + MDP3_INTR_DTV_OUT_DONE, + MDP3_INTR_DTV_OUT_START_OF_FRAME, + MDP3_INTR_DTV_OUT_UNDERFLOW, + MDP3_INTR_DTV_OUT_LINE, + MDP3_INTR_DMA_P_AUTO_FREFRESH_START, + MDP3_INTR_DMA_S_AUTO_FREFRESH_START, + MDP3_INTR_QPIC_EOF_ENABLE, +}; + +#define MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT BIT(0) +#define MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT BIT(1) +#define MDP3_PPP_DONE MDP3_INTR_DP0_ROI_DONE + +#define MDP3_DMA_P_BUSY_BIT BIT(6) + +#endif /* MDP3_HWIO_H */ diff --git a/drivers/video/fbdev/msm/mdp3_layer.c b/drivers/video/fbdev/msm/mdp3_layer.c new file mode 100644 index 0000000000000000000000000000000000000000..0078466dc2a4b326b98ffa59724b7b989aa09b12 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_layer.c @@ -0,0 +1,348 @@ +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mdp3_ctrl.h" +#include "mdp3.h" +#include "mdp3_ppp.h" +#include "mdp3_ctrl.h" +#include "mdss_fb.h" +#include "mdss_sync.h" + +enum { + MDP3_RELEASE_FENCE = 0, + MDP3_RETIRE_FENCE, +}; + +static struct mdss_fence *__mdp3_create_fence(struct msm_fb_data_type *mfd, + struct msm_sync_pt_data *sync_pt_data, u32 fence_type, + int *fence_fd, int value) +{ + struct mdss_fence *sync_fence = NULL; + char fence_name[32]; + struct mdp3_session_data *mdp3_session; + + mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + + if (fence_type == MDP3_RETIRE_FENCE) + snprintf(fence_name, sizeof(fence_name), "fb%d_retire", + mfd->index); + else + snprintf(fence_name, sizeof(fence_name), "fb%d_release", + mfd->index); + + if ((fence_type == MDP3_RETIRE_FENCE) && + (mfd->panel.type == MIPI_CMD_PANEL)) { + if (sync_pt_data->timeline_retire) { + value = sync_pt_data->timeline_retire->value + 1 + + mdp3_session->retire_cnt++; + sync_fence = mdss_fb_sync_get_fence( + sync_pt_data->timeline_retire, + fence_name, value); + + } else { + return ERR_PTR(-EPERM); + } + } else { + if (fence_type == MDP3_RETIRE_FENCE) + sync_fence = mdss_fb_sync_get_fence( + sync_pt_data->timeline_retire, + fence_name, value); + else + sync_fence = mdss_fb_sync_get_fence( + sync_pt_data->timeline, + fence_name, value); + } + + if (IS_ERR_OR_NULL(sync_fence)) { + pr_err("%s: unable to retrieve release fence\n", fence_name); + goto end; + } + + *fence_fd = mdss_get_sync_fence_fd(sync_fence); + if (*fence_fd < 0) { + pr_err("%s: get_unused_fd_flags failed error:0x%x\n", + fence_name, *fence_fd); + mdss_put_sync_fence(sync_fence); + sync_fence = NULL; + goto end; + } + pr_debug("%s:val=%d\n", mdss_get_sync_fence_name(sync_fence), value); +end: + return sync_fence; +} + +/* + * __handle_buffer_fences() - copy sync fences and return release + * fence to caller. + * + * This function copies all input sync fences to acquire fence array and + * returns release fences to caller. It acts like buff_sync ioctl. + */ +static int __mdp3_handle_buffer_fences(struct msm_fb_data_type *mfd, + struct mdp_layer_commit_v1 *commit, struct mdp_input_layer *layer_list) +{ + struct mdss_fence *fence, *release_fence, *retire_fence; + struct msm_sync_pt_data *sync_pt_data = NULL; + struct mdp_input_layer *layer; + int value; + + u32 acq_fen_count, i, ret = 0; + u32 layer_count = commit->input_layer_cnt; + + sync_pt_data = &mfd->mdp_sync_pt_data; + if (!sync_pt_data) { + pr_err("sync point data are NULL\n"); + return -EINVAL; + } + + i = mdss_fb_wait_for_fence(sync_pt_data); + if (i > 0) + pr_warn("%s: waited on %d active fences\n", + sync_pt_data->fence_name, i); + + mutex_lock(&sync_pt_data->sync_mutex); + for (i = 0, acq_fen_count = 0; i < layer_count; i++) { + layer = &layer_list[i]; + + if (layer->buffer.fence < 0) + continue; + + fence = mdss_get_fd_sync_fence(layer->buffer.fence); + if (!fence) { + pr_err("%s: sync fence get failed! fd=%d\n", + sync_pt_data->fence_name, layer->buffer.fence); + ret = -EINVAL; + goto sync_fence_err; + } else { + sync_pt_data->acq_fen[acq_fen_count++] = fence; + } + } + + sync_pt_data->acq_fen_cnt = acq_fen_count; + if (ret) + goto sync_fence_err; + + value = sync_pt_data->threshold + + atomic_read(&sync_pt_data->commit_cnt); + + release_fence = __mdp3_create_fence(mfd, sync_pt_data, + MDP3_RELEASE_FENCE, &commit->release_fence, value); + if (IS_ERR_OR_NULL(release_fence)) { + pr_err("unable to retrieve release fence\n"); + ret = PTR_ERR(release_fence); + goto release_fence_err; + } + + retire_fence = __mdp3_create_fence(mfd, sync_pt_data, + MDP3_RETIRE_FENCE, &commit->retire_fence, value); + if (IS_ERR_OR_NULL(retire_fence)) { + pr_err("unable to retrieve retire fence\n"); + ret = PTR_ERR(retire_fence); + goto retire_fence_err; + } + + mutex_unlock(&sync_pt_data->sync_mutex); + return ret; + +retire_fence_err: + put_unused_fd(commit->release_fence); + mdss_put_sync_fence(release_fence); +release_fence_err: + commit->retire_fence = -1; + commit->release_fence = -1; +sync_fence_err: + for (i = 0; i < sync_pt_data->acq_fen_cnt; i++) + mdss_put_sync_fence(sync_pt_data->acq_fen[i]); + sync_pt_data->acq_fen_cnt = 0; + + mutex_unlock(&sync_pt_data->sync_mutex); + + return ret; +} + +/* + * __map_layer_buffer() - map input layer buffer + * + */ +static int __mdp3_map_layer_buffer(struct msm_fb_data_type *mfd, + struct mdp_input_layer *input_layer) +{ + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct mdp3_dma *dma = mdp3_session->dma; + struct mdp_input_layer *layer = NULL; + struct mdp_layer_buffer *buffer; + struct msmfb_data img; + bool is_panel_type_cmd = false; + struct mdp3_img_data data; + int rc = 0; + + layer = &input_layer[0]; + buffer = &layer->buffer; + + /* current implementation only supports one plane mapping */ + if (buffer->planes[0].fd < 0) { + pr_err("invalid file descriptor for layer buffer\n"); + goto err; + } + + memset(&img, 0, sizeof(img)); + img.memory_id = buffer->planes[0].fd; + img.offset = buffer->planes[0].offset; + + memset(&data, 0, sizeof(struct mdp3_img_data)); + + if (mfd->panel.type == MIPI_CMD_PANEL) + is_panel_type_cmd = true; + if (is_panel_type_cmd) { + rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); + if (rc) { + pr_err("fail to enable iommu\n"); + return rc; + } + } + + rc = mdp3_get_img(&img, &data, MDP3_CLIENT_DMA_P); + if (rc) { + pr_err("fail to get overlay buffer\n"); + goto err; + } + + if (data.len < dma->source_config.stride * dma->source_config.height) { + pr_err("buf size(0x%lx) is smaller than dma config(0x%x)\n", + data.len, (dma->source_config.stride * + dma->source_config.height)); + mdp3_put_img(&data, MDP3_CLIENT_DMA_P); + rc = -EINVAL; + goto err; + } + + rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data); + if (rc) { + pr_err("fail to queue the overlay buffer, buffer drop\n"); + mdp3_put_img(&data, MDP3_CLIENT_DMA_P); + goto err; + } + rc = 0; +err: + if (is_panel_type_cmd) + mdp3_iommu_disable(MDP3_CLIENT_DMA_P); + return rc; +} + +int mdp3_layer_pre_commit(struct msm_fb_data_type *mfd, + struct file *file, struct mdp_layer_commit_v1 *commit) +{ + int ret; + struct mdp_input_layer *layer, *layer_list; + struct mdp3_session_data *mdp3_session; + struct mdp3_dma *dma; + int layer_count = commit->input_layer_cnt; + int stride, format; + + /* Handle NULL commit */ + if (!layer_count) { + pr_debug("Handle NULL commit\n"); + return 0; + } + + mdp3_session = mfd->mdp.private1; + dma = mdp3_session->dma; + + mutex_lock(&mdp3_session->lock); + + mdp3_bufq_deinit(&mdp3_session->bufq_in); + + layer_list = commit->input_layers; + layer = &layer_list[0]; + + stride = layer->buffer.width * ppp_bpp(layer->buffer.format); + format = mdp3_ctrl_get_source_format(layer->buffer.format); + pr_debug("stride:%d layer_width:%d", stride, layer->buffer.width); + + if ((dma->source_config.format != format) || + (dma->source_config.stride != stride)) { + dma->source_config.format = format; + dma->source_config.stride = stride; + dma->output_config.pack_pattern = + mdp3_ctrl_get_pack_pattern(layer->buffer.format); + dma->update_src_cfg = true; + } + mdp3_session->overlay.id = 1; + + ret = __mdp3_handle_buffer_fences(mfd, commit, layer_list); + if (ret) { + pr_err("Failed to handle buffer fences\n"); + mutex_unlock(&mdp3_session->lock); + return ret; + } + + ret = __mdp3_map_layer_buffer(mfd, layer); + if (ret) { + pr_err("Failed to map buffer\n"); + mutex_unlock(&mdp3_session->lock); + return ret; + } + + pr_debug("mdp3 precommit ret = %d\n", ret); + mutex_unlock(&mdp3_session->lock); + return ret; +} + +/* + * mdp3_layer_atomic_validate() - validate input layers + * @mfd: Framebuffer data structure for display + * @commit: Commit version-1 structure for display + * + * This function validates only input layers received from client. It + * does perform any validation for mdp_output_layer defined for writeback + * display. + */ +int mdp3_layer_atomic_validate(struct msm_fb_data_type *mfd, + struct file *file, struct mdp_layer_commit_v1 *commit) +{ + struct mdp3_session_data *mdp3_session; + + if (!mfd || !commit) { + pr_err("invalid input params\n"); + return -EINVAL; + } + + if (mdss_fb_is_power_off(mfd)) { + pr_err("display interface is in off state fb:%d\n", + mfd->index); + return -EPERM; + } + + mdp3_session = mfd->mdp.private1; + + if (mdp3_session->in_splash_screen) { + mdp3_ctrl_reset(mfd); + mdp3_session->in_splash_screen = 0; + } + + return 0; +} + diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c new file mode 100644 index 0000000000000000000000000000000000000000..34f08230aa51395446ef9e6ae280d1ba73c4e740 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_ppp.c @@ -0,0 +1,1721 @@ +/* Copyright (c) 2007, 2013-2014, 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" +#include + +#include "mdss_fb.h" +#include "mdp3_ppp.h" +#include "mdp3_hwio.h" +#include "mdp3.h" +#include "mdss_debug.h" +#include "mdss_sync.h" + +#define MDP_IS_IMGTYPE_BAD(x) ((x) >= MDP_IMGTYPE_LIMIT) +#define MDP_RELEASE_BW_TIMEOUT 50 + +#define MDP_PPP_MAX_BPP 4 +#define MDP_PPP_DYNAMIC_FACTOR 3 +#define MDP_PPP_MAX_READ_WRITE 3 +#define MDP_PPP_MAX_WIDTH 0xFFF +#define ENABLE_SOLID_FILL 0x2 +#define DISABLE_SOLID_FILL 0x0 +#define BLEND_LATENCY 3 +#define CSC_LATENCY 1 + +#define YUV_BW_FUDGE_NUM 10 +#define YUV_BW_FUDGE_DEN 10 + +struct ppp_resource ppp_res; + +static const bool valid_fmt[MDP_IMGTYPE_LIMIT] = { + [MDP_RGB_565] = true, + [MDP_BGR_565] = true, + [MDP_RGB_888] = true, + [MDP_BGR_888] = true, + [MDP_BGRA_8888] = true, + [MDP_RGBA_8888] = true, + [MDP_ARGB_8888] = true, + [MDP_XRGB_8888] = true, + [MDP_RGBX_8888] = true, + [MDP_Y_CRCB_H2V2] = true, + [MDP_Y_CBCR_H2V2] = true, + [MDP_Y_CBCR_H2V2_ADRENO] = true, + [MDP_Y_CBCR_H2V2_VENUS] = true, + [MDP_YCRYCB_H2V1] = true, + [MDP_Y_CBCR_H2V1] = true, + [MDP_Y_CRCB_H2V1] = true, + [MDP_BGRX_8888] = true, +}; + +#define MAX_LIST_WINDOW 16 +#define MDP3_PPP_MAX_LIST_REQ 8 + +struct blit_req_list { + int count; + struct mdp_blit_req req_list[MAX_LIST_WINDOW]; + struct mdp3_img_data src_data[MAX_LIST_WINDOW]; + struct mdp3_img_data dst_data[MAX_LIST_WINDOW]; + struct mdss_fence *acq_fen[MDP_MAX_FENCE_FD]; + u32 acq_fen_cnt; + int cur_rel_fen_fd; + struct sync_pt *cur_rel_sync_pt; + struct mdss_fence *cur_rel_fence; + struct mdss_fence *last_rel_fence; +}; + +struct blit_req_queue { + struct blit_req_list req[MDP3_PPP_MAX_LIST_REQ]; + int count; + int push_idx; + int pop_idx; +}; + +struct ppp_status { + bool wait_for_pop; + struct completion ppp_comp; + struct completion pop_q_comp; + struct mutex req_mutex; /* Protect request queue */ + struct mutex config_ppp_mutex; /* Only one client configure register */ + struct msm_fb_data_type *mfd; + + struct kthread_work blit_work; + struct kthread_worker kworker; + struct task_struct *blit_thread; + struct blit_req_queue req_q; + + struct mdss_timeline *timeline; + + int timeline_value; + + struct timer_list free_bw_timer; + struct work_struct free_bw_work; + bool bw_update; + bool bw_on; + u32 mdp_clk; +}; + +static struct ppp_status *ppp_stat; +static bool is_blit_optimization_possible(struct blit_req_list *req, int indx); + +static inline u64 fudge_factor(u64 val, u32 numer, u32 denom) +{ + u64 result = (val * (u64)numer); + + do_div(result, denom); + return result; +} + +int ppp_get_bpp(uint32_t format, uint32_t fb_format) +{ + int bpp = -EINVAL; + + if (format == MDP_FB_FORMAT) + format = fb_format; + + bpp = ppp_bpp(format); + if (bpp <= 0) + pr_err("%s incorrect format %d\n", __func__, format); + return bpp; +} + +int mdp3_ppp_get_img(struct mdp_img *img, struct mdp_blit_req *req, + struct mdp3_img_data *data) +{ + struct msmfb_data fb_data; + uint32_t stride; + int bpp = ppp_bpp(img->format); + + if (bpp <= 0) { + pr_err("%s incorrect format %d\n", __func__, img->format); + return -EINVAL; + } + + if (img->width > MDP_PPP_MAX_WIDTH) { + pr_err("%s incorrect width %d\n", __func__, img->width); + return -EINVAL; + } + + fb_data.flags = img->priv; + fb_data.memory_id = img->memory_id; + fb_data.offset = 0; + + stride = img->width * bpp; + data->padding = 16 * stride; + + return mdp3_get_img(&fb_data, data, MDP3_CLIENT_PPP); +} + +/* Check format */ +int mdp3_ppp_verify_fmt(struct mdp_blit_req *req) +{ + if (MDP_IS_IMGTYPE_BAD(req->src.format) || + MDP_IS_IMGTYPE_BAD(req->dst.format)) { + pr_err("%s: Color format out of range\n", __func__); + return -EINVAL; + } + + if (!valid_fmt[req->src.format] || + !valid_fmt[req->dst.format]) { + pr_err("%s: Color format not supported\n", __func__); + return -EINVAL; + } + return 0; +} + +/* Check resolution */ +int mdp3_ppp_verify_res(struct mdp_blit_req *req) +{ + if ((req->src.width == 0) || (req->src.height == 0) || + (req->src_rect.w == 0) || (req->src_rect.h == 0) || + (req->dst.width == 0) || (req->dst.height == 0) || + (req->dst_rect.w == 0) || (req->dst_rect.h == 0)) { + pr_err("%s: Height/width can't be 0\n", __func__); + return -EINVAL; + } + + if (((req->src_rect.x + req->src_rect.w) > req->src.width) || + ((req->src_rect.y + req->src_rect.h) > req->src.height)) { + pr_err("%s: src roi larger than boundary\n", __func__); + return -EINVAL; + } + + if (((req->dst_rect.x + req->dst_rect.w) > req->dst.width) || + ((req->dst_rect.y + req->dst_rect.h) > req->dst.height)) { + pr_err("%s: dst roi larger than boundary\n", __func__); + return -EINVAL; + } + return 0; +} + +/* scaling range check */ +int mdp3_ppp_verify_scale(struct mdp_blit_req *req) +{ + u32 src_width, src_height, dst_width, dst_height; + + src_width = req->src_rect.w; + src_height = req->src_rect.h; + + if (req->flags & MDP_ROT_90) { + dst_width = req->dst_rect.h; + dst_height = req->dst_rect.w; + } else { + dst_width = req->dst_rect.w; + dst_height = req->dst_rect.h; + } + + switch (req->dst.format) { + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V2: + src_width = (src_width / 2) * 2; + src_height = (src_height / 2) * 2; + dst_width = (dst_width / 2) * 2; + dst_height = (dst_height / 2) * 2; + break; + + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + case MDP_YCRYCB_H2V1: + src_width = (src_width / 2) * 2; + dst_width = (dst_width / 2) * 2; + break; + + default: + break; + } + + if (((MDP_SCALE_Q_FACTOR * dst_width) / src_width > + MDP_MAX_X_SCALE_FACTOR) + || ((MDP_SCALE_Q_FACTOR * dst_width) / src_width < + MDP_MIN_X_SCALE_FACTOR)) { + pr_err("%s: x req scale factor beyond capability\n", __func__); + return -EINVAL; + } + + if (((MDP_SCALE_Q_FACTOR * dst_height) / src_height > + MDP_MAX_Y_SCALE_FACTOR) + || ((MDP_SCALE_Q_FACTOR * dst_height) / src_height < + MDP_MIN_Y_SCALE_FACTOR)) { + pr_err("%s: y req scale factor beyond capability\n", __func__); + return -EINVAL; + } + return 0; +} + +/* operation check */ +int mdp3_ppp_verify_op(struct mdp_blit_req *req) +{ + /* + * MDP_DEINTERLACE & MDP_SHARPENING Flags are not valid for MDP3 + * so using them together for MDP_SMART_BLIT. + */ + if ((req->flags & MDP_SMART_BLIT) == MDP_SMART_BLIT) + return 0; + if (req->flags & MDP_DEINTERLACE) { + pr_err("\n%s(): deinterlace not supported", __func__); + return -EINVAL; + } + + if (req->flags & MDP_SHARPENING) { + pr_err("\n%s(): sharpening not supported", __func__); + return -EINVAL; + } + return 0; +} + +int mdp3_ppp_verify_req(struct mdp_blit_req *req) +{ + int rc; + + if (req == NULL) { + pr_err("%s: req == null\n", __func__); + return -EINVAL; + } + + rc = mdp3_ppp_verify_fmt(req); + rc |= mdp3_ppp_verify_res(req); + rc |= mdp3_ppp_verify_scale(req); + rc |= mdp3_ppp_verify_op(req); + + return rc; +} + +int mdp3_ppp_pipe_wait(void) +{ + int ret = 1; + + /* + * wait 200 ms for ppp operation to complete before declaring + * the MDP hung + */ + ret = wait_for_completion_timeout( + &ppp_stat->ppp_comp, msecs_to_jiffies(200)); + if (!ret) + pr_err("%s: Timed out waiting for the MDP.\n", + __func__); + + return ret; +} + +uint32_t mdp3_calc_tpval(struct ppp_img_desc *img, uint32_t old_tp) +{ + uint32_t tpVal; + uint8_t plane_tp; + + tpVal = 0; + if ((img->color_fmt == MDP_RGB_565) + || (img->color_fmt == MDP_BGR_565)) { + /* transparent color conversion into 24 bpp */ + plane_tp = (uint8_t) ((old_tp & 0xF800) >> 11); + tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 16; + plane_tp = (uint8_t) (old_tp & 0x1F); + tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 8; + + plane_tp = (uint8_t) ((old_tp & 0x7E0) >> 5); + tpVal |= ((plane_tp << 2) | ((plane_tp & 0x30) >> 4)); + } else { + /* 24bit RGB to RBG conversion */ + tpVal = (old_tp & 0xFF00) >> 8; + tpVal |= (old_tp & 0xFF) << 8; + tpVal |= (old_tp & 0xFF0000); + } + + return tpVal; +} + +static void mdp3_ppp_intr_handler(int type, void *arg) +{ + complete(&ppp_stat->ppp_comp); +} + +static int mdp3_ppp_callback_setup(void) +{ + int rc; + struct mdp3_intr_cb ppp_done_cb = { + .cb = mdp3_ppp_intr_handler, + .data = NULL, + }; + + rc = mdp3_set_intr_callback(MDP3_PPP_DONE, &ppp_done_cb); + return rc; +} + +void mdp3_ppp_kickoff(void) +{ + init_completion(&ppp_stat->ppp_comp); + mdp3_irq_enable(MDP3_PPP_DONE); + ppp_enable(); + ATRACE_BEGIN("mdp3_wait_for_ppp_comp"); + mdp3_ppp_pipe_wait(); + ATRACE_END("mdp3_wait_for_ppp_comp"); + mdp3_irq_disable(MDP3_PPP_DONE); +} + +struct bpp_info { + int bpp_num; + int bpp_den; + int bpp_pln; +}; + +int mdp3_get_bpp_info(int format, struct bpp_info *bpp) +{ + int rc = 0; + + switch (format) { + case MDP_RGB_565: + case MDP_BGR_565: + bpp->bpp_num = 2; + bpp->bpp_den = 1; + bpp->bpp_pln = 2; + break; + case MDP_RGB_888: + case MDP_BGR_888: + bpp->bpp_num = 3; + bpp->bpp_den = 1; + bpp->bpp_pln = 3; + break; + case MDP_BGRA_8888: + case MDP_RGBA_8888: + case MDP_ARGB_8888: + case MDP_XRGB_8888: + case MDP_RGBX_8888: + case MDP_BGRX_8888: + bpp->bpp_num = 4; + bpp->bpp_den = 1; + bpp->bpp_pln = 4; + break; + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_ADRENO: + case MDP_Y_CBCR_H2V2_VENUS: + bpp->bpp_num = 3; + bpp->bpp_den = 2; + bpp->bpp_pln = 1; + break; + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + bpp->bpp_num = 2; + bpp->bpp_den = 1; + bpp->bpp_pln = 1; + break; + case MDP_YCRYCB_H2V1: + bpp->bpp_num = 2; + bpp->bpp_den = 1; + bpp->bpp_pln = 2; + break; + default: + rc = -EINVAL; + } + return rc; +} + +bool mdp3_is_blend(struct mdp_blit_req *req) +{ + if ((req->transp_mask != MDP_TRANSP_NOP) || + (req->alpha < MDP_ALPHA_NOP) || + (req->src.format == MDP_ARGB_8888) || + (req->src.format == MDP_BGRA_8888) || + (req->src.format == MDP_RGBA_8888)) + return true; + return false; +} + +bool mdp3_is_scale(struct mdp_blit_req *req) +{ + if (req->flags & MDP_ROT_90) { + if (req->src_rect.w != req->dst_rect.h || + req->src_rect.h != req->dst_rect.w) + return true; + } else { + if (req->src_rect.h != req->dst_rect.h || + req->src_rect.w != req->dst_rect.w) + return true; + } + return false; +} + +u32 mdp3_clk_calc(struct msm_fb_data_type *mfd, + struct blit_req_list *lreq, u32 fps) +{ + int i, lcount = 0; + struct mdp_blit_req *req; + u64 mdp_clk_rate = 0; + u32 scale_x = 0, scale_y = 0, scale = 0; + u32 blend_l, csc_l; + + lcount = lreq->count; + + blend_l = 100 * BLEND_LATENCY; + csc_l = 100 * CSC_LATENCY; + + for (i = 0; i < lcount; i++) { + req = &(lreq->req_list[i]); + + if (req->flags & MDP_SMART_BLIT) + continue; + + if (mdp3_is_scale(req)) { + if (req->flags & MDP_ROT_90) { + scale_x = 100 * req->src_rect.h / + req->dst_rect.w; + scale_y = 100 * req->src_rect.w / + req->dst_rect.h; + } else { + scale_x = 100 * req->src_rect.w / + req->dst_rect.w; + scale_y = 100 * req->src_rect.h / + req->dst_rect.h; + } + scale = max(scale_x, scale_y); + } + scale = scale >= 100 ? scale : 100; + if (mdp3_is_blend(req)) + scale = max(scale, blend_l); + + if (!check_if_rgb(req->src.format)) + scale = max(scale, csc_l); + + mdp_clk_rate += (req->src_rect.w * req->src_rect.h * + scale / 100) * fps; + } + mdp_clk_rate += (ppp_res.solid_fill_pixel * fps); + mdp_clk_rate = fudge_factor(mdp_clk_rate, + CLK_FUDGE_NUM, CLK_FUDGE_DEN); + pr_debug("mdp_clk_rate for ppp = %llu\n", mdp_clk_rate); + mdp_clk_rate = mdp3_clk_round_off(mdp_clk_rate); + + return mdp_clk_rate; +} + +u64 mdp3_adjust_scale_factor(struct mdp_blit_req *req, u32 bw_req, int bpp) +{ + int src_h, src_w; + int dst_h, dst_w; + + src_h = req->src_rect.h; + src_w = req->src_rect.w; + + dst_h = req->dst_rect.h; + dst_w = req->dst_rect.w; + + if ((!(req->flags & MDP_ROT_90) && src_h == dst_h && + src_w == dst_w) || ((req->flags & MDP_ROT_90) && + src_h == dst_w && src_w == dst_h)) + return bw_req; + + bw_req = (bw_req + (bw_req * dst_h) / (4 * src_h)); + bw_req = (bw_req + (bw_req * dst_w) / (4 * src_w) + + (bw_req * dst_w) / (bpp * src_w)); + return bw_req; +} + +int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd, + struct blit_req_list *lreq) +{ + struct mdss_panel_info *panel_info = mfd->panel_info; + int i, lcount = 0; + struct mdp_blit_req *req; + struct bpp_info bpp; + u64 old_solid_fill_pixel = 0; + u64 new_solid_fill_pixel = 0; + u64 src_read_bw = 0; + u32 bg_read_bw = 0; + u32 dst_write_bw = 0; + u64 honest_ppp_ab = 0; + u32 fps = 0; + int smart_blit_fg_indx = -1; + u32 smart_blit_bg_read_bw = 0; + + ATRACE_BEGIN(__func__); + lcount = lreq->count; + if (lcount == 0) { + pr_err("Blit with request count 0, continue to recover!!!\n"); + ATRACE_END(__func__); + return 0; + } + if (lreq->req_list[0].flags & MDP_SOLID_FILL) { + req = &(lreq->req_list[0]); + mdp3_get_bpp_info(req->dst.format, &bpp); + old_solid_fill_pixel = ppp_res.solid_fill_pixel; + new_solid_fill_pixel = req->dst_rect.w * req->dst_rect.h; + ppp_res.solid_fill_pixel += new_solid_fill_pixel; + ppp_res.solid_fill_byte += req->dst_rect.w * req->dst_rect.h * + bpp.bpp_num / bpp.bpp_den; + if ((old_solid_fill_pixel >= new_solid_fill_pixel) || + (mdp3_res->solid_fill_vote_en)) { + pr_debug("Last fill pixels are higher or fill_en %d\n", + mdp3_res->solid_fill_vote_en); + ATRACE_END(__func__); + return 0; + } + } + + for (i = 0; i < lcount; i++) { + /* Set Smart blit flag before BW calculation */ + is_blit_optimization_possible(lreq, i); + req = &(lreq->req_list[i]); + + if (req->fps > 0 && req->fps <= panel_info->mipi.frame_rate) { + if (fps == 0) + fps = req->fps; + else + fps = panel_info->mipi.frame_rate; + } + + mdp3_get_bpp_info(req->src.format, &bpp); + if (lreq->req_list[i].flags & MDP_SMART_BLIT) { + /* + * Flag for smart blit FG layer index + * If blit request at index "n" has + * MDP_SMART_BLIT flag set then it will be used as BG + * layer in smart blit and request at index "n+1" + * will be used as FG layer + */ + smart_blit_fg_indx = i + 1; + bg_read_bw = req->src_rect.w * req->src_rect.h * + bpp.bpp_num / bpp.bpp_den; + bg_read_bw = mdp3_adjust_scale_factor(req, + bg_read_bw, bpp.bpp_pln); + /* Cache read BW of smart blit BG layer */ + smart_blit_bg_read_bw = bg_read_bw; + } else { + src_read_bw = req->src_rect.w * req->src_rect.h * + bpp.bpp_num / bpp.bpp_den; + src_read_bw = mdp3_adjust_scale_factor(req, + src_read_bw, bpp.bpp_pln); + if (!(check_if_rgb(req->src.format))) { + src_read_bw = fudge_factor(src_read_bw, + YUV_BW_FUDGE_NUM, + YUV_BW_FUDGE_DEN); + } + mdp3_get_bpp_info(req->dst.format, &bpp); + + if (smart_blit_fg_indx == i) { + bg_read_bw = smart_blit_bg_read_bw; + smart_blit_fg_indx = -1; + } else { + if ((req->transp_mask != MDP_TRANSP_NOP) || + (req->alpha < MDP_ALPHA_NOP) || + (req->src.format == MDP_ARGB_8888) || + (req->src.format == MDP_BGRA_8888) || + (req->src.format == MDP_RGBA_8888)) { + bg_read_bw = req->dst_rect.w * + req->dst_rect.h * + bpp.bpp_num / bpp.bpp_den; + bg_read_bw = mdp3_adjust_scale_factor( + req, bg_read_bw, + bpp.bpp_pln); + } else { + bg_read_bw = 0; + } + } + dst_write_bw = req->dst_rect.w * req->dst_rect.h * + bpp.bpp_num / bpp.bpp_den; + honest_ppp_ab += (src_read_bw + bg_read_bw + + dst_write_bw); + } + } + + if (fps == 0) + fps = panel_info->mipi.frame_rate; + + if (lreq->req_list[0].flags & MDP_SOLID_FILL) { + honest_ppp_ab = ppp_res.solid_fill_byte * 4; + pr_debug("solid fill honest_ppp_ab %llu\n", honest_ppp_ab); + } else { + honest_ppp_ab += ppp_res.solid_fill_byte; + mdp3_res->solid_fill_vote_en = true; + } + + honest_ppp_ab = honest_ppp_ab * fps; + if (honest_ppp_ab != ppp_res.next_ab) { + ppp_res.next_ab = honest_ppp_ab; + ppp_res.next_ib = honest_ppp_ab; + ppp_stat->bw_update = true; + pr_debug("solid fill ab = %llx, total ab = %llx ", + (ppp_res.solid_fill_byte * fps), honest_ppp_ab); + pr_debug("(%d fps) Solid_fill_vote %d\n", + fps, mdp3_res->solid_fill_vote_en); + ATRACE_INT("mdp3_ppp_bus_quota", honest_ppp_ab); + } + ppp_res.clk_rate = mdp3_clk_calc(mfd, lreq, fps); + ATRACE_INT("mdp3_ppp_clk_rate", ppp_res.clk_rate); + ATRACE_END(__func__); + return 0; +} + +int mdp3_ppp_turnon(struct msm_fb_data_type *mfd, int on_off) +{ + uint64_t ab = 0, ib = 0; + int rate = 0; + int rc; + + if (on_off) { + rate = ppp_res.clk_rate; + ab = ppp_res.next_ab; + ib = ppp_res.next_ib; + } + mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, rate, MDP3_CLIENT_PPP); + rc = mdp3_res_update(on_off, 0, MDP3_CLIENT_PPP); + if (rc < 0) { + pr_err("%s: mdp3_clk_enable failed\n", __func__); + return rc; + } + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib); + if (rc < 0) { + mdp3_res_update(!on_off, 0, MDP3_CLIENT_PPP); + pr_err("%s: scale_set_quota failed\n", __func__); + return rc; + } + ppp_stat->bw_on = on_off; + ppp_stat->mdp_clk = MDP_CORE_CLK_RATE_SVS; + ppp_stat->bw_update = false; + return 0; +} + +void mdp3_start_ppp(struct ppp_blit_op *blit_op) +{ + /* Wait for the pipe to clear */ + if (MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS) & + MDP3_PPP_ACTIVE) { + pr_err("ppp core is hung up on previous request\n"); + return; + } + config_ppp_op_mode(blit_op); + if (blit_op->solid_fill) { + MDP3_REG_WRITE(0x10138, 0x10000000); + MDP3_REG_WRITE(0x1014c, 0xffffffff); + MDP3_REG_WRITE(0x101b8, 0); + MDP3_REG_WRITE(0x101bc, 0); + MDP3_REG_WRITE(0x1013c, 0); + MDP3_REG_WRITE(0x10140, 0); + MDP3_REG_WRITE(0x10144, 0); + MDP3_REG_WRITE(0x10148, 0); + MDP3_REG_WRITE(MDP3_TFETCH_FILL_COLOR, + blit_op->solid_fill_color); + MDP3_REG_WRITE(MDP3_TFETCH_SOLID_FILL, + ENABLE_SOLID_FILL); + } else { + MDP3_REG_WRITE(MDP3_TFETCH_SOLID_FILL, + DISABLE_SOLID_FILL); + } + /* Skip PPP kickoff for SMART_BLIT BG layer */ + if (blit_op->mdp_op & MDPOP_SMART_BLIT) + pr_debug("Skip mdp3_ppp_kickoff\n"); + else + mdp3_ppp_kickoff(); + + if (!(blit_op->solid_fill)) { + ppp_res.solid_fill_pixel = 0; + ppp_res.solid_fill_byte = 0; + } +} + +static int solid_fill_workaround(struct mdp_blit_req *req, + struct ppp_blit_op *blit_op) +{ + /* Make width 2 when there is a solid fill of width 1, and make + * sure width does not become zero while trying to avoid odd width + */ + if (blit_op->dst.roi.width == 1) { + if (req->dst_rect.x + 2 > req->dst.width) { + pr_err("%s: Unable to handle solid fill of width 1", + __func__); + return -EINVAL; + } + blit_op->dst.roi.width = 2; + } + if (blit_op->src.roi.width == 1) { + if (req->src_rect.x + 2 > req->src.width) { + pr_err("%s: Unable to handle solid fill of width 1", + __func__); + return -EINVAL; + } + blit_op->src.roi.width = 2; + } + + /* Avoid odd width, as it could hang ppp during solid fill */ + blit_op->dst.roi.width = (blit_op->dst.roi.width / 2) * 2; + blit_op->src.roi.width = (blit_op->src.roi.width / 2) * 2; + + /* Set src format to RGBX, to avoid ppp hang issues */ + blit_op->src.color_fmt = MDP_RGBX_8888; + + /* Avoid RGBA format, as it could hang ppp during solid fill */ + if (blit_op->dst.color_fmt == MDP_RGBA_8888) + blit_op->dst.color_fmt = MDP_RGBX_8888; + return 0; +} + +static int mdp3_ppp_process_req(struct ppp_blit_op *blit_op, + struct mdp_blit_req *req, struct mdp3_img_data *src_data, + struct mdp3_img_data *dst_data) +{ + unsigned long srcp0_start, srcp0_len, dst_start, dst_len; + uint32_t dst_width, dst_height; + int ret = 0; + + srcp0_start = (unsigned long) src_data->addr; + srcp0_len = (unsigned long) src_data->len; + dst_start = (unsigned long) dst_data->addr; + dst_len = (unsigned long) dst_data->len; + + blit_op->dst.prop.width = req->dst.width; + blit_op->dst.prop.height = req->dst.height; + + blit_op->dst.color_fmt = req->dst.format; + blit_op->dst.p0 = (void *) dst_start; + blit_op->dst.p0 += req->dst.offset; + + blit_op->dst.roi.x = req->dst_rect.x; + blit_op->dst.roi.y = req->dst_rect.y; + blit_op->dst.roi.width = req->dst_rect.w; + blit_op->dst.roi.height = req->dst_rect.h; + + blit_op->src.roi.x = req->src_rect.x; + blit_op->src.roi.y = req->src_rect.y; + blit_op->src.roi.width = req->src_rect.w; + blit_op->src.roi.height = req->src_rect.h; + + blit_op->src.prop.width = req->src.width; + blit_op->src.prop.height = req->src.height; + blit_op->src.color_fmt = req->src.format; + + + blit_op->src.p0 = (void *) (srcp0_start + req->src.offset); + if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_ADRENO) + blit_op->src.p1 = + (void *) ((uint32_t) blit_op->src.p0 + + ALIGN((ALIGN(req->src.width, 32) * + ALIGN(req->src.height, 32)), 4096)); + else if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_VENUS) + blit_op->src.p1 = + (void *) ((uint32_t) blit_op->src.p0 + + ALIGN((ALIGN(req->src.width, 128) * + ALIGN(req->src.height, 32)), 4096)); + else + blit_op->src.p1 = (void *) ((uint32_t) blit_op->src.p0 + + req->src.width * req->src.height); + + if (req->flags & MDP_IS_FG) + blit_op->mdp_op |= MDPOP_LAYER_IS_FG; + + /* blending check */ + if (req->transp_mask != MDP_TRANSP_NOP) { + blit_op->mdp_op |= MDPOP_TRANSP; + blit_op->blend.trans_color = + mdp3_calc_tpval(&blit_op->src, req->transp_mask); + } else { + blit_op->blend.trans_color = 0; + } + + req->alpha &= 0xff; + if (req->alpha < MDP_ALPHA_NOP) { + blit_op->mdp_op |= MDPOP_ALPHAB; + blit_op->blend.const_alpha = req->alpha; + } else { + blit_op->blend.const_alpha = 0xff; + } + + /* rotation check */ + if (req->flags & MDP_FLIP_LR) + blit_op->mdp_op |= MDPOP_LR; + if (req->flags & MDP_FLIP_UD) + blit_op->mdp_op |= MDPOP_UD; + if (req->flags & MDP_ROT_90) + blit_op->mdp_op |= MDPOP_ROT90; + if (req->flags & MDP_DITHER) + blit_op->mdp_op |= MDPOP_DITHER; + + if (req->flags & MDP_BLEND_FG_PREMULT) + blit_op->mdp_op |= MDPOP_FG_PM_ALPHA; + + /* scale check */ + if (req->flags & MDP_ROT_90) { + dst_width = req->dst_rect.h; + dst_height = req->dst_rect.w; + } else { + dst_width = req->dst_rect.w; + dst_height = req->dst_rect.h; + } + + if ((blit_op->src.roi.width != dst_width) || + (blit_op->src.roi.height != dst_height)) + blit_op->mdp_op |= MDPOP_ASCALE; + + if (req->flags & MDP_BLUR) + blit_op->mdp_op |= MDPOP_ASCALE | MDPOP_BLUR; + + if (req->flags & MDP_SOLID_FILL) { + ret = solid_fill_workaround(req, blit_op); + if (ret) + return ret; + + blit_op->solid_fill_color = (req->const_color.g & 0xFF)| + (req->const_color.r & 0xFF) << 8 | + (req->const_color.b & 0xFF) << 16 | + (req->const_color.alpha & 0xFF) << 24; + blit_op->solid_fill = true; + } else { + blit_op->solid_fill = false; + } + + if (req->flags & MDP_SMART_BLIT) + blit_op->mdp_op |= MDPOP_SMART_BLIT; + + return ret; +} + +static void mdp3_ppp_tile_workaround(struct ppp_blit_op *blit_op, + struct mdp_blit_req *req) +{ + int dst_h, src_w, i; + uint32_t mdp_op = blit_op->mdp_op; + void *src_p0 = blit_op->src.p0; + void *src_p1 = blit_op->src.p1; + void *dst_p0 = blit_op->dst.p0; + + src_w = req->src_rect.w; + dst_h = blit_op->dst.roi.height; + /* bg tile fetching HW workaround */ + for (i = 0; i < (req->dst_rect.h / 16); i++) { + /* this tile size */ + blit_op->dst.roi.height = 16; + blit_op->src.roi.width = + (16 * req->src_rect.w) / req->dst_rect.h; + + /* if it's out of scale range... */ + if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / + blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) + blit_op->src.roi.width = + (MDP_SCALE_Q_FACTOR * + blit_op->dst.roi.height) / + MDP_MAX_X_SCALE_FACTOR; + else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / + blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) + blit_op->src.roi.width = + (MDP_SCALE_Q_FACTOR * + blit_op->dst.roi.height) / + MDP_MIN_X_SCALE_FACTOR; + + mdp3_start_ppp(blit_op); + + /* next tile location */ + blit_op->dst.roi.y += 16; + blit_op->src.roi.x += blit_op->src.roi.width; + + /* this is for a remainder update */ + dst_h -= 16; + src_w -= blit_op->src.roi.width; + /* restore parameters that may have been overwritten */ + blit_op->mdp_op = mdp_op; + blit_op->src.p0 = src_p0; + blit_op->src.p1 = src_p1; + blit_op->dst.p0 = dst_p0; + } + + if ((dst_h < 0) || (src_w < 0)) + pr_err("msm_fb: mdp_blt_ex() unexpected result! line:%d\n", + __LINE__); + + /* remainder update */ + if ((dst_h > 0) && (src_w > 0)) { + u32 tmp_v; + + blit_op->dst.roi.height = dst_h; + blit_op->src.roi.width = src_w; + + if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / + blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) { + tmp_v = (MDP_SCALE_Q_FACTOR * + blit_op->dst.roi.height) / + MDP_MAX_X_SCALE_FACTOR + + ((MDP_SCALE_Q_FACTOR * + blit_op->dst.roi.height) % + MDP_MAX_X_SCALE_FACTOR ? 1 : 0); + + /* move x location as roi width gets bigger */ + blit_op->src.roi.x -= tmp_v - blit_op->src.roi.width; + blit_op->src.roi.width = tmp_v; + } else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / + blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) { + tmp_v = (MDP_SCALE_Q_FACTOR * + blit_op->dst.roi.height) / + MDP_MIN_X_SCALE_FACTOR + + ((MDP_SCALE_Q_FACTOR * + blit_op->dst.roi.height) % + MDP_MIN_X_SCALE_FACTOR ? 1 : 0); + /* + * we don't move x location for continuity of + * source image + */ + blit_op->src.roi.width = tmp_v; + } + + + mdp3_start_ppp(blit_op); + } +} + +static int mdp3_ppp_blit(struct msm_fb_data_type *mfd, + struct mdp_blit_req *req, struct mdp3_img_data *src_data, + struct mdp3_img_data *dst_data) +{ + struct ppp_blit_op blit_op; + int ret = 0; + + memset(&blit_op, 0, sizeof(blit_op)); + + if (req->dst.format == MDP_FB_FORMAT) + req->dst.format = mfd->fb_imgType; + if (req->src.format == MDP_FB_FORMAT) + req->src.format = mfd->fb_imgType; + + if (mdp3_ppp_verify_req(req)) { + pr_err("%s: invalid image!\n", __func__); + return -EINVAL; + } + + ret = mdp3_ppp_process_req(&blit_op, req, src_data, dst_data); + if (ret) { + pr_err("%s: Failed to process the blit request", __func__); + return ret; + } + + if (((blit_op.mdp_op & (MDPOP_TRANSP | MDPOP_ALPHAB)) || + (req->src.format == MDP_ARGB_8888) || + (req->src.format == MDP_BGRA_8888) || + (req->src.format == MDP_RGBA_8888)) && + (blit_op.mdp_op & MDPOP_ROT90) && (req->dst_rect.w <= 16)) { + mdp3_ppp_tile_workaround(&blit_op, req); + } else { + mdp3_start_ppp(&blit_op); + } + + return 0; +} + +static int mdp3_ppp_blit_workaround(struct msm_fb_data_type *mfd, + struct mdp_blit_req *req, unsigned int remainder, + struct mdp3_img_data *src_data, + struct mdp3_img_data *dst_data) +{ + int ret; + struct mdp_blit_req splitreq; + int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; + int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; + + /* make new request as provide by user */ + splitreq = *req; + + /* break dest roi at width*/ + d_y_0 = d_y_1 = req->dst_rect.y; + d_h_0 = d_h_1 = req->dst_rect.h; + d_x_0 = req->dst_rect.x; + + if (remainder == 14 || remainder == 6) + d_w_1 = req->dst_rect.w / 2; + else + d_w_1 = (req->dst_rect.w - 1) / 2 - 1; + + d_w_0 = req->dst_rect.w - d_w_1; + d_x_1 = d_x_0 + d_w_0; + /* blit first region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x05) || + ((splitreq.flags & 0x07) == 0x02) || + ((splitreq.flags & 0x07) == 0x0)) { + + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_1) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_1) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_0) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_0 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_0) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_0 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } + + /* No need to split in height */ + ret = mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data); + + if (ret) + return ret; + /* blit second region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x05) || + ((splitreq.flags & 0x07) == 0x02) || + ((splitreq.flags & 0x07) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + + /* No need to split in height ... just width */ + return mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data); +} + +int mdp3_ppp_start_blit(struct msm_fb_data_type *mfd, + struct mdp_blit_req *req, + struct mdp3_img_data *src_data, + struct mdp3_img_data *dst_data) +{ + int ret; + unsigned int remainder = 0, is_bpp_4 = 0; + + if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) { + pr_err("mdp_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0)) + return 0; + + /* MDP width split workaround */ + remainder = (req->dst_rect.w) % 16; + ret = ppp_get_bpp(req->dst.format, mfd->fb_imgType); + if (ret <= 0) { + pr_err("mdp_ppp: incorrect bpp!\n"); + return -EINVAL; + } + is_bpp_4 = (ret == 4) ? 1 : 0; + + if ((is_bpp_4 && (remainder == 6 || remainder == 14)) && + !(req->flags & MDP_SOLID_FILL)) + ret = mdp3_ppp_blit_workaround(mfd, req, remainder, + src_data, dst_data); + else + ret = mdp3_ppp_blit(mfd, req, src_data, dst_data); + return ret; +} + +void mdp3_ppp_wait_for_fence(struct blit_req_list *req) +{ + int i, ret = 0; + + ATRACE_BEGIN(__func__); + /* buf sync */ + for (i = 0; i < req->acq_fen_cnt; i++) { + ret = mdss_wait_sync_fence(req->acq_fen[i], + WAIT_FENCE_FINAL_TIMEOUT); + if (ret < 0) { + pr_err("%s: sync_fence_wait failed! ret = %x\n", + __func__, ret); + break; + } + mdss_put_sync_fence(req->acq_fen[i]); + } + ATRACE_END(__func__); + if (ret < 0) { + while (i < req->acq_fen_cnt) { + mdss_put_sync_fence(req->acq_fen[i]); + i++; + } + } + req->acq_fen_cnt = 0; +} + +void mdp3_ppp_signal_timeline(struct blit_req_list *req) +{ + mdss_inc_timeline(ppp_stat->timeline, 1); + MDSS_XLOG(ppp_stat->timeline->value, ppp_stat->timeline_value); + req->last_rel_fence = req->cur_rel_fence; + req->cur_rel_fence = 0; +} + + +static void mdp3_ppp_deinit_buf_sync(struct blit_req_list *req) +{ + int i; + + put_unused_fd(req->cur_rel_fen_fd); + mdss_put_sync_fence(req->cur_rel_fence); + req->cur_rel_fence = NULL; + req->cur_rel_fen_fd = 0; + ppp_stat->timeline_value--; + for (i = 0; i < req->acq_fen_cnt; i++) + mdss_put_sync_fence(req->acq_fen[i]); + req->acq_fen_cnt = 0; +} + +static int mdp3_ppp_handle_buf_sync(struct blit_req_list *req, + struct mdp_buf_sync *buf_sync) +{ + int i, fence_cnt = 0, ret = 0; + int acq_fen_fd[MDP_MAX_FENCE_FD]; + struct mdss_fence *fence; + + if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) || + (ppp_stat->timeline == NULL)) + return -EINVAL; + + if (buf_sync->acq_fen_fd_cnt) + ret = copy_from_user(acq_fen_fd, buf_sync->acq_fen_fd, + buf_sync->acq_fen_fd_cnt * sizeof(int)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) { + fence = mdss_get_fd_sync_fence(acq_fen_fd[i]); + if (fence == NULL) { + pr_info("%s: null fence! i=%d fd=%d\n", __func__, i, + acq_fen_fd[i]); + ret = -EINVAL; + break; + } + req->acq_fen[i] = fence; + } + fence_cnt = i; + if (ret) + goto buf_sync_err_1; + req->acq_fen_cnt = fence_cnt; + if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT) + mdp3_ppp_wait_for_fence(req); + + MDSS_XLOG(ppp_stat->timeline_value); + + /* create fence */ + req->cur_rel_fence = mdss_get_sync_fence(ppp_stat->timeline, + "ppp_fence", NULL, ppp_stat->timeline_value++); + if (req->cur_rel_fence == NULL) { + req->cur_rel_sync_pt = NULL; + pr_err("%s: cannot create fence\n", __func__); + ret = -ENOMEM; + goto buf_sync_err_2; + } + /* create fd */ + return ret; +buf_sync_err_2: + ppp_stat->timeline_value--; +buf_sync_err_1: + for (i = 0; i < fence_cnt; i++) + mdss_put_sync_fence(req->acq_fen[i]); + req->acq_fen_cnt = 0; + return ret; +} + +void mdp3_ppp_req_push(struct blit_req_queue *req_q, struct blit_req_list *req) +{ + int idx = req_q->push_idx; + + req_q->req[idx] = *req; + req_q->count++; + req_q->push_idx = (req_q->push_idx + 1) % MDP3_PPP_MAX_LIST_REQ; +} + +struct blit_req_list *mdp3_ppp_next_req(struct blit_req_queue *req_q) +{ + struct blit_req_list *req; + + if (req_q->count == 0) + return NULL; + req = &req_q->req[req_q->pop_idx]; + return req; +} + +void mdp3_ppp_req_pop(struct blit_req_queue *req_q) +{ + req_q->count--; + req_q->pop_idx = (req_q->pop_idx + 1) % MDP3_PPP_MAX_LIST_REQ; +} + +void mdp3_free_fw_timer_func(unsigned long arg) +{ + mdp3_res->solid_fill_vote_en = false; + schedule_work(&ppp_stat->free_bw_work); +} + +static void mdp3_free_bw_wq_handler(struct work_struct *work) +{ + struct msm_fb_data_type *mfd = ppp_stat->mfd; + + mutex_lock(&ppp_stat->config_ppp_mutex); + if (ppp_stat->bw_on) + mdp3_ppp_turnon(mfd, 0); + mutex_unlock(&ppp_stat->config_ppp_mutex); +} + +static bool is_hw_workaround_needed(struct mdp_blit_req req) +{ + bool result = false; + bool is_bpp_4 = false; + uint32_t remainder = 0; + uint32_t bpp = ppp_get_bpp(req.dst.format, ppp_stat->mfd->fb_imgType); + + /* MDP width split workaround */ + remainder = (req.dst_rect.w) % 16; + is_bpp_4 = (bpp == 4) ? 1 : 0; + if ((is_bpp_4 && (remainder == 6 || remainder == 14)) && + !(req.flags & MDP_SOLID_FILL)) + result = true; + + /* bg tile fetching HW workaround */ + if (((req.alpha < MDP_ALPHA_NOP) || + (req.transp_mask != MDP_TRANSP_NOP) || + (req.src.format == MDP_ARGB_8888) || + (req.src.format == MDP_BGRA_8888) || + (req.src.format == MDP_RGBA_8888)) && + (req.flags & MDP_ROT_90) && (req.dst_rect.w <= 16)) + result = true; + + return result; +} + +static bool is_roi_equal(struct mdp_blit_req req0, + struct mdp_blit_req req1) +{ + bool result = false; + struct mdss_panel_info *panel_info = ppp_stat->mfd->panel_info; + + /* + * Check req0 and req1 layer destination ROI and return true if + * they are equal. + */ + if ((req0.dst_rect.x == req1.dst_rect.x) && + (req0.dst_rect.y == req1.dst_rect.y) && + (req0.dst_rect.w == req1.dst_rect.w) && + (req0.dst_rect.h == req1.dst_rect.h)) + result = true; + /* + * Layers are source cropped and cropped layer width and hight are + * same panel width and height + */ + else if ((req0.dst_rect.w == req1.dst_rect.w) && + (req0.dst_rect.h == req1.dst_rect.h) && + (req0.dst_rect.w == panel_info->xres) && + (req0.dst_rect.h == panel_info->yres)) + result = true; + + return result; +} + +static bool is_scaling_needed(struct mdp_blit_req req) +{ + bool result = true; + + /* Return true if layer need scaling else return false */ + if ((req.src_rect.w == req.dst_rect.w) && + (req.src_rect.h == req.dst_rect.h)) + result = false; + return result; +} + +static bool is_blit_optimization_possible(struct blit_req_list *req, int indx) +{ + int next = indx + 1; + bool status = false; + struct mdp3_img_data tmp_data; + bool dst_roi_equal = false; + bool hw_woraround_active = false; + struct mdp_blit_req bg_req; + struct mdp_blit_req fg_req; + + if (!(mdp3_res->smart_blit_en)) { + pr_debug("Smart BLIT disabled from sysfs\n"); + return status; + } + if (next < req->count) { + bg_req = req->req_list[indx]; + fg_req = req->req_list[next]; + hw_woraround_active = is_hw_workaround_needed(bg_req); + dst_roi_equal = is_roi_equal(bg_req, fg_req); + /* + * Check userspace Smart BLIT Flag for current and next + * request Flag for smart blit FG layer index If blit + * request at index "n" has MDP_SMART_BLIT flag set then + * it will be used as BG layer in smart blit + * and request at index "n+1" will be used as FG layer + */ + if ((bg_req.flags & MDP_SMART_BLIT) && + (!(fg_req.flags & MDP_SMART_BLIT)) && + (!(hw_woraround_active))) + status = true; + /* + * Enable SMART blit between request 0(BG) & request 1(FG) when + * destination ROI of BG and FG layer are same, + * No scaling on BG layer + * No rotation on BG Layer. + * BG Layer color format is RGB and marked as MDP_IS_FG. + */ + else if ((mdp3_res->smart_blit_en & SMART_BLIT_RGB_EN) && + (indx == 0) && (dst_roi_equal) && + (bg_req.flags & MDP_IS_FG) && + (!(is_scaling_needed(bg_req))) && + (!(bg_req.flags & (MDP_ROT_90))) && + (check_if_rgb(bg_req.src.format)) && + (!(hw_woraround_active))) { + status = true; + req->req_list[indx].flags |= MDP_SMART_BLIT; + pr_debug("Optimize RGB Blit for Req Indx %d\n", indx); + } + /* + * Swap BG and FG layer to enable SMART blit between request + * 0(BG) & request 1(FG) when destination ROI of BG and FG + * layer are same, No scaling on FG and BG layer + * No rotation on FG Layer. BG Layer color format is YUV + */ + else if ((indx == 0) && + (mdp3_res->smart_blit_en & SMART_BLIT_YUV_EN) && + (!(fg_req.flags & (MDP_ROT_90))) && (dst_roi_equal) && + (!(check_if_rgb(bg_req.src.format))) && + (!(hw_woraround_active))) { + /* + * swap blit requests at index 0 and 1. YUV layer at + * index 0 is replaced with UI layer request present + * at index 1. Since UI layer will be in background + * set IS_FG flag and clear it from YUV layer flags + */ + if (!(is_scaling_needed(req->req_list[next]))) { + if (bg_req.flags & MDP_IS_FG) { + req->req_list[indx].flags &= + ~MDP_IS_FG; + req->req_list[next].flags |= MDP_IS_FG; + } + bg_req = req->req_list[next]; + req->req_list[next] = req->req_list[indx]; + req->req_list[indx] = bg_req; + + tmp_data = req->src_data[next]; + req->src_data[next] = req->src_data[indx]; + req->src_data[indx] = tmp_data; + + tmp_data = req->dst_data[next]; + req->dst_data[next] = req->dst_data[indx]; + req->dst_data[indx] = tmp_data; + status = true; + req->req_list[indx].flags |= MDP_SMART_BLIT; + pr_debug("Optimize YUV Blit for Req Indx %d\n", + indx); + } + } + } + return status; +} + +static void mdp3_ppp_blit_handler(struct kthread_work *work) +{ + struct msm_fb_data_type *mfd = ppp_stat->mfd; + struct blit_req_list *req; + int i, rc = 0; + bool smart_blit = false; + int smart_blit_fg_index = -1; + + mutex_lock(&ppp_stat->config_ppp_mutex); + req = mdp3_ppp_next_req(&ppp_stat->req_q); + if (!req) { + mutex_unlock(&ppp_stat->config_ppp_mutex); + return; + } + + if (!ppp_stat->bw_on) { + mdp3_ppp_turnon(mfd, 1); + if (rc < 0) { + mutex_unlock(&ppp_stat->config_ppp_mutex); + pr_err("%s: Enable ppp resources failed\n", __func__); + return; + } + } + while (req) { + mdp3_ppp_wait_for_fence(req); + mdp3_calc_ppp_res(mfd, req); + if (ppp_res.clk_rate != ppp_stat->mdp_clk) { + ppp_stat->mdp_clk = ppp_res.clk_rate; + mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, + ppp_stat->mdp_clk, MDP3_CLIENT_PPP); + } + if (ppp_stat->bw_update) { + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, + ppp_res.next_ab, ppp_res.next_ib); + if (rc < 0) { + pr_err("%s: bw set quota failed\n", __func__); + return; + } + ppp_stat->bw_update = false; + } + ATRACE_BEGIN("mpd3_ppp_start"); + for (i = 0; i < req->count; i++) { + smart_blit = is_blit_optimization_possible(req, i); + if (smart_blit) + /* + * Blit request index of FG layer in + * smart blit + */ + smart_blit_fg_index = i + 1; + if (!(req->req_list[i].flags & MDP_NO_BLIT)) { + /* Do the actual blit. */ + if (!rc) { + rc = mdp3_ppp_start_blit(mfd, + &(req->req_list[i]), + &req->src_data[i], + &req->dst_data[i]); + } + /* Unmap blit source buffer */ + if (smart_blit == false) { + mdp3_put_img(&req->src_data[i], + MDP3_CLIENT_PPP); + } + if (smart_blit_fg_index == i) { + /* Unmap smart blit BG buffer */ + mdp3_put_img(&req->src_data[i - 1], + MDP3_CLIENT_PPP); + smart_blit_fg_index = -1; + } + mdp3_put_img(&req->dst_data[i], + MDP3_CLIENT_PPP); + smart_blit = false; + } + } + ATRACE_END("mdp3_ppp_start"); + /* Signal to release fence */ + mutex_lock(&ppp_stat->req_mutex); + mdp3_ppp_signal_timeline(req); + mdp3_ppp_req_pop(&ppp_stat->req_q); + req = mdp3_ppp_next_req(&ppp_stat->req_q); + if (ppp_stat->wait_for_pop) + complete(&ppp_stat->pop_q_comp); + mutex_unlock(&ppp_stat->req_mutex); + } + mod_timer(&ppp_stat->free_bw_timer, jiffies + + msecs_to_jiffies(MDP_RELEASE_BW_TIMEOUT)); + mutex_unlock(&ppp_stat->config_ppp_mutex); +} + +int mdp3_ppp_parse_req(void __user *p, + struct mdp_async_blit_req_list *req_list_header, + int async) +{ + struct blit_req_list *req; + struct blit_req_queue *req_q = &ppp_stat->req_q; + struct mdss_fence *fence = NULL; + int count, rc, idx, i; + + count = req_list_header->count; + + mutex_lock(&ppp_stat->req_mutex); + while (req_q->count >= MDP3_PPP_MAX_LIST_REQ) { + ppp_stat->wait_for_pop = true; + mutex_unlock(&ppp_stat->req_mutex); + rc = wait_for_completion_timeout( + &ppp_stat->pop_q_comp, 5 * HZ); + if (rc == 0) { + /* This will only occur if there is serious problem */ + pr_err("%s: timeout exiting queuing request\n", + __func__); + return -EBUSY; + } + mutex_lock(&ppp_stat->req_mutex); + ppp_stat->wait_for_pop = false; + } + idx = req_q->push_idx; + req = &req_q->req[idx]; + + if (copy_from_user(&req->req_list, p, + sizeof(struct mdp_blit_req) * count)) { + mutex_unlock(&ppp_stat->req_mutex); + return -EFAULT; + } + + rc = mdp3_ppp_handle_buf_sync(req, &req_list_header->sync); + if (rc < 0) { + pr_err("%s: Failed create sync point\n", __func__); + mutex_unlock(&ppp_stat->req_mutex); + return rc; + } + req->count = count; + + /* We need to grab ion handle while running in client thread */ + for (i = 0; i < count; i++) { + rc = mdp3_ppp_get_img(&req->req_list[i].src, + &req->req_list[i], &req->src_data[i]); + if (rc < 0 || req->src_data[i].len == 0) { + pr_err("mdp_ppp: couldn't retrieve src img from mem\n"); + goto parse_err_1; + } + + rc = mdp3_ppp_get_img(&req->req_list[i].dst, + &req->req_list[i], &req->dst_data[i]); + if (rc < 0 || req->dst_data[i].len == 0) { + mdp3_put_img(&req->src_data[i], MDP3_CLIENT_PPP); + pr_err("mdp_ppp: couldn't retrieve dest img from mem\n"); + goto parse_err_1; + } + } + + if (async) { + req->cur_rel_fen_fd = mdss_get_sync_fence_fd( + req->cur_rel_fence); + rc = copy_to_user(req_list_header->sync.rel_fen_fd, + &req->cur_rel_fen_fd, sizeof(int)); + if (rc) { + pr_err("%s:copy_to_user failed\n", __func__); + goto parse_err_2; + } + } else { + fence = req->cur_rel_fence; + } + + mdp3_ppp_req_push(req_q, req); + mutex_unlock(&ppp_stat->req_mutex); + kthread_queue_work(&ppp_stat->kworker, &ppp_stat->blit_work); + if (!async) { + /* wait for release fence */ + rc = mdss_wait_sync_fence(fence, + 5 * MSEC_PER_SEC); + if (rc < 0) + pr_err("%s: sync blit! rc = %x\n", __func__, rc); + + mdss_put_sync_fence(fence); + fence = NULL; + } + return 0; + +parse_err_2: + put_unused_fd(req->cur_rel_fen_fd); +parse_err_1: + for (i--; i >= 0; i--) { + mdp3_put_img(&req->src_data[i], MDP3_CLIENT_PPP); + mdp3_put_img(&req->dst_data[i], MDP3_CLIENT_PPP); + } + mdp3_ppp_deinit_buf_sync(req); + mutex_unlock(&ppp_stat->req_mutex); + return rc; +} + +int mdp3_ppp_res_init(struct msm_fb_data_type *mfd) +{ + int rc; + struct sched_param param = {.sched_priority = 16}; + const char timeline_name[] = "mdp3_ppp"; + + ppp_stat = kzalloc(sizeof(struct ppp_status), GFP_KERNEL); + if (!ppp_stat) + return -ENOMEM; + + /*Setup sync_pt timeline for ppp*/ + ppp_stat->timeline = mdss_create_timeline(timeline_name); + if (ppp_stat->timeline == NULL) { + pr_err("%s: cannot create time line\n", __func__); + return -ENOMEM; + } + ppp_stat->timeline_value = 1; + + kthread_init_worker(&ppp_stat->kworker); + kthread_init_work(&ppp_stat->blit_work, mdp3_ppp_blit_handler); + ppp_stat->blit_thread = kthread_run(kthread_worker_fn, + &ppp_stat->kworker, + "mdp3_ppp"); + + if (IS_ERR(ppp_stat->blit_thread)) { + rc = PTR_ERR(ppp_stat->blit_thread); + pr_err("ERROR: unable to start ppp blit thread,err = %d\n", + rc); + ppp_stat->blit_thread = NULL; + return rc; + } + if (sched_setscheduler(ppp_stat->blit_thread, SCHED_FIFO, ¶m)) + pr_warn("set priority failed for mdp3 blit thread\n"); + + INIT_WORK(&ppp_stat->free_bw_work, mdp3_free_bw_wq_handler); + init_completion(&ppp_stat->pop_q_comp); + mutex_init(&ppp_stat->req_mutex); + mutex_init(&ppp_stat->config_ppp_mutex); + init_timer(&ppp_stat->free_bw_timer); + ppp_stat->free_bw_timer.function = mdp3_free_fw_timer_func; + ppp_stat->free_bw_timer.data = 0; + ppp_stat->mfd = mfd; + mdp3_ppp_callback_setup(); + return 0; +} diff --git a/drivers/video/fbdev/msm/mdp3_ppp.h b/drivers/video/fbdev/msm/mdp3_ppp.h new file mode 100644 index 0000000000000000000000000000000000000000..1f82851384368babaf4422166dc3c25067be9a6b --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_ppp.h @@ -0,0 +1,430 @@ +/* Copyright (c) 2007, 2013, 2016, 2018, The Linux Foundation. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 MDP3_PPP_H +#define MDP3_PPP_H +#include "mdp3.h" +#include "mdss_fb.h" + +#define PPP_WRITEL(val, off) MDP3_REG_WRITE(off, val) + +#define MAX_BLIT_REQ 16 +#define PPP_UPSCALE_MAX 64 +#define PPP_BLUR_SCALE_MAX 128 +#define PPP_LUT_MAX 256 + +#define MDPOP_SMART_BLIT BIT(31) /* blit optimization flag */ + +/* MDP PPP Operations */ +#define MDPOP_NOP 0 +#define MDPOP_LR BIT(0) /* left to right flip */ +#define MDPOP_UD BIT(1) /* up and down flip */ +#define MDPOP_ROT90 BIT(2) /* rotate image to 90 degree */ +#define MDPOP_ROT180 (MDPOP_UD|MDPOP_LR) +#define MDPOP_ROT270 (MDPOP_ROT90|MDPOP_UD|MDPOP_LR) +#define MDPOP_ASCALE BIT(7) +#define MDPOP_ALPHAB BIT(8) /* enable alpha blending */ +#define MDPOP_TRANSP BIT(9) /* enable transparency */ +#define MDPOP_DITHER BIT(10) /* enable dither */ +#define MDPOP_SHARPENING BIT(11) /* enable sharpening */ +#define MDPOP_BLUR BIT(12) /* enable blur */ +#define MDPOP_FG_PM_ALPHA BIT(13) +#define MDPOP_LAYER_IS_FG BIT(14) + +#define MDPOP_ROTATION (MDPOP_ROT90|MDPOP_LR|MDPOP_UD) + +#define PPP_OP_CONVERT_YCBCR2RGB BIT(2) +#define PPP_OP_CONVERT_ON BIT(3) +#define PPP_OP_SCALE_X_ON BIT(0) +#define PPP_OP_SCALE_Y_ON BIT(1) +#define PPP_OP_ROT_ON BIT(8) +#define PPP_OP_ROT_90 BIT(9) +#define PPP_OP_FLIP_LR BIT(10) +#define PPP_OP_FLIP_UD BIT(11) +#define PPP_OP_BLEND_ON BIT(12) +#define PPP_OP_BLEND_CONSTANT_ALPHA BIT(14) +#define PPP_OP_BLEND_BG_ALPHA BIT(13) +#define PPP_OP_BLEND_EQ_REVERSE BIT(15) +#define PPP_OP_DITHER_EN BIT(16) +#define PPP_BLEND_CALPHA_TRNASP BIT(24) + +#define PPP_OP_BLEND_SRCPIXEL_ALPHA 0 +#define PPP_OP_BLEND_ALPHA_BLEND_NORMAL 0 +#define PPP_OP_BLEND_ALPHA_BLEND_REVERSE BIT(15) + +#define PPP_BLEND_BG_USE_ALPHA_SEL (1 << 0) +#define PPP_BLEND_BG_ALPHA_REVERSE (1 << 3) +#define PPP_BLEND_BG_SRCPIXEL_ALPHA (0 << 1) +#define PPP_BLEND_BG_DSTPIXEL_ALPHA (1 << 1) +#define PPP_BLEND_BG_CONSTANT_ALPHA (2 << 1) +#define PPP_BLEND_BG_CONST_ALPHA_VAL(x) ((x) << 24) +#define PPP_OP_BG_CHROMA_H2V1 BIT(25) + +#define CLR_G 0x0 +#define CLR_B 0x1 +#define CLR_R 0x2 +#define CLR_ALPHA 0x3 + +#define CLR_Y CLR_G +#define CLR_CB CLR_B +#define CLR_CR CLR_R + +/* from lsb to msb */ +#define PPP_GET_PACK_PATTERN(a, x, y, z, bit) \ + (((a)<<(bit*3))|((x)<<(bit*2))|((y)< + +#include "mdss_fb.h" +#include "mdp3_ppp.h" + +#define MDP_IS_IMGTYPE_BAD(x) ((x) >= MDP_IMGTYPE_LIMIT) + +/* bg_config_lut not needed since it is same as src */ +const uint32_t src_cfg_lut[MDP_IMGTYPE_LIMIT] = { + [MDP_RGB_565] = MDP_RGB_565_SRC_REG, + [MDP_BGR_565] = MDP_RGB_565_SRC_REG, + [MDP_RGB_888] = MDP_RGB_888_SRC_REG, + [MDP_BGR_888] = MDP_RGB_888_SRC_REG, + [MDP_BGRA_8888] = MDP_RGBX_8888_SRC_REG, + [MDP_RGBA_8888] = MDP_RGBX_8888_SRC_REG, + [MDP_ARGB_8888] = MDP_RGBX_8888_SRC_REG, + [MDP_XRGB_8888] = MDP_RGBX_8888_SRC_REG, + [MDP_RGBX_8888] = MDP_RGBX_8888_SRC_REG, + [MDP_Y_CRCB_H2V2] = MDP_Y_CBCR_H2V2_SRC_REG, + [MDP_Y_CBCR_H2V2] = MDP_Y_CBCR_H2V2_SRC_REG, + [MDP_Y_CBCR_H2V2_ADRENO] = MDP_Y_CBCR_H2V2_SRC_REG, + [MDP_Y_CBCR_H2V2_VENUS] = MDP_Y_CBCR_H2V2_SRC_REG, + [MDP_YCRYCB_H2V1] = MDP_YCRYCB_H2V1_SRC_REG, + [MDP_Y_CBCR_H2V1] = MDP_Y_CRCB_H2V1_SRC_REG, + [MDP_Y_CRCB_H2V1] = MDP_Y_CRCB_H2V1_SRC_REG, + [MDP_BGRX_8888] = MDP_RGBX_8888_SRC_REG, +}; + +const uint32_t out_cfg_lut[MDP_IMGTYPE_LIMIT] = { + [MDP_RGB_565] = MDP_RGB_565_DST_REG, + [MDP_BGR_565] = MDP_RGB_565_DST_REG, + [MDP_RGB_888] = MDP_RGB_888_DST_REG, + [MDP_BGR_888] = MDP_RGB_888_DST_REG, + [MDP_BGRA_8888] = MDP_RGBX_8888_DST_REG, + [MDP_RGBA_8888] = MDP_RGBX_8888_DST_REG, + [MDP_ARGB_8888] = MDP_RGBX_8888_DST_REG, + [MDP_XRGB_8888] = MDP_RGBX_8888_DST_REG, + [MDP_RGBX_8888] = MDP_RGBX_8888_DST_REG, + [MDP_Y_CRCB_H2V2] = MDP_Y_CBCR_H2V2_DST_REG, + [MDP_Y_CBCR_H2V2] = MDP_Y_CBCR_H2V2_DST_REG, + [MDP_Y_CBCR_H2V2_ADRENO] = MDP_Y_CBCR_H2V2_DST_REG, + [MDP_Y_CBCR_H2V2_VENUS] = MDP_Y_CBCR_H2V2_DST_REG, + [MDP_YCRYCB_H2V1] = MDP_YCRYCB_H2V1_DST_REG, + [MDP_Y_CBCR_H2V1] = MDP_Y_CRCB_H2V1_DST_REG, + [MDP_Y_CRCB_H2V1] = MDP_Y_CRCB_H2V1_DST_REG, + [MDP_BGRX_8888] = MDP_RGBX_8888_DST_REG, +}; + +const uint32_t pack_patt_lut[MDP_IMGTYPE_LIMIT] = { + [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), + [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), + [MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), + [MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), + [MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, + CLR_G, CLR_R, 8), + [MDP_RGBA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, + CLR_G, CLR_B, 8), + [MDP_ARGB_8888] = PPP_GET_PACK_PATTERN(CLR_R, + CLR_G, CLR_B, CLR_ALPHA, 8), + [MDP_XRGB_8888] = PPP_GET_PACK_PATTERN(CLR_R, + CLR_G, CLR_B, CLR_ALPHA, 8), + [MDP_RGBX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, + CLR_G, CLR_B, 8), + [MDP_Y_CRCB_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8), + [MDP_Y_CBCR_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8), + [MDP_Y_CBCR_H2V2_ADRENO] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, + CLR_CR, 8), + [MDP_Y_CBCR_H2V2_VENUS] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, + CLR_CR, 8), + [MDP_YCRYCB_H2V1] = PPP_GET_PACK_PATTERN(CLR_Y, + CLR_CR, CLR_Y, CLR_CB, 8), + [MDP_Y_CBCR_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8), + [MDP_Y_CRCB_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8), + [MDP_BGRX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, + CLR_G, CLR_R, 8), +}; + +const uint32_t swapped_pack_patt_lut[MDP_IMGTYPE_LIMIT] = { + [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), + [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), + [MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), + [MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), + [MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, + CLR_G, CLR_B, 8), + [MDP_RGBA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, + CLR_G, CLR_R, 8), + [MDP_ARGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, + CLR_G, CLR_R, 8), + [MDP_XRGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, + CLR_G, CLR_R, 8), + [MDP_RGBX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, + CLR_G, CLR_R, 8), + [MDP_Y_CRCB_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8), + [MDP_Y_CBCR_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8), + [MDP_Y_CBCR_H2V2_ADRENO] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, + CLR_CB, 8), + [MDP_Y_CBCR_H2V2_VENUS] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, + CLR_CB, 8), + [MDP_YCRYCB_H2V1] = PPP_GET_PACK_PATTERN(CLR_Y, + CLR_CB, CLR_Y, CLR_CR, 8), + [MDP_Y_CBCR_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8), + [MDP_Y_CRCB_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8), + [MDP_BGRX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, + CLR_G, CLR_B, 8), +}; + +const uint32_t dst_op_reg[MDP_IMGTYPE_LIMIT] = { + [MDP_Y_CRCB_H2V2] = PPP_OP_DST_CHROMA_420, + [MDP_Y_CBCR_H2V2] = PPP_OP_DST_CHROMA_420, + [MDP_Y_CBCR_H2V1] = PPP_OP_DST_CHROMA_H2V1, + [MDP_Y_CRCB_H2V1] = PPP_OP_DST_CHROMA_H2V1, + [MDP_YCRYCB_H2V1] = PPP_OP_DST_CHROMA_H2V1, +}; + +const uint32_t src_op_reg[MDP_IMGTYPE_LIMIT] = { + [MDP_Y_CRCB_H2V2] = PPP_OP_SRC_CHROMA_420 | PPP_OP_COLOR_SPACE_YCBCR, + [MDP_Y_CBCR_H2V2] = PPP_OP_SRC_CHROMA_420 | PPP_OP_COLOR_SPACE_YCBCR, + [MDP_Y_CBCR_H2V2_ADRENO] = PPP_OP_SRC_CHROMA_420 | + PPP_OP_COLOR_SPACE_YCBCR, + [MDP_Y_CBCR_H2V2_VENUS] = PPP_OP_SRC_CHROMA_420 | + PPP_OP_COLOR_SPACE_YCBCR, + [MDP_Y_CBCR_H2V1] = PPP_OP_SRC_CHROMA_H2V1, + [MDP_Y_CRCB_H2V1] = PPP_OP_SRC_CHROMA_H2V1, + [MDP_YCRYCB_H2V1] = PPP_OP_SRC_CHROMA_H2V1, +}; + +const uint32_t bytes_per_pixel[MDP_IMGTYPE_LIMIT] = { + [MDP_RGB_565] = 2, + [MDP_BGR_565] = 2, + [MDP_RGB_888] = 3, + [MDP_BGR_888] = 3, + [MDP_XRGB_8888] = 4, + [MDP_ARGB_8888] = 4, + [MDP_RGBA_8888] = 4, + [MDP_BGRA_8888] = 4, + [MDP_RGBX_8888] = 4, + [MDP_Y_CBCR_H2V1] = 1, + [MDP_Y_CBCR_H2V2] = 1, + [MDP_Y_CBCR_H2V2_ADRENO] = 1, + [MDP_Y_CBCR_H2V2_VENUS] = 1, + [MDP_Y_CRCB_H2V1] = 1, + [MDP_Y_CRCB_H2V2] = 1, + [MDP_YCRYCB_H2V1] = 2, + [MDP_BGRX_8888] = 4, +}; + +const bool per_pixel_alpha[MDP_IMGTYPE_LIMIT] = { + [MDP_BGRA_8888] = true, + [MDP_RGBA_8888] = true, + [MDP_ARGB_8888] = true, +}; + +const bool multi_plane[MDP_IMGTYPE_LIMIT] = { + [MDP_Y_CRCB_H2V2] = true, + [MDP_Y_CBCR_H2V2] = true, + [MDP_Y_CBCR_H2V1] = true, + [MDP_Y_CRCB_H2V1] = true, +}; + +/* lut default */ +uint32_t default_pre_lut_val[PPP_LUT_MAX] = { + 0x0, + 0x151515, + 0x1d1d1d, + 0x232323, + 0x272727, + 0x2b2b2b, + 0x2f2f2f, + 0x333333, + 0x363636, + 0x393939, + 0x3b3b3b, + 0x3e3e3e, + 0x404040, + 0x434343, + 0x454545, + 0x474747, + 0x494949, + 0x4b4b4b, + 0x4d4d4d, + 0x4f4f4f, + 0x515151, + 0x535353, + 0x555555, + 0x565656, + 0x585858, + 0x5a5a5a, + 0x5b5b5b, + 0x5d5d5d, + 0x5e5e5e, + 0x606060, + 0x616161, + 0x636363, + 0x646464, + 0x666666, + 0x676767, + 0x686868, + 0x6a6a6a, + 0x6b6b6b, + 0x6c6c6c, + 0x6e6e6e, + 0x6f6f6f, + 0x707070, + 0x717171, + 0x727272, + 0x747474, + 0x757575, + 0x767676, + 0x777777, + 0x787878, + 0x797979, + 0x7a7a7a, + 0x7c7c7c, + 0x7d7d7d, + 0x7e7e7e, + 0x7f7f7f, + 0x808080, + 0x818181, + 0x828282, + 0x838383, + 0x848484, + 0x858585, + 0x868686, + 0x878787, + 0x888888, + 0x898989, + 0x8a8a8a, + 0x8b8b8b, + 0x8c8c8c, + 0x8d8d8d, + 0x8e8e8e, + 0x8f8f8f, + 0x8f8f8f, + 0x909090, + 0x919191, + 0x929292, + 0x939393, + 0x949494, + 0x959595, + 0x969696, + 0x969696, + 0x979797, + 0x989898, + 0x999999, + 0x9a9a9a, + 0x9b9b9b, + 0x9c9c9c, + 0x9c9c9c, + 0x9d9d9d, + 0x9e9e9e, + 0x9f9f9f, + 0xa0a0a0, + 0xa0a0a0, + 0xa1a1a1, + 0xa2a2a2, + 0xa3a3a3, + 0xa4a4a4, + 0xa4a4a4, + 0xa5a5a5, + 0xa6a6a6, + 0xa7a7a7, + 0xa7a7a7, + 0xa8a8a8, + 0xa9a9a9, + 0xaaaaaa, + 0xaaaaaa, + 0xababab, + 0xacacac, + 0xadadad, + 0xadadad, + 0xaeaeae, + 0xafafaf, + 0xafafaf, + 0xb0b0b0, + 0xb1b1b1, + 0xb2b2b2, + 0xb2b2b2, + 0xb3b3b3, + 0xb4b4b4, + 0xb4b4b4, + 0xb5b5b5, + 0xb6b6b6, + 0xb6b6b6, + 0xb7b7b7, + 0xb8b8b8, + 0xb8b8b8, + 0xb9b9b9, + 0xbababa, + 0xbababa, + 0xbbbbbb, + 0xbcbcbc, + 0xbcbcbc, + 0xbdbdbd, + 0xbebebe, + 0xbebebe, + 0xbfbfbf, + 0xc0c0c0, + 0xc0c0c0, + 0xc1c1c1, + 0xc1c1c1, + 0xc2c2c2, + 0xc3c3c3, + 0xc3c3c3, + 0xc4c4c4, + 0xc5c5c5, + 0xc5c5c5, + 0xc6c6c6, + 0xc6c6c6, + 0xc7c7c7, + 0xc8c8c8, + 0xc8c8c8, + 0xc9c9c9, + 0xc9c9c9, + 0xcacaca, + 0xcbcbcb, + 0xcbcbcb, + 0xcccccc, + 0xcccccc, + 0xcdcdcd, + 0xcecece, + 0xcecece, + 0xcfcfcf, + 0xcfcfcf, + 0xd0d0d0, + 0xd0d0d0, + 0xd1d1d1, + 0xd2d2d2, + 0xd2d2d2, + 0xd3d3d3, + 0xd3d3d3, + 0xd4d4d4, + 0xd4d4d4, + 0xd5d5d5, + 0xd6d6d6, + 0xd6d6d6, + 0xd7d7d7, + 0xd7d7d7, + 0xd8d8d8, + 0xd8d8d8, + 0xd9d9d9, + 0xd9d9d9, + 0xdadada, + 0xdbdbdb, + 0xdbdbdb, + 0xdcdcdc, + 0xdcdcdc, + 0xdddddd, + 0xdddddd, + 0xdedede, + 0xdedede, + 0xdfdfdf, + 0xdfdfdf, + 0xe0e0e0, + 0xe0e0e0, + 0xe1e1e1, + 0xe1e1e1, + 0xe2e2e2, + 0xe3e3e3, + 0xe3e3e3, + 0xe4e4e4, + 0xe4e4e4, + 0xe5e5e5, + 0xe5e5e5, + 0xe6e6e6, + 0xe6e6e6, + 0xe7e7e7, + 0xe7e7e7, + 0xe8e8e8, + 0xe8e8e8, + 0xe9e9e9, + 0xe9e9e9, + 0xeaeaea, + 0xeaeaea, + 0xebebeb, + 0xebebeb, + 0xececec, + 0xececec, + 0xededed, + 0xededed, + 0xeeeeee, + 0xeeeeee, + 0xefefef, + 0xefefef, + 0xf0f0f0, + 0xf0f0f0, + 0xf1f1f1, + 0xf1f1f1, + 0xf2f2f2, + 0xf2f2f2, + 0xf2f2f2, + 0xf3f3f3, + 0xf3f3f3, + 0xf4f4f4, + 0xf4f4f4, + 0xf5f5f5, + 0xf5f5f5, + 0xf6f6f6, + 0xf6f6f6, + 0xf7f7f7, + 0xf7f7f7, + 0xf8f8f8, + 0xf8f8f8, + 0xf9f9f9, + 0xf9f9f9, + 0xfafafa, + 0xfafafa, + 0xfafafa, + 0xfbfbfb, + 0xfbfbfb, + 0xfcfcfc, + 0xfcfcfc, + 0xfdfdfd, + 0xfdfdfd, + 0xfefefe, + 0xfefefe, + 0xffffff, + 0xffffff, +}; + +uint32_t default_post_lut_val[PPP_LUT_MAX] = { + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x10101, + 0x10101, + 0x10101, + 0x10101, + 0x10101, + 0x10101, + 0x10101, + 0x10101, + 0x10101, + 0x10101, + 0x20202, + 0x20202, + 0x20202, + 0x20202, + 0x20202, + 0x20202, + 0x30303, + 0x30303, + 0x30303, + 0x30303, + 0x30303, + 0x40404, + 0x40404, + 0x40404, + 0x40404, + 0x40404, + 0x50505, + 0x50505, + 0x50505, + 0x50505, + 0x60606, + 0x60606, + 0x60606, + 0x70707, + 0x70707, + 0x70707, + 0x70707, + 0x80808, + 0x80808, + 0x80808, + 0x90909, + 0x90909, + 0xa0a0a, + 0xa0a0a, + 0xa0a0a, + 0xb0b0b, + 0xb0b0b, + 0xb0b0b, + 0xc0c0c, + 0xc0c0c, + 0xd0d0d, + 0xd0d0d, + 0xe0e0e, + 0xe0e0e, + 0xe0e0e, + 0xf0f0f, + 0xf0f0f, + 0x101010, + 0x101010, + 0x111111, + 0x111111, + 0x121212, + 0x121212, + 0x131313, + 0x131313, + 0x141414, + 0x151515, + 0x151515, + 0x161616, + 0x161616, + 0x171717, + 0x171717, + 0x181818, + 0x191919, + 0x191919, + 0x1a1a1a, + 0x1b1b1b, + 0x1b1b1b, + 0x1c1c1c, + 0x1c1c1c, + 0x1d1d1d, + 0x1e1e1e, + 0x1f1f1f, + 0x1f1f1f, + 0x202020, + 0x212121, + 0x212121, + 0x222222, + 0x232323, + 0x242424, + 0x242424, + 0x252525, + 0x262626, + 0x272727, + 0x272727, + 0x282828, + 0x292929, + 0x2a2a2a, + 0x2b2b2b, + 0x2c2c2c, + 0x2c2c2c, + 0x2d2d2d, + 0x2e2e2e, + 0x2f2f2f, + 0x303030, + 0x313131, + 0x323232, + 0x333333, + 0x333333, + 0x343434, + 0x353535, + 0x363636, + 0x373737, + 0x383838, + 0x393939, + 0x3a3a3a, + 0x3b3b3b, + 0x3c3c3c, + 0x3d3d3d, + 0x3e3e3e, + 0x3f3f3f, + 0x404040, + 0x414141, + 0x424242, + 0x434343, + 0x444444, + 0x464646, + 0x474747, + 0x484848, + 0x494949, + 0x4a4a4a, + 0x4b4b4b, + 0x4c4c4c, + 0x4d4d4d, + 0x4f4f4f, + 0x505050, + 0x515151, + 0x525252, + 0x535353, + 0x545454, + 0x565656, + 0x575757, + 0x585858, + 0x595959, + 0x5b5b5b, + 0x5c5c5c, + 0x5d5d5d, + 0x5e5e5e, + 0x606060, + 0x616161, + 0x626262, + 0x646464, + 0x656565, + 0x666666, + 0x686868, + 0x696969, + 0x6a6a6a, + 0x6c6c6c, + 0x6d6d6d, + 0x6f6f6f, + 0x707070, + 0x717171, + 0x737373, + 0x747474, + 0x767676, + 0x777777, + 0x797979, + 0x7a7a7a, + 0x7c7c7c, + 0x7d7d7d, + 0x7f7f7f, + 0x808080, + 0x828282, + 0x838383, + 0x858585, + 0x868686, + 0x888888, + 0x898989, + 0x8b8b8b, + 0x8d8d8d, + 0x8e8e8e, + 0x909090, + 0x919191, + 0x939393, + 0x959595, + 0x969696, + 0x989898, + 0x9a9a9a, + 0x9b9b9b, + 0x9d9d9d, + 0x9f9f9f, + 0xa1a1a1, + 0xa2a2a2, + 0xa4a4a4, + 0xa6a6a6, + 0xa7a7a7, + 0xa9a9a9, + 0xababab, + 0xadadad, + 0xafafaf, + 0xb0b0b0, + 0xb2b2b2, + 0xb4b4b4, + 0xb6b6b6, + 0xb8b8b8, + 0xbababa, + 0xbbbbbb, + 0xbdbdbd, + 0xbfbfbf, + 0xc1c1c1, + 0xc3c3c3, + 0xc5c5c5, + 0xc7c7c7, + 0xc9c9c9, + 0xcbcbcb, + 0xcdcdcd, + 0xcfcfcf, + 0xd1d1d1, + 0xd3d3d3, + 0xd5d5d5, + 0xd7d7d7, + 0xd9d9d9, + 0xdbdbdb, + 0xdddddd, + 0xdfdfdf, + 0xe1e1e1, + 0xe3e3e3, + 0xe5e5e5, + 0xe7e7e7, + 0xe9e9e9, + 0xebebeb, + 0xeeeeee, + 0xf0f0f0, + 0xf2f2f2, + 0xf4f4f4, + 0xf6f6f6, + 0xf8f8f8, + 0xfbfbfb, + 0xfdfdfd, + 0xffffff, +}; + +struct ppp_csc_table rgb2yuv = { + .fwd_matrix = { + 0x83, + 0x102, + 0x32, + 0xffb5, + 0xff6c, + 0xe1, + 0xe1, + 0xff45, + 0xffdc, + }, + .rev_matrix = { + 0x254, + 0x0, + 0x331, + 0x254, + 0xff38, + 0xfe61, + 0x254, + 0x409, + 0x0, + }, + .bv = { + 0x10, + 0x80, + 0x80, + }, + .lv = { + 0x10, + 0xeb, + 0x10, + 0xf0, + }, +}; + +struct ppp_csc_table default_table2 = { + .fwd_matrix = { + 0x5d, + 0x13a, + 0x20, + 0xffcd, + 0xff54, + 0xe1, + 0xe1, + 0xff35, + }, + .rev_matrix = { + 0x254, + 0x0, + 0x396, + 0x254, + 0xff94, + 0xfef0, + 0x254, + 0x43a, + 0x0, + }, + .bv = { + 0x10, + 0x80, + 0x80, + }, + .lv = { + 0x10, + 0xeb, + 0x10, + 0xf0, + }, +}; + +const struct ppp_table upscale_table[PPP_UPSCALE_MAX] = { + { 0x5fffc, 0x0 }, + { 0x50200, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50204, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50208, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5020c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50210, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50214, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50218, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5021c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50220, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50224, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50228, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5022c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50230, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50234, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50238, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5023c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50240, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50244, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50248, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5024c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50250, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50254, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50258, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5025c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50260, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50264, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50268, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5026c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50270, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50274, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50278, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5027c, 0x34003fe }, +}; + +const struct ppp_table mdp_gaussian_blur_table[PPP_BLUR_SCALE_MAX] = { + /* max variance */ + { 0x5fffc, 0x20000080 }, + { 0x50280, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50284, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50288, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5028c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50290, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50294, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50298, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5029c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ac, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502bc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502cc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502dc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ec, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502fc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50300, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50304, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50308, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5030c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50310, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50314, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50318, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5031c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50320, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50324, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50328, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5032c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50330, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50334, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50338, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5033c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50340, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50344, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50348, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5034c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50350, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50354, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50358, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5035c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50360, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50364, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50368, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5036c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50370, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50374, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50378, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5037c, 0x20000080 }, +}; + +const struct ppp_table downscale_x_table_pt2topt4[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static const struct ppp_table downscale_x_table_pt4topt6[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static const struct ppp_table downscale_x_table_pt6topt8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50280, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50284, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50288, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5028c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50290, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50294, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50298, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5029c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x502a0, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x502a4, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x502a8, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x502ac, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x502b0, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x502b4, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x502b8, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x502bc, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x502c0, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x502c4, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x502c8, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x502cc, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x502d0, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x502d4, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x502d8, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x502dc, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x502e0, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x502e4, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x502e8, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x502ec, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x502f0, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x502f4, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x502f8, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x502fc, 0x1c0003f8 }, +}; + +static const struct ppp_table downscale_x_table_pt8topt1[] = { + { 0x5fffc, 0x0 }, + { 0x50280, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50284, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50288, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5028c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50290, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50294, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50298, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5029c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x502a0, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x502a4, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x502a8, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x502ac, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x502b0, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x502b4, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x502b8, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x502bc, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x502c0, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x502c4, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x502c8, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x502cc, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x502d0, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x502d4, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x502d8, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x502dc, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x502e0, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x502e4, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x502e8, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x502ec, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x502f0, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x502f4, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x502f8, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x502fc, 0x34003fe }, +}; + +static const struct ppp_table *downscale_x_table[PPP_DOWNSCALE_MAX] = { + [PPP_DOWNSCALE_PT2TOPT4] = downscale_x_table_pt2topt4, + [PPP_DOWNSCALE_PT4TOPT6] = downscale_x_table_pt4topt6, + [PPP_DOWNSCALE_PT6TOPT8] = downscale_x_table_pt6topt8, + [PPP_DOWNSCALE_PT8TOPT1] = downscale_x_table_pt8topt1, +}; + +static const struct ppp_table downscale_y_table_pt2topt4[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static const struct ppp_table downscale_y_table_pt4topt6[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static const struct ppp_table downscale_y_table_pt6topt8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50300, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50304, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50308, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5030c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50310, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50314, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50318, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5031c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x50320, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x50324, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x50328, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x5032c, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x50330, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x50334, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x50338, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x5033c, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x50340, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x50344, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x50348, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x5034c, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x50350, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x50354, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x50358, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x5035c, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x50360, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x50364, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x50368, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x5036c, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x50370, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x50374, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x50378, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x5037c, 0x1c0003f8 }, +}; + +static const struct ppp_table downscale_y_table_pt8topt1[] = { + { 0x5fffc, 0x0 }, + { 0x50300, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50304, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50308, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5030c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50310, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50314, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50318, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5031c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50320, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50324, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50328, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5032c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50330, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50334, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50338, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5033c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50340, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50344, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50348, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5034c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50350, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50354, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50358, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5035c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50360, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50364, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50368, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5036c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50370, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50374, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50378, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5037c, 0x34003fe }, +}; + +static const struct ppp_table *downscale_y_table[PPP_DOWNSCALE_MAX] = { + [PPP_DOWNSCALE_PT2TOPT4] = downscale_y_table_pt2topt4, + [PPP_DOWNSCALE_PT4TOPT6] = downscale_y_table_pt4topt6, + [PPP_DOWNSCALE_PT6TOPT8] = downscale_y_table_pt6topt8, + [PPP_DOWNSCALE_PT8TOPT1] = downscale_y_table_pt8topt1, +}; + +void ppp_load_table(const struct ppp_table *table, int len) +{ + int i; + + for (i = 0; i < len; i++) + PPP_WRITEL(table[i].val, table[i].reg); +} + +void ppp_load_up_lut(void) +{ + ppp_load_table(upscale_table, + PPP_UPSCALE_MAX); +} + +void ppp_load_gaussian_lut(void) +{ + ppp_load_table(mdp_gaussian_blur_table, + PPP_BLUR_SCALE_MAX); +} + +void ppp_load_x_scale_table(int idx) +{ + ppp_load_table(downscale_x_table[idx], 64); +} + +void ppp_load_y_scale_table(int idx) +{ + ppp_load_table(downscale_y_table[idx], 64); +} + +uint32_t ppp_bpp(uint32_t type) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + return bytes_per_pixel[type]; +} + +uint32_t ppp_src_config(uint32_t type) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + return src_cfg_lut[type]; +} + +uint32_t ppp_out_config(uint32_t type) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + return out_cfg_lut[type]; +} + +uint32_t ppp_pack_pattern(uint32_t type, uint32_t yuv2rgb) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + if (yuv2rgb) + return swapped_pack_patt_lut[type]; + + return pack_patt_lut[type]; +} + +uint32_t ppp_dst_op_reg(uint32_t type) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + return dst_op_reg[type]; +} + +uint32_t ppp_src_op_reg(uint32_t type) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + return src_op_reg[type]; +} + +bool ppp_per_p_alpha(uint32_t type) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + return per_pixel_alpha[type]; +} + +bool ppp_multi_plane(uint32_t type) +{ + if (MDP_IS_IMGTYPE_BAD(type)) + return 0; + return multi_plane[type]; +} + +uint32_t *ppp_default_pre_lut(void) +{ + return default_pre_lut_val; +} + +uint32_t *ppp_default_post_lut(void) +{ + return default_post_lut_val; +} + +struct ppp_csc_table *ppp_csc_rgb2yuv(void) +{ + return &rgb2yuv; +} + +struct ppp_csc_table *ppp_csc_table2(void) +{ + return &default_table2; +} diff --git a/drivers/video/fbdev/msm/mdp3_ppp_hwio.c b/drivers/video/fbdev/msm/mdp3_ppp_hwio.c new file mode 100644 index 0000000000000000000000000000000000000000..15f240989b4bbfcc3a19db2d42c099baba77f353 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp3_ppp_hwio.c @@ -0,0 +1,1365 @@ +/* Copyright (c) 2007, 2012-2013, 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" + +#include "mdss_fb.h" +#include "mdp3_ppp.h" +#include "mdp3_hwio.h" +#include "mdss_debug.h" + +/* SHIM Q Factor */ +#define PHI_Q_FACTOR 29 +#define PQF_PLUS_5 (PHI_Q_FACTOR + 5) /* due to 32 phases */ +#define PQF_PLUS_4 (PHI_Q_FACTOR + 4) +#define PQF_PLUS_2 (PHI_Q_FACTOR + 2) /* to get 4.0 */ +#define PQF_MINUS_2 (PHI_Q_FACTOR - 2) /* to get 0.25 */ +#define PQF_PLUS_5_PLUS_2 (PQF_PLUS_5 + 2) +#define PQF_PLUS_5_MINUS_2 (PQF_PLUS_5 - 2) + +enum { + LAYER_FG = 0, + LAYER_BG, + LAYER_FB, + LAYER_MAX, +}; + +static long long mdp_do_div(uint64_t num, uint64_t den) +{ + do_div(num, den); + return num; +} + +static int mdp_calc_scale_params(uint32_t org, uint32_t dim_in, + uint32_t dim_out, bool is_W, int32_t *phase_init_ptr, + uint32_t *phase_step_ptr) +{ + bool rpa_on = false; + int init_phase = 0; + uint64_t numer = 0; + uint64_t denom = 0; + int64_t point5 = 1; + int64_t one = 1; + int64_t k1, k2, k3, k4; /* linear equation coefficients */ + uint64_t int_mask; + uint64_t fract_mask; + uint64_t Os; + int64_t Osprime; + int64_t Od; + int64_t Odprime; + int64_t Oreq; + int64_t init_phase_temp; + int64_t delta; + uint32_t mult; + + /* + * The phase accumulator should really be rational for all cases in a + * general purpose polyphase scaler for a tiled architecture with + * non-zero * origin capability because there is no way to represent + * certain scale factors in fixed point regardless of precision. + * The error incurred in attempting to use fixed point is most + * eggregious for SF where 1/SF is an integral multiple of 1/3. + * + * Set the RPA flag for this dimension. + * + * In order for 1/SF (dim_in/dim_out) to be an integral multiple of + * 1/3, dim_out must be an integral multiple of 3. + */ + if (!(dim_out % 3)) { + mult = dim_out / 3; + rpa_on = (!(dim_in % mult)); + } + + numer = dim_out; + denom = dim_in; + + /* + * convert to U30.34 before division + * + * The K vectors carry 4 extra bits of precision + * and are rounded. + * + * We initially go 5 bits over then round by adding + * 1 and right shifting by 1 + * so final result is U31.33 + */ + numer <<= PQF_PLUS_5; + + /* now calculate the scale factor (aka k3) */ + k3 = ((mdp_do_div(numer, denom) + 1) >> 1); + + /* check scale factor for legal range [0.25 - 4.0] */ + if (((k3 >> 4) < (1LL << PQF_MINUS_2)) || + ((k3 >> 4) > (1LL << PQF_PLUS_2))) { + return -EINVAL; + } + + /* calculate inverse scale factor (aka k1) for phase init */ + numer = dim_in; + denom = dim_out; + numer <<= PQF_PLUS_5; + k1 = ((mdp_do_div(numer, denom) + 1) >> 1); + + /* + * calculate initial phase and ROI overfetch + */ + /* convert point5 & one to S39.24 (will always be positive) */ + point5 <<= (PQF_PLUS_4 - 1); + one <<= PQF_PLUS_4; + k2 = ((k1 - one) >> 1); + init_phase = (int)(k2 >> 4); + k4 = ((k3 - one) >> 1); + if (k3 != one) { + /* calculate the masks */ + fract_mask = one - 1; + int_mask = ~fract_mask; + + if (!rpa_on) { + /* + * FIXED POINT IMPLEMENTATION + */ + if (org) { + /* + * The complicated case; ROI origin != 0 + * init_phase needs to be adjusted + * OF is also position dependent + */ + + /* map (org - .5) into destination space */ + Os = ((uint64_t) org << 1) - 1; + Od = ((k3 * Os) >> 1) + k4; + + /* take the ceiling */ + Odprime = (Od & int_mask); + if (Odprime != Od) + Odprime += one; + + /* now map that back to source space */ + Osprime = (k1 * (Odprime >> PQF_PLUS_4)) + k2; + + /* then floor & decrement to calc the required + * starting coordinate + */ + Oreq = (Osprime & int_mask) - one; + + /* calculate initial phase */ + init_phase_temp = Osprime - Oreq; + delta = ((int64_t) (org) << PQF_PLUS_4) - Oreq; + init_phase_temp -= delta; + + /* limit to valid range before 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 { + /* + * RPA IMPLEMENTATION + * + * init_phase needs to be calculated in all RPA_on + * cases because it's a numerator, not a fixed + * point value. + */ + + /* map (org - .5) into destination space */ + Os = ((uint64_t) org << PQF_PLUS_4) - point5; + Od = mdp_do_div((dim_out * (Os + point5)), + dim_in); + Od -= point5; + + /* take the ceiling */ + Odprime = (Od & int_mask); + if (Odprime != Od) + Odprime += one; + + /* now map that back to source space */ + Osprime = + mdp_do_div((dim_in * (Odprime + point5)), + dim_out); + Osprime -= point5; + + /* + * then floor & decrement to calculate the required + * starting coordinate + */ + Oreq = (Osprime & int_mask) - one; + + /* calculate initial phase */ + 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); + } + } + + /* return the scale parameters */ + *phase_init_ptr = init_phase; + *phase_step_ptr = (uint32_t) (k1 >> 4); + + return 0; +} + +static int scale_idx(int factor) +{ + int idx; + + if (factor > 80) + idx = PPP_DOWNSCALE_PT8TOPT1; + else if (factor > 60) + idx = PPP_DOWNSCALE_PT6TOPT8; + else if (factor > 40) + idx = PPP_DOWNSCALE_PT4TOPT6; + else + idx = PPP_DOWNSCALE_PT2TOPT4; + + return idx; +} + +inline int32_t comp_conv_rgb2yuv(int32_t comp, int32_t y_high, + int32_t y_low, int32_t c_high, int32_t c_low) +{ + if (comp < 0) + comp = 0; + if (comp > 255) + comp = 255; + + /* clamp */ + if (comp < y_low) + comp = y_low; + if (comp > y_high) + comp = y_high; + return comp; +} + +static uint32_t conv_rgb2yuv(uint32_t input_pixel, + uint16_t *matrix_vector, + uint16_t *bv, + uint16_t *clamp_vector) +{ + uint8_t input_C2, input_C0, input_C1; + uint32_t output; + int32_t comp_C2, comp_C1, comp_C0, temp; + int32_t temp1, temp2, temp3; + int32_t matrix[9]; + int32_t bias_vector[3]; + int32_t Y_low_limit, Y_high_limit, C_low_limit, C_high_limit; + int32_t i; + + input_C2 = (input_pixel >> 16) & 0xFF; + input_C1 = (input_pixel >> 8) & 0xFF; + input_C0 = (input_pixel >> 0) & 0xFF; + + comp_C0 = input_C0; + comp_C1 = input_C1; + comp_C2 = input_C2; + + for (i = 0; i < MDP_CSC_SIZE; i++) + matrix[i] = + ((int32_t) (((int32_t) matrix_vector[i]) << 20)) >> 20; + + bias_vector[0] = (int32_t) (bv[0] & 0xFF); + bias_vector[1] = (int32_t) (bv[1] & 0xFF); + bias_vector[2] = (int32_t) (bv[2] & 0xFF); + + Y_low_limit = (int32_t) clamp_vector[0]; + Y_high_limit = (int32_t) clamp_vector[1]; + C_low_limit = (int32_t) clamp_vector[2]; + C_high_limit = (int32_t) clamp_vector[3]; + + /* + * Color Conversion + * reorder input colors + */ + temp = comp_C2; + comp_C2 = comp_C1; + comp_C1 = comp_C0; + comp_C0 = temp; + + /* matrix multiplication */ + temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + + comp_C2 * matrix[2]; + temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + + comp_C2 * matrix[5]; + temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + + comp_C2 * matrix[8]; + + comp_C0 = temp1 + 0x100; + comp_C1 = temp2 + 0x100; + comp_C2 = temp3 + 0x100; + + /* take integer part */ + comp_C0 >>= 9; + comp_C1 >>= 9; + comp_C2 >>= 9; + + /* post bias (+) */ + comp_C0 += bias_vector[0]; + comp_C1 += bias_vector[1]; + comp_C2 += bias_vector[2]; + + /* limit pixel to 8-bit */ + comp_C0 = comp_conv_rgb2yuv(comp_C0, Y_high_limit, + Y_low_limit, C_high_limit, C_low_limit); + comp_C1 = comp_conv_rgb2yuv(comp_C1, Y_high_limit, + Y_low_limit, C_high_limit, C_low_limit); + comp_C2 = comp_conv_rgb2yuv(comp_C2, Y_high_limit, + Y_low_limit, C_high_limit, C_low_limit); + + output = (comp_C2 << 16) | (comp_C1 << 8) | comp_C0; + return output; +} + +inline void y_h_even_num(struct ppp_img_desc *img) +{ + img->roi.y = (img->roi.y / 2) * 2; + img->roi.height = (img->roi.height / 2) * 2; +} + +inline void x_w_even_num(struct ppp_img_desc *img) +{ + img->roi.x = (img->roi.x / 2) * 2; + img->roi.width = (img->roi.width / 2) * 2; +} + +bool check_if_rgb(int color) +{ + bool rgb = false; + + switch (color) { + case MDP_RGB_565: + case MDP_BGR_565: + case MDP_RGB_888: + case MDP_BGR_888: + case MDP_BGRA_8888: + case MDP_RGBA_8888: + case MDP_ARGB_8888: + case MDP_XRGB_8888: + case MDP_RGBX_8888: + case MDP_BGRX_8888: + rgb = true; + default: + break; + } + return rgb; +} + +uint8_t *mdp_adjust_rot_addr(struct ppp_blit_op *iBuf, + uint8_t *addr, uint32_t bpp, uint32_t uv, uint32_t layer) +{ + uint32_t ystride = 0; + uint32_t h_slice = 1; + uint32_t roi_width = 0; + uint32_t roi_height = 0; + uint32_t color_fmt = 0; + + if (layer == LAYER_BG) { + ystride = iBuf->bg.prop.width * bpp; + roi_width = iBuf->bg.roi.width; + roi_height = iBuf->bg.roi.height; + color_fmt = iBuf->bg.color_fmt; + } else { + ystride = iBuf->dst.prop.width * bpp; + roi_width = iBuf->dst.roi.width; + roi_height = iBuf->dst.roi.height; + color_fmt = iBuf->dst.color_fmt; + } + if (uv && ((color_fmt == MDP_Y_CBCR_H2V2) || + (color_fmt == MDP_Y_CRCB_H2V2))) + h_slice = 2; + + if (((iBuf->mdp_op & MDPOP_ROT90) == MDPOP_ROT90) ^ + ((iBuf->mdp_op & MDPOP_LR) == MDPOP_LR)) { + addr += (roi_width - MIN(16, roi_width)) * bpp; + } + if ((iBuf->mdp_op & MDPOP_UD) == MDPOP_UD) { + addr += ((roi_height - MIN(16, roi_height))/h_slice) * + ystride; + } + + return addr; +} + +void mdp_adjust_start_addr(struct ppp_blit_op *blit_op, + struct ppp_img_desc *img, int v_slice, + int h_slice, uint32_t layer) +{ + uint32_t bpp = ppp_bpp(img->color_fmt); + int x = img->roi.x; + int y = img->roi.y; + uint32_t width = img->prop.width; + + if (img->color_fmt == MDP_Y_CBCR_H2V2_ADRENO && layer == 0) + img->p0 += (x + y * ALIGN(width, 32)) * bpp; + else if (img->color_fmt == MDP_Y_CBCR_H2V2_VENUS && layer == 0) + img->p0 += (x + y * ALIGN(width, 128)) * bpp; + else + img->p0 += (x + y * width) * bpp; + if (layer != LAYER_FG) + img->p0 = mdp_adjust_rot_addr(blit_op, img->p0, bpp, 0, layer); + + if (img->p1) { + /* + * MDP_Y_CBCR_H2V2/MDP_Y_CRCB_H2V2 cosite for now + * we need to shift x direction same as y dir for offsite + */ + if ((img->color_fmt == MDP_Y_CBCR_H2V2_ADRENO || + img->color_fmt == MDP_Y_CBCR_H2V2_VENUS) + && layer == 0) + img->p1 += ((x / h_slice) * h_slice + ((y == 0) ? 0 : + (((y + 1) / v_slice - 1) * (ALIGN(width/2, 32) * 2)))) + * bpp; + else + img->p1 += ((x / h_slice) * h_slice + + ((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp; + + if (layer != LAYER_FG) + img->p1 = mdp_adjust_rot_addr(blit_op, + img->p1, bpp, 0, layer); + } +} + +int load_ppp_lut(int tableType, uint32_t *lut) +{ + int i; + uint32_t base_addr; + + base_addr = tableType ? MDP3_PPP_POST_LUT : MDP3_PPP_PRE_LUT; + for (i = 0; i < PPP_LUT_MAX; i++) + PPP_WRITEL(lut[i], base_addr + MDP3_PPP_LUTn(i)); + + return 0; +} + +/* Configure Primary CSC Matrix */ +int load_primary_matrix(struct ppp_csc_table *csc) +{ + int i; + + for (i = 0; i < MDP_CSC_SIZE; i++) + PPP_WRITEL(csc->fwd_matrix[i], MDP3_PPP_CSC_PFMVn(i)); + + for (i = 0; i < MDP_CSC_SIZE; i++) + PPP_WRITEL(csc->rev_matrix[i], MDP3_PPP_CSC_PRMVn(i)); + + for (i = 0; i < MDP_BV_SIZE; i++) + PPP_WRITEL(csc->bv[i], MDP3_PPP_CSC_PBVn(i)); + + for (i = 0; i < MDP_LV_SIZE; i++) + PPP_WRITEL(csc->lv[i], MDP3_PPP_CSC_PLVn(i)); + + return 0; +} + +/* Load Secondary CSC Matrix */ +int load_secondary_matrix(struct ppp_csc_table *csc) +{ + int i; + + for (i = 0; i < MDP_CSC_SIZE; i++) + PPP_WRITEL(csc->fwd_matrix[i], MDP3_PPP_CSC_SFMVn(i)); + + for (i = 0; i < MDP_CSC_SIZE; i++) + PPP_WRITEL(csc->rev_matrix[i], MDP3_PPP_CSC_SRMVn(i)); + + for (i = 0; i < MDP_BV_SIZE; i++) + PPP_WRITEL(csc->bv[i], MDP3_PPP_CSC_SBVn(i)); + + for (i = 0; i < MDP_LV_SIZE; i++) + PPP_WRITEL(csc->lv[i], MDP3_PPP_CSC_SLVn(i)); + return 0; +} + +int load_csc_matrix(int matrix_type, struct ppp_csc_table *csc) +{ + if (matrix_type == CSC_PRIMARY_MATRIX) + return load_primary_matrix(csc); + + return load_secondary_matrix(csc); +} + +int config_ppp_src(struct ppp_img_desc *src, uint32_t yuv2rgb) +{ + uint32_t val; + + val = ((src->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) | + (src->roi.width & MDP3_PPP_XY_MASK); + PPP_WRITEL(val, MDP3_PPP_SRC_SIZE); + + PPP_WRITEL(src->p0, MDP3_PPP_SRCP0_ADDR); + PPP_WRITEL(src->p1, MDP3_PPP_SRCP1_ADDR); + PPP_WRITEL(src->p3, MDP3_PPP_SRCP3_ADDR); + + val = (src->stride0 & MDP3_PPP_STRIDE_MASK) | + ((src->stride1 & MDP3_PPP_STRIDE_MASK) << + MDP3_PPP_STRIDE1_OFFSET); + PPP_WRITEL(val, MDP3_PPP_SRC_YSTRIDE1_ADDR); + val = ((src->stride2 & MDP3_PPP_STRIDE_MASK) << + MDP3_PPP_STRIDE1_OFFSET); + PPP_WRITEL(val, MDP3_PPP_SRC_YSTRIDE2_ADDR); + + val = ppp_src_config(src->color_fmt); + val |= (src->roi.x % 2) ? PPP_SRC_BPP_ROI_ODD_X : 0; + val |= (src->roi.y % 2) ? PPP_SRC_BPP_ROI_ODD_Y : 0; + PPP_WRITEL(val, MDP3_PPP_SRC_FORMAT); + PPP_WRITEL(ppp_pack_pattern(src->color_fmt, yuv2rgb), + MDP3_PPP_SRC_UNPACK_PATTERN1); + return 0; +} + +int config_ppp_out(struct ppp_img_desc *dst, uint32_t yuv2rgb) +{ + uint32_t val; + bool pseudoplanr_output = false; + + switch (dst->color_fmt) { + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + pseudoplanr_output = true; + break; + default: + break; + } + val = ppp_out_config(dst->color_fmt); + if (pseudoplanr_output) + val |= PPP_DST_PLANE_PSEUDOPLN; + PPP_WRITEL(val, MDP3_PPP_OUT_FORMAT); + PPP_WRITEL(ppp_pack_pattern(dst->color_fmt, yuv2rgb), + MDP3_PPP_OUT_PACK_PATTERN1); + + val = ((dst->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) | + (dst->roi.width & MDP3_PPP_XY_MASK); + PPP_WRITEL(val, MDP3_PPP_OUT_SIZE); + + PPP_WRITEL(dst->p0, MDP3_PPP_OUTP0_ADDR); + PPP_WRITEL(dst->p1, MDP3_PPP_OUTP1_ADDR); + PPP_WRITEL(dst->p3, MDP3_PPP_OUTP3_ADDR); + + val = (dst->stride0 & MDP3_PPP_STRIDE_MASK) | + ((dst->stride1 & MDP3_PPP_STRIDE_MASK) << + MDP3_PPP_STRIDE1_OFFSET); + PPP_WRITEL(val, MDP3_PPP_OUT_YSTRIDE1_ADDR); + val = ((dst->stride2 & MDP3_PPP_STRIDE_MASK) << + MDP3_PPP_STRIDE1_OFFSET); + PPP_WRITEL(val, MDP3_PPP_OUT_YSTRIDE2_ADDR); + return 0; +} + +int config_ppp_background(struct ppp_img_desc *bg, uint32_t yuv2rgb) +{ + uint32_t val; + + PPP_WRITEL(bg->p0, MDP3_PPP_BGP0_ADDR); + PPP_WRITEL(bg->p1, MDP3_PPP_BGP1_ADDR); + PPP_WRITEL(bg->p3, MDP3_PPP_BGP3_ADDR); + + val = (bg->stride0 & MDP3_PPP_STRIDE_MASK) | + ((bg->stride1 & MDP3_PPP_STRIDE_MASK) << + MDP3_PPP_STRIDE1_OFFSET); + PPP_WRITEL(val, MDP3_PPP_BG_YSTRIDE1_ADDR); + val = ((bg->stride2 & MDP3_PPP_STRIDE_MASK) << + MDP3_PPP_STRIDE1_OFFSET); + PPP_WRITEL(val, MDP3_PPP_BG_YSTRIDE2_ADDR); + + PPP_WRITEL(ppp_src_config(bg->color_fmt), + MDP3_PPP_BG_FORMAT); + PPP_WRITEL(ppp_pack_pattern(bg->color_fmt, yuv2rgb), + MDP3_PPP_BG_UNPACK_PATTERN1); + return 0; +} + +void ppp_edge_rep_luma_pixel(struct ppp_blit_op *blit_op, + struct ppp_edge_rep *er) +{ + if (blit_op->mdp_op & MDPOP_ASCALE) { + + er->is_scale_enabled = 1; + + if (blit_op->mdp_op & MDPOP_ROT90) { + er->dst_roi_width = blit_op->dst.roi.height; + er->dst_roi_height = blit_op->dst.roi.width; + } else { + er->dst_roi_width = blit_op->dst.roi.width; + er->dst_roi_height = blit_op->dst.roi.height; + } + + /* + * Find out the luma pixels needed for scaling in the + * x direction (LEFT and RIGHT). Locations of pixels are + * relative to the ROI. Upper-left corner of ROI corresponds + * to coordinates (0,0). Also set the number of luma pixel + * to repeat. + */ + if (blit_op->src.roi.width > 3 * er->dst_roi_width) { + /* scale factor < 1/3 */ + er->luma_interp_point_right = + (blit_op->src.roi.width - 1); + } else if (blit_op->src.roi.width == 3 * er->dst_roi_width) { + /* scale factor == 1/3 */ + er->luma_interp_point_right = + (blit_op->src.roi.width - 1) + 1; + er->luma_repeat_right = 1; + } else if ((blit_op->src.roi.width > er->dst_roi_width) && + (blit_op->src.roi.width < 3 * er->dst_roi_width)) { + /* 1/3 < scale factor < 1 */ + er->luma_interp_point_left = -1; + er->luma_interp_point_right = + (blit_op->src.roi.width - 1) + 1; + er->luma_repeat_left = 1; + er->luma_repeat_right = 1; + } else if (blit_op->src.roi.width == er->dst_roi_width) { + /* scale factor == 1 */ + er->luma_interp_point_left = -1; + er->luma_interp_point_right = + (blit_op->src.roi.width - 1) + 2; + er->luma_repeat_left = 1; + er->luma_repeat_right = 2; + } else { + /* scale factor > 1 */ + er->luma_interp_point_left = -2; + er->luma_interp_point_right = + (blit_op->src.roi.width - 1) + 2; + er->luma_repeat_left = 2; + er->luma_repeat_right = 2; + } + + /* + * Find out the number of pixels needed for scaling in the + * y direction (TOP and BOTTOM). Locations of pixels are + * relative to the ROI. Upper-left corner of ROI corresponds + * to coordinates (0,0). Also set the number of luma pixel + * to repeat. + */ + if (blit_op->src.roi.height > 3 * er->dst_roi_height) { + er->luma_interp_point_bottom = + (blit_op->src.roi.height - 1); + } else if (blit_op->src.roi.height == 3 * er->dst_roi_height) { + er->luma_interp_point_bottom = + (blit_op->src.roi.height - 1) + 1; + er->luma_repeat_bottom = 1; + } else if ((blit_op->src.roi.height > er->dst_roi_height) && + (blit_op->src.roi.height < 3 * er->dst_roi_height)) { + er->luma_interp_point_top = -1; + er->luma_interp_point_bottom = + (blit_op->src.roi.height - 1) + 1; + er->luma_repeat_top = 1; + er->luma_repeat_bottom = 1; + } else if (blit_op->src.roi.height == er->dst_roi_height) { + er->luma_interp_point_top = -1; + er->luma_interp_point_bottom = + (blit_op->src.roi.height - 1) + 2; + er->luma_repeat_top = 1; + er->luma_repeat_bottom = 2; + } else { + er->luma_interp_point_top = -2; + er->luma_interp_point_bottom = + (blit_op->src.roi.height - 1) + 2; + er->luma_repeat_top = 2; + er->luma_repeat_bottom = 2; + } + } else { + /* + * Since no scaling needed, Tile Fetch does not require any + * more luma pixel than what the ROI contains. + */ + er->luma_interp_point_right = + (int32_t) (blit_op->src.roi.width - 1); + er->luma_interp_point_bottom = + (int32_t) (blit_op->src.roi.height - 1); + } + /* After adding the ROI offsets, we have locations of + * luma_interp_points relative to the image. + */ + er->luma_interp_point_left += (int32_t) (blit_op->src.roi.x); + er->luma_interp_point_right += (int32_t) (blit_op->src.roi.x); + er->luma_interp_point_top += (int32_t) (blit_op->src.roi.y); + er->luma_interp_point_bottom += (int32_t) (blit_op->src.roi.y); +} + +void ppp_edge_rep_chroma_pixel(struct ppp_blit_op *blit_op, + struct ppp_edge_rep *er) +{ + bool chroma_edge_enable = true; + uint32_t is_yuv_offsite_vertical = 0; + + /* find out which chroma pixels are needed for chroma upsampling. */ + switch (blit_op->src.color_fmt) { + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_YCRYCB_H2V1: + er->chroma_interp_point_left = er->luma_interp_point_left >> 1; + er->chroma_interp_point_right = + (er->luma_interp_point_right + 1) >> 1; + er->chroma_interp_point_top = er->luma_interp_point_top; + er->chroma_interp_point_bottom = er->luma_interp_point_bottom; + break; + + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_ADRENO: + case MDP_Y_CBCR_H2V2_VENUS: + case MDP_Y_CRCB_H2V2: + er->chroma_interp_point_left = er->luma_interp_point_left >> 1; + er->chroma_interp_point_right = + (er->luma_interp_point_right + 1) >> 1; + er->chroma_interp_point_top = + (er->luma_interp_point_top - 1) >> 1; + er->chroma_interp_point_bottom = + (er->luma_interp_point_bottom + 1) >> 1; + is_yuv_offsite_vertical = 1; + break; + + default: + chroma_edge_enable = false; + er->chroma_interp_point_left = er->luma_interp_point_left; + er->chroma_interp_point_right = er->luma_interp_point_right; + er->chroma_interp_point_top = er->luma_interp_point_top; + er->chroma_interp_point_bottom = er->luma_interp_point_bottom; + + break; + } + + if (chroma_edge_enable) { + /* Defines which chroma pixels belongs to the roi */ + switch (blit_op->src.color_fmt) { + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_YCRYCB_H2V1: + er->chroma_bound_left = blit_op->src.roi.x / 2; + /* there are half as many chroma pixel as luma pixels */ + er->chroma_bound_right = + (blit_op->src.roi.width + + blit_op->src.roi.x - 1) / 2; + er->chroma_bound_top = blit_op->src.roi.y; + er->chroma_bound_bottom = + (blit_op->src.roi.height + blit_op->src.roi.y - 1); + break; + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_ADRENO: + case MDP_Y_CBCR_H2V2_VENUS: + case MDP_Y_CRCB_H2V2: + /* + * cosite in horizontal dir, and offsite in vertical dir + * width of chroma ROI is 1/2 of size of luma ROI + * height of chroma ROI is 1/2 of size of luma ROI + */ + er->chroma_bound_left = blit_op->src.roi.x / 2; + er->chroma_bound_right = + (blit_op->src.roi.width + + blit_op->src.roi.x - 1) / 2; + er->chroma_bound_top = blit_op->src.roi.y / 2; + er->chroma_bound_bottom = + (blit_op->src.roi.height + + blit_op->src.roi.y - 1) / 2; + break; + + default: + /* + * If no valid chroma sub-sampling format specified, + * assume 4:4:4 ( i.e. fully sampled). + */ + er->chroma_bound_left = blit_op->src.roi.x; + er->chroma_bound_right = blit_op->src.roi.width + + blit_op->src.roi.x - 1; + er->chroma_bound_top = blit_op->src.roi.y; + er->chroma_bound_bottom = + (blit_op->src.roi.height + blit_op->src.roi.y - 1); + break; + } + + /* + * Knowing which chroma pixels are needed, and which chroma + * pixels belong to the ROI (i.e. available for fetching ), + * calculate how many chroma pixels Tile Fetch needs to + * duplicate. If any required chroma pixels falls outside + * of the ROI, Tile Fetch must obtain them by replicating + * pixels. + */ + if (er->chroma_bound_left > er->chroma_interp_point_left) + er->chroma_repeat_left = + er->chroma_bound_left - + er->chroma_interp_point_left; + else + er->chroma_repeat_left = 0; + + if (er->chroma_interp_point_right > er->chroma_bound_right) + er->chroma_repeat_right = + er->chroma_interp_point_right - + er->chroma_bound_right; + else + er->chroma_repeat_right = 0; + + if (er->chroma_bound_top > er->chroma_interp_point_top) + er->chroma_repeat_top = + er->chroma_bound_top - + er->chroma_interp_point_top; + else + er->chroma_repeat_top = 0; + + if (er->chroma_interp_point_bottom > er->chroma_bound_bottom) + er->chroma_repeat_bottom = + er->chroma_interp_point_bottom - + er->chroma_bound_bottom; + else + er->chroma_repeat_bottom = 0; + + if (er->is_scale_enabled && (blit_op->src.roi.height == 1) + && is_yuv_offsite_vertical) { + er->chroma_repeat_bottom = 3; + er->chroma_repeat_top = 0; + } + } +} + +int config_ppp_edge_rep(struct ppp_blit_op *blit_op) +{ + uint32_t reg = 0; + struct ppp_edge_rep er; + + memset(&er, 0, sizeof(er)); + + ppp_edge_rep_luma_pixel(blit_op, &er); + + /* + * After adding the ROI offsets, we have locations of + * chroma_interp_points relative to the image. + */ + er.chroma_interp_point_left = er.luma_interp_point_left; + er.chroma_interp_point_right = er.luma_interp_point_right; + er.chroma_interp_point_top = er.luma_interp_point_top; + er.chroma_interp_point_bottom = er.luma_interp_point_bottom; + + ppp_edge_rep_chroma_pixel(blit_op, &er); + /* ensure repeats are >=0 and no larger than 3 pixels */ + if ((er.chroma_repeat_left < 0) || (er.chroma_repeat_right < 0) || + (er.chroma_repeat_top < 0) || (er.chroma_repeat_bottom < 0)) + return -EINVAL; + if ((er.chroma_repeat_left > 3) || (er.chroma_repeat_right > 3) || + (er.chroma_repeat_top > 3) || (er.chroma_repeat_bottom > 3)) + return -EINVAL; + if ((er.luma_repeat_left < 0) || (er.luma_repeat_right < 0) || + (er.luma_repeat_top < 0) || (er.luma_repeat_bottom < 0)) + return -EINVAL; + if ((er.luma_repeat_left > 3) || (er.luma_repeat_right > 3) || + (er.luma_repeat_top > 3) || (er.luma_repeat_bottom > 3)) + return -EINVAL; + + reg |= (er.chroma_repeat_left & 3) << MDP_LEFT_CHROMA; + reg |= (er.chroma_repeat_right & 3) << MDP_RIGHT_CHROMA; + reg |= (er.chroma_repeat_top & 3) << MDP_TOP_CHROMA; + reg |= (er.chroma_repeat_bottom & 3) << MDP_BOTTOM_CHROMA; + reg |= (er.luma_repeat_left & 3) << MDP_LEFT_LUMA; + reg |= (er.luma_repeat_right & 3) << MDP_RIGHT_LUMA; + reg |= (er.luma_repeat_top & 3) << MDP_TOP_LUMA; + reg |= (er.luma_repeat_bottom & 3) << MDP_BOTTOM_LUMA; + PPP_WRITEL(reg, MDP3_PPP_SRC_EDGE_REP); + return 0; +} + +int config_ppp_bg_edge_rep(struct ppp_blit_op *blit_op) +{ + uint32_t reg = 0; + + switch (blit_op->dst.color_fmt) { + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + if (blit_op->dst.roi.y == 0) + reg |= BIT(MDP_TOP_CHROMA); + + if ((blit_op->dst.roi.y + blit_op->dst.roi.height) == + blit_op->dst.prop.height) { + reg |= BIT(MDP_BOTTOM_CHROMA); + } + + if (((blit_op->dst.roi.x + blit_op->dst.roi.width) == + blit_op->dst.prop.width) && + ((blit_op->dst.roi.width % 2) == 0)) + reg |= BIT(MDP_RIGHT_CHROMA); + break; + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_YCRYCB_H2V1: + if (((blit_op->dst.roi.x + blit_op->dst.roi.width) == + blit_op->dst.prop.width) && + ((blit_op->dst.roi.width % 2) == 0)) + reg |= BIT(MDP_RIGHT_CHROMA); + break; + default: + break; + } + PPP_WRITEL(reg, MDP3_PPP_BG_EDGE_REP); + return 0; +} + +int config_ppp_lut(uint32_t *pppop_reg_ptr, int lut_c0_en, + int lut_c1_en, int lut_c2_en) +{ + if (lut_c0_en) + *pppop_reg_ptr |= MDP_LUT_C0_EN; + if (lut_c1_en) + *pppop_reg_ptr |= MDP_LUT_C1_EN; + if (lut_c2_en) + *pppop_reg_ptr |= MDP_LUT_C2_EN; + return 0; +} + +int config_ppp_scale(struct ppp_blit_op *blit_op, uint32_t *pppop_reg_ptr) +{ + struct ppp_img_desc *src = &blit_op->src; + struct ppp_img_desc *dst = &blit_op->dst; + uint32_t dstW, dstH; + uint32_t x_fac, y_fac; + uint32_t mdp_blur = 0; + uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; + int x_idx, y_idx; + + if (blit_op->mdp_op & MDPOP_ASCALE) { + if (blit_op->mdp_op & MDPOP_ROT90) { + dstW = dst->roi.height; + dstH = dst->roi.width; + } else { + dstW = dst->roi.width; + dstH = dst->roi.height; + } + *pppop_reg_ptr |= + (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); + + mdp_blur = blit_op->mdp_op & MDPOP_BLUR; + + if ((dstW != src->roi.width) || + (dstH != src->roi.height) || mdp_blur) { + + /* + * Use source origin as 0 for computing initial + * phase and step size. Incorrect initial phase and + * step size value results in green line issue. + */ + mdp_calc_scale_params(0, + blit_op->src.roi.width, + dstW, 1, &phase_init_x, + &phase_step_x); + mdp_calc_scale_params(0, + blit_op->src.roi.height, + dstH, 0, &phase_init_y, + &phase_step_y); + + PPP_WRITEL(phase_init_x, MDP3_PPP_SCALE_PHASEX_INIT); + PPP_WRITEL(phase_init_y, MDP3_PPP_SCALE_PHASEY_INIT); + PPP_WRITEL(phase_step_x, MDP3_PPP_SCALE_PHASEX_STEP); + PPP_WRITEL(phase_step_y, MDP3_PPP_SCALE_PHASEY_STEP); + + + if (dstW > src->roi.width || dstH > src->roi.height) + ppp_load_up_lut(); + + if (mdp_blur) + ppp_load_gaussian_lut(); + + if (dstW <= src->roi.width) { + x_fac = (dstW * 100) / src->roi.width; + x_idx = scale_idx(x_fac); + ppp_load_x_scale_table(x_idx); + } + if (dstH <= src->roi.height) { + y_fac = (dstH * 100) / src->roi.height; + y_idx = scale_idx(y_fac); + ppp_load_y_scale_table(y_idx); + } + + } else { + blit_op->mdp_op &= ~(MDPOP_ASCALE); + } + } + config_ppp_edge_rep(blit_op); + config_ppp_bg_edge_rep(blit_op); + return 0; +} + +int config_ppp_csc(int src_color, int dst_color, uint32_t *pppop_reg_ptr) +{ + bool inputRGB, outputRGB; + + inputRGB = check_if_rgb(src_color); + outputRGB = check_if_rgb(dst_color); + + if ((!inputRGB) && (outputRGB)) + *pppop_reg_ptr |= PPP_OP_CONVERT_YCBCR2RGB | + PPP_OP_CONVERT_ON; + if ((inputRGB) && (!outputRGB)) + *pppop_reg_ptr |= PPP_OP_CONVERT_ON; + + return 0; +} + +int config_ppp_blend(struct ppp_blit_op *blit_op, + uint32_t *pppop_reg_ptr, + bool is_yuv_smart_blit, int smart_blit_bg_alpha) +{ + struct ppp_csc_table *csc; + uint32_t alpha, trans_color; + uint32_t val = 0; + int c_fmt = blit_op->src.color_fmt; + int bg_alpha; + + csc = ppp_csc_rgb2yuv(); + alpha = blit_op->blend.const_alpha; + trans_color = blit_op->blend.trans_color; + if (blit_op->mdp_op & MDPOP_FG_PM_ALPHA) { + if (ppp_per_p_alpha(c_fmt)) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA; + } else { + if ((blit_op->mdp_op & MDPOP_ALPHAB) + && (blit_op->blend.const_alpha == 0xff)) { + blit_op->mdp_op &= ~(MDPOP_ALPHAB); + } + + if ((blit_op->mdp_op & MDPOP_ALPHAB) + || (blit_op->mdp_op & MDPOP_TRANSP)) { + + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL; + } + } + + bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | + PPP_BLEND_BG_ALPHA_REVERSE; + + if ((ppp_per_p_alpha(c_fmt)) && !(blit_op->mdp_op & + MDPOP_LAYER_IS_FG)) { + bg_alpha |= PPP_BLEND_BG_SRCPIXEL_ALPHA; + } else { + bg_alpha |= PPP_BLEND_BG_CONSTANT_ALPHA; + bg_alpha |= blit_op->blend.const_alpha << 24; + } + PPP_WRITEL(bg_alpha, MDP3_PPP_BLEND_BG_ALPHA_SEL); + + if (blit_op->mdp_op & MDPOP_TRANSP) + *pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP; + } else if (ppp_per_p_alpha(c_fmt)) { + if (blit_op->mdp_op & MDPOP_LAYER_IS_FG) + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA; + else + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_SRCPIXEL_ALPHA; + PPP_WRITEL(0, MDP3_PPP_BLEND_BG_ALPHA_SEL); + } else { + if ((blit_op->mdp_op & MDPOP_ALPHAB) + && (blit_op->blend.const_alpha == 0xff)) { + blit_op->mdp_op &= + ~(MDPOP_ALPHAB); + } + + if ((blit_op->mdp_op & MDPOP_ALPHAB) + || (blit_op->mdp_op & MDPOP_TRANSP)) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL; + } + + if (blit_op->mdp_op & MDPOP_TRANSP) + *pppop_reg_ptr |= + PPP_BLEND_CALPHA_TRNASP; + if (is_yuv_smart_blit) { + *pppop_reg_ptr |= PPP_OP_ROT_ON | + PPP_OP_BLEND_ON | + PPP_OP_BLEND_BG_ALPHA | + PPP_OP_BLEND_EQ_REVERSE; + + if (smart_blit_bg_alpha < 0xFF) + bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | + PPP_BLEND_BG_DSTPIXEL_ALPHA; + else + bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | + PPP_BLEND_BG_DSTPIXEL_ALPHA | + PPP_BLEND_BG_CONSTANT_ALPHA; + + bg_alpha |= smart_blit_bg_alpha << 24; + PPP_WRITEL(bg_alpha, MDP3_PPP_BLEND_BG_ALPHA_SEL); + } else { + PPP_WRITEL(0, MDP3_PPP_BLEND_BG_ALPHA_SEL); + } + } + + if (*pppop_reg_ptr & PPP_OP_BLEND_ON) { + if (is_yuv_smart_blit) + config_ppp_background(&blit_op->bg, 1); + else + config_ppp_background(&blit_op->bg, 0); + + if (blit_op->dst.color_fmt == MDP_YCRYCB_H2V1) { + *pppop_reg_ptr |= PPP_OP_BG_CHROMA_H2V1; + if (blit_op->mdp_op & MDPOP_TRANSP) { + trans_color = conv_rgb2yuv(trans_color, + &csc->fwd_matrix[0], + &csc->bv[0], + &csc->lv[0]); + } + } + } + if (is_yuv_smart_blit) { + PPP_WRITEL(0, MDP3_PPP_BLEND_PARAM); + } else { + val = (alpha << MDP_BLEND_CONST_ALPHA); + val |= (trans_color & MDP_BLEND_TRASP_COL_MASK); + PP_WRITEL(val, MDP3_PPP_BLEND_PARAM); + } + return 0; +} + +int config_ppp_rotation(uint32_t mdp_op, uint32_t *pppop_reg_ptr) +{ + *pppop_reg_ptr |= PPP_OP_ROT_ON; + + if (mdp_op & MDPOP_ROT90) + *pppop_reg_ptr |= PPP_OP_ROT_90; + if (mdp_op & MDPOP_LR) + *pppop_reg_ptr |= PPP_OP_FLIP_LR; + if (mdp_op & MDPOP_UD) + *pppop_reg_ptr |= PPP_OP_FLIP_UD; + + return 0; +} + +int config_ppp_op_mode(struct ppp_blit_op *blit_op) +{ + uint32_t yuv2rgb; + uint32_t ppp_operation_reg = 0; + int sv_slice, sh_slice; + int dv_slice, dh_slice; + static struct ppp_img_desc bg_img_param; + static int bg_alpha; + static int bg_mdp_ops; + bool is_yuv_smart_blit = false; + + /* + * Detect YUV smart blit, + * Check cached BG image plane 0 address is not NILL and + * source color format is YUV than it is YUV smart blit + * mark is_yuv_smart_blit true. + */ + if ((bg_img_param.p0) && + (!(check_if_rgb(blit_op->src.color_fmt)))) + is_yuv_smart_blit = true; + + sv_slice = sh_slice = dv_slice = dh_slice = 1; + + ppp_operation_reg |= ppp_dst_op_reg(blit_op->dst.color_fmt); + switch (blit_op->dst.color_fmt) { + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + y_h_even_num(&blit_op->dst); + y_h_even_num(&blit_op->src); + dv_slice = 2; + /* fall-through */ + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_YCRYCB_H2V1: + x_w_even_num(&blit_op->dst); + x_w_even_num(&blit_op->src); + dh_slice = 2; + break; + default: + break; + } + + ppp_operation_reg |= ppp_src_op_reg(blit_op->src.color_fmt); + switch (blit_op->src.color_fmt) { + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_ADRENO: + case MDP_Y_CBCR_H2V2_VENUS: + case MDP_Y_CRCB_H2V2: + sh_slice = sv_slice = 2; + break; + case MDP_YCRYCB_H2V1: + x_w_even_num(&blit_op->dst); + x_w_even_num(&blit_op->src); + /* fall-through */ + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V1: + sh_slice = 2; + break; + default: + break; + } + + config_ppp_csc(blit_op->src.color_fmt, + blit_op->dst.color_fmt, &ppp_operation_reg); + yuv2rgb = ppp_operation_reg & PPP_OP_CONVERT_YCBCR2RGB; + + if (blit_op->mdp_op & MDPOP_DITHER) + ppp_operation_reg |= PPP_OP_DITHER_EN; + + if (blit_op->mdp_op & MDPOP_ROTATION) + config_ppp_rotation(blit_op->mdp_op, &ppp_operation_reg); + + if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_ADRENO) { + blit_op->src.stride0 = ALIGN(blit_op->src.prop.width, 32) * + ppp_bpp(blit_op->src.color_fmt); + blit_op->src.stride1 = 2 * ALIGN(blit_op->src.prop.width/2, 32); + } else if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_VENUS) { + blit_op->src.stride0 = ALIGN(blit_op->src.prop.width, 128) * + ppp_bpp(blit_op->src.color_fmt); + blit_op->src.stride1 = blit_op->src.stride0; + } else { + blit_op->src.stride0 = blit_op->src.prop.width * + ppp_bpp(blit_op->src.color_fmt); + blit_op->src.stride1 = blit_op->src.stride0; + } + + blit_op->dst.stride0 = blit_op->dst.prop.width * + ppp_bpp(blit_op->dst.color_fmt); + + if (ppp_multi_plane(blit_op->dst.color_fmt)) { + blit_op->dst.p1 = blit_op->dst.p0; + blit_op->dst.p1 += blit_op->dst.prop.width * + blit_op->dst.prop.height * + ppp_bpp(blit_op->dst.color_fmt); + } else { + blit_op->dst.p1 = NULL; + } + + if ((bg_img_param.p0) && (!(blit_op->mdp_op & MDPOP_SMART_BLIT))) { + /* + * Use cached smart blit BG layer info in + * smart Blit FG request + */ + blit_op->bg = bg_img_param; + if (check_if_rgb(blit_op->bg.color_fmt)) { + blit_op->bg.p1 = 0; + blit_op->bg.stride1 = 0; + } + memset(&bg_img_param, 0, sizeof(bg_img_param)); + } else { + blit_op->bg = blit_op->dst; + } + /* Cache smart blit BG layer info */ + if (blit_op->mdp_op & MDPOP_SMART_BLIT) + bg_img_param = blit_op->src; + + /* Jumping from Y-Plane to Chroma Plane */ + /* first pixel addr calculation */ + mdp_adjust_start_addr(blit_op, &blit_op->src, sv_slice, + sh_slice, LAYER_FG); + mdp_adjust_start_addr(blit_op, &blit_op->bg, dv_slice, + dh_slice, LAYER_BG); + mdp_adjust_start_addr(blit_op, &blit_op->dst, dv_slice, + dh_slice, LAYER_FB); + + config_ppp_scale(blit_op, &ppp_operation_reg); + + config_ppp_blend(blit_op, &ppp_operation_reg, is_yuv_smart_blit, + bg_alpha); + + config_ppp_src(&blit_op->src, yuv2rgb); + config_ppp_out(&blit_op->dst, yuv2rgb); + + /* Cache Smart blit BG alpha adn MDP OP values */ + if (blit_op->mdp_op & MDPOP_SMART_BLIT) { + bg_alpha = blit_op->blend.const_alpha; + bg_mdp_ops = blit_op->mdp_op; + } else { + bg_alpha = 0; + bg_mdp_ops = 0; + } + pr_debug("BLIT FG Param Fmt %d (x %d,y %d,w %d,h %d), ", + blit_op->src.color_fmt, blit_op->src.prop.x, + blit_op->src.prop.y, blit_op->src.prop.width, + blit_op->src.prop.height); + pr_debug("ROI(x %d,y %d,w %d, h %d) ", + blit_op->src.roi.x, blit_op->src.roi.y, + blit_op->src.roi.width, blit_op->src.roi.height); + pr_debug("Addr_P0 %pK, Stride S0 %d Addr_P1 %pK, Stride S1 %d\n", + blit_op->src.p0, blit_op->src.stride0, + blit_op->src.p1, blit_op->src.stride1); + + if (blit_op->bg.p0 != blit_op->dst.p0) { + pr_debug("BLIT BG Param Fmt %d (x %d,y %d,w %d,h %d), ", + blit_op->bg.color_fmt, blit_op->bg.prop.x, + blit_op->bg.prop.y, blit_op->bg.prop.width, + blit_op->bg.prop.height); + pr_debug("ROI(x %d,y %d, w %d, h %d) ", + blit_op->bg.roi.x, blit_op->bg.roi.y, + blit_op->bg.roi.width, blit_op->bg.roi.height); + pr_debug("Addr %pK, Stride S0 %d Addr_P1 %pK, Stride S1 %d\n", + blit_op->bg.p0, blit_op->bg.stride0, + blit_op->bg.p1, blit_op->bg.stride1); + } + pr_debug("BLIT FB Param Fmt %d (x %d,y %d,w %d,h %d), ", + blit_op->dst.color_fmt, blit_op->dst.prop.x, + blit_op->dst.prop.y, blit_op->dst.prop.width, + blit_op->dst.prop.height); + pr_debug("ROI(x %d,y %d, w %d, h %d) ", + blit_op->dst.roi.x, blit_op->dst.roi.y, + blit_op->dst.roi.width, blit_op->dst.roi.height); + pr_debug("Addr %p, Stride S0 %d Addr_P1 %p, Stride S1 %d\n", + blit_op->dst.p0, blit_op->dst.stride0, + blit_op->dst.p1, blit_op->dst.stride1); + + PPP_WRITEL(ppp_operation_reg, MDP3_PPP_OP_MODE); + mb(); /* make sure everything is written before enable */ + MDSS_XLOG(ppp_operation_reg, blit_op->src.roi.x, blit_op->src.roi.y, + blit_op->src.roi.width, blit_op->src.roi.height); + MDSS_XLOG(blit_op->dst.roi.x, blit_op->dst.roi.y, + blit_op->dst.roi.width, blit_op->dst.roi.height); + return 0; +} + +void ppp_enable(void) +{ + PPP_WRITEL(0x1000, 0x30); + mb(); /* make sure everything is written before enable */ +} + +int mdp3_ppp_init(void) +{ + load_ppp_lut(LUT_PRE_TABLE, ppp_default_pre_lut()); + load_ppp_lut(LUT_POST_TABLE, ppp_default_post_lut()); + load_csc_matrix(CSC_PRIMARY_MATRIX, ppp_csc_rgb2yuv()); + load_csc_matrix(CSC_SECONDARY_MATRIX, ppp_csc_table2()); + return 0; +} diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h new file mode 100644 index 0000000000000000000000000000000000000000..22844df4882e450878b65cd0e34dab3f7d9d3f93 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss.h @@ -0,0 +1,617 @@ +/* Copyright (c) 2012-2018, 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 MDSS_H +#define MDSS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "mdss_panel.h" + +#define MAX_DRV_SUP_MMB_BLKS 44 +#define MAX_DRV_SUP_PIPES 10 +#define MAX_CLIENT_NAME_LEN 20 + +#define MDSS_PINCTRL_STATE_DEFAULT "mdss_default" +#define MDSS_PINCTRL_STATE_SLEEP "mdss_sleep" + +enum mdss_mdp_clk_type { + MDSS_CLK_AHB, + MDSS_CLK_AXI, + MDSS_CLK_MDP_CORE, + MDSS_CLK_MDP_LUT, + MDSS_CLK_MDP_VSYNC, + MDSS_CLK_MNOC_AHB, + MDSS_CLK_THROTTLE_AXI, + MDSS_MAX_CLK +}; + +enum mdss_iommu_domain_type { + MDSS_IOMMU_DOMAIN_UNSECURE, + MDSS_IOMMU_DOMAIN_ROT_UNSECURE, + MDSS_IOMMU_DOMAIN_SECURE, + MDSS_IOMMU_DOMAIN_ROT_SECURE, + MDSS_IOMMU_MAX_DOMAIN +}; + +enum mdss_bus_vote_type { + VOTE_INDEX_DISABLE, + VOTE_INDEX_LOW, + VOTE_INDEX_MID, + VOTE_INDEX_HIGH, + VOTE_INDEX_MAX, +}; + +struct mdss_hw_settings { + char __iomem *reg; + u32 val; +}; + +struct mdss_max_bw_settings { + u32 mdss_max_bw_mode; + u32 mdss_max_bw_val; +}; + +struct mdss_debug_inf { + void *debug_data; + void (*debug_enable_clock)(int on); +}; + +struct mdss_perf_tune { + unsigned long min_mdp_clk; + u64 min_bus_vote; +}; + +#define MDSS_IRQ_SUSPEND -1 +#define MDSS_IRQ_RESUME 1 +#define MDSS_IRQ_REQ 0 + +struct mdss_intr { + /* requested intr */ + u32 req; + /* currently enabled intr */ + u32 curr; + int state; + spinlock_t lock; +}; + +struct simplified_prefill_factors { + u32 fmt_mt_nv12_factor; + u32 fmt_mt_factor; + u32 fmt_linear_factor; + u32 scale_factor; + u32 xtra_ff_factor; +}; + +struct mdss_prefill_data { + u32 ot_bytes; + u32 y_buf_bytes; + u32 y_scaler_lines_bilinear; + u32 y_scaler_lines_caf; + u32 post_scaler_pixels; + u32 pp_pixels; + u32 fbc_lines; + u32 ts_threshold; + u32 ts_end; + u32 ts_overhead; + struct mult_factor ts_rate; + struct simplified_prefill_factors prefill_factors; +}; + +struct mdss_mdp_dsc { + u32 num; + char __iomem *base; +}; + +enum mdss_hw_index { + MDSS_HW_MDP, + MDSS_HW_DSI0 = 1, + MDSS_HW_DSI1, + MDSS_HW_HDMI, + MDSS_HW_EDP, + MDSS_HW_MISC, + MDSS_MAX_HW_BLK +}; + +enum mdss_bus_clients { + MDSS_MDP_RT, + MDSS_DSI_RT, + MDSS_HW_RT, + MDSS_MDP_NRT, + MDSS_MAX_BUS_CLIENTS +}; + +struct mdss_pp_block_off { + u32 sspp_igc_lut_off; + u32 vig_pcc_off; + u32 rgb_pcc_off; + u32 dma_pcc_off; + u32 lm_pgc_off; + u32 dspp_gamut_off; + u32 dspp_pcc_off; + u32 dspp_pgc_off; +}; + +enum mdss_hw_quirk { + MDSS_QUIRK_BWCPANIC, + MDSS_QUIRK_ROTCDP, + MDSS_QUIRK_DOWNSCALE_HANG, + MDSS_QUIRK_DSC_RIGHT_ONLY_PU, + MDSS_QUIRK_DSC_2SLICE_PU_THRPUT, + MDSS_QUIRK_DMA_BI_DIR, + MDSS_QUIRK_FMT_PACK_PATTERN, + MDSS_QUIRK_NEED_SECURE_MAP, + MDSS_QUIRK_SRC_SPLIT_ALWAYS, + MDSS_QUIRK_HDR_SUPPORT_ENABLED, + MDSS_QUIRK_MDP_CLK_SET_RATE, + MDSS_QUIRK_MAX, +}; + +enum mdss_hw_capabilities { + MDSS_CAPS_YUV_CONFIG, + MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED, + MDSS_CAPS_3D_MUX_UNDERRUN_RECOVERY_SUPPORTED, + MDSS_CAPS_MIXER_1_FOR_WB, + MDSS_CAPS_QSEED3, + MDSS_CAPS_DEST_SCALER, + MDSS_CAPS_10_BIT_SUPPORTED, + MDSS_CAPS_MAX, +}; + +enum mdss_qos_settings { + MDSS_QOS_PER_PIPE_IB, + MDSS_QOS_OVERHEAD_FACTOR, + MDSS_QOS_CDP, + MDSS_QOS_OTLIM, + MDSS_QOS_PER_PIPE_LUT, + MDSS_QOS_SIMPLIFIED_PREFILL, + MDSS_QOS_VBLANK_PANIC_CTRL, + MDSS_QOS_TS_PREFILL, + MDSS_QOS_REMAPPER, + MDSS_QOS_IB_NOCR, + MDSS_QOS_MAX, +}; + +enum mdss_mdp_pipe_type { + MDSS_MDP_PIPE_TYPE_INVALID = -1, + MDSS_MDP_PIPE_TYPE_VIG = 0, + MDSS_MDP_PIPE_TYPE_RGB, + MDSS_MDP_PIPE_TYPE_DMA, + MDSS_MDP_PIPE_TYPE_CURSOR, + MDSS_MDP_PIPE_TYPE_MAX, +}; + +struct reg_bus_client { + char name[MAX_CLIENT_NAME_LEN]; + short usecase_ndx; + u32 id; + struct list_head list; +}; + +struct mdss_smmu_client { + struct device *dev; + struct dma_iommu_mapping *mmu_mapping; + struct mdss_module_power mp; + struct reg_bus_client *reg_bus_clt; + bool domain_attached; + bool handoff_pending; + char __iomem *mmu_base; +}; + +struct mdss_mdp_qseed3_lut_tbl { + bool valid; + u32 *dir_lut; + u32 *cir_lut; + u32 *sep_lut; +}; + +struct mdss_scaler_block { + u32 vig_scaler_off; + u32 vig_scaler_lut_off; + u32 has_dest_scaler; + char __iomem *dest_base; + u32 ndest_scalers; + u32 *dest_scaler_off; + u32 *dest_scaler_lut_off; + struct mdss_mdp_qseed3_lut_tbl lut_tbl; + + /* + * Lock is mainly to serialize access to LUT. + * LUT values come asynchronously from userspace + * via ioctl. + */ + struct mutex scaler_lock; +}; + +struct mdss_data_type; + +struct mdss_smmu_ops { + int (*smmu_attach)(struct mdss_data_type *mdata); + int (*smmu_detach)(struct mdss_data_type *mdata); + int (*smmu_get_domain_id)(u32 type); + struct dma_buf_attachment * (*smmu_dma_buf_attach)( + struct dma_buf *dma_buf, struct device *devce, + int domain); + int (*smmu_map_dma_buf)(struct dma_buf *dma_buf, + struct sg_table *table, int domain, + dma_addr_t *iova, unsigned long *size, int dir); + void (*smmu_unmap_dma_buf)(struct sg_table *table, int domain, + int dir, struct dma_buf *dma_buf); + int (*smmu_dma_alloc_coherent)(struct device *dev, size_t size, + dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr, + gfp_t gfp, int domain); + void (*smmu_dma_free_coherent)(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t phys, dma_addr_t iova, + int domain); + int (*smmu_map)(int domain, phys_addr_t iova, phys_addr_t phys, int + gfp_order, int prot); + void (*smmu_unmap)(int domain, unsigned long iova, int gfp_order); + char * (*smmu_dsi_alloc_buf)(struct device *dev, int size, + dma_addr_t *dmap, gfp_t gfp); + int (*smmu_dsi_map_buffer)(phys_addr_t phys, unsigned int domain, + unsigned long size, dma_addr_t *dma_addr, + void *cpu_addr, int dir); + void (*smmu_dsi_unmap_buffer)(dma_addr_t dma_addr, int domain, + unsigned long size, int dir); + void (*smmu_deinit)(struct mdss_data_type *mdata); + struct sg_table * (*smmu_sg_table_clone)(struct sg_table *orig_table, + gfp_t gfp_mask, bool padding); +}; + +struct mdss_data_type { + u32 mdp_rev; + struct clk *mdp_clk[MDSS_MAX_CLK]; + struct regulator *fs; + struct regulator *venus; + struct regulator *vdd_cx; + bool batfet_required; + struct regulator *batfet; + bool en_svs_high; + u32 max_mdp_clk_rate; + struct mdss_util_intf *mdss_util; + struct mdss_panel_data *pdata; + unsigned long mdp_clk_rate; + + struct platform_device *pdev; + struct mdss_io_data mdss_io; + struct mdss_io_data vbif_io; + struct mdss_io_data vbif_nrt_io; + char __iomem *mdp_base; + + struct mdss_smmu_client mdss_smmu[MDSS_IOMMU_MAX_DOMAIN]; + struct mdss_smmu_ops smmu_ops; + struct mutex reg_lock; + + /* bitmap to track pipes that have BWC enabled */ + DECLARE_BITMAP(bwc_enable_map, MAX_DRV_SUP_PIPES); + /* bitmap to track hw workarounds */ + DECLARE_BITMAP(mdss_quirk_map, MDSS_QUIRK_MAX); + /* bitmap to track total mmbs in use */ + DECLARE_BITMAP(mmb_alloc_map, MAX_DRV_SUP_MMB_BLKS); + /* bitmap to track qos applicable settings */ + DECLARE_BITMAP(mdss_qos_map, MDSS_QOS_MAX); + /* bitmap to track hw capabilities/features */ + DECLARE_BITMAP(mdss_caps_map, MDSS_CAPS_MAX); + + u32 has_bwc; + /* values used when HW has a common panic/robust LUT */ + u32 default_panic_lut0; + u32 default_panic_lut1; + u32 default_robust_lut; + + /* values used when HW has panic/robust LUTs per pipe */ + u32 default_panic_lut_per_pipe_linear; + u32 default_panic_lut_per_pipe_tile; + u32 default_robust_lut_per_pipe_linear; + u32 default_robust_lut_per_pipe_tile; + + u32 has_decimation; + bool has_fixed_qos_arbiter_enabled; + bool has_panic_ctrl; + u32 wfd_mode; + u32 has_no_lut_read; + atomic_t sd_client_count; + u8 has_wb_ad; + u8 has_non_scalar_rgb; + bool has_src_split; + bool idle_pc_enabled; + bool has_pingpong_split; + bool has_pixel_ram; + bool needs_hist_vote; + bool has_ubwc; + bool has_wb_ubwc; + bool has_separate_rotator; + + u32 default_ot_rd_limit; + u32 default_ot_wr_limit; + + struct irq_domain *irq_domain; + u32 *mdp_irq_mask; + u32 mdp_hist_irq_mask; + u32 mdp_intf_irq_mask; + + int suspend_fs_ena; + u8 clk_ena; + u8 fs_ena; + u8 vsync_ena; + + struct notifier_block gdsc_cb; + + u32 res_init; + + u32 highest_bank_bit; + u32 smp_mb_cnt; + u32 smp_mb_size; + u32 smp_mb_per_pipe; + u32 pixel_ram_size; + + u32 rot_block_size; + + /* HW RT bus (AXI) */ + u32 hw_rt_bus_hdl; + u32 hw_rt_bus_ref_cnt; + + /* data bus (AXI) */ + u32 bus_hdl; + u32 bus_ref_cnt; + struct mutex bus_lock; + + /* register bus (AHB) */ + u32 reg_bus_hdl; + u32 reg_bus_usecase_ndx; + struct list_head reg_bus_clist; + struct mutex reg_bus_lock; + struct reg_bus_client *reg_bus_clt; + struct reg_bus_client *pp_reg_bus_clt; + + u32 axi_port_cnt; + u32 nrt_axi_port_cnt; + u32 bus_channels; + u32 curr_bw_uc_idx; + u32 ao_bw_uc_idx; /* active only idx */ + struct msm_bus_scale_pdata *bus_scale_table; + struct msm_bus_scale_pdata *reg_bus_scale_table; + struct msm_bus_scale_pdata *hw_rt_bus_scale_table; + u32 max_bw_low; + u32 max_bw_high; + u32 max_bw_per_pipe; + u32 *vbif_rt_qos; + u32 *vbif_nrt_qos; + u32 npriority_lvl; + u32 rot_dwnscale_min; + u32 rot_dwnscale_max; + + struct mult_factor ab_factor; + struct mult_factor ib_factor; + struct mult_factor ib_factor_overlap; + struct mult_factor clk_factor; + struct mult_factor per_pipe_ib_factor; + bool apply_post_scale_bytes; + bool hflip_buffer_reused; + + u32 disable_prefill; + u32 *clock_levels; + u32 nclk_lvl; + + u32 enable_gate; + u32 enable_bw_release; + u32 enable_rotator_bw_release; + u32 enable_cdp; + u32 serialize_wait4pp; + u32 wait4autorefresh; + u32 lines_before_active; + + struct mdss_hw_settings *hw_settings; + + int rects_per_sspp[MDSS_MDP_PIPE_TYPE_MAX]; + struct mdss_mdp_pipe *vig_pipes; + struct mdss_mdp_pipe *rgb_pipes; + struct mdss_mdp_pipe *dma_pipes; + struct mdss_mdp_pipe *cursor_pipes; + u32 nvig_pipes; + u32 nrgb_pipes; + u32 ndma_pipes; + u32 max_target_zorder; + u8 ncursor_pipes; + u32 max_cursor_size; + + u32 nppb_ctl; + u32 *ppb_ctl; + u32 nppb_cfg; + u32 *ppb_cfg; + char __iomem *slave_pingpong_base; + + struct mdss_mdp_mixer *mixer_intf; + struct mdss_mdp_mixer *mixer_wb; + u32 nmixers_intf; + u32 nmixers_wb; + u32 max_mixer_width; + u32 max_pipe_width; + + struct mdss_mdp_writeback *wb; + u32 nwb; + u32 *wb_offsets; + u32 nwb_offsets; + struct mutex wb_lock; + + struct mdss_mdp_ctl *ctl_off; + u32 nctl; + u32 ndspp; + + struct mdss_mdp_dp_intf *dp_off; + u32 ndp; + void *video_intf; + u32 nintf; + + struct mdss_mdp_ad *ad_off; + struct mdss_ad_info *ad_cfgs; + u32 nad_cfgs; + u32 nmax_concurrent_ad_hw; + struct workqueue_struct *ad_calc_wq; + u32 ad_debugen; + bool mem_retain; + + struct mdss_intr hist_intr; + + int iommu_attached; + + struct debug_bus *dbg_bus; + u32 dbg_bus_size; + struct vbif_debug_bus *vbif_dbg_bus; + u32 vbif_dbg_bus_size; + struct vbif_debug_bus *nrt_vbif_dbg_bus; + u32 nrt_vbif_dbg_bus_size; + struct mdss_debug_inf debug_inf; + bool mixer_switched; + struct mdss_panel_cfg pan_cfg; + struct mdss_prefill_data prefill_data; + u32 min_prefill_lines; /* this changes within different chipsets */ + u32 props; + + int handoff_pending; + bool idle_pc; + struct mdss_perf_tune perf_tune; + bool traffic_shaper_en; + int iommu_ref_cnt; + u32 latency_buff_per; + atomic_t active_intf_cnt; + bool has_rot_dwnscale; + bool regulator_notif_register; + + u64 ab[MDSS_MAX_BUS_CLIENTS]; + u64 ib[MDSS_MAX_BUS_CLIENTS]; + struct mdss_pp_block_off pp_block_off; + + struct mdss_mdp_cdm *cdm_off; + u32 ncdm; + struct mutex cdm_lock; + + struct mdss_mdp_dsc *dsc_off; + u32 ndsc; + + struct mdss_max_bw_settings *max_bw_settings; + u32 bw_mode_bitmap; + u32 max_bw_settings_cnt; + bool bw_limit_pending; + + struct mdss_max_bw_settings *max_per_pipe_bw_settings; + u32 mdss_per_pipe_bw_cnt; + u32 min_bw_per_pipe; + + u32 bcolor0; + u32 bcolor1; + u32 bcolor2; + struct mdss_scaler_block *scaler_off; + + u32 splash_intf_sel; + u32 splash_split_disp; + struct mult_factor bus_throughput_factor; +}; + +extern struct mdss_data_type *mdss_res; + +struct irq_info { + u32 irq; + u32 irq_mask; + u32 irq_wake_mask; + u32 irq_ena; + u32 irq_wake_ena; + u32 irq_buzy; +}; + +struct mdss_hw { + u32 hw_ndx; + void *ptr; + struct irq_info *irq_info; + irqreturn_t (*irq_handler)(int irq, void *ptr); +}; + +struct irq_info *mdss_intr_line(void); +void mdss_bus_bandwidth_ctrl(int enable); +int mdss_iommu_ctrl(int enable); +int mdss_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota); +int mdss_update_reg_bus_vote(struct reg_bus_client *bus_client, + u32 usecase_ndx); +struct reg_bus_client *mdss_reg_bus_vote_client_create(char *client_name); +void mdss_reg_bus_vote_client_destroy(struct reg_bus_client *bus_client); + +struct mdss_util_intf { + bool mdp_probe_done; + int (*register_irq)(struct mdss_hw *hw); + void (*enable_irq)(struct mdss_hw *hw); + void (*disable_irq)(struct mdss_hw *hw); + void (*enable_wake_irq)(struct mdss_hw *hw); + void (*disable_wake_irq)(struct mdss_hw *hw); + void (*disable_irq_nosync)(struct mdss_hw *hw); + int (*irq_dispatch)(u32 hw_ndx, int irq, void *ptr); + int (*get_iommu_domain)(u32 type); + int (*iommu_attached)(void); + int (*iommu_ctrl)(int enable); + void (*iommu_lock)(void); + void (*iommu_unlock)(void); + void (*bus_bandwidth_ctrl)(int enable); + int (*bus_scale_set_quota)(int client, u64 ab_quota, u64 ib_quota); + int (*panel_intf_status)(u32 disp_num, u32 intf_type); + struct mdss_panel_cfg* (*panel_intf_type)(int intf_val); + int (*dyn_clk_gating_ctrl)(int enable); + bool (*param_check)(char *param_string); + bool display_disabled; +}; + +struct mdss_util_intf *mdss_get_util_intf(void); +bool mdss_get_irq_enable_state(struct mdss_hw *hw); + +static inline int mdss_get_sd_client_cnt(void) +{ + if (!mdss_res) + return 0; + else + return atomic_read(&mdss_res->sd_client_count); +} + +static inline void mdss_set_quirk(struct mdss_data_type *mdata, + enum mdss_hw_quirk bit) +{ + set_bit(bit, mdata->mdss_quirk_map); +} + +static inline bool mdss_has_quirk(struct mdss_data_type *mdata, + enum mdss_hw_quirk bit) +{ + return test_bit(bit, mdata->mdss_quirk_map); +} + +#define MDSS_VBIF_WRITE(mdata, offset, value, nrt_vbif) \ + (nrt_vbif ? mdss_reg_w(&mdata->vbif_nrt_io, offset, value, 0) :\ + mdss_reg_w(&mdata->vbif_io, offset, value, 0)) +#define MDSS_VBIF_READ(mdata, offset, nrt_vbif) \ + (nrt_vbif ? mdss_reg_r(&mdata->vbif_nrt_io, offset, 0) :\ + mdss_reg_r(&mdata->vbif_io, offset, 0)) +#define MDSS_REG_WRITE(mdata, offset, value) \ + mdss_reg_w(&mdata->mdss_io, offset, value, 0) +#define MDSS_REG_READ(mdata, offset) \ + mdss_reg_r(&mdata->mdss_io, offset, 0) + +#endif /* MDSS_H */ diff --git a/drivers/video/fbdev/msm/mdss_cec_core.c b/drivers/video/fbdev/msm/mdss_cec_core.c new file mode 100644 index 0000000000000000000000000000000000000000..23a3ce55c2d62f68302b48d6678bb7c40095a825 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_cec_core.c @@ -0,0 +1,799 @@ +/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include + +#include "mdss_fb.h" +#include "mdss_cec_core.h" + +#define CEC_ENABLE_MASK BIT(0) +#define CEC_WAKEUP_ENABLE_MASK BIT(1) + +struct cec_msg_node { + struct cec_msg msg; + struct list_head list; +}; + +struct cec_ctl { + bool enabled; + bool compliance_enabled; + bool cec_wakeup_en; + + u8 logical_addr; + + spinlock_t lock; + struct list_head msg_head; + struct cec_abstract_init_data init_data; + +}; + +static struct cec_ctl *cec_get_ctl(struct device *dev) +{ + struct fb_info *fbi; + struct msm_fb_data_type *mfd; + struct mdss_panel_info *pinfo; + + if (!dev) { + pr_err("invalid input\n"); + goto error; + } + + fbi = dev_get_drvdata(dev); + if (!fbi) { + pr_err("invalid fbi\n"); + goto error; + } + + mfd = fbi->par; + if (!mfd) { + pr_err("invalid mfd\n"); + goto error; + } + + pinfo = mfd->panel_info; + if (!pinfo) { + pr_err("invalid pinfo\n"); + goto error; + } + + return pinfo->cec_data; + +error: + return NULL; +} + +static int cec_msg_send(struct cec_ctl *ctl, struct cec_msg *msg) +{ + int ret = -EINVAL; + struct cec_ops *ops; + + if (!ctl || !msg) { + pr_err("invalid input\n"); + goto end; + } + + ops = ctl->init_data.ops; + + if (ops && ops->send_msg) + ret = ops->send_msg(ops->data, msg); +end: + return ret; +} + +static void cec_dump_msg(struct cec_ctl *ctl, struct cec_msg *msg) +{ + int i; + unsigned long flags; + + if (!ctl || !msg) { + pr_err("invalid input\n"); + return; + } + + spin_lock_irqsave(&ctl->lock, flags); + pr_debug("==%pS dump start ==\n", + __builtin_return_address(0)); + + pr_debug("cec: sender_id: %d\n", msg->sender_id); + pr_debug("cec: recvr_id: %d\n", msg->recvr_id); + + if (msg->frame_size < 2) { + pr_debug("cec: polling message\n"); + spin_unlock_irqrestore(&ctl->lock, flags); + return; + } + + pr_debug("cec: opcode: %02x\n", msg->opcode); + for (i = 0; i < msg->frame_size - 2; i++) + pr_debug("cec: operand(%2d) : %02x\n", i + 1, msg->operand[i]); + + pr_debug("==%pS dump end ==\n", + __builtin_return_address(0)); + spin_unlock_irqrestore(&ctl->lock, flags); +} + +static int cec_disable(struct cec_ctl *ctl) +{ + unsigned long flags; + int ret = -EINVAL; + struct cec_msg_node *msg_node, *tmp; + struct cec_ops *ops; + + if (!ctl) { + pr_err("Invalid input\n"); + goto end; + } + + spin_lock_irqsave(&ctl->lock, flags); + list_for_each_entry_safe(msg_node, tmp, &ctl->msg_head, list) { + list_del(&msg_node->list); + kfree(msg_node); + } + spin_unlock_irqrestore(&ctl->lock, flags); + + ops = ctl->init_data.ops; + + if (ops && ops->enable) + ret = ops->enable(ops->data, false); + + if (!ret) + ctl->enabled = false; + +end: + return ret; +} + +static int cec_enable(struct cec_ctl *ctl) +{ + int ret = -EINVAL; + struct cec_ops *ops; + + if (!ctl) { + pr_err("Invalid input\n"); + goto end; + } + + INIT_LIST_HEAD(&ctl->msg_head); + + ops = ctl->init_data.ops; + + if (ops && ops->enable) + ret = ops->enable(ops->data, true); + + if (!ret) + ctl->enabled = true; + +end: + return ret; +} + +static int cec_send_abort_opcode(struct cec_ctl *ctl, + struct cec_msg *in_msg, u8 reason_operand) +{ + int i = 0; + struct cec_msg out_msg; + + if (!ctl || !in_msg) { + pr_err("Invalid input\n"); + return -EINVAL; + } + + out_msg.sender_id = 0x4; + out_msg.recvr_id = in_msg->sender_id; + out_msg.opcode = 0x0; /* opcode for feature abort */ + out_msg.operand[i++] = in_msg->opcode; + out_msg.operand[i++] = reason_operand; + out_msg.frame_size = i + 2; + + return cec_msg_send(ctl, &out_msg); +} + +static int cec_msg_parser(struct cec_ctl *ctl, struct cec_msg *in_msg) +{ + int rc = 0, i = 0; + struct cec_msg out_msg; + + if (!ctl || !in_msg) { + pr_err("Invalid input\n"); + rc = -EINVAL; + goto end; + } + + pr_debug("in_msg->opcode = 0x%x\n", in_msg->opcode); + switch (in_msg->opcode) { + case CEC_MSG_SET_OSD_STRING: + /* Set OSD String */ + pr_debug("Recvd OSD Str=[0x%x]\n", + in_msg->operand[3]); + break; + case CEC_MSG_GIVE_PHYS_ADDR: + /* Give Phy Addr */ + pr_debug("Recvd a Give Phy Addr cmd\n"); + + out_msg.sender_id = 0x4; + /* Broadcast */ + out_msg.recvr_id = 0xF; + out_msg.opcode = 0x84; + out_msg.operand[i++] = 0x10; + out_msg.operand[i++] = 0x0; + out_msg.operand[i++] = 0x04; + out_msg.frame_size = i + 2; + + rc = cec_msg_send(ctl, &out_msg); + break; + case CEC_MSG_ABORT: + /* Abort */ + pr_debug("Recvd an abort cmd.\n"); + + /* reason = "Refused" */ + rc = cec_send_abort_opcode(ctl, in_msg, 0x04); + break; + case CEC_MSG_GIVE_OSD_NAME: + /* Give OSD name */ + pr_debug("Recvd 'Give OSD name' cmd.\n"); + + out_msg.sender_id = 0x4; + out_msg.recvr_id = in_msg->sender_id; + out_msg.opcode = 0x47; /* OSD Name */ + /* Display control byte */ + out_msg.operand[i++] = 0x0; + out_msg.operand[i++] = 'H'; + out_msg.operand[i++] = 'e'; + out_msg.operand[i++] = 'l'; + out_msg.operand[i++] = 'l'; + out_msg.operand[i++] = 'o'; + out_msg.operand[i++] = ' '; + out_msg.operand[i++] = 'W'; + out_msg.operand[i++] = 'o'; + out_msg.operand[i++] = 'r'; + out_msg.operand[i++] = 'l'; + out_msg.operand[i++] = 'd'; + out_msg.frame_size = i + 2; + + rc = cec_msg_send(ctl, &out_msg); + break; + case CEC_MSG_GIVE_POWER_STATUS: + /* Give Device Power status */ + pr_debug("Recvd a Power status message\n"); + + out_msg.sender_id = 0x4; + out_msg.recvr_id = in_msg->sender_id; + out_msg.opcode = 0x90; /* OSD String */ + out_msg.operand[i++] = 'H'; + out_msg.operand[i++] = 'e'; + out_msg.operand[i++] = 'l'; + out_msg.operand[i++] = 'l'; + out_msg.operand[i++] = 'o'; + out_msg.operand[i++] = ' '; + out_msg.operand[i++] = 'W'; + out_msg.operand[i++] = 'o'; + out_msg.operand[i++] = 'r'; + out_msg.operand[i++] = 'l'; + out_msg.operand[i++] = 'd'; + out_msg.frame_size = i + 2; + + rc = cec_msg_send(ctl, &out_msg); + break; + case CEC_MSG_ROUTE_CHANGE_CMD: + /* Routing Change cmd */ + case CEC_MSG_SET_STREAM_PATH: + /* Set Stream Path */ + pr_debug("Recvd Set Stream or Routing Change cmd\n"); + + out_msg.sender_id = 0x4; + out_msg.recvr_id = 0xF; /* broadcast this message */ + out_msg.opcode = 0x82; /* Active Source */ + out_msg.operand[i++] = 0x10; + out_msg.operand[i++] = 0x0; + out_msg.frame_size = i + 2; + + rc = cec_msg_send(ctl, &out_msg); + if (rc) + goto end; + + /* sending message */ + memset(&out_msg, 0x0, sizeof(struct cec_msg)); + i = 0; + out_msg.sender_id = 0x4; + out_msg.recvr_id = in_msg->sender_id; + out_msg.opcode = 0x04; /* opcode for Image View On */ + out_msg.frame_size = i + 2; + + rc = cec_msg_send(ctl, &out_msg); + break; + case CEC_MSG_USER_CTRL_PRESS: + /* User Control Pressed */ + pr_debug("User Control Pressed\n"); + break; + case CEC_MSG_USER_CTRL_RELEASE: + /* User Control Released */ + pr_debug("User Control Released\n"); + break; + default: + pr_debug("Recvd an unknown cmd = [%u]\n", + in_msg->opcode); + + /* reason = "Unrecognized opcode" */ + rc = cec_send_abort_opcode(ctl, in_msg, 0x0); + break; + } +end: + return rc; +} + +static int cec_msg_recv(void *data, struct cec_msg *msg) +{ + unsigned long flags; + struct cec_ctl *ctl = data; + struct cec_msg_node *msg_node; + int ret = 0; + + if (!ctl) { + pr_err("invalid input\n"); + ret = -EINVAL; + goto end; + } + + if (!ctl->enabled) { + pr_err("cec not enabled\n"); + ret = -ENODEV; + goto end; + } + + msg_node = kzalloc(sizeof(*msg_node), GFP_KERNEL); + if (!msg_node) { + ret = -ENOMEM; + goto end; + } + + msg_node->msg = *msg; + + pr_debug("CEC read frame done\n"); + cec_dump_msg(ctl, &msg_node->msg); + + spin_lock_irqsave(&ctl->lock, flags); + if (ctl->compliance_enabled) { + spin_unlock_irqrestore(&ctl->lock, flags); + + ret = cec_msg_parser(ctl, &msg_node->msg); + if (ret) + pr_err("msg parsing failed\n"); + + kfree(msg_node); + } else { + list_add_tail(&msg_node->list, &ctl->msg_head); + spin_unlock_irqrestore(&ctl->lock, flags); + + /* wake-up sysfs read_msg context */ + sysfs_notify(ctl->init_data.kobj, "cec", "rd_msg"); + } +end: + return ret; +} + +static ssize_t cec_rda_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + unsigned long flags; + struct cec_ctl *ctl = cec_get_ctl(dev); + + if (!ctl) { + pr_err("Invalid input\n"); + ret = -EINVAL; + goto end; + } + + spin_lock_irqsave(&ctl->lock, flags); + if (ctl->enabled) { + pr_debug("cec is enabled\n"); + ret = snprintf(buf, PAGE_SIZE, "%d\n", 1); + } else { + pr_err("cec is disabled\n"); + ret = snprintf(buf, PAGE_SIZE, "%d\n", 0); + } + spin_unlock_irqrestore(&ctl->lock, flags); +end: + return ret; +} + +static ssize_t cec_wta_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int val; + bool cec_en; + ssize_t ret; + struct cec_ctl *ctl = cec_get_ctl(dev); + struct cec_ops *ops; + + if (!ctl) { + pr_err("Invalid input\n"); + ret = -EINVAL; + goto end; + } + + ops = ctl->init_data.ops; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_err("kstrtoint failed.\n"); + goto end; + } + + cec_en = (val & CEC_ENABLE_MASK) ? true : false; + + /* bit 1 is used for wakeup feature */ + if ((val & CEC_ENABLE_MASK) && (val & CEC_WAKEUP_ENABLE_MASK)) + ctl->cec_wakeup_en = true; + else + ctl->cec_wakeup_en = false; + + if (ops && ops->wakeup_en) + ops->wakeup_en(ops->data, ctl->cec_wakeup_en); + + if (ctl->enabled == cec_en) { + pr_debug("cec is already %s\n", + cec_en ? "enabled" : "disabled"); + goto bail; + } + + if (cec_en) + ret = cec_enable(ctl); + else + ret = cec_disable(ctl); + + if (ret) + goto end; + +bail: + ret = strnlen(buf, PAGE_SIZE); +end: + return ret; +} + +static ssize_t cec_rda_enable_compliance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long flags; + ssize_t ret; + struct cec_ctl *ctl = cec_get_ctl(dev); + + if (!ctl) { + pr_err("Invalid ctl\n"); + return -EINVAL; + } + + spin_lock_irqsave(&ctl->lock, flags); + ret = snprintf(buf, PAGE_SIZE, "%d\n", + ctl->compliance_enabled); + + spin_unlock_irqrestore(&ctl->lock, flags); + + return ret; +} + +static ssize_t cec_wta_enable_compliance(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int val; + ssize_t ret; + struct cec_ctl *ctl = cec_get_ctl(dev); + struct cec_ops *ops; + + if (!ctl) { + pr_err("Invalid ctl\n"); + ret = -EINVAL; + goto end; + } + + ops = ctl->init_data.ops; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_err("kstrtoint failed.\n"); + goto end; + } + + ctl->compliance_enabled = (val == 1) ? true : false; + + if (ctl->compliance_enabled) { + ret = cec_enable(ctl); + if (ret) + goto end; + + ctl->logical_addr = 0x4; + + if (ops && ops->wt_logical_addr) + ops->wt_logical_addr(ops->data, ctl->logical_addr); + + } else { + ctl->logical_addr = 0; + + ret = cec_disable(ctl); + if (ret) + goto end; + } + + ret = strnlen(buf, PAGE_SIZE); +end: + return ret; +} + +static ssize_t cec_rda_logical_addr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long flags; + ssize_t ret; + struct cec_ctl *ctl = cec_get_ctl(dev); + + if (!ctl) { + pr_err("Invalid ctl\n"); + return -EINVAL; + } + + spin_lock_irqsave(&ctl->lock, flags); + ret = snprintf(buf, PAGE_SIZE, "%d\n", ctl->logical_addr); + spin_unlock_irqrestore(&ctl->lock, flags); + + return ret; +} + +static ssize_t cec_wta_logical_addr(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int logical_addr; + unsigned long flags; + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct cec_ctl *ctl = cec_get_ctl(dev); + struct cec_ops *ops; + + if (!ctl) { + pr_err("Invalid ctl\n"); + ret = -EINVAL; + goto end; + } + + ops = ctl->init_data.ops; + + ret = kstrtoint(buf, 10, &logical_addr); + if (ret) { + pr_err("kstrtoint failed\n"); + goto end; + } + + if (logical_addr < 0 || logical_addr > 15) { + pr_err("Invalid logical address\n"); + ret = -EINVAL; + goto end; + } + + spin_lock_irqsave(&ctl->lock, flags); + ctl->logical_addr = (u8)logical_addr; + if (ctl->enabled) { + if (ops && ops->wt_logical_addr) + ops->wt_logical_addr(ops->data, ctl->logical_addr); + } + spin_unlock_irqrestore(&ctl->lock, flags); +end: + return ret; +} + +static ssize_t cec_rda_msg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i = 0; + unsigned long flags; + struct cec_msg_node *msg_node, *tmp; + struct cec_ctl *ctl = cec_get_ctl(dev); + ssize_t ret; + + if (!ctl) { + pr_err("Invalid ctl\n"); + ret = -EINVAL; + goto end; + } + + if (!ctl->enabled) { + pr_err("cec not enabled\n"); + ret = -EINVAL; + goto end; + } + + spin_lock_irqsave(&ctl->lock, flags); + + if (ctl->compliance_enabled) { + spin_unlock_irqrestore(&ctl->lock, flags); + pr_err("Read no allowed in compliance mode\n"); + ret = -EPERM; + goto end; + } + + if (list_empty_careful(&ctl->msg_head)) { + spin_unlock_irqrestore(&ctl->lock, flags); + pr_err("CEC message queue is empty\n"); + ret = -EINVAL; + goto end; + } + + list_for_each_entry_safe(msg_node, tmp, &ctl->msg_head, list) { + if ((i + 1) * sizeof(struct cec_msg) > PAGE_SIZE) { + pr_debug("Overflowing PAGE_SIZE.\n"); + break; + } + + memcpy(buf + (i * sizeof(struct cec_msg)), &msg_node->msg, + sizeof(struct cec_msg)); + list_del(&msg_node->list); + kfree(msg_node); + i++; + } + + spin_unlock_irqrestore(&ctl->lock, flags); + + ret = i * sizeof(struct cec_msg); +end: + return ret; +} + +static ssize_t cec_wta_msg(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret; + unsigned long flags; + struct cec_msg *msg = (struct cec_msg *)buf; + struct cec_ctl *ctl = cec_get_ctl(dev); + + if (!ctl) { + pr_err("Invalid ctl\n"); + ret = -EINVAL; + goto end; + } + + spin_lock_irqsave(&ctl->lock, flags); + if (ctl->compliance_enabled) { + spin_unlock_irqrestore(&ctl->lock, flags); + pr_err("Write not allowed in compliance mode\n"); + ret = -EPERM; + goto end; + } + + if (!ctl->enabled) { + spin_unlock_irqrestore(&ctl->lock, flags); + pr_err("CEC is not configed.\n"); + ret = -EPERM; + goto end; + } + spin_unlock_irqrestore(&ctl->lock, flags); + + if (msg->frame_size > MAX_OPERAND_SIZE) { + pr_err("msg frame too big!\n"); + ret = -EINVAL; + goto end; + } + ret = cec_msg_send(ctl, msg); + if (ret) { + pr_err("cec_msg_send failed\n"); + goto end; + } + + ret = sizeof(struct cec_msg); +end: + return ret; +} + +static DEVICE_ATTR(enable, 0644, cec_rda_enable, + cec_wta_enable); +static DEVICE_ATTR(enable_compliance, 0644, + cec_rda_enable_compliance, cec_wta_enable_compliance); +static DEVICE_ATTR(logical_addr, 0600, + cec_rda_logical_addr, cec_wta_logical_addr); +static DEVICE_ATTR(rd_msg, 0444, cec_rda_msg, NULL); +static DEVICE_ATTR(wr_msg, 0600, NULL, cec_wta_msg); + +static struct attribute *cec_fs_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_enable_compliance.attr, + &dev_attr_logical_addr.attr, + &dev_attr_rd_msg.attr, + &dev_attr_wr_msg.attr, + NULL, +}; + +static struct attribute_group cec_fs_attr_group = { + .name = "cec", + .attrs = cec_fs_attrs, +}; + +/** + * cec_abstract_deinit() - Release CEC abstract module + * @input: CEC abstract data + * + * This API release all the resources allocated for this + * module. + * + * Return: 0 on success otherwise error code. + */ +int cec_abstract_deinit(void *input) +{ + struct cec_ctl *ctl = (struct cec_ctl *)input; + + if (!ctl) + return -EINVAL; + + sysfs_remove_group(ctl->init_data.kobj, &cec_fs_attr_group); + + kfree(ctl); + + return 0; +} + +/** + * cec_abstract_init() - Initialize CEC abstract module + * @init_data: data needed to initialize the CEC abstraction module + * + * This API will initialize the CEC abstract module which connects + * CEC client with CEC hardware. It creates sysfs nodes for client + * to read and write CEC messages. It interacts with hardware with + * provided operation function pointers. Also provides callback + * function pointers to let the hardware inform about incoming + * CEC message. + * + * Return: pinter to cec abstract data which needs to be passed + * as parameter with callback functions. + */ +void *cec_abstract_init(struct cec_abstract_init_data *init_data) +{ + struct cec_ctl *ctl = NULL; + int ret = 0; + + if (!init_data) { + pr_err("invalid input\n"); + ret = -EINVAL; + goto end; + } + + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); + if (!ctl) { + ret = -ENOMEM; + goto end; + } + + /* keep a copy of init data */ + ctl->init_data = *init_data; + + ret = sysfs_create_group(ctl->init_data.kobj, &cec_fs_attr_group); + if (ret) { + pr_err("cec sysfs group creation failed\n"); + goto end; + } + + spin_lock_init(&ctl->lock); + + /* provide callback function pointers */ + if (init_data->cbs) { + init_data->cbs->msg_recv_notify = cec_msg_recv; + init_data->cbs->data = ctl; + } + + return ctl; +end: + kfree(ctl); + return ERR_PTR(ret); +} + diff --git a/drivers/video/fbdev/msm/mdss_cec_core.h b/drivers/video/fbdev/msm/mdss_cec_core.h new file mode 100644 index 0000000000000000000000000000000000000000..f8196a0aa38408a5dcd4c04aa6beec440ce0426d --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_cec_core.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2015-2016, 2018, 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 __MDSS_CEC_CORE_H__ +#define __MDSS_CEC_CORE_H__ + +#define MAX_OPERAND_SIZE 14 + +/* total size: HEADER block (1) + opcode block (1) + operands (14) */ +#define MAX_CEC_FRAME_SIZE (MAX_OPERAND_SIZE + 2) + +/* CEC message set */ +#define CEC_MSG_SET_OSD_STRING 0x64 +#define CEC_MSG_GIVE_PHYS_ADDR 0x83 +#define CEC_MSG_ABORT 0xFF +#define CEC_MSG_GIVE_OSD_NAME 0x46 +#define CEC_MSG_GIVE_POWER_STATUS 0x8F +#define CEC_MSG_ROUTE_CHANGE_CMD 0x80 +#define CEC_MSG_SET_STREAM_PATH 0x86 +#define CEC_MSG_USER_CTRL_PRESS 0x44 +#define CEC_MSG_USER_CTRL_RELEASE 0x45 + +/** + * struct cec_msg - CEC message related data + * @sender_id: CEC message initiator's id + * @recvr_id: CEC message destination's id + * @opcode: CEC message opcode + * @operand: CEC message operands corresponding to opcode + * @frame_size: total CEC frame size + * @retransmit: number of re-tries to transmit message + * + * Basic CEC message structure used by both client and driver. + */ +struct cec_msg { + u8 sender_id; + u8 recvr_id; + u8 opcode; + u8 operand[MAX_OPERAND_SIZE]; + u8 frame_size; + u8 retransmit; +}; + +/** + * struct cec_ops - CEC operations function pointers + * @enable: function pointer to enable CEC + * @send_msg: function pointer to send CEC message + * @wt_logical_addr: function pointer to write logical address + * @wakeup_en: function pointer to enable wakeup feature + * @is_wakeup_en: function pointer to query wakeup feature state + * @device_suspend: function pointer to update device suspend state + * @data: pointer to the data needed to send with operation functions + * + * Defines all the operations that abstract module can call + * to programe the CEC driver. + */ +struct cec_ops { + int (*enable)(void *data, bool enable); + int (*send_msg)(void *data, + struct cec_msg *msg); + void (*wt_logical_addr)(void *data, u8 addr); + void (*wakeup_en)(void *data, bool en); + bool (*is_wakeup_en)(void *data); + void (*device_suspend)(void *data, bool suspend); + void *data; +}; + +/** + * struct cec_cbs - CEC callback function pointers + * @msg_recv_notify: function pointer called CEC driver to notify incoming msg + * @data: pointer to data needed to be send with the callback function + * + * Defines callback functions which CEC driver can callback to notify any + * change in the hardware. + */ +struct cec_cbs { + int (*msg_recv_notify)(void *data, struct cec_msg *msg); + void *data; +}; + +/** + * struct cec_abstract_init_data - initalization data for abstract module + * @ops: pointer to struct containing all operation function pointers + * @cbs: pointer to struct containing all callack function pointers + * @kobj: pointer to kobject instance associated with CEC driver. + * + * Defines initialization data needed by init API to initialize the module. + */ +struct cec_abstract_init_data { + struct cec_ops *ops; + struct cec_cbs *cbs; + struct kobject *kobj; +}; + +void *cec_abstract_init(struct cec_abstract_init_data *init_data); +int cec_abstract_deinit(void *input); +#endif /* __MDSS_CEC_CORE_H_*/ diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..a81f149f59ce9f97d95f4a0dc2ef135590eac9a4 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -0,0 +1,4318 @@ +/* + * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * Copyright (C) 1994 Martin Schaller + * + * 2001 - Documented with DocBook + * - Brad Douglas + * + * 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 +#include + +#include + +#include "mdss_fb.h" +#include "mdss_compat_utils.h" +#include "mdss_mdp_hwio.h" +#include "mdss_mdp.h" + +#define MSMFB_CURSOR32 _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor32) +#define MSMFB_SET_LUT32 _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap32) +#define MSMFB_HISTOGRAM32 _IOWR(MSMFB_IOCTL_MAGIC, 132,\ + struct mdp_histogram_data32) +#define MSMFB_GET_CCS_MATRIX32 _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs32) +#define MSMFB_SET_CCS_MATRIX32 _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs32) +#define MSMFB_OVERLAY_SET32 _IOWR(MSMFB_IOCTL_MAGIC, 135,\ + struct mdp_overlay32) + +#define MSMFB_OVERLAY_GET32 _IOR(MSMFB_IOCTL_MAGIC, 140,\ + struct mdp_overlay32) +#define MSMFB_OVERLAY_BLT32 _IOWR(MSMFB_IOCTL_MAGIC, 142,\ + struct msmfb_overlay_blt32) +#define MSMFB_HISTOGRAM_START32 _IOR(MSMFB_IOCTL_MAGIC, 144,\ + struct mdp_histogram_start_req32) + +#define MSMFB_OVERLAY_3D32 _IOWR(MSMFB_IOCTL_MAGIC, 147,\ + struct msmfb_overlay_3d32) + +#define MSMFB_MIXER_INFO32 _IOWR(MSMFB_IOCTL_MAGIC, 148,\ + struct msmfb_mixer_info_req32) +#define MSMFB_MDP_PP32 _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp32) +#define MSMFB_BUFFER_SYNC32 _IOW(MSMFB_IOCTL_MAGIC, 162, struct mdp_buf_sync32) +#define MSMFB_OVERLAY_PREPARE32 _IOWR(MSMFB_IOCTL_MAGIC, 169, \ + struct mdp_overlay_list32) +#define MSMFB_ATOMIC_COMMIT32 _IOWR(MDP_IOCTL_MAGIC, 128, compat_caddr_t) + +#define MSMFB_ASYNC_POSITION_UPDATE_32 _IOWR(MDP_IOCTL_MAGIC, 129, \ + struct mdp_position_update32) + +static int __copy_layer_pp_info_params(struct mdp_input_layer *layer, + struct mdp_input_layer32 *layer32); + +static unsigned int __do_compat_ioctl_nr(unsigned int cmd32) +{ + unsigned int cmd; + + switch (cmd32) { + case MSMFB_CURSOR32: + cmd = MSMFB_CURSOR; + break; + case MSMFB_SET_LUT32: + cmd = MSMFB_SET_LUT; + break; + case MSMFB_HISTOGRAM32: + cmd = MSMFB_HISTOGRAM; + break; + case MSMFB_GET_CCS_MATRIX32: + cmd = MSMFB_GET_CCS_MATRIX; + break; + case MSMFB_SET_CCS_MATRIX32: + cmd = MSMFB_SET_CCS_MATRIX; + break; + case MSMFB_OVERLAY_SET32: + cmd = MSMFB_OVERLAY_SET; + break; + case MSMFB_OVERLAY_GET32: + cmd = MSMFB_OVERLAY_GET; + break; + case MSMFB_OVERLAY_BLT32: + cmd = MSMFB_OVERLAY_BLT; + break; + case MSMFB_OVERLAY_3D32: + cmd = MSMFB_OVERLAY_3D; + break; + case MSMFB_MIXER_INFO32: + cmd = MSMFB_MIXER_INFO; + break; + case MSMFB_MDP_PP32: + cmd = MSMFB_MDP_PP; + break; + case MSMFB_BUFFER_SYNC32: + cmd = MSMFB_BUFFER_SYNC; + break; + case MSMFB_OVERLAY_PREPARE32: + cmd = MSMFB_OVERLAY_PREPARE; + break; + case MSMFB_ATOMIC_COMMIT32: + cmd = MSMFB_ATOMIC_COMMIT; + break; + case MSMFB_ASYNC_POSITION_UPDATE_32: + cmd = MSMFB_ASYNC_POSITION_UPDATE; + break; + default: + cmd = cmd32; + break; + } + + return cmd; +} + +static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit, + struct mdp_layer_commit32 *commit32) +{ + unsigned int destsize = sizeof(commit->commit_v1.reserved); + unsigned int srcsize = sizeof(commit32->commit_v1.reserved); + unsigned int count = (destsize <= srcsize ? destsize : srcsize); + + commit->version = commit32->version; + commit->commit_v1.flags = commit32->commit_v1.flags; + commit->commit_v1.input_layer_cnt = + commit32->commit_v1.input_layer_cnt; + commit->commit_v1.left_roi = commit32->commit_v1.left_roi; + commit->commit_v1.right_roi = commit32->commit_v1.right_roi; + commit->commit_v1.bl_level = commit32->commit_v1.bl_level; + memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved, + count); +} + +static struct mdp_input_layer32 *__create_layer_list32( + struct mdp_layer_commit32 *commit32, + u32 layer_count) +{ + u32 buffer_size32; + struct mdp_input_layer32 *layer_list32; + int ret; + + buffer_size32 = sizeof(struct mdp_input_layer32) * layer_count; + + layer_list32 = kmalloc(buffer_size32, GFP_KERNEL); + if (!layer_list32) { + layer_list32 = ERR_PTR(-ENOMEM); + goto end; + } + + ret = copy_from_user(layer_list32, + compat_ptr(commit32->commit_v1.input_layers), + sizeof(struct mdp_input_layer32) * layer_count); + if (ret) { + pr_err("layer list32 copy from user failed, ptr %pK\n", + compat_ptr(commit32->commit_v1.input_layers)); + kfree(layer_list32); + ret = -EFAULT; + layer_list32 = ERR_PTR(ret); + } + +end: + return layer_list32; +} + +static int __copy_scale_params(struct mdp_input_layer *layer, + struct mdp_input_layer32 *layer32) +{ + struct mdp_scale_data *scale; + int ret; + + if (!(layer->flags & MDP_LAYER_ENABLE_PIXEL_EXT)) + return 0; + + scale = kmalloc(sizeof(struct mdp_scale_data), GFP_KERNEL); + if (!scale) { + ret = -ENOMEM; + goto end; + } + + /* scale structure size is same for compat and 64bit version */ + ret = copy_from_user(scale, compat_ptr(layer32->scale), + sizeof(struct mdp_scale_data)); + if (ret) { + kfree(scale); + pr_err("scale param copy from user failed, ptr %pK\n", + compat_ptr(layer32->scale)); + ret = -EFAULT; + } else { + layer->scale = scale; + } +end: + return ret; +} + +static struct mdp_input_layer *__create_layer_list( + struct mdp_layer_commit *commit, + struct mdp_input_layer32 *layer_list32, + u32 layer_count) +{ + int i, ret = 0; + u32 buffer_size; + struct mdp_input_layer *layer, *layer_list; + struct mdp_input_layer32 *layer32; + + buffer_size = sizeof(struct mdp_input_layer) * layer_count; + + layer_list = kmalloc(buffer_size, GFP_KERNEL); + if (!layer_list) { + layer_list = ERR_PTR(-ENOMEM); + goto end; + } + + commit->commit_v1.input_layers = layer_list; + + for (i = 0; i < layer_count; i++) { + layer = &layer_list[i]; + layer32 = &layer_list32[i]; + + layer->flags = layer32->flags; + layer->pipe_ndx = layer32->pipe_ndx; + layer->horz_deci = layer32->horz_deci; + layer->vert_deci = layer32->vert_deci; + layer->z_order = layer32->z_order; + layer->transp_mask = layer32->transp_mask; + layer->bg_color = layer32->bg_color; + layer->blend_op = layer32->blend_op; + layer->alpha = layer32->alpha; + layer->color_space = layer32->color_space; + layer->src_rect = layer32->src_rect; + layer->dst_rect = layer32->dst_rect; + layer->buffer = layer32->buffer; + memcpy(&layer->reserved, &layer32->reserved, + sizeof(layer->reserved)); + + layer->scale = NULL; + ret = __copy_scale_params(layer, layer32); + if (ret) + break; + + layer->pp_info = NULL; + ret = __copy_layer_pp_info_params(layer, layer32); + if (ret) + break; + } + + if (ret) { + for (i--; i >= 0; i--) { + kfree(layer_list[i].scale); + mdss_mdp_free_layer_pp_info(&layer_list[i]); + } + kfree(layer_list); + layer_list = ERR_PTR(ret); + } + +end: + return layer_list; +} + +static int __copy_to_user_atomic_commit(struct mdp_layer_commit *commit, + struct mdp_layer_commit32 *commit32, + struct mdp_input_layer32 *layer_list32, + unsigned long argp, u32 layer_count) +{ + int i, ret; + struct mdp_input_layer *layer_list; + + layer_list = commit->commit_v1.input_layers; + + for (i = 0; i < layer_count; i++) + layer_list32[i].error_code = layer_list[i].error_code; + + ret = copy_to_user(compat_ptr(commit32->commit_v1.input_layers), + layer_list32, + sizeof(struct mdp_input_layer32) * layer_count); + if (ret) + goto end; + + ret = copy_to_user(compat_ptr(commit32->commit_v1.output_layer), + commit->commit_v1.output_layer, + sizeof(struct mdp_output_layer)); + if (ret) + goto end; + + commit32->commit_v1.release_fence = + commit->commit_v1.release_fence; + commit32->commit_v1.retire_fence = + commit->commit_v1.retire_fence; + + ret = copy_to_user((void __user *)argp, commit32, + sizeof(struct mdp_layer_commit32)); + +end: + return ret; +} + +static int __compat_atomic_commit(struct fb_info *info, unsigned int cmd, + unsigned long argp, struct file *file) +{ + int ret, i; + struct mdp_layer_commit commit; + struct mdp_layer_commit32 commit32; + u32 layer_count; + struct mdp_input_layer *layer_list = NULL; + struct mdp_input_layer32 *layer_list32 = NULL; + struct mdp_output_layer *output_layer = NULL; + struct mdp_frc_info *frc_info = NULL; + + /* copy top level memory from 32 bit structure to kernel memory */ + ret = copy_from_user(&commit32, (void __user *)argp, + sizeof(struct mdp_layer_commit32)); + if (ret) { + pr_err("%s:copy_from_user failed, ptr %pK\n", __func__, + (void __user *)argp); + ret = -EFAULT; + return ret; + } + + memset(&commit, 0, sizeof(struct mdp_layer_commit)); + __copy_atomic_commit_struct(&commit, &commit32); + + if (commit32.commit_v1.output_layer) { + int buffer_size = sizeof(struct mdp_output_layer); + + output_layer = kzalloc(buffer_size, GFP_KERNEL); + if (!output_layer) + return -ENOMEM; + + ret = copy_from_user(output_layer, + compat_ptr(commit32.commit_v1.output_layer), + buffer_size); + if (ret) { + pr_err("fail to copy output layer from user, ptr %pK\n", + compat_ptr(commit32.commit_v1.output_layer)); + ret = -EFAULT; + goto layer_list_err; + } + + commit.commit_v1.output_layer = output_layer; + } + + layer_count = commit32.commit_v1.input_layer_cnt; + if (layer_count > MAX_LAYER_COUNT) { + ret = -EINVAL; + goto layer_list_err; + } else if (layer_count) { + /* + * allocate memory for layer list in 32bit domain and copy it + * from user + */ + layer_list32 = __create_layer_list32(&commit32, layer_count); + if (IS_ERR_OR_NULL(layer_list32)) { + ret = PTR_ERR(layer_list32); + goto layer_list_err; + } + + /* + * allocate memory for layer list in kernel memory domain and + * copy layer info from 32bit structures to kernel memory + */ + layer_list = __create_layer_list(&commit, layer_list32, + layer_count); + if (IS_ERR_OR_NULL(layer_list)) { + ret = PTR_ERR(layer_list); + goto layer_list_err; + } + } + + if (commit32.commit_v1.frc_info) { + int buffer_size = sizeof(struct mdp_frc_info); + + frc_info = kzalloc(buffer_size, GFP_KERNEL); + if (!frc_info) { + ret = -ENOMEM; + goto frc_err; + } + + ret = copy_from_user(frc_info, + compat_ptr(commit32.commit_v1.frc_info), + buffer_size); + if (ret) { + pr_err("fail to copy frc info from user, ptr %p\n", + compat_ptr(commit32.commit_v1.frc_info)); + kfree(frc_info); + ret = -EFAULT; + goto frc_err; + } + + commit.commit_v1.frc_info = frc_info; + } + + ret = mdss_fb_atomic_commit(info, &commit, file); + if (ret) + pr_err("atomic commit failed ret:%d\n", ret); + + if (layer_count) + __copy_to_user_atomic_commit(&commit, &commit32, layer_list32, + argp, layer_count); + + for (i = 0; i < layer_count; i++) { + kfree(layer_list[i].scale); + mdss_mdp_free_layer_pp_info(&layer_list[i]); + } + + kfree(frc_info); +frc_err: + kfree(layer_list); +layer_list_err: + kfree(layer_list32); + kfree(output_layer); + return ret; +} + +static int __copy_to_user_async_position_update( + struct mdp_position_update *update_pos, + struct mdp_position_update32 *update_pos32, + unsigned long argp, u32 layer_cnt) +{ + int ret; + + ret = copy_to_user(update_pos32->input_layers, + update_pos->input_layers, + sizeof(struct mdp_async_layer) * layer_cnt); + if (ret) + goto end; + + ret = copy_to_user((void __user *) argp, update_pos32, + sizeof(struct mdp_position_update32)); + +end: + return ret; +} + +static struct mdp_async_layer *__create_async_layer_list( + struct mdp_position_update32 *update_pos32, u32 layer_cnt) +{ + u32 buffer_size; + struct mdp_async_layer *layer_list; + int ret; + + buffer_size = sizeof(struct mdp_async_layer) * layer_cnt; + + layer_list = kmalloc(buffer_size, GFP_KERNEL); + if (!layer_list) { + layer_list = ERR_PTR(-ENOMEM); + goto end; + } + + ret = copy_from_user(layer_list, + update_pos32->input_layers, buffer_size); + if (ret) { + pr_err("layer list32 copy from user failed\n"); + kfree(layer_list); + layer_list = ERR_PTR(ret); + } + +end: + return layer_list; +} + +static int __compat_async_position_update(struct fb_info *info, + unsigned int cmd, unsigned long argp) +{ + struct mdp_position_update update_pos; + struct mdp_position_update32 update_pos32; + struct mdp_async_layer *layer_list = NULL; + u32 layer_cnt, ret; + + /* copy top level memory from 32 bit structure to kernel memory */ + ret = copy_from_user(&update_pos32, (void __user *)argp, + sizeof(struct mdp_position_update32)); + if (ret) { + pr_err("%s:copy_from_user failed\n", __func__); + return ret; + } + + update_pos.input_layer_cnt = update_pos32.input_layer_cnt; + layer_cnt = update_pos32.input_layer_cnt; + if ((!layer_cnt) || (layer_cnt > MAX_LAYER_COUNT)) { + pr_err("invalid async layers :%d to update\n", layer_cnt); + return -EINVAL; + } + + layer_list = __create_async_layer_list(&update_pos32, + layer_cnt); + if (IS_ERR_OR_NULL(layer_list)) + return PTR_ERR(layer_list); + + update_pos.input_layers = layer_list; + + ret = mdss_fb_async_position_update(info, &update_pos); + if (ret) + pr_err("async position update failed ret:%d\n", ret); + + ret = __copy_to_user_async_position_update(&update_pos, &update_pos32, + argp, layer_cnt); + if (ret) + pr_err("copy to user of async update position failed\n"); + + kfree(layer_list); + return ret; +} + +static int mdss_fb_compat_buf_sync(struct fb_info *info, unsigned int cmd, + unsigned long arg, struct file *file) +{ + struct mdp_buf_sync32 __user *buf_sync32; + struct mdp_buf_sync __user *buf_sync; + u32 data; + int ret; + + buf_sync = compat_alloc_user_space(sizeof(*buf_sync)); + if (!buf_sync) { + pr_err("%s:%u: compat alloc error [%zu] bytes\n", + __func__, __LINE__, sizeof(*buf_sync)); + return -EINVAL; + } + buf_sync32 = compat_ptr(arg); + + if (copy_in_user(&buf_sync->flags, &buf_sync32->flags, + 3 * sizeof(u32))) + return -EFAULT; + + if (get_user(data, &buf_sync32->acq_fen_fd) || + put_user(compat_ptr(data), &buf_sync->acq_fen_fd) || + get_user(data, &buf_sync32->rel_fen_fd) || + put_user(compat_ptr(data), &buf_sync->rel_fen_fd) || + get_user(data, &buf_sync32->retire_fen_fd) || + put_user(compat_ptr(data), &buf_sync->retire_fen_fd)) + return -EFAULT; + + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) buf_sync, file); + if (ret) { + pr_err("%s: failed %d\n", __func__, ret); + return ret; + } + + if (copy_in_user(compat_ptr(buf_sync32->rel_fen_fd), + buf_sync->rel_fen_fd, + sizeof(int))) + return -EFAULT; + if (copy_in_user(compat_ptr(buf_sync32->retire_fen_fd), + buf_sync->retire_fen_fd, + sizeof(int))) { + if (buf_sync->flags & MDP_BUF_SYNC_FLAG_RETIRE_FENCE) + return -EFAULT; + pr_debug("%s: no retire fence fd for wb\n", + __func__); + } + + return ret; +} + +static int __from_user_fb_cmap(struct fb_cmap __user *cmap, + struct fb_cmap32 __user *cmap32) +{ + __u32 data; + + if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32))) + return -EFAULT; + + if (get_user(data, &cmap32->red) || + put_user(compat_ptr(data), &cmap->red) || + get_user(data, &cmap32->green) || + put_user(compat_ptr(data), &cmap->green) || + get_user(data, &cmap32->blue) || + put_user(compat_ptr(data), &cmap->blue) || + get_user(data, &cmap32->transp) || + put_user(compat_ptr(data), &cmap->transp)) + return -EFAULT; + + return 0; +} + +static int __to_user_fb_cmap(struct fb_cmap __user *cmap, + struct fb_cmap32 __user *cmap32) +{ + unsigned long data; + + if (copy_in_user(&cmap32->start, &cmap->start, 2 * sizeof(__u32))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &cmap->red) || + put_user((compat_caddr_t) data, &cmap32->red) || + get_user(data, (unsigned long *) &cmap->green) || + put_user((compat_caddr_t) data, &cmap32->green) || + get_user(data, (unsigned long *) &cmap->blue) || + put_user((compat_caddr_t) data, &cmap32->blue) || + get_user(data, (unsigned long *) &cmap->transp) || + put_user((compat_caddr_t) data, &cmap32->transp)) + return -EFAULT; + + return 0; +} + +static int __from_user_fb_image(struct fb_image __user *image, + struct fb_image32 __user *image32) +{ + __u32 data; + + if (copy_in_user(&image->dx, &image32->dx, 6 * sizeof(u32)) || + copy_in_user(&image->depth, &image32->depth, sizeof(u8))) + return -EFAULT; + + if (get_user(data, &image32->data) || + put_user(compat_ptr(data), &image->data)) + return -EFAULT; + + if (__from_user_fb_cmap(&image->cmap, &image32->cmap)) + return -EFAULT; + + return 0; +} + +static int mdss_fb_compat_cursor(struct fb_info *info, unsigned int cmd, + unsigned long arg, struct file *file) +{ + struct fb_cursor32 __user *cursor32; + struct fb_cursor __user *cursor; + __u32 data; + int ret; + + cursor = compat_alloc_user_space(sizeof(*cursor)); + if (!cursor) { + pr_err("%s:%u: compat alloc error [%zu] bytes\n", + __func__, __LINE__, sizeof(*cursor)); + return -EINVAL; + } + cursor32 = compat_ptr(arg); + + if (copy_in_user(&cursor->set, &cursor32->set, 3 * sizeof(u16))) + return -EFAULT; + + if (get_user(data, &cursor32->mask) || + put_user(compat_ptr(data), &cursor->mask)) + return -EFAULT; + + if (copy_in_user(&cursor->hot, &cursor32->hot, sizeof(struct fbcurpos))) + return -EFAULT; + + if (__from_user_fb_image(&cursor->image, &cursor32->image)) + return -EFAULT; + + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) cursor, file); + return ret; +} + +static int mdss_fb_compat_set_lut(struct fb_info *info, unsigned long arg, + struct file *file) +{ + struct fb_cmap_user __user *cmap; + struct fb_cmap32 __user *cmap32; + __u32 data; + int ret; + + cmap = compat_alloc_user_space(sizeof(*cmap)); + cmap32 = compat_ptr(arg); + + if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32))) + return -EFAULT; + + if (get_user(data, &cmap32->red) || + put_user(compat_ptr(data), &cmap->red) || + get_user(data, &cmap32->green) || + put_user(compat_ptr(data), &cmap->green) || + get_user(data, &cmap32->blue) || + put_user(compat_ptr(data), &cmap->blue) || + get_user(data, &cmap32->transp) || + put_user(compat_ptr(data), &cmap->transp)) + return -EFAULT; + + ret = mdss_fb_do_ioctl(info, MSMFB_SET_LUT, (unsigned long) cmap, file); + if (!ret) + pr_debug("%s: compat ioctl successful\n", __func__); + + return ret; +} + +static int __from_user_sharp_cfg( + struct mdp_sharp_cfg32 __user *sharp_cfg32, + struct mdp_sharp_cfg __user *sharp_cfg) +{ + if (copy_in_user(&sharp_cfg->flags, + &sharp_cfg32->flags, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg->strength, + &sharp_cfg32->strength, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg->edge_thr, + &sharp_cfg32->edge_thr, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg->smooth_thr, + &sharp_cfg32->smooth_thr, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg->noise_thr, + &sharp_cfg32->noise_thr, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_sharp_cfg( + struct mdp_sharp_cfg32 __user *sharp_cfg32, + struct mdp_sharp_cfg __user *sharp_cfg) +{ + if (copy_in_user(&sharp_cfg32->flags, + &sharp_cfg->flags, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg32->strength, + &sharp_cfg->strength, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg32->edge_thr, + &sharp_cfg->edge_thr, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg32->smooth_thr, + &sharp_cfg->smooth_thr, + sizeof(uint32_t)) || + copy_in_user(&sharp_cfg32->noise_thr, + &sharp_cfg->noise_thr, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_histogram_cfg( + struct mdp_histogram_cfg32 __user *hist_cfg32, + struct mdp_histogram_cfg __user *hist_cfg) +{ + if (copy_in_user(&hist_cfg->ops, + &hist_cfg32->ops, + sizeof(uint32_t)) || + copy_in_user(&hist_cfg->block, + &hist_cfg32->block, + sizeof(uint32_t)) || + copy_in_user(&hist_cfg->frame_cnt, + &hist_cfg32->frame_cnt, + sizeof(uint8_t)) || + copy_in_user(&hist_cfg->bit_mask, + &hist_cfg32->bit_mask, + sizeof(uint8_t)) || + copy_in_user(&hist_cfg->num_bins, + &hist_cfg32->num_bins, + sizeof(uint16_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_histogram_cfg( + struct mdp_histogram_cfg32 __user *hist_cfg32, + struct mdp_histogram_cfg __user *hist_cfg) +{ + if (copy_in_user(&hist_cfg32->ops, + &hist_cfg->ops, + sizeof(uint32_t)) || + copy_in_user(&hist_cfg32->block, + &hist_cfg->block, + sizeof(uint32_t)) || + copy_in_user(&hist_cfg32->frame_cnt, + &hist_cfg->frame_cnt, + sizeof(uint8_t)) || + copy_in_user(&hist_cfg32->bit_mask, + &hist_cfg->bit_mask, + sizeof(uint8_t)) || + copy_in_user(&hist_cfg32->num_bins, + &hist_cfg->num_bins, + sizeof(uint16_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_pcc_coeff( + struct mdp_pcc_coeff32 __user *pcc_coeff32, + struct mdp_pcc_coeff __user *pcc_coeff) +{ + if (copy_in_user(&pcc_coeff->c, + &pcc_coeff32->c, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->r, + &pcc_coeff32->r, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->g, + &pcc_coeff32->g, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->b, + &pcc_coeff32->b, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->rr, + &pcc_coeff32->rr, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->gg, + &pcc_coeff32->gg, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->bb, + &pcc_coeff32->bb, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->rg, + &pcc_coeff32->rg, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->gb, + &pcc_coeff32->gb, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->rb, + &pcc_coeff32->rb, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->rgb_0, + &pcc_coeff32->rgb_0, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff->rgb_1, + &pcc_coeff32->rgb_1, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_pcc_coeff( + struct mdp_pcc_coeff32 __user *pcc_coeff32, + struct mdp_pcc_coeff __user *pcc_coeff) +{ + if (copy_in_user(&pcc_coeff32->c, + &pcc_coeff->c, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->r, + &pcc_coeff->r, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->g, + &pcc_coeff->g, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->b, + &pcc_coeff->b, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->rr, + &pcc_coeff->rr, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->gg, + &pcc_coeff->gg, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->bb, + &pcc_coeff->bb, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->rg, + &pcc_coeff->rg, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->gb, + &pcc_coeff->gb, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->rb, + &pcc_coeff->rb, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->rgb_0, + &pcc_coeff->rgb_0, + sizeof(uint32_t)) || + copy_in_user(&pcc_coeff32->rgb_1, + &pcc_coeff->rgb_1, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_pcc_coeff_v17( + struct mdp_pcc_cfg_data32 __user *pcc_cfg32, + struct mdp_pcc_cfg_data __user *pcc_cfg) +{ + struct mdp_pcc_data_v1_7_32 pcc_cfg_payload32; + struct mdp_pcc_data_v1_7 pcc_cfg_payload; + + if (copy_from_user(&pcc_cfg_payload32, + compat_ptr(pcc_cfg32->cfg_payload), + sizeof(struct mdp_pcc_data_v1_7_32))) { + pr_err("failed to copy payload for pcc from user\n"); + return -EFAULT; + } + + memset(&pcc_cfg_payload, 0, sizeof(pcc_cfg_payload)); + pcc_cfg_payload.r.b = pcc_cfg_payload32.r.b; + pcc_cfg_payload.r.g = pcc_cfg_payload32.r.g; + pcc_cfg_payload.r.c = pcc_cfg_payload32.r.c; + pcc_cfg_payload.r.r = pcc_cfg_payload32.r.r; + pcc_cfg_payload.r.gb = pcc_cfg_payload32.r.gb; + pcc_cfg_payload.r.rb = pcc_cfg_payload32.r.rb; + pcc_cfg_payload.r.rg = pcc_cfg_payload32.r.rg; + pcc_cfg_payload.r.rgb = pcc_cfg_payload32.r.rgb; + + pcc_cfg_payload.g.b = pcc_cfg_payload32.g.b; + pcc_cfg_payload.g.g = pcc_cfg_payload32.g.g; + pcc_cfg_payload.g.c = pcc_cfg_payload32.g.c; + pcc_cfg_payload.g.r = pcc_cfg_payload32.g.r; + pcc_cfg_payload.g.gb = pcc_cfg_payload32.g.gb; + pcc_cfg_payload.g.rb = pcc_cfg_payload32.g.rb; + pcc_cfg_payload.g.rg = pcc_cfg_payload32.g.rg; + pcc_cfg_payload.g.rgb = pcc_cfg_payload32.g.rgb; + + pcc_cfg_payload.b.b = pcc_cfg_payload32.b.b; + pcc_cfg_payload.b.g = pcc_cfg_payload32.b.g; + pcc_cfg_payload.b.c = pcc_cfg_payload32.b.c; + pcc_cfg_payload.b.r = pcc_cfg_payload32.b.r; + pcc_cfg_payload.b.gb = pcc_cfg_payload32.b.gb; + pcc_cfg_payload.b.rb = pcc_cfg_payload32.b.rb; + pcc_cfg_payload.b.rg = pcc_cfg_payload32.b.rg; + pcc_cfg_payload.b.rgb = pcc_cfg_payload32.b.rgb; + + if (copy_to_user(pcc_cfg->cfg_payload, &pcc_cfg_payload, + sizeof(pcc_cfg_payload))) { + pr_err("failed to copy payload for pcc to user\n"); + return -EFAULT; + } + return 0; +} + +static int __from_user_pcc_cfg_data( + struct mdp_pcc_cfg_data32 __user *pcc_cfg32, + struct mdp_pcc_cfg_data __user *pcc_cfg) +{ + u32 version; + + if (copy_in_user(&pcc_cfg->block, + &pcc_cfg32->block, + sizeof(uint32_t)) || + copy_in_user(&pcc_cfg->ops, + &pcc_cfg32->ops, + sizeof(uint32_t)) || + copy_in_user(&pcc_cfg->version, + &pcc_cfg32->version, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_from_user(&version, &pcc_cfg32->version, sizeof(u32))) { + pr_err("failed to copy version for pcc\n"); + return -EFAULT; + } + + switch (version) { + case mdp_pcc_v1_7: + if (__from_user_pcc_coeff_v17(pcc_cfg32, pcc_cfg)) { + pr_err("failed to copy pcc v17 data\n"); + return -EFAULT; + } + break; + default: + pr_debug("pcc version %d not supported use legacy\n", version); + if (__from_user_pcc_coeff( + compat_ptr((uintptr_t)&pcc_cfg32->r), + &pcc_cfg->r) || + __from_user_pcc_coeff( + compat_ptr((uintptr_t)&pcc_cfg32->g), + &pcc_cfg->g) || + __from_user_pcc_coeff( + compat_ptr((uintptr_t)&pcc_cfg32->b), + &pcc_cfg->b)) + return -EFAULT; + break; + } + return 0; +} + +static int __to_user_pcc_coeff_v1_7( + struct mdp_pcc_cfg_data32 __user *pcc_cfg32, + struct mdp_pcc_cfg_data __user *pcc_cfg) +{ + struct mdp_pcc_data_v1_7_32 pcc_cfg_payload32; + struct mdp_pcc_data_v1_7 pcc_cfg_payload; + + memset(&pcc_cfg_payload32, 0, sizeof(pcc_cfg_payload32)); + if (copy_from_user(&pcc_cfg_payload, + pcc_cfg->cfg_payload, + sizeof(struct mdp_pcc_data_v1_7))) { + pr_err("failed to copy payload for pcc from user\n"); + return -EFAULT; + } + + pcc_cfg_payload32.r.b = pcc_cfg_payload.r.b; + pcc_cfg_payload32.r.g = pcc_cfg_payload.r.g; + pcc_cfg_payload32.r.c = pcc_cfg_payload.r.c; + pcc_cfg_payload32.r.r = pcc_cfg_payload.r.r; + pcc_cfg_payload32.r.gb = pcc_cfg_payload.r.gb; + pcc_cfg_payload32.r.rb = pcc_cfg_payload.r.rb; + pcc_cfg_payload32.r.rg = pcc_cfg_payload.r.rg; + pcc_cfg_payload32.r.rgb = pcc_cfg_payload.r.rgb; + + pcc_cfg_payload32.g.b = pcc_cfg_payload.g.b; + pcc_cfg_payload32.g.g = pcc_cfg_payload.g.g; + pcc_cfg_payload32.g.c = pcc_cfg_payload.g.c; + pcc_cfg_payload32.g.r = pcc_cfg_payload.g.r; + pcc_cfg_payload32.g.gb = pcc_cfg_payload.g.gb; + pcc_cfg_payload32.g.rb = pcc_cfg_payload.g.rb; + pcc_cfg_payload32.g.rg = pcc_cfg_payload.g.rg; + pcc_cfg_payload32.g.rgb = pcc_cfg_payload.g.rgb; + + pcc_cfg_payload32.b.b = pcc_cfg_payload.b.b; + pcc_cfg_payload32.b.g = pcc_cfg_payload.b.g; + pcc_cfg_payload32.b.c = pcc_cfg_payload.b.c; + pcc_cfg_payload32.b.r = pcc_cfg_payload.b.r; + pcc_cfg_payload32.b.gb = pcc_cfg_payload.b.gb; + pcc_cfg_payload32.b.rb = pcc_cfg_payload.b.rb; + pcc_cfg_payload32.b.rg = pcc_cfg_payload.b.rg; + pcc_cfg_payload32.b.rgb = pcc_cfg_payload.b.rgb; + + if (copy_to_user(compat_ptr(pcc_cfg32->cfg_payload), + &pcc_cfg_payload32, + sizeof(pcc_cfg_payload32))) { + pr_err("failed to copy payload for pcc to user\n"); + return -EFAULT; + } + + return 0; +} + + +static int __to_user_pcc_cfg_data( + struct mdp_pcc_cfg_data32 __user *pcc_cfg32, + struct mdp_pcc_cfg_data __user *pcc_cfg) +{ + u32 version; + u32 ops; + + if (copy_from_user(&ops, &pcc_cfg->ops, sizeof(u32))) { + pr_err("failed to copy op for pcc\n"); + return -EFAULT; + } + + if (!(ops & MDP_PP_OPS_READ)) { + pr_debug("Read op is not set. Skipping compat copyback\n"); + return 0; + } + + if (copy_from_user(&version, &pcc_cfg->version, sizeof(u32))) { + pr_err("failed to copy version for pcc\n"); + return -EFAULT; + } + + switch (version) { + case mdp_pcc_v1_7: + if (__to_user_pcc_coeff_v1_7(pcc_cfg32, pcc_cfg)) { + pr_err("failed to copy pcc v1_7 data\n"); + return -EFAULT; + } + break; + default: + pr_debug("version invalid, fallback to legacy\n"); + + if (__to_user_pcc_coeff( + compat_ptr((uintptr_t)&pcc_cfg32->r), + &pcc_cfg->r) || + __to_user_pcc_coeff( + compat_ptr((uintptr_t)&pcc_cfg32->g), + &pcc_cfg->g) || + __to_user_pcc_coeff( + compat_ptr((uintptr_t)&pcc_cfg32->b), + &pcc_cfg->b)) + return -EFAULT; + break; + } + + return 0; +} + +static int __from_user_csc_cfg( + struct mdp_csc_cfg32 __user *csc_data32, + struct mdp_csc_cfg __user *csc_data) +{ + if (copy_in_user(&csc_data->flags, + &csc_data32->flags, + sizeof(uint32_t)) || + copy_in_user(&csc_data->csc_mv[0], + &csc_data32->csc_mv[0], + 9 * sizeof(uint32_t)) || + copy_in_user(&csc_data->csc_pre_bv[0], + &csc_data32->csc_pre_bv[0], + 3 * sizeof(uint32_t)) || + copy_in_user(&csc_data->csc_post_bv[0], + &csc_data32->csc_post_bv[0], + 3 * sizeof(uint32_t)) || + copy_in_user(&csc_data->csc_pre_lv[0], + &csc_data32->csc_pre_lv[0], + 6 * sizeof(uint32_t)) || + copy_in_user(&csc_data->csc_post_lv[0], + &csc_data32->csc_post_lv[0], + 6 * sizeof(uint32_t))) + return -EFAULT; + + return 0; +} +static int __to_user_csc_cfg( + struct mdp_csc_cfg32 __user *csc_data32, + struct mdp_csc_cfg __user *csc_data) +{ + if (copy_in_user(&csc_data32->flags, + &csc_data->flags, + sizeof(uint32_t)) || + copy_in_user(&csc_data32->csc_mv[0], + &csc_data->csc_mv[0], + 9 * sizeof(uint32_t)) || + copy_in_user(&csc_data32->csc_pre_bv[0], + &csc_data->csc_pre_bv[0], + 3 * sizeof(uint32_t)) || + copy_in_user(&csc_data32->csc_post_bv[0], + &csc_data->csc_post_bv[0], + 3 * sizeof(uint32_t)) || + copy_in_user(&csc_data32->csc_pre_lv[0], + &csc_data->csc_pre_lv[0], + 6 * sizeof(uint32_t)) || + copy_in_user(&csc_data32->csc_post_lv[0], + &csc_data->csc_post_lv[0], + 6 * sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_csc_cfg_data( + struct mdp_csc_cfg_data32 __user *csc_cfg32, + struct mdp_csc_cfg_data __user *csc_cfg) +{ + if (copy_in_user(&csc_cfg->block, + &csc_cfg32->block, + sizeof(uint32_t))) + return -EFAULT; + + if (__from_user_csc_cfg( + compat_ptr((uintptr_t)&csc_cfg32->csc_data), + &csc_cfg->csc_data)) + return -EFAULT; + + return 0; +} + +static int __to_user_csc_cfg_data( + struct mdp_csc_cfg_data32 __user *csc_cfg32, + struct mdp_csc_cfg_data __user *csc_cfg) +{ + if (copy_in_user(&csc_cfg32->block, + &csc_cfg->block, + sizeof(uint32_t))) + return -EFAULT; + + if (__to_user_csc_cfg( + compat_ptr((uintptr_t)&csc_cfg32->csc_data), + &csc_cfg->csc_data)) + return -EFAULT; + + return 0; +} + +static int __from_user_igc_lut_data_v17( + struct mdp_igc_lut_data32 __user *igc_lut32, + struct mdp_igc_lut_data __user *igc_lut) +{ + struct mdp_igc_lut_data_v1_7_32 igc_cfg_payload_32; + struct mdp_igc_lut_data_v1_7 igc_cfg_payload; + + if (copy_from_user(&igc_cfg_payload_32, + compat_ptr(igc_lut32->cfg_payload), + sizeof(igc_cfg_payload_32))) { + pr_err("failed to copy payload from user for igc\n"); + return -EFAULT; + } + + memset(&igc_cfg_payload, 0, sizeof(igc_cfg_payload)); + igc_cfg_payload.c0_c1_data = compat_ptr(igc_cfg_payload_32.c0_c1_data); + igc_cfg_payload.c2_data = compat_ptr(igc_cfg_payload_32.c2_data); + igc_cfg_payload.len = igc_cfg_payload_32.len; + igc_cfg_payload.table_fmt = igc_cfg_payload_32.table_fmt; + if (copy_to_user(igc_lut->cfg_payload, &igc_cfg_payload, + sizeof(igc_cfg_payload))) { + pr_err("failed to copy payload to user for igc\n"); + return -EFAULT; + } + return 0; +} + +static int __from_user_igc_lut_data( + struct mdp_igc_lut_data32 __user *igc_lut32, + struct mdp_igc_lut_data __user *igc_lut) +{ + uint32_t data; + uint32_t version = mdp_igc_vmax; + int ret = 0; + + if (copy_in_user(&igc_lut->block, + &igc_lut32->block, + sizeof(uint32_t)) || + copy_in_user(&igc_lut->len, + &igc_lut32->len, + sizeof(uint32_t)) || + copy_in_user(&igc_lut->ops, + &igc_lut32->ops, + sizeof(uint32_t)) || + copy_in_user(&igc_lut->version, + &igc_lut32->version, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(version, &igc_lut32->version)) { + pr_err("failed to copy the version for IGC\n"); + return -EFAULT; + } + + switch (version) { + case mdp_igc_v1_7: + ret = __from_user_igc_lut_data_v17(igc_lut32, igc_lut); + if (ret) + pr_err("failed to copy payload for igc version %d ret %d\n", + version, ret); + break; + default: + pr_debug("version not supported fallback to legacy %d\n", + version); + if (get_user(data, &igc_lut32->c0_c1_data) || + put_user(compat_ptr(data), &igc_lut->c0_c1_data) || + get_user(data, &igc_lut32->c2_data) || + put_user(compat_ptr(data), &igc_lut->c2_data)) + return -EFAULT; + break; + } + return ret; +} + +static int __to_user_igc_lut_data( + struct mdp_igc_lut_data32 __user *igc_lut32, + struct mdp_igc_lut_data __user *igc_lut) +{ + unsigned long data; + + if (copy_in_user(&igc_lut32->block, + &igc_lut->block, + sizeof(uint32_t)) || + copy_in_user(&igc_lut32->len, + &igc_lut->len, + sizeof(uint32_t)) || + copy_in_user(&igc_lut32->ops, + &igc_lut->ops, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &igc_lut->c0_c1_data) || + put_user((compat_caddr_t) data, &igc_lut32->c0_c1_data) || + get_user(data, (unsigned long *) &igc_lut->c2_data) || + put_user((compat_caddr_t) data, &igc_lut32->c2_data)) + return -EFAULT; + + return 0; +} + +static int __from_user_ar_gc_lut_data( + struct mdp_ar_gc_lut_data32 __user *ar_gc_data32, + struct mdp_ar_gc_lut_data __user *ar_gc_data) +{ + if (copy_in_user(&ar_gc_data->x_start, + &ar_gc_data32->x_start, + sizeof(uint32_t)) || + copy_in_user(&ar_gc_data->slope, + &ar_gc_data32->slope, + sizeof(uint32_t)) || + copy_in_user(&ar_gc_data->offset, + &ar_gc_data32->offset, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_ar_gc_lut_data( + struct mdp_ar_gc_lut_data32 __user *ar_gc_data32, + struct mdp_ar_gc_lut_data __user *ar_gc_data) +{ + if (copy_in_user(&ar_gc_data32->x_start, + &ar_gc_data->x_start, + sizeof(uint32_t)) || + copy_in_user(&ar_gc_data32->slope, + &ar_gc_data->slope, + sizeof(uint32_t)) || + copy_in_user(&ar_gc_data32->offset, + &ar_gc_data->offset, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + + +static int __from_user_pgc_lut_data_v1_7( + struct mdp_pgc_lut_data32 __user *pgc_lut32, + struct mdp_pgc_lut_data __user *pgc_lut) +{ + struct mdp_pgc_lut_data_v1_7_32 pgc_cfg_payload_32; + struct mdp_pgc_lut_data_v1_7 pgc_cfg_payload; + + if (copy_from_user(&pgc_cfg_payload_32, + compat_ptr(pgc_lut32->cfg_payload), + sizeof(pgc_cfg_payload_32))) { + pr_err("failed to copy from user the pgc32 payload\n"); + return -EFAULT; + } + memset(&pgc_cfg_payload, 0, sizeof(pgc_cfg_payload)); + pgc_cfg_payload.c0_data = compat_ptr(pgc_cfg_payload_32.c0_data); + pgc_cfg_payload.c1_data = compat_ptr(pgc_cfg_payload_32.c1_data); + pgc_cfg_payload.c2_data = compat_ptr(pgc_cfg_payload_32.c2_data); + pgc_cfg_payload.len = pgc_cfg_payload_32.len; + if (copy_to_user(pgc_lut->cfg_payload, &pgc_cfg_payload, + sizeof(pgc_cfg_payload))) { + pr_err("failed to copy to user pgc payload\n"); + return -EFAULT; + } + return 0; +} + +static int __from_user_pgc_lut_data_legacy( + struct mdp_pgc_lut_data32 __user *pgc_lut32, + struct mdp_pgc_lut_data __user *pgc_lut) +{ + struct mdp_ar_gc_lut_data32 __user *r_data_temp32; + struct mdp_ar_gc_lut_data32 __user *g_data_temp32; + struct mdp_ar_gc_lut_data32 __user *b_data_temp32; + struct mdp_ar_gc_lut_data __user *r_data_temp; + struct mdp_ar_gc_lut_data __user *g_data_temp; + struct mdp_ar_gc_lut_data __user *b_data_temp; + uint8_t num_r_stages, num_g_stages, num_b_stages; + int i; + + if (copy_from_user(&num_r_stages, + &pgc_lut32->num_r_stages, + sizeof(uint8_t)) || + copy_from_user(&num_g_stages, + &pgc_lut32->num_g_stages, + sizeof(uint8_t)) || + copy_from_user(&num_b_stages, + &pgc_lut32->num_b_stages, + sizeof(uint8_t))) + return -EFAULT; + + if (num_r_stages > GC_LUT_SEGMENTS || num_b_stages > GC_LUT_SEGMENTS + || num_r_stages > GC_LUT_SEGMENTS || !num_r_stages || !num_b_stages + || !num_g_stages) { + pr_err("invalid number of stages r_stages %d b_stages %d g_stages %d\n", + num_r_stages, num_b_stages, num_r_stages); + return -EFAULT; + } + + r_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->r_data); + r_data_temp = pgc_lut->r_data; + + for (i = 0; i < num_r_stages; i++) { + if (__from_user_ar_gc_lut_data( + &r_data_temp32[i], + &r_data_temp[i])) + return -EFAULT; + } + + g_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->g_data); + g_data_temp = pgc_lut->g_data; + + for (i = 0; i < num_g_stages; i++) { + if (__from_user_ar_gc_lut_data( + &g_data_temp32[i], + &g_data_temp[i])) + return -EFAULT; + } + + b_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->b_data); + b_data_temp = pgc_lut->b_data; + + for (i = 0; i < num_b_stages; i++) { + if (__from_user_ar_gc_lut_data( + &b_data_temp32[i], + &b_data_temp[i])) + return -EFAULT; + } + return 0; +} + +static int __from_user_pgc_lut_data( + struct mdp_pgc_lut_data32 __user *pgc_lut32, + struct mdp_pgc_lut_data __user *pgc_lut) +{ + u32 version = mdp_pgc_vmax; + int ret = 0; + + if (copy_in_user(&pgc_lut->block, + &pgc_lut32->block, + sizeof(uint32_t)) || + copy_in_user(&pgc_lut->flags, + &pgc_lut32->flags, + sizeof(uint32_t)) || + copy_in_user(&pgc_lut->num_r_stages, + &pgc_lut32->num_r_stages, + sizeof(uint8_t)) || + copy_in_user(&pgc_lut->num_g_stages, + &pgc_lut32->num_g_stages, + sizeof(uint8_t)) || + copy_in_user(&pgc_lut->num_b_stages, + &pgc_lut32->num_b_stages, + sizeof(uint8_t)) || + copy_in_user(&pgc_lut->version, + &pgc_lut32->version, + sizeof(uint32_t))) + return -EFAULT; + if (copy_from_user(&version, &pgc_lut32->version, sizeof(u32))) { + pr_err("version copying failed\n"); + return -EFAULT; + } + switch (version) { + case mdp_pgc_v1_7: + ret = __from_user_pgc_lut_data_v1_7(pgc_lut32, pgc_lut); + if (ret) + pr_err("failed to copy pgc v17\n"); + break; + default: + pr_debug("version %d not supported fallback to legacy\n", + version); + ret = __from_user_pgc_lut_data_legacy(pgc_lut32, pgc_lut); + if (ret) + pr_err("copy from user pgc lut legacy failed ret %d\n", + ret); + break; + } + return ret; +} + +static int __to_user_pgc_lut_data( + struct mdp_pgc_lut_data32 __user *pgc_lut32, + struct mdp_pgc_lut_data __user *pgc_lut) +{ + struct mdp_ar_gc_lut_data32 __user *r_data_temp32; + struct mdp_ar_gc_lut_data32 __user *g_data_temp32; + struct mdp_ar_gc_lut_data32 __user *b_data_temp32; + struct mdp_ar_gc_lut_data __user *r_data_temp; + struct mdp_ar_gc_lut_data __user *g_data_temp; + struct mdp_ar_gc_lut_data __user *b_data_temp; + uint8_t num_r_stages, num_g_stages, num_b_stages; + int i; + + if (copy_in_user(&pgc_lut32->block, + &pgc_lut->block, + sizeof(uint32_t)) || + copy_in_user(&pgc_lut32->flags, + &pgc_lut->flags, + sizeof(uint32_t)) || + copy_in_user(&pgc_lut32->num_r_stages, + &pgc_lut->num_r_stages, + sizeof(uint8_t)) || + copy_in_user(&pgc_lut32->num_g_stages, + &pgc_lut->num_g_stages, + sizeof(uint8_t)) || + copy_in_user(&pgc_lut32->num_b_stages, + &pgc_lut->num_b_stages, + sizeof(uint8_t))) + return -EFAULT; + + if (copy_from_user(&num_r_stages, + &pgc_lut->num_r_stages, + sizeof(uint8_t)) || + copy_from_user(&num_g_stages, + &pgc_lut->num_g_stages, + sizeof(uint8_t)) || + copy_from_user(&num_b_stages, + &pgc_lut->num_b_stages, + sizeof(uint8_t))) + return -EFAULT; + + r_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->r_data); + r_data_temp = pgc_lut->r_data; + for (i = 0; i < num_r_stages; i++) { + if (__to_user_ar_gc_lut_data( + &r_data_temp32[i], + &r_data_temp[i])) + return -EFAULT; + } + + g_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->g_data); + g_data_temp = pgc_lut->g_data; + for (i = 0; i < num_g_stages; i++) { + if (__to_user_ar_gc_lut_data( + &g_data_temp32[i], + &g_data_temp[i])) + return -EFAULT; + } + + b_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->b_data); + b_data_temp = pgc_lut->b_data; + for (i = 0; i < num_b_stages; i++) { + if (__to_user_ar_gc_lut_data( + &b_data_temp32[i], + &b_data_temp[i])) + return -EFAULT; + } + + return 0; +} + +static int __from_user_hist_lut_data_v1_7( + struct mdp_hist_lut_data32 __user *hist_lut32, + struct mdp_hist_lut_data __user *hist_lut) +{ + struct mdp_hist_lut_data_v1_7_32 hist_lut_cfg_payload32; + struct mdp_hist_lut_data_v1_7 hist_lut_cfg_payload; + + if (copy_from_user(&hist_lut_cfg_payload32, + compat_ptr(hist_lut32->cfg_payload), + sizeof(hist_lut_cfg_payload32))) { + pr_err("failed to copy the Hist Lut payload from userspace\n"); + return -EFAULT; + } + + memset(&hist_lut_cfg_payload, 0, sizeof(hist_lut_cfg_payload)); + hist_lut_cfg_payload.len = hist_lut_cfg_payload32.len; + hist_lut_cfg_payload.data = compat_ptr(hist_lut_cfg_payload32.data); + + if (copy_to_user(hist_lut->cfg_payload, + &hist_lut_cfg_payload, + sizeof(hist_lut_cfg_payload))) { + pr_err("Failed to copy to user hist lut cfg payload\n"); + return -EFAULT; + } + + return 0; +} + +static int __from_user_hist_lut_data( + struct mdp_hist_lut_data32 __user *hist_lut32, + struct mdp_hist_lut_data __user *hist_lut) +{ + uint32_t version = 0; + uint32_t data; + + if (copy_in_user(&hist_lut->block, + &hist_lut32->block, + sizeof(uint32_t)) || + copy_in_user(&hist_lut->version, + &hist_lut32->version, + sizeof(uint32_t)) || + copy_in_user(&hist_lut->hist_lut_first, + &hist_lut32->hist_lut_first, + sizeof(uint32_t)) || + copy_in_user(&hist_lut->ops, + &hist_lut32->ops, + sizeof(uint32_t)) || + copy_in_user(&hist_lut->len, + &hist_lut32->len, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_from_user(&version, + &hist_lut32->version, + sizeof(uint32_t))) { + pr_err("failed to copy the version info\n"); + return -EFAULT; + } + + switch (version) { + case mdp_hist_lut_v1_7: + if (__from_user_hist_lut_data_v1_7(hist_lut32, hist_lut)) { + pr_err("failed to get hist lut data for version %d\n", + version); + return -EFAULT; + } + break; + default: + pr_debug("version invalid, fallback to legacy\n"); + if (get_user(data, &hist_lut32->data) || + put_user(compat_ptr(data), &hist_lut->data)) + return -EFAULT; + break; + } + + return 0; +} + +static int __to_user_hist_lut_data( + struct mdp_hist_lut_data32 __user *hist_lut32, + struct mdp_hist_lut_data __user *hist_lut) +{ + unsigned long data; + + if (copy_in_user(&hist_lut32->block, + &hist_lut->block, + sizeof(uint32_t)) || + copy_in_user(&hist_lut32->ops, + &hist_lut->ops, + sizeof(uint32_t)) || + copy_in_user(&hist_lut32->len, + &hist_lut->len, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &hist_lut->data) || + put_user((compat_caddr_t) data, &hist_lut32->data)) + return -EFAULT; + + return 0; +} + +static int __from_user_rgb_lut_data( + struct mdp_rgb_lut_data32 __user *rgb_lut32, + struct mdp_rgb_lut_data __user *rgb_lut) +{ + if (copy_in_user(&rgb_lut->flags, &rgb_lut32->flags, + sizeof(uint32_t)) || + copy_in_user(&rgb_lut->lut_type, &rgb_lut32->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + return __from_user_fb_cmap(&rgb_lut->cmap, &rgb_lut32->cmap); +} + +static int __to_user_rgb_lut_data( + struct mdp_rgb_lut_data32 __user *rgb_lut32, + struct mdp_rgb_lut_data __user *rgb_lut) +{ + if (copy_in_user(&rgb_lut32->flags, &rgb_lut->flags, + sizeof(uint32_t)) || + copy_in_user(&rgb_lut32->lut_type, &rgb_lut->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + return __to_user_fb_cmap(&rgb_lut->cmap, &rgb_lut32->cmap); +} + +static int __from_user_lut_cfg_data( + struct mdp_lut_cfg_data32 __user *lut_cfg32, + struct mdp_lut_cfg_data __user *lut_cfg) +{ + uint32_t lut_type; + int ret = 0; + + if (copy_from_user(&lut_type, &lut_cfg32->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_in_user(&lut_cfg->lut_type, + &lut_cfg32->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + switch (lut_type) { + case mdp_lut_igc: + ret = __from_user_igc_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.igc_lut_data), + &lut_cfg->data.igc_lut_data); + break; + case mdp_lut_pgc: + ret = __from_user_pgc_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.pgc_lut_data), + &lut_cfg->data.pgc_lut_data); + break; + case mdp_lut_hist: + ret = __from_user_hist_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.hist_lut_data), + &lut_cfg->data.hist_lut_data); + break; + case mdp_lut_rgb: + ret = __from_user_rgb_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.rgb_lut_data), + &lut_cfg->data.rgb_lut_data); + break; + default: + break; + } + + return ret; +} + +static int __to_user_lut_cfg_data( + struct mdp_lut_cfg_data32 __user *lut_cfg32, + struct mdp_lut_cfg_data __user *lut_cfg) +{ + uint32_t lut_type; + int ret = 0; + + if (copy_from_user(&lut_type, &lut_cfg->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_in_user(&lut_cfg32->lut_type, + &lut_cfg->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + switch (lut_type) { + case mdp_lut_igc: + ret = __to_user_igc_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.igc_lut_data), + &lut_cfg->data.igc_lut_data); + break; + case mdp_lut_pgc: + ret = __to_user_pgc_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.pgc_lut_data), + &lut_cfg->data.pgc_lut_data); + break; + case mdp_lut_hist: + ret = __to_user_hist_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.hist_lut_data), + &lut_cfg->data.hist_lut_data); + break; + case mdp_lut_rgb: + ret = __to_user_rgb_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.rgb_lut_data), + &lut_cfg->data.rgb_lut_data); + break; + default: + break; + } + + return ret; +} + +static int __from_user_qseed_cfg( + struct mdp_qseed_cfg32 __user *qseed_data32, + struct mdp_qseed_cfg __user *qseed_data) +{ + uint32_t data; + + if (copy_in_user(&qseed_data->table_num, + &qseed_data32->table_num, + sizeof(uint32_t)) || + copy_in_user(&qseed_data->ops, + &qseed_data32->ops, + sizeof(uint32_t)) || + copy_in_user(&qseed_data->len, + &qseed_data32->len, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, &qseed_data32->data) || + put_user(compat_ptr(data), &qseed_data->data)) + return -EFAULT; + + return 0; +} + +static int __to_user_qseed_cfg( + struct mdp_qseed_cfg32 __user *qseed_data32, + struct mdp_qseed_cfg __user *qseed_data) +{ + unsigned long data; + + if (copy_in_user(&qseed_data32->table_num, + &qseed_data->table_num, + sizeof(uint32_t)) || + copy_in_user(&qseed_data32->ops, + &qseed_data->ops, + sizeof(uint32_t)) || + copy_in_user(&qseed_data32->len, + &qseed_data->len, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &qseed_data->data) || + put_user((compat_caddr_t) data, &qseed_data32->data)) + return -EFAULT; + + return 0; +} + +static int __from_user_qseed_cfg_data( + struct mdp_qseed_cfg_data32 __user *qseed_cfg32, + struct mdp_qseed_cfg_data __user *qseed_cfg) +{ + if (copy_in_user(&qseed_cfg->block, + &qseed_cfg32->block, + sizeof(uint32_t))) + return -EFAULT; + + if (__from_user_qseed_cfg( + compat_ptr((uintptr_t)&qseed_cfg32->qseed_data), + &qseed_cfg->qseed_data)) + return -EFAULT; + + return 0; +} + +static int __to_user_qseed_cfg_data( + struct mdp_qseed_cfg_data32 __user *qseed_cfg32, + struct mdp_qseed_cfg_data __user *qseed_cfg) +{ + if (copy_in_user(&qseed_cfg32->block, + &qseed_cfg->block, + sizeof(uint32_t))) + return -EFAULT; + + if (__to_user_qseed_cfg( + compat_ptr((uintptr_t)&qseed_cfg32->qseed_data), + &qseed_cfg->qseed_data)) + return -EFAULT; + + return 0; +} + +static int __from_user_bl_scale_data( + struct mdp_bl_scale_data32 __user *bl_scale32, + struct mdp_bl_scale_data __user *bl_scale) +{ + if (copy_in_user(&bl_scale->min_lvl, + &bl_scale32->min_lvl, + sizeof(uint32_t)) || + copy_in_user(&bl_scale->scale, + &bl_scale32->scale, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_pa_cfg( + struct mdp_pa_cfg32 __user *pa_data32, + struct mdp_pa_cfg __user *pa_data) +{ + if (copy_in_user(&pa_data->flags, + &pa_data32->flags, + sizeof(uint32_t)) || + copy_in_user(&pa_data->hue_adj, + &pa_data32->hue_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_data->sat_adj, + &pa_data32->sat_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_data->val_adj, + &pa_data32->val_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_data->cont_adj, + &pa_data32->cont_adj, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_pa_cfg( + struct mdp_pa_cfg32 __user *pa_data32, + struct mdp_pa_cfg __user *pa_data) +{ + if (copy_in_user(&pa_data32->flags, + &pa_data->flags, + sizeof(uint32_t)) || + copy_in_user(&pa_data32->hue_adj, + &pa_data->hue_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_data32->sat_adj, + &pa_data->sat_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_data32->val_adj, + &pa_data->val_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_data32->cont_adj, + &pa_data->cont_adj, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_pa_cfg_data( + struct mdp_pa_cfg_data32 __user *pa_cfg32, + struct mdp_pa_cfg_data __user *pa_cfg) +{ + if (copy_in_user(&pa_cfg->block, + &pa_cfg32->block, + sizeof(uint32_t))) + return -EFAULT; + if (__from_user_pa_cfg( + compat_ptr((uintptr_t)&pa_cfg32->pa_data), + &pa_cfg->pa_data)) + return -EFAULT; + + return 0; +} + +static int __to_user_pa_cfg_data( + struct mdp_pa_cfg_data32 __user *pa_cfg32, + struct mdp_pa_cfg_data __user *pa_cfg) +{ + if (copy_in_user(&pa_cfg32->block, + &pa_cfg->block, + sizeof(uint32_t))) + return -EFAULT; + if (__to_user_pa_cfg( + compat_ptr((uintptr_t)&pa_cfg32->pa_data), + &pa_cfg->pa_data)) + return -EFAULT; + + return 0; +} + +static int __from_user_mem_col_cfg( + struct mdp_pa_mem_col_cfg32 __user *mem_col_cfg32, + struct mdp_pa_mem_col_cfg __user *mem_col_cfg) +{ + if (copy_in_user(&mem_col_cfg->color_adjust_p0, + &mem_col_cfg32->color_adjust_p0, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg->color_adjust_p1, + &mem_col_cfg32->color_adjust_p1, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg->hue_region, + &mem_col_cfg32->hue_region, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg->sat_region, + &mem_col_cfg32->sat_region, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg->val_region, + &mem_col_cfg32->val_region, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_mem_col_cfg( + struct mdp_pa_mem_col_cfg32 __user *mem_col_cfg32, + struct mdp_pa_mem_col_cfg __user *mem_col_cfg) +{ + if (copy_in_user(&mem_col_cfg32->color_adjust_p0, + &mem_col_cfg->color_adjust_p0, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg32->color_adjust_p1, + &mem_col_cfg->color_adjust_p1, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg32->hue_region, + &mem_col_cfg->hue_region, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg32->sat_region, + &mem_col_cfg->sat_region, + sizeof(uint32_t)) || + copy_in_user(&mem_col_cfg32->val_region, + &mem_col_cfg->val_region, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_pa_v2_data( + struct mdp_pa_v2_data32 __user *pa_v2_data32, + struct mdp_pa_v2_data __user *pa_v2_data) +{ + uint32_t data; + + if (copy_in_user(&pa_v2_data->flags, + &pa_v2_data32->flags, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data->global_hue_adj, + &pa_v2_data32->global_hue_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data->global_sat_adj, + &pa_v2_data32->global_sat_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data->global_val_adj, + &pa_v2_data32->global_val_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data->global_cont_adj, + &pa_v2_data32->global_cont_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data->six_zone_thresh, + &pa_v2_data32->six_zone_thresh, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data->six_zone_len, + &pa_v2_data32->six_zone_len, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, &pa_v2_data32->six_zone_curve_p0) || + put_user(compat_ptr(data), &pa_v2_data->six_zone_curve_p0) || + get_user(data, &pa_v2_data32->six_zone_curve_p1) || + put_user(compat_ptr(data), &pa_v2_data->six_zone_curve_p1)) + return -EFAULT; + + if (__from_user_mem_col_cfg( + compat_ptr((uintptr_t)&pa_v2_data32->skin_cfg), + &pa_v2_data->skin_cfg) || + __from_user_mem_col_cfg( + compat_ptr((uintptr_t)&pa_v2_data32->sky_cfg), + &pa_v2_data->sky_cfg) || + __from_user_mem_col_cfg( + compat_ptr((uintptr_t)&pa_v2_data32->fol_cfg), + &pa_v2_data->fol_cfg)) + return -EFAULT; + + return 0; +} + +static int __to_user_pa_v2_data( + struct mdp_pa_v2_data32 __user *pa_v2_data32, + struct mdp_pa_v2_data __user *pa_v2_data) +{ + unsigned long data; + + if (copy_in_user(&pa_v2_data32->flags, + &pa_v2_data->flags, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data32->global_hue_adj, + &pa_v2_data->global_hue_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data32->global_sat_adj, + &pa_v2_data->global_sat_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data32->global_val_adj, + &pa_v2_data->global_val_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data32->global_cont_adj, + &pa_v2_data->global_cont_adj, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data32->six_zone_thresh, + &pa_v2_data->six_zone_thresh, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_data32->six_zone_len, + &pa_v2_data->six_zone_len, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &pa_v2_data->six_zone_curve_p0) || + put_user((compat_caddr_t) data, &pa_v2_data32->six_zone_curve_p0) || + get_user(data, (unsigned long *) &pa_v2_data->six_zone_curve_p1) || + put_user((compat_caddr_t) data, &pa_v2_data32->six_zone_curve_p1)) + return -EFAULT; + + if (__to_user_mem_col_cfg( + compat_ptr((uintptr_t)&pa_v2_data32->skin_cfg), + &pa_v2_data->skin_cfg) || + __to_user_mem_col_cfg( + compat_ptr((uintptr_t)&pa_v2_data32->sky_cfg), + &pa_v2_data->sky_cfg) || + __to_user_mem_col_cfg( + compat_ptr((uintptr_t)&pa_v2_data32->fol_cfg), + &pa_v2_data->fol_cfg)) + return -EFAULT; + + return 0; +} + +static inline void __from_user_pa_mem_col_data_v1_7( + struct mdp_pa_mem_col_data_v1_7_32 *mem_col_data32, + struct mdp_pa_mem_col_data_v1_7 *mem_col_data) +{ + mem_col_data->color_adjust_p0 = mem_col_data32->color_adjust_p0; + mem_col_data->color_adjust_p1 = mem_col_data32->color_adjust_p1; + mem_col_data->color_adjust_p2 = mem_col_data32->color_adjust_p2; + mem_col_data->blend_gain = mem_col_data32->blend_gain; + mem_col_data->sat_hold = mem_col_data32->sat_hold; + mem_col_data->val_hold = mem_col_data32->val_hold; + mem_col_data->hue_region = mem_col_data32->hue_region; + mem_col_data->sat_region = mem_col_data32->sat_region; + mem_col_data->val_region = mem_col_data32->val_region; +} + + +static int __from_user_pa_data_v1_7( + struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, + struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) +{ + struct mdp_pa_data_v1_7_32 pa_cfg_payload32; + struct mdp_pa_data_v1_7 pa_cfg_payload; + + if (copy_from_user(&pa_cfg_payload32, + compat_ptr(pa_v2_cfg32->cfg_payload), + sizeof(pa_cfg_payload32))) { + pr_err("failed to copy the PA payload from userspace\n"); + return -EFAULT; + } + + memset(&pa_cfg_payload, 0, sizeof(pa_cfg_payload)); + pa_cfg_payload.mode = pa_cfg_payload32.mode; + pa_cfg_payload.global_hue_adj = pa_cfg_payload32.global_hue_adj; + pa_cfg_payload.global_sat_adj = pa_cfg_payload32.global_sat_adj; + pa_cfg_payload.global_val_adj = pa_cfg_payload32.global_val_adj; + pa_cfg_payload.global_cont_adj = pa_cfg_payload32.global_cont_adj; + + __from_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.skin_cfg, + &pa_cfg_payload.skin_cfg); + __from_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.sky_cfg, + &pa_cfg_payload.sky_cfg); + __from_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.fol_cfg, + &pa_cfg_payload.fol_cfg); + + pa_cfg_payload.six_zone_thresh = pa_cfg_payload32.six_zone_thresh; + pa_cfg_payload.six_zone_adj_p0 = pa_cfg_payload32.six_zone_adj_p0; + pa_cfg_payload.six_zone_adj_p1 = pa_cfg_payload32.six_zone_adj_p1; + pa_cfg_payload.six_zone_sat_hold = pa_cfg_payload32.six_zone_sat_hold; + pa_cfg_payload.six_zone_val_hold = pa_cfg_payload32.six_zone_val_hold; + pa_cfg_payload.six_zone_len = pa_cfg_payload32.six_zone_len; + + pa_cfg_payload.six_zone_curve_p0 = + compat_ptr(pa_cfg_payload32.six_zone_curve_p0); + pa_cfg_payload.six_zone_curve_p1 = + compat_ptr(pa_cfg_payload32.six_zone_curve_p1); + + if (copy_to_user(pa_v2_cfg->cfg_payload, &pa_cfg_payload, + sizeof(pa_cfg_payload))) { + pr_err("Failed to copy to user pa cfg payload\n"); + return -EFAULT; + } + + return 0; +} + +static int __from_user_pa_v2_cfg_data( + struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, + struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) +{ + uint32_t version; + + if (copy_in_user(&pa_v2_cfg->block, + &pa_v2_cfg32->block, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_cfg->version, + &pa_v2_cfg32->version, + sizeof(uint32_t)) || + copy_in_user(&pa_v2_cfg->flags, + &pa_v2_cfg32->flags, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_from_user(&version, + &pa_v2_cfg32->version, + sizeof(uint32_t))) { + pr_err("failed to copy the version info\n"); + return -EFAULT; + } + + switch (version) { + case mdp_pa_v1_7: + if (__from_user_pa_data_v1_7(pa_v2_cfg32, pa_v2_cfg)) { + pr_err("failed to get pa data for version %d\n", + version); + return -EFAULT; + } + break; + default: + pr_debug("version invalid, fallback to legacy\n"); + if (__from_user_pa_v2_data( + compat_ptr((uintptr_t)&pa_v2_cfg32->pa_v2_data), + &pa_v2_cfg->pa_v2_data)) + return -EFAULT; + break; + } + + return 0; +} + +static inline void __to_user_pa_mem_col_data_v1_7( + struct mdp_pa_mem_col_data_v1_7_32 *mem_col_data32, + struct mdp_pa_mem_col_data_v1_7 *mem_col_data) +{ + mem_col_data32->color_adjust_p0 = mem_col_data->color_adjust_p0; + mem_col_data32->color_adjust_p1 = mem_col_data->color_adjust_p1; + mem_col_data32->color_adjust_p2 = mem_col_data->color_adjust_p2; + mem_col_data32->blend_gain = mem_col_data->blend_gain; + mem_col_data32->sat_hold = mem_col_data->sat_hold; + mem_col_data32->val_hold = mem_col_data->val_hold; + mem_col_data32->hue_region = mem_col_data->hue_region; + mem_col_data32->sat_region = mem_col_data->sat_region; + mem_col_data32->val_region = mem_col_data->val_region; +} + +static int __to_user_pa_data_v1_7( + struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, + struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) +{ + struct mdp_pa_data_v1_7_32 pa_cfg_payload32; + struct mdp_pa_data_v1_7 pa_cfg_payload; + + memset(&pa_cfg_payload32, 0, sizeof(pa_cfg_payload32)); + if (copy_from_user(&pa_cfg_payload, + pa_v2_cfg->cfg_payload, + sizeof(pa_cfg_payload))) { + pr_err("failed to copy the PA payload from userspace\n"); + return -EFAULT; + } + + pa_cfg_payload32.mode = pa_cfg_payload.mode; + pa_cfg_payload32.global_hue_adj = pa_cfg_payload.global_hue_adj; + pa_cfg_payload32.global_sat_adj = pa_cfg_payload.global_sat_adj; + pa_cfg_payload32.global_val_adj = pa_cfg_payload.global_val_adj; + pa_cfg_payload32.global_cont_adj = pa_cfg_payload.global_cont_adj; + + __to_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.skin_cfg, + &pa_cfg_payload.skin_cfg); + __to_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.sky_cfg, + &pa_cfg_payload.sky_cfg); + __to_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.fol_cfg, + &pa_cfg_payload.fol_cfg); + + pa_cfg_payload32.six_zone_thresh = pa_cfg_payload.six_zone_thresh; + pa_cfg_payload32.six_zone_adj_p0 = pa_cfg_payload.six_zone_adj_p0; + pa_cfg_payload32.six_zone_adj_p1 = pa_cfg_payload.six_zone_adj_p1; + pa_cfg_payload32.six_zone_sat_hold = pa_cfg_payload.six_zone_sat_hold; + pa_cfg_payload32.six_zone_val_hold = pa_cfg_payload.six_zone_val_hold; + pa_cfg_payload32.six_zone_len = pa_cfg_payload.six_zone_len; + + if (copy_to_user(compat_ptr(pa_v2_cfg32->cfg_payload), + &pa_cfg_payload32, + sizeof(pa_cfg_payload32))) { + pr_err("Failed to copy to user pa cfg payload\n"); + return -EFAULT; + } + + return 0; +} + +static int __to_user_pa_v2_cfg_data( + struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, + struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) +{ + uint32_t version = 0; + uint32_t flags = 0; + + if (copy_from_user(&version, + &pa_v2_cfg32->version, + sizeof(uint32_t))) + return -EFAULT; + + switch (version) { + case mdp_pa_v1_7: + if (copy_from_user(&flags, + &pa_v2_cfg32->flags, + sizeof(uint32_t))) { + pr_err("failed to get PA v1_7 flags\n"); + return -EFAULT; + } + + if (!(flags & MDP_PP_OPS_READ)) { + pr_debug("Read op not set. Skipping compat copyback\n"); + return 0; + } + + if (__to_user_pa_data_v1_7(pa_v2_cfg32, pa_v2_cfg)) { + pr_err("failed to set pa data for version %d\n", + version); + return -EFAULT; + } + break; + default: + pr_debug("version invalid, fallback to legacy\n"); + + if (copy_from_user(&flags, + &pa_v2_cfg32->pa_v2_data.flags, + sizeof(uint32_t))) { + pr_err("failed to get PAv2 flags\n"); + return -EFAULT; + } + + if (!(flags & MDP_PP_OPS_READ)) { + pr_debug("Read op not set. Skipping compat copyback\n"); + return 0; + } + + if (__to_user_pa_v2_data( + compat_ptr((uintptr_t)&pa_v2_cfg32->pa_v2_data), + &pa_v2_cfg->pa_v2_data)) + return -EFAULT; + break; + } + + return 0; +} + +static int __from_user_dither_cfg_data( + struct mdp_dither_cfg_data32 __user *dither_cfg32, + struct mdp_dither_cfg_data __user *dither_cfg) +{ + if (copy_in_user(&dither_cfg->block, + &dither_cfg32->block, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg->flags, + &dither_cfg32->flags, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg->g_y_depth, + &dither_cfg32->g_y_depth, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg->r_cr_depth, + &dither_cfg32->r_cr_depth, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg->b_cb_depth, + &dither_cfg32->b_cb_depth, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_dither_cfg_data( + struct mdp_dither_cfg_data32 __user *dither_cfg32, + struct mdp_dither_cfg_data __user *dither_cfg) +{ + if (copy_in_user(&dither_cfg32->block, + &dither_cfg->block, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg32->flags, + &dither_cfg->flags, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg32->g_y_depth, + &dither_cfg->g_y_depth, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg32->r_cr_depth, + &dither_cfg->r_cr_depth, + sizeof(uint32_t)) || + copy_in_user(&dither_cfg32->b_cb_depth, + &dither_cfg->b_cb_depth, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_gamut_cfg_data_v17( + struct mdp_gamut_cfg_data32 __user *gamut_cfg32, + struct mdp_gamut_cfg_data __user *gamut_cfg) +{ + struct mdp_gamut_data_v1_7 gamut_cfg_payload; + struct mdp_gamut_data_v1_7_32 gamut_cfg_payload32; + u32 i = 0; + + if (copy_from_user(&gamut_cfg_payload32, + compat_ptr(gamut_cfg32->cfg_payload), + sizeof(gamut_cfg_payload32))) { + pr_err("failed to copy the gamut payload from userspace\n"); + return -EFAULT; + } + + memset(&gamut_cfg_payload, 0, sizeof(gamut_cfg_payload)); + gamut_cfg_payload.mode = gamut_cfg_payload32.mode; + for (i = 0; i < MDP_GAMUT_TABLE_NUM_V1_7; i++) { + gamut_cfg_payload.tbl_size[i] = + gamut_cfg_payload32.tbl_size[i]; + gamut_cfg_payload.c0_data[i] = + compat_ptr(gamut_cfg_payload32.c0_data[i]); + gamut_cfg_payload.c1_c2_data[i] = + compat_ptr(gamut_cfg_payload32.c1_c2_data[i]); + } + for (i = 0; i < MDP_GAMUT_SCALE_OFF_TABLE_NUM; i++) { + gamut_cfg_payload.tbl_scale_off_sz[i] = + gamut_cfg_payload32.tbl_scale_off_sz[i]; + gamut_cfg_payload.scale_off_data[i] = + compat_ptr(gamut_cfg_payload32.scale_off_data[i]); + } + if (copy_to_user(gamut_cfg->cfg_payload, &gamut_cfg_payload, + sizeof(gamut_cfg_payload))) { + pr_err("failed to copy the gamut payload to userspace\n"); + return -EFAULT; + } + return 0; +} + +static int __from_user_gamut_cfg_data( + struct mdp_gamut_cfg_data32 __user *gamut_cfg32, + struct mdp_gamut_cfg_data __user *gamut_cfg) +{ + uint32_t data, version; + int i; + + if (copy_in_user(&gamut_cfg->block, + &gamut_cfg32->block, + sizeof(uint32_t)) || + copy_in_user(&gamut_cfg->flags, + &gamut_cfg32->flags, + sizeof(uint32_t)) || + copy_in_user(&gamut_cfg->gamut_first, + &gamut_cfg32->gamut_first, + sizeof(uint32_t)) || + copy_in_user(&gamut_cfg->tbl_size[0], + &gamut_cfg32->tbl_size[0], + MDP_GAMUT_TABLE_NUM * sizeof(uint32_t)) || + copy_in_user(&gamut_cfg->version, + &gamut_cfg32->version, + sizeof(uint32_t))) + return 0; + + if (copy_from_user(&version, &gamut_cfg32->version, sizeof(u32))) { + pr_err("failed to copy the version info\n"); + return -EFAULT; + } + + switch (version) { + case mdp_gamut_v1_7: + if (__from_user_gamut_cfg_data_v17(gamut_cfg32, gamut_cfg)) { + pr_err("failed to get the gamut data for version %d\n", + version); + return -EFAULT; + } + break; + default: + pr_debug("version invalid fallback to legacy\n"); + /* The Gamut LUT data contains 3 static arrays for R, G, and B + * gamut data. Each these arrays contains pointers dynamic arrays + * which hold the gamut LUTs for R, G, and B. Must copy the array of + * pointers from 32 bit to 64 bit addresses. + */ + for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { + if (get_user(data, &gamut_cfg32->r_tbl[i]) || + put_user(compat_ptr(data), &gamut_cfg->r_tbl[i])) + return -EFAULT; + } + + for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { + if (get_user(data, &gamut_cfg32->g_tbl[i]) || + put_user(compat_ptr(data), &gamut_cfg->g_tbl[i])) + return -EFAULT; + } + + for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { + if (get_user(data, &gamut_cfg32->b_tbl[i]) || + put_user(compat_ptr(data), &gamut_cfg->b_tbl[i])) + return -EFAULT; + } + break; + } + return 0; +} + +static int __to_user_gamut_cfg_data( + struct mdp_gamut_cfg_data32 __user *gamut_cfg32, + struct mdp_gamut_cfg_data __user *gamut_cfg) +{ + unsigned long data; + int i; + + if (copy_in_user(&gamut_cfg32->block, + &gamut_cfg->block, + sizeof(uint32_t)) || + copy_in_user(&gamut_cfg32->flags, + &gamut_cfg->flags, + sizeof(uint32_t)) || + copy_in_user(&gamut_cfg32->gamut_first, + &gamut_cfg->gamut_first, + sizeof(uint32_t)) || + copy_in_user(&gamut_cfg32->tbl_size[0], + &gamut_cfg->tbl_size[0], + MDP_GAMUT_TABLE_NUM * sizeof(uint32_t))) + return 0; + + for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { + if (get_user(data, (unsigned long *) &gamut_cfg->r_tbl[i]) || + put_user((compat_caddr_t)data, &gamut_cfg32->r_tbl[i])) + return -EFAULT; + } + + for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { + if (get_user(data, (unsigned long *) &gamut_cfg->g_tbl[i]) || + put_user((compat_caddr_t)data, &gamut_cfg32->g_tbl[i])) + return -EFAULT; + } + + for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { + if (get_user(data, (unsigned long *) &gamut_cfg->b_tbl[i]) || + put_user((compat_caddr_t)data, &gamut_cfg32->g_tbl[i])) + return -EFAULT; + } + + return 0; +} + +static int __from_user_calib_config_data( + struct mdp_calib_config_data32 __user *calib_cfg32, + struct mdp_calib_config_data __user *calib_cfg) +{ + if (copy_in_user(&calib_cfg->ops, + &calib_cfg32->ops, + sizeof(uint32_t)) || + copy_in_user(&calib_cfg->addr, + &calib_cfg32->addr, + sizeof(uint32_t)) || + copy_in_user(&calib_cfg->data, + &calib_cfg32->data, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __to_user_calib_config_data( + struct mdp_calib_config_data32 __user *calib_cfg32, + struct mdp_calib_config_data __user *calib_cfg) +{ + if (copy_in_user(&calib_cfg32->ops, + &calib_cfg->ops, + sizeof(uint32_t)) || + copy_in_user(&calib_cfg32->addr, + &calib_cfg->addr, + sizeof(uint32_t)) || + copy_in_user(&calib_cfg32->data, + &calib_cfg->data, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_ad_init( + struct mdss_ad_init32 __user *ad_init32, + struct mdss_ad_init __user *ad_init) +{ + uint32_t data; + + if (copy_in_user(&ad_init->asym_lut[0], + &ad_init32->asym_lut[0], + 33 * sizeof(uint32_t)) || + copy_in_user(&ad_init->color_corr_lut[0], + &ad_init32->color_corr_lut[0], + 33 * sizeof(uint32_t)) || + copy_in_user(&ad_init->i_control[0], + &ad_init32->i_control[0], + 2 * sizeof(uint8_t)) || + copy_in_user(&ad_init->black_lvl, + &ad_init32->black_lvl, + sizeof(uint16_t)) || + copy_in_user(&ad_init->white_lvl, + &ad_init32->white_lvl, + sizeof(uint16_t)) || + copy_in_user(&ad_init->var, + &ad_init32->var, + sizeof(uint8_t)) || + copy_in_user(&ad_init->limit_ampl, + &ad_init32->limit_ampl, + sizeof(uint8_t)) || + copy_in_user(&ad_init->i_dither, + &ad_init32->i_dither, + sizeof(uint8_t)) || + copy_in_user(&ad_init->slope_max, + &ad_init32->slope_max, + sizeof(uint8_t)) || + copy_in_user(&ad_init->slope_min, + &ad_init32->slope_min, + sizeof(uint8_t)) || + copy_in_user(&ad_init->dither_ctl, + &ad_init32->dither_ctl, + sizeof(uint8_t)) || + copy_in_user(&ad_init->format, + &ad_init32->format, + sizeof(uint8_t)) || + copy_in_user(&ad_init->auto_size, + &ad_init32->auto_size, + sizeof(uint8_t)) || + copy_in_user(&ad_init->frame_w, + &ad_init32->frame_w, + sizeof(uint16_t)) || + copy_in_user(&ad_init->frame_h, + &ad_init32->frame_h, + sizeof(uint16_t)) || + copy_in_user(&ad_init->logo_v, + &ad_init32->logo_v, + sizeof(uint8_t)) || + copy_in_user(&ad_init->logo_h, + &ad_init32->logo_h, + sizeof(uint8_t)) || + copy_in_user(&ad_init->alpha, + &ad_init32->alpha, + sizeof(uint32_t)) || + copy_in_user(&ad_init->alpha_base, + &ad_init32->alpha_base, + sizeof(uint32_t)) || + copy_in_user(&ad_init->bl_lin_len, + &ad_init32->bl_lin_len, + sizeof(uint32_t)) || + copy_in_user(&ad_init->bl_att_len, + &ad_init32->bl_att_len, + sizeof(uint32_t))) + return -EFAULT; + + + if (get_user(data, &ad_init32->bl_lin) || + put_user(compat_ptr(data), &ad_init->bl_lin) || + get_user(data, &ad_init32->bl_lin_inv) || + put_user(compat_ptr(data), &ad_init->bl_lin_inv) || + get_user(data, &ad_init32->bl_att_lut) || + put_user(compat_ptr(data), &ad_init->bl_att_lut)) + return -EFAULT; + + return 0; +} + +static int __from_user_ad_cfg( + struct mdss_ad_cfg32 __user *ad_cfg32, + struct mdss_ad_cfg __user *ad_cfg) +{ + if (copy_in_user(&ad_cfg->mode, + &ad_cfg32->mode, + sizeof(uint32_t)) || + copy_in_user(&ad_cfg->al_calib_lut[0], + &ad_cfg32->al_calib_lut[0], + 33 * sizeof(uint32_t)) || + copy_in_user(&ad_cfg->backlight_min, + &ad_cfg32->backlight_min, + sizeof(uint16_t)) || + copy_in_user(&ad_cfg->backlight_max, + &ad_cfg32->backlight_max, + sizeof(uint16_t)) || + copy_in_user(&ad_cfg->backlight_scale, + &ad_cfg32->backlight_scale, + sizeof(uint16_t)) || + copy_in_user(&ad_cfg->amb_light_min, + &ad_cfg32->amb_light_min, + sizeof(uint16_t)) || + copy_in_user(&ad_cfg->filter[0], + &ad_cfg32->filter[0], + 2 * sizeof(uint16_t)) || + copy_in_user(&ad_cfg->calib[0], + &ad_cfg32->calib[0], + 4 * sizeof(uint16_t)) || + copy_in_user(&ad_cfg->strength_limit, + &ad_cfg32->strength_limit, + sizeof(uint8_t)) || + copy_in_user(&ad_cfg->t_filter_recursion, + &ad_cfg32->t_filter_recursion, + sizeof(uint8_t)) || + copy_in_user(&ad_cfg->stab_itr, + &ad_cfg32->stab_itr, + sizeof(uint16_t)) || + copy_in_user(&ad_cfg->bl_ctrl_mode, + &ad_cfg32->bl_ctrl_mode, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_ad_init_cfg( + struct mdss_ad_init_cfg32 __user *ad_info32, + struct mdss_ad_init_cfg __user *ad_info) +{ + uint32_t op; + + if (copy_from_user(&op, &ad_info32->ops, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_in_user(&ad_info->ops, + &ad_info32->ops, + sizeof(uint32_t))) + return -EFAULT; + + if (op & MDP_PP_AD_INIT) { + if (__from_user_ad_init( + compat_ptr((uintptr_t)&ad_info32->params.init), + &ad_info->params.init)) + return -EFAULT; + } else if (op & MDP_PP_AD_CFG) { + if (__from_user_ad_cfg( + compat_ptr((uintptr_t)&ad_info32->params.cfg), + &ad_info->params.cfg)) + return -EFAULT; + } else { + pr_err("Invalid AD init/config operation\n"); + return -EINVAL; + } + + return 0; +} + +static int __from_user_ad_input( + struct mdss_ad_input32 __user *ad_input32, + struct mdss_ad_input __user *ad_input) +{ + int mode; + + if (copy_from_user(&mode, + &ad_input32->mode, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_in_user(&ad_input->mode, + &ad_input32->mode, + sizeof(uint32_t)) || + copy_in_user(&ad_input->output, + &ad_input32->output, + sizeof(uint32_t))) + return -EFAULT; + + switch (mode) { + case MDSS_AD_MODE_AUTO_BL: + case MDSS_AD_MODE_AUTO_STR: + if (copy_in_user(&ad_input->in.amb_light, + &ad_input32->in.amb_light, + sizeof(uint32_t))) + return -EFAULT; + break; + case MDSS_AD_MODE_TARG_STR: + case MDSS_AD_MODE_MAN_STR: + if (copy_in_user(&ad_input->in.strength, + &ad_input32->in.strength, + sizeof(uint32_t))) + return -EFAULT; + break; + case MDSS_AD_MODE_CALIB: + if (copy_in_user(&ad_input->in.calib_bl, + &ad_input32->in.calib_bl, + sizeof(uint32_t))) + return -EFAULT; + break; + } + + return 0; +} + +static int __to_user_ad_input( + struct mdss_ad_input32 __user *ad_input32, + struct mdss_ad_input __user *ad_input) +{ + int mode; + + if (copy_from_user(&mode, + &ad_input->mode, + sizeof(uint32_t))) + return -EFAULT; + + if (copy_in_user(&ad_input32->mode, + &ad_input->mode, + sizeof(uint32_t)) || + copy_in_user(&ad_input32->output, + &ad_input->output, + sizeof(uint32_t))) + return -EFAULT; + + switch (mode) { + case MDSS_AD_MODE_AUTO_BL: + case MDSS_AD_MODE_AUTO_STR: + if (copy_in_user(&ad_input32->in.amb_light, + &ad_input->in.amb_light, + sizeof(uint32_t))) + return -EFAULT; + break; + case MDSS_AD_MODE_TARG_STR: + case MDSS_AD_MODE_MAN_STR: + if (copy_in_user(&ad_input32->in.strength, + &ad_input->in.strength, + sizeof(uint32_t))) + return -EFAULT; + break; + case MDSS_AD_MODE_CALIB: + if (copy_in_user(&ad_input32->in.calib_bl, + &ad_input->in.calib_bl, + sizeof(uint32_t))) + return -EFAULT; + break; + } + + return 0; +} + +static int __from_user_calib_cfg( + struct mdss_calib_cfg32 __user *calib_cfg32, + struct mdss_calib_cfg __user *calib_cfg) +{ + if (copy_in_user(&calib_cfg->ops, + &calib_cfg32->ops, + sizeof(uint32_t)) || + copy_in_user(&calib_cfg->calib_mask, + &calib_cfg32->calib_mask, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_calib_config_buffer( + struct mdp_calib_config_buffer32 __user *calib_buffer32, + struct mdp_calib_config_buffer __user *calib_buffer) +{ + uint32_t data; + + if (copy_in_user(&calib_buffer->ops, + &calib_buffer32->ops, + sizeof(uint32_t)) || + copy_in_user(&calib_buffer->size, + &calib_buffer32->size, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, &calib_buffer32->buffer) || + put_user(compat_ptr(data), &calib_buffer->buffer)) + return -EFAULT; + + return 0; +} + +static int __to_user_calib_config_buffer( + struct mdp_calib_config_buffer32 __user *calib_buffer32, + struct mdp_calib_config_buffer __user *calib_buffer) +{ + unsigned long data; + + if (copy_in_user(&calib_buffer32->ops, + &calib_buffer->ops, + sizeof(uint32_t)) || + copy_in_user(&calib_buffer32->size, + &calib_buffer->size, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &calib_buffer->buffer) || + put_user((compat_caddr_t) data, &calib_buffer32->buffer)) + return -EFAULT; + + return 0; +} + +static int __from_user_calib_dcm_state( + struct mdp_calib_dcm_state32 __user *calib_dcm32, + struct mdp_calib_dcm_state __user *calib_dcm) +{ + if (copy_in_user(&calib_dcm->ops, + &calib_dcm32->ops, + sizeof(uint32_t)) || + copy_in_user(&calib_dcm->dcm_state, + &calib_dcm32->dcm_state, + sizeof(uint32_t))) + return -EFAULT; + + return 0; +} + +static u32 __pp_compat_size_igc(void) +{ + u32 alloc_size = 0; + /* When we have multiple versions pick largest struct size */ + alloc_size = sizeof(struct mdp_igc_lut_data_v1_7); + return alloc_size; +} + +static u32 __pp_compat_size_hist_lut(void) +{ + u32 alloc_size = 0; + /* When we have multiple versions pick largest struct size */ + alloc_size = sizeof(struct mdp_hist_lut_data_v1_7); + return alloc_size; +} + +static u32 __pp_compat_size_pgc(void) +{ + u32 tbl_sz_max = 0; + + tbl_sz_max = 3 * GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data); + tbl_sz_max += sizeof(struct mdp_pgc_lut_data_v1_7); + return tbl_sz_max; +} + +static u32 __pp_compat_size_pcc(void) +{ + /* if new version of PCC is added return max struct size */ + return sizeof(struct mdp_pcc_data_v1_7); +} + +static u32 __pp_compat_size_pa(void) +{ + /* if new version of PA is added return max struct size */ + return sizeof(struct mdp_pa_data_v1_7); +} + +static u32 __pp_compat_size_gamut(void) +{ + return sizeof(struct mdp_gamut_data_v1_7); +} + +static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, + struct msmfb_mdp_pp __user **pp, + uint32_t op) +{ + uint32_t alloc_size = 0, lut_type, pgc_size = 0; + + alloc_size = sizeof(struct msmfb_mdp_pp); + switch (op) { + case mdp_op_lut_cfg: + if (copy_from_user(&lut_type, + &pp32->data.lut_cfg_data.lut_type, + sizeof(uint32_t))) + return -EFAULT; + + switch (lut_type) { + case mdp_lut_pgc: + + pgc_size = GC_LUT_SEGMENTS * + sizeof(struct mdp_ar_gc_lut_data); + alloc_size += __pp_compat_size_pgc(); + + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) + return -ENOMEM; + memset(*pp, 0, alloc_size); + + (*pp)->data.lut_cfg_data.data.pgc_lut_data.r_data = + (struct mdp_ar_gc_lut_data *) + ((unsigned long) *pp + + sizeof(struct msmfb_mdp_pp)); + (*pp)->data.lut_cfg_data.data.pgc_lut_data.g_data = + (struct mdp_ar_gc_lut_data *) + ((unsigned long) *pp + + sizeof(struct msmfb_mdp_pp) + + pgc_size); + (*pp)->data.lut_cfg_data.data.pgc_lut_data.b_data = + (struct mdp_ar_gc_lut_data *) + ((unsigned long) *pp + + sizeof(struct msmfb_mdp_pp) + + (2 * pgc_size)); + (*pp)->data.lut_cfg_data.data.pgc_lut_data.cfg_payload + = (void *)((unsigned long) *pp + + sizeof(struct msmfb_mdp_pp) + + (3 * pgc_size)); + break; + case mdp_lut_igc: + alloc_size += __pp_compat_size_igc(); + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) { + pr_err("failed to alloc from user size %d for igc\n", + alloc_size); + return -ENOMEM; + } + memset(*pp, 0, alloc_size); + (*pp)->data.lut_cfg_data.data.igc_lut_data.cfg_payload + = (void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)); + break; + case mdp_lut_hist: + alloc_size += __pp_compat_size_hist_lut(); + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) { + pr_err("failed to alloc from user size %d for hist lut\n", + alloc_size); + return -ENOMEM; + } + memset(*pp, 0, alloc_size); + (*pp)->data.lut_cfg_data.data.hist_lut_data.cfg_payload + = (void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)); + break; + default: + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) { + pr_err("failed to alloc from user size %d for lut_type %d\n", + alloc_size, lut_type); + return -ENOMEM; + } + memset(*pp, 0, alloc_size); + break; + } + break; + case mdp_op_pcc_cfg: + alloc_size += __pp_compat_size_pcc(); + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) { + pr_err("alloc from user size %d for pcc fail\n", + alloc_size); + return -ENOMEM; + } + memset(*pp, 0, alloc_size); + (*pp)->data.pcc_cfg_data.cfg_payload = + (void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)); + break; + case mdp_op_gamut_cfg: + alloc_size += __pp_compat_size_gamut(); + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) { + pr_err("alloc from user size %d for pcc fail\n", + alloc_size); + return -ENOMEM; + } + memset(*pp, 0, alloc_size); + (*pp)->data.gamut_cfg_data.cfg_payload = + (void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)); + break; + case mdp_op_pa_v2_cfg: + alloc_size += __pp_compat_size_pa(); + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) { + pr_err("alloc from user size %d for pcc fail\n", + alloc_size); + return -ENOMEM; + } + memset(*pp, 0, alloc_size); + (*pp)->data.pa_v2_cfg_data.cfg_payload = + (void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)); + break; + default: + *pp = compat_alloc_user_space(alloc_size); + if (*pp == NULL) + return -ENOMEM; + memset(*pp, 0, alloc_size); + break; + } + return 0; +} + +static int mdss_compat_pp_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg, struct file *file) +{ + uint32_t op; + int ret = 0; + struct msmfb_mdp_pp32 __user *pp32; + struct msmfb_mdp_pp __user *pp; + + pp32 = compat_ptr(arg); + if (copy_from_user(&op, &pp32->op, sizeof(uint32_t))) + return -EFAULT; + + ret = __pp_compat_alloc(pp32, &pp, op); + if (ret) + return ret; + + if (copy_in_user(&pp->op, &pp32->op, sizeof(uint32_t))) + return -EFAULT; + + switch (op) { + case mdp_op_pcc_cfg: + ret = __from_user_pcc_cfg_data( + compat_ptr((uintptr_t)&pp32->data.pcc_cfg_data), + &pp->data.pcc_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_pcc_cfg_data( + compat_ptr((uintptr_t)&pp32->data.pcc_cfg_data), + &pp->data.pcc_cfg_data); + break; + case mdp_op_csc_cfg: + ret = __from_user_csc_cfg_data( + compat_ptr((uintptr_t)&pp32->data.csc_cfg_data), + &pp->data.csc_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_csc_cfg_data( + compat_ptr((uintptr_t)&pp32->data.csc_cfg_data), + &pp->data.csc_cfg_data); + break; + case mdp_op_lut_cfg: + ret = __from_user_lut_cfg_data( + compat_ptr((uintptr_t)&pp32->data.lut_cfg_data), + &pp->data.lut_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_lut_cfg_data( + compat_ptr((uintptr_t)&pp32->data.lut_cfg_data), + &pp->data.lut_cfg_data); + break; + case mdp_op_qseed_cfg: + ret = __from_user_qseed_cfg_data( + compat_ptr((uintptr_t)&pp32->data.qseed_cfg_data), + &pp->data.qseed_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_qseed_cfg_data( + compat_ptr((uintptr_t)&pp32->data.qseed_cfg_data), + &pp->data.qseed_cfg_data); + break; + case mdp_bl_scale_cfg: + ret = __from_user_bl_scale_data( + compat_ptr((uintptr_t)&pp32->data.bl_scale_data), + &pp->data.bl_scale_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + break; + case mdp_op_pa_cfg: + ret = __from_user_pa_cfg_data( + compat_ptr((uintptr_t)&pp32->data.pa_cfg_data), + &pp->data.pa_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_pa_cfg_data( + compat_ptr((uintptr_t)&pp32->data.pa_cfg_data), + &pp->data.pa_cfg_data); + break; + case mdp_op_pa_v2_cfg: + ret = __from_user_pa_v2_cfg_data( + compat_ptr((uintptr_t)&pp32->data.pa_v2_cfg_data), + &pp->data.pa_v2_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_pa_v2_cfg_data( + compat_ptr((uintptr_t)&pp32->data.pa_v2_cfg_data), + &pp->data.pa_v2_cfg_data); + break; + case mdp_op_dither_cfg: + ret = __from_user_dither_cfg_data( + compat_ptr((uintptr_t)&pp32->data.dither_cfg_data), + &pp->data.dither_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_dither_cfg_data( + compat_ptr((uintptr_t)&pp32->data.dither_cfg_data), + &pp->data.dither_cfg_data); + break; + case mdp_op_gamut_cfg: + ret = __from_user_gamut_cfg_data( + compat_ptr((uintptr_t)&pp32->data.gamut_cfg_data), + &pp->data.gamut_cfg_data); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_gamut_cfg_data( + compat_ptr((uintptr_t)&pp32->data.gamut_cfg_data), + &pp->data.gamut_cfg_data); + break; + case mdp_op_calib_cfg: + ret = __from_user_calib_config_data( + compat_ptr((uintptr_t)&pp32->data.calib_cfg), + &pp->data.calib_cfg); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_calib_config_data( + compat_ptr((uintptr_t)&pp32->data.calib_cfg), + &pp->data.calib_cfg); + break; + case mdp_op_ad_cfg: + ret = __from_user_ad_init_cfg( + compat_ptr((uintptr_t)&pp32->data.ad_init_cfg), + &pp->data.ad_init_cfg); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + break; + case mdp_op_ad_input: + ret = __from_user_ad_input( + compat_ptr((uintptr_t)&pp32->data.ad_input), + &pp->data.ad_input); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_ad_input( + compat_ptr((uintptr_t)&pp32->data.ad_input), + &pp->data.ad_input); + break; + case mdp_op_calib_mode: + ret = __from_user_calib_cfg( + compat_ptr((uintptr_t)&pp32->data.mdss_calib_cfg), + &pp->data.mdss_calib_cfg); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + break; + case mdp_op_calib_buffer: + ret = __from_user_calib_config_buffer( + compat_ptr((uintptr_t)&pp32->data.calib_buffer), + &pp->data.calib_buffer); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + if (ret) + goto pp_compat_exit; + ret = __to_user_calib_config_buffer( + compat_ptr((uintptr_t)&pp32->data.calib_buffer), + &pp->data.calib_buffer); + break; + case mdp_op_calib_dcm_state: + ret = __from_user_calib_dcm_state( + compat_ptr((uintptr_t)&pp32->data.calib_dcm), + &pp->data.calib_dcm); + if (ret) + goto pp_compat_exit; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); + break; + default: + break; + } + +pp_compat_exit: + return ret; +} + +static int __from_user_pp_params(struct mdp_overlay_pp_params32 *ppp32, + struct mdp_overlay_pp_params *ppp) +{ + int ret = 0; + + if (copy_in_user(&ppp->config_ops, + &ppp32->config_ops, + sizeof(uint32_t))) + return -EFAULT; + + ret = __from_user_csc_cfg( + compat_ptr((uintptr_t)&ppp32->csc_cfg), + &ppp->csc_cfg); + if (ret) + return ret; + ret = __from_user_qseed_cfg( + compat_ptr((uintptr_t)&ppp32->qseed_cfg[0]), + &ppp->qseed_cfg[0]); + if (ret) + return ret; + ret = __from_user_qseed_cfg( + compat_ptr((uintptr_t)&ppp32->qseed_cfg[1]), + &ppp->qseed_cfg[1]); + if (ret) + return ret; + ret = __from_user_pa_cfg( + compat_ptr((uintptr_t)&ppp32->pa_cfg), + &ppp->pa_cfg); + if (ret) + return ret; + ret = __from_user_igc_lut_data( + compat_ptr((uintptr_t)&ppp32->igc_cfg), + &ppp->igc_cfg); + if (ret) + return ret; + ret = __from_user_sharp_cfg( + compat_ptr((uintptr_t)&ppp32->sharp_cfg), + &ppp->sharp_cfg); + if (ret) + return ret; + ret = __from_user_histogram_cfg( + compat_ptr((uintptr_t)&ppp32->hist_cfg), + &ppp->hist_cfg); + if (ret) + return ret; + ret = __from_user_hist_lut_data( + compat_ptr((uintptr_t)&ppp32->hist_lut_cfg), + &ppp->hist_lut_cfg); + if (ret) + return ret; + ret = __from_user_pa_v2_data( + compat_ptr((uintptr_t)&ppp32->pa_v2_cfg), + &ppp->pa_v2_cfg); + + return ret; +} + +static int __to_user_pp_params(struct mdp_overlay_pp_params *ppp, + struct mdp_overlay_pp_params32 *ppp32) +{ + int ret = 0; + + if (copy_in_user(&ppp32->config_ops, + &ppp->config_ops, + sizeof(uint32_t))) + return -EFAULT; + + ret = __to_user_csc_cfg( + compat_ptr((uintptr_t)&ppp32->csc_cfg), + &ppp->csc_cfg); + if (ret) + return ret; + ret = __to_user_qseed_cfg( + compat_ptr((uintptr_t)&ppp32->qseed_cfg[0]), + &ppp->qseed_cfg[0]); + if (ret) + return ret; + ret = __to_user_qseed_cfg( + compat_ptr((uintptr_t)&ppp32->qseed_cfg[1]), + &ppp->qseed_cfg[1]); + if (ret) + return ret; + ret = __to_user_pa_cfg( + compat_ptr((uintptr_t)&ppp32->pa_cfg), + &ppp->pa_cfg); + if (ret) + return ret; + ret = __to_user_igc_lut_data( + compat_ptr((uintptr_t)&ppp32->igc_cfg), + &ppp->igc_cfg); + if (ret) + return ret; + ret = __to_user_sharp_cfg( + compat_ptr((uintptr_t)&ppp32->sharp_cfg), + &ppp->sharp_cfg); + if (ret) + return ret; + ret = __to_user_histogram_cfg( + compat_ptr((uintptr_t)&ppp32->hist_cfg), + &ppp->hist_cfg); + if (ret) + return ret; + ret = __to_user_hist_lut_data( + compat_ptr((uintptr_t)&ppp32->hist_lut_cfg), + &ppp->hist_lut_cfg); + if (ret) + return ret; + ret = __to_user_pa_v2_data( + compat_ptr((uintptr_t)&ppp32->pa_v2_cfg), + &ppp->pa_v2_cfg); + + return ret; +} + +static int __from_user_hist_start_req( + struct mdp_histogram_start_req32 __user *hist_req32, + struct mdp_histogram_start_req __user *hist_req) +{ + if (copy_in_user(&hist_req->block, + &hist_req32->block, + sizeof(uint32_t)) || + copy_in_user(&hist_req->frame_cnt, + &hist_req32->frame_cnt, + sizeof(uint8_t)) || + copy_in_user(&hist_req->bit_mask, + &hist_req32->bit_mask, + sizeof(uint8_t)) || + copy_in_user(&hist_req->num_bins, + &hist_req32->num_bins, + sizeof(uint16_t))) + return -EFAULT; + + return 0; +} + +static int __from_user_hist_data( + struct mdp_histogram_data32 __user *hist_data32, + struct mdp_histogram_data __user *hist_data) +{ + uint32_t data; + + if (copy_in_user(&hist_data->block, + &hist_data32->block, + sizeof(uint32_t)) || + copy_in_user(&hist_data->bin_cnt, + &hist_data32->bin_cnt, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, &hist_data32->c0) || + put_user(compat_ptr(data), &hist_data->c0) || + get_user(data, &hist_data32->c1) || + put_user(compat_ptr(data), &hist_data->c1) || + get_user(data, &hist_data32->c2) || + put_user(compat_ptr(data), &hist_data->c2) || + get_user(data, &hist_data32->extra_info) || + put_user(compat_ptr(data), &hist_data->extra_info)) + return -EFAULT; + + return 0; +} + +static int __to_user_hist_data( + struct mdp_histogram_data32 __user *hist_data32, + struct mdp_histogram_data __user *hist_data) +{ + unsigned long data; + + if (copy_in_user(&hist_data32->block, + &hist_data->block, + sizeof(uint32_t)) || + copy_in_user(&hist_data32->bin_cnt, + &hist_data->bin_cnt, + sizeof(uint32_t))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &hist_data->c0) || + put_user((compat_caddr_t) data, &hist_data32->c0) || + get_user(data, (unsigned long *) &hist_data->c1) || + put_user((compat_caddr_t) data, &hist_data32->c1) || + get_user(data, (unsigned long *) &hist_data->c2) || + put_user((compat_caddr_t) data, &hist_data32->c2) || + get_user(data, (unsigned long *) &hist_data->extra_info) || + put_user((compat_caddr_t) data, &hist_data32->extra_info)) + return -EFAULT; + + return 0; +} + +static int mdss_histo_compat_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg, struct file *file) +{ + struct mdp_histogram_data __user *hist; + struct mdp_histogram_data32 __user *hist32; + struct mdp_histogram_start_req __user *hist_req; + struct mdp_histogram_start_req32 __user *hist_req32; + int ret = 0; + + switch (cmd) { + case MSMFB_HISTOGRAM_START: + hist_req32 = compat_ptr(arg); + hist_req = compat_alloc_user_space( + sizeof(struct mdp_histogram_start_req)); + if (!hist_req) { + pr_err("%s:%u: compat alloc error [%zu] bytes\n", + __func__, __LINE__, + sizeof(struct mdp_histogram_start_req)); + return -EINVAL; + } + memset(hist_req, 0, sizeof(struct mdp_histogram_start_req)); + ret = __from_user_hist_start_req(hist_req32, hist_req); + if (ret) + goto histo_compat_err; + ret = mdss_fb_do_ioctl(info, cmd, + (unsigned long) hist_req, file); + break; + case MSMFB_HISTOGRAM_STOP: + ret = mdss_fb_do_ioctl(info, cmd, arg, file); + break; + case MSMFB_HISTOGRAM: + hist32 = compat_ptr(arg); + hist = compat_alloc_user_space( + sizeof(struct mdp_histogram_data)); + if (!hist) { + pr_err("%s:%u: compat alloc error [%zu] bytes\n", + __func__, __LINE__, + sizeof(struct mdp_histogram_data)); + return -EINVAL; + } + memset(hist, 0, sizeof(struct mdp_histogram_data)); + ret = __from_user_hist_data(hist32, hist); + if (ret) + goto histo_compat_err; + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) hist, file); + if (ret) + goto histo_compat_err; + ret = __to_user_hist_data(hist32, hist); + break; + default: + break; + } + +histo_compat_err: + return ret; +} + +static int __copy_layer_pp_info_qseed_params( + struct mdp_overlay_pp_params *pp_info, + struct mdp_overlay_pp_params32 *pp_info32) +{ + pp_info->qseed_cfg[0].table_num = pp_info32->qseed_cfg[0].table_num; + pp_info->qseed_cfg[0].ops = pp_info32->qseed_cfg[0].ops; + pp_info->qseed_cfg[0].len = pp_info32->qseed_cfg[0].len; + pp_info->qseed_cfg[0].data = compat_ptr(pp_info32->qseed_cfg[0].data); + + pp_info->qseed_cfg[1].table_num = pp_info32->qseed_cfg[1].table_num; + pp_info->qseed_cfg[1].ops = pp_info32->qseed_cfg[1].ops; + pp_info->qseed_cfg[1].len = pp_info32->qseed_cfg[1].len; + pp_info->qseed_cfg[1].data = compat_ptr(pp_info32->qseed_cfg[1].data); + + return 0; +} + +static int __copy_layer_igc_lut_data_v1_7( + struct mdp_igc_lut_data_v1_7 *cfg_payload, + struct mdp_igc_lut_data_v1_7_32 __user *cfg_payload32) +{ + struct mdp_igc_lut_data_v1_7_32 local_cfg_payload32; + int ret = 0; + + ret = copy_from_user(&local_cfg_payload32, + cfg_payload32, + sizeof(struct mdp_igc_lut_data_v1_7_32)); + if (ret) { + pr_err("copy from user failed, IGC cfg payload = %pK\n", + cfg_payload32); + ret = -EFAULT; + goto exit; + } + + cfg_payload->table_fmt = local_cfg_payload32.table_fmt; + cfg_payload->len = local_cfg_payload32.len; + cfg_payload->c0_c1_data = compat_ptr(local_cfg_payload32.c0_c1_data); + cfg_payload->c2_data = compat_ptr(local_cfg_payload32.c2_data); + +exit: + return ret; +} + +static int __copy_layer_pp_info_igc_params( + struct mdp_overlay_pp_params *pp_info, + struct mdp_overlay_pp_params32 *pp_info32) +{ + void *cfg_payload = NULL; + uint32_t payload_size = 0; + int ret = 0; + + pp_info->igc_cfg.block = pp_info32->igc_cfg.block; + pp_info->igc_cfg.version = pp_info32->igc_cfg.version; + pp_info->igc_cfg.ops = pp_info32->igc_cfg.ops; + + if (pp_info->igc_cfg.version != 0) { + payload_size = __pp_compat_size_igc(); + + cfg_payload = kmalloc(payload_size, GFP_KERNEL); + if (!cfg_payload) { + ret = -ENOMEM; + goto exit; + } + } + + switch (pp_info->igc_cfg.version) { + case mdp_igc_v1_7: + ret = __copy_layer_igc_lut_data_v1_7(cfg_payload, + compat_ptr(pp_info32->igc_cfg.cfg_payload)); + if (ret) { + pr_err("compat copy of IGC cfg payload failed, ret %d\n", + ret); + kfree(cfg_payload); + cfg_payload = NULL; + goto exit; + } + break; + default: + pr_debug("No version set, fallback to legacy IGC version\n"); + pp_info->igc_cfg.len = pp_info32->igc_cfg.len; + pp_info->igc_cfg.c0_c1_data = + compat_ptr(pp_info32->igc_cfg.c0_c1_data); + pp_info->igc_cfg.c2_data = + compat_ptr(pp_info32->igc_cfg.c2_data); + kfree(cfg_payload); + cfg_payload = NULL; + break; + } +exit: + pp_info->igc_cfg.cfg_payload = cfg_payload; + return ret; +} + +static int __copy_layer_hist_lut_data_v1_7( + struct mdp_hist_lut_data_v1_7 *cfg_payload, + struct mdp_hist_lut_data_v1_7_32 __user *cfg_payload32) +{ + struct mdp_hist_lut_data_v1_7_32 local_cfg_payload32; + int ret = 0; + + ret = copy_from_user(&local_cfg_payload32, + cfg_payload32, + sizeof(struct mdp_hist_lut_data_v1_7_32)); + if (ret) { + pr_err("copy from user failed, hist lut cfg_payload = %pK\n", + cfg_payload32); + ret = -EFAULT; + goto exit; + } + + cfg_payload->len = local_cfg_payload32.len; + cfg_payload->data = compat_ptr(local_cfg_payload32.data); +exit: + return ret; +} + +static int __copy_layer_pp_info_hist_lut_params( + struct mdp_overlay_pp_params *pp_info, + struct mdp_overlay_pp_params32 *pp_info32) +{ + void *cfg_payload = NULL; + uint32_t payload_size = 0; + int ret = 0; + + pp_info->hist_lut_cfg.block = pp_info32->hist_lut_cfg.block; + pp_info->hist_lut_cfg.version = pp_info32->hist_lut_cfg.version; + pp_info->hist_lut_cfg.ops = pp_info32->hist_lut_cfg.ops; + pp_info->hist_lut_cfg.hist_lut_first = + pp_info32->hist_lut_cfg.hist_lut_first; + + if (pp_info->hist_lut_cfg.version != 0) { + payload_size = __pp_compat_size_hist_lut(); + + cfg_payload = kmalloc(payload_size, GFP_KERNEL); + if (!cfg_payload) { + ret = -ENOMEM; + goto exit; + } + } + + switch (pp_info->hist_lut_cfg.version) { + case mdp_hist_lut_v1_7: + ret = __copy_layer_hist_lut_data_v1_7(cfg_payload, + compat_ptr(pp_info32->hist_lut_cfg.cfg_payload)); + if (ret) { + pr_err("compat copy of Hist LUT cfg payload failed, ret %d\n", + ret); + kfree(cfg_payload); + cfg_payload = NULL; + goto exit; + } + break; + default: + pr_debug("version invalid, fallback to legacy\n"); + pp_info->hist_lut_cfg.len = pp_info32->hist_lut_cfg.len; + pp_info->hist_lut_cfg.data = + compat_ptr(pp_info32->hist_lut_cfg.data); + kfree(cfg_payload); + cfg_payload = NULL; + break; + } +exit: + pp_info->hist_lut_cfg.cfg_payload = cfg_payload; + return ret; +} + +static int __copy_layer_pa_data_v1_7( + struct mdp_pa_data_v1_7 *cfg_payload, + struct mdp_pa_data_v1_7_32 __user *cfg_payload32) +{ + struct mdp_pa_data_v1_7_32 local_cfg_payload32; + int ret = 0; + + ret = copy_from_user(&local_cfg_payload32, + cfg_payload32, + sizeof(struct mdp_pa_data_v1_7_32)); + if (ret) { + pr_err("copy from user failed, pa cfg_payload = %pK\n", + cfg_payload32); + ret = -EFAULT; + goto exit; + } + + cfg_payload->mode = local_cfg_payload32.mode; + cfg_payload->global_hue_adj = local_cfg_payload32.global_hue_adj; + cfg_payload->global_sat_adj = local_cfg_payload32.global_sat_adj; + cfg_payload->global_val_adj = local_cfg_payload32.global_val_adj; + cfg_payload->global_cont_adj = local_cfg_payload32.global_cont_adj; + + memcpy(&cfg_payload->skin_cfg, &local_cfg_payload32.skin_cfg, + sizeof(struct mdp_pa_mem_col_data_v1_7)); + memcpy(&cfg_payload->sky_cfg, &local_cfg_payload32.sky_cfg, + sizeof(struct mdp_pa_mem_col_data_v1_7)); + memcpy(&cfg_payload->fol_cfg, &local_cfg_payload32.fol_cfg, + sizeof(struct mdp_pa_mem_col_data_v1_7)); + + cfg_payload->six_zone_thresh = local_cfg_payload32.six_zone_thresh; + cfg_payload->six_zone_adj_p0 = local_cfg_payload32.six_zone_adj_p0; + cfg_payload->six_zone_adj_p1 = local_cfg_payload32.six_zone_adj_p1; + cfg_payload->six_zone_sat_hold = local_cfg_payload32.six_zone_sat_hold; + cfg_payload->six_zone_val_hold = local_cfg_payload32.six_zone_val_hold; + cfg_payload->six_zone_len = local_cfg_payload32.six_zone_len; + + cfg_payload->six_zone_curve_p0 = + compat_ptr(local_cfg_payload32.six_zone_curve_p0); + cfg_payload->six_zone_curve_p1 = + compat_ptr(local_cfg_payload32.six_zone_curve_p1); +exit: + return ret; +} + +static int __copy_layer_pp_info_pa_v2_params( + struct mdp_overlay_pp_params *pp_info, + struct mdp_overlay_pp_params32 *pp_info32) +{ + void *cfg_payload = NULL; + uint32_t payload_size = 0; + int ret = 0; + + pp_info->pa_v2_cfg_data.block = pp_info32->pa_v2_cfg_data.block; + pp_info->pa_v2_cfg_data.version = pp_info32->pa_v2_cfg_data.version; + pp_info->pa_v2_cfg_data.flags = pp_info32->pa_v2_cfg_data.flags; + + if (pp_info->pa_v2_cfg_data.version != 0) { + payload_size = __pp_compat_size_pa(); + + cfg_payload = kmalloc(payload_size, GFP_KERNEL); + if (!cfg_payload) { + ret = -ENOMEM; + goto exit; + } + } + + switch (pp_info->pa_v2_cfg_data.version) { + case mdp_pa_v1_7: + ret = __copy_layer_pa_data_v1_7(cfg_payload, + compat_ptr(pp_info32->pa_v2_cfg_data.cfg_payload)); + if (ret) { + pr_err("compat copy of PA cfg payload failed, ret %d\n", + ret); + kfree(cfg_payload); + cfg_payload = NULL; + goto exit; + } + break; + default: + pr_debug("version invalid\n"); + kfree(cfg_payload); + cfg_payload = NULL; + break; + } +exit: + pp_info->pa_v2_cfg_data.cfg_payload = cfg_payload; + return ret; +} + +static int __copy_layer_pp_info_legacy_pa_v2_params( + struct mdp_overlay_pp_params *pp_info, + struct mdp_overlay_pp_params32 *pp_info32) +{ + pp_info->pa_v2_cfg.global_hue_adj = + pp_info32->pa_v2_cfg.global_hue_adj; + pp_info->pa_v2_cfg.global_sat_adj = + pp_info32->pa_v2_cfg.global_sat_adj; + pp_info->pa_v2_cfg.global_val_adj = + pp_info32->pa_v2_cfg.global_val_adj; + pp_info->pa_v2_cfg.global_cont_adj = + pp_info32->pa_v2_cfg.global_cont_adj; + + memcpy(&pp_info->pa_v2_cfg.skin_cfg, + &pp_info32->pa_v2_cfg.skin_cfg, + sizeof(struct mdp_pa_mem_col_cfg)); + memcpy(&pp_info->pa_v2_cfg.sky_cfg, + &pp_info32->pa_v2_cfg.sky_cfg, + sizeof(struct mdp_pa_mem_col_cfg)); + memcpy(&pp_info->pa_v2_cfg.fol_cfg, + &pp_info32->pa_v2_cfg.fol_cfg, + sizeof(struct mdp_pa_mem_col_cfg)); + + pp_info->pa_v2_cfg.six_zone_thresh = + pp_info32->pa_v2_cfg.six_zone_thresh; + pp_info->pa_v2_cfg.six_zone_len = + pp_info32->pa_v2_cfg.six_zone_len; + + pp_info->pa_v2_cfg.six_zone_curve_p0 = + compat_ptr(pp_info32->pa_v2_cfg.six_zone_curve_p0); + pp_info->pa_v2_cfg.six_zone_curve_p1 = + compat_ptr(pp_info32->pa_v2_cfg.six_zone_curve_p1); + + return 0; +} + +static int __copy_layer_pp_info_pcc_params( + struct mdp_overlay_pp_params *pp_info, + struct mdp_overlay_pp_params32 *pp_info32) +{ + void *cfg_payload = NULL; + uint32_t payload_size = 0; + int ret = 0; + + pp_info->pcc_cfg_data.block = pp_info32->pcc_cfg_data.block; + pp_info->pcc_cfg_data.version = pp_info32->pcc_cfg_data.version; + pp_info->pcc_cfg_data.ops = pp_info32->pcc_cfg_data.ops; + + if (pp_info->pcc_cfg_data.version != 0) { + payload_size = __pp_compat_size_pcc(); + + cfg_payload = kmalloc(payload_size, GFP_KERNEL); + if (!cfg_payload) { + ret = -ENOMEM; + goto exit; + } + } + + switch (pp_info->pcc_cfg_data.version) { + case mdp_pcc_v1_7: + ret = copy_from_user(cfg_payload, + compat_ptr(pp_info32->pcc_cfg_data.cfg_payload), + sizeof(struct mdp_pcc_data_v1_7)); + if (ret) { + pr_err("compat copy of PCC cfg payload failed, ptr %pK\n", + compat_ptr( + pp_info32->pcc_cfg_data.cfg_payload)); + ret = -EFAULT; + kfree(cfg_payload); + cfg_payload = NULL; + goto exit; + } + break; + default: + pr_debug("version invalid, fallback to legacy\n"); + kfree(cfg_payload); + cfg_payload = NULL; + break; + } +exit: + pp_info->pcc_cfg_data.cfg_payload = cfg_payload; + return ret; +} + + +static int __copy_layer_pp_info_params(struct mdp_input_layer *layer, + struct mdp_input_layer32 *layer32) +{ + struct mdp_overlay_pp_params *pp_info; + struct mdp_overlay_pp_params32 pp_info32; + int ret = 0; + + if (!(layer->flags & MDP_LAYER_PP)) + return 0; + + ret = copy_from_user(&pp_info32, + compat_ptr(layer32->pp_info), + sizeof(struct mdp_overlay_pp_params32)); + if (ret) { + pr_err("pp info copy from user failed, pp_info %pK\n", + compat_ptr(layer32->pp_info)); + ret = -EFAULT; + goto exit; + } + + pp_info = kmalloc(sizeof(struct mdp_overlay_pp_params), GFP_KERNEL); + if (!pp_info) { + ret = -ENOMEM; + goto exit; + } + memset(pp_info, 0, sizeof(struct mdp_overlay_pp_params)); + + pp_info->config_ops = pp_info32.config_ops; + + memcpy(&pp_info->csc_cfg, &pp_info32.csc_cfg, + sizeof(struct mdp_csc_cfg)); + memcpy(&pp_info->sharp_cfg, &pp_info32.sharp_cfg, + sizeof(struct mdp_sharp_cfg)); + memcpy(&pp_info->hist_cfg, &pp_info32.hist_cfg, + sizeof(struct mdp_histogram_cfg)); + memcpy(&pp_info->pa_cfg, &pp_info32.pa_cfg, + sizeof(struct mdp_pa_cfg)); + + ret = __copy_layer_pp_info_qseed_params(pp_info, &pp_info32); + if (ret) { + pr_err("compat copy pp_info QSEED params failed, ret %d\n", + ret); + goto exit_pp_info; + } + ret = __copy_layer_pp_info_legacy_pa_v2_params(pp_info, &pp_info32); + if (ret) { + pr_err("compat copy pp_info Legacy PAv2 params failed, ret %d\n", + ret); + goto exit_pp_info; + } + ret = __copy_layer_pp_info_igc_params(pp_info, &pp_info32); + if (ret) { + pr_err("compat copy pp_info IGC params failed, ret %d\n", + ret); + goto exit_pp_info; + } + ret = __copy_layer_pp_info_hist_lut_params(pp_info, &pp_info32); + if (ret) { + pr_err("compat copy pp_info Hist LUT params failed, ret %d\n", + ret); + goto exit_igc; + } + ret = __copy_layer_pp_info_pa_v2_params(pp_info, &pp_info32); + if (ret) { + pr_err("compat copy pp_info PAv2 params failed, ret %d\n", + ret); + goto exit_hist_lut; + } + ret = __copy_layer_pp_info_pcc_params(pp_info, &pp_info32); + if (ret) { + pr_err("compat copy pp_info PCC params failed, ret %d\n", + ret); + goto exit_pa; + } + + layer->pp_info = pp_info; + + return ret; + +exit_pa: + kfree(pp_info->pa_v2_cfg_data.cfg_payload); +exit_hist_lut: + kfree(pp_info->hist_lut_cfg.cfg_payload); +exit_igc: + kfree(pp_info->igc_cfg.cfg_payload); +exit_pp_info: + kfree(pp_info); +exit: + return ret; +} + + +static int __to_user_mdp_overlay(struct mdp_overlay32 __user *ov32, + struct mdp_overlay __user *ov) +{ + int ret = 0; + + ret = copy_in_user(&ov32->src, &ov->src, sizeof(ov32->src)) || + copy_in_user(&ov32->src_rect, + &ov->src_rect, sizeof(ov32->src_rect)) || + copy_in_user(&ov32->dst_rect, + &ov->dst_rect, sizeof(ov32->dst_rect)); + if (ret) + return -EFAULT; + + ret |= put_user(ov->z_order, &ov32->z_order); + ret |= put_user(ov->is_fg, &ov32->is_fg); + ret |= put_user(ov->alpha, &ov32->alpha); + ret |= put_user(ov->blend_op, &ov32->blend_op); + ret |= put_user(ov->transp_mask, &ov32->transp_mask); + ret |= put_user(ov->flags, &ov32->flags); + ret |= put_user(ov->id, &ov32->id); + ret |= put_user(ov->priority, &ov32->priority); + if (ret) + return -EFAULT; + + ret = copy_in_user(&ov32->user_data, &ov->user_data, + sizeof(ov32->user_data)); + if (ret) + return -EFAULT; + + ret |= put_user(ov->horz_deci, &ov32->horz_deci); + ret |= put_user(ov->vert_deci, &ov32->vert_deci); + if (ret) + return -EFAULT; + + ret = __to_user_pp_params( + &ov->overlay_pp_cfg, + compat_ptr((uintptr_t) &ov32->overlay_pp_cfg)); + if (ret) + return -EFAULT; + + ret = copy_in_user(&ov32->scale, &ov->scale, + sizeof(struct mdp_scale_data)); + if (ret) + return -EFAULT; + + ret = put_user(ov->frame_rate, &ov32->frame_rate); + if (ret) + return -EFAULT; + + return 0; +} + + +static int __from_user_mdp_overlay(struct mdp_overlay *ov, + struct mdp_overlay32 __user *ov32) +{ + __u32 data; + + if (copy_in_user(&ov->src, &ov32->src, + sizeof(ov32->src)) || + copy_in_user(&ov->src_rect, &ov32->src_rect, + sizeof(ov32->src_rect)) || + copy_in_user(&ov->dst_rect, &ov32->dst_rect, + sizeof(ov32->dst_rect))) + return -EFAULT; + + if (get_user(data, &ov32->z_order) || + put_user(data, &ov->z_order) || + get_user(data, &ov32->is_fg) || + put_user(data, &ov->is_fg) || + get_user(data, &ov32->alpha) || + put_user(data, &ov->alpha) || + get_user(data, &ov32->blend_op) || + put_user(data, &ov->blend_op) || + get_user(data, &ov32->transp_mask) || + put_user(data, &ov->transp_mask) || + get_user(data, &ov32->flags) || + put_user(data, &ov->flags) || + get_user(data, &ov32->pipe_type) || + put_user(data, &ov->pipe_type) || + get_user(data, &ov32->id) || + put_user(data, &ov->id) || + get_user(data, &ov32->priority) || + put_user(data, &ov->priority)) + return -EFAULT; + + if (copy_in_user(&ov->user_data, &ov32->user_data, + sizeof(ov32->user_data))) + return -EFAULT; + + if (get_user(data, &ov32->horz_deci) || + put_user(data, &ov->horz_deci) || + get_user(data, &ov32->vert_deci) || + put_user(data, &ov->vert_deci)) + return -EFAULT; + + if (__from_user_pp_params( + compat_ptr((uintptr_t) &ov32->overlay_pp_cfg), + &ov->overlay_pp_cfg)) + return -EFAULT; + + if (copy_in_user(&ov->scale, &ov32->scale, + sizeof(struct mdp_scale_data))) + return -EFAULT; + + if (get_user(data, &ov32->frame_rate) || + put_user(data, &ov->frame_rate)) + return -EFAULT; + + return 0; +} + +static int __from_user_mdp_overlaylist(struct mdp_overlay_list *ovlist, + struct mdp_overlay_list32 *ovlist32, + struct mdp_overlay **to_list_head) +{ + __u32 i, ret; + unsigned long data, from_list_head; + struct mdp_overlay32 *iter; + + if (!to_list_head || !ovlist32 || !ovlist) { + pr_err("%s:%u: null error\n", __func__, __LINE__); + return -EINVAL; + } + + if (copy_in_user(&ovlist->num_overlays, &ovlist32->num_overlays, + sizeof(ovlist32->num_overlays))) + return -EFAULT; + + if (copy_in_user(&ovlist->flags, &ovlist32->flags, + sizeof(ovlist32->flags))) + return -EFAULT; + + if (copy_in_user(&ovlist->processed_overlays, + &ovlist32->processed_overlays, + sizeof(ovlist32->processed_overlays))) + return -EFAULT; + + if (get_user(data, &ovlist32->overlay_list)) { + ret = -EFAULT; + goto validate_exit; + } + for (i = 0; i < ovlist32->num_overlays; i++) { + if (get_user(from_list_head, (__u32 *)data + i)) { + ret = -EFAULT; + goto validate_exit; + } + + iter = compat_ptr(from_list_head); + if (__from_user_mdp_overlay(to_list_head[i], + (struct mdp_overlay32 *)(iter))) { + ret = -EFAULT; + goto validate_exit; + } + } + ovlist->overlay_list = to_list_head; + + return 0; + +validate_exit: + pr_err("%s: %u: copy error\n", __func__, __LINE__); + return -EFAULT; +} + +static int __to_user_mdp_overlaylist(struct mdp_overlay_list32 *ovlist32, + struct mdp_overlay_list *ovlist, + struct mdp_overlay **l_ptr) +{ + __u32 i, ret; + unsigned long data, data1; + struct mdp_overlay32 *temp; + struct mdp_overlay *l = l_ptr[0]; + + if (copy_in_user(&ovlist32->num_overlays, &ovlist->num_overlays, + sizeof(ovlist32->num_overlays))) + return -EFAULT; + + if (get_user(data, &ovlist32->overlay_list)) { + ret = -EFAULT; + pr_err("%s:%u: err\n", __func__, __LINE__); + goto validate_exit; + } + + for (i = 0; i < ovlist32->num_overlays; i++) { + if (get_user(data1, (__u32 *)data + i)) { + ret = -EFAULT; + goto validate_exit; + } + temp = compat_ptr(data1); + if (__to_user_mdp_overlay( + (struct mdp_overlay32 *) temp, + l + i)) { + ret = -EFAULT; + goto validate_exit; + } + } + + if (copy_in_user(&ovlist32->flags, &ovlist->flags, + sizeof(ovlist32->flags))) + return -EFAULT; + + if (copy_in_user(&ovlist32->processed_overlays, + &ovlist->processed_overlays, + sizeof(ovlist32->processed_overlays))) + return -EFAULT; + + return 0; + +validate_exit: + pr_err("%s: %u: copy error\n", __func__, __LINE__); + return -EFAULT; + +} + +void mdss_compat_align_list(void __user *total_mem_chunk, + struct mdp_overlay __user **list_ptr, u32 num_ov) +{ + int i = 0; + struct mdp_overlay __user *contig_overlays; + + contig_overlays = total_mem_chunk + sizeof(struct mdp_overlay_list) + + (num_ov * sizeof(struct mdp_overlay *)); + + for (i = 0; i < num_ov; i++) + list_ptr[i] = contig_overlays + i; +} + +static u32 __pp_sspp_size(void) +{ + u32 size = 0; + /* pick the largest of the revision when multiple revs are supported */ + size = sizeof(struct mdp_igc_lut_data_v1_7); + size += sizeof(struct mdp_pa_data_v1_7); + size += sizeof(struct mdp_pcc_data_v1_7); + size += sizeof(struct mdp_hist_lut_data_v1_7); + return size; +} + +static int __pp_sspp_set_offsets(struct mdp_overlay *ov) +{ + if (!ov) { + pr_err("invalid overlay pointer\n"); + return -EFAULT; + } + ov->overlay_pp_cfg.igc_cfg.cfg_payload = (void *)((unsigned long)ov + + sizeof(struct mdp_overlay)); + ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload = + ov->overlay_pp_cfg.igc_cfg.cfg_payload + + sizeof(struct mdp_igc_lut_data_v1_7); + ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload = + ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload + + sizeof(struct mdp_pa_data_v1_7); + ov->overlay_pp_cfg.hist_lut_cfg.cfg_payload = + ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload + + sizeof(struct mdp_pcc_data_v1_7); + return 0; +} + +int mdss_compat_overlay_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg, struct file *file) +{ + struct mdp_overlay *ov, **layers_head; + struct mdp_overlay32 *ov32; + struct mdp_overlay_list __user *ovlist; + struct mdp_overlay_list32 __user *ovlist32; + size_t layers_refs_sz, layers_sz, prepare_sz; + void __user *total_mem_chunk; + uint32_t num_overlays; + uint32_t alloc_size = 0; + int ret; + + if (!info || !info->par) + return -EINVAL; + + + switch (cmd) { + case MSMFB_MDP_PP: + ret = mdss_compat_pp_ioctl(info, cmd, arg, file); + break; + case MSMFB_HISTOGRAM_START: + case MSMFB_HISTOGRAM_STOP: + case MSMFB_HISTOGRAM: + ret = mdss_histo_compat_ioctl(info, cmd, arg, file); + break; + case MSMFB_OVERLAY_GET: + alloc_size += sizeof(*ov) + __pp_sspp_size(); + ov = compat_alloc_user_space(alloc_size); + if (!ov) { + pr_err("%s:%u: compat alloc error [%zu] bytes\n", + __func__, __LINE__, sizeof(*ov)); + return -EINVAL; + } + ov32 = compat_ptr(arg); + ret = __pp_sspp_set_offsets(ov); + if (ret) { + pr_err("setting the pp offsets failed ret %d\n", ret); + return ret; + } + ret = __from_user_mdp_overlay(ov, ov32); + if (ret) + pr_err("%s: compat mdp overlay failed\n", __func__); + else + ret = mdss_fb_do_ioctl(info, cmd, + (unsigned long) ov, file); + ret = __to_user_mdp_overlay(ov32, ov); + break; + case MSMFB_OVERLAY_SET: + alloc_size += sizeof(*ov) + __pp_sspp_size(); + ov = compat_alloc_user_space(alloc_size); + if (!ov) { + pr_err("%s:%u: compat alloc error [%zu] bytes\n", + __func__, __LINE__, sizeof(*ov)); + return -EINVAL; + } + ret = __pp_sspp_set_offsets(ov); + if (ret) { + pr_err("setting the pp offsets failed ret %d\n", ret); + return ret; + } + ov32 = compat_ptr(arg); + ret = __from_user_mdp_overlay(ov, ov32); + if (ret) { + pr_err("%s: compat mdp overlay failed\n", __func__); + } else { + ret = mdss_fb_do_ioctl(info, cmd, + (unsigned long) ov, file); + ret = __to_user_mdp_overlay(ov32, ov); + } + break; + case MSMFB_OVERLAY_PREPARE: + ovlist32 = compat_ptr(arg); + if (get_user(num_overlays, &ovlist32->num_overlays)) { + pr_err("compat mdp prepare failed: invalid arg\n"); + return -EFAULT; + } + + if (num_overlays >= OVERLAY_MAX) { + pr_err("%s: No: of overlays exceeds max\n", __func__); + return -EINVAL; + } + + layers_sz = num_overlays * sizeof(struct mdp_overlay); + prepare_sz = sizeof(struct mdp_overlay_list); + layers_refs_sz = num_overlays * sizeof(struct mdp_overlay *); + + total_mem_chunk = compat_alloc_user_space( + prepare_sz + layers_refs_sz + layers_sz); + if (!total_mem_chunk) { + pr_err("%s:%u: compat alloc error [%zu] bytes\n", + __func__, __LINE__, + layers_refs_sz + layers_sz + prepare_sz); + return -EINVAL; + } + + layers_head = total_mem_chunk + prepare_sz; + mdss_compat_align_list(total_mem_chunk, layers_head, + num_overlays); + ovlist = (struct mdp_overlay_list *)total_mem_chunk; + + ret = __from_user_mdp_overlaylist(ovlist, ovlist32, + layers_head); + if (ret) { + pr_err("compat mdp overlaylist failed\n"); + } else { + ret = mdss_fb_do_ioctl(info, cmd, + (unsigned long) ovlist, file); + if (!ret) + ret = __to_user_mdp_overlaylist(ovlist32, + ovlist, layers_head); + } + break; + case MSMFB_OVERLAY_UNSET: + case MSMFB_OVERLAY_PLAY: + case MSMFB_OVERLAY_VSYNC_CTRL: + case MSMFB_METADATA_SET: + case MSMFB_METADATA_GET: + default: + pr_debug("%s: overlay ioctl cmd=[%u]\n", __func__, cmd); + ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) arg, file); + break; + } + return ret; +} + +/* + * mdss_fb_compat_ioctl() - MDSS Framebuffer compat ioctl function + * @info: pointer to framebuffer info + * @cmd: ioctl command + * @arg: argument to ioctl + * + * This function adds the compat translation layer for framebuffer + * ioctls to allow 32-bit userspace call ioctls on the mdss + * framebuffer device driven in 64-bit kernel. + */ +int mdss_fb_compat_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg, struct file *file) +{ + int ret; + + if (!info || !info->par) + return -EINVAL; + + cmd = __do_compat_ioctl_nr(cmd); + switch (cmd) { + case MSMFB_CURSOR: + ret = mdss_fb_compat_cursor(info, cmd, arg, file); + break; + case MSMFB_SET_LUT: + ret = mdss_fb_compat_set_lut(info, arg, file); + break; + case MSMFB_BUFFER_SYNC: + ret = mdss_fb_compat_buf_sync(info, cmd, arg, file); + break; + case MSMFB_ATOMIC_COMMIT: + ret = __compat_atomic_commit(info, cmd, arg, file); + break; + case MSMFB_ASYNC_POSITION_UPDATE: + ret = __compat_async_position_update(info, cmd, arg); + break; + case MSMFB_MDP_PP: + case MSMFB_HISTOGRAM_START: + case MSMFB_HISTOGRAM_STOP: + case MSMFB_HISTOGRAM: + case MSMFB_OVERLAY_GET: + case MSMFB_OVERLAY_SET: + case MSMFB_OVERLAY_UNSET: + case MSMFB_OVERLAY_PLAY: + case MSMFB_OVERLAY_VSYNC_CTRL: + case MSMFB_METADATA_SET: + case MSMFB_METADATA_GET: + case MSMFB_OVERLAY_PREPARE: + ret = mdss_compat_overlay_ioctl(info, cmd, arg, file); + break; + case MSMFB_NOTIFY_UPDATE: + case MSMFB_DISPLAY_COMMIT: + default: + ret = mdss_fb_do_ioctl(info, cmd, arg, file); + break; + } + + if (ret == -ENOTSUPP) + pr_err("%s: unsupported ioctl\n", __func__); + else if (ret) + pr_debug("%s: ioctl err cmd=%u ret=%d\n", __func__, cmd, ret); + + return ret; +} +EXPORT_SYMBOL(mdss_fb_compat_ioctl); diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..819106b6bf3d4edcf6e986e318d71d9b7d3e1250 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_compat_utils.h @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2014-2016, 2018, 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 MDSS_COMPAT_UTILS_H +#define MDSS_COMPAT_UTILS_H + +/* + * To allow proper structure padding for 64bit/32bit target + */ +#ifndef MDP_LAYER_COMMIT_V1_PAD +#ifdef __LP64 +#define MDP_LAYER_COMMIT_V1_PAD 2 +#else +#define MDP_LAYER_COMMIT_V1_PAD 3 +#endif +#endif + +struct mdp_buf_sync32 { + u32 flags; + u32 acq_fen_fd_cnt; + u32 session_id; + compat_caddr_t acq_fen_fd; + compat_caddr_t rel_fen_fd; + compat_caddr_t retire_fen_fd; +}; + +struct fb_cmap32 { + u32 start; + u32 len; + compat_caddr_t red; + compat_caddr_t green; + compat_caddr_t blue; + compat_caddr_t transp; +}; + +struct fb_image32 { + u32 dx; + u32 dy; + u32 width; + u32 height; + u32 fg_color; + u32 bg_color; + u8 depth; + compat_caddr_t data; + struct fb_cmap32 cmap; +}; + +struct fb_cursor32 { + u16 set; + u16 enable; + u16 rop; + compat_caddr_t mask; + struct fbcurpos hot; + struct fb_image32 image; +}; + +struct mdp_ccs32 { +}; + +struct msmfb_overlay_blt32 { +}; + +struct msmfb_overlay_3d32 { +}; + +struct msmfb_mixer_info_req32 { +}; + +struct msmfb_metadata32 { + uint32_t op; + uint32_t flags; + union { + struct mdp_misr misr_request; + struct mdp_blend_cfg blend_cfg; + struct mdp_mixer_cfg mixer_cfg; + uint32_t panel_frame_rate; + uint32_t video_info_code; + struct mdss_hw_caps caps; + uint8_t secure_en; + } data; +}; + +struct mdp_histogram_start_req32 { + uint32_t block; + uint8_t frame_cnt; + uint8_t bit_mask; + uint16_t num_bins; +}; + +struct mdp_histogram_data32 { + uint32_t block; + uint32_t bin_cnt; + compat_caddr_t c0; + compat_caddr_t c1; + compat_caddr_t c2; + compat_caddr_t extra_info; +}; + +struct mdp_pcc_coeff32 { + uint32_t c, r, g, b, rr, gg, bb, rg, gb, rb, rgb_0, rgb_1; +}; + +struct mdp_pcc_coeff_v1_7_32 { + uint32_t c, r, g, b, rg, gb, rb, rgb; +}; + +struct mdp_pcc_data_v1_7_32 { + struct mdp_pcc_coeff_v1_7_32 r, g, b; +}; +struct mdp_pcc_cfg_data32 { + uint32_t version; + uint32_t block; + uint32_t ops; + struct mdp_pcc_coeff32 r, g, b; + compat_caddr_t cfg_payload; +}; + +struct mdp_csc_cfg32 { + /* flags for enable CSC, toggling RGB,YUV input/output */ + uint32_t flags; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; +}; + +struct mdp_csc_cfg_data32 { + uint32_t block; + struct mdp_csc_cfg32 csc_data; +}; + +struct mdp_bl_scale_data32 { + uint32_t min_lvl; + uint32_t scale; +}; + +struct mdp_pa_mem_col_cfg32 { + uint32_t color_adjust_p0; + uint32_t color_adjust_p1; + uint32_t hue_region; + uint32_t sat_region; + uint32_t val_region; +}; + +struct mdp_pa_v2_data32 { + /* Mask bits for PA features */ + uint32_t flags; + uint32_t global_hue_adj; + uint32_t global_sat_adj; + uint32_t global_val_adj; + uint32_t global_cont_adj; + struct mdp_pa_mem_col_cfg32 skin_cfg; + struct mdp_pa_mem_col_cfg32 sky_cfg; + struct mdp_pa_mem_col_cfg32 fol_cfg; + uint32_t six_zone_len; + uint32_t six_zone_thresh; + compat_caddr_t six_zone_curve_p0; + compat_caddr_t six_zone_curve_p1; +}; + +struct mdp_pa_mem_col_data_v1_7_32 { + uint32_t color_adjust_p0; + uint32_t color_adjust_p1; + uint32_t color_adjust_p2; + uint32_t blend_gain; + uint8_t sat_hold; + uint8_t val_hold; + uint32_t hue_region; + uint32_t sat_region; + uint32_t val_region; +}; + +struct mdp_pa_data_v1_7_32 { + uint32_t mode; + uint32_t global_hue_adj; + uint32_t global_sat_adj; + uint32_t global_val_adj; + uint32_t global_cont_adj; + struct mdp_pa_mem_col_data_v1_7_32 skin_cfg; + struct mdp_pa_mem_col_data_v1_7_32 sky_cfg; + struct mdp_pa_mem_col_data_v1_7_32 fol_cfg; + uint32_t six_zone_thresh; + uint32_t six_zone_adj_p0; + uint32_t six_zone_adj_p1; + uint8_t six_zone_sat_hold; + uint8_t six_zone_val_hold; + uint32_t six_zone_len; + compat_caddr_t six_zone_curve_p0; + compat_caddr_t six_zone_curve_p1; +}; + +struct mdp_pa_v2_cfg_data32 { + uint32_t version; + uint32_t block; + uint32_t flags; + struct mdp_pa_v2_data32 pa_v2_data; + compat_caddr_t cfg_payload; +}; + +struct mdp_pa_cfg32 { + uint32_t flags; + uint32_t hue_adj; + uint32_t sat_adj; + uint32_t val_adj; + uint32_t cont_adj; +}; + +struct mdp_pa_cfg_data32 { + uint32_t block; + struct mdp_pa_cfg32 pa_data; +}; + +struct mdp_igc_lut_data_v1_7_32 { + uint32_t table_fmt; + uint32_t len; + compat_caddr_t c0_c1_data; + compat_caddr_t c2_data; +}; + +struct mdp_rgb_lut_data32 { + uint32_t flags; + uint32_t lut_type; + struct fb_cmap32 cmap; +}; + +struct mdp_igc_lut_data32 { + uint32_t block; + uint32_t version; + uint32_t len, ops; + compat_caddr_t c0_c1_data; + compat_caddr_t c2_data; + compat_caddr_t cfg_payload; +}; + +struct mdp_hist_lut_data_v1_7_32 { + uint32_t len; + compat_caddr_t data; +}; + +struct mdp_hist_lut_data32 { + uint32_t block; + uint32_t version; + uint32_t hist_lut_first; + uint32_t ops; + uint32_t len; + compat_caddr_t data; + compat_caddr_t cfg_payload; +}; + +struct mdp_ar_gc_lut_data32 { + uint32_t x_start; + uint32_t slope; + uint32_t offset; +}; + +struct mdp_pgc_lut_data_v1_7_32 { + uint32_t len; + compat_caddr_t c0_data; + compat_caddr_t c1_data; + compat_caddr_t c2_data; +}; + +struct mdp_pgc_lut_data32 { + uint32_t version; + uint32_t block; + uint32_t flags; + uint8_t num_r_stages; + uint8_t num_g_stages; + uint8_t num_b_stages; + compat_caddr_t r_data; + compat_caddr_t g_data; + compat_caddr_t b_data; + compat_caddr_t cfg_payload; +}; + +struct mdp_lut_cfg_data32 { + uint32_t lut_type; + union { + struct mdp_igc_lut_data32 igc_lut_data; + struct mdp_pgc_lut_data32 pgc_lut_data; + struct mdp_hist_lut_data32 hist_lut_data; + struct mdp_rgb_lut_data32 rgb_lut_data; + } data; +}; + +struct mdp_qseed_cfg32 { + uint32_t table_num; + uint32_t ops; + uint32_t len; + compat_caddr_t data; +}; + +struct mdp_qseed_cfg_data32 { + uint32_t block; + struct mdp_qseed_cfg32 qseed_data; +}; + +struct mdp_dither_cfg_data32 { + uint32_t block; + uint32_t flags; + uint32_t g_y_depth; + uint32_t r_cr_depth; + uint32_t b_cb_depth; +}; + +struct mdp_gamut_data_v1_7_32 { + uint32_t mode; + uint32_t tbl_size[MDP_GAMUT_TABLE_NUM_V1_7]; + compat_caddr_t c0_data[MDP_GAMUT_TABLE_NUM_V1_7]; + compat_caddr_t c1_c2_data[MDP_GAMUT_TABLE_NUM_V1_7]; + uint32_t tbl_scale_off_sz[MDP_GAMUT_SCALE_OFF_TABLE_NUM]; + compat_caddr_t scale_off_data[MDP_GAMUT_SCALE_OFF_TABLE_NUM]; +}; + +struct mdp_gamut_cfg_data32 { + uint32_t block; + uint32_t flags; + uint32_t version; + uint32_t gamut_first; + uint32_t tbl_size[MDP_GAMUT_TABLE_NUM]; + compat_caddr_t r_tbl[MDP_GAMUT_TABLE_NUM]; + compat_caddr_t g_tbl[MDP_GAMUT_TABLE_NUM]; + compat_caddr_t b_tbl[MDP_GAMUT_TABLE_NUM]; + compat_caddr_t cfg_payload; +}; + +struct mdp_calib_config_data32 { + uint32_t ops; + uint32_t addr; + uint32_t data; +}; + +struct mdp_calib_config_buffer32 { + uint32_t ops; + uint32_t size; + compat_caddr_t buffer; +}; + +struct mdp_calib_dcm_state32 { + uint32_t ops; + uint32_t dcm_state; +}; + +struct mdss_ad_init32 { + uint32_t asym_lut[33]; + uint32_t color_corr_lut[33]; + uint8_t i_control[2]; + uint16_t black_lvl; + uint16_t white_lvl; + uint8_t var; + uint8_t limit_ampl; + uint8_t i_dither; + uint8_t slope_max; + uint8_t slope_min; + uint8_t dither_ctl; + uint8_t format; + uint8_t auto_size; + uint16_t frame_w; + uint16_t frame_h; + uint8_t logo_v; + uint8_t logo_h; + uint32_t alpha; + uint32_t alpha_base; + uint32_t bl_lin_len; + uint32_t bl_att_len; + compat_caddr_t bl_lin; + compat_caddr_t bl_lin_inv; + compat_caddr_t bl_att_lut; +}; + +struct mdss_ad_cfg32 { + uint32_t mode; + uint32_t al_calib_lut[33]; + uint16_t backlight_min; + uint16_t backlight_max; + uint16_t backlight_scale; + uint16_t amb_light_min; + uint16_t filter[2]; + uint16_t calib[4]; + uint8_t strength_limit; + uint8_t t_filter_recursion; + uint16_t stab_itr; + uint32_t bl_ctrl_mode; +}; + +/* ops uses standard MDP_PP_* flags */ +struct mdss_ad_init_cfg32 { + uint32_t ops; + union { + struct mdss_ad_init32 init; + struct mdss_ad_cfg32 cfg; + } params; +}; + +struct mdss_ad_input32 { + uint32_t mode; + union { + uint32_t amb_light; + uint32_t strength; + uint32_t calib_bl; + } in; + uint32_t output; +}; + +struct mdss_calib_cfg32 { + uint32_t ops; + uint32_t calib_mask; +}; + +struct mdp_histogram_cfg32 { + uint32_t ops; + uint32_t block; + uint8_t frame_cnt; + uint8_t bit_mask; + uint16_t num_bins; +}; + +struct mdp_sharp_cfg32 { + uint32_t flags; + uint32_t strength; + uint32_t edge_thr; + uint32_t smooth_thr; + uint32_t noise_thr; +}; + +struct mdp_overlay_pp_params32 { + uint32_t config_ops; + struct mdp_csc_cfg32 csc_cfg; + struct mdp_qseed_cfg32 qseed_cfg[2]; + struct mdp_pa_cfg32 pa_cfg; + struct mdp_pa_v2_data32 pa_v2_cfg; + struct mdp_igc_lut_data32 igc_cfg; + struct mdp_sharp_cfg32 sharp_cfg; + struct mdp_histogram_cfg32 hist_cfg; + struct mdp_hist_lut_data32 hist_lut_cfg; + struct mdp_pa_v2_cfg_data32 pa_v2_cfg_data; + struct mdp_pcc_cfg_data32 pcc_cfg_data; +}; + +struct msmfb_mdp_pp32 { + uint32_t op; + union { + struct mdp_pcc_cfg_data32 pcc_cfg_data; + struct mdp_csc_cfg_data32 csc_cfg_data; + struct mdp_lut_cfg_data32 lut_cfg_data; + struct mdp_qseed_cfg_data32 qseed_cfg_data; + struct mdp_bl_scale_data32 bl_scale_data; + struct mdp_pa_cfg_data32 pa_cfg_data; + struct mdp_pa_v2_cfg_data32 pa_v2_cfg_data; + struct mdp_dither_cfg_data32 dither_cfg_data; + struct mdp_gamut_cfg_data32 gamut_cfg_data; + struct mdp_calib_config_data32 calib_cfg; + struct mdss_ad_init_cfg32 ad_init_cfg; + struct mdss_calib_cfg32 mdss_calib_cfg; + struct mdss_ad_input32 ad_input; + struct mdp_calib_config_buffer32 calib_buffer; + struct mdp_calib_dcm_state32 calib_dcm; + } data; +}; + +struct mdp_overlay32 { + struct msmfb_img src; + struct mdp_rect src_rect; + struct mdp_rect dst_rect; + uint32_t z_order; /* stage number */ + uint32_t is_fg; /* control alpha & transp */ + uint32_t alpha; + uint32_t blend_op; + uint32_t transp_mask; + uint32_t flags; + uint32_t pipe_type; + uint32_t id; + uint8_t priority; + uint32_t user_data[6]; + uint32_t bg_color; + uint8_t horz_deci; + uint8_t vert_deci; + struct mdp_overlay_pp_params32 overlay_pp_cfg; + struct mdp_scale_data scale; + uint8_t color_space; + uint32_t frame_rate; +}; + +struct mdp_overlay_list32 { + uint32_t num_overlays; + compat_caddr_t overlay_list; + uint32_t flags; + uint32_t processed_overlays; +}; + +struct mdp_input_layer32 { + uint32_t flags; + uint32_t pipe_ndx; + uint8_t horz_deci; + uint8_t vert_deci; + uint8_t alpha; + uint16_t z_order; + uint32_t transp_mask; + uint32_t bg_color; + enum mdss_mdp_blend_op blend_op; + enum mdp_color_space color_space; + struct mdp_rect src_rect; + struct mdp_rect dst_rect; + compat_caddr_t scale; + struct mdp_layer_buffer buffer; + compat_caddr_t pp_info; + int error_code; + uint32_t reserved[6]; +}; + +struct mdp_output_layer32 { + uint32_t flags; + uint32_t writeback_ndx; + struct mdp_layer_buffer buffer; + enum mdp_color_space color_space; + uint32_t reserved[5]; +}; +struct mdp_layer_commit_v1_32 { + uint32_t flags; + int release_fence; + struct mdp_rect left_roi; + struct mdp_rect right_roi; + compat_caddr_t input_layers; + uint32_t input_layer_cnt; + compat_caddr_t output_layer; + int retire_fence; + compat_caddr_t dest_scaler; + uint32_t dest_scaler_cnt; + compat_caddr_t frc_info; + uint32_t bl_level; /* BL level to be updated in commit */ + uint32_t reserved[MDP_LAYER_COMMIT_V1_PAD]; +}; + +struct mdp_layer_commit32 { + uint32_t version; + union { + struct mdp_layer_commit_v1_32 commit_v1; + }; +}; + +struct mdp_position_update32 { + compat_caddr_t __user *input_layers; + uint32_t input_layer_cnt; +}; + +#endif diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..b920d2e1d7cf7c7e21c7d1a23ec1f05e2ea42d55 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dba_utils.c @@ -0,0 +1,913 @@ +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include