Required properties:
- compatible: Must be either "qcom,cpu-clock-8996" or
"qcom,cpu-clock-8996-v3" or "qcom,cpu-clock-8996-pro"
+ or "qcom,cpu-clock-8996-auto"
- reg: Pairs of physical base addresses and region sizes of
memory mapped registers.
- reg-names: Names of the bases for the above registers. Expected
clock for the CBF.
- cbf-dev: The CBF cache device to which the OPP table for the
CBF clock domain will be added.
+
+Optional properties:
+- qcom,pwrcl-early-boot-freq: Power cluster early boot up frequency in HZ.
+- qcom,perfcl-early-boot-freq: Perf cluster early boot up frequency in HZ.
+
Example:
clock_cpu: qcom,cpu-clock-8996@ {
compatible = "qcom,cpu-clock-8996";
Required Properties:
- compatible: The bus devices need to be compatible with
- "qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom, ext-mdm9x45",
- "qcom,ext-mdm9x55".
+ "qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom,ext-mdm9x45",
+ "qcom,ext-mdm9x55", "qcom,ext-apq8096".
Required named gpio properties:
- qcom,mdm2ap-errfatal-gpio: gpio for the external modem to indicate to the apps processor
on behalf of the subsystem driver.
- qcom,mdm-link-info: a string indicating additional info about the physical link.
For example: "devID_domain.bus.slot" in case of PCIe.
+- qcom,mdm-auto-boot: Boolean. To indicate this instance of esoc boots independently.
+- qcom,mdm-statusline-not-a-powersource: Boolean. If set, status line to esoc device is not a
+ power source.
+- qcom,mdm-userspace-handle-shutdown: Boolean. If set, userspace handles shutdown requests.
Example:
mdm0: qcom,mdm0 {
compatible = "qcom,apq8094-liquid"
compatible = "qcom,apq8094-mtp"
compatible = "qcom,apq8094-dragonboard"
+compatible = "qcom,apq8096-adp"
compatible = "qcom,apq8096-cdp"
compatible = "qcom,apq8096-mtp"
compatible = "qcom,apq8096-dragonboard"
--- /dev/null
+* MSM 64bit L0, L1, L2 and L3 cache error reporting driver
+
+Required properties:
+- compatible: Should be "qcom,kryo_cache_erp64"
+- reg: I/O address L3 hardware block.
+- interrupts: Should contain the L0/L1, L2 and L3 cache error interrupt number.
+- interrupt-names: Should contain the interrupt names "l1_irq", "l2_irq_info_0",
+ "l2_irq_info_1", "l2_irq_err_0", "l2_irq_err_1", "l3_irq".
+
+Example:
+ qcom,cache_erp64@6500000 {
+ compatible = "qcom,kryo_cache_erp64";
+ reg = <0x6500000 0x4000>;
+ /*
+ * PPI 0 for L0/L1
+ * SPI 1 for Cluster 1 L2 Info
+ * SPI 9 for Cluster 2 L2 Info
+ * SPI 2 for Cluster 1 L2 Error
+ * SPI 10 for Cluster 2 L2 Error
+ * SPI 17 for L3 error
+ */
+ interrupts = <1 0 0>, <0 1 0>, <0 9 0>, <0 2 0>, <0 10 0>,
+ <0 17 0>;
+ interrupt-names = "l1_irq", "l2_irq_info_0", "l2_irq_info_1",
+ "l2_irq_err_0", "l2_irq_err_1", "l3_irq";
+ };
--- /dev/null
+* MSM M4M error reporting driver
+
+Required properties:
+- compatible: Should be "qcom,m4m_erp".
+- reg: I/O address M4M hardware block.
+- interrupts: Should contain the M4M error interrupt number.
+- interrupt-names: Should contain the interrupt names "m4m_irq".
+
+Example:
+ qcom,m4m_erp64@9A40000 {
+ compatible = "qcom,m4m_erp";
+ reg = <0x9A40000 0x40000>;
+ interrupts = <0 22 0>;
+ interrupt-names = "m4m_irq";
+ };
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.
--- /dev/null
+* Neutrino Ethernet *
+This driver implements Ethernet driver for Neutrino ethernet controller
+Required properties:
+ - compatible: Should be "qcom,ntn_avb"
+ - ntn-rst-gpio: Neutrino reset GPIO
+ - vdd-ntn-hsic-supply: neutrino HSIC power supply
+ - vdd-ntn-pci-supply: PCIe core power supply
+ - vdd-ntn-io-supply: Neutrino IO power supply
+ - vdd-ntn-phy-supply: Ethernet Phy power supply
+ - vdd-ntn-core-supply: Neutrino core power supply
+ - pinctrl-names: should contain GPIO details
+ - pinctrl-0: Neutrino reset GPIO [this is from MSM]
+ - ntn-rst-delay-msec: dealy (msec) required after PCIe reset for stabilization
+ - ntn-rc-num: PCIe root complex number on which Neutrino is connected
+Example:
+ qcom,ntn_avb {
+ compatible = "qcom,ntn_avb";
+ ntn-rst-gpio = <&pm8994_gpios 13 0>;
+ vdd-ntn-hsic-supply = <&pm8994_l25>;
+ vdd-ntn-pci-supply = <&pm8994_s4>;
+ vdd-ntn-io-supply = <&pm8994_s4>;
+ vdd-ntn-phy-supply = <&pm8994_l9>;
+ vdd-ntn-core-supply = <&pm8994_l19>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&ntn_default>;
+ qcom,ntn-rst-delay-msec = <100>;
+ qcom,ntn-rc-num = <1>;
+ };
- qcom,complete-ramdump: Boolean. If set, complete ramdump i.e. region between start address of
first segment to end address of last segment will be collected without
leaving any hole in between.
+- qcom,ignore-ssr-failure: Boolean. If set, SSR failures are not considered fatal.
Example:
qcom,venus@fdce0000 {
Definition: String which indicates the charging mode. Can be one of the
following:
Standalone/Parallel Master - "qcom,smb138x-charger"
- smb138x Parallel Slave - "qcom,smb138x-parallel-slave"
- smb1355 Parallel Slave - "qcom,smb1355-parallel-slave",
+ Parallel Slave - "qcom,smb138x-parallel-slave"
- qcom,pmic-revid
Usage: required
Usage: optional
Value type: <u32>
Definition: Specifies parallel charging mode. If not specified, MID-MID
- option is selected by default. Note that smb1355 can only
- run in MID-MID configuration.
+ option is selected by default.
- qcom,suspend-input
Usage: optional
=======
smb138x_charger: qcom,smb138x-charger {
- compatible = "qcom,smb138x-charger";
+ compatible = "qcom,qpnp-smb138x-charger";
#address-cells = <1>;
#size-cells = <1>;
--- /dev/null
+ON Semiconductor NCP6335d regulator
+
+NCP6335d is able to deliver up to 5.0 A, with programmable output voltage from
+0.6 V to 1.87 V in 10mV steps, with synchronous rectification and automatic PWM/
+PFM transitions, enable pins and power good/fail signaling.
+
+The NCP6335d interface is via I2C bus.
+
+Required Properties:
+- compatible: Must be "onnn,ncp6335d-regulator".
+- reg: The device 8-bit I2C address.
+- regulator-min-microvolt: Minimum voltage in microvolts supported by this
+ regulator.
+- regulator-max-microvolt: Maximum voltage in microvolts supported by this
+ regulator.
+- onnn,min-setpoint: Minimum setpoint voltage in microvolts supported
+ by this regulator.
+- onnn,step-size: The step size of the regulator, in uV.
+- onnn,min-slew-ns: Minimum time in ns needed to change voltage by
+ one step size. This value corresponds to DVS
+ mode bit of 00b in command register.
+- onnn,max-slew-ns: Maximum time in ns needed to change voltage by
+ one step size. This value corresponds to DVS
+ mode bit of 11b in command register.
+- onnn,vsel: Working vsel register. Supported value are 0
+ or 1.
+- onnn,slew-ns: Time in ns needed to change voltage by one step
+ size. Supported value are 333, 666, 1333, 2666.
+
+Optional Properties:
+- onnn,discharge-enable: Present: discharge enabled.
+ Not Present: discharge disabled.
+- onnn,restore-reg: Present: Restore vsel register from backup register.
+ Not Present: No restore.
+- onnn,vsel-gpio: Present: GPIO connects to the VSEL pin and set the
+ VSEL pin according to device tree flag.
+ Not Present: No GPIO is connected to vsel pin.
+- pinctrl-names: The state name of the VSEL pin configuration.
+ Only support: "default"
+- pinctrl-0: The phandles of the pin configuration node in
+ pinctrl for VSEL pin.
+ For details of pinctrl properties, please refer to:
+ "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
+- onnn,sleep-enable: Present: Forced in sleep mode when EN and VSEL
+ pins are low.
+ Not Present: Low quiescent current mode when EN and VSEL
+ pins are low.
+- onnn,mode: A string which specifies the initial mode to use for the regulator.
+ Supported values are "pwm" and "auto". PWM mode is more
+ robust, but draws more current than auto mode. If this propery
+ is not specified, then the regulator will be in the hardware default mode.
+
+Example:
+ i2c_0 {
+ ncp6335d-regulator@1c {
+ compatible = "onnn,ncp6335d-regulator";
+ reg = <0x1c>;
+ onnn,vsel = <0>;
+ onnn,slew-rate-ns = <2666>;
+ onnn,discharge-enable;
+ onnn,step-size = <10000>;
+ onnn,min-slew-ns = <333>;
+ onnn,max-slew-ns = <2666>;
+ pintrl-names = "default";
+ pinctrl-0 = <&ext_buck_vsel_default>;
+
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1350000>;
+ onnn,min-setpoint = <600000>;
+ onnn,vsel-gpio = <&msmgpio 2 1>;
+ };
+ };
==============================================================
+mount-max:
+
+This denotes the maximum number of mounts that may exist
+in a mount namespace.
+
+==============================================================
+
2. /proc/sys/fs/binfmt_misc
----------------------------------------------------------
VERSION = 4
PATCHLEVEL = 4
-SUBLEVEL = 59
+SUBLEVEL = 65
EXTRAVERSION =
NAME = Blurry Fish Butt
$(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE
$(call if_changed,cat)
- @echo ' Kernel: $@ is ready'
+ @$(kecho) ' Kernel: $@ is ready'
endif
msm8996-v2-liquid.dtb \
msm8996-v2-dtp.dtb \
msm8996-v3-auto-cdp.dtb \
+ msm8996-v3-auto-adp.dtb \
msm8996-v3-pmi8994-cdp.dtb \
msm8996-v3-pmi8994-mtp.dtb \
msm8996-v3-pmi8994-pmk8001-cdp.dtb \
msm8996-v3-dtp.dtb \
msm8996-v3-pm8004-mmxf-adp.dtb \
msm8996-v3-pm8004-agave-adp.dtb \
+ msm8996-v3-pm8004-agave-adp-lite.dtb \
+ msm8996pro-auto-adp.dtb \
+ msm8996pro-auto-adp-lite.dtb \
+ msm8996pro-auto-cdp.dtb \
msm8996pro-pmi8994-cdp.dtb \
msm8996pro-pmi8994-mtp.dtb \
msm8996pro-pmi8994-pmk8001-cdp.dtb \
msm8996pro-pmi8996-mtp.dtb \
msm8996pro-pmi8996-pmk8001-cdp.dtb \
msm8996pro-pmi8996-pmk8001-mtp.dtb \
+ msm8996pro-v1.1-auto-cdp.dtb \
msm8996pro-v1.1-pmi8994-cdp.dtb \
msm8996pro-v1.1-pmi8994-mtp.dtb \
msm8996pro-v1.1-pmi8994-pmk8001-cdp.dtb \
msm8996pro-v1.1-pmi8996-mtp.dtb \
msm8996pro-v1.1-pmi8996-pmk8001-cdp.dtb \
msm8996pro-v1.1-pmi8996-pmk8001-mtp.dtb \
+ apq8096pro-auto-cdp.dtb \
+ apq8096pro-v1.1-auto-adp.dtb \
+ apq8096pro-v1.1-auto-adp-lite.dtb \
apq8096pro-liquid.dtb \
+ apq8096pro-v1.1-auto-cdp.dtb \
msm8996-v3.0-pmi8994-cdp.dtb \
msm8996-v3.0-pmi8994-mtp.dtb \
msm8996-v3.0-pmi8994-pm8004-cdp.dtb \
apq8096-v3-dragonboard.dtb \
apq8096-v3-sbc.dtb \
apq8096-v3-auto-dragonboard.dtb \
+ apq8096-v3-auto-adp.dtb \
+ apq8096-v3-auto-cdp.dtb \
apq8096-v3.0-pmi8994-cdp.dtb \
apq8096-v3.0-pmi8994-mtp.dtb \
apq8096-v3.0-pmi8994-pm8004-cdp.dtb \
};
&soc {
+ qcom,ntn_avb {
+ compatible = "qcom,ntn_avb";
+
+ ntn-rst-gpio = <&pm8994_gpios 13 0>;
+
+ vdd-ntn-hsic-supply = <&pm8994_l25>;
+ vdd-ntn-pci-supply = <&pm8994_s4>;
+ vdd-ntn-io-supply = <&pm8994_s4>;
+
+ qcom,ntn-rst-delay-msec = <100>;
+ qcom,ntn-rc-num = <1>;
+ };
+
qcom,cnss {
wlan-bootstrap-gpio = <&tlmm 46 0>;
wlan-en-gpio = <&pm8994_gpios 8 0>;
status = "okay";
};
- gpio@cc00 { /* GPIO 13 */
- qcom,mode = <1>; /* DIGITAL OUT */
- qcom,vin-sel = <2>; /* 1.8 */
- qcom,src-sel = <0>; /* GPIO */
- qcom,master-en = <1>; /* Enable GPIO */
+ gpio@cc00 { /* GPIO 13 - NTN_RST */
+ qcom,mode = <1>; /* DIGITAL OUT */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,pull = <5>;
+ qcom,vin-sel = <2>; /* 1.8 */
+ qcom,out-strength = <1>;
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,master-en = <1>; /* Enable GPIO */
status = "okay";
};
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 <dt-bindings/gpio/gpio.h>
+#include "apq8096-v3.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-agave-adp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ 8096 v3 AUTO ADP";
+ compatible = "qcom,apq8096-adp", "qcom,apq8096", "qcom,adp";
+ qcom,msm-id = <311 0x30001>;
+ qcom,board-id = <0x02010019 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <122 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
+&soc {
+ qcom,msm-ssc-sensors {
+ status = "disabled";
+ };
+
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+};
+
+&slim_msm {
+ status = "disabled";
+};
+
+&pm8994_mpps {
+ mpp@a500 { /* MPP 6 */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,vin-sel = <2>; /* S4 1.8V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
+
+&hl7509_en_vreg {
+ status = "ok";
+};
+
+&hl7509_vreg {
+ status = "ok";
+};
+
+&sdhc_2 {
+ cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_cd_on_sbc>;
+};
+
+&i2c_7 {
+ silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/
+ status = "disabled";
+ };
+};
+
+&gfx_cpr {
+ vdd-supply = <&hl7509_vreg>;
+ qcom,cpr-step-quot-init-min = <20>;
+ qcom,cpr-step-quot-init-max = <26>;
+ qcom,voltage-step = <10000>;
+ /delete-property/ qcom,cpr-enable;
+};
+
+&gfx_vreg {
+ qcom,cpr-voltage-ceiling =
+ <600000 670000 670000 750000 830000
+ 910000 960000 1020000>;
+ qcom,cpr-voltage-floor =
+ <600000 600000 600000 600000 600000
+ 600000 600000 600000>;
+};
+
+&pm8994_l3 {
+ regulator-min-microvolt = <875000>;
+ regulator-max-microvolt = <875000>;
+ qcom,init-voltage = <875000>;
+};
+
+&pm8994_l11 {
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ qcom,init-voltage = <850000>;
+};
+
+&pm8994_l17 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+};
+
+&pm8994_l23 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+};
+
+&pm8994_l27 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <800000>;
+ qcom,init-voltage = <800000>;
+};
+
+&pm8994_l29 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ qcom,init-voltage = <2500000>;
+};
+
+&rpm_bus {
+ rpm-regulator-ldoa26 {
+ /delete-node/ pm8994_l26_corner;
+ /delete-node/ pm8994_l26_floor_corner;
+
+ pm8994_l26: regulator-l26 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa31 {
+ status = "okay";
+ pm8994_l31: regulator-l31 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+ status = "okay";
+ };
+ };
+};
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 "apq8096-v3.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-auto-cdp.dtsi"
+#include "msm8996v3-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ 8096 v3 AUTO CDP";
+ compatible = "qcom,apq8096-cdp", "qcom,apq8096", "qcom,cdp";
+ qcom,msm-id = <311 0x30001>;
+ qcom,board-id = <0x03010001 0>;
+};
+
+&soc {
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <127 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 "apq8096pro.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-auto-cdp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ 8096 pro AUTO CDP";
+ compatible = "qcom,apq8096-cdp", "qcom,apq8096", "qcom,cdp";
+ qcom,msm-id = <316 0x10000>;
+ qcom,board-id = <0x03010001 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <127 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
+&soc {
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+};
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 <dt-bindings/gpio/gpio.h>
+#include "apq8096pro.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-agave-adp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ 8096pro V1.1 AUTO ADP LITE";
+ compatible = "qcom,apq8096-adp", "qcom,msm8996", "qcom,adp";
+ qcom,msm-id = <316 0x10001>;
+ qcom,board-id = <0x03010019 0>;
+};
+
+&spi_9 {
+ status = "disabled";
+};
+
+&soc {
+ qcom,msm-ssc-sensors {
+ status = "disabled";
+ };
+
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+
+ i2c@75b6000 { /* BLSP8 */
+ /* ADV7533 HDMI Bridge Chip removed on ADP Lite */
+ adv7533@3d {
+ status = "disabled";
+ };
+ adv7533@39 {
+ status = "disabled";
+ };
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
+&slim_msm {
+ status = "disabled";
+};
+
+&pm8994_mpps {
+ mpp@a500 { /* MPP 6 */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,vin-sel = <2>; /* S4 1.8V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
+
+&sdhc_2 {
+ cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_cd_on_sbc>;
+};
+
+&i2c_7 {
+ silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/
+ status = "disabled";
+ };
+};
+
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 <dt-bindings/gpio/gpio.h>
+#include "apq8096pro-v1.1.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-agave-adp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO ADP";
+ compatible = "qcom,apq8096-adp", "qcom,msm8996", "qcom,adp";
+ qcom,msm-id = <316 0x10001>;
+ qcom,board-id = <0x02010019 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <122 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
+&soc {
+ qcom,msm-ssc-sensors {
+ status = "disabled";
+ };
+
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+
+ qcom,adv7481@70 {
+ qcom,cam-vreg-min-voltage = <1300000 0 1800000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 1800000>;
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
+&slim_msm {
+ status = "disabled";
+};
+
+&pm8994_mpps {
+ mpp@a500 { /* MPP 6 */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,vin-sel = <2>; /* S4 1.8V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
+
+&sdhc_2 {
+ cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_cd_on_sbc>;
+};
+
+&i2c_7 {
+ silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/
+ status = "disabled";
+ };
+};
+
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 "apq8096pro-v1.1.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-auto-cdp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ 8096 pro v1.1 AUTO CDP";
+ compatible = "qcom,msm8996-cdp", "qcom,msm8996", "qcom,cdp";
+ qcom,msm-id = <316 0x10001>;
+ qcom,board-id = <0x03010001 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <127 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
--- /dev/null
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. Common device definitions should be placed inside the
+ * msm8996.dtsi file.
+ */
+
+#include "msm8996pro-v1.1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ 8096 pro v1.1";
+ qcom,msm-id = <312 0x10001>;
+};
+
+&soc {
+ qcom,rmnet-ipa {
+ status = "disabled";
+ };
+};
+
+&ipa_hw {
+ status = "disabled";
+};
/dts-v1/;
#include "apq8098.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
/dts-v1/;
#include "apq8098.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
/dts-v1/;
#include "apq8098-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
/dts-v1/;
#include "apq8098-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
/dts-v1/;
#include "apq8098-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd-skuk-hdk.dtsi"
/ {
/dts-v1/;
#include "apq8098-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd.dtsi"
/ {
/dts-v1/;
#include "apq8098-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
/dts-v1/;
#include "apq8098-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
qcom,mdss-pref-prim-intf = "hdmi";
};
-&msm_gpu {
- dma-coherent;
-};
-
&sde_hdmi {
qcom,display-type = "primary";
};
/dts-v1/;
#include "apq8098-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
/dts-v1/;
#include "apq8098-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd.dtsi"
/ {
15 01 00 00 00 00 02 0b 55
15 01 00 00 00 00 02 0c 14
15 01 00 00 00 00 02 0d 28
- 15 01 00 00 00 00 02 0e 00
- 15 01 00 00 00 00 02 0f 00
+ 15 01 00 00 00 00 02 0e 40
+ 15 01 00 00 00 00 02 0f 80
15 01 00 00 00 00 02 10 00
15 01 00 00 00 00 02 11 22
15 01 00 00 00 00 02 12 0a
15 01 00 00 00 00 02 1d 00
15 01 00 00 00 00 02 1e 80
15 01 00 00 00 00 02 1f 00
- 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 20 03
15 01 00 00 00 00 02 21 03
- 15 01 00 00 00 00 02 22 22
+ 15 01 00 00 00 00 02 22 25
15 01 00 00 00 00 02 23 25
15 01 00 00 00 00 02 24 00
15 01 00 00 00 00 02 25 a7
- 15 01 00 00 00 00 02 26 00
+ 15 01 00 00 00 00 02 26 80
15 01 00 00 00 00 02 27 a5
15 01 00 00 00 00 02 28 06
15 01 00 00 00 00 02 29 85
- 15 01 00 00 00 00 02 2a 3f
+ 15 01 00 00 00 00 02 2a 30
15 01 00 00 00 00 02 2b 97
15 01 00 00 00 00 02 2f 25
15 01 00 00 00 00 02 30 26
15 01 00 00 00 00 02 45 00
15 01 00 00 00 00 02 46 00
15 01 00 00 00 00 02 47 00
- 15 01 00 00 00 00 02 48 00
+ 15 01 00 00 00 00 02 48 03
15 01 00 00 00 00 02 49 03
15 01 00 00 00 00 02 4a 00
15 01 00 00 00 00 02 4b 00
15 01 00 00 00 00 02 4f 4c
15 01 00 00 00 00 02 50 0d
15 01 00 00 00 00 02 51 0e
- 15 01 00 00 00 00 02 52 23
+ 15 01 00 00 00 00 02 52 20
15 01 00 00 00 00 02 53 97
15 01 00 00 00 00 02 54 4b
15 01 00 00 00 00 02 55 4c
qcom,mdss-dsi-color-order = "rgb_swap_rgb";
qcom,mdss-dsi-on-command = [
15 01 00 00 00 00 02 fe 0d
+ 15 01 00 00 00 00 02 0b c0
15 01 00 00 00 00 02 42 00
15 01 00 00 00 00 02 18 08
15 01 00 00 00 00 02 08 41
--- /dev/null
+/* 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.
+ */
+
+&soc {
+ mdm0: qcom,mdm0 {
+ compatible = "qcom,ext-mdm9x45";
+ cell-index = <0>;
+ #address-cells = <0>;
+ interrupt-parent = <&mdm0>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-names =
+ "err_fatal_irq",
+ "status_irq";
+ status = "disabled";
+ };
+
+ mdm1: qcom,mdm1 {
+ compatible = "qcom,ext-mdm9x45";
+ cell-index = <0>;
+ #address-cells = <0>;
+ interrupt-parent = <&mdm1>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-names =
+ "err_fatal_irq",
+ "status_irq";
+ status = "disabled";
+ };
+
+ mdm2: qcom,mdm2 {
+ compatible = "qcom,ext-mdm9x45";
+ cell-index = <0>;
+ #address-cells = <0>;
+ interrupt-parent = <&mdm2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-names =
+ "err_fatal_irq",
+ "status_irq";
+ status = "disabled";
+ };
+};
*/
iommus = <&mmss_smmu 42>;
};
+
+ iommu_coherent_test_device {
+ compatible = "iommu-debug-test";
+ /*
+ * 43 shouldn't be used by anyone on the mmss_smmu. 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 = <&mmss_smmu 43>;
+ dma-coherent;
+ };
};
status = "ok";
};
+&pcie0 {
+ qcom,phy-sequence = <0x404 0x01 0x00
+ 0x034 0x1c 0x00
+ 0x038 0x10 0x00
+ 0x174 0x33 0x00
+ 0x194 0x06 0x00
+ 0x0c8 0x42 0x00
+ 0x128 0x00 0x00
+ 0x144 0xff 0x00
+ 0x148 0x1f 0x00
+ 0x178 0x01 0x00
+ 0x19c 0x01 0x00
+ 0x18c 0x00 0x00
+ 0x184 0x0a 0x00
+ 0x00c 0x09 0x00
+ 0x0d0 0x82 0x00
+ 0x0e4 0x03 0x00
+ 0x0e0 0x55 0x00
+ 0x0dc 0x55 0x00
+ 0x054 0x00 0x00
+ 0x050 0x1a 0x00
+ 0x04c 0x0a 0x00
+ 0x174 0x33 0x00
+ 0x03c 0x02 0x00
+ 0x040 0x1f 0x00
+ 0x0ac 0x04 0x00
+ 0x078 0x0b 0x00
+ 0x084 0x16 0x00
+ 0x090 0x28 0x00
+ 0x10c 0x00 0x00
+ 0x108 0x80 0x00
+ 0x010 0x01 0x00
+ 0x01c 0x31 0x00
+ 0x020 0x01 0x00
+ 0x014 0x02 0x00
+ 0x018 0x00 0x00
+ 0x024 0x2f 0x00
+ 0x028 0x19 0x00
+ 0x0c4 0x15 0x00
+ 0x070 0x0f 0x00
+ 0x048 0x0f 0x00
+ 0x074 0x19 0x00
+ 0x038 0x10 0x00
+ 0x178 0x00 0x00
+ 0x0c4 0x40 0x00
+ 0x400 0x00 0x00
+ 0x408 0x03 0x00>;
+
+ qcom,port-phy-sequence = <0x1068 0x45 0x00
+ 0x1094 0x06 0x00
+ 0x1310 0x1c 0x00
+ 0x1318 0x17 0x00
+ 0x12d8 0x01 0x00
+ 0x12dc 0x00 0x00
+ 0x12e0 0xdb 0x00
+ 0x1320 0x18 0x00
+ 0x121c 0x04 0x00
+ 0x1210 0x04 0x00
+ 0x1458 0x4c 0x00
+ 0x14a0 0x00 0x00
+ 0x14a4 0x01 0x00
+ 0x14a8 0x05 0x00
+ 0x1248 0x4b 0x00
+ 0x131c 0x14 0x00
+ 0x1454 0x05 0x00
+ 0x1404 0x02 0x00
+ 0x146c 0x00 0x00
+ 0x1460 0xa3 0x00
+ 0x1318 0x19 0x00
+ 0x1428 0x0e 0x00
+ 0x1054 0x08 0x00
+ 0x14f8 0x04 0x00
+ 0x14ec 0x06 0x00
+ 0x104c 0x2e 0x00
+ 0x1404 0x03 0x0a
+ 0x1400 0x00 0x00
+ 0x1408 0x0a 0x00>;
+
+ /delete-property/ qcom,l1-supported;
+ /delete-property/ qcom,l1ss-supported;
+ /delete-property/ qcom,aux-clk-sync;
+};
+
+&pcie1 {
+ qcom,phy-sequence = <0x404 0x01 0x00
+ 0x034 0x1c 0x00
+ 0x038 0x10 0x00
+ 0x174 0x33 0x00
+ 0x194 0x06 0x00
+ 0x0c8 0x42 0x00
+ 0x128 0x00 0x00
+ 0x144 0xff 0x00
+ 0x148 0x1f 0x00
+ 0x178 0x01 0x00
+ 0x19c 0x01 0x00
+ 0x18c 0x00 0x00
+ 0x184 0x0a 0x00
+ 0x00c 0x09 0x00
+ 0x0d0 0x82 0x00
+ 0x0e4 0x03 0x00
+ 0x0e0 0x55 0x00
+ 0x0dc 0x55 0x00
+ 0x054 0x00 0x00
+ 0x050 0x1a 0x00
+ 0x04c 0x0a 0x00
+ 0x174 0x33 0x00
+ 0x03c 0x02 0x00
+ 0x040 0x1f 0x00
+ 0x0ac 0x04 0x00
+ 0x078 0x0b 0x00
+ 0x084 0x16 0x00
+ 0x090 0x28 0x00
+ 0x10c 0x00 0x00
+ 0x108 0x80 0x00
+ 0x010 0x01 0x00
+ 0x01c 0x31 0x00
+ 0x020 0x01 0x00
+ 0x014 0x02 0x00
+ 0x018 0x00 0x00
+ 0x024 0x2f 0x00
+ 0x028 0x19 0x00
+ 0x0c4 0x15 0x00
+ 0x070 0x0f 0x00
+ 0x048 0x0f 0x00
+ 0x074 0x19 0x00
+ 0x038 0x10 0x00
+ 0x178 0x00 0x00
+ 0x0c4 0x40 0x00
+ 0x400 0x00 0x00
+ 0x408 0x03 0x00>;
+
+ qcom,port-phy-sequence = <0x2068 0x45 0x00
+ 0x2094 0x06 0x00
+ 0x2310 0x1c 0x00
+ 0x2318 0x17 0x00
+ 0x22d8 0x01 0x00
+ 0x22dc 0x00 0x00
+ 0x22e0 0xdb 0x00
+ 0x2320 0x18 0x00
+ 0x221c 0x04 0x00
+ 0x2210 0x04 0x00
+ 0x2458 0x4c 0x00
+ 0x24a0 0x00 0x00
+ 0x24a4 0x01 0x00
+ 0x24a8 0x05 0x00
+ 0x2248 0x4b 0x00
+ 0x231c 0x14 0x00
+ 0x2454 0x05 0x00
+ 0x2404 0x02 0x00
+ 0x246c 0x00 0x00
+ 0x2460 0xa3 0x00
+ 0x2318 0x19 0x00
+ 0x2428 0x0e 0x00
+ 0x2054 0x08 0x00
+ 0x24f8 0x04 0x00
+ 0x24ec 0x06 0x00
+ 0x204c 0x2e 0x00
+ 0x2404 0x03 0x0a
+ 0x2400 0x00 0x00
+ 0x2408 0x0a 0x00>;
+
+ qcom,msi-gicm-addr = <0x09BD0040>;
+ qcom,msi-gicm-base = <0x240>;
+
+ /delete-property/ qcom,l1-supported;
+ /delete-property/ qcom,l1ss-supported;
+ /delete-property/ qcom,aux-clk-sync;
+};
+
+&pcie2 {
+ qcom,phy-sequence = <0x404 0x01 0x00
+ 0x034 0x1c 0x00
+ 0x038 0x10 0x00
+ 0x174 0x33 0x00
+ 0x194 0x06 0x00
+ 0x0c8 0x42 0x00
+ 0x128 0x00 0x00
+ 0x144 0xff 0x00
+ 0x148 0x1f 0x00
+ 0x178 0x01 0x00
+ 0x19c 0x01 0x00
+ 0x18c 0x00 0x00
+ 0x184 0x0a 0x00
+ 0x00c 0x09 0x00
+ 0x0d0 0x82 0x00
+ 0x0e4 0x03 0x00
+ 0x0e0 0x55 0x00
+ 0x0dc 0x55 0x00
+ 0x054 0x00 0x00
+ 0x050 0x1a 0x00
+ 0x04c 0x0a 0x00
+ 0x174 0x33 0x00
+ 0x03c 0x02 0x00
+ 0x040 0x1f 0x00
+ 0x0ac 0x04 0x00
+ 0x078 0x0b 0x00
+ 0x084 0x16 0x00
+ 0x090 0x28 0x00
+ 0x10c 0x00 0x00
+ 0x108 0x80 0x00
+ 0x010 0x01 0x00
+ 0x01c 0x31 0x00
+ 0x020 0x01 0x00
+ 0x014 0x02 0x00
+ 0x018 0x00 0x00
+ 0x024 0x2f 0x00
+ 0x028 0x19 0x00
+ 0x0c4 0x15 0x00
+ 0x070 0x0f 0x00
+ 0x048 0x0f 0x00
+ 0x074 0x19 0x00
+ 0x038 0x10 0x00
+ 0x178 0x00 0x00
+ 0x0c4 0x40 0x00
+ 0x400 0x00 0x00
+ 0x408 0x03 0x00>;
+
+ qcom,port-phy-sequence = <0x3068 0x45 0x00
+ 0x3094 0x06 0x00
+ 0x3310 0x1c 0x00
+ 0x3318 0x17 0x00
+ 0x32d8 0x01 0x00
+ 0x32dc 0x00 0x00
+ 0x32e0 0xdb 0x00
+ 0x3320 0x18 0x00
+ 0x321c 0x04 0x00
+ 0x3210 0x04 0x00
+ 0x3458 0x4c 0x00
+ 0x34a0 0x00 0x00
+ 0x34a4 0x01 0x00
+ 0x34a8 0x05 0x00
+ 0x3248 0x4b 0x00
+ 0x331c 0x14 0x00
+ 0x3454 0x05 0x00
+ 0x3404 0x02 0x00
+ 0x346c 0x00 0x00
+ 0x3460 0xa3 0x00
+ 0x3318 0x19 0x00
+ 0x3428 0x0e 0x00
+ 0x3054 0x08 0x00
+ 0x34f8 0x04 0x00
+ 0x34ec 0x06 0x00
+ 0x304c 0x2e 0x00
+ 0x3404 0x03 0x0a
+ 0x3400 0x00 0x00
+ 0x3408 0x0a 0x00>;
+
+ /delete-property/ qcom,l1-supported;
+ /delete-property/ qcom,l1ss-supported;
+ /delete-property/ qcom,aux-clk-sync;
+};
+
&uartblsp2dm1 {
status = "ok";
pinctrl-names = "default";
&mdss_hdmi_cec_suspend>;
};
-#include "msm8996-mdss-panels.dtsi"
+#include "msm8996-sde-display.dtsi"
&mdss_mdp {
qcom,mdss-pref-prim-intf = "dsi";
};
+&dsi_adv_7533_1 {
+ qcom,dsi-display-active;
+ qcom,dsi-panel = <&dsi_adv7533_1080p>;
+
+ qcom,panel-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <3300000>;
+ qcom,supply-max-voltage = <3300000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+};
+
+&dsi_adv_7533_2 {
+ qcom,dsi-display-active;
+ qcom,dsi-panel = <&dsi_adv7533_1080p>;
+
+ qcom,panel-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <3300000>;
+ qcom,supply-max-voltage = <3300000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+};
+
&mdss_dsi {
hw-config = "dual_dsi";
};
};
&soc {
+ qcom,ntn_avb {
+ compatible = "qcom,ntn_avb";
+
+ ntn-rst-gpio = <&pm8994_gpios 13 0>;
+
+ vdd-ntn-hsic-supply = <&pm8994_l25>;
+ vdd-ntn-pci-supply = <&pm8994_s4>;
+ vdd-ntn-io-supply = <&pm8994_s4>;
+
+ qcom,ntn-rst-delay-msec = <100>;
+ qcom,ntn-rc-num = <1>;
+ };
+
i2c@75ba000 {
synaptics@20 {
compatible = "synaptics,dsx";
clock-names = "iface_clk", "core_clk";
clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>,
<&clock_gcc clk_gcc_blsp2_qup6_i2c_apps_clk>;
+ status = "disabled";
};
};
asoc-codec-names = "msm-stub-codec.1";
};
- usb_detect {
+ usb_detect: usb_detect {
compatible = "qcom,gpio-usbdetect";
- qcom,vbus-det-irq = <&pm8994_gpios 17 0>;
+ qcom,vbus-det-gpio = <&pm8994_gpios 17 0>;
+ interrupt-parent = <&spmi_bus>;
+ interrupts = <0x0 0x9 0x0 IRQ_TYPE_NONE>;
+ interrupt-names ="pmic_id_irq";
+ };
+
+ loopback1: qcom,msm-pcm-loopback-low-latency {
+ compatible = "qcom,msm-pcm-loopback";
+ qcom,msm-pcm-loopback-low-latency;
};
loopback1: qcom,msm-pcm-loopback-low-latency {
status = "okay";
};
- gpio@cc00 { /* GPIO 13 - HPH_EN0 */
- qcom,mode = <1>;
- qcom,output-type = <0>;
+ gpio@cc00 { /* GPIO 13 - NTN_RST */
+ qcom,mode = <1>; /* DIGITAL OUT */
+ qcom,output-type = <0>; /* CMOS logic */
qcom,pull = <5>;
- qcom,vin-sel = <2>;
+ qcom,vin-sel = <2>; /* 1.8 */
qcom,out-strength = <1>;
- qcom,src-sel = <2>;
- qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,master-en = <1>; /* Enable GPIO */
status = "okay";
};
};
&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1 2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 133 0
- 0x0 1 &intc 0 180 0
- 0x0 2 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "pwr_event_irq", "pmic_id_irq";
-
+ extcon = <&usb_detect>;
vbus_dwc3-supply = <&usb_otg_switch>;
vdda33-supply = <&pm8994_l24>;
vdda18-supply = <&pm8994_l12>;
pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
clocks = <&clock_gcc clk_bb_clk2_pin>;
clock-names = "ref_clk";
+ status = "disabled";
};
};
};
&soc {
+ qcom,ntn_avb {
+ compatible = "qcom,ntn_avb";
+
+ ntn-rst-gpio = <&pm8994_gpios 13 0>;
+
+ vdd-ntn-hsic-supply = <&pm8994_l25>;
+ vdd-ntn-pci-supply = <&pm8994_s4>;
+ vdd-ntn-io-supply = <&pm8994_s4>;
+
+ qcom,ntn-rst-delay-msec = <100>;
+ qcom,ntn-rc-num = <1>;
+ };
+
i2c@75ba000 {
synaptics@20 {
compatible = "synaptics,dsx";
status = "okay";
};
- gpio@cc00 { /* GPIO 13 - HPH_EN0 */
- qcom,mode = <1>;
- qcom,output-type = <0>;
+ gpio@cc00 { /* GPIO 13 - NTN_RST */
+ qcom,mode = <1>; /* DIGITAL OUT */
+ qcom,output-type = <0>; /* CMOS logic */
qcom,pull = <5>;
- qcom,vin-sel = <2>;
+ qcom,vin-sel = <2>; /* 1.8 */
qcom,out-strength = <1>;
- qcom,src-sel = <2>;
- qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,master-en = <1>; /* Enable GPIO */
status = "okay";
};
/delete-property/ vin-supply;
};
+&pcie0 {
+ /delete-property/ qcom,l1-supported;
+ /delete-property/ qcom,l1ss-supported;
+ /delete-property/ qcom,aux-clk-sync;
+};
+
&pcie1 {
+ qcom,msi-gicm-addr = <0x09BD0040>;
+ qcom,msi-gicm-base = <0x240>;
+
/delete-property/ qcom,boot-option;
+ /delete-property/ qcom,l1-supported;
+ /delete-property/ qcom,l1ss-supported;
+ /delete-property/ qcom,aux-clk-sync;
+ /delete-property/ qcom,ep-wakeirq;
};
&pcie2 {
wake-gpio = <&tlmm 54 0>;
/delete-property/ qcom,boot-option;
+ /delete-property/ qcom,l1-supported;
+ /delete-property/ qcom,l1ss-supported;
+ /delete-property/ qcom,aux-clk-sync;
+ /delete-property/ qcom,ep-wakeirq;
};
&wsa881x_211 {
/delete-property/ qcom,spkr-sd-n-gpio;
};
-&hl7509_en_vreg {
- status = "ok";
-};
-
-&hl7509_vreg {
- status = "ok";
-};
-
-&gfx_cpr {
- vdd-supply = <&hl7509_vreg>;
- qcom,cpr-step-quot-init-min = <20>;
- qcom,cpr-step-quot-init-max = <26>;
- qcom,voltage-step = <10000>;
- /delete-property/ qcom,cpr-enable;
-};
-
-&gfx_vreg {
- qcom,cpr-voltage-ceiling =
- <600000 670000 670000 750000 830000
- 910000 960000 1020000>;
- qcom,cpr-voltage-floor =
- <600000 600000 600000 600000 600000
- 600000 600000 600000>;
-};
-
-&pm8994_l3 {
- regulator-min-microvolt = <875000>;
- regulator-max-microvolt = <875000>;
- qcom,init-voltage = <875000>;
-};
-
-&pm8994_l11 {
- regulator-min-microvolt = <850000>;
- regulator-max-microvolt = <850000>;
- qcom,init-voltage = <850000>;
-};
-
-&pm8994_l17 {
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- qcom,init-voltage = <1800000>;
-};
-
-&pm8994_l23 {
- regulator-min-microvolt = <1100000>;
- regulator-max-microvolt = <1100000>;
- qcom,init-voltage = <1100000>;
-};
-
-&pm8994_l27 {
- regulator-min-microvolt = <800000>;
- regulator-max-microvolt = <800000>;
- qcom,init-voltage = <800000>;
-};
-&pm8994_l29 {
- regulator-min-microvolt = <2500000>;
- regulator-max-microvolt = <2500000>;
- qcom,init-voltage = <2500000>;
-};
-
-&rpm_bus {
- rpm-regulator-ldoa26 {
- /delete-node/ pm8994_l26_corner;
- /delete-node/ pm8994_l26_floor_corner;
-
- pm8994_l26: regulator-l26 {
- regulator-min-microvolt = <1100000>;
- regulator-max-microvolt = <1100000>;
- qcom,init-voltage = <1100000>;
- status = "okay";
- };
- };
-
- rpm-regulator-ldoa31 {
- status = "okay";
- pm8994_l31: regulator-l31 {
- regulator-min-microvolt = <1100000>;
- regulator-max-microvolt = <1100000>;
- qcom,init-voltage = <1100000>;
- status = "okay";
- };
- };
-};
interrupt-controller;
#interrupt-cells = <2>;
+ /* pin governing mux between QSPI and modem on auto boards */
+ modem_mux: mdm_mux {
+ mux {
+ pins = "gpio121";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio121";
+ drive-strength = <2>;
+ bias-pull-up;
+ output-high;
+ };
+ };
/* add pingrp for adv7533 */
pmx_adv7533: pmx_adv7533 {
adv7533_0_int_active: adv7533_0_int_active {
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
qcom,psci-cpu-mode = <4>;
qcom,latency-us = <80>;
qcom,ss-power = <196>;
- qcom,energy-overhead = <45300>;
+ qcom,energy-overhead = <62248>;
qcom,time-overhead = <210>;
qcom,reset-level =
<LPM_RESET_LVL_PC>;
qcom,psci-cpu-mode = <4>;
qcom,latency-us = <80>;
qcom,ss-power = <196>;
- qcom,energy-overhead = <45300>;
+ qcom,energy-overhead = <62248>;
qcom,time-overhead = <210>;
qcom,reset-level =
<LPM_RESET_LVL_PC>;
qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
<79 379>, /* qusb2phy_dmse_hv_prim */
<80 384>, /* qusb2phy_dmse_hv_sec */
+ <81 379>, /* qusb2phy_dpse_hv_prim */
+ <82 384>, /* qusb2phy_dpse_hv_sec */
<52 275>, /* qmp_usb3_lfps_rxterm_irq */
<87 358>, /* ee0_krait_hlos_spmi_periph_irq */
<0xff 16>, /* APCj_qgicdrCpu0HwFaultIrptReq */
<0xff 94>, /* osmmu_CIrpt[0] */
<0xff 97>, /* iommu_nonsecure_irq */
<0xff 99>, /* msm_iommu_pmon_nonsecure_irq */
+ <0xff 101>, /* camss_jpeg_mmu_cirpt */
<0xff 102>, /* osmmu_CIrpt[1] */
<0xff 105>, /* iommu_pmon_nonsecure_irq */
<0xff 108>, /* osmmu_PMIrpt */
<0xff 132>, /* blsp1_qup_irq(5) */
<0xff 133>, /* blsp2_qup_irq(0) */
<0xff 134>, /* blsp2_qup_irq(1) */
+ <0xff 135>, /* blsp2_qup_irq(2) */
<0xff 138>, /* blsp2_qup_irq(5) */
<0xff 140>, /* blsp1_uart_irq(1) */
<0xff 146>, /* blsp2_uart_irq(1) */
<0xff 165>, /* usb30_hs_phy_irq */
<0xff 166>, /* sdc1_pwr_cmd_irq */
<0xff 170>, /* sdcc_pwr_cmd_irq */
+ <0xff 171>, /* usb20_hs_phy_irq */
+ <0xff 172>, /* usb20_power_event_irq */
<0xff 173>, /* sdc1_irq[0] */
<0xff 174>, /* o_wcss_apss_smd_med */
<0xff 175>, /* o_wcss_apss_smd_low */
<0xff 208>,
<0xff 210>,
<0xff 211>, /* usb_dwc3_otg */
+ <0xff 212>, /* usb30_power_event_irq */
<0xff 215>, /* o_bimc_intr(0) */
<0xff 224>, /* spdm_realtime_irq[1] */
<0xff 238>, /* crypto_bam_irq[0] */
<0xff 295>, /* camss_cpp_mmu_cirpt[0] */
<0xff 296>, /* camss_cpp_mmu_pmirpt */
<0xff 297>, /* ufs_intrq */
+ <0xff 298>, /* camss_cpp_mmu_cirpt */
<0xff 302>, /* qdss_etrbytecnt_irq */
<0xff 310>, /* pcie20_1_int_pls_err */
<0xff 311>, /* pcie20_1_int_aer_legacy */
<0xff 330>, /* camss_irq4 */
<0xff 331>, /* camss_irq5 */
<0xff 332>, /* sps */
+ <0xff 341>, /* camss_irq6 */
<0xff 346>, /* camss_irq8 */
<0xff 347>, /* camss_irq9 */
<0xff 352>, /* mdss_mmu_cirpt[0] */
* GNU General Public License for more details.
*/
-#include "dsi-panel-toshiba-720p-video.dtsi"
-#include "dsi-panel-sharp-dualmipi-wqxga-video.dtsi"
-#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi"
-#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi"
-#include "dsi-panel-nt35597-dsc-wqxga-video.dtsi"
-#include "dsi-panel-jdi-dualmipi-video.dtsi"
-#include "dsi-panel-jdi-dualmipi-cmd.dtsi"
-#include "dsi-panel-jdi-4k-dualmipi-video-nofbc.dtsi"
-#include "dsi-panel-sim-video.dtsi"
-#include "dsi-panel-sim-dualmipi-video.dtsi"
-#include "dsi-panel-sim-cmd.dtsi"
-#include "dsi-panel-sim-dualmipi-cmd.dtsi"
-#include "dsi-panel-nt35597-dsc-wqxga-cmd.dtsi"
-#include "dsi-panel-hx8379a-truly-fwvga-video.dtsi"
-#include "dsi-panel-r69007-dualdsi-wqxga-cmd.dtsi"
+#include "msm8996-mdss-panels.dtsi"
#include "dsi-panel-jdi-1080p-video.dtsi"
#include "dsi-panel-sharp-1080p-cmd.dtsi"
qcom,dsi-panel = <&dsi_dual_sharp_video>;
vddio-supply = <&pm8994_l14>;
- lab-supply = <&lab_regulator>;
- ibb-supply = <&ibb_regulator>;
- qcom,dsi-display-active;
+ /delete-property/ lab-supply;
+ /delete-property/ ibb-supply;
+ /delete-property/ qcom,dsi-display-active;
};
single_dsi_sim_vid: qcom,dsi-display@1 {
qcom,dsi-panel = <&dsi_sim_vid>;
vddio-supply = <&pm8994_l14>;
- lab-supply = <&lab_regulator>;
- ibb-supply = <&ibb_regulator>;
+ /delete-property/ lab-supply;
+ /delete-property/ ibb-supply;
};
dsi_toshiba_720p_vid: qcom,dsi-display@2 {
qcom,dsi-panel = <&dsi_sharp_1080_cmd>;
vddio-supply = <&pm8994_l14>;
vdd-supply = <&pm8994_l19>;
- lab-supply = <&lab_regulator>;
- ibb-supply = <&ibb_regulator>;
+ /delete-property/ lab-supply;
+ /delete-property/ ibb-supply;
};
sde_wb: qcom,wb-display@0 {
qcom,dsi-panel = <&dsi_dual_nt35597_cmd>;
vddio-supply = <&pm8994_l14>;
- lab-supply = <&lab_regulator>;
- ibb-supply = <&ibb_regulator>;
+ /delete-property/ lab-supply;
+ /delete-property/ ibb-supply;
};
dsi_dual_nt35597_video_1: qcom,dsi-display@6 {
qcom,dsi-panel = <&dsi_dual_nt35597_video>;
vddio-supply = <&pm8994_l14>;
- lab-supply = <&lab_regulator>;
- ibb-supply = <&ibb_regulator>;
+ /delete-property/ lab-supply;
+ /delete-property/ ibb-supply;
+ };
+
+ dsi_adv_7533_1: qcom,dsi-display@7 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_adv_7533_1";
+ qcom,display-type = "secondary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0>;
+ qcom,dsi-phy = <&mdss_dsi_phy0>;
+ clocks = <&clock_mmss clk_ext_byte0_clk_src>,
+ <&clock_mmss clk_ext_pclk0_clk_src>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ qcom,dsi-panel = <&dsi_adv7533_720p>;
+ vddio-supply = <&pm8994_l14>;
+ qcom,bridge-index = <0>;
+ };
+
+ dsi_adv_7533_2: qcom,dsi-display@8 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_adv_7533_2";
+ qcom,display-type = "tertiary";
+
+ qcom,dsi-ctrl = <&mdss_dsi1>;
+ qcom,dsi-phy = <&mdss_dsi_phy1>;
+ clocks = <&clock_mmss clk_ext_byte1_clk_src>,
+ <&clock_mmss clk_ext_pclk1_clk_src>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ qcom,dsi-panel = <&dsi_adv7533_720p>;
+ vddio-supply = <&pm8994_l14>;
+ qcom,bridge-index = <1>;
+ };
+
+ msm_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";
+ qcom,msm_ext_disp = <&msm_ext_disp>;
+ };
+ };
+
+ sde_hdmi: qcom,hdmi-display {
+ compatible = "qcom,hdmi-display";
+ label = "sde_hdmi";
+ qcom,display-type = "primary";
+ qcom,msm_ext_disp = <&msm_ext_disp>;
};
};
&mdss_mdp {
- connectors = <&dsi_dual_sharp_video_1
- &sde_wb>;
+ connectors = <&mdss_hdmi &sde_hdmi &dsi_adv_7533_1 &dsi_adv_7533_2>;
};
&dsi_dual_sharp_video {
iommus = <&mdp_smmu 0>;
};
- smmu_rot_unsec: qcom,smmu_rot_unsec_cb {
- compatible = "qcom,smmu_rot_unsec";
- iommus = <&rot_smmu 0>;
- };
-
- smmu_mdp_sec: qcom,smmu_mdp_sec_cb {
- compatible = "qcom,smmu_mdp_sec";
- iommus = <&mdp_smmu 1>;
- };
-
- smmu_rot_sec: qcom,smmu_rot_sec_cb {
- compatible = "qcom,smmu_rot_sec";
- iommus = <&rot_smmu 1>;
- };
-
/* data and reg bus scale settings */
qcom,sde-data-bus {
qcom,msm-bus,name = "mdss_sde";
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 <dt-bindings/gpio/gpio.h>
+#include "msm8996-v3.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-agave-adp.dtsi"
+#include "msm8996v3-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8996 v3 AUTO ADP";
+ compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp";
+ qcom,msm-id = <310 0x30001>;
+ qcom,board-id = <0x02010019 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <122 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
+&soc {
+ qcom,msm-ssc-sensors {
+ status = "disabled";
+ };
+
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
+&slim_msm {
+ status = "disabled";
+};
+
+&pm8994_mpps {
+ mpp@a500 { /* MPP 6 */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,vin-sel = <2>; /* S4 1.8V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
+
+&sdhc_2 {
+ cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_cd_on_sbc>;
+};
+
+&i2c_7 {
+ silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/
+ status = "disabled";
+ };
+};
+
#include "msm8996-v3.dtsi"
#include "msm8996-pm8994.dtsi"
#include "msm8996-auto-cdp.dtsi"
+#include "msm8996v3-auto.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM 8996 v3 AUTO CDP";
qcom,board-id = <0x03010001 0>;
};
+&soc {
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
&spi_9 {
status = "ok";
can-controller@0 {
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 "msm8996-v3.dtsi"
+#include "msm8996-pm8994-pm8004.dtsi"
+#include "msm8996-agave-adp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8996 v3 + PM8004 ADP LITE";
+ compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp";
+ qcom,board-id = <0x03010019 0>;
+};
+
+&spi_9 {
+ status = "disabled";
+};
qcom,pmic-id = <0x20009 0x2000A 0x0 0x0>;
interrupt-parent = <&intc>;
+ chosen {
+ bootargs = "fpsimd.fpsimd_settings=1 app_setting.use_app_setting=1 app_setting.use_32bit_app_setting=1";
+ };
+
aliases {
sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
sdhc2 = &sdhc_2; /* SDC2 SD card slot */
intc: interrupt-controller@09bc0000 {
compatible = "arm,gic-v3";
+ reg = <0x9bc0000 0x10000>, /* GICD */
+ <0x9c00000 0x100000>; /* GICR * 4 */
#interrupt-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
interrupt-controller;
#redistributor-regions = <1>;
redistributor-stride = <0x0 0x40000>;
- reg = <0x09bc0000 0x10000>, /* GICD */
- <0x09c00000 0x100000>; /* GICR * 4 */
+
interrupts = <1 9 4>;
+
+ gic-its@09BE0000 {
+ compatible = "arm,gic-v3-its";
+ msi-contoller;
+ reg = <0x9be0000 0x20000>;
+ };
};
timer {
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 <dt-bindings/gpio/gpio.h>
+#include "msm8996pro.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-agave-adp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO ADP LITE";
+ compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp";
+ qcom,msm-id = <315 0x10000>;
+ qcom,board-id = <0x03010019 0>;
+};
+
+&spi_9 {
+ status = "disabled";
+};
+
+&soc {
+ qcom,msm-ssc-sensors {
+ status = "disabled";
+ };
+
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+
+ i2c@75b6000 { /* BLSP8 */
+ /* ADV7533 HDMI Bridge Chip removed on ADP Lite */
+ adv7533@3d {
+ status = "disabled";
+ };
+ adv7533@39 {
+ status = "disabled";
+ };
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
+&slim_msm {
+ status = "disabled";
+};
+
+&pm8994_mpps {
+ mpp@a500 { /* MPP 6 */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,vin-sel = <2>; /* S4 1.8V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
+
+&sdhc_2 {
+ cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_cd_on_sbc>;
+};
+
+&i2c_7 {
+ silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/
+ status = "disabled";
+ };
+};
+
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 <dt-bindings/gpio/gpio.h>
+#include "msm8996pro.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-agave-adp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO ADP";
+ compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp";
+ qcom,msm-id = <315 0x10000>;
+ qcom,board-id = <0x02010019 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <122 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
+&soc {
+ qcom,msm-ssc-sensors {
+ status = "disabled";
+ };
+
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
+&slim_msm {
+ status = "disabled";
+};
+
+&pm8994_mpps {
+ mpp@a500 { /* MPP 6 */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,vin-sel = <2>; /* S4 1.8V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
+
+&sdhc_2 {
+ cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_cd_on_sbc>;
+};
+
+&i2c_7 {
+ silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/
+ status = "disabled";
+ };
+};
+
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 "msm8996pro.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-auto-cdp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO CDP";
+ compatible = "qcom,msm8996-cdp", "qcom,msm8996", "qcom,cdp";
+ qcom,msm-id = <315 0x10000>;
+ qcom,board-id = <0x03010001 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <127 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
+&soc {
+ qcom,msm-thermal {
+ qcom,hotplug-temp = <115>;
+ qcom,hotplug-temp-hysteresis = <25>;
+ qcom,therm-reset-temp = <119>;
+ };
+};
--- /dev/null
+/* Copyright (c) 2016-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 "msm8996v3-auto.dtsi"
+
+&gfx_vreg {
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <8 8 8>;
+ qcom,cpr-corners = <8>;
+
+ qcom,cpr-corner-fmax-map =
+ <2 4 6 8>;
+
+ qcom,cpr-voltage-ceiling =
+ <600000 670000 670000 750000 830000 910000 960000
+ 1020000>;
+
+ qcom,cpr-voltage-floor =
+ <600000 600000 600000 600000 600000 600000 600000
+ 600000>;
+
+ qcom,mem-acc-voltage =
+ <1 1 1 1 2 2 2 2>;
+
+ qcom,corner-frequencies =
+ <0 133000000 214000000 315000000 401800000 510000000 560000000
+ 624000000>;
+
+ qcom,cpr-target-quotients =
+ <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>,
+ <0 0 0 0 0 0 185 179 291 299 304 319 0 0 0 0>,
+ <0 0 0 0 0 0 287 273 425 426 443 453 0 0 0 0>,
+ <0 0 0 0 0 0 414 392 584 576 608 612 0 0 0 0>,
+ <0 0 0 0 0 0 459 431 684 644 692 679 0 0 0 0>,
+ <0 0 0 0 0 0 577 543 798 768 823 810 0 0 0 0>,
+ <0 0 0 0 0 0 669 629 886 864 924 911 0 0 0 0>,
+ <0 0 0 0 0 0 771 725 984 970 1036 1024 0 0 0 0>;
+
+ qcom,cpr-ro-scaling-factor =
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>;
+
+ qcom,cpr-open-loop-voltage-fuse-adjustment =
+ <0 (-20000) (-20000) (-40000)>;
+
+ qcom,cpr-closed-loop-voltage-adjustment =
+ <0 0 30000 10000 10000 45000 25000 25000>;
+
+ qcom,cpr-floor-to-ceiling-max-range =
+ <0 70000 70000 75000 80000 90000 95000 100000>;
+
+ qcom,cpr-fused-closed-loop-voltage-adjustment-map =
+ <0 2 2 2 2 0 0 4>;
+};
+
+&apcc_cpr {
+ /delete-property/ qcom,cpr-enable;
+};
+
+&apc0_pwrcl_vreg {
+ regulator-max-microvolt = <19>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <19 19 19>;
+ qcom,cpr-corners = <19>;
+
+ qcom,cpr-corner-fmax-map =
+ <3 6 9 14 19>;
+
+ qcom,cpr-voltage-ceiling =
+ <670000 670000 670000 670000 670000 670000 745000 745000
+ 745000 905000 905000 905000 905000 905000 1140000 1140000
+ 1140000 1140000 1140000>;
+
+ qcom,cpr-voltage-floor =
+ <470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000>;
+
+ qcom,cpr-floor-to-ceiling-max-range =
+ <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+
+ qcom,corner-frequencies =
+ <307200000 384000000 460800000 537600000 614400000
+ 691200000 768000000 844800000 902400000 979200000
+ 1056000000 1132800000 1209600000 1286400000 1363200000
+ 1440000000 1516800000 1593600000 1785600000>;
+
+ qcom,cpr-open-loop-voltage-fuse-adjustment =
+ <0 0 0 0 0 >;
+
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ <0 0 0 0 0 >;
+
+ qcom,cpr-open-loop-voltage-adjustment =
+ <0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000)
+ (-11000) (-12000) (-12000) (-13000) (-15000)>;
+
+ qcom,cpr-open-loop-voltage-min-diff =
+ <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+
+ qcom,cpr-closed-loop-voltage-adjustment =
+ <0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000)
+ (-11000) (-12000) (-12000) (-13000) (-15000)>;
+
+ qcom,allow-aging-voltage-adjustment = <0>;
+ qcom,allow-aging-open-loop-voltage-adjustment = <0>;
+};
+
+&apc0_cbf_vreg {
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <19 19 19>;
+ qcom,cpr-corners = <19>;
+
+ qcom,cpr-corner-fmax-map =
+ <1 4 7 14 19>;
+
+ qcom,cpr-voltage-ceiling =
+ <670000 670000 670000 670000 745000 745000 745000 905000
+ 905000 905000 905000 905000 905000 905000 1140000 1140000
+ 1140000 1140000 1140000>;
+
+ qcom,cpr-voltage-floor =
+ <470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000>;
+
+ qcom,cpr-floor-to-ceiling-max-range =
+ <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+
+ qcom,corner-frequencies =
+ <192000000 307200000 384000000 441600000 537600000
+ 614400000 691200000 768000000 844800000 902400000
+ 979200000 1056000000 1132800000 1190400000 1286400000
+ 1363200000 1440000000 1516800000 1593600000>;
+
+ qcom,cpr-open-loop-voltage-fuse-adjustment =
+ <0 0 0 15000 0 >;
+
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ <0 0 0 0 0 >;
+
+ qcom,allow-aging-voltage-adjustment = <0>;
+ qcom,allow-aging-open-loop-voltage-adjustment = <0>;
+};
+
+&apc1_vreg {
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <25 25 25>;
+ qcom,cpr-corners = <25>;
+
+ qcom,cpr-corner-fmax-map =
+ <4 7 10 15 25>;
+
+ qcom,cpr-voltage-ceiling =
+ <670000 670000 670000 670000 670000 670000 670000 745000
+ 745000 745000 905000 905000 905000 905000 905000 1140000
+ 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000
+ 1140000>;
+
+ qcom,cpr-voltage-floor =
+ <470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
+ 470000>;
+
+ qcom,cpr-floor-to-ceiling-max-range =
+ <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+
+ qcom,corner-frequencies =
+ <307200000 384000000 460800000 537600000 614400000
+ 691200000 748800000 825600000 902400000 979200000
+ 1056000000 1132800000 1209600000 1286400000 1363200000
+ 1440000000 1516800000 1593600000 1670400000 1747200000
+ 1824000000 1900800000 1977600000 2054400000 2150400000>;
+
+ qcom,cpr-open-loop-voltage-fuse-adjustment =
+ <0 0 0 0 0 >;
+
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ <0 0 0 0 0 >;
+
+ qcom,cpr-open-loop-voltage-adjustment =
+ <0 0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000)
+ (-10000) (-11000) (-11000) (-12000) (-12000) (-13000) (-13000)
+ (-14000) (-14000) (-15000)>;
+
+ qcom,cpr-open-loop-voltage-min-diff =
+ <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+
+ qcom,cpr-closed-loop-voltage-adjustment =
+ <0 0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000)
+ (-10000) (-11000) (-11000) (-12000) (-12000) (-13000) (-13000)
+ (-14000) (-14000) (-15000)>;
+
+ qcom,allow-aging-voltage-adjustment = <0>;
+ qcom,allow-aging-open-loop-voltage-adjustment = <0>;
+};
+
+&clock_cpu {
+ compatible = "qcom,cpu-clock-8996-auto";
+ qcom,pwrcl-early-boot-freq = < 1286400000 >;
+ qcom,perfcl-early-boot-freq = < 1363200000 >;
+
+ qcom,pwrcl-speedbin0-v0 =
+ < 0 0 >,
+ < 768000000 7 >,
+ < 844800000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >;
+
+ qcom,pwrcl-speedbin1-v0 =
+ < 0 0 >,
+ < 768000000 7 >,
+ < 844800000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >;
+
+ qcom,pwrcl-speedbin2-v0 =
+ < 0 0 >,
+ < 768000000 7 >,
+ < 844800000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >;
+
+ qcom,perfcl-speedbin0-v0 =
+ < 0 0 >,
+ < 825600000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >,
+ < 1670400000 19 >,
+ < 1747200000 20 >,
+ < 1824000000 21 >,
+ < 1900800000 22 >,
+ < 1977600000 23 >,
+ < 2054400000 24 >;
+
+ qcom,perfcl-speedbin1-v0 =
+ < 0 0 >,
+ < 825600000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >,
+ < 1670400000 19 >;
+
+ qcom,perfcl-speedbin2-v0 =
+ < 0 0 >,
+ < 825600000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >;
+
+ qcom,cbf-speedbin0-v0 =
+ < 0 0 >,
+ < 537600000 5 >,
+ < 614400000 6 >,
+ < 691200000 7 >,
+ < 768000000 8 >,
+ < 844800000 9 >,
+ < 902400000 10 >,
+ < 979200000 11 >,
+ < 1056000000 12 >,
+ < 1132800000 13 >,
+ < 1190400000 14 >,
+ < 1286400000 15 >,
+ < 1363200000 16 >,
+ < 1440000000 17 >,
+ < 1516800000 18 >,
+ < 1593600000 19 >;
+
+ qcom,cbf-speedbin1-v0 =
+ < 0 0 >,
+ < 537600000 5 >,
+ < 614400000 6 >,
+ < 691200000 7 >,
+ < 768000000 8 >,
+ < 844800000 9 >,
+ < 902400000 10 >,
+ < 979200000 11 >,
+ < 1056000000 12 >,
+ < 1132800000 13 >,
+ < 1190400000 14 >,
+ < 1286400000 15 >,
+ < 1363200000 16 >,
+ < 1440000000 17 >,
+ < 1516800000 18 >,
+ < 1593600000 19 >;
+
+ qcom,cbf-speedbin2-v0 =
+ < 0 0 >,
+ < 537600000 5 >,
+ < 614400000 6 >,
+ < 691200000 7 >,
+ < 768000000 8 >,
+ < 844800000 9 >,
+ < 902400000 10 >,
+ < 979200000 11 >,
+ < 1056000000 12 >,
+ < 1132800000 13 >,
+ < 1190400000 14 >;
+};
+
+&msm_cpufreq {
+ qcom,cpufreq-table-0 =
+ < 768000 >,
+ < 844800 >,
+ < 902400 >,
+ < 979200 >,
+ < 1056000 >,
+ < 1132800 >,
+ < 1209600 >,
+ < 1286400 >,
+ < 1363200 >,
+ < 1440000 >,
+ < 1516800 >,
+ < 1593600 >;
+ qcom,cpufreq-table-2 =
+ < 825600 >,
+ < 902400 >,
+ < 979200 >,
+ < 1056000 >,
+ < 1132800 >,
+ < 1209600 >,
+ < 1286400 >,
+ < 1363200 >,
+ < 1440000 >,
+ < 1516800 >,
+ < 1593600 >,
+ < 1670400 >,
+ < 1747200 >,
+ < 1824000 >,
+ < 1900800 >,
+ < 1977600 >,
+ < 2054400 >;
+};
+
+&m4m_cache {
+ freq-tbl-khz =
+ < 537600 >,
+ < 614400 >,
+ < 691200 >,
+ < 768000 >,
+ < 844800 >,
+ < 902400 >,
+ < 979200 >,
+ < 1056000 >,
+ < 1132800 >,
+ < 1190400 >,
+ < 1286400 >,
+ < 1363200 >,
+ < 1440000 >,
+ < 1516800 >,
+ < 1593600 >;
+};
+
+&devfreq_cpufreq {
+ mincpubw-cpufreq {
+ cpu-to-dev-map-0 =
+ < 1593600 1525 >;
+ cpu-to-dev-map-2 =
+ < 1977600 1525 >,
+ < 2054400 5195 >;
+ };
+};
+
+&clock_gpu {
+ qcom,gfxfreq-speedbin0 =
+ < 0 0 0 >,
+ < 315000000 4 4 >,
+ < 401800000 5 5 >,
+ < 510000000 6 5 >,
+ < 560000000 7 7 >,
+ < 624000000 8 7 >;
+
+ qcom,gfxfreq-mx-speedbin0 =
+ < 0 0 >,
+ < 315000000 4 >,
+ < 401800000 5 >,
+ < 510000000 5 >,
+ < 560000000 7 >,
+ < 624000000 7 >;
+
+ qcom,gfxfreq-speedbin1 =
+ < 0 0 0 >,
+ < 315000000 4 4 >,
+ < 401800000 5 5 >,
+ < 510000000 6 5 >;
+
+ qcom,gfxfreq-mx-speedbin1 =
+ < 0 0 >,
+ < 315000000 4 >,
+ < 401800000 5 >,
+ < 510000000 5 >;
+
+ qcom,gfxfreq-speedbin2 =
+ < 0 0 0 >,
+ < 315000000 4 4 >;
+
+ qcom,gfxfreq-mx-speedbin2 =
+ < 0 0 >,
+ < 315000000 4 >;
+};
--- /dev/null
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 "msm8996pro-v1.1.dtsi"
+#include "msm8996-pm8994.dtsi"
+#include "msm8996-auto-cdp.dtsi"
+#include "msm8996pro-auto.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8996pro v1.1 AUTO CDP";
+ compatible = "qcom,msm8996-cdp", "qcom,msm8996", "qcom,cdp";
+ qcom,msm-id = <315 0x10001>;
+ qcom,board-id = <0x03010001 0>;
+};
+
+&spi_9 {
+ status = "ok";
+ can-controller@0 {
+ compatible = "renesas,rh850";
+ reg = <0>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <127 0>;
+ spi-max-frequency = <5000000>;
+ };
+};
+
+&pil_modem {
+ pinctrl-names = "default";
+ pinctrl-0 = <&modem_mux>;
+};
+
-/* 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
/ {
model = "Qualcomm Technologies, Inc. MSM 8996pro";
qcom,msm-id = <305 0x10000>;
+
+ chosen {
+ bootargs = "fpsimd.fpsimd_settings=1 app_setting.use_app_setting=0 app_setting.use_32bit_app_setting_pro=1";
+ };
};
&apc_apm {
< 1516800000 17 >,
< 1593600000 18 >,
< 1996800000 20 >;
+ qcom,pwrcl-speedbin2-v0 =
+ < 0 0 >,
+ < 307200000 1 >,
+ < 384000000 2 >,
+ < 460800000 3 >,
+ < 537600000 4 >,
+ < 614400000 5 >,
+ < 691200000 6 >,
+ < 768000000 7 >,
+ < 844800000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >;
qcom,perfcl-speedbin0-v0 =
< 0 0 >,
< 307200000 1 >,
< 1977600000 23 >,
< 2054400000 24 >,
< 2150400000 25 >;
+ qcom,perfcl-speedbin2-v0 =
+ < 0 0 >,
+ < 307200000 1 >,
+ < 384000000 2 >,
+ < 460800000 3 >,
+ < 537600000 4 >,
+ < 614400000 5 >,
+ < 691200000 6 >,
+ < 748800000 7 >,
+ < 825600000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >,
+ < 1670400000 19 >,
+ < 1747200000 20 >,
+ < 1824000000 21 >,
+ < 1900800000 22 >;
qcom,cbf-speedbin0-v0 =
< 0 0 >,
< 192000000 1 >,
< 1440000000 17 >,
< 1516800000 18 >,
< 1593600000 19 >;
+ qcom,cbf-speedbin2-v0 =
+ < 0 0 >,
+ < 192000000 1 >,
+ < 307200000 2 >,
+ < 384000000 3 >,
+ < 441600000 4 >,
+ < 537600000 5 >,
+ < 614400000 6 >,
+ < 691200000 7 >,
+ < 768000000 8 >,
+ < 844800000 9 >,
+ < 902400000 10 >,
+ < 979200000 11 >,
+ < 1056000000 12 >,
+ < 1132800000 13 >,
+ < 1190400000 14 >,
+ < 1286400000 15 >,
+ < 1363200000 16 >,
+ < 1440000000 17 >,
+ < 1516800000 18 >,
+ < 1593600000 19 >;
};
&clock_mmss {
< 510000000 5 >,
< 560000000 7 >,
< 624000000 7 >;
+ qcom,gfxfreq-speedbin2 =
+ < 0 0 0 >,
+ < 133000000 2 4 >,
+ < 214000000 3 4 >,
+ < 315000000 4 4 >,
+ < 401800000 5 5 >,
+ < 510000000 6 5 >;
+ qcom,gfxfreq-mx-speedbin2 =
+ < 0 0 >,
+ < 133000000 4 >,
+ < 214000000 4 >,
+ < 315000000 4 >,
+ < 401800000 5 >,
+ < 510000000 5 >;
};
&msm_cpufreq {
--- /dev/null
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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.
+ */
+
+&hl7509_en_vreg {
+ status = "ok";
+};
+
+&hl7509_vreg {
+ status = "ok";
+};
+
+&gfx_cpr {
+ vdd-supply = <&hl7509_vreg>;
+ qcom,cpr-step-quot-init-min = <20>;
+ qcom,cpr-step-quot-init-max = <26>;
+ qcom,voltage-step = <10000>;
+ /delete-property/ qcom,cpr-enable;
+};
+
+&gfx_vreg {
+ qcom,cpr-voltage-ceiling =
+ <600000 670000 670000 750000 830000
+ 910000 960000 1020000>;
+ qcom,cpr-voltage-floor =
+ <600000 600000 600000 600000 600000
+ 600000 600000 600000>;
+};
+
+&pm8994_l3 {
+ regulator-min-microvolt = <875000>;
+ regulator-max-microvolt = <875000>;
+ qcom,init-voltage = <875000>;
+};
+
+&pm8994_l11 {
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ qcom,init-voltage = <850000>;
+};
+
+&pm8994_l17 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+};
+
+&pm8994_l23 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+};
+
+&pm8994_l27 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <800000>;
+ qcom,init-voltage = <800000>;
+};
+
+&pm8994_l29 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ qcom,init-voltage = <2500000>;
+};
+
+&rpm_bus {
+ rpm-regulator-ldoa26 {
+ /delete-node/ pm8994_l26_corner;
+ /delete-node/ pm8994_l26_floor_corner;
+
+ pm8994_l26: regulator-l26 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+ status = "okay";
+ };
+ };
+
+ rpm-regulator-ldoa31 {
+ status = "okay";
+ pm8994_l31: regulator-l31 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+ status = "okay";
+ };
+ };
+};
+
+&clock_cpu {
+ qcom,pwrcl-speedbin0-v0 =
+ < 0 0 >,
+ < 768000000 7 >,
+ < 844800000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >;
+
+ qcom,perfcl-speedbin0-v0 =
+ < 0 0 >,
+ < 825600000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1209600000 13 >,
+ < 1286400000 14 >,
+ < 1363200000 15 >,
+ < 1440000000 16 >,
+ < 1516800000 17 >,
+ < 1593600000 18 >,
+ < 1670400000 19 >,
+ < 1747200000 20 >,
+ < 1824000000 21 >,
+ < 1900800000 22 >,
+ < 1977600000 23 >,
+ < 2054400000 24 >;
+
+ qcom,cbf-speedbin0-v0 =
+ < 0 0 >,
+ < 537600000 5 >,
+ < 614400000 6 >,
+ < 691200000 7 >,
+ < 768000000 8 >,
+ < 844800000 9 >,
+ < 902400000 10 >,
+ < 979200000 11 >,
+ < 1056000000 12 >,
+ < 1132800000 13 >,
+ < 1190400000 14 >,
+ < 1286400000 15 >,
+ < 1363200000 16 >,
+ < 1440000000 17 >,
+ < 1516800000 18 >,
+ < 1593600000 19 >;
+};
+
+&clock_gpu {
+ qcom,gfxfreq-speedbin0 =
+ < 0 0 0 >,
+ < 315000000 4 4 >,
+ < 401800000 5 5 >,
+ < 510000000 6 5 >,
+ < 560000000 7 7 >,
+ < 624000000 8 7 >;
+
+ qcom,gfxfreq-mx-speedbin0 =
+ < 0 0 >,
+ < 315000000 4 >,
+ < 401800000 5 >,
+ < 510000000 5 >,
+ < 560000000 7 >,
+ < 624000000 7 >;
+};
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
qcom,supply-max-voltage = <0>;
qcom,supply-enable-load = <0>;
qcom,supply-disable-load = <0>;
+ qcom,supply-lp-mode-disable-allowed;
};
};
qcom,supply-max-voltage = <880000>;
qcom,supply-enable-load = <73400>;
qcom,supply-disable-load = <32>;
+ qcom,supply-lp-mode-disable-allowed;
};
};
qcom,pluggable;
};
};
-
-#include "msm8998-mdss-panels.dtsi"
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
bias-pull-down;
};
};
+
+ pcie0_wake_sleep: pcie0_wake_sleep {
+ mux {
+ pins = "gpio37";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio37";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
};
hph_en0_ctrl {
/dts-v1/;
#include "msm8998.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd-skuk.dtsi"
#include "msm8998-camera-sensor-skuk.dtsi"
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd-vr1.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd.dtsi"
/ {
pm8998_s5: regulator-s5 {
regulator-min-microvolt = <1904000>;
regulator-max-microvolt = <2040000>;
+ qcom,init-pin-ctrl-mode = <8>; /* PMIC_AWAKE */
+ qcom,send-defaults;
status = "okay";
};
};
pm8998_s7: regulator-s7 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1028000>;
+ qcom,init-pin-ctrl-mode = <8>; /* PMIC_AWAKE */
+ qcom,send-defaults;
status = "okay";
};
};
rpm-regulator-ldoa24 {
status = "okay";
pm8998_l24: regulator-l24 {
- regulator-min-microvolt = <3088000>;
+ regulator-min-microvolt = <1848000>;
regulator-max-microvolt = <3088000>;
parent-supply = <&pm8998_l12>;
status = "okay";
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
/dts-v1/;
#include "msm8998-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd-skuk.dtsi"
#include "msm8998-camera-sensor-skuk-evt3.dtsi"
/dts-v1/;
#include "msm8998-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd-skuk-hdk.dtsi"
/ {
/dts-v1/;
#include "msm8998-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd-skuk.dtsi"
#include "msm8998-camera-sensor-skuk.dtsi"
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd-vr1.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd.dtsi"
/ {
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-cdp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.1-interposer-sdm660.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-interposer-sdm660-cdp.dtsi"
#include "msm8998-interposer-pm660.dtsi"
#include "msm8998-interposer-sdm660-audio.dtsi"
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.1-interposer-sdm660.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-interposer-sdm660-mtp.dtsi"
#include "msm8998-interposer-pm660.dtsi"
#include "msm8998-interposer-sdm660-audio.dtsi"
/dts-v1/;
+#include "msm8998-v2.1-interposer-sdm660.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-v2.1-interposer-sdm660-qrd.dtsi"
#include "msm8998-interposer-pm660.dtsi"
#include "msm8998-interposer-sdm660-audio.dtsi"
/dts-v1/;
#include "msm8998-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-mtp.dtsi"
/ {
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
/dts-v1/;
#include "msm8998-v2.1.dtsi"
+#include "msm8998-mdss-panels.dtsi"
#include "msm8998-qrd.dtsi"
/ {
0x800 0x00 0x00
0x808 0x03 0x00>;
- pinctrl-names = "default";
+ pinctrl-names = "default", "sleep";
pinctrl-0 = <&pcie0_clkreq_default
&pcie0_perst_default
&pcie0_wake_default>;
+ pinctrl-1 = <&pcie0_clkreq_default
+ &pcie0_perst_default
+ &pcie0_wake_sleep>;
perst-gpio = <&tlmm 35 0>;
wake-gpio = <&tlmm 37 0>;
<&clock_gcc clk_rf_clk3_pin>;
clock-names = "rf_clk3_clk", "rf_clk3_pin_clk";
qcom,smmu-support;
+ qcom,smmu-s1-en;
+ qcom,smmu-fast-map;
+ qcom,smmu-coherent;
+ qcom,smmu-mapping = <0x20000000 0xe0000000>;
status = "disabled";
};
&pm660_fg {
qcom,battery-data = <&qrd_batterydata>;
qcom,fg-jeita-thresholds = <0 5 55 55>;
+ qcom,battery-thermal-coefficients = [9d 50 ff];
};
&uartblsp1dm1 {
&pm660_fg {
qcom,battery-data = <&qrd_batterydata>;
qcom,fg-jeita-thresholds = <0 5 55 55>;
+ qcom,battery-thermal-coefficients = [9d 50 ff];
};
&i2c_2 {
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CLOCK_CPU_OSM=y
CONFIG_QCOM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
-CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
CONFIG_IOMMU_DEBUG=y
CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CLOCK_CPU_OSM=y
CONFIG_QCOM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
-CONFIG_IOMMU_IO_PGTABLE_FAST=y
-CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y
CONFIG_ARM_SMMU=y
CONFIG_IOMMU_DEBUG=y
CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_DEBUG_OBJECTS_WORK=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=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
+/*
+ * Copyright (c) 2016-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 ASMARM_DMA_CONTIGUOUS_H
#define ASMARM_DMA_CONTIGUOUS_H
#ifdef __KERNEL__
-#ifdef CONFIG_DMA_CMA
#include <linux/types.h>
void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
#endif
-#endif
#endif
next = kvm_pgd_addr_end(addr, end);
if (!pgd_none(*pgd))
unmap_puds(kvm, pgd, addr, next);
+ /*
+ * If we are dealing with a large range in
+ * stage2 table, release the kvm->mmu_lock
+ * to prevent starvation and lockup detector
+ * warnings.
+ */
+ if (kvm && (next != end))
+ cond_resched_lock(&kvm->mmu_lock);
} while (pgd++, addr = next, addr != end);
}
*/
static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
{
+ assert_spin_locked(&kvm->mmu_lock);
unmap_range(kvm, kvm->arch.pgd, start, size);
}
int idx;
idx = srcu_read_lock(&kvm->srcu);
+ down_read(¤t->mm->mmap_sem);
spin_lock(&kvm->mmu_lock);
slots = kvm_memslots(kvm);
stage2_unmap_memslot(kvm, memslot);
spin_unlock(&kvm->mmu_lock);
+ up_read(¤t->mm->mmap_sem);
srcu_read_unlock(&kvm->srcu, idx);
}
if (kvm->arch.pgd == NULL)
return;
+ spin_lock(&kvm->mmu_lock);
unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
+ spin_unlock(&kvm->mmu_lock);
+
kvm_free_hwpgd(kvm_get_hwpgd(kvm));
if (KVM_PREALLOC_LEVEL > 0)
kfree(kvm->arch.pgd);
(KVM_PHYS_SIZE >> PAGE_SHIFT))
return -EFAULT;
+ down_read(¤t->mm->mmap_sem);
/*
* A memory region could potentially cover multiple VMAs, and any holes
* between them, so iterate over all of them to find out if we can map
pa += vm_start - vma->vm_start;
/* IO region dirty page logging not allowed */
- if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES)
- return -EINVAL;
+ if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES) {
+ ret = -EINVAL;
+ goto out;
+ }
ret = kvm_phys_addr_ioremap(kvm, gpa, pa,
vm_end - vm_start,
} while (hva < reg_end);
if (change == KVM_MR_FLAGS_ONLY)
- return ret;
+ goto out;
spin_lock(&kvm->mmu_lock);
if (ret)
else
stage2_flush_memslot(kvm, memslot);
spin_unlock(&kvm->mmu_lock);
+out:
+ up_read(¤t->mm->mmap_sem);
return ret;
}
endchoice
+config MSM_APP_API
+ bool "API support to enable / disable app settings for MSM8996"
+ depends on ARCH_MSM8996 && (ENABLE_FP_SIMD_SETTINGS || MSM_APP_SETTINGS)
+ help
+ Add API support to enable / disable the app settings to be used
+ at runtime. These APIs are used to enable / disable app setting
+ when specific aarch32 or aarch64 processes are running.
+
+ If you are not sure what to do, select 'N' here.
+
+config ENABLE_FP_SIMD_SETTINGS
+ bool "Enable FP(Floating Point) Settings for Qualcomm MSM8996"
+ depends on ARCH_MSM8996
+ select MSM_APP_API
+ help
+ Enable FP(Floating Point) and SIMD settings for the MSM8996 during
+ the execution of the aarch32 processes and disable these settings
+ when you switch to the aarch64 processes.
+
+ If you are not sure what to do, select 'N' here.
+
+config MSM_APP_SETTINGS
+ bool "Support to enable / disable app settings for MSM8996"
+ depends on ARCH_MSM8996
+ select MSM_APP_API
+ help
+ Expose an interface used by the userspace at runtime to
+ enable / disable the app specific settings.
+
+ If you are not sure what to do, select 'N' here.
+
choice
prompt "Virtual address space size"
default ARM64_VA_BITS_39 if ARM64_4K_PAGES
--- /dev/null
+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_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=15
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_SCHED_HMP=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=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_MEMBARRIER is not set
+CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+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_MSM8996=y
+CONFIG_PCI=y
+CONFIG_PCI_MSM=y
+CONFIG_ENABLE_FP_SIMD_SETTINGS=y
+CONFIG_MSM_APP_SETTINGS=y
+CONFIG_SCHED_MC=y
+CONFIG_NR_CPUS=8
+CONFIG_PREEMPT=y
+CONFIG_HZ_100=y
+CONFIG_CMA=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y
+CONFIG_SECCOMP=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=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_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_BOOST=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_ESP=y
+# CONFIG_INET_LRO is not set
+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_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=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_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_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_VLAN_8021Q=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_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_SOCKEV_NLMCAST=y
+CONFIG_CAN=y
+CONFIG_CAN_RH850=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+# CONFIG_BT_HS is not set
+# CONFIG_BT_LE is not set
+# CONFIG_BT_DEBUGFS is not set
+CONFIG_MSM_BT_POWER=y
+CONFIG_BTFM_SLIM=y
+CONFIG_BTFM_SLIM_WCN3990=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+# CONFIG_CFG80211_CRDA_SUPPORT is not set
+CONFIG_RFKILL=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+# CONFIG_PNP_DEBUG_MESSAGES is not set
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_QSEECOM=y
+CONFIG_HDCP_QSEECOM=y
+CONFIG_PROFILER=y
+CONFIG_UID_SYS_STATS=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_UFS_QCOM_ICE=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_REQ_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_E1000E=y
+CONFIG_MSM_RMNET_MHI=y
+CONFIG_RNDIS_IPA=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_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CNSS_CRYPTO=y
+CONFIG_ATH_CARDS=y
+CONFIG_WIL6210=m
+CONFIG_CNSS=y
+CONFIG_CLD_LL_CORE=y
+CONFIG_BUS_AUTO_SUSPEND=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y
+CONFIG_SECURE_TOUCH=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MSM_HS=y
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_DIAG_CHAR=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
+# CONFIG_ACPI_I2C_OPREGION is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
+CONFIG_I2C_MSM_V2=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SOUNDWIRE=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SPMI=y
+CONFIG_PINCTRL_MSM8996=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_QPNP_PIN=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_RESET_XGENE=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1351_USB_CHARGER=y
+CONFIG_MSM_BCL_CTL=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_MSM_PM=y
+CONFIG_APSS_CORE_EA=y
+CONFIG_MSM_APM=y
+CONFIG_SENSORS_EPM_ADC=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_LIMITS_MONITOR=y
+CONFIG_LIMITS_LITE_HW=y
+CONFIG_THERMAL_MONITOR=y
+CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_WCD9335_CODEC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_FAN53555=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_QPNP_LABIBB=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_CPR3_HMSS=y
+CONFIG_REGULATOR_CPR3_MMSS=y
+CONFIG_REGULATOR_KRYO=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=y
+CONFIG_SOC_CAMERA_PLATFORM=y
+CONFIG_MSM_CAMERA=y
+CONFIG_MSM_CAMERA_DEBUG=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSMB_CAMERA_DEBUG=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI20_HEADER=y
+CONFIG_MSM_CSI22_HEADER=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSI31_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISPIF=y
+CONFIG_IMX134=y
+CONFIG_IMX132=y
+CONFIG_OV9724=y
+CONFIG_OV5648=y
+CONFIG_GC0339=y
+CONFIG_OV8825=y
+CONFIG_OV8865=y
+CONFIG_s5k4e1=y
+CONFIG_OV12830=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_FD=y
+CONFIG_MSM_JPEGDMA=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_VIDC_VMEM=y
+CONFIG_MSM_VIDC_GOVERNORS=y
+CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_QCOM_KGSL=y
+CONFIG_FB=y
+CONFIG_FB_MSM=y
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=m
+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_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MSM8996=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_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_ISP1760=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_QTI_KS_BRIDGE=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_MSM_SSPHY_QMP=y
+CONFIG_MSM_QUSB_PHY=y
+CONFIG_USB_ULPI=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=4
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_QCRNDIS=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=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_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_USB_CONFIGFS_F_QDSS=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_CQ_HCI=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
+CONFIG_LEDS_QPNP_WLED=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_ESOC=y
+CONFIG_ESOC_DEV=y
+CONFIG_ESOC_CLIENT=y
+CONFIG_ESOC_MDM_4x=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SW_SYNC_USER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_QPNP_REVID=y
+CONFIG_QPNP_COINCELL=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
+CONFIG_GPIO_USB_DETECT=y
+CONFIG_MSM_MHI=y
+CONFIG_MSM_MHI_UCI=y
+CONFIG_SEEMP_CORE=y
+CONFIG_USB_BAM=y
+CONFIG_MSM_MDSS_PLL=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_IOMMU_IO_PGTABLE_FAST=y
+CONFIG_ARM_SMMU=y
+CONFIG_IOMMU_DEBUG=y
+CONFIG_IOMMU_TESTS=y
+CONFIG_MSM_SMEM=y
+CONFIG_QPNP_HAPTIC=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMD_XPRT=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_SMEM_LOGGING=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_RPM_SMD=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_SYSMON_GLINK_COMM=y
+CONFIG_MSM_IPC_ROUTER_MHI_XPRT=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_GLINK_PKT=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_QCOM_SCM=y
+CONFIG_QCOM_SCM_XPU=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_RUN_QUEUE_STATS=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_BOOT_TIME_MARKER=y
+CONFIG_MSM_ADSP_LOADER=y
+CONFIG_MSM_PERFORMANCE=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_MPM_OF=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_AVTIMER=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
+CONFIG_MSM_RPM_LOG=y
+CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_QCOM_SMCINVOKE=y
+CONFIG_MSM_CACHE_M4M_ERP64=y
+CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_CE=y
+CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_UE=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
+CONFIG_QCOM_BIMC_BWMON=y
+CONFIG_ARM_MEMLAT_MON=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=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_SENSORS_SSC=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=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_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_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_IPC_LOGGING=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_DEBUG_ALIGN_RODATA=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_QPDI=y
+CONFIG_CORESIGHT_SOURCE_DUMMY=y
+CONFIG_PFK=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=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_CRYPTO_CRC32_ARM64=y
+CONFIG_QMI_ENCDEC=y
--- /dev/null
+# 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_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_RCU_EXPERT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=15
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_SCHED_HMP=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+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_MEMBARRIER is not set
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+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_MSM8996=y
+CONFIG_PCI=y
+CONFIG_PCI_MSM=y
+CONFIG_ENABLE_FP_SIMD_SETTINGS=y
+CONFIG_MSM_APP_SETTINGS=y
+CONFIG_SCHED_MC=y
+CONFIG_NR_CPUS=8
+CONFIG_PREEMPT=y
+CONFIG_HZ_100=y
+CONFIG_CMA=y
+CONFIG_CMA_DEBUGFS=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y
+CONFIG_SECCOMP=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CMDLINE="console=ttyAMA0"
+# 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_PM_DEBUG=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_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_BOOST=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_LRO is not set
+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_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=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_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_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_VLAN_8021Q=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_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_SOCKEV_NLMCAST=y
+CONFIG_CAN=y
+CONFIG_CAN_RH850=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_BTFM_SLIM=y
+CONFIG_BTFM_SLIM_WCN3990=y
+CONFIG_CFG80211=y
+CONFIG_RFKILL=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_QSEECOM=y
+CONFIG_HDCP_QSEECOM=y
+CONFIG_PROFILER=y
+CONFIG_UID_SYS_STATS=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_UFS_QCOM_ICE=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_REQ_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_E1000E=y
+CONFIG_MSM_RMNET_MHI=y
+CONFIG_RNDIS_IPA=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CNSS_CRYPTO=y
+CONFIG_ATH_CARDS=y
+CONFIG_WIL6210=m
+CONFIG_CNSS=y
+CONFIG_CLD_LL_CORE=y
+CONFIG_BUS_AUTO_SUSPEND=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y
+CONFIG_SECURE_TOUCH=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_HS=y
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_DIAG_CHAR=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
+CONFIG_I2C_MSM_V2=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SOUNDWIRE=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SPMI=y
+CONFIG_PINCTRL_MSM8996=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_QPNP_PIN=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_RESET_XGENE=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1351_USB_CHARGER=y
+CONFIG_MSM_BCL_CTL=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_MSM_PM=y
+CONFIG_APSS_CORE_EA=y
+CONFIG_MSM_APM=y
+CONFIG_SENSORS_EPM_ADC=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_LIMITS_MONITOR=y
+CONFIG_LIMITS_LITE_HW=y
+CONFIG_THERMAL_MONITOR=y
+CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_WCD9335_CODEC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_FAN53555=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_QPNP_LABIBB=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_CPR3_HMSS=y
+CONFIG_REGULATOR_CPR3_MMSS=y
+CONFIG_REGULATOR_KRYO=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_PROXY_CONSUMER=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_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=y
+CONFIG_SOC_CAMERA_PLATFORM=y
+CONFIG_MSM_CAMERA=y
+CONFIG_MSM_CAMERA_DEBUG=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSMB_CAMERA_DEBUG=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI20_HEADER=y
+CONFIG_MSM_CSI22_HEADER=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSI31_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISPIF=y
+CONFIG_IMX134=y
+CONFIG_IMX132=y
+CONFIG_OV9724=y
+CONFIG_OV5648=y
+CONFIG_GC0339=y
+CONFIG_OV8825=y
+CONFIG_OV8865=y
+CONFIG_s5k4e1=y
+CONFIG_OV12830=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_FD=y
+CONFIG_MSM_JPEGDMA=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_VIDC_VMEM=y
+CONFIG_MSM_VIDC_GOVERNORS=y
+CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_QCOM_KGSL=y
+CONFIG_FB=y
+CONFIG_FB_MSM=y
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=m
+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_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MSM8996=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_USB=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_ISP1760=y
+CONFIG_USB_QTI_KS_BRIDGE=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_MSM_SSPHY_QMP=y
+CONFIG_MSM_QUSB_PHY=y
+CONFIG_USB_ULPI=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_QCRNDIS=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=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_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_USB_CONFIGFS_F_QDSS=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_SPI=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_EXYNOS=y
+CONFIG_MMC_CQ_HCI=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
+CONFIG_LEDS_QPNP_WLED=y
+CONFIG_LEDS_SYSCON=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_ESOC=y
+CONFIG_ESOC_DEV=y
+CONFIG_ESOC_CLIENT=y
+CONFIG_ESOC_DEBUG=y
+CONFIG_ESOC_MDM_4x=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SW_SYNC_USER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_QPNP_REVID=y
+CONFIG_QPNP_COINCELL=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
+CONFIG_GSI=y
+CONFIG_IPA3=y
+CONFIG_RMNET_IPA3=y
+CONFIG_IPA_UT=y
+CONFIG_GPIO_USB_DETECT=y
+CONFIG_MSM_MHI=y
+CONFIG_MSM_MHI_UCI=y
+CONFIG_MSM_MHI_DEBUG=y
+CONFIG_SEEMP_CORE=y
+CONFIG_USB_BAM=y
+CONFIG_MSM_MDSS_PLL=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_IOMMU_IO_PGTABLE_FAST=y
+CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y
+CONFIG_ARM_SMMU=y
+CONFIG_IOMMU_DEBUG=y
+CONFIG_IOMMU_DEBUG_TRACKING=y
+CONFIG_IOMMU_TESTS=y
+CONFIG_MSM_SMEM=y
+CONFIG_QPNP_HAPTIC=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMD_XPRT=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_SMEM_LOGGING=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_RPM_SMD=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_MSM_SERVICE_LOCATOR=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_SYSMON_GLINK_COMM=y
+CONFIG_MSM_IPC_ROUTER_MHI_XPRT=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_GLINK_PKT=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_QCOM_SCM=y
+CONFIG_QCOM_SCM_XPU=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_RUN_QUEUE_STATS=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_BOOT_TIME_MARKER=y
+CONFIG_MSM_ADSP_LOADER=y
+CONFIG_MSM_PERFORMANCE=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_TRACER_PKT=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_MSM_MPM_OF=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_AVTIMER=y
+CONFIG_QCOM_REMOTEQDSS=y
+CONFIG_MSM_SERVICE_NOTIFIER=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
+CONFIG_MSM_RPM_LOG=y
+CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_QCOM_SMCINVOKE=y
+CONFIG_MSM_CACHE_M4M_ERP64=y
+CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_CE=y
+CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_UE=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
+CONFIG_QCOM_BIMC_BWMON=y
+CONFIG_ARM_MEMLAT_MON=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=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_ARM_GIC_V3_ACL=y
+CONFIG_PHY_XGENE=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_SENSORS_SSC=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=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_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_MAGIC_SYSRQ=y
+CONFIG_DEBUG_OBJECTS=y
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_WORK=y
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
+CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_PANIC_ON_SCHED_BUG=y
+CONFIG_PANIC_ON_RT_THROTTLING=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_LIST=y
+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_IRQSOFF_TRACER=y
+CONFIG_PREEMPT_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_PANIC_ON_DATA_CORRUPTION=y
+CONFIG_ARM64_PTDUMP=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_FREE_PAGES_RDONLY=y
+CONFIG_KERNEL_TEXT_RDONLY=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_QPDI=y
+CONFIG_CORESIGHT_SOURCE_DUMMY=y
+CONFIG_PFK=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=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_CRYPTO_CRC32_ARM64=y
+CONFIG_XZ_DEC=y
+CONFIG_QMI_ENCDEC=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_GLINK_PKT=y
CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
CONFIG_QCOM_SCM=y
CONFIG_QCOM_SCM_XPU=y
CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
CONFIG_TRACER_PKT=y
CONFIG_MSM_MPM_OF=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_QBT1000=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_GLINK_PKT=y
CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
CONFIG_QCOM_SCM=y
CONFIG_QCOM_SCM_XPU=y
CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_TRACER_PKT=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_DUMMY=y
CONFIG_TUN=y
CONFIG_SKY2=y
+CONFIG_MSM_RMNET_MHI=y
CONFIG_RNDIS_IPA=y
CONFIG_SMSC911X=y
CONFIG_PPP=y
CONFIG_IPA3=y
CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
+CONFIG_MSM_MHI=y
+CONFIG_MSM_MHI_UCI=y
CONFIG_SEEMP_CORE=y
CONFIG_USB_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_BONDING=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+CONFIG_MSM_RMNET_MHI=y
CONFIG_RNDIS_IPA=y
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_IPA3=y
CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
+CONFIG_MSM_MHI=y
+CONFIG_MSM_MHI_UCI=y
+CONFIG_MSM_MHI_DEBUG=y
CONFIG_SEEMP_CORE=y
CONFIG_USB_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_MSM_RPM_SMD=y
CONFIG_QCOM_BUS_SCALING=y
CONFIG_MSM_SERVICE_LOCATOR=y
+CONFIG_QCOM_DCC=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_SYSMON_GLINK_COMM=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_ICNSS=y
CONFIG_MSM_RUN_QUEUE_STATS=y
CONFIG_MSM_BOOT_STATS=y
-CONFIG_QCOM_CPUSS_DUMP=y
CONFIG_MSM_ADSP_LOADER=y
CONFIG_MSM_CDSP_LOADER=y
CONFIG_MSM_PERFORMANCE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_DEBUG_OBJECTS_WORK=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=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
--- /dev/null
+/* Copyright (c) 2016-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 __ASM_APP_API_H
+#define __ASM_APP_API_H
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#define APP_SETTING_BIT 30
+#define MAX_ENTRIES 10
+
+/*
+ * APIs to set / clear the app setting bits
+ * in the register.
+ */
+#ifdef CONFIG_MSM_APP_API
+extern void set_app_setting_bit(uint32_t bit);
+extern void clear_app_setting_bit(uint32_t bit);
+extern void set_app_setting_bit_for_32bit_apps(void);
+extern void clear_app_setting_bit_for_32bit_apps(void);
+#else
+static inline void set_app_setting_bit(uint32_t bit) {}
+static inline void clear_app_setting_bit(uint32_t bit) {}
+static inline void set_app_setting_bit_for_32bit_apps(void) {}
+static inline void clear_app_setting_bit_for_32bit_apps(void) {}
+#endif
+
+#ifdef CONFIG_MSM_APP_SETTINGS
+extern void switch_app_setting_bit(struct task_struct *prev,
+ struct task_struct *next);
+extern void switch_32bit_app_setting_bit(struct task_struct *prev,
+ struct task_struct *next);
+extern void apply_app_setting_bit(struct file *file);
+extern bool use_app_setting;
+extern bool use_32bit_app_setting;
+extern bool use_32bit_app_setting_pro;
+#endif
+
+#endif
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013,2016-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
#define _ASM_DMA_CONTIGUOUS_H
#ifdef __KERNEL__
-#ifdef CONFIG_DMA_CMA
#include <linux/types.h>
#endif
#endif
-
-#endif
#include <linux/dma-debug.h>
#include <linux/kmemcheck.h>
#include <linux/kref.h>
-#include <linux/dma-mapping-fast.h>
struct dma_iommu_mapping {
/* iommu specific data */
spinlock_t lock;
struct kref kref;
-
+#ifdef CONFIG_IOMMU_IO_PGTABLE_FAST
struct dma_fast_smmu_mapping *fast;
+#endif
};
#ifdef CONFIG_ARM64_DMA_USE_IOMMU
*/
#include <asm/ptrace.h>
#include <asm/user.h>
+#include <asm/fpsimd.h>
/*
* AArch64 static relocation types.
((x)->e_flags & EF_ARM_EABI_MASK))
#define compat_start_thread compat_start_thread
-#define COMPAT_SET_PERSONALITY(ex) set_thread_flag(TIF_32BIT);
+#define COMPAT_SET_PERSONALITY(ex) \
+do { \
+ set_thread_flag(TIF_32BIT); \
+} while (0)
+
#define COMPAT_ARCH_DLINFO
extern int aarch32_setup_vectors_page(struct linux_binprm *bprm,
int uses_interp);
u32 num_regs);
extern void fpsimd_load_partial_state(struct fpsimd_partial_state *state);
+#ifdef CONFIG_ENABLE_FP_SIMD_SETTINGS
+extern void fpsimd_disable_trap(void);
+extern void fpsimd_enable_trap(void);
+#else
+static inline void fpsimd_disable_trap(void) {}
+static inline void fpsimd_enable_trap(void) {}
+#endif
+
#endif
#endif
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
arm64-obj-$(CONFIG_ACPI) += acpi.o
arm64-obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
+arm64-obj-$(CONFIG_MSM_APP_API) += app_api.o
+arm64-obj-$(CONFIG_MSM_APP_SETTINGS) += app_setting.o
arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o
arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o
arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o
--- /dev/null
+/* Copyright (c) 2016-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 <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/cpu.h>
+#include <linux/export.h>
+
+#include <asm/app_api.h>
+
+static spinlock_t spinlock;
+static spinlock_t spinlock_32bit_app;
+static DEFINE_PER_CPU(int, app_config_applied);
+static unsigned long app_config_set[NR_CPUS];
+static unsigned long app_config_clear[NR_CPUS];
+
+void set_app_setting_bit(uint32_t bit)
+{
+ unsigned long flags;
+ uint64_t reg;
+ int cpu;
+
+ spin_lock_irqsave(&spinlock, flags);
+ asm volatile("mrs %0, S3_1_C15_C15_0" : "=r" (reg));
+ reg = reg | BIT(bit);
+ isb();
+ asm volatile("msr S3_1_C15_C15_0, %0" : : "r" (reg));
+ isb();
+ if (bit == APP_SETTING_BIT) {
+ cpu = raw_smp_processor_id();
+ app_config_set[cpu]++;
+
+ this_cpu_write(app_config_applied, 1);
+ }
+ spin_unlock_irqrestore(&spinlock, flags);
+
+}
+EXPORT_SYMBOL(set_app_setting_bit);
+
+void clear_app_setting_bit(uint32_t bit)
+{
+ unsigned long flags;
+ uint64_t reg;
+ int cpu;
+
+ spin_lock_irqsave(&spinlock, flags);
+ asm volatile("mrs %0, S3_1_C15_C15_0" : "=r" (reg));
+ reg = reg & ~BIT(bit);
+ isb();
+ asm volatile("msr S3_1_C15_C15_0, %0" : : "r" (reg));
+ isb();
+ if (bit == APP_SETTING_BIT) {
+ cpu = raw_smp_processor_id();
+ app_config_clear[cpu]++;
+
+ this_cpu_write(app_config_applied, 0);
+ }
+ spin_unlock_irqrestore(&spinlock, flags);
+}
+EXPORT_SYMBOL(clear_app_setting_bit);
+
+void set_app_setting_bit_for_32bit_apps(void)
+{
+ unsigned long flags;
+ uint64_t reg;
+
+ spin_lock_irqsave(&spinlock_32bit_app, flags);
+ if (use_32bit_app_setting) {
+ asm volatile("mrs %0, S3_0_c15_c15_0 " : "=r" (reg));
+ reg = reg | BIT(24);
+ isb();
+ asm volatile("msr S3_0_c15_c15_0, %0" : : "r" (reg));
+ isb();
+ asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg));
+ reg = reg | BIT(18) | BIT(2) | BIT(0);
+ isb();
+ asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg));
+ isb();
+ } else if (use_32bit_app_setting_pro) {
+ asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg));
+ reg = reg | BIT(18);
+ isb();
+ asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg));
+ isb();
+ }
+ spin_unlock_irqrestore(&spinlock_32bit_app, flags);
+}
+EXPORT_SYMBOL(set_app_setting_bit_for_32bit_apps);
+
+void clear_app_setting_bit_for_32bit_apps(void)
+{
+ unsigned long flags;
+ uint64_t reg;
+
+ spin_lock_irqsave(&spinlock_32bit_app, flags);
+ if (use_32bit_app_setting) {
+ asm volatile("mrs %0, S3_0_c15_c15_0 " : "=r" (reg));
+ reg = reg & ~BIT(24);
+ isb();
+ asm volatile("msr S3_0_c15_c15_0, %0" : : "r" (reg));
+ isb();
+ asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg));
+ reg = reg & ~BIT(18);
+ reg = reg & ~BIT(2);
+ reg = reg & ~BIT(0);
+ isb();
+ asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg));
+ isb();
+ } else if (use_32bit_app_setting_pro) {
+ asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg));
+ reg = reg & ~BIT(18);
+ isb();
+ asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg));
+ isb();
+ }
+ spin_unlock_irqrestore(&spinlock_32bit_app, flags);
+}
+EXPORT_SYMBOL(clear_app_setting_bit_for_32bit_apps);
+
+static int __init init_app_api(void)
+{
+ spin_lock_init(&spinlock);
+ spin_lock_init(&spinlock_32bit_app);
+ return 0;
+}
+early_initcall(init_app_api);
--- /dev/null
+/* Copyright (c) 2016-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 <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+
+#include <asm/app_api.h>
+
+#define MAX_LEN 100
+
+static char *lib_names[MAX_ENTRIES];
+static unsigned int count;
+static struct mutex mutex;
+
+static char lib_str[MAX_LEN] = "";
+static struct kparam_string kps = {
+ .string = lib_str,
+ .maxlen = MAX_LEN,
+};
+static int set_name(const char *str, struct kernel_param *kp);
+module_param_call(lib_name, set_name, param_get_string, &kps, S_IWUSR);
+
+bool use_app_setting = true;
+module_param(use_app_setting, bool, 0644);
+MODULE_PARM_DESC(use_app_setting, "control use of app specific settings");
+
+bool use_32bit_app_setting = true;
+module_param(use_32bit_app_setting, bool, 0644);
+MODULE_PARM_DESC(use_32bit_app_setting, "control use of 32 bit app specific settings");
+
+bool use_32bit_app_setting_pro;
+module_param(use_32bit_app_setting_pro, bool, 0644);
+MODULE_PARM_DESC(use_32bit_app_setting_pro, "control use of 32 bit app specific settings");
+
+static int set_name(const char *str, struct kernel_param *kp)
+{
+ int len = strlen(str);
+ char *name;
+
+ if (len >= MAX_LEN) {
+ pr_err("app_setting: name string too long\n");
+ return -ENOSPC;
+ }
+
+ /*
+ * echo adds '\n' which we need to chop off later
+ */
+ name = kzalloc(len + 1, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ strlcpy(name, str, len + 1);
+
+ if (name[len - 1] == '\n')
+ name[len - 1] = '\0';
+
+ mutex_lock(&mutex);
+ if (count < MAX_ENTRIES) {
+ lib_names[count] = name;
+ /*
+ * mb to ensure that the new lib_names entry is present
+ * before updating the view presented by get_lib_names
+ */
+ mb();
+ count++;
+ } else {
+ pr_err("app_setting: set name failed. Max entries reached\n");
+ kfree(name);
+ mutex_unlock(&mutex);
+ return -EPERM;
+ }
+ mutex_unlock(&mutex);
+
+ return 0;
+}
+
+void switch_app_setting_bit(struct task_struct *prev, struct task_struct *next)
+{
+ if (prev->mm && unlikely(prev->mm->app_setting))
+ clear_app_setting_bit(APP_SETTING_BIT);
+
+ if (next->mm && unlikely(next->mm->app_setting))
+ set_app_setting_bit(APP_SETTING_BIT);
+}
+EXPORT_SYMBOL(switch_app_setting_bit);
+
+void switch_32bit_app_setting_bit(struct task_struct *prev,
+ struct task_struct *next)
+{
+ if (prev->mm && unlikely(is_compat_thread(task_thread_info(prev))))
+ clear_app_setting_bit_for_32bit_apps();
+
+ if (next->mm && unlikely(is_compat_thread(task_thread_info(next))))
+ set_app_setting_bit_for_32bit_apps();
+}
+EXPORT_SYMBOL(switch_32bit_app_setting_bit);
+
+void apply_app_setting_bit(struct file *file)
+{
+ bool found = false;
+ int i;
+
+ if (file && file->f_path.dentry) {
+ const char *name = file->f_path.dentry->d_name.name;
+
+ for (i = 0; i < count; i++) {
+ if (unlikely(!strcmp(name, lib_names[i]))) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ preempt_disable();
+ set_app_setting_bit(APP_SETTING_BIT);
+ /* This will take care of child processes as well */
+ current->mm->app_setting = 1;
+ preempt_enable();
+ }
+ }
+}
+EXPORT_SYMBOL(apply_app_setting_bit);
+
+static int __init app_setting_init(void)
+{
+ mutex_init(&mutex);
+ return 0;
+}
+module_init(app_setting_init);
ret
ENDPROC(fpsimd_load_partial_state)
+#ifdef CONFIG_ENABLE_FP_SIMD_SETTINGS
+ENTRY(fpsimd_enable_trap)
+ mrs x0, cpacr_el1
+ bic x0, x0, #(3 << 20)
+ orr x0, x0, #(1 << 20)
+ msr cpacr_el1, x0
+ ret
+ENDPROC(fpsimd_enable_trap)
+ENTRY(fpsimd_disable_trap)
+ mrs x0, cpacr_el1
+ orr x0, x0, #(3 << 20)
+ msr cpacr_el1, x0
+ ret
+ENDPROC(fpsimd_disable_trap)
+#endif
+
#endif
cmp x24, #ESR_ELx_EC_IABT_LOW // instruction abort in EL0
b.eq el0_ia
cmp x24, #ESR_ELx_EC_FP_ASIMD // FP/ASIMD access
- b.eq el0_fpsimd_acc
+ b.eq el0_fpsimd_acc_compat
cmp x24, #ESR_ELx_EC_FP_EXC32 // FP/ASIMD exception
b.eq el0_fpsimd_exc
cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception
mov x1, sp
bl do_fpsimd_acc
b ret_to_user
+el0_fpsimd_acc_compat:
+ /*
+ * Floating Point or Advanced SIMD access
+ */
+ enable_dbg
+ ct_user_exit
+ mov x0, x25
+ mov x1, sp
+ bl do_fpsimd_acc_compat
+ b ret_to_user
+
el0_fpsimd_exc:
/*
* Floating Point or Advanced SIMD exception
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <asm/fpsimd.h>
#include <asm/cputype.h>
+#include <asm/app_api.h>
#define FPEXC_IOF (1 << 0)
#define FPEXC_DZF (1 << 1)
#define FPEXC_IXF (1 << 4)
#define FPEXC_IDF (1 << 7)
+#define FP_SIMD_BIT 31
+
/*
* In order to reduce the number of times the FPSIMD state is needlessly saved
* and restored, we need to keep track of two things:
* whatever is in the FPSIMD registers is not saved to memory, but discarded.
*/
static DEFINE_PER_CPU(struct fpsimd_state *, fpsimd_last_state);
+static DEFINE_PER_CPU(int, fpsimd_stg_enable);
+
+static int fpsimd_settings = 0x1; /* default = 0x1 */
+module_param(fpsimd_settings, int, 0644);
+
+void fpsimd_settings_enable(void)
+{
+ set_app_setting_bit(FP_SIMD_BIT);
+}
+
+void fpsimd_settings_disable(void)
+{
+ clear_app_setting_bit(FP_SIMD_BIT);
+}
/*
* Trapped FP/ASIMD access.
*/
void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs)
{
- /* TODO: implement lazy context saving/restoring */
- WARN_ON(1);
+ if (!fpsimd_settings)
+ return;
+
+ fpsimd_disable_trap();
+ fpsimd_settings_disable();
+ this_cpu_write(fpsimd_stg_enable, 0);
+}
+
+void do_fpsimd_acc_compat(unsigned int esr, struct pt_regs *regs)
+{
+ if (!fpsimd_settings)
+ return;
+
+ fpsimd_disable_trap();
+ fpsimd_settings_enable();
+ this_cpu_write(fpsimd_stg_enable, 1);
}
/*
if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE))
fpsimd_save_state(¤t->thread.fpsimd_state);
+ if (fpsimd_settings && __this_cpu_read(fpsimd_stg_enable)) {
+ fpsimd_settings_disable();
+ this_cpu_write(fpsimd_stg_enable, 0);
+ }
+
if (next->mm) {
/*
* If we are switching to a task whose most recent userland
else
set_ti_thread_flag(task_thread_info(next),
TIF_FOREIGN_FPSTATE);
+
+ if (!fpsimd_settings)
+ return;
+
+ if (test_ti_thread_flag(task_thread_info(next), TIF_32BIT))
+ fpsimd_enable_trap();
+ else
+ fpsimd_disable_trap();
}
}
*/
bl cpu_do_resume
+#ifdef CONFIG_KASAN
+ mov x0, sp
+ bl kasan_unpoison_remaining_stack
+#endif
+
ldp x19, x20, [x29, #16]
ldp x21, x22, [x29, #32]
ldp x23, x24, [x29, #48]
#define strlen_user(str) strnlen_user(str, 32767)
-extern unsigned long __must_check __copy_user_zeroing(void *to,
- const void __user *from,
- unsigned long n);
+extern unsigned long raw_copy_from_user(void *to, const void __user *from,
+ unsigned long n);
static inline unsigned long
copy_from_user(void *to, const void __user *from, unsigned long n)
{
+ unsigned long res = n;
if (likely(access_ok(VERIFY_READ, from, n)))
- return __copy_user_zeroing(to, from, n);
- memset(to, 0, n);
- return n;
+ res = raw_copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
-#define __copy_from_user(to, from, n) __copy_user_zeroing(to, from, n)
+#define __copy_from_user(to, from, n) raw_copy_from_user(to, from, n)
#define __copy_from_user_inatomic __copy_from_user
extern unsigned long __must_check __copy_user(void __user *to,
COPY \
"1:\n" \
" .section .fixup,\"ax\"\n" \
- " MOV D1Ar1,#0\n" \
FIXUP \
" MOVT D1Ar1,#HI(1b)\n" \
" JUMP D1Ar1,#LO(1b)\n" \
"MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"22:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #32\n" \
"23:\n" \
- "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "SUB %3, %3, #32\n" \
"24:\n" \
+ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "25:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "26:\n" \
"SUB %3, %3, #32\n" \
"DCACHE [%1+#-64], D0Ar6\n" \
"BR $Lloop"id"\n" \
\
"MOV RAPF, %1\n" \
- "25:\n" \
+ "27:\n" \
"MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "26:\n" \
+ "28:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "29:\n" \
"SUB %3, %3, #32\n" \
- "27:\n" \
+ "30:\n" \
"MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "28:\n" \
+ "31:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "32:\n" \
"SUB %0, %0, #8\n" \
- "29:\n" \
+ "33:\n" \
"SETL [%0++], D0.7, D1.7\n" \
"SUB %3, %3, #32\n" \
"1:" \
" .long 26b,3b\n" \
" .long 27b,3b\n" \
" .long 28b,3b\n" \
- " .long 29b,4b\n" \
+ " .long 29b,3b\n" \
+ " .long 30b,3b\n" \
+ " .long 31b,3b\n" \
+ " .long 32b,3b\n" \
+ " .long 33b,4b\n" \
" .previous\n" \
: "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \
: "0" (to), "1" (from), "2" (ret), "3" (n) \
- : "D1Ar1", "D0Ar2", "memory")
+ : "D1Ar1", "D0Ar2", "cc", "memory")
/* rewind 'to' and 'from' pointers when a fault occurs
*
#define __asm_copy_to_user_64bit_rapf_loop(to, from, ret, n, id)\
__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \
"LSR D0Ar2, D0Ar2, #8\n" \
- "AND D0Ar2, D0Ar2, #0x7\n" \
+ "ANDS D0Ar2, D0Ar2, #0x7\n" \
"ADDZ D0Ar2, D0Ar2, #4\n" \
"SUB D0Ar2, D0Ar2, #1\n" \
"MOV D1Ar1, #4\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"22:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
"23:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "24:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
"SUB %3, %3, #16\n" \
- "25:\n" \
+ "24:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "26:\n" \
+ "25:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "26:\n" \
"SUB %3, %3, #16\n" \
"27:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"28:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "29:\n" \
+ "SUB %3, %3, #16\n" \
+ "30:\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "31:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "32:\n" \
"SUB %3, %3, #16\n" \
"DCACHE [%1+#-64], D0Ar6\n" \
"BR $Lloop"id"\n" \
\
"MOV RAPF, %1\n" \
- "29:\n" \
+ "33:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "30:\n" \
+ "34:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "35:\n" \
"SUB %3, %3, #16\n" \
- "31:\n" \
+ "36:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "32:\n" \
+ "37:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "38:\n" \
"SUB %3, %3, #16\n" \
- "33:\n" \
+ "39:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "34:\n" \
+ "40:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "41:\n" \
"SUB %3, %3, #16\n" \
- "35:\n" \
+ "42:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "36:\n" \
+ "43:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "44:\n" \
"SUB %0, %0, #4\n" \
- "37:\n" \
+ "45:\n" \
"SETD [%0++], D0.7\n" \
"SUB %3, %3, #16\n" \
"1:" \
" .long 34b,3b\n" \
" .long 35b,3b\n" \
" .long 36b,3b\n" \
- " .long 37b,4b\n" \
+ " .long 37b,3b\n" \
+ " .long 38b,3b\n" \
+ " .long 39b,3b\n" \
+ " .long 40b,3b\n" \
+ " .long 41b,3b\n" \
+ " .long 42b,3b\n" \
+ " .long 43b,3b\n" \
+ " .long 44b,3b\n" \
+ " .long 45b,4b\n" \
" .previous\n" \
: "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \
: "0" (to), "1" (from), "2" (ret), "3" (n) \
- : "D1Ar1", "D0Ar2", "memory")
+ : "D1Ar1", "D0Ar2", "cc", "memory")
/* rewind 'to' and 'from' pointers when a fault occurs
*
#define __asm_copy_to_user_32bit_rapf_loop(to, from, ret, n, id)\
__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \
"LSR D0Ar2, D0Ar2, #8\n" \
- "AND D0Ar2, D0Ar2, #0x7\n" \
+ "ANDS D0Ar2, D0Ar2, #0x7\n" \
"ADDZ D0Ar2, D0Ar2, #4\n" \
"SUB D0Ar2, D0Ar2, #1\n" \
"MOV D1Ar1, #4\n" \
if ((unsigned long) src & 1) {
__asm_copy_to_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 1) {
/* Worst case - byte copy */
while (n > 0) {
__asm_copy_to_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
}
if (((unsigned long) src & 2) && n >= 2) {
__asm_copy_to_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 2) {
/* Second worst case - word copy */
while (n >= 2) {
__asm_copy_to_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
}
while (n >= 8) {
__asm_copy_to_user_8x64(dst, src, retn);
n -= 8;
+ if (retn)
+ return retn + n;
}
}
if (n >= RAPF_MIN_BUF_SIZE) {
while (n >= 8) {
__asm_copy_to_user_8x64(dst, src, retn);
n -= 8;
+ if (retn)
+ return retn + n;
}
}
#endif
while (n >= 16) {
__asm_copy_to_user_16(dst, src, retn);
n -= 16;
+ if (retn)
+ return retn + n;
}
while (n >= 4) {
__asm_copy_to_user_4(dst, src, retn);
n -= 4;
+ if (retn)
+ return retn + n;
}
switch (n) {
break;
}
+ /*
+ * If we get here, retn correctly reflects the number of failing
+ * bytes.
+ */
return retn;
}
EXPORT_SYMBOL(__copy_user);
__asm_copy_user_cont(to, from, ret, \
" GETB D1Ar1,[%1++]\n" \
"2: SETB [%0++],D1Ar1\n", \
- "3: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
+ "3: ADD %2,%2,#1\n", \
" .long 2b,3b\n")
#define __asm_copy_from_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
__asm_copy_user_cont(to, from, ret, \
" GETW D1Ar1,[%1++]\n" \
"2: SETW [%0++],D1Ar1\n" COPY, \
- "3: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
+ "3: ADD %2,%2,#2\n" FIXUP, \
" .long 2b,3b\n" TENTRY)
#define __asm_copy_from_user_2(to, from, ret) \
__asm_copy_from_user_2x_cont(to, from, ret, \
" GETB D1Ar1,[%1++]\n" \
"4: SETB [%0++],D1Ar1\n", \
- "5: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
+ "5: ADD %2,%2,#1\n", \
" .long 4b,5b\n")
#define __asm_copy_from_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
__asm_copy_user_cont(to, from, ret, \
" GETD D1Ar1,[%1++]\n" \
"2: SETD [%0++],D1Ar1\n" COPY, \
- "3: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
+ "3: ADD %2,%2,#4\n" FIXUP, \
" .long 2b,3b\n" TENTRY)
#define __asm_copy_from_user_4(to, from, ret) \
__asm_copy_from_user_4x_cont(to, from, ret, "", "", "")
-#define __asm_copy_from_user_5(to, from, ret) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "4: SETB [%0++],D1Ar1\n", \
- "5: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 4b,5b\n")
-
-#define __asm_copy_from_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "4: SETW [%0++],D1Ar1\n" COPY, \
- "5: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 4b,5b\n" TENTRY)
-
-#define __asm_copy_from_user_6(to, from, ret) \
- __asm_copy_from_user_6x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_7(to, from, ret) \
- __asm_copy_from_user_6x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "6: SETB [%0++],D1Ar1\n", \
- "7: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 6b,7b\n")
-
-#define __asm_copy_from_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "4: SETD [%0++],D1Ar1\n" COPY, \
- "5: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 4b,5b\n" TENTRY)
-
-#define __asm_copy_from_user_8(to, from, ret) \
- __asm_copy_from_user_8x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_9(to, from, ret) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "6: SETB [%0++],D1Ar1\n", \
- "7: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 6b,7b\n")
-
-#define __asm_copy_from_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "6: SETW [%0++],D1Ar1\n" COPY, \
- "7: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 6b,7b\n" TENTRY)
-
-#define __asm_copy_from_user_10(to, from, ret) \
- __asm_copy_from_user_10x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_11(to, from, ret) \
- __asm_copy_from_user_10x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "8: SETB [%0++],D1Ar1\n", \
- "9: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 8b,9b\n")
-
-#define __asm_copy_from_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "6: SETD [%0++],D1Ar1\n" COPY, \
- "7: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 6b,7b\n" TENTRY)
-
-#define __asm_copy_from_user_12(to, from, ret) \
- __asm_copy_from_user_12x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_13(to, from, ret) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "8: SETB [%0++],D1Ar1\n", \
- "9: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 8b,9b\n")
-
-#define __asm_copy_from_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "8: SETW [%0++],D1Ar1\n" COPY, \
- "9: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 8b,9b\n" TENTRY)
-
-#define __asm_copy_from_user_14(to, from, ret) \
- __asm_copy_from_user_14x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_15(to, from, ret) \
- __asm_copy_from_user_14x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "10: SETB [%0++],D1Ar1\n", \
- "11: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 10b,11b\n")
-
-#define __asm_copy_from_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "8: SETD [%0++],D1Ar1\n" COPY, \
- "9: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 8b,9b\n" TENTRY)
-
-#define __asm_copy_from_user_16(to, from, ret) \
- __asm_copy_from_user_16x_cont(to, from, ret, "", "", "")
-
#define __asm_copy_from_user_8x64(to, from, ret) \
asm volatile ( \
" GETL D0Ar2,D1Ar1,[%1++]\n" \
"2: SETL [%0++],D0Ar2,D1Ar1\n" \
"1:\n" \
" .section .fixup,\"ax\"\n" \
- " MOV D1Ar1,#0\n" \
- " MOV D0Ar2,#0\n" \
"3: ADD %2,%2,#8\n" \
- " SETL [%0++],D0Ar2,D1Ar1\n" \
" MOVT D0Ar2,#HI(1b)\n" \
" JUMP D0Ar2,#LO(1b)\n" \
" .previous\n" \
*
* Rationale:
* A fault occurs while reading from user buffer, which is the
- * source. Since the fault is at a single address, we only
- * need to rewind by 8 bytes.
+ * source.
* Since we don't write to kernel buffer until we read first,
* the kernel buffer is at the right state and needn't be
- * corrected.
+ * corrected, but the source must be rewound to the beginning of
+ * the block, which is LSM_STEP*8 bytes.
+ * LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ * and stored in D0Ar2
+ *
+ * NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ * LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ * a fault happens at the 4th write, LSM_STEP will be 0
+ * instead of 4. The code copes with that.
*/
#define __asm_copy_from_user_64bit_rapf_loop(to, from, ret, n, id) \
__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \
- "SUB %1, %1, #8\n")
+ "LSR D0Ar2, D0Ar2, #5\n" \
+ "ANDS D0Ar2, D0Ar2, #0x38\n" \
+ "ADDZ D0Ar2, D0Ar2, #32\n" \
+ "SUB %1, %1, D0Ar2\n")
/* rewind 'from' pointer when a fault occurs
*
* Rationale:
* A fault occurs while reading from user buffer, which is the
- * source. Since the fault is at a single address, we only
- * need to rewind by 4 bytes.
+ * source.
* Since we don't write to kernel buffer until we read first,
* the kernel buffer is at the right state and needn't be
- * corrected.
+ * corrected, but the source must be rewound to the beginning of
+ * the block, which is LSM_STEP*4 bytes.
+ * LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ * and stored in D0Ar2
+ *
+ * NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ * LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ * a fault happens at the 4th write, LSM_STEP will be 0
+ * instead of 4. The code copes with that.
*/
#define __asm_copy_from_user_32bit_rapf_loop(to, from, ret, n, id) \
__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \
- "SUB %1, %1, #4\n")
+ "LSR D0Ar2, D0Ar2, #6\n" \
+ "ANDS D0Ar2, D0Ar2, #0x1c\n" \
+ "ADDZ D0Ar2, D0Ar2, #16\n" \
+ "SUB %1, %1, D0Ar2\n")
-/* Copy from user to kernel, zeroing the bytes that were inaccessible in
- userland. The return-value is the number of bytes that were
- inaccessible. */
-unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
- unsigned long n)
+/*
+ * Copy from user to kernel. The return-value is the number of bytes that were
+ * inaccessible.
+ */
+unsigned long raw_copy_from_user(void *pdst, const void __user *psrc,
+ unsigned long n)
{
register char *dst asm ("A0.2") = pdst;
register const char __user *src asm ("A1.2") = psrc;
if ((unsigned long) src & 1) {
__asm_copy_from_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 1) {
/* Worst case - byte copy */
__asm_copy_from_user_1(dst, src, retn);
n--;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
if (((unsigned long) src & 2) && n >= 2) {
__asm_copy_from_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 2) {
/* Second worst case - word copy */
__asm_copy_from_user_2(dst, src, retn);
n -= 2;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
- /* We only need one check after the unalignment-adjustments,
- because if both adjustments were done, either both or
- neither reference had an exception. */
- if (retn != 0)
- goto copy_exception_bytes;
-
#ifdef USE_RAPF
/* 64 bit copy loop */
if (!(((unsigned long) src | (unsigned long) dst) & 7)) {
__asm_copy_from_user_8x64(dst, src, retn);
n -= 8;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
__asm_copy_from_user_8x64(dst, src, retn);
n -= 8;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
#endif
n -= 4;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
/* If we get here, there were no memory read faults. */
/* If we get here, retn correctly reflects the number of failing
bytes. */
return retn;
-
- copy_exception_bytes:
- /* We already have "retn" bytes cleared, and need to clear the
- remaining "n" bytes. A non-optimized simple byte-for-byte in-line
- memset is preferred here, since this isn't speed-critical code and
- we'd rather have this a leaf-function than calling memset. */
- {
- char *endp;
- for (endp = dst + n; dst < endp; dst++)
- *dst = 0;
- }
-
- return retn + n;
}
-EXPORT_SYMBOL(__copy_user_zeroing);
+EXPORT_SYMBOL(raw_copy_from_user);
#define __asm_clear_8x64(to, ret) \
asm volatile ( \
select HAVE_CONTEXT_TRACKING
select HAVE_GENERIC_DMA_COHERENT
select HAVE_IDE
+ select HAVE_IRQ_EXIT_ON_IRQ_STACK
select HAVE_OPROFILE
select HAVE_PERF_EVENTS
select PERF_USE_VMALLOC
select CPU_SUPPORTS_HIGHMEM
select CPU_SUPPORTS_MSA
select GENERIC_CSUM
- select MIPS_O32_FP64_SUPPORT if MIPS32_O32
+ select MIPS_O32_FP64_SUPPORT if 32BIT || MIPS32_O32
help
Choose this option to build a kernel for release 6 or later of the
MIPS64 architecture. New MIPS processors, starting with the Warrior
#include <irq.h>
+#define IRQ_STACK_SIZE THREAD_SIZE
+
+extern void *irq_stack[NR_CPUS];
+
+static inline bool on_irq_stack(int cpu, unsigned long sp)
+{
+ unsigned long low = (unsigned long)irq_stack[cpu];
+ unsigned long high = low + IRQ_STACK_SIZE;
+
+ return (low <= sp && sp <= high);
+}
+
#ifdef CONFIG_I8259
static inline int irq_canonicalize(int irq)
{
" andi %[ticket], %[ticket], 0xffff \n"
" bne %[ticket], %[my_ticket], 4f \n"
" subu %[ticket], %[my_ticket], %[ticket] \n"
- "2: \n"
+ "2: .insn \n"
" .subsection 2 \n"
"4: andi %[ticket], %[ticket], 0xffff \n"
" sll %[ticket], 5 \n"
" sc %[ticket], %[ticket_ptr] \n"
" beqz %[ticket], 1b \n"
" li %[ticket], 1 \n"
- "2: \n"
+ "2: .insn \n"
" .subsection 2 \n"
"3: b 2b \n"
" li %[ticket], 0 \n"
" .set reorder \n"
__WEAK_LLSC_MB
" li %2, 1 \n"
- "2: \n"
+ "2: .insn \n"
: "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp), "=&r" (ret)
: GCC_OFF_SMALL_ASM() (rw->lock)
: "memory");
" lui %1, 0x8000 \n"
" sc %1, %0 \n"
" li %2, 1 \n"
- "2: \n"
+ "2: .insn \n"
: "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp),
"=&r" (ret)
: GCC_OFF_SMALL_ASM() (rw->lock)
LONG_S $25, PT_R25(sp)
LONG_S $28, PT_R28(sp)
LONG_S $31, PT_R31(sp)
+
+ /* Set thread_info if we're coming from user mode */
+ mfc0 k0, CP0_STATUS
+ sll k0, 3 /* extract cu0 bit */
+ bltz k0, 9f
+
ori $28, sp, _THREAD_MASK
xori $28, _THREAD_MASK
#ifdef CONFIG_CPU_CAVIUM_OCTEON
.set mips64
pref 0, 0($28) /* Prefetch the current pointer */
#endif
+9:
.set pop
.endm
OFFSET(TI_REGS, thread_info, regs);
DEFINE(_THREAD_SIZE, THREAD_SIZE);
DEFINE(_THREAD_MASK, THREAD_MASK);
+ DEFINE(_IRQ_STACK_SIZE, IRQ_STACK_SIZE);
BLANK();
}
LONG_L s0, TI_REGS($28)
LONG_S sp, TI_REGS($28)
- PTR_LA ra, ret_from_irq
- PTR_LA v0, plat_irq_dispatch
- jr v0
+
+ /*
+ * SAVE_ALL ensures we are using a valid kernel stack for the thread.
+ * Check if we are already using the IRQ stack.
+ */
+ move s1, sp # Preserve the sp
+
+ /* Get IRQ stack for this CPU */
+ ASM_CPUID_MFC0 k0, ASM_SMP_CPUID_REG
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+ lui k1, %hi(irq_stack)
+#else
+ lui k1, %highest(irq_stack)
+ daddiu k1, %higher(irq_stack)
+ dsll k1, 16
+ daddiu k1, %hi(irq_stack)
+ dsll k1, 16
+#endif
+ LONG_SRL k0, SMP_CPUID_PTRSHIFT
+ LONG_ADDU k1, k0
+ LONG_L t0, %lo(irq_stack)(k1)
+
+ # Check if already on IRQ stack
+ PTR_LI t1, ~(_THREAD_SIZE-1)
+ and t1, t1, sp
+ beq t0, t1, 2f
+
+ /* Switch to IRQ stack */
+ li t1, _IRQ_STACK_SIZE
+ PTR_ADD sp, t0, t1
+
+2:
+ jal plat_irq_dispatch
+
+ /* Restore sp */
+ move sp, s1
+
+ j ret_from_irq
#ifdef CONFIG_CPU_MICROMIPS
nop
#endif
LONG_L s0, TI_REGS($28)
LONG_S sp, TI_REGS($28)
- PTR_LA ra, ret_from_irq
- jr v0
+
+ /*
+ * SAVE_ALL ensures we are using a valid kernel stack for the thread.
+ * Check if we are already using the IRQ stack.
+ */
+ move s1, sp # Preserve the sp
+
+ /* Get IRQ stack for this CPU */
+ ASM_CPUID_MFC0 k0, ASM_SMP_CPUID_REG
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+ lui k1, %hi(irq_stack)
+#else
+ lui k1, %highest(irq_stack)
+ daddiu k1, %higher(irq_stack)
+ dsll k1, 16
+ daddiu k1, %hi(irq_stack)
+ dsll k1, 16
+#endif
+ LONG_SRL k0, SMP_CPUID_PTRSHIFT
+ LONG_ADDU k1, k0
+ LONG_L t0, %lo(irq_stack)(k1)
+
+ # Check if already on IRQ stack
+ PTR_LI t1, ~(_THREAD_SIZE-1)
+ and t1, t1, sp
+ beq t0, t1, 2f
+
+ /* Switch to IRQ stack */
+ li t1, _IRQ_STACK_SIZE
+ PTR_ADD sp, t0, t1
+
+2:
+ jalr v0
+
+ /* Restore sp */
+ move sp, s1
+
+ j ret_from_irq
END(except_vec_vi_handler)
/*
#include <linux/atomic.h>
#include <asm/uaccess.h>
+void *irq_stack[NR_CPUS];
+
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
* each architecture has to answer this themselves.
irq_set_noprobe(i);
arch_init_irq();
+
+ for_each_possible_cpu(i) {
+ int irq_pages = IRQ_STACK_SIZE / PAGE_SIZE;
+ void *s = (void *)__get_free_pages(GFP_KERNEL, irq_pages);
+
+ irq_stack[i] = s;
+ pr_debug("CPU%d IRQ stack at 0x%p - 0x%p\n", i,
+ irq_stack[i], irq_stack[i] + IRQ_STACK_SIZE);
+ }
}
#ifdef CONFIG_DEBUG_STACKOVERFLOW
#include <asm/cpu.h>
#include <asm/dsp.h>
#include <asm/fpu.h>
+#include <asm/irq.h>
#include <asm/msa.h>
#include <asm/pgtable.h>
#include <asm/mipsregs.h>
unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
unsigned long pc, unsigned long *ra)
{
- unsigned long stack_page = (unsigned long)task_stack_page(task);
+ unsigned long stack_page = 0;
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ if (on_irq_stack(cpu, *sp)) {
+ stack_page = (unsigned long)irq_stack[cpu];
+ break;
+ }
+ }
+
+ if (!stack_page)
+ stack_page = (unsigned long)task_stack_page(task);
+
return unwind_stack_by_address(stack_page, sp, pc, ra);
}
#endif
if (!np_xbar)
panic("Failed to load xbar nodes from devicetree");
- if (of_address_to_resource(np_pmu, 0, &res_xbar))
+ if (of_address_to_resource(np_xbar, 0, &res_xbar))
panic("Failed to get xbar resources");
if (request_mem_region(res_xbar.start, resource_size(&res_xbar),
res_xbar.name) < 0)
static void build_huge_handler_tail(u32 **p, struct uasm_reloc **r,
struct uasm_label **l,
unsigned int pte,
- unsigned int ptr)
+ unsigned int ptr,
+ unsigned int flush)
{
#ifdef CONFIG_SMP
UASM_i_SC(p, pte, 0, ptr);
#else
UASM_i_SW(p, pte, 0, ptr);
#endif
+ if (cpu_has_ftlb && flush) {
+ BUG_ON(!cpu_has_tlbinv);
+
+ UASM_i_MFC0(p, ptr, C0_ENTRYHI);
+ uasm_i_ori(p, ptr, ptr, MIPS_ENTRYHI_EHINV);
+ UASM_i_MTC0(p, ptr, C0_ENTRYHI);
+ build_tlb_write_entry(p, l, r, tlb_indexed);
+
+ uasm_i_xori(p, ptr, ptr, MIPS_ENTRYHI_EHINV);
+ UASM_i_MTC0(p, ptr, C0_ENTRYHI);
+ build_huge_update_entries(p, pte, ptr);
+ build_huge_tlb_write_entry(p, l, r, pte, tlb_random, 0);
+
+ return;
+ }
+
build_huge_update_entries(p, pte, ptr);
build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed, 0);
}
uasm_l_tlbl_goaround2(&l, p);
}
uasm_i_ori(&p, wr.r1, wr.r1, (_PAGE_ACCESSED | _PAGE_VALID));
- build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
+ build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1);
#endif
uasm_l_nopage_tlbl(&l, p);
build_tlb_probe_entry(&p);
uasm_i_ori(&p, wr.r1, wr.r1,
_PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
- build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
+ build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1);
#endif
uasm_l_nopage_tlbs(&l, p);
build_tlb_probe_entry(&p);
uasm_i_ori(&p, wr.r1, wr.r1,
_PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
- build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
+ build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 0);
#endif
uasm_l_nopage_tlbm(&l, p);
static struct rt2880_pmx_func jtag_func[] = { FUNC("jtag", 0, 17, 5) };
static struct rt2880_pmx_func mdio_func[] = { FUNC("mdio", 0, 22, 2) };
static struct rt2880_pmx_func lna_a_func[] = { FUNC("lna a", 0, 32, 3) };
-static struct rt2880_pmx_func lna_g_func[] = { FUNC("lna a", 0, 35, 3) };
+static struct rt2880_pmx_func lna_g_func[] = { FUNC("lna g", 0, 35, 3) };
static struct rt2880_pmx_func pci_func[] = {
FUNC("pci-dev", 0, 40, 32),
FUNC("pci-host2", 1, 40, 32),
FUNC("pci-fnc", 3, 40, 32)
};
static struct rt2880_pmx_func ge1_func[] = { FUNC("ge1", 0, 72, 12) };
-static struct rt2880_pmx_func ge2_func[] = { FUNC("ge1", 0, 84, 12) };
+static struct rt2880_pmx_func ge2_func[] = { FUNC("ge2", 0, 84, 12) };
static struct rt2880_pmx_group rt3883_pinmux_data[] = {
GRP("i2c", i2c_func, 1, RT3883_GPIO_MODE_I2C),
return alloc_bootmem_align(size, align);
}
+int __init early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
+ bool nomap)
+{
+ reserve_bootmem(base, size, BOOTMEM_DEFAULT);
+ return 0;
+}
+
void __init early_init_devtree(void *params)
{
__be32 *dtb = (u32 *)__dtb_start;
}
#endif /* CONFIG_BLK_DEV_INITRD */
+ early_init_fdt_reserve_self();
+ early_init_fdt_scan_reserved_mem();
+
unflatten_and_copy_device_tree();
setup_cpuinfo();
nb = aligninfo[instr].len;
flags = aligninfo[instr].flags;
- /* ldbrx/stdbrx overlap lfs/stfs in the DSISR unfortunately */
- if (IS_XFORM(instruction) && ((instruction >> 1) & 0x3ff) == 532) {
- nb = 8;
- flags = LD+SW;
- } else if (IS_XFORM(instruction) &&
- ((instruction >> 1) & 0x3ff) == 660) {
- nb = 8;
- flags = ST+SW;
+ /*
+ * Handle some cases which give overlaps in the DSISR values.
+ */
+ if (IS_XFORM(instruction)) {
+ switch (get_xop(instruction)) {
+ case 532: /* ldbrx */
+ nb = 8;
+ flags = LD+SW;
+ break;
+ case 660: /* stdbrx */
+ nb = 8;
+ flags = ST+SW;
+ break;
+ case 20: /* lwarx */
+ case 84: /* ldarx */
+ case 116: /* lharx */
+ case 276: /* lqarx */
+ return 0; /* not emulated ever */
+ }
}
/* Byteswap little endian loads and stores */
addi r8,r1,INT_FRAME_SIZE /* Get the kprobed function entry */
- lwz r3,GPR1(r1)
+ ld r3,GPR1(r1)
subi r3,r3,INT_FRAME_SIZE /* dst: Allocate a trampoline exception frame */
mr r4,r1 /* src: current exception frame */
mr r1,r3 /* Reroute the trampoline frame to r1 */
addi r6,r6,8
bdnz 2b
- /* Do real store operation to complete stwu */
- lwz r5,GPR1(r1)
+ /* Do real store operation to complete stdu */
+ ld r5,GPR1(r1)
std r8,0(r5)
/* Clear _TIF_EMULATE_STACK_STORE flag */
unsigned long lpcr = mfspr(SPRN_LPCR);
mtspr(SPRN_LPCR, lpcr | LPCR_AIL_3);
}
+
+ /*
+ * Fixup HFSCR:TM based on CPU features. The bit is set by our
+ * early asm init because at that point we haven't updated our
+ * CPU features from firmware and device-tree. Here we have,
+ * so let's do it.
+ */
+ if (cpu_has_feature(CPU_FTR_HVMODE) && !cpu_has_feature(CPU_FTR_TM_COMP))
+ mtspr(SPRN_HFSCR, mfspr(SPRN_HFSCR) & ~HFSCR_TM);
}
/*
unsigned long psize = batch->psize;
int ssize = batch->ssize;
int i;
+ unsigned int use_local;
+
+ use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) &&
+ mmu_psize_defs[psize].tlbiel && !cxl_ctx_in_use();
local_irq_save(flags);
} pte_iterate_hashed_end();
}
- if (mmu_has_feature(MMU_FTR_TLBIEL) &&
- mmu_psize_defs[psize].tlbiel && local) {
+ if (use_local) {
asm volatile("ptesync":::"memory");
for (i = 0; i < number; i++) {
vpn = batch->vpn[i];
unsigned long decompress_kernel(void)
{
- unsigned long output_addr;
- unsigned char *output;
+ void *output, *kernel_end;
- output_addr = ((unsigned long) &_end + HEAP_SIZE + 4095UL) & -4096UL;
- check_ipl_parmblock((void *) 0, output_addr + SZ__bss_start);
- memset(&_bss, 0, &_ebss - &_bss);
- free_mem_ptr = (unsigned long)&_end;
- free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
- output = (unsigned char *) output_addr;
+ output = (void *) ALIGN((unsigned long) &_end + HEAP_SIZE, PAGE_SIZE);
+ kernel_end = output + SZ__bss_start;
+ check_ipl_parmblock((void *) 0, (unsigned long) kernel_end);
#ifdef CONFIG_BLK_DEV_INITRD
/*
* Move the initrd right behind the end of the decompressed
- * kernel image.
+ * kernel image. This also prevents initrd corruption caused by
+ * bss clearing since kernel_end will always be located behind the
+ * current bss section..
*/
- if (INITRD_START && INITRD_SIZE &&
- INITRD_START < (unsigned long) output + SZ__bss_start) {
- check_ipl_parmblock(output + SZ__bss_start,
- INITRD_START + INITRD_SIZE);
- memmove(output + SZ__bss_start,
- (void *) INITRD_START, INITRD_SIZE);
- INITRD_START = (unsigned long) output + SZ__bss_start;
+ if (INITRD_START && INITRD_SIZE && kernel_end > (void *) INITRD_START) {
+ check_ipl_parmblock(kernel_end, INITRD_SIZE);
+ memmove(kernel_end, (void *) INITRD_START, INITRD_SIZE);
+ INITRD_START = (unsigned long) kernel_end;
}
#endif
+ /*
+ * Clear bss section. free_mem_ptr and free_mem_end_ptr need to be
+ * initialized afterwards since they reside in bss.
+ */
+ memset(&_bss, 0, &_ebss - &_bss);
+ free_mem_ptr = (unsigned long) &_end;
+ free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
+
puts("Uncompressing Linux... ");
__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
puts("Ok, booting the kernel.\n");
{
pgste_t pgste;
+ if (pte_present(entry))
+ pte_val(entry) &= ~_PAGE_UNUSED;
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste_val(pgste) &= ~_PGSTE_GPS_ZERO;
" jg 2b\n" \
".popsection\n" \
EX_TABLE(0b,3b) EX_TABLE(1b,3b) \
- : "=d" (__rc), "=Q" (*(to)) \
+ : "=d" (__rc), "+Q" (*(to)) \
: "d" (size), "Q" (*(from)), \
"d" (__reg0), "K" (-EFAULT) \
: "cc"); \
{
vdso32_enabled = simple_strtoul(s, NULL, 0);
- if (vdso32_enabled > 1)
+ if (vdso32_enabled > 1) {
pr_warn("vdso32 values other than 0 and 1 are no longer allowed; vdso disabled\n");
+ vdso32_enabled = 0;
+ }
return 1;
}
/* Register vsyscall32 into the ABI table */
#include <linux/sysctl.h>
+static const int zero;
+static const int one = 1;
+
static struct ctl_table abi_table2[] = {
{
.procname = "vsyscall32",
.data = &vdso32_enabled,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (int *)&zero,
+ .extra2 = (int *)&one,
},
{}
};
#define ARCH_DLINFO_IA32 \
do { \
- if (vdso32_enabled) { \
+ if (VDSO_CURRENT_BASE) { \
NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \
NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \
} \
* @size: number of bytes to write back
*
* Write back a cache range using the CLWB (cache line write back)
- * instruction. This function requires explicit ordering with an
- * arch_wmb_pmem() call. This API is internal to the x86 PMEM implementation.
+ * instruction. Note that @size is internally rounded up to be cache
+ * line size aligned.
*/
static inline void __arch_wb_cache_pmem(void *vaddr, size_t size)
{
clwb(p);
}
-/*
- * copy_from_iter_nocache() on x86 only uses non-temporal stores for iovec
- * iterators, so for other types (bvec & kvec) we must do a cache write-back.
- */
-static inline bool __iter_needs_pmem_wb(struct iov_iter *i)
-{
- return iter_is_iovec(i) == false;
-}
-
/**
* arch_copy_from_iter_pmem - copy data from an iterator to PMEM
* @addr: PMEM destination address
/* TODO: skip the write-back by always using non-temporal stores */
len = copy_from_iter_nocache(vaddr, bytes, i);
- if (__iter_needs_pmem_wb(i))
- __arch_wb_cache_pmem(vaddr, bytes);
+ /*
+ * In the iovec case on x86_64 copy_from_iter_nocache() uses
+ * non-temporal stores for the bulk of the transfer, but we need
+ * to manually flush if the transfer is unaligned. A cached
+ * memory copy is used when destination or size is not naturally
+ * aligned. That is:
+ * - Require 8-byte alignment when size is 8 bytes or larger.
+ * - Require 4-byte alignment when size is 4 bytes.
+ *
+ * In the non-iovec case the entire destination needs to be
+ * flushed.
+ */
+ if (iter_is_iovec(i)) {
+ unsigned long flushed, dest = (unsigned long) addr;
+
+ if (bytes < 8) {
+ if (!IS_ALIGNED(dest, 4) || (bytes != 4))
+ __arch_wb_cache_pmem(addr, 1);
+ } else {
+ if (!IS_ALIGNED(dest, 8)) {
+ dest = ALIGN(dest, boot_cpu_data.x86_clflush_size);
+ __arch_wb_cache_pmem(addr, 1);
+ }
+
+ flushed = dest - (unsigned long) addr;
+ if (bytes > flushed && !IS_ALIGNED(bytes - flushed, 8))
+ __arch_wb_cache_pmem(addr + bytes - 1, 1);
+ }
+ } else
+ __arch_wb_cache_pmem(addr, bytes);
return len;
}
"load_store",
"insn_fetch",
"combined_unit",
- "",
+ "decode_unit",
"northbridge",
"execution_unit",
};
cpuc->lbr_entries[i].to = msr_lastbranch.to;
cpuc->lbr_entries[i].mispred = 0;
cpuc->lbr_entries[i].predicted = 0;
+ cpuc->lbr_entries[i].in_tx = 0;
+ cpuc->lbr_entries[i].abort = 0;
+ cpuc->lbr_entries[i].cycles = 0;
cpuc->lbr_entries[i].reserved = 0;
}
cpuc->lbr_stack.nr = i;
}
page = nested_get_page(vcpu, vmptr);
- if (page == NULL ||
- *(u32 *)kmap(page) != VMCS12_REVISION) {
+ if (page == NULL) {
nested_vmx_failInvalid(vcpu);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ }
+ if (*(u32 *)kmap(page) != VMCS12_REVISION) {
kunmap(page);
+ nested_release_page_clean(page);
+ nested_vmx_failInvalid(vcpu);
skip_emulated_instruction(vcpu);
return 1;
}
kunmap(page);
+ nested_release_page_clean(page);
vmx->nested.vmxon_ptr = vmptr;
break;
case EXIT_REASON_VMCLEAR:
* devmem_is_allowed() checks to see if /dev/mem access to a certain address
* is valid. The argument is a physical page number.
*
- *
- * On x86, access has to be given to the first megabyte of ram because that area
- * contains BIOS code and data regions used by X and dosemu and similar apps.
- * Access has to be given to non-kernel-ram areas as well, these contain the PCI
- * mmio resources as well as potential bios/acpi data regions.
+ * On x86, access has to be given to the first megabyte of RAM because that
+ * area traditionally contains BIOS code and data regions used by X, dosemu,
+ * and similar apps. Since they map the entire memory range, the whole range
+ * must be allowed (for mapping), but any areas that would otherwise be
+ * disallowed are flagged as being "zero filled" instead of rejected.
+ * Access has to be given to non-kernel-ram areas as well, these contain the
+ * PCI mmio resources as well as potential bios/acpi data regions.
*/
int devmem_is_allowed(unsigned long pagenr)
{
- if (pagenr < 256)
- return 1;
- if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
+ if (page_is_ram(pagenr)) {
+ /*
+ * For disallowed memory regions in the low 1MB range,
+ * request that the page be shown as all zeros.
+ */
+ if (pagenr < 256)
+ return 2;
+
+ return 0;
+ }
+
+ /*
+ * This must follow RAM test, since System RAM is considered a
+ * restricted resource under CONFIG_STRICT_IOMEM.
+ */
+ if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) {
+ /* Low 1MB bypasses iomem restrictions. */
+ if (pagenr < 256)
+ return 1;
+
return 0;
- if (!page_is_ram(pagenr))
- return 1;
- return 0;
+ }
+
+ return 1;
}
void free_init_pages(char *what, unsigned long begin, unsigned long end)
size = PFN_PHYS(xen_start_info->nr_p2m_frames);
}
- if (!xen_is_e820_reserved(start, size)) {
- memblock_reserve(start, size);
+ memblock_reserve(start, size);
+ if (!xen_is_e820_reserved(start, size))
return;
- }
#ifdef CONFIG_X86_32
/*
BUG();
#else
xen_relocate_p2m();
+ memblock_free(start, size);
#endif
}
bio_list_init(&punt);
bio_list_init(&nopunt);
- while ((bio = bio_list_pop(current->bio_list)))
+ while ((bio = bio_list_pop(¤t->bio_list[0])))
bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio);
+ current->bio_list[0] = nopunt;
- *current->bio_list = nopunt;
+ bio_list_init(&nopunt);
+ while ((bio = bio_list_pop(¤t->bio_list[1])))
+ bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio);
+ current->bio_list[1] = nopunt;
spin_lock(&bs->rescue_lock);
bio_list_merge(&bs->rescue_list, &punt);
* we retry with the original gfp_flags.
*/
- if (current->bio_list && !bio_list_empty(current->bio_list))
+ if (current->bio_list &&
+ (!bio_list_empty(¤t->bio_list[0]) ||
+ !bio_list_empty(¤t->bio_list[1])))
gfp_mask &= ~__GFP_DIRECT_RECLAIM;
p = mempool_alloc(bs->bio_pool, gfp_mask);
*/
blk_qc_t generic_make_request(struct bio *bio)
{
- struct bio_list bio_list_on_stack;
+ /*
+ * bio_list_on_stack[0] contains bios submitted by the current
+ * make_request_fn.
+ * bio_list_on_stack[1] contains bios that were submitted before
+ * the current make_request_fn, but that haven't been processed
+ * yet.
+ */
+ struct bio_list bio_list_on_stack[2];
blk_qc_t ret = BLK_QC_T_NONE;
if (!generic_make_request_checks(bio))
* should be added at the tail
*/
if (current->bio_list) {
- bio_list_add(current->bio_list, bio);
+ bio_list_add(¤t->bio_list[0], bio);
goto out;
}
* bio_list, and call into ->make_request() again.
*/
BUG_ON(bio->bi_next);
- bio_list_init(&bio_list_on_stack);
- current->bio_list = &bio_list_on_stack;
+ bio_list_init(&bio_list_on_stack[0]);
+ current->bio_list = bio_list_on_stack;
do {
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
if (likely(blk_queue_enter(q, __GFP_DIRECT_RECLAIM) == 0)) {
+ struct bio_list lower, same;
+
+ /* Create a fresh bio_list for all subordinate requests */
+ bio_list_on_stack[1] = bio_list_on_stack[0];
+ bio_list_init(&bio_list_on_stack[0]);
ret = q->make_request_fn(q, bio);
blk_queue_exit(q);
-
- bio = bio_list_pop(current->bio_list);
+ /* sort new bios into those for a lower level
+ * and those for the same level
+ */
+ bio_list_init(&lower);
+ bio_list_init(&same);
+ while ((bio = bio_list_pop(&bio_list_on_stack[0])) != NULL)
+ if (q == bdev_get_queue(bio->bi_bdev))
+ bio_list_add(&same, bio);
+ else
+ bio_list_add(&lower, bio);
+ /* now assemble so we handle the lowest level first */
+ bio_list_merge(&bio_list_on_stack[0], &lower);
+ bio_list_merge(&bio_list_on_stack[0], &same);
+ bio_list_merge(&bio_list_on_stack[0], &bio_list_on_stack[1]);
} else {
- struct bio *bio_next = bio_list_pop(current->bio_list);
-
bio_io_error(bio);
- bio = bio_next;
}
+ bio = bio_list_pop(&bio_list_on_stack[0]);
} while (bio);
current->bio_list = NULL; /* deactivate */
INIT_LIST_HEAD(&tags->page_list);
tags->rqs = kzalloc_node(set->queue_depth * sizeof(struct request *),
- GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY,
+ GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY,
set->numa_node);
if (!tags->rqs) {
blk_mq_free_tags(tags);
do {
page = alloc_pages_node(set->numa_node,
- GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO,
+ GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO,
this_order);
if (page)
break;
* Allow kmemleak to scan these pages as they contain pointers
* to additional allocations like via ops->init_request().
*/
- kmemleak_alloc(p, order_to_size(this_order), 1, GFP_KERNEL);
+ kmemleak_alloc(p, order_to_size(this_order), 1, GFP_NOIO);
entries_per_page = order_to_size(this_order) / rq_size;
to_do = min(entries_per_page, set->queue_depth - i);
left -= to_do * rq_size;
kobject_put(disk->part0.holder_dir);
kobject_put(disk->slave_dir);
- disk->driverfs_dev = NULL;
if (!sysfs_deprecated)
sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
pm_runtime_set_memalloc_noio(disk_to_dev(disk), false);
crypto_completion_t complete;
void *data;
u8 *result;
+ u32 flags;
void *ubuf[] CRYPTO_MINALIGN_ATTR;
};
priv->result = req->result;
priv->complete = req->base.complete;
priv->data = req->base.data;
+ priv->flags = req->base.flags;
+
/*
* WARNING: We do not backup req->priv here! The req->priv
* is for internal use of the Crypto API and the
return 0;
}
-static void ahash_restore_req(struct ahash_request *req)
+static void ahash_restore_req(struct ahash_request *req, int err)
{
struct ahash_request_priv *priv = req->priv;
+ if (!err)
+ memcpy(priv->result, req->result,
+ crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
+
/* Restore the original crypto request. */
req->result = priv->result;
- req->base.complete = priv->complete;
- req->base.data = priv->data;
+
+ ahash_request_set_callback(req, priv->flags,
+ priv->complete, priv->data);
req->priv = NULL;
/* Free the req->priv.priv from the ADJUSTED request. */
kzfree(priv);
}
-static void ahash_op_unaligned_finish(struct ahash_request *req, int err)
+static void ahash_notify_einprogress(struct ahash_request *req)
{
struct ahash_request_priv *priv = req->priv;
+ struct crypto_async_request oreq;
- if (err == -EINPROGRESS)
- return;
-
- if (!err)
- memcpy(priv->result, req->result,
- crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
+ oreq.data = priv->data;
- ahash_restore_req(req);
+ priv->complete(&oreq, -EINPROGRESS);
}
static void ahash_op_unaligned_done(struct crypto_async_request *req, int err)
{
struct ahash_request *areq = req->data;
+ if (err == -EINPROGRESS) {
+ ahash_notify_einprogress(areq);
+ return;
+ }
+
/*
* Restore the original request, see ahash_op_unaligned() for what
* goes where.
*/
/* First copy req->result into req->priv.result */
- ahash_op_unaligned_finish(areq, err);
+ ahash_restore_req(areq, err);
/* Complete the ORIGINAL request. */
areq->base.complete(&areq->base, err);
return err;
err = op(req);
- ahash_op_unaligned_finish(req, err);
+ if (err == -EINPROGRESS ||
+ (err == -EBUSY && (ahash_request_flags(req) &
+ CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return err;
+
+ ahash_restore_req(req, err);
return err;
}
}
EXPORT_SYMBOL_GPL(crypto_ahash_digest);
-static void ahash_def_finup_finish2(struct ahash_request *req, int err)
+static void ahash_def_finup_done2(struct crypto_async_request *req, int err)
{
- struct ahash_request_priv *priv = req->priv;
+ struct ahash_request *areq = req->data;
if (err == -EINPROGRESS)
return;
- if (!err)
- memcpy(priv->result, req->result,
- crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
-
- ahash_restore_req(req);
-}
-
-static void ahash_def_finup_done2(struct crypto_async_request *req, int err)
-{
- struct ahash_request *areq = req->data;
-
- ahash_def_finup_finish2(areq, err);
+ ahash_restore_req(areq, err);
areq->base.complete(&areq->base, err);
}
goto out;
req->base.complete = ahash_def_finup_done2;
- req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
+
err = crypto_ahash_reqtfm(req)->final(req);
+ if (err == -EINPROGRESS ||
+ (err == -EBUSY && (ahash_request_flags(req) &
+ CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return err;
out:
- ahash_def_finup_finish2(req, err);
+ ahash_restore_req(req, err);
return err;
}
{
struct ahash_request *areq = req->data;
+ if (err == -EINPROGRESS) {
+ ahash_notify_einprogress(areq);
+ return;
+ }
+
+ areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
+
err = ahash_def_finup_finish1(areq, err);
+ if (areq->priv)
+ return;
areq->base.complete(&areq->base, err);
}
return err;
err = tfm->update(req);
+ if (err == -EINPROGRESS ||
+ (err == -EBUSY && (ahash_request_flags(req) &
+ CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return err;
+
return ahash_def_finup_finish1(req, err);
}
# Makefile for the Linux ACPI interpreter
#
-ccflags-y := -Os
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
#
ACPI_MODULE_NAME("platform");
static const struct acpi_device_id forbidden_id_list[] = {
- {"PNP0000", 0}, /* PIC */
- {"PNP0100", 0}, /* Timer */
- {"PNP0200", 0}, /* AT DMA Controller */
+ {"PNP0000", 0}, /* PIC */
+ {"PNP0100", 0}, /* Timer */
+ {"PNP0200", 0}, /* AT DMA Controller */
+ {"ACPI0009", 0}, /* IOxAPIC */
+ {"ACPI000A", 0}, /* IOAPIC */
{"", 0},
};
const struct nfit_set_info_map *map0 = m0;
const struct nfit_set_info_map *map1 = m1;
- return map0->region_offset - map1->region_offset;
+ if (map0->region_offset < map1->region_offset)
+ return -1;
+ else if (map0->region_offset > map1->region_offset)
+ return 1;
+ return 0;
}
/* Retrieve the nth entry referencing this spa */
return -EINVAL;
/* The state of the list is 'on' IFF all resources are 'on'. */
+ cur_state = 0;
list_for_each_entry(entry, list, node) {
struct acpi_power_resource *resource = entry->resource;
acpi_handle handle = resource->device.handle;
if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) {
bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
- clear_page(mem);
+ memset(mem, 0, PAGE_SIZE);
return 0;
}
cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
if (size == PAGE_SIZE)
- copy_page(mem, cmem);
+ memcpy(mem, cmem, PAGE_SIZE);
else
ret = zcomp_decompress(zram->comp, cmem, size, mem);
zs_unmap_object(meta->mem_pool, handle);
if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
src = kmap_atomic(page);
- copy_page(cmem, src);
+ memcpy(cmem, src, PAGE_SIZE);
kunmap_atomic(src);
} else {
memcpy(cmem, src, clen);
#define BTFM_SLIM_H
#include <linux/slimbus/slimbus.h>
-#define BTFMSLIM_DBG(fmt, arg...) pr_debug(fmt "\n", ## arg)
+#define BTFMSLIM_DBG(fmt, arg...) pr_debug("%s: " fmt "\n", __func__, ## arg)
#define BTFMSLIM_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg)
#define BTFMSLIM_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg)
{
int ret = 0;
uint8_t reg_val = 0;
+ uint8_t port_bit = 0;
uint16_t reg;
BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable);
if (rxport) {
+ if (enable) {
+ /* For SCO Rx, A2DP Rx */
+ reg_val = 0x1;
+ port_bit = port_num - 0x10;
+ reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0(port_bit);
+ BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)",
+ reg_val, reg);
+ ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD);
+ if (ret) {
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x",
+ ret, reg);
+ goto error;
+ }
+ }
/* Port enable */
reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10);
goto enable_disable_rxport;
}
- /* txport */
if (!enable)
goto enable_disable_txport;
- /* Multiple Channel Setting - only for FM Tx */
+ /* txport */
+ /* Multiple Channel Setting */
if (is_fm_port(port_num)) {
reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) |
(0x1 << CHRK_SB_PGD_PORT_TX2_FM);
BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
goto error;
}
+ } else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) {
+ /* SCO Tx */
+ reg_val = 0x1 << CHRK_SB_PGD_PORT_TX_SCO;
+ reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
+ BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)",
+ reg_val, reg);
+ ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD);
+ if (ret) {
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x",
+ ret, reg);
+ goto error;
+ }
}
/* Enable Tx port hw auto recovery for underrun or overrun error */
controlling the behavior of this hardware.
config DEVPORT
- bool
- depends on !M68K
+ bool "/dev/port character device"
depends on ISA || PCI
default y
+ help
+ Say Y here if you want to support the /dev/port device. The /dev/port
+ device is similar to /dev/mem, but for I/O ports.
source "drivers/s390/char/Kconfig"
{
int i;
INIT_HLIST_HEAD(&me->drivers);
+ INIT_HLIST_HEAD(&me->maps);
spin_lock_init(&me->hlock);
mutex_init(&me->smd_mutex);
me->channel = &gcinfo[0];
start = 0x60000000;
VERIFY(err, !IS_ERR_OR_NULL(sess->smmu.mapping =
arm_iommu_create_mapping(&platform_bus_type,
- start, 0x7fffffff)));
+ start, 0x70000000)));
if (err)
goto bail;
iommu_set_fault_handler(sess->smmu.mapping->domain,
atomic_set(&glink_info->tx_intent_ready, 0);
}
+static void diag_glink_late_init_work_fn(struct work_struct *work)
+{
+ struct diag_glink_info *glink_info = container_of(work,
+ struct diag_glink_info,
+ late_init_work);
+ if (!glink_info || !glink_info->hdl)
+ return;
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "p: %d t: %d\n",
+ glink_info->peripheral, glink_info->type);
+ diagfwd_channel_open(glink_info->fwd_ctxt);
+}
+
static void diag_glink_transport_notify_state(void *handle, const void *priv,
unsigned event)
{
glink_info->inited = 1;
if (atomic_read(&glink_info->opened))
- diagfwd_channel_open(glink_info->fwd_ctxt);
+ queue_work(glink_info->wq, &(glink_info->late_init_work));
DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
glink_info->name);
INIT_WORK(&(glink_info->connect_work), diag_glink_connect_work_fn);
INIT_WORK(&(glink_info->remote_disconnect_work),
diag_glink_remote_disconnect_work_fn);
+ INIT_WORK(&(glink_info->late_init_work), diag_glink_late_init_work_fn);
link_info.glink_link_state_notif_cb = diag_glink_notify_cb;
link_info.transport = NULL;
link_info.edge = glink_info->edge;
struct work_struct read_work;
struct work_struct connect_work;
struct work_struct remote_disconnect_work;
+ struct work_struct late_init_work;
struct diagfwd_info *fwd_ctxt;
};
#endif
#ifdef CONFIG_STRICT_DEVMEM
+static inline int page_is_allowed(unsigned long pfn)
+{
+ return devmem_is_allowed(pfn);
+}
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
u64 from = ((u64)pfn) << PAGE_SHIFT;
return 1;
}
#else
+static inline int page_is_allowed(unsigned long pfn)
+{
+ return 1;
+}
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
return 1;
while (count > 0) {
unsigned long remaining;
+ int allowed;
sz = size_inside_page(p, count);
- if (!range_is_allowed(p >> PAGE_SHIFT, count))
+ allowed = page_is_allowed(p >> PAGE_SHIFT);
+ if (!allowed)
return -EPERM;
+ if (allowed == 2) {
+ /* Show zeros for restricted memory. */
+ remaining = clear_user(buf, sz);
+ } else {
+ /*
+ * On ia64 if a page has been mapped somewhere as
+ * uncached, then it must also be accessed uncached
+ * by the kernel or data corruption may occur.
+ */
+ ptr = xlate_dev_mem_ptr(p);
+ if (!ptr)
+ return -EFAULT;
- /*
- * On ia64 if a page has been mapped somewhere as uncached, then
- * it must also be accessed uncached by the kernel or data
- * corruption may occur.
- */
- ptr = xlate_dev_mem_ptr(p);
- if (!ptr)
- return -EFAULT;
+ remaining = copy_to_user(buf, ptr, sz);
+
+ unxlate_dev_mem_ptr(p, ptr);
+ }
- remaining = copy_to_user(buf, ptr, sz);
- unxlate_dev_mem_ptr(p, ptr);
if (remaining)
return -EFAULT;
#endif
while (count > 0) {
+ int allowed;
+
sz = size_inside_page(p, count);
- if (!range_is_allowed(p >> PAGE_SHIFT, sz))
+ allowed = page_is_allowed(p >> PAGE_SHIFT);
+ if (!allowed)
return -EPERM;
- /*
- * On ia64 if a page has been mapped somewhere as uncached, then
- * it must also be accessed uncached by the kernel or data
- * corruption may occur.
- */
- ptr = xlate_dev_mem_ptr(p);
- if (!ptr) {
- if (written)
- break;
- return -EFAULT;
- }
+ /* Skip actual writing when a page is marked as restricted. */
+ if (allowed == 1) {
+ /*
+ * On ia64 if a page has been mapped somewhere as
+ * uncached, then it must also be accessed uncached
+ * by the kernel or data corruption may occur.
+ */
+ ptr = xlate_dev_mem_ptr(p);
+ if (!ptr) {
+ if (written)
+ break;
+ return -EFAULT;
+ }
- copied = copy_from_user(ptr, buf, sz);
- unxlate_dev_mem_ptr(p, ptr);
- if (copied) {
- written += sz - copied;
- if (written)
- break;
- return -EFAULT;
+ copied = copy_from_user(ptr, buf, sz);
+ unxlate_dev_mem_ptr(p, ptr);
+ if (copied) {
+ written += sz - copied;
+ if (written)
+ break;
+ return -EFAULT;
+ }
}
buf += sz;
{
struct port *port;
struct scatterlist sg[1];
+ void *data;
+ int ret;
if (unlikely(early_put_chars))
return early_put_chars(vtermno, buf, count);
if (!port)
return -EPIPE;
- sg_init_one(sg, buf, count);
- return __send_to_port(port, sg, 1, count, (void *)buf, false);
+ data = kmemdup(buf, count, GFP_ATOMIC);
+ if (!data)
+ return -ENOMEM;
+
+ sg_init_one(sg, data, count);
+ ret = __send_to_port(port, sg, 1, count, data, false);
+ kfree(data);
+ return ret;
}
/*
NULL,
};
+static void clk_state_subtree(struct clk_core *c)
+{
+ int vdd_level = 0;
+ struct clk_core *child;
+
+ if (!c)
+ return;
+
+ if (c->vdd_class) {
+ vdd_level = clk_find_vdd_level(c, c->rate);
+ if (vdd_level < 0)
+ vdd_level = 0;
+ }
+
+ trace_clk_state(c->name, c->prepare_count, c->enable_count,
+ c->rate, vdd_level);
+
+ hlist_for_each_entry(child, &c->children, child_node)
+ clk_state_subtree(child);
+}
+
+static int clk_state_show(struct seq_file *s, void *data)
+{
+ struct clk_core *c;
+ struct hlist_head **lists = (struct hlist_head **)s->private;
+
+ clk_prepare_lock();
+
+ for (; *lists; lists++)
+ hlist_for_each_entry(c, *lists, child_node)
+ clk_state_subtree(c);
+
+ clk_prepare_unlock();
+
+ return 0;
+}
+
+
+static int clk_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_state_show, inode->i_private);
+}
+
+static const struct file_operations clk_state_fops = {
+ .open = clk_state_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
int level)
{
if (!d)
return -ENOMEM;
+ d = debugfs_create_file("trace_clocks", S_IRUGO, rootdir, &all_lists,
+ &clk_state_fops);
+ if (!d)
+ return -ENOMEM;
+
mutex_lock(&clk_debug_lock);
hlist_for_each_entry(core, &clk_debug_list, debug_node)
clk_debug_create_one(core, rootdir);
/*
- * 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
unsigned long pwrclrate, perfclrate, cbfrate;
int pvs_ver = 0;
u32 pte_efuse;
+ u32 clk_rate;
char perfclspeedbinstr[] = "qcom,perfcl-speedbinXX-vXX";
char pwrclspeedbinstr[] = "qcom,pwrcl-speedbinXX-vXX";
char cbfspeedbinstr[] = "qcom,cbf-speedbinXX-vXX";
clk_prepare_enable(&pwrcl_alt_pll.c);
clk_prepare_enable(&cbf_pll.c);
+ /* Override the existing ealry boot frequency for power cluster */
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,pwrcl-early-boot-freq", &clk_rate);
+ if (!ret)
+ pwrcl_early_boot_rate = clk_rate;
+
+ /* Override the existing ealry boot frequency for perf cluster */
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,perfcl-early-boot-freq", &clk_rate);
+ if (!ret)
+ perfcl_early_boot_rate = clk_rate;
+
/* Set the early boot rate. This may also switch us to the ACD leg */
clk_set_rate(&pwrcl_clk.c, pwrcl_early_boot_rate);
clk_set_rate(&perfcl_clk.c, perfcl_early_boot_rate);
{ .compatible = "qcom,cpu-clock-8996" },
{ .compatible = "qcom,cpu-clock-8996-v3" },
{ .compatible = "qcom,cpu-clock-8996-pro" },
+ { .compatible = "qcom,cpu-clock-8996-auto" },
{}
};
#define HF_MUX_SEL_LF_MUX 0x1
#define LF_MUX_SEL_ALT_PLL 0x1
+#define PWRCL_EARLY_BOOT_RATE 1286400000
+#define PERFCL_EARLY_BOOT_RATE 1363200000
+
static int use_alt_pll;
module_param(use_alt_pll, int, 0444);
cpu_clocks_v3 = true;
cpu_clocks_pro = true;
} else if (of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-8996-auto")) {
+ cpu_clocks_v3 = true;
+ cpu_clocks_pro = true;
+ pwrcl_early_boot_rate = PWRCL_EARLY_BOOT_RATE;
+ perfcl_early_boot_rate = PERFCL_EARLY_BOOT_RATE;
+ } else if (of_find_compatible_node(NULL, NULL,
"qcom,cpu-clock-8996-v3")) {
cpu_clocks_v3 = true;
} else if (!of_find_compatible_node(NULL, NULL,
regval |= BIT(21);
writel_relaxed(regval, virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE);
- /*
- * Set the HMSS_AHB_CLK_SLEEP_ENA bit to allow the hmss_ahb_clk to be
- * turned off by hardware during certain apps low power modes.
- */
- regval = readl_relaxed(virt_base + GCC_APCS_CLOCK_SLEEP_ENA_VOTE);
- regval |= BIT(21);
- writel_relaxed(regval, virt_base + GCC_APCS_CLOCK_SLEEP_ENA_VOTE);
-
vdd_dig.vdd_uv[1] = RPM_REGULATOR_CORNER_SVS_KRAIT;
vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig");
if (IS_ERR(vdd_dig.regulator[0])) {
/* Try to run it through DECO0 */
ret = run_descriptor_deco0(ctrldev, desc, &status);
- if (ret || status) {
+ if (ret ||
+ (status && status != JRSTA_SSRC_JUMP_HALT_CC)) {
dev_err(ctrldev,
"Failed to deinstantiate RNG4 SH%d\n",
sh_idx);
struct device *dev = mdm->dev;
int ret;
bool graceful_shutdown = false;
+ u32 status, err_fatal;
switch (cmd) {
case ESOC_PWR_ON:
+ if (esoc->auto_boot) {
+ /*
+ * If esoc has already booted, we would have missed
+ * status change interrupt. Read status and err_fatal
+ * signals to arrive at the state of esoc.
+ */
+ esoc->clink_ops->get_status(&status, esoc);
+ esoc->clink_ops->get_err_fatal(&err_fatal, esoc);
+ if (err_fatal)
+ return -EIO;
+ if (status && !mdm->ready) {
+ mdm->ready = true;
+ esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
+ }
+ }
gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
- mdm_enable_irqs(mdm);
mdm->init = 1;
mdm_do_first_power_on(mdm);
+ mdm_enable_irqs(mdm);
break;
case ESOC_PWR_OFF:
mdm_disable_irqs(mdm);
mdm->debug = 0;
mdm->ready = false;
mdm->trig_cnt = 0;
+ if (esoc->primary)
+ break;
graceful_shutdown = true;
- ret = sysmon_send_shutdown(&esoc->subsys);
- if (ret) {
- dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n",
- ret);
- graceful_shutdown = false;
- goto force_poff;
+ if (!esoc->userspace_handle_shutdown) {
+ ret = sysmon_send_shutdown(&esoc->subsys);
+ if (ret) {
+ dev_err(mdm->dev,
+ "sysmon shutdown fail, ret = %d\n", ret);
+ graceful_shutdown = false;
+ goto force_poff;
+ }
+ } else {
+ esoc_clink_queue_request(ESOC_REQ_SEND_SHUTDOWN, esoc);
}
dev_dbg(mdm->dev, "Waiting for status gpio go low\n");
status_down = false;
esoc->subsys.sysmon_shutdown_ret);
}
+ if (esoc->primary)
+ break;
/*
* Force a shutdown of the mdm. This is required in order
* to prevent the mdm from immediately powering back on
- * after the shutdown
+ * after the shutdown. Avoid setting status to 0, if line is
+ * monitored by multiple mdms(might be wrongly interpreted as
+ * a primary crash).
*/
- gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
+ if (esoc->statusline_not_a_powersource == false)
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
esoc_clink_queue_request(ESOC_REQ_SHUTDOWN, esoc);
mdm_power_down(mdm);
mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG);
*/
mdm->ready = false;
cancel_delayed_work(&mdm->mdm2ap_status_check_work);
- gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
- dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n");
- msleep(mdm->ramdump_delay_ms);
+ if (!mdm->esoc->auto_boot) {
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+ dev_dbg(mdm->dev,
+ "set ap2mdm errfatal to force reset\n");
+ msleep(mdm->ramdump_delay_ms);
+ }
break;
case ESOC_EXE_DEBUG:
mdm->debug = 1;
status_down = false;
dev_dbg(dev, "signal apq err fatal for graceful restart\n");
gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+ if (esoc->primary)
+ break;
timeout = local_clock();
do_div(timeout, NSEC_PER_MSEC);
timeout += MDM_MODEM_TIMEOUT;
goto mdm_pwroff_irq;
esoc = mdm->esoc;
dev_err(dev, "%s: mdm sent errfatal interrupt\n",
- __func__);
+ __func__);
+ subsys_set_crash_status(esoc->subsys_dev, true);
/* disable irq ?*/
esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc);
return IRQ_HANDLED;
return IRQ_HANDLED;
dev = mdm->dev;
esoc = mdm->esoc;
+ /*
+ * On auto boot devices, there is a possibility of receiving
+ * status change interrupt before esoc_clink structure is
+ * initialized. Ignore them.
+ */
+ if (!esoc)
+ return IRQ_HANDLED;
value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
if (value == 0 && mdm->ready) {
dev_err(dev, "unexpected reset external modem\n");
+ subsys_set_crash_status(esoc->subsys_dev, true);
esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc);
} else if (value == 1) {
+ /*
+ * In auto_boot cases, bailout early if mdm
+ * is up already.
+ */
+ if (esoc->auto_boot && mdm->ready)
+ return IRQ_HANDLED;
+
cancel_delayed_work(&mdm->mdm2ap_status_check_work);
dev_dbg(dev, "status = 1: mdm is now ready\n");
mdm->ready = true;
queue_work(mdm->mdm_queue, &mdm->mdm_status_work);
if (mdm->get_restart_reason)
queue_work(mdm->mdm_queue, &mdm->restart_reason_work);
+ if (esoc->auto_boot)
+ esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
}
return IRQ_HANDLED;
}
return IRQ_HANDLED;
}
-static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
+static void mdm_get_status(u32 *status, struct esoc_clink *esoc)
{
struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
*status = 0;
else
*status = 1;
- return 0;
+}
+
+static void mdm_get_err_fatal(u32 *status, struct esoc_clink *esoc)
+{
+ struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
+
+ if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_ERRFATAL)) == 0)
+ *status = 0;
+ else
+ *status = 1;
}
static void mdm_configure_debug(struct mdm_ctrl *mdm)
&mdm->ramdump_delay_ms);
if (ret)
mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY;
- /* Multilple gpio_request calls are allowed */
+ /*
+ * In certain scenarios, multiple esoc devices are monitoring
+ * same AP2MDM_STATUS line. But only one of them will have a
+ * successful gpio_request call. Initialize gpio only if request
+ * succeeds.
+ */
if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS"))
dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n");
- /* Multilple gpio_request calls are allowed */
+ else
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL"))
dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n",
__func__);
+ else
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) {
dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n",
__func__);
}
}
- gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
- gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
-
if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY)))
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0);
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
+ esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
+ esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
return 0;
}
+static int mdm9x45_setup_hw(struct mdm_ctrl *mdm,
+ const struct mdm_ops *ops,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct esoc_clink *esoc;
+ const struct esoc_clink_ops *const clink_ops = ops->clink_ops;
+ const struct mdm_pon_ops *pon_ops = ops->pon_ops;
+
+ mdm->dev = &pdev->dev;
+ mdm->pon_ops = pon_ops;
+ esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(esoc)) {
+ dev_err(mdm->dev, "cannot allocate esoc device\n");
+ return PTR_ERR(esoc);
+ }
+ esoc->pdev = pdev;
+ mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
+ if (!mdm->mdm_queue) {
+ dev_err(mdm->dev, "could not create mdm_queue\n");
+ return -ENOMEM;
+ }
+ mdm->irq_mask = 0;
+ mdm->ready = false;
+ ret = mdm_dt_parse_gpios(mdm);
+ if (ret)
+ return ret;
+ dev_err(mdm->dev, "parsing gpio done\n");
+ ret = mdm_pon_dt_init(mdm);
+ if (ret)
+ return ret;
+ dev_dbg(mdm->dev, "pon dt init done\n");
+ ret = mdm_pinctrl_init(mdm);
+ if (ret)
+ return ret;
+ dev_err(mdm->dev, "pinctrl init done\n");
+ ret = mdm_pon_setup(mdm);
+ if (ret)
+ return ret;
+ dev_dbg(mdm->dev, "pon setup done\n");
+ ret = mdm_configure_ipc(mdm, pdev);
+ if (ret)
+ return ret;
+ mdm_configure_debug(mdm);
+ dev_err(mdm->dev, "ipc configure done\n");
+ esoc->name = MDM9x45_LABEL;
+ esoc->link_name = MDM9x45_PCIE;
+ esoc->clink_ops = clink_ops;
+ esoc->parent = mdm->dev;
+ esoc->owner = THIS_MODULE;
+ esoc->np = pdev->dev.of_node;
+
+ esoc->auto_boot = of_property_read_bool(esoc->np,
+ "qcom,mdm-auto-boot");
+ esoc->statusline_not_a_powersource = of_property_read_bool(esoc->np,
+ "qcom,mdm-statusline-not-a-powersource");
+ esoc->userspace_handle_shutdown = of_property_read_bool(esoc->np,
+ "qcom,mdm-userspace-handle-shutdown");
+ set_esoc_clink_data(esoc, mdm);
+ ret = esoc_clink_register(esoc);
+ if (ret) {
+ dev_err(mdm->dev, "esoc registration failed\n");
+ return ret;
+ }
+ dev_dbg(mdm->dev, "esoc registration done\n");
+ init_completion(&mdm->debug_done);
+ INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
+ INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
+ INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
+ mdm->get_restart_reason = false;
+ mdm->debug_fail = false;
+ mdm->esoc = esoc;
+ mdm->init = 0;
+ if (esoc->auto_boot)
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
+ return 0;
+}
+
static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
const struct mdm_ops *ops,
struct platform_device *pdev)
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
+ esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
return 0;
}
+static int apq8096_setup_hw(struct mdm_ctrl *mdm,
+ const struct mdm_ops *ops,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct device_node *node;
+ struct esoc_clink *esoc;
+ const struct esoc_clink_ops *const clink_ops = ops->clink_ops;
+ const struct mdm_pon_ops *pon_ops = ops->pon_ops;
+
+ mdm->dev = &pdev->dev;
+ mdm->pon_ops = pon_ops;
+ node = pdev->dev.of_node;
+ esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(esoc)) {
+ dev_err(mdm->dev, "cannot allocate esoc device\n");
+ return PTR_ERR(esoc);
+ }
+ esoc->pdev = pdev;
+ mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
+ if (!mdm->mdm_queue) {
+ dev_err(mdm->dev, "could not create mdm_queue\n");
+ return -ENOMEM;
+ }
+ mdm->irq_mask = 0;
+ mdm->ready = false;
+ ret = mdm_dt_parse_gpios(mdm);
+ if (ret)
+ return ret;
+ dev_dbg(mdm->dev, "parsing gpio done\n");
+ ret = mdm_pon_dt_init(mdm);
+ if (ret)
+ return ret;
+ dev_dbg(mdm->dev, "pon dt init done\n");
+ ret = mdm_pinctrl_init(mdm);
+ if (ret)
+ return ret;
+ dev_dbg(mdm->dev, "pinctrl init done\n");
+ ret = mdm_pon_setup(mdm);
+ if (ret)
+ return ret;
+ dev_dbg(mdm->dev, "pon setup done\n");
+ ret = mdm_configure_ipc(mdm, pdev);
+ if (ret)
+ return ret;
+ dev_dbg(mdm->dev, "ipc configure done\n");
+ esoc->name = APQ8096_LABEL;
+ esoc->link_name = APQ8096_PCIE;
+ esoc->clink_ops = clink_ops;
+ esoc->parent = mdm->dev;
+ esoc->owner = THIS_MODULE;
+ esoc->np = pdev->dev.of_node;
+ esoc->auto_boot = of_property_read_bool(esoc->np,
+ "qcom,mdm-auto-boot");
+ esoc->primary = of_property_read_bool(esoc->np,
+ "qcom,mdm-primary");
+ set_esoc_clink_data(esoc, mdm);
+ ret = esoc_clink_register(esoc);
+ if (ret) {
+ dev_err(mdm->dev, "esoc registration failed\n");
+ return ret;
+ }
+ dev_dbg(mdm->dev, "esoc registration done\n");
+ init_completion(&mdm->debug_done);
+ INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
+ INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
+ INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
+ mdm->get_restart_reason = false;
+ mdm->debug_fail = false;
+ mdm->esoc = esoc;
+ mdm->init = 0;
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
+ return 0;
+}
+
static struct esoc_clink_ops mdm_cops = {
.cmd_exe = mdm_cmd_exe,
.get_status = mdm_get_status,
+ .get_err_fatal = mdm_get_err_fatal,
.notify = mdm_notify,
};
.pon_ops = &mdm9x35_pon_ops,
};
+static struct mdm_ops mdm9x45_ops = {
+ .clink_ops = &mdm_cops,
+ .config_hw = mdm9x45_setup_hw,
+ .pon_ops = &mdm9x45_pon_ops,
+};
+
+static struct mdm_ops apq8096_ops = {
+ .clink_ops = &mdm_cops,
+ .config_hw = apq8096_setup_hw,
+ .pon_ops = &apq8096_pon_ops,
+};
+
static struct mdm_ops mdm9x55_ops = {
.clink_ops = &mdm_cops,
.config_hw = mdm9x55_setup_hw,
.data = &mdm9x25_ops, },
{ .compatible = "qcom,ext-mdm9x35",
.data = &mdm9x35_ops, },
+ { .compatible = "qcom,ext-mdm9x45",
+ .data = &mdm9x45_ops, },
{ .compatible = "qcom,ext-mdm9x55",
.data = &mdm9x55_ops, },
+ { .compatible = "qcom,ext-apq8096",
+ .data = &apq8096_ops, },
{},
};
MODULE_DEVICE_TABLE(of, mdm_dt_match);
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/reboot.h>
+#include <linux/of.h>
#include "esoc.h"
#include "mdm-dbg.h"
break;
case ESOC_UNEXPECTED_RESET:
case ESOC_ERR_FATAL:
- if (mdm_drv->mode == CRASH)
+ /*
+ * Modem can crash while we are waiting for boot_done during
+ * a subsystem_get(). Setting mode to CRASH will prevent a
+ * subsequent subsystem_get() from entering poweron ops. Avoid
+ * this by seting mode to CRASH only if device was up and
+ * running.
+ */
+ if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN)
return;
mdm_drv->mode = CRASH;
queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
subsys);
struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
+ int timeout = INT_MAX;
- if (!esoc_req_eng_enabled(esoc_clink)) {
+ if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) {
dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
wait_for_completion(&mdm_drv->req_eng_wait);
}
return ret;
}
}
- wait_for_completion(&mdm_drv->boot_done);
- if (mdm_drv->boot_fail) {
+
+ /*
+ * In autoboot case, it is possible that we can forever wait for
+ * boot completion, when esoc fails to boot. This is because there
+ * is no helper application which can alert esoc driver about boot
+ * failure. Prevent going to wait forever in such case.
+ */
+ if (esoc_clink->auto_boot)
+ timeout = 10 * HZ;
+ ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout);
+ if (mdm_drv->boot_fail || ret <= 0) {
dev_err(&esoc_clink->dev, "booting failed\n");
return -EIO;
}
static int mdm_register_ssr(struct esoc_clink *esoc_clink)
{
- esoc_clink->subsys.shutdown = mdm_subsys_shutdown;
- esoc_clink->subsys.ramdump = mdm_subsys_ramdumps;
- esoc_clink->subsys.powerup = mdm_subsys_powerup;
- esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown;
+ struct subsys_desc *subsys = &esoc_clink->subsys;
+
+ subsys->shutdown = mdm_subsys_shutdown;
+ subsys->ramdump = mdm_subsys_ramdumps;
+ subsys->powerup = mdm_subsys_powerup;
+ subsys->crash_shutdown = mdm_crash_shutdown;
return esoc_clink_register_ssr(esoc_clink);
}
.name = "MDM9x55",
.data = NULL,
},
+ {
+ .name = "MDM9x45",
+ .data = NULL,
+ },
+ {
+ .name = "APQ8096",
+ .data = NULL,
+ },
};
static struct esoc_drv esoc_ssr_drv = {
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 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
return 0;
}
+/* This function can be called from atomic context. */
+static int mdm9x45_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
+{
+ int soft_reset_direction_assert = 0,
+ soft_reset_direction_de_assert = 1;
+
+ if (mdm->soft_reset_inverted) {
+ soft_reset_direction_assert = 1;
+ soft_reset_direction_de_assert = 0;
+ }
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction_assert);
+ /*
+ * Allow PS hold assert to be detected
+ */
+ if (!atomic)
+ usleep_range(1000000, 1005000);
+ else
+ mdelay(1000);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction_de_assert);
+ return 0;
+}
static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
{
struct device *dev = mdm->dev;
dev_dbg(dev, "Powering on modem for the first time\n");
+ if (mdm->esoc->auto_boot)
+ return 0;
+
mdm_toggle_soft_reset(mdm, false);
/* Add a delay to allow PON sequence to complete*/
msleep(50);
return 0;
}
+static int mdm9x45_power_down(struct mdm_ctrl *mdm)
+{
+ int soft_reset_direction_assert = 0,
+ soft_reset_direction_de_assert = 1;
+
+ if (mdm->soft_reset_inverted) {
+ soft_reset_direction_assert = 1;
+ soft_reset_direction_de_assert = 0;
+ }
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction_assert);
+ /*
+ * Allow PS hold assert to be detected
+ */
+ msleep(3003);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction_de_assert);
+ return 0;
+}
+
static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
+ if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET)))
+ return;
+
dev_dbg(mdm->dev, "Triggering mdm cold reset");
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
!!mdm->soft_reset_inverted);
!mdm->soft_reset_inverted);
}
+static int apq8096_pon_dt_init(struct mdm_ctrl *mdm)
+{
+ return 0;
+}
+
static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm)
{
int val;
return 0;
}
+/* This function can be called from atomic context. */
+static int apq8096_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
+{
+ return 0;
+}
+
+static int apq8096_power_down(struct mdm_ctrl *mdm)
+{
+ return 0;
+}
+
+static void apq8096_cold_reset(struct mdm_ctrl *mdm)
+{
+}
+
struct mdm_pon_ops mdm9x25_pon_ops = {
.pon = mdm4x_do_first_power_on,
.soft_reset = mdm4x_toggle_soft_reset,
struct mdm_pon_ops mdm9x45_pon_ops = {
.pon = mdm4x_do_first_power_on,
- .soft_reset = mdm4x_toggle_soft_reset,
- .poff_force = mdm4x_power_down,
+ .soft_reset = mdm9x45_toggle_soft_reset,
+ .poff_force = mdm9x45_power_down,
.cold_reset = mdm4x_cold_reset,
.dt_init = mdm4x_pon_dt_init,
.setup = mdm4x_pon_setup,
.dt_init = mdm4x_pon_dt_init,
.setup = mdm4x_pon_setup,
};
+
+struct mdm_pon_ops apq8096_pon_ops = {
+ .pon = mdm4x_do_first_power_on,
+ .soft_reset = apq8096_toggle_soft_reset,
+ .poff_force = apq8096_power_down,
+ .cold_reset = apq8096_cold_reset,
+ .dt_init = apq8096_pon_dt_init,
+ .setup = mdm4x_pon_setup,
+};
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 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
#define MDM9x45_PCIE "PCIe"
#define MDM9x55_LABEL "MDM9x55"
#define MDM9x55_PCIE "PCIe"
+#define APQ8096_LABEL "APQ8096"
+#define APQ8096_PCIE "PCIe"
#define MDM2AP_STATUS_TIMEOUT_MS 120000L
#define MDM_MODEM_TIMEOUT 3000
#define DEF_RAMDUMP_TIMEOUT 120000
extern struct mdm_pon_ops mdm9x35_pon_ops;
extern struct mdm_pon_ops mdm9x45_pon_ops;
extern struct mdm_pon_ops mdm9x55_pon_ops;
+extern struct mdm_pon_ops apq8096_pon_ops;
#endif
* @link_info: additional info about the physical link.
* @parent: parent device.
* @dev: device for userspace interface.
+ * @pdev: platform device to interface with SSR driver.
* @id: id of the external device.
* @owner: owner of the device.
* @clink_ops: control operations for the control link
* @subsys_desc: descriptor for subsystem restart
* @subsys_dev: ssr device handle.
* @np: device tree node for esoc_clink.
+ * @auto_boot: boots independently.
+ * @primary: primary esoc controls(reset/poweroff) all secondary
+ * esocs, but not otherway around.
+ * @statusline_not_a_powersource: True if status line to esoc is not a
+ * power source.
+ * @userspace_handle_shutdown: True if user space handles shutdown requests.
*/
struct esoc_clink {
const char *name;
const char *link_info;
struct device *parent;
struct device dev;
+ struct platform_device *pdev;
unsigned int id;
struct module *owner;
const struct esoc_clink_ops const *clink_ops;
struct subsys_desc subsys;
struct subsys_device *subsys_dev;
struct device_node *np;
+ bool auto_boot;
+ bool primary;
+ bool statusline_not_a_powersource;
+ bool userspace_handle_shutdown;
};
/**
* struct esoc_clink_ops: Operations to control external soc
* @cmd_exe: Execute control command
* @get_status: Get current status, or response to previous command
+ * @get_err_fatal: Get status of err fatal signal
* @notify_esoc: notify external soc of events
*/
struct esoc_clink_ops {
int (*cmd_exe)(enum esoc_cmd cmd, struct esoc_clink *dev);
- int (*get_status)(u32 *status, struct esoc_clink *dev);
+ void (*get_status)(u32 *status, struct esoc_clink *dev);
+ void (*get_err_fatal)(u32 *status, struct esoc_clink *dev);
void (*notify)(enum esoc_notify notify, struct esoc_clink *dev);
};
snprintf(subsys_name, len, "esoc%d", esoc_clink->id);
esoc_clink->subsys.name = subsys_name;
esoc_clink->dev.of_node = esoc_clink->np;
- esoc_clink->subsys.dev = &esoc_clink->dev;
+ esoc_clink->subsys.dev = &esoc_clink->pdev->dev;
esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys);
if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) {
dev_err(&esoc_clink->dev, "failed to register ssr node\n");
clink_ops->notify(esoc_cmd, esoc_clink);
break;
case ESOC_GET_STATUS:
- err = clink_ops->get_status(&status, esoc_clink);
- if (err)
- return err;
+ clink_ops->get_status(&status, esoc_clink);
+ put_user(status, (unsigned int __user *)uarg);
+ break;
+ case ESOC_GET_ERR_FATAL:
+ clink_ops->get_err_fatal(&status, esoc_clink);
put_user(status, (unsigned int __user *)uarg);
break;
case ESOC_WAIT_FOR_CRASH:
struct intel_rps_client semaphores, mmioflips;
/* manual wa residency calculations */
- struct intel_rps_ei up_ei, down_ei;
+ struct intel_rps_ei ei;
/*
* Protects RPS/RC6 register access and PCU communication.
ei->media_c0 = I915_READ(VLV_MEDIA_C0_COUNT);
}
-static bool vlv_c0_above(struct drm_i915_private *dev_priv,
- const struct intel_rps_ei *old,
- const struct intel_rps_ei *now,
- int threshold)
-{
- u64 time, c0;
- unsigned int mul = 100;
-
- if (old->cz_clock == 0)
- return false;
-
- if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH)
- mul <<= 8;
-
- time = now->cz_clock - old->cz_clock;
- time *= threshold * dev_priv->czclk_freq;
-
- /* Workload can be split between render + media, e.g. SwapBuffers
- * being blitted in X after being rendered in mesa. To account for
- * this we need to combine both engines into our activity counter.
- */
- c0 = now->render_c0 - old->render_c0;
- c0 += now->media_c0 - old->media_c0;
- c0 *= mul * VLV_CZ_CLOCK_TO_MILLI_SEC;
-
- return c0 >= time;
-}
-
void gen6_rps_reset_ei(struct drm_i915_private *dev_priv)
{
- vlv_c0_read(dev_priv, &dev_priv->rps.down_ei);
- dev_priv->rps.up_ei = dev_priv->rps.down_ei;
+ memset(&dev_priv->rps.ei, 0, sizeof(dev_priv->rps.ei));
}
static u32 vlv_wa_c0_ei(struct drm_i915_private *dev_priv, u32 pm_iir)
{
+ const struct intel_rps_ei *prev = &dev_priv->rps.ei;
struct intel_rps_ei now;
u32 events = 0;
- if ((pm_iir & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED)) == 0)
+ if ((pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) == 0)
return 0;
vlv_c0_read(dev_priv, &now);
if (now.cz_clock == 0)
return 0;
- if (pm_iir & GEN6_PM_RP_DOWN_EI_EXPIRED) {
- if (!vlv_c0_above(dev_priv,
- &dev_priv->rps.down_ei, &now,
- dev_priv->rps.down_threshold))
- events |= GEN6_PM_RP_DOWN_THRESHOLD;
- dev_priv->rps.down_ei = now;
- }
+ if (prev->cz_clock) {
+ u64 time, c0;
+ unsigned int mul;
- if (pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) {
- if (vlv_c0_above(dev_priv,
- &dev_priv->rps.up_ei, &now,
- dev_priv->rps.up_threshold))
- events |= GEN6_PM_RP_UP_THRESHOLD;
- dev_priv->rps.up_ei = now;
+ mul = VLV_CZ_CLOCK_TO_MILLI_SEC * 100; /* scale to threshold% */
+ if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH)
+ mul <<= 8;
+
+ time = now.cz_clock - prev->cz_clock;
+ time *= dev_priv->czclk_freq;
+
+ /* Workload can be split between render + media,
+ * e.g. SwapBuffers being blitted in X after being rendered in
+ * mesa. To account for this we need to combine both engines
+ * into our activity counter.
+ */
+ c0 = now.render_c0 - prev->render_c0;
+ c0 += now.media_c0 - prev->media_c0;
+ c0 *= mul;
+
+ if (c0 > time * dev_priv->rps.up_threshold)
+ events = GEN6_PM_RP_UP_THRESHOLD;
+ else if (c0 < time * dev_priv->rps.down_threshold)
+ events = GEN6_PM_RP_DOWN_THRESHOLD;
}
+ dev_priv->rps.ei = now;
return events;
}
/* Let's track the enabled rps events */
if (IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
/* WaGsvRC0ResidencyMethod:vlv */
- dev_priv->pm_rps_events = GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED;
+ dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED;
else
dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS;
break;
}
+ /* When byt can survive without system hang with dynamic
+ * sw freq adjustments, this restriction can be lifted.
+ */
+ if (IS_VALLEYVIEW(dev_priv))
+ goto skip_hw_write;
+
I915_WRITE(GEN6_RP_UP_EI,
GT_INTERVAL_FROM_US(dev_priv, ei_up));
I915_WRITE(GEN6_RP_UP_THRESHOLD,
GEN6_RP_UP_BUSY_AVG |
GEN6_RP_DOWN_IDLE_AVG);
+skip_hw_write:
dev_priv->rps.power = new_power;
dev_priv->rps.up_threshold = threshold_up;
dev_priv->rps.down_threshold = threshold_down;
{
u32 mask = 0;
+ /* We use UP_EI_EXPIRED interupts for both up/down in manual mode */
if (val > dev_priv->rps.min_freq_softlimit)
- mask |= GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT;
+ mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT;
if (val < dev_priv->rps.max_freq_softlimit)
mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_UP_THRESHOLD;
{
mutex_lock(&dev_priv->rps.hw_lock);
if (dev_priv->rps.enabled) {
- if (dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED))
+ if (dev_priv->pm_rps_events & GEN6_PM_RP_UP_EI_EXPIRED)
gen6_rps_reset_ei(dev_priv);
I915_WRITE(GEN6_PMINTRMSK,
gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq));
sde/sde_vbif.o \
sde_dbg_evtlog.o \
sde_io_util.o \
+ dba_bridge.o \
sde_edid_parser.o
# use drm gpu driver only if qcom_kgsl driver not available
struct msm_gpu *gpu;
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
+ struct msm_gpu_config a3xx_config = { 0 };
int ret;
if (!pdev) {
adreno_gpu->registers = a3xx_registers;
adreno_gpu->reg_offsets = a3xx_register_offsets;
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
+ a3xx_config.ioname = MSM_GPU_DEFAULT_IONAME;
+ a3xx_config.irqname = MSM_GPU_DEFAULT_IRQNAME;
+ a3xx_config.nr_rings = 1;
+ a3xx_config.va_start = 0x300000;
+ a3xx_config.va_end = 0xffffffff;
+
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, &a3xx_config);
if (ret)
goto fail;
struct msm_gpu *gpu;
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
+ struct msm_gpu_config a4xx_config = { 0 };
int ret;
if (!pdev) {
adreno_gpu->registers = a4xx_registers;
adreno_gpu->reg_offsets = a4xx_register_offsets;
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
+ a4xx_config.ioname = MSM_GPU_DEFAULT_IONAME;
+ a4xx_config.irqname = MSM_GPU_DEFAULT_IRQNAME;
+ a4xx_config.nr_rings = 1;
+ a4xx_config.va_start = 0x300000;
+ a4xx_config.va_end = 0xffffffff;
+
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, &a4xx_config);
if (ret)
goto fail;
0xe9c0, 0xe9c7, 0xe9d0, 0xe9d1, 0xea00, 0xea01, 0xea10, 0xea1c,
0xea40, 0xea68, 0xea80, 0xea80, 0xea82, 0xeaa3, 0xeaa5, 0xeac2,
0xeb80, 0xeb8f, 0xebb0, 0xebb0, 0xec00, 0xec05, 0xec08, 0xece9,
- 0xecf0, 0xecf0, 0xf400, 0xf400, 0xf800, 0xf807,
+ 0xecf0, 0xecf0, 0xf800, 0xf807,
~0
};
struct a5xx_gpu *a5xx_gpu = NULL;
struct adreno_gpu *adreno_gpu;
struct msm_gpu *gpu;
+ struct msm_gpu_config a5xx_config = { 0 };
int ret;
if (!pdev) {
/* Check the efuses for some configuration */
a5xx_efuses_read(pdev, adreno_gpu);
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 4);
+ a5xx_config.ioname = MSM_GPU_DEFAULT_IONAME;
+ a5xx_config.irqname = MSM_GPU_DEFAULT_IRQNAME;
+
+ /* Set the number of rings to 4 - yay preemption */
+ a5xx_config.nr_rings = 4;
+
+ /*
+ * Set the user domain range to fall into the TTBR1 region for global
+ * objects
+ */
+ a5xx_config.va_start = 0x800000000;
+ a5xx_config.va_end = 0x8ffffffff;
+
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, &a5xx_config);
if (ret) {
a5xx_destroy(&(a5xx_gpu->base.base));
return ERR_PTR(ret);
}
}
+static void a5xx_snapshot_preemption(struct msm_gpu *gpu, struct msm_snapshot
+ *snapshot)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
+ struct msm_snapshot_gpu_object header = {
+ .type = SNAPSHOT_GPU_OBJECT_GLOBAL,
+ .size = A5XX_PREEMPT_RECORD_SIZE >> 2,
+ .pt_base = 0,
+ };
+ int index;
+
+ if (gpu->nr_rings <= 1)
+ return;
+
+ for (index = 0; index < gpu->nr_rings; index++) {
+
+ header.gpuaddr = a5xx_gpu->preempt_iova[index];
+
+ if (!SNAPSHOT_HEADER(snapshot, header,
+ SNAPSHOT_SECTION_GPU_OBJECT_V2,
+ A5XX_PREEMPT_RECORD_SIZE >> 2))
+ return;
+
+ SNAPSHOT_MEMCPY(snapshot, a5xx_gpu->preempt[index],
+ A5XX_PREEMPT_RECORD_SIZE);
+ }
+}
+
int a5xx_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot)
{
struct crashdump crashdump = { 0 };
/* CP MERCIU */
a5xx_snapshot_cp_merciu(gpu, snapshot);
+ /* Preemption records*/
+ a5xx_snapshot_preemption(gpu, snapshot);
+
crashdump_destroy(gpu, &crashdump);
snapshot->priv = NULL;
ring->gpu->name, ring->id);
}
-static const char *iommu_ports[] = {
- "gfx3d_user",
-};
-
/* Read the set of powerlevels */
static int _adreno_get_pwrlevels(struct msm_gpu *gpu, struct device_node *node)
{
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
struct adreno_gpu *adreno_gpu,
- const struct adreno_gpu_funcs *funcs, int nr_rings)
+ const struct adreno_gpu_funcs *funcs,
+ struct msm_gpu_config *gpu_config)
{
struct adreno_platform_config *config = pdev->dev.platform_data;
- struct msm_gpu_config adreno_gpu_config = { 0 };
struct msm_gpu *gpu = &adreno_gpu->base;
struct msm_mmu *mmu;
int ret;
/* Get the rest of the target configuration from the device tree */
adreno_of_parse(pdev, gpu);
- adreno_gpu_config.ioname = "kgsl_3d0_reg_memory";
- adreno_gpu_config.irqname = "kgsl_3d0_irq";
- adreno_gpu_config.nr_rings = nr_rings;
-
- adreno_gpu_config.va_start = SZ_16M;
- adreno_gpu_config.va_end = 0xffffffff;
-
- if (adreno_gpu->revn >= 500) {
- /* 5XX targets use a 64 bit region */
- adreno_gpu_config.va_start = 0x800000000;
- adreno_gpu_config.va_end = 0x8ffffffff;
- } else {
- adreno_gpu_config.va_start = 0x300000;
- adreno_gpu_config.va_end = 0xffffffff;
- }
-
- adreno_gpu_config.nr_rings = nr_rings;
-
ret = msm_gpu_init(drm, pdev, &adreno_gpu->base, &funcs->base,
- adreno_gpu->info->name, &adreno_gpu_config);
+ adreno_gpu->info->name, gpu_config);
if (ret)
return ret;
mmu = gpu->aspace->mmu;
if (mmu) {
- ret = mmu->funcs->attach(mmu, iommu_ports,
- ARRAY_SIZE(iommu_ports));
+ ret = mmu->funcs->attach(mmu, NULL, 0);
if (ret)
return ret;
}
return ERR_PTR(-ENODEV);
if (groupid >= adreno_gpu->nr_counter_groups)
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(-ENODEV);
return (struct adreno_counter_group *)
adreno_gpu->counter_groups[groupid];
struct adreno_counter_group *group =
get_counter_group(gpu, groupid);
- if (!IS_ERR(group) && group->funcs.read)
+ if (!IS_ERR_OR_NULL(group) && group->funcs.read)
return group->funcs.read(gpu, group, counterid);
return 0;
struct adreno_counter_group *group =
get_counter_group(gpu, groupid);
- if (!IS_ERR(group) && group->funcs.put)
+ if (!IS_ERR_OR_NULL(group) && group->funcs.put)
group->funcs.put(gpu, group, counterid);
}
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
- int nr_rings);
+ struct msm_gpu_config *config);
void adreno_gpu_cleanup(struct adreno_gpu *gpu);
void adreno_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot);
--- /dev/null
+/*
+ * Copyright (c) 2016-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 <video/msm_dba.h>
+#include "drm_edid.h"
+#include "sde_kms.h"
+#include "dba_bridge.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "dba_bridge:[%s] " fmt, __func__
+
+/**
+ * struct dba_bridge - DBA bridge information
+ * @base: drm_bridge base
+ * @client_name: Client's name who calls the init
+ * @chip_name: Bridge chip name
+ * @name: Bridge chip name
+ * @id: Bridge driver index
+ * @display: Private display handle
+ * @list: Bridge chip driver list node
+ * @ops: DBA operation container
+ * @dba_ctx: DBA context
+ * @mode: DRM mode info
+ * @hdmi_mode: HDMI or DVI mode for the sink
+ * @num_of_input_lanes: Number of input lanes in case of DSI/LVDS
+ * @pluggable: If it's pluggable
+ * @panel_count: Number of panels attached to this display
+ */
+struct dba_bridge {
+ struct drm_bridge base;
+ char client_name[MSM_DBA_CLIENT_NAME_LEN];
+ char chip_name[MSM_DBA_CHIP_NAME_MAX_LEN];
+ u32 id;
+ void *display;
+ struct list_head list;
+ struct msm_dba_ops ops;
+ void *dba_ctx;
+ struct drm_display_mode mode;
+ bool hdmi_mode;
+ u32 num_of_input_lanes;
+ bool pluggable;
+ u32 panel_count;
+};
+#define to_dba_bridge(x) container_of((x), struct dba_bridge, base)
+
+static void _dba_bridge_cb(void *data, enum msm_dba_callback_event event)
+{
+ struct dba_bridge *d_bridge = data;
+
+ if (!d_bridge) {
+ SDE_ERROR("Invalid data\n");
+ return;
+ }
+
+ DRM_DEBUG("event: %d\n", event);
+
+ switch (event) {
+ case MSM_DBA_CB_HPD_CONNECT:
+ DRM_DEBUG("HPD CONNECT\n");
+ break;
+ case MSM_DBA_CB_HPD_DISCONNECT:
+ DRM_DEBUG("HPD DISCONNECT\n");
+ break;
+ default:
+ DRM_DEBUG("event:%d is not supported\n", event);
+ break;
+ }
+}
+
+static int _dba_bridge_attach(struct drm_bridge *bridge)
+{
+ struct dba_bridge *d_bridge = to_dba_bridge(bridge);
+ struct msm_dba_reg_info info;
+ int ret = 0;
+
+ if (!bridge) {
+ SDE_ERROR("Invalid params\n");
+ return -EINVAL;
+ }
+
+ memset(&info, 0, sizeof(info));
+ /* initialize DBA registration data */
+ strlcpy(info.client_name, d_bridge->client_name,
+ MSM_DBA_CLIENT_NAME_LEN);
+ strlcpy(info.chip_name, d_bridge->chip_name,
+ MSM_DBA_CHIP_NAME_MAX_LEN);
+ info.instance_id = d_bridge->id;
+ info.cb = _dba_bridge_cb;
+ info.cb_data = d_bridge;
+
+ /* register client with DBA and get device's ops*/
+ if (IS_ENABLED(CONFIG_MSM_DBA)) {
+ d_bridge->dba_ctx = msm_dba_register_client(&info,
+ &d_bridge->ops);
+ if (IS_ERR_OR_NULL(d_bridge->dba_ctx)) {
+ SDE_ERROR("dba register failed\n");
+ ret = PTR_ERR(d_bridge->dba_ctx);
+ goto error;
+ }
+ } else {
+ SDE_ERROR("DBA not enabled\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ DRM_INFO("client:%s bridge:[%s:%d] attached\n",
+ d_bridge->client_name, d_bridge->chip_name, d_bridge->id);
+
+error:
+ return ret;
+}
+
+static void _dba_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ if (!bridge) {
+ SDE_ERROR("Invalid params\n");
+ return;
+ }
+}
+
+static void _dba_bridge_enable(struct drm_bridge *bridge)
+{
+ int rc = 0;
+ struct dba_bridge *d_bridge = to_dba_bridge(bridge);
+ struct msm_dba_video_cfg video_cfg;
+ struct drm_display_mode *mode;
+ struct hdmi_avi_infoframe avi_frame;
+
+ if (!bridge) {
+ SDE_ERROR("Invalid params\n");
+ return;
+ }
+
+ memset(&video_cfg, 0, sizeof(video_cfg));
+ memset(&avi_frame, 0, sizeof(avi_frame));
+ mode = &d_bridge->mode;
+ video_cfg.h_active = mode->hdisplay;
+ video_cfg.v_active = mode->vdisplay;
+ video_cfg.h_front_porch = mode->hsync_start - mode->hdisplay;
+ video_cfg.v_front_porch = mode->vsync_start - mode->vdisplay;
+ video_cfg.h_back_porch = mode->htotal - mode->hsync_end;
+ video_cfg.v_back_porch = mode->vtotal - mode->vsync_end;
+ video_cfg.h_pulse_width = mode->hsync_end - mode->hsync_start;
+ video_cfg.v_pulse_width = mode->vsync_end - mode->vsync_start;
+ video_cfg.pclk_khz = mode->clock;
+ video_cfg.hdmi_mode = d_bridge->hdmi_mode;
+ video_cfg.num_of_input_lanes = d_bridge->num_of_input_lanes;
+
+ SDE_DEBUG(
+ "video=h[%d,%d,%d,%d] v[%d,%d,%d,%d] pclk=%d hdmi=%d lane=%d\n",
+ video_cfg.h_active, video_cfg.h_front_porch,
+ video_cfg.h_pulse_width, video_cfg.h_back_porch,
+ video_cfg.v_active, video_cfg.v_front_porch,
+ video_cfg.v_pulse_width, video_cfg.v_back_porch,
+ video_cfg.pclk_khz, video_cfg.hdmi_mode,
+ video_cfg.num_of_input_lanes);
+
+ rc = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, mode);
+ if (rc) {
+ SDE_ERROR("get avi frame failed ret=%d\n", rc);
+ } else {
+ video_cfg.scaninfo = avi_frame.scan_mode;
+ switch (avi_frame.picture_aspect) {
+ case HDMI_PICTURE_ASPECT_4_3:
+ video_cfg.ar = MSM_DBA_AR_4_3;
+ break;
+ case HDMI_PICTURE_ASPECT_16_9:
+ video_cfg.ar = MSM_DBA_AR_16_9;
+ break;
+ default:
+ break;
+ }
+ video_cfg.vic = avi_frame.video_code;
+ DRM_INFO("scaninfo=%d ar=%d vic=%d\n",
+ video_cfg.scaninfo, video_cfg.ar, video_cfg.vic);
+ }
+
+ if (d_bridge->ops.video_on) {
+ rc = d_bridge->ops.video_on(d_bridge->dba_ctx, true,
+ &video_cfg, 0);
+ if (rc)
+ SDE_ERROR("video on failed ret=%d\n", rc);
+ }
+}
+
+static void _dba_bridge_disable(struct drm_bridge *bridge)
+{
+ int rc = 0;
+ struct dba_bridge *d_bridge = to_dba_bridge(bridge);
+
+ if (!bridge) {
+ SDE_ERROR("Invalid params\n");
+ return;
+ }
+
+ if (d_bridge->ops.video_on) {
+ rc = d_bridge->ops.video_on(d_bridge->dba_ctx, false, NULL, 0);
+ if (rc)
+ SDE_ERROR("video off failed ret=%d\n", rc);
+ }
+}
+
+static void _dba_bridge_post_disable(struct drm_bridge *bridge)
+{
+ if (!bridge) {
+ SDE_ERROR("Invalid params\n");
+ return;
+ }
+}
+
+static void _dba_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct dba_bridge *d_bridge = to_dba_bridge(bridge);
+
+ if (!bridge || !mode || !adjusted_mode || !d_bridge) {
+ SDE_ERROR("Invalid params\n");
+ return;
+ } else if (!d_bridge->panel_count) {
+ SDE_ERROR("Panel count is 0\n");
+ return;
+ }
+
+ d_bridge->mode = *adjusted_mode;
+ /* Adjust mode according to number of panels */
+ d_bridge->mode.hdisplay /= d_bridge->panel_count;
+ d_bridge->mode.hsync_start /= d_bridge->panel_count;
+ d_bridge->mode.hsync_end /= d_bridge->panel_count;
+ d_bridge->mode.htotal /= d_bridge->panel_count;
+ d_bridge->mode.clock /= d_bridge->panel_count;
+}
+
+static bool _dba_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ bool ret = true;
+
+ if (!bridge || !mode || !adjusted_mode) {
+ SDE_ERROR("Invalid params\n");
+ return false;
+ }
+
+ return ret;
+}
+
+static const struct drm_bridge_funcs _dba_bridge_ops = {
+ .attach = _dba_bridge_attach,
+ .mode_fixup = _dba_bridge_mode_fixup,
+ .pre_enable = _dba_bridge_pre_enable,
+ .enable = _dba_bridge_enable,
+ .disable = _dba_bridge_disable,
+ .post_disable = _dba_bridge_post_disable,
+ .mode_set = _dba_bridge_mode_set,
+};
+
+struct drm_bridge *dba_bridge_init(struct drm_device *dev,
+ struct drm_encoder *encoder,
+ struct dba_bridge_init *data)
+{
+ int rc = 0;
+ struct dba_bridge *bridge;
+ struct msm_drm_private *priv = NULL;
+
+ if (!dev || !encoder || !data) {
+ SDE_ERROR("dev=%pK or encoder=%pK or data=%pK is NULL\n",
+ dev, encoder, data);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ priv = dev->dev_private;
+ if (!priv) {
+ SDE_ERROR("Private data is not present\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge) {
+ SDE_ERROR("out of memory\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ INIT_LIST_HEAD(&bridge->list);
+ strlcpy(bridge->client_name, data->client_name,
+ MSM_DBA_CLIENT_NAME_LEN);
+ strlcpy(bridge->chip_name, data->chip_name,
+ MSM_DBA_CHIP_NAME_MAX_LEN);
+ bridge->id = data->id;
+ bridge->display = data->display;
+ bridge->hdmi_mode = data->hdmi_mode;
+ bridge->num_of_input_lanes = data->num_of_input_lanes;
+ bridge->pluggable = data->pluggable;
+ bridge->panel_count = data->panel_count;
+ bridge->base.funcs = &_dba_bridge_ops;
+ bridge->base.encoder = encoder;
+
+ rc = drm_bridge_attach(dev, &bridge->base);
+ if (rc) {
+ SDE_ERROR("failed to attach bridge, rc=%d\n", rc);
+ goto error_free_bridge;
+ }
+
+ if (data->precede_bridge) {
+ /* Insert current bridge */
+ bridge->base.next = data->precede_bridge->next;
+ data->precede_bridge->next = &bridge->base;
+ } else {
+ encoder->bridge = &bridge->base;
+ }
+
+ if (!bridge->pluggable) {
+ if (bridge->ops.power_on)
+ bridge->ops.power_on(bridge->dba_ctx, true, 0);
+ if (bridge->ops.check_hpd)
+ bridge->ops.check_hpd(bridge->dba_ctx, 0);
+ }
+
+ return &bridge->base;
+
+error_free_bridge:
+ kfree(bridge);
+error:
+ return ERR_PTR(rc);
+}
+
+void dba_bridge_cleanup(struct drm_bridge *bridge)
+{
+ struct dba_bridge *d_bridge = to_dba_bridge(bridge);
+
+ if (!bridge)
+ return;
+
+ if (IS_ENABLED(CONFIG_MSM_DBA)) {
+ if (!IS_ERR_OR_NULL(d_bridge->dba_ctx))
+ msm_dba_deregister_client(d_bridge->dba_ctx);
+ }
+
+ if (d_bridge->base.encoder)
+ d_bridge->base.encoder->bridge = NULL;
+
+ kfree(bridge);
+}
--- /dev/null
+/*
+ * Copyright (c) 2016-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 _DBA_BRIDGE_H_
+#define _DBA_BRIDGE_H_
+
+#include <linux/types.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "msm_drv.h"
+
+/**
+ * struct dba_bridge_init - Init parameters for DBA bridge
+ * @client_name: Client's name who calls the init
+ * @chip_name: Bridge chip name
+ * @id: Bridge driver index
+ * @display: Private display handle
+ * @hdmi_mode: HDMI or DVI mode for the sink
+ * @num_of_input_lanes: Number of input lanes in case of DSI/LVDS
+ * @precede_bridge: Precede bridge chip
+ * @pluggable: If it's pluggable
+ * @panel_count: Number of panels attached to this display
+ */
+struct dba_bridge_init {
+ const char *client_name;
+ const char *chip_name;
+ u32 id;
+ void *display;
+ bool hdmi_mode;
+ u32 num_of_input_lanes;
+ struct drm_bridge *precede_bridge;
+ bool pluggable;
+ u32 panel_count;
+};
+
+/**
+ * dba_bridge_init - Initialize the DBA bridge
+ * @dev: Pointer to drm device handle
+ * @encoder: Pointer to drm encoder handle
+ * @data: Pointer to init data
+ * Returns: pointer of struct drm_bridge
+ */
+struct drm_bridge *dba_bridge_init(struct drm_device *dev,
+ struct drm_encoder *encoder,
+ struct dba_bridge_init *data);
+
+/**
+ * dba_bridge_cleanup - Clean up the DBA bridge
+ * @bridge: Pointer to DBA bridge handle
+ * Returns: void
+ */
+void dba_bridge_cleanup(struct drm_bridge *bridge);
+
+#endif /* _DBA_BRIDGE_H_ */
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-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
DSI_W32(ctrl, DSI_CTRL, reg_value);
+ /* Force clock lane in HS */
+ reg_value = DSI_R32(ctrl, DSI_LANE_CTRL);
+ if (cfg->force_clk_lane_hs)
+ reg_value |= BIT(28);
+ else
+ reg_value &= ~BIT(28);
+ DSI_W32(ctrl, DSI_LANE_CTRL, reg_value);
+
pr_debug("[DSI_%d]Host configuration complete\n", ctrl->index);
}
{
u32 reg = 0;
+ reg = DSI_R32(ctrl, DSI_LANE_CTRL);
if (lanes & DSI_CLOCK_LANE)
- reg = BIT(4);
+ reg |= BIT(4);
if (lanes & DSI_DATA_LANE_0)
reg |= BIT(0);
if (lanes & DSI_DATA_LANE_1)
u32 reg = 0;
reg = DSI_R32(ctrl, DSI_LANE_CTRL);
- reg &= ~BIT(4); /* clock lane */
+ if (lanes & DSI_CLOCK_LANE)
+ reg &= ~BIT(4); /* clock lane */
if (lanes & DSI_DATA_LANE_0)
reg &= ~BIT(0);
if (lanes & DSI_DATA_LANE_1)
* HPG recommends separate writes for clearing ULPS_REQUEST and
* ULPS_EXIT.
*/
- DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
+ reg = DSI_R32(ctrl, DSI_LANE_CTRL);
+ if (lanes & DSI_CLOCK_LANE)
+ reg &= ~BIT(12);
+ if (lanes & DSI_DATA_LANE_0)
+ reg &= ~BIT(8);
+ if (lanes & DSI_DATA_LANE_1)
+ reg &= ~BIT(9);
+ if (lanes & DSI_DATA_LANE_2)
+ reg &= ~BIT(10);
+ if (lanes & DSI_DATA_LANE_3)
+ reg &= ~BIT(11);
+ DSI_W32(ctrl, DSI_LANE_CTRL, reg);
pr_debug("[DSI_%d] ULPS request cleared\n", ctrl->index);
}
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
* @ignore_rx_eot: Ignore Rx EOT packets if set to true.
* @append_tx_eot: Append EOT packets for forward transmissions if set to
* true.
+ * @force_clk_lane_hs: Force clock lane in high speed mode.
*/
struct dsi_host_common_cfg {
enum dsi_pixel_format dst_format;
u32 t_clk_pre;
bool ignore_rx_eot;
bool append_tx_eot;
+ bool force_clk_lane_hs;
};
/**
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
#include <linux/of.h>
#include "msm_drv.h"
+#include "sde_kms.h"
#include "dsi_display.h"
#include "dsi_panel.h"
#include "dsi_ctrl.h"
#include "dsi_ctrl_hw.h"
#include "dsi_drm.h"
+#include "dba_bridge.h"
#define to_dsi_display(x) container_of(x, struct dsi_display, host)
+#define DSI_DBA_CLIENT_NAME "dsi"
static DEFINE_MUTEX(dsi_display_list_lock);
static LIST_HEAD(dsi_display_list);
if (dsi_display == NULL)
return -EINVAL;
- panel = dsi_display->panel;
+ panel = dsi_display->panel[0];
rc = dsi_panel_set_backlight(panel, bl_lvl);
if (rc)
display->ctrl[i].phy->name);
}
- len += snprintf(buf + len, (SZ_4K - len),
- "\tPanel = %s\n", display->panel->name);
+ for (i = 0; i < display->panel_count; i++)
+ len += snprintf(buf + len, (SZ_4K - len),
+ "\tPanel_%d = %s\n", i, display->panel[i]->name);
len += snprintf(buf + len, (SZ_4K - len),
"\tClock master = %s\n",
static int dsi_display_parse_dt(struct dsi_display *display)
{
int rc = 0;
- int i;
+ int i, size;
u32 phy_count = 0;
struct device_node *of_node;
goto error;
}
- of_node = of_parse_phandle(display->pdev->dev.of_node,
- "qcom,dsi-panel", 0);
- if (!of_node) {
- pr_err("No Panel device present\n");
+ if (of_get_property(display->pdev->dev.of_node, "qcom,dsi-panel",
+ &size)) {
+ display->panel_count = size / sizeof(int);
+ display->panel_of = devm_kzalloc(&display->pdev->dev,
+ sizeof(struct device_node *) * display->panel_count,
+ GFP_KERNEL);
+ if (!display->panel_of) {
+ SDE_ERROR("out of memory for panel_of\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+ display->panel = devm_kzalloc(&display->pdev->dev,
+ sizeof(struct dsi_panel *) * display->panel_count,
+ GFP_KERNEL);
+ if (!display->panel) {
+ SDE_ERROR("out of memory for panel\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+ for (i = 0; i < display->panel_count; i++) {
+ display->panel_of[i] =
+ of_parse_phandle(display->pdev->dev.of_node,
+ "qcom,dsi-panel", i);
+ if (!display->panel_of[i]) {
+ SDE_ERROR("of_parse dsi-panel failed\n");
+ rc = -ENODEV;
+ goto error;
+ }
+ }
+ } else {
+ SDE_ERROR("No qcom,dsi-panel of node\n");
rc = -ENODEV;
goto error;
- } else {
- display->panel_of = of_node;
+ }
+
+ if (of_get_property(display->pdev->dev.of_node, "qcom,bridge-index",
+ &size)) {
+ if (size / sizeof(int) != display->panel_count) {
+ SDE_ERROR("size=%lu is different than count=%u\n",
+ size / sizeof(int), display->panel_count);
+ rc = -EINVAL;
+ goto error;
+ }
+ display->bridge_idx = devm_kzalloc(&display->pdev->dev,
+ sizeof(u32) * display->panel_count, GFP_KERNEL);
+ if (!display->bridge_idx) {
+ SDE_ERROR("out of memory for bridge_idx\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+ for (i = 0; i < display->panel_count; i++) {
+ rc = of_property_read_u32_index(
+ display->pdev->dev.of_node,
+ "qcom,bridge-index", i,
+ &(display->bridge_idx[i]));
+ if (rc) {
+ SDE_ERROR(
+ "read bridge-index error,i=%d rc=%d\n",
+ i, rc);
+ rc = -ENODEV;
+ goto error;
+ }
+ }
}
rc = dsi_display_parse_lane_map(display);
goto error;
}
error:
+ if (rc) {
+ if (display->panel_of)
+ for (i = 0; i < display->panel_count; i++)
+ if (display->panel_of[i])
+ of_node_put(display->panel_of[i]);
+ devm_kfree(&display->pdev->dev, display->panel_of);
+ devm_kfree(&display->pdev->dev, display->panel);
+ devm_kfree(&display->pdev->dev, display->bridge_idx);
+ display->panel_count = 0;
+ }
return rc;
}
}
}
- display->panel = dsi_panel_get(&display->pdev->dev, display->panel_of);
- if (IS_ERR_OR_NULL(display->panel)) {
- rc = PTR_ERR(display->panel);
- pr_err("failed to get panel, rc=%d\n", rc);
- display->panel = NULL;
- goto error_ctrl_put;
+ for (i = 0; i < display->panel_count; i++) {
+ display->panel[i] = dsi_panel_get(&display->pdev->dev,
+ display->panel_of[i]);
+ if (IS_ERR_OR_NULL(display->panel)) {
+ rc = PTR_ERR(display->panel);
+ pr_err("failed to get panel, rc=%d\n", rc);
+ display->panel[i] = NULL;
+ goto error_ctrl_put;
+ }
}
rc = dsi_display_clocks_init(display);
if (rc)
pr_err("clocks deinit failed, rc=%d\n", rc);
+ for (i = 0; i < display->panel_count; i++)
+ dsi_panel_put(display->panel[i]);
+
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
dsi_phy_put(ctrl->phy);
return false;
}
- cur = &display->panel->mode;
+ cur = &display->panel[0]->mode;
if (cur->timing.h_active != tgt->timing.h_active) {
pr_debug("timing.h_active differs %d %d\n",
}
timing = &dsi_mode->timing;
- dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
+ dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps);
if (!dfps_caps.dfps_support) {
pr_err("dfps not supported\n");
return -ENOTSUPP;
}
}
- panel_mode = &display->panel->mode;
+ panel_mode = &display->panel[0]->mode;
memcpy(panel_mode, dsi_mode, sizeof(*panel_mode));
error:
}
m_ctrl = display->ctrl[display->clk_master_idx].ctrl;
- dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
+ /* Only check the first panel */
+ dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps);
if (!dfps_caps.dfps_support) {
pr_err("dfps not supported by panel\n");
return -EINVAL;
int i;
struct dsi_display_ctrl *ctrl;
- rc = dsi_panel_get_host_cfg_for_mode(display->panel,
+ rc = dsi_panel_get_host_cfg_for_mode(display->panel[0],
mode,
&display->config);
if (rc) {
struct drm_device *drm;
struct dsi_display *display;
struct platform_device *pdev = to_platform_device(dev);
- int i, rc = 0;
+ int i, j, rc = 0;
if (!dev || !pdev || !master) {
pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n",
goto error_ctrl_deinit;
}
- rc = dsi_panel_drv_init(display->panel, &display->host);
- if (rc) {
- if (rc != -EPROBE_DEFER)
- pr_err("[%s] failed to initialize panel driver, rc=%d\n",
- display->name, rc);
- goto error_host_deinit;
+ for (j = 0; j < display->panel_count; j++) {
+ rc = dsi_panel_drv_init(display->panel[j], &display->host);
+ if (rc) {
+ if (rc != -EPROBE_DEFER)
+ SDE_ERROR(
+ "[%s]Failed to init panel driver, rc=%d\n",
+ display->name, rc);
+ goto error_panel_deinit;
+ }
}
- rc = dsi_panel_get_mode_count(display->panel, &display->num_of_modes);
+ rc = dsi_panel_get_mode_count(display->panel[0],
+ &display->num_of_modes);
if (rc) {
pr_err("[%s] failed to get mode count, rc=%d\n",
display->name, rc);
goto error;
error_panel_deinit:
- (void)dsi_panel_drv_deinit(display->panel);
-error_host_deinit:
+ for (j--; j >= 0; j--)
+ (void)dsi_panel_drv_deinit(display->panel[j]);
(void)dsi_display_mipi_host_deinit(display);
error_ctrl_deinit:
for (i = i - 1; i >= 0; i--) {
mutex_lock(&display->display_lock);
- rc = dsi_panel_drv_deinit(display->panel);
- if (rc)
- pr_err("[%s] failed to deinit panel driver, rc=%d\n",
- display->name, rc);
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_drv_deinit(display->panel[i]);
+ if (rc)
+ SDE_ERROR("[%s] failed to deinit panel driver, rc=%d\n",
+ display->name, rc);
+ }
rc = dsi_display_mipi_host_deinit(display);
if (rc)
display->pdev = pdev;
platform_set_drvdata(pdev, display);
mutex_lock(&dsi_display_list_lock);
- list_add(&display->list, &dsi_display_list);
+ list_add_tail(&display->list, &dsi_display_list);
mutex_unlock(&dsi_display_list_lock);
if (display->is_active) {
int dsi_display_dev_remove(struct platform_device *pdev)
{
- int rc = 0;
+ int rc = 0, i;
struct dsi_display *display;
struct dsi_display *pos, *tmp;
mutex_unlock(&dsi_display_list_lock);
platform_set_drvdata(pdev, NULL);
+ if (display->panel_of)
+ for (i = 0; i < display->panel_count; i++)
+ if (display->panel_of[i])
+ of_node_put(display->panel_of[i]);
+ devm_kfree(&pdev->dev, display->panel_of);
+ devm_kfree(&pdev->dev, display->panel);
+ devm_kfree(&pdev->dev, display->bridge_idx);
devm_kfree(&pdev->dev, display);
return rc;
}
int dsi_display_drm_bridge_init(struct dsi_display *display,
struct drm_encoder *enc)
{
- int rc = 0;
+ int rc = 0, i;
struct dsi_bridge *bridge;
+ struct drm_bridge *dba_bridge;
+ struct dba_bridge_init init_data;
+ struct drm_bridge *precede_bridge;
struct msm_drm_private *priv = NULL;
+ struct dsi_panel *panel;
+ u32 *bridge_idx;
+ u32 num_of_lanes = 0;
if (!display || !display->drm_dev || !enc) {
pr_err("invalid param(s)\n");
priv = display->drm_dev->dev_private;
if (!priv) {
- pr_err("Private data is not present\n");
+ SDE_ERROR("Private data is not present\n");
rc = -EINVAL;
- goto error;
+ goto out;
}
if (display->bridge) {
- pr_err("display is already initialize\n");
- goto error;
+ SDE_ERROR("display is already initialize\n");
+ goto out;
}
bridge = dsi_drm_bridge_init(display, display->drm_dev, enc);
if (IS_ERR_OR_NULL(bridge)) {
rc = PTR_ERR(bridge);
- pr_err("[%s] brige init failed, %d\n", display->name, rc);
- goto error;
+ SDE_ERROR("[%s] brige init failed, %d\n", display->name, rc);
+ goto out;
}
display->bridge = bridge;
priv->bridges[priv->num_bridges++] = &bridge->base;
+ precede_bridge = &bridge->base;
+
+ if (display->panel_count >= MAX_BRIDGES - 1) {
+ SDE_ERROR("too many bridge chips=%d\n", display->panel_count);
+ goto error_bridge;
+ }
+
+ for (i = 0; i < display->panel_count; i++) {
+ panel = display->panel[i];
+ if (panel && display->bridge_idx &&
+ panel->dba_config.dba_panel) {
+ bridge_idx = display->bridge_idx + i;
+ num_of_lanes = 0;
+ memset(&init_data, 0x00, sizeof(init_data));
+ if (panel->host_config.data_lanes & DSI_DATA_LANE_0)
+ num_of_lanes++;
+ if (panel->host_config.data_lanes & DSI_DATA_LANE_1)
+ num_of_lanes++;
+ if (panel->host_config.data_lanes & DSI_DATA_LANE_2)
+ num_of_lanes++;
+ if (panel->host_config.data_lanes & DSI_DATA_LANE_3)
+ num_of_lanes++;
+ init_data.client_name = DSI_DBA_CLIENT_NAME;
+ init_data.chip_name = panel->dba_config.bridge_name;
+ init_data.id = *bridge_idx;
+ init_data.display = display;
+ init_data.hdmi_mode = panel->dba_config.hdmi_mode;
+ init_data.num_of_input_lanes = num_of_lanes;
+ init_data.precede_bridge = precede_bridge;
+ init_data.panel_count = display->panel_count;
+ dba_bridge = dba_bridge_init(display->drm_dev, enc,
+ &init_data);
+ if (IS_ERR_OR_NULL(dba_bridge)) {
+ rc = PTR_ERR(dba_bridge);
+ SDE_ERROR("[%s:%d] dba brige init failed, %d\n",
+ init_data.chip_name, init_data.id, rc);
+ goto error_dba_bridge;
+ }
+ priv->bridges[priv->num_bridges++] = dba_bridge;
+ precede_bridge = dba_bridge;
+ }
+ }
-error:
+ goto out;
+
+error_dba_bridge:
+ for (i = 1; i < MAX_BRIDGES; i++) {
+ dba_bridge_cleanup(priv->bridges[i]);
+ priv->bridges[i] = NULL;
+ }
+error_bridge:
+ dsi_drm_bridge_cleanup(display->bridge);
+ display->bridge = NULL;
+ priv->bridges[0] = NULL;
+ priv->num_bridges = 0;
+out:
mutex_unlock(&display->display_lock);
return rc;
}
int dsi_display_drm_bridge_deinit(struct dsi_display *display)
{
- int rc = 0;
+ int rc = 0, i;
+ struct msm_drm_private *priv = NULL;
if (!display) {
- pr_err("Invalid params\n");
+ SDE_ERROR("Invalid params\n");
+ return -EINVAL;
+ }
+ priv = display->drm_dev->dev_private;
+
+ if (!priv) {
+ SDE_ERROR("Private data is not present\n");
return -EINVAL;
}
mutex_lock(&display->display_lock);
+ for (i = 1; i < MAX_BRIDGES; i++) {
+ dba_bridge_cleanup(priv->bridges[i]);
+ priv->bridges[i] = NULL;
+ }
+
dsi_drm_bridge_cleanup(display->bridge);
display->bridge = NULL;
+ priv->bridges[0] = NULL;
+ priv->num_bridges = 0;
mutex_unlock(&display->display_lock);
return rc;
display = disp;
mutex_lock(&display->display_lock);
- rc = dsi_panel_get_phy_props(display->panel, &phy_props);
+ rc = dsi_panel_get_phy_props(display->panel[0], &phy_props);
if (rc) {
pr_err("[%s] failed to get panel phy props, rc=%d\n",
display->name, rc);
info->max_height = 1080;
info->compression = MSM_DISPLAY_COMPRESS_NONE;
- switch (display->panel->mode.panel_mode) {
+ switch (display->panel[0]->mode.panel_mode) {
case DSI_OP_VIDEO_MODE:
info->capabilities |= MSM_DISPLAY_CAP_VID_MODE;
break;
break;
default:
pr_err("unknwown dsi panel mode %d\n",
- display->panel->mode.panel_mode);
+ display->panel[0]->mode.panel_mode);
break;
}
error:
mutex_lock(&display->display_lock);
- rc = dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
+ rc = dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps);
if (rc) {
pr_err("[%s] failed to get dfps caps from panel\n",
display->name);
/* Insert the dfps "sub-modes" between main panel modes */
int panel_mode_idx = i / num_dfps_rates;
- rc = dsi_panel_get_mode(display->panel, panel_mode_idx, modes);
+ rc = dsi_panel_get_mode(display->panel[0], panel_mode_idx,
+ modes);
if (rc) {
pr_err("[%s] failed to get mode from panel\n",
display->name);
adj_mode = *mode;
adjust_timing_by_ctrl_count(display, &adj_mode);
- rc = dsi_panel_validate_mode(display->panel, &adj_mode);
+ rc = dsi_panel_validate_mode(display->panel[0], &adj_mode);
if (rc) {
pr_err("[%s] panel mode validation failed, rc=%d\n",
display->name, rc);
int dsi_display_prepare(struct dsi_display *display)
{
- int rc = 0;
+ int rc = 0, i, j;
if (!display) {
pr_err("Invalid params\n");
mutex_lock(&display->display_lock);
- rc = dsi_panel_pre_prepare(display->panel);
- if (rc) {
- pr_err("[%s] panel pre-prepare failed, rc=%d\n",
- display->name, rc);
- goto error;
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_pre_prepare(display->panel[i]);
+ if (rc) {
+ SDE_ERROR("[%s] panel pre-prepare failed, rc=%d\n",
+ display->name, rc);
+ goto error_panel_post_unprep;
+ }
}
rc = dsi_display_ctrl_power_on(display);
goto error_ctrl_link_off;
}
- rc = dsi_panel_prepare(display->panel);
- if (rc) {
- pr_err("[%s] panel prepare failed, rc=%d\n", display->name, rc);
- goto error_host_engine_off;
+ for (j = 0; j < display->panel_count; j++) {
+ rc = dsi_panel_prepare(display->panel[j]);
+ if (rc) {
+ SDE_ERROR("[%s] panel prepare failed, rc=%d\n",
+ display->name, rc);
+ goto error_panel_unprep;
+ }
}
goto error;
-error_host_engine_off:
+error_panel_unprep:
+ for (j--; j >= 0; j--)
+ (void)dsi_panel_unprepare(display->panel[j]);
(void)dsi_display_ctrl_host_disable(display);
error_ctrl_link_off:
(void)dsi_display_ctrl_link_clk_off(display);
error_ctrl_pwr_off:
(void)dsi_display_ctrl_power_off(display);
error_panel_post_unprep:
- (void)dsi_panel_post_unprepare(display->panel);
+ for (i--; i >= 0; i--)
+ (void)dsi_panel_post_unprepare(display->panel[i]);
error:
mutex_unlock(&display->display_lock);
return rc;
int dsi_display_enable(struct dsi_display *display)
{
- int rc = 0;
+ int rc = 0, i;
if (!display) {
pr_err("Invalid params\n");
mutex_lock(&display->display_lock);
- rc = dsi_panel_enable(display->panel);
- if (rc) {
- pr_err("[%s] failed to enable DSI panel, rc=%d\n",
- display->name, rc);
- goto error;
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_enable(display->panel[i]);
+ if (rc) {
+ SDE_ERROR("[%s] failed to enable DSI panel, rc=%d\n",
+ display->name, rc);
+ goto error_disable_panel;
+ }
}
if (display->config.panel_mode == DSI_OP_VIDEO_MODE) {
goto error;
error_disable_panel:
- (void)dsi_panel_disable(display->panel);
+ for (i--; i >= 0; i--)
+ (void)dsi_panel_disable(display->panel[i]);
error:
mutex_unlock(&display->display_lock);
return rc;
int dsi_display_post_enable(struct dsi_display *display)
{
- int rc = 0;
+ int rc = 0, i;
if (!display) {
pr_err("Invalid params\n");
mutex_lock(&display->display_lock);
- rc = dsi_panel_post_enable(display->panel);
- if (rc)
- pr_err("[%s] panel post-enable failed, rc=%d\n",
- display->name, rc);
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_post_enable(display->panel[i]);
+ if (rc)
+ SDE_ERROR("[%s] panel post-enable failed, rc=%d\n",
+ display->name, rc);
+ }
mutex_unlock(&display->display_lock);
return rc;
int dsi_display_pre_disable(struct dsi_display *display)
{
- int rc = 0;
+ int rc = 0, i;
if (!display) {
pr_err("Invalid params\n");
mutex_lock(&display->display_lock);
- rc = dsi_panel_pre_disable(display->panel);
- if (rc)
- pr_err("[%s] panel pre-disable failed, rc=%d\n",
- display->name, rc);
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_pre_disable(display->panel[i]);
+ if (rc)
+ SDE_ERROR("[%s] panel pre-disable failed, rc=%d\n",
+ display->name, rc);
+ }
mutex_unlock(&display->display_lock);
return rc;
int dsi_display_disable(struct dsi_display *display)
{
- int rc = 0;
+ int rc = 0, i;
if (!display) {
pr_err("Invalid params\n");
pr_err("[%s] display wake up failed, rc=%d\n",
display->name, rc);
- rc = dsi_panel_disable(display->panel);
- if (rc)
- pr_err("[%s] failed to disable DSI panel, rc=%d\n",
- display->name, rc);
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_disable(display->panel[i]);
+ if (rc)
+ SDE_ERROR("[%s] failed to disable DSI panel, rc=%d\n",
+ display->name, rc);
+ }
if (display->config.panel_mode == DSI_OP_VIDEO_MODE) {
rc = dsi_display_vid_engine_disable(display);
int dsi_display_unprepare(struct dsi_display *display)
{
- int rc = 0;
+ int rc = 0, i;
if (!display) {
pr_err("Invalid params\n");
pr_err("[%s] display wake up failed, rc=%d\n",
display->name, rc);
- rc = dsi_panel_unprepare(display->panel);
- if (rc)
- pr_err("[%s] panel unprepare failed, rc=%d\n",
- display->name, rc);
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_unprepare(display->panel[i]);
+ if (rc)
+ SDE_ERROR("[%s] panel unprepare failed, rc=%d\n",
+ display->name, rc);
+ }
rc = dsi_display_ctrl_host_disable(display);
if (rc)
pr_err("[%s] failed to power DSI vregs, rc=%d\n",
display->name, rc);
- rc = dsi_panel_post_unprepare(display->panel);
- if (rc)
- pr_err("[%s] panel post-unprepare failed, rc=%d\n",
- display->name, rc);
+ for (i = 0; i < display->panel_count; i++) {
+ rc = dsi_panel_post_unprepare(display->panel[i]);
+ if (rc)
+ pr_err("[%s] panel post-unprepare failed, rc=%d\n",
+ display->name, rc);
+ }
mutex_unlock(&display->display_lock);
return rc;
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-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
* @display_lock: Mutex for dsi_display interface.
* @ctrl_count: Number of DSI interfaces required by panel.
* @ctrl: Controller information for DSI display.
+ * @panel_count: Number of DSI panel.
* @panel: Handle to DSI panel.
- * @panel_of: pHandle to DSI panel.
+ * @panel_of: pHandle to DSI panel, it's an array with panel_count
+ * of struct device_node pointers.
+ * @bridge_idx: Bridge chip index for each panel_of.
* @type: DSI display type.
* @clk_master_idx: The master controller for controlling clocks. This is an
* index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array.
struct dsi_display_ctrl ctrl[MAX_DSI_CTRLS_PER_DISPLAY];
/* panel info */
- struct dsi_panel *panel;
- struct device_node *panel_of;
+ u32 panel_count;
+ struct dsi_panel **panel;
+ struct device_node **panel_of;
+ u32 *bridge_idx;
enum dsi_display_type type;
u32 clk_master_idx;
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
struct drm_display_mode *adjusted_mode)
{
struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
+ struct dsi_panel *panel;
- if (!bridge || !mode || !adjusted_mode) {
+ if (!bridge || !mode || !adjusted_mode || !c_bridge->display ||
+ !c_bridge->display->panel[0]) {
pr_err("Invalid params\n");
return;
}
+ /* dsi drm bridge is always the first panel */
+ panel = c_bridge->display->panel[0];
memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode));
convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode));
pr_debug("note: using panel cmd/vid mode instead of user val\n");
- c_bridge->dsi_mode.panel_mode =
- c_bridge->display->panel->mode.panel_mode;
+ c_bridge->dsi_mode.panel_mode = panel->mode.panel_mode;
}
static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge,
{
struct dsi_display *dsi_display = display;
struct dsi_panel *panel;
+ int i;
if (!info || !dsi_display)
return -EINVAL;
break;
}
- if (!dsi_display->panel) {
- pr_debug("invalid panel data\n");
- goto end;
- }
-
- panel = dsi_display->panel;
- sde_kms_info_add_keystr(info, "panel name", panel->name);
-
- switch (panel->mode.panel_mode) {
- case DSI_OP_VIDEO_MODE:
- sde_kms_info_add_keystr(info, "panel mode", "video");
- break;
- case DSI_OP_CMD_MODE:
- sde_kms_info_add_keystr(info, "panel mode", "command");
- sde_kms_info_add_keyint(info, "mdp_transfer_time_us",
- panel->cmd_config.mdp_transfer_time_us);
- break;
- default:
- pr_debug("invalid panel type:%d\n", panel->mode.panel_mode);
- break;
- }
- sde_kms_info_add_keystr(info, "dfps support",
- panel->dfps_caps.dfps_support ? "true" : "false");
+ for (i = 0; i < dsi_display->panel_count; i++) {
+ if (!dsi_display->panel[i]) {
+ pr_debug("invalid panel data\n");
+ goto end;
+ }
- switch (panel->phy_props.rotation) {
- case DSI_PANEL_ROTATE_NONE:
- sde_kms_info_add_keystr(info, "panel orientation", "none");
- break;
- case DSI_PANEL_ROTATE_H_FLIP:
- sde_kms_info_add_keystr(info, "panel orientation", "horz flip");
- break;
- case DSI_PANEL_ROTATE_V_FLIP:
- sde_kms_info_add_keystr(info, "panel orientation", "vert flip");
- break;
- default:
- pr_debug("invalid panel rotation:%d\n",
+ panel = dsi_display->panel[i];
+ sde_kms_info_add_keystr(info, "panel name", panel->name);
+
+ switch (panel->mode.panel_mode) {
+ case DSI_OP_VIDEO_MODE:
+ sde_kms_info_add_keystr(info, "panel mode", "video");
+ break;
+ case DSI_OP_CMD_MODE:
+ sde_kms_info_add_keystr(info, "panel mode", "command");
+ break;
+ default:
+ pr_debug("invalid panel type:%d\n",
+ panel->mode.panel_mode);
+ break;
+ }
+ sde_kms_info_add_keystr(info, "dfps support",
+ panel->dfps_caps.dfps_support ?
+ "true" : "false");
+
+ switch (panel->phy_props.rotation) {
+ case DSI_PANEL_ROTATE_NONE:
+ sde_kms_info_add_keystr(info, "panel orientation",
+ "none");
+ break;
+ case DSI_PANEL_ROTATE_H_FLIP:
+ sde_kms_info_add_keystr(info, "panel orientation",
+ "horz flip");
+ break;
+ case DSI_PANEL_ROTATE_V_FLIP:
+ sde_kms_info_add_keystr(info, "panel orientation",
+ "vert flip");
+ break;
+ default:
+ pr_debug("invalid panel rotation:%d\n",
panel->phy_props.rotation);
- break;
- }
+ break;
+ }
- switch (panel->bl_config.type) {
- case DSI_BACKLIGHT_PWM:
- sde_kms_info_add_keystr(info, "backlight type", "pwm");
- break;
- case DSI_BACKLIGHT_WLED:
- sde_kms_info_add_keystr(info, "backlight type", "wled");
- break;
- case DSI_BACKLIGHT_DCS:
- sde_kms_info_add_keystr(info, "backlight type", "dcs");
- break;
- default:
- pr_debug("invalid panel backlight type:%d\n",
- panel->bl_config.type);
- break;
+ switch (panel->bl_config.type) {
+ case DSI_BACKLIGHT_PWM:
+ sde_kms_info_add_keystr(info, "backlight type", "pwm");
+ break;
+ case DSI_BACKLIGHT_WLED:
+ sde_kms_info_add_keystr(info, "backlight type", "wled");
+ break;
+ case DSI_BACKLIGHT_DCS:
+ sde_kms_info_add_keystr(info, "backlight type", "dcs");
+ break;
+ default:
+ pr_debug("invalid panel backlight type:%d\n",
+ panel->bl_config.type);
+ break;
+ }
}
end:
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
#include <linux/gpio.h>
#include <linux/of_gpio.h>
+#include "sde_kms.h"
#include "dsi_panel.h"
#include "dsi_ctrl_hw.h"
else
state = panel->pinctrl.suspend;
- rc = pinctrl_select_state(panel->pinctrl.pinctrl, state);
- if (rc)
- pr_err("[%s] failed to set pin state, rc=%d\n", panel->name,
- rc);
+ if (panel->pinctrl.pinctrl && state) {
+ rc = pinctrl_select_state(panel->pinctrl.pinctrl, state);
+ if (rc)
+ pr_err("[%s] failed to set pin state, rc=%d\n",
+ panel->name, rc);
+ }
return rc;
}
case DSI_BACKLIGHT_WLED:
rc = dsi_panel_led_bl_register(panel, bl);
break;
+ case DSI_BACKLIGHT_UNKNOWN:
+ DRM_INFO("backlight type is unknown\n");
+ break;
default:
pr_err("Backlight type(%d) not supported\n", bl->type);
rc = -ENOTSUPP;
host->append_tx_eot = of_property_read_bool(of_node,
"qcom,mdss-dsi-tx-eot-append");
+ host->force_clk_lane_hs = of_property_read_bool(of_node,
+ "qcom,mdss-dsi-force-clock-lane-hs");
return 0;
}
{
int rc = 0;
+ /* Need to set GPIO default value to -1, since 0 is a valid value */
+ panel->reset_config.disp_en_gpio = -1;
panel->reset_config.reset_gpio = of_get_named_gpio(of_node,
"qcom,platform-reset-gpio",
0);
return rc;
}
+static int dsi_panel_parse_dba_config(struct dsi_panel *panel,
+ struct device_node *of_node)
+{
+ int rc = 0, len = 0;
+
+ panel->dba_config.dba_panel = of_property_read_bool(of_node,
+ "qcom,dba-panel");
+
+ if (panel->dba_config.dba_panel) {
+ panel->dba_config.hdmi_mode = of_property_read_bool(of_node,
+ "qcom,hdmi-mode");
+
+ panel->dba_config.bridge_name = of_get_property(of_node,
+ "qcom,bridge-name", &len);
+ if (!panel->dba_config.bridge_name || len <= 0) {
+ SDE_ERROR(
+ "%s:%d Unable to read bridge_name, data=%pK,len=%d\n",
+ __func__, __LINE__, panel->dba_config.bridge_name, len);
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+
+error:
+ return rc;
+}
+
struct dsi_panel *dsi_panel_get(struct device *parent,
struct device_node *of_node)
{
if (rc)
pr_err("failed to parse backlight config, rc=%d\n", rc);
+ rc = dsi_panel_parse_dba_config(panel, of_node);
+ if (rc)
+ pr_err("failed to parse dba config, rc=%d\n", rc);
+
panel->panel_of_node = of_node;
drm_panel_init(&panel->drm_panel);
mutex_init(&panel->panel_lock);
{
u32 i;
+ if (!panel)
+ return;
+
for (i = 0; i < DSI_CMD_SET_MAX; i++)
dsi_panel_destroy_cmd_packets(&panel->cmd_sets[i]);
}
rc = dsi_panel_pinctrl_init(panel);
- if (rc) {
+ if (rc)
pr_err("[%s] failed to init pinctrl, rc=%d\n", panel->name, rc);
- goto error_vreg_put;
- }
rc = dsi_panel_gpio_request(panel);
if (rc) {
(void)dsi_panel_gpio_release(panel);
error_pinctrl_deinit:
(void)dsi_panel_pinctrl_deinit(panel);
-error_vreg_put:
(void)dsi_panel_vreg_put(panel);
exit:
mutex_unlock(&panel->panel_lock);
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
int disp_en_gpio;
};
+/**
+ * struct dsi_panel_dba - DSI DBA panel information
+ * @dba_panel: Indicate if it's DBA panel
+ * @bridge_name: Bridge chip name
+ * @hdmi_mode: If bridge chip is in hdmi mode.
+ */
+struct dsi_panel_dba {
+ bool dba_panel;
+ const char *bridge_name;
+ bool hdmi_mode;
+};
+
struct dsi_panel {
const char *name;
struct device_node *panel_of_node;
struct dsi_panel_reset_config reset_config;
struct dsi_pinctrl_info pinctrl;
+ struct dsi_panel_dba dba_config;
+
bool lp11_init;
};
.read = _sde_hdmi_edid_vendor_name_read,
};
+static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
+{
+ u32 pclk_delta, pclk;
+ u64 pclk_clip = pclk_in;
+
+ /* as per standard, 0.5% of deviation is allowed */
+ pclk = mode->clock * HDMI_KHZ_TO_HZ;
+ pclk_delta = pclk * 5 / 1000;
+
+ if (pclk_in < (pclk - pclk_delta))
+ pclk_clip = pclk - pclk_delta;
+ else if (pclk_in > (pclk + pclk_delta))
+ pclk_clip = pclk + pclk_delta;
+
+ if (pclk_in != pclk_clip)
+ pr_warn("clip pclk from %lld to %lld\n", pclk_in, pclk_clip);
+
+ return pclk_clip;
+}
+
+/**
+ * _sde_hdmi_update_pll_delta() - Update the HDMI pixel clock as per input ppm
+ *
+ * @ppm: ppm is parts per million multiplied by 1000.
+ * return: 0 on success, non-zero in case of failure.
+ *
+ * The input ppm will be clipped if it's more than or less than 5% of the TMDS
+ * clock rate defined by HDMI spec.
+ */
+static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
+{
+ struct hdmi *hdmi = display->ctrl.ctrl;
+ struct drm_display_mode *current_mode = &display->mode;
+ u64 cur_pclk, dst_pclk;
+ u64 clip_pclk;
+ int rc = 0;
+
+ if (!hdmi->power_on || !display->connected) {
+ SDE_ERROR("HDMI display is not ready\n");
+ return -EINVAL;
+ }
+
+ /* get current pclk */
+ cur_pclk = hdmi->pixclock;
+ /* get desired pclk */
+ dst_pclk = cur_pclk * (1000000000 + ppm);
+ do_div(dst_pclk, 1000000000);
+
+ clip_pclk = _sde_hdmi_clip_valid_pclk(current_mode, dst_pclk);
+
+ /* update pclk */
+ if (clip_pclk != cur_pclk) {
+ SDE_DEBUG("PCLK changes from %llu to %llu when delta is %d\n",
+ cur_pclk, clip_pclk, ppm);
+
+ rc = clk_set_rate(hdmi->pwr_clks[0], clip_pclk);
+ if (rc < 0) {
+ SDE_ERROR("PLL update failed, reset clock rate\n");
+ return rc;
+ }
+
+ hdmi->pixclock = clip_pclk;
+ }
+
+ return rc;
+}
+
+static ssize_t _sde_hdmi_debugfs_pll_delta_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char buf[10];
+ int ppm = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ if (kstrtoint(buf, 0, &ppm))
+ return -EFAULT;
+
+ if (ppm)
+ _sde_hdmi_update_pll_delta(display, ppm);
+
+ return count;
+}
+
+static const struct file_operations pll_delta_fops = {
+ .open = simple_open,
+ .write = _sde_hdmi_debugfs_pll_delta_write,
+};
+
static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
{
int rc = 0;
struct dentry *dir, *dump_file, *edid_modes;
struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info;
- struct dentry *edid_vcdb_info, *edid_vendor_name;
+ struct dentry *edid_vcdb_info, *edid_vendor_name, *pll_file;
dir = debugfs_create_dir(display->name, NULL);
if (!dir) {
&dump_info_fops);
if (IS_ERR_OR_NULL(dump_file)) {
rc = PTR_ERR(dump_file);
- SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ SDE_ERROR("[%s]debugfs create dump_info file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ pll_file = debugfs_create_file("pll_delta",
+ 0644,
+ dir,
+ display,
+ &pll_delta_fops);
+ if (IS_ERR_OR_NULL(pll_file)) {
+ rc = PTR_ERR(pll_file);
+ SDE_ERROR("[%s]debugfs create pll_delta file failed, rc=%d\n",
display->name, rc);
goto error_remove_dir;
}
return rc;
}
+int sde_hdmi_set_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ int property_index,
+ uint64_t value,
+ void *display)
+{
+ int rc = 0;
+
+ if (!connector || !display) {
+ SDE_ERROR("connector=%pK or display=%pK is NULL\n",
+ connector, display);
+ return 0;
+ }
+
+ SDE_DEBUG("\n");
+
+ if (property_index == CONNECTOR_PROP_PLL_DELTA)
+ rc = _sde_hdmi_update_pll_delta(display, value);
+
+ return rc;
+}
+
u32 sde_hdmi_get_num_of_displays(void)
{
u32 count = 0;
* @non_pluggable: If HDMI display is non pluggable
* @num_of_modes: Number of modes supported by display if non pluggable.
* @mode_list: Mode list if non pluggable.
+ * @mode: Current display mode.
* @connected: If HDMI display is connected.
* @is_tpg_enabled: TPG state.
* @hpd_work: HPD work structure.
bool non_pluggable;
u32 num_of_modes;
struct list_head mode_list;
+ struct drm_display_mode mode;
bool connected;
bool is_tpg_enabled;
void *display);
/**
+ * sde_hdmi_set_property() - set the connector properties
+ * @connector: Handle to the connector.
+ * @state: Handle to the connector state.
+ * @property_index: property index.
+ * @value: property value.
+ * @display: Handle to the display.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_set_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ int property_index,
+ uint64_t value,
+ void *display);
+
+/**
* sde_hdmi_bridge_init() - init sde hdmi bridge
* @hdmi: Handle to the hdmi.
*
{
return 0;
}
+
+static inline int sde_hdmi_set_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ int property_index,
+ uint64_t value,
+ void *display)
+{
+ return 0;
+}
+
#endif /*#else of CONFIG_DRM_SDE_HDMI*/
#endif /* _SDE_HDMI_H_ */
#define HDMI_AUDIO_INFO_FRAME_PACKET_VERSION 0x1
#define HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH 0x0A
-#define HDMI_KHZ_TO_HZ 1000
-#define HDMI_MHZ_TO_HZ 1000000
#define HDMI_ACR_N_MULTIPLIER 128
#define DEFAULT_AUDIO_SAMPLE_RATE_HZ 48000
hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, packet_control);
}
+static inline void _sde_hdmi_save_mode(struct hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ struct sde_connector *c_conn = to_sde_connector(hdmi->connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+
+ drm_mode_copy(&display->mode, mode);
+}
+
static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
DRM_DEBUG("hdmi setup info frame\n");
}
_sde_hdmi_bridge_setup_scrambler(hdmi, mode);
+
+ _sde_hdmi_save_mode(hdmi, mode);
}
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
mdp4_crtc_cancel_pending_flip(priv->crtcs[i], file);
if (aspace) {
- aspace->mmu->funcs->detach(aspace->mmu,
- iommu_ports, ARRAY_SIZE(iommu_ports));
+ aspace->mmu->funcs->detach(aspace->mmu);
msm_gem_address_space_destroy(aspace);
}
}
drm_gem_object_unreference_unlocked(mdp4_kms->blank_cursor_bo);
if (aspace) {
- aspace->mmu->funcs->detach(aspace->mmu,
- iommu_ports, ARRAY_SIZE(iommu_ports));
+ aspace->mmu->funcs->detach(aspace->mmu);
msm_gem_address_space_put(aspace);
}
return ret;
}
-static const char *iommu_ports[] = {
- "mdp_port0_cb0", "mdp_port1_cb0",
-};
-
struct msm_kms *mdp4_kms_init(struct drm_device *dev)
{
struct platform_device *pdev = dev->platformdev;
mdelay(16);
if (config->iommu) {
- struct msm_mmu *mmu = msm_iommu_new(&pdev->dev, config->iommu);
-
- if (IS_ERR(mmu)) {
- ret = PTR_ERR(mmu);
- goto fail;
- }
+ config->iommu->geometry.aperture_start = 0x1000;
+ config->iommu->geometry.aperture_end = 0xffffffff;
aspace = msm_gem_address_space_create(&pdev->dev,
- mmu, "mdp4", 0x1000, 0xffffffff);
+ config->iommu, MSM_IOMMU_DOMAIN_DEFAULT, "mdp4");
if (IS_ERR(aspace)) {
ret = PTR_ERR(aspace);
goto fail;
mdp4_kms->aspace = aspace;
- ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports,
- ARRAY_SIZE(iommu_ports));
+ ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0);
if (ret)
goto fail;
} else {
#include "msm_mmu.h"
#include "mdp5_kms.h"
-static const char *iommu_ports[] = {
- "mdp_0",
-};
-
static int mdp5_hw_init(struct msm_kms *kms)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
mdp5_kms->aspace = aspace;
- ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports,
- ARRAY_SIZE(iommu_ports));
+ ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "failed to attach iommu: %d\n",
ret);
return 0;
}
+static int msm_ioctl_gem_sync(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+
+ struct drm_msm_gem_sync *arg = data;
+ int i;
+
+ for (i = 0; i < arg->nr_ops; i++) {
+ struct drm_msm_gem_syncop syncop;
+ struct drm_gem_object *obj;
+ int ret;
+ void __user *ptr =
+ (void __user *)(uintptr_t)
+ (arg->ops + (i * sizeof(syncop)));
+
+ ret = copy_from_user(&syncop, ptr, sizeof(syncop));
+ if (ret)
+ return -EFAULT;
+
+ obj = drm_gem_object_lookup(dev, file, syncop.handle);
+ if (!obj)
+ return -ENOENT;
+
+ msm_gem_sync(obj, syncop.op);
+
+ drm_gem_object_unreference_unlocked(obj);
+ }
+
+ return 0;
+}
+
void msm_send_crtc_notification(struct drm_crtc *crtc,
struct drm_event *event, u8 *payload)
{
DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_COUNTER_READ, msm_ioctl_counter_read,
DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_GEM_SYNC, msm_ioctl_gem_sync,
+ DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct vm_operations_struct vm_ops = {
CONNECTOR_PROP_DST_Y,
CONNECTOR_PROP_DST_W,
CONNECTOR_PROP_DST_H,
+ CONNECTOR_PROP_PLL_DELTA,
/* enum/bitmask properties */
CONNECTOR_PROP_TOPOLOGY_NAME,
/* For GPU and legacy display */
struct msm_gem_address_space *
msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
- const char *name);
+ int type, const char *name);
struct msm_gem_address_space *
msm_gem_address_space_create_instance(struct msm_mmu *parent, const char *name,
uint64_t start, uint64_t end);
uint32_t size, uint32_t flags);
struct drm_gem_object *msm_gem_import(struct drm_device *dev,
uint32_t size, struct sg_table *sgt);
+void msm_gem_sync(struct drm_gem_object *obj, u32 op);
int msm_framebuffer_prepare(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace);
return 0;
}
+void msm_gem_sync(struct drm_gem_object *obj, u32 op)
+{
+ struct drm_device *dev = obj->dev;
+ struct msm_gem_object *msm_obj = to_msm_bo(obj);
+
+ if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED))
+ return;
+
+ switch (op) {
+ case MSM_GEM_SYNC_TO_CPU:
+ dma_sync_sg_for_cpu(dev->dev, msm_obj->sgt->sgl,
+ msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ break;
+ case MSM_GEM_SYNC_TO_DEV:
+ dma_sync_sg_for_device(dev->dev, msm_obj->sgt->sgl,
+ msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ break;
+ }
+}
+
#ifdef CONFIG_DEBUG_FS
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
{
/* Additional internal-use only BO flags: */
#define MSM_BO_STOLEN 0x10000000 /* try to use stolen/splash memory */
-struct msm_gem_aspace_ops {
- int (*map)(struct msm_gem_address_space *, struct msm_gem_vma *,
- struct sg_table *sgt, void *priv, unsigned int flags);
-
- void (*unmap)(struct msm_gem_address_space *, struct msm_gem_vma *,
- struct sg_table *sgt, void *priv);
-
- void (*destroy)(struct msm_gem_address_space *);
-};
-
struct msm_gem_address_space {
const char *name;
struct msm_mmu *mmu;
- const struct msm_gem_aspace_ops *ops;
struct kref kref;
+ struct drm_mm mm;
+ u64 va_len;
};
struct msm_gem_vma {
void __user *userptr =
to_user_ptr(args->bos + (i * sizeof(submit_bo)));
- ret = copy_from_user_inatomic(&submit_bo, userptr, sizeof(submit_bo));
- if (unlikely(ret)) {
+ if (copy_from_user_inatomic(&submit_bo, userptr,
+ sizeof(submit_bo))) {
pagefault_enable();
spin_unlock(&file->table_lock);
- ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
- if (ret)
+ if (copy_from_user(&submit_bo, userptr,
+ sizeof(submit_bo))) {
+ ret = -EFAULT;
goto out;
+ }
+
spin_lock(&file->table_lock);
pagefault_disable();
}
uint32_t off;
bool valid;
- ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
- if (ret)
+ if (copy_from_user(&submit_reloc, userptr,
+ sizeof(submit_reloc)))
return -EFAULT;
if (submit_reloc.submit_offset % 4) {
struct msm_gem_address_space *aspace = container_of(kref,
struct msm_gem_address_space, kref);
- if (aspace->ops->destroy)
- aspace->ops->destroy(aspace);
+ if (aspace->va_len)
+ drm_mm_takedown(&aspace->mm);
+
+ aspace->mmu->funcs->destroy(aspace->mmu);
kfree(aspace);
}
kref_put(&aspace->kref, msm_gem_address_space_destroy);
}
-/* SDE address space operations */
-static void smmu_aspace_unmap_vma(struct msm_gem_address_space *aspace,
- struct msm_gem_vma *vma, struct sg_table *sgt,
- void *priv)
-{
- struct dma_buf *buf = priv;
-
- if (buf)
- aspace->mmu->funcs->unmap_dma_buf(aspace->mmu,
- sgt, buf, DMA_BIDIRECTIONAL);
- else
- aspace->mmu->funcs->unmap_sg(aspace->mmu, sgt,
- DMA_BIDIRECTIONAL);
-
- vma->iova = 0;
-
- msm_gem_address_space_put(aspace);
-}
-
-
-static int smmu_aspace_map_vma(struct msm_gem_address_space *aspace,
- struct msm_gem_vma *vma, struct sg_table *sgt,
- void *priv, unsigned int flags)
-{
- struct dma_buf *buf = priv;
- int ret;
-
- if (buf)
- ret = aspace->mmu->funcs->map_dma_buf(aspace->mmu, sgt, buf,
- DMA_BIDIRECTIONAL);
- else
- ret = aspace->mmu->funcs->map_sg(aspace->mmu, sgt,
- DMA_BIDIRECTIONAL);
-
- if (!ret)
- vma->iova = sg_dma_address(sgt->sgl);
-
- /* Get a reference to the aspace to keep it around */
- kref_get(&aspace->kref);
-
- return ret;
-}
-
-static const struct msm_gem_aspace_ops smmu_aspace_ops = {
- .map = smmu_aspace_map_vma,
- .unmap = smmu_aspace_unmap_vma,
-};
-
-struct msm_gem_address_space *
-msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu,
- const char *name)
+static struct msm_gem_address_space *
+msm_gem_address_space_new(struct msm_mmu *mmu, const char *name,
+ uint64_t start, uint64_t end)
{
struct msm_gem_address_space *aspace;
aspace->name = name;
aspace->mmu = mmu;
- aspace->ops = &smmu_aspace_ops;
-
- kref_init(&aspace->kref);
-
- return aspace;
-}
-/* GPU address space operations */
-struct msm_iommu_aspace {
- struct msm_gem_address_space base;
- struct drm_mm mm;
-};
+ aspace->va_len = end - start;
-#define to_iommu_aspace(aspace) \
- ((struct msm_iommu_aspace *) \
- container_of(aspace, struct msm_iommu_aspace, base))
+ if (aspace->va_len)
+ drm_mm_init(&aspace->mm, (start >> PAGE_SHIFT),
+ (end >> PAGE_SHIFT) - 1);
-static void iommu_aspace_unmap_vma(struct msm_gem_address_space *aspace,
- struct msm_gem_vma *vma, struct sg_table *sgt, void *priv)
-{
- if (!vma->iova)
- return;
-
- if (aspace->mmu)
- aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt);
-
- drm_mm_remove_node(&vma->node);
-
- vma->iova = 0;
+ kref_init(&aspace->kref);
- msm_gem_address_space_put(aspace);
+ return aspace;
}
-static int iommu_aspace_map_vma(struct msm_gem_address_space *aspace,
- struct msm_gem_vma *vma, struct sg_table *sgt, void *priv,
- unsigned int flags)
+static int allocate_iova(struct msm_gem_address_space *aspace,
+ struct msm_gem_vma *vma, struct sg_table *sgt,
+ u64 *iova)
{
- struct msm_iommu_aspace *local = to_iommu_aspace(aspace);
- size_t size = 0;
struct scatterlist *sg;
+ size_t size = 0;
int ret, i;
- int iommu_flags = IOMMU_READ;
-
- if (!(flags & MSM_BO_GPU_READONLY))
- iommu_flags |= IOMMU_WRITE;
- if (flags & MSM_BO_PRIVILEGED)
- iommu_flags |= IOMMU_PRIV;
-
- if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(aspace->mmu))
- iommu_flags |= IOMMU_CACHE;
+ if (!aspace->va_len)
+ return 0;
if (WARN_ON(drm_mm_node_allocated(&vma->node)))
return 0;
for_each_sg(sgt->sgl, sg, sgt->nents, i)
size += sg->length + sg->offset;
- ret = drm_mm_insert_node(&local->mm, &vma->node, size >> PAGE_SHIFT,
+ ret = drm_mm_insert_node(&aspace->mm, &vma->node, size >> PAGE_SHIFT,
0, DRM_MM_SEARCH_DEFAULT);
- if (ret)
- return ret;
-
- vma->iova = vma->node.start << PAGE_SHIFT;
-
- if (aspace->mmu)
- ret = aspace->mmu->funcs->map(aspace->mmu, vma->iova, sgt,
- iommu_flags);
- /* Get a reference to the aspace to keep it around */
- kref_get(&aspace->kref);
+ if (!ret && iova)
+ *iova = vma->node.start << PAGE_SHIFT;
return ret;
}
-static void iommu_aspace_destroy(struct msm_gem_address_space *aspace)
+int msm_gem_map_vma(struct msm_gem_address_space *aspace,
+ struct msm_gem_vma *vma, struct sg_table *sgt,
+ void *priv, unsigned int flags)
{
- struct msm_iommu_aspace *local = to_iommu_aspace(aspace);
-
- drm_mm_takedown(&local->mm);
- aspace->mmu->funcs->destroy(aspace->mmu);
-}
-
-static const struct msm_gem_aspace_ops msm_iommu_aspace_ops = {
- .map = iommu_aspace_map_vma,
- .unmap = iommu_aspace_unmap_vma,
- .destroy = iommu_aspace_destroy,
-};
+ u64 iova = 0;
+ int ret;
-static struct msm_gem_address_space *
-msm_gem_address_space_new(struct msm_mmu *mmu, const char *name,
- uint64_t start, uint64_t end)
-{
- struct msm_iommu_aspace *local;
+ if (!aspace)
+ return -EINVAL;
- if (!mmu)
- return ERR_PTR(-EINVAL);
+ ret = allocate_iova(aspace, vma, sgt, &iova);
+ if (ret)
+ return ret;
- local = kzalloc(sizeof(*local), GFP_KERNEL);
- if (!local)
- return ERR_PTR(-ENOMEM);
+ ret = aspace->mmu->funcs->map(aspace->mmu, iova, sgt,
+ flags, priv);
- drm_mm_init(&local->mm, (start >> PAGE_SHIFT),
- (end >> PAGE_SHIFT) - 1);
+ if (ret) {
+ if (drm_mm_node_allocated(&vma->node))
+ drm_mm_remove_node(&vma->node);
- local->base.name = name;
- local->base.mmu = mmu;
- local->base.ops = &msm_iommu_aspace_ops;
+ return ret;
+ }
- kref_init(&local->base.kref);
+ vma->iova = sg_dma_address(sgt->sgl);
+ kref_get(&aspace->kref);
- return &local->base;
+ return 0;
}
-int msm_gem_map_vma(struct msm_gem_address_space *aspace,
- struct msm_gem_vma *vma, struct sg_table *sgt,
- void *priv, unsigned int flags)
+void msm_gem_unmap_vma(struct msm_gem_address_space *aspace,
+ struct msm_gem_vma *vma, struct sg_table *sgt, void *priv)
{
- if (aspace && aspace->ops->map)
- return aspace->ops->map(aspace, vma, sgt, priv, flags);
+ if (!aspace || !vma->iova)
+ return;
- return -EINVAL;
+ aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv);
+
+ if (drm_mm_node_allocated(&vma->node))
+ drm_mm_remove_node(&vma->node);
+
+ vma->iova = 0;
+
+ msm_gem_address_space_put(aspace);
}
-void msm_gem_unmap_vma(struct msm_gem_address_space *aspace,
- struct msm_gem_vma *vma, struct sg_table *sgt, void *priv)
+struct msm_gem_address_space *
+msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu,
+ const char *name)
{
- if (aspace && aspace->ops->unmap)
- aspace->ops->unmap(aspace, vma, sgt, priv);
+ return msm_gem_address_space_new(mmu, name, 0, 0);
}
struct msm_gem_address_space *
msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
- const char *name)
+ int type, const char *name)
{
- struct msm_mmu *mmu = msm_iommu_new(dev, domain);
+ struct msm_mmu *mmu = msm_iommu_new(dev, type, domain);
if (IS_ERR(mmu))
return (struct msm_gem_address_space *) mmu;
if (ret)
return ret;
+ if (gpu->aspace && gpu->aspace->mmu)
+ msm_mmu_enable(gpu->aspace->mmu);
+
return 0;
}
if (WARN_ON(gpu->active_cnt < 0))
return -EINVAL;
+ if (gpu->aspace && gpu->aspace->mmu)
+ msm_mmu_disable(gpu->aspace->mmu);
+
ret = disable_axi(gpu);
if (ret)
return ret;
dev_info(drm->dev, "%s: using IOMMU\n", name);
gpu->aspace = msm_gem_address_space_create(&pdev->dev,
- iommu, "gpu");
+ iommu, MSM_IOMMU_DOMAIN_USER, "gpu");
if (IS_ERR(gpu->aspace)) {
ret = PTR_ERR(gpu->aspace);
dev_err(drm->dev, "failed to init iommu: %d\n", ret);
struct msm_gem_submit;
struct msm_gpu_perfcntr;
+#define MSM_GPU_DEFAULT_IONAME "kgsl_3d0_reg_memory"
+#define MSM_GPU_DEFAULT_IRQNAME "kgsl_3d0_irq"
+
struct msm_gpu_config {
const char *ioname;
const char *irqname;
#include <linux/of_platform.h>
#include <linux/of_address.h>
+#include <soc/qcom/secure_buffer.h>
#include "msm_drv.h"
#include "msm_iommu.h"
return 0;
}
-/*
- * Get and enable the IOMMU clocks so that we can make
- * sure they stay on the entire duration so that we can
- * safely change the pagetable from the GPU
- */
-static void _get_iommu_clocks(struct msm_mmu *mmu, struct platform_device *pdev)
+static void iommu_get_clocks(struct msm_iommu *iommu, struct device *dev)
{
- struct msm_iommu *iommu = to_msm_iommu(mmu);
- struct device *dev;
struct property *prop;
const char *name;
int i = 0;
- if (WARN_ON(!pdev))
- return;
-
- dev = &pdev->dev;
-
iommu->nr_clocks =
of_property_count_strings(dev->of_node, "clock-names");
- if (iommu->nr_clocks < 0) {
- iommu->nr_clocks = 0;
+ if (iommu->nr_clocks < 0)
return;
- }
if (WARN_ON(iommu->nr_clocks > ARRAY_SIZE(iommu->clocks)))
iommu->nr_clocks = ARRAY_SIZE(iommu->clocks);
if (i == iommu->nr_clocks)
break;
- iommu->clocks[i] = clk_get(dev, name);
- if (iommu->clocks[i])
- clk_prepare_enable(iommu->clocks[i]);
-
- i++;
+ iommu->clocks[i++] = clk_get(dev, name);
}
}
-static int _attach_iommu_device(struct msm_mmu *mmu,
- struct iommu_domain *domain, const char **names, int cnt)
+
+static void msm_iommu_clocks_enable(struct msm_mmu *mmu)
{
+ struct msm_iommu *iommu = to_msm_iommu(mmu);
int i;
- /* See if there is a iommus member in the current device. If not, look
- * for the names and see if there is one in there.
- */
+ if (!iommu->nr_clocks)
+ iommu_get_clocks(iommu, mmu->dev->parent);
- if (of_find_property(mmu->dev->of_node, "iommus", NULL))
- return iommu_attach_device(domain, mmu->dev);
-
- /* Look through the list of names for a target */
- for (i = 0; i < cnt; i++) {
- struct device_node *node =
- of_find_node_by_name(mmu->dev->of_node, names[i]);
-
- if (!node)
- continue;
-
- if (of_find_property(node, "iommus", NULL)) {
- struct platform_device *pdev;
-
- /* Get the platform device for the node */
- of_platform_populate(node->parent, NULL, NULL,
- mmu->dev);
-
- pdev = of_find_device_by_node(node);
-
- if (!pdev)
- continue;
-
- _get_iommu_clocks(mmu,
- of_find_device_by_node(node->parent));
+ for (i = 0; i < iommu->nr_clocks; i++) {
+ if (iommu->clocks[i])
+ clk_prepare_enable(iommu->clocks[i]);
+ }
+}
- mmu->dev = &pdev->dev;
+static void msm_iommu_clocks_disable(struct msm_mmu *mmu)
+{
+ struct msm_iommu *iommu = to_msm_iommu(mmu);
+ int i;
- return iommu_attach_device(domain, mmu->dev);
- }
+ for (i = 0; i < iommu->nr_clocks; i++) {
+ if (iommu->clocks[i])
+ clk_disable_unprepare(iommu->clocks[i]);
}
+}
- dev_err(mmu->dev, "Couldn't find a IOMMU device\n");
- return -ENODEV;
+static int msm_iommu_attach(struct msm_mmu *mmu, const char **names,
+ int cnt)
+{
+ struct msm_iommu *iommu = to_msm_iommu(mmu);
+
+ return iommu_attach_device(iommu->domain, mmu->dev);
}
-static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt)
+static int msm_iommu_attach_user(struct msm_mmu *mmu, const char **names,
+ int cnt)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
- int val = 1, ret;
+ int ret, val = 1;
/* Hope springs eternal */
- iommu->allow_dynamic = true;
-
- /* per-instance pagetables need TTBR1 support in the IOMMU driver */
- ret = iommu_domain_set_attr(iommu->domain,
- DOMAIN_ATTR_ENABLE_TTBR1, &val);
- if (ret)
- iommu->allow_dynamic = false;
+ iommu->allow_dynamic = !iommu_domain_set_attr(iommu->domain,
+ DOMAIN_ATTR_ENABLE_TTBR1, &val) ? true : false;
/* Mark the GPU as I/O coherent if it is supported */
iommu->is_coherent = of_dma_is_coherent(mmu->dev->of_node);
- /* Attach the device to the domain */
- ret = _attach_iommu_device(mmu, iommu->domain, names, cnt);
+ ret = iommu_attach_device(iommu->domain, mmu->dev);
if (ret)
return ret;
return 0;
}
+static int msm_iommu_attach_secure(struct msm_mmu *mmu, const char **names,
+ int cnt)
+{
+ struct msm_iommu *iommu = to_msm_iommu(mmu);
+ int ret, vmid = VMID_CP_PIXEL;
+
+ ret = iommu_domain_set_attr(iommu->domain, DOMAIN_ATTR_SECURE_VMID,
+ &vmid);
+ if (ret)
+ return ret;
+
+ return iommu_attach_device(iommu->domain, mmu->dev);
+}
+
static void msm_iommu_detach(struct msm_mmu *mmu)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
- int i;
iommu_detach_device(iommu->domain, mmu->dev);
-
- for (i = 0; i < iommu->nr_clocks; i++) {
- if (iommu->clocks[i])
- clk_disable(iommu->clocks[i]);
- }
}
static void msm_iommu_detach_dynamic(struct msm_mmu *mmu)
}
static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova,
- struct sg_table *sgt, int prot)
+ struct sg_table *sgt, u32 flags, void *priv)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
struct iommu_domain *domain = iommu->domain;
- struct scatterlist *sg;
- uint64_t da = iova;
- unsigned int i, j;
int ret;
+ u32 prot = IOMMU_READ;
if (!domain || !sgt)
return -EINVAL;
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
- phys_addr_t pa = sg_phys(sg) - sg->offset;
- size_t bytes = sg->length + sg->offset;
+ if (!(flags & MSM_BO_GPU_READONLY))
+ prot |= IOMMU_WRITE;
- VERB("map[%d]: %016llx %pa(%zx)", i, iova, &pa, bytes);
+ if (flags & MSM_BO_PRIVILEGED)
+ prot |= IOMMU_PRIV;
- ret = iommu_map(domain, da, pa, bytes, prot);
- if (ret)
- goto fail;
+ if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(mmu))
+ prot |= IOMMU_CACHE;
- da += bytes;
- }
-
- return 0;
-
-fail:
- da = iova;
+ /* iommu_map_sg returns the number of bytes mapped */
+ ret = iommu_map_sg(domain, iova, sgt->sgl, sgt->nents, prot);
+ if (ret)
+ sgt->sgl->dma_address = iova;
- for_each_sg(sgt->sgl, sg, i, j) {
- size_t bytes = sg->length + sg->offset;
- iommu_unmap(domain, da, bytes);
- da += bytes;
- }
- return ret;
+ return ret ? 0 : -ENOMEM;
}
-static int msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova,
- struct sg_table *sgt)
+static void msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova,
+ struct sg_table *sgt, void *priv)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
struct iommu_domain *domain = iommu->domain;
struct scatterlist *sg;
- uint64_t da = iova;
- int i;
+ size_t len = 0;
+ int ret, i;
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
- size_t bytes = sg->length + sg->offset;
- size_t unmapped;
+ for_each_sg(sgt->sgl, sg, sgt->nents, i)
+ len += sg->length;
- unmapped = iommu_unmap(domain, da, bytes);
- if (unmapped < bytes)
- return unmapped;
+ ret = iommu_unmap(domain, iova, len);
+ if (ret != len)
+ dev_warn(mmu->dev, "could not unmap iova %llx\n", iova);
- VERB("unmap[%d]: %016llx(%zx)", i, iova, bytes);
-
- BUG_ON(!PAGE_ALIGNED(bytes));
-
- da += bytes;
- }
-
- return 0;
+ sgt->sgl->dma_address = 0;
}
static void msm_iommu_destroy(struct msm_mmu *mmu)
kfree(iommu);
}
-static const struct msm_mmu_funcs funcs = {
+static struct device *find_context_bank(const char *name)
+{
+ struct device_node *node = of_find_node_by_name(NULL, name);
+ struct platform_device *pdev, *parent;
+
+ if (!node)
+ return ERR_PTR(-ENODEV);
+
+ if (!of_find_property(node, "iommus", NULL))
+ return ERR_PTR(-ENODEV);
+
+ /* Get the parent device */
+ parent = of_find_device_by_node(node->parent);
+
+ /* Populate the sub nodes */
+ of_platform_populate(parent->dev.of_node, NULL, NULL, &parent->dev);
+
+ /* Get the context bank device */
+ pdev = of_find_device_by_node(node);
+
+ return pdev ? &pdev->dev : ERR_PTR(-ENODEV);
+}
+
+static const struct msm_mmu_funcs default_funcs = {
.attach = msm_iommu_attach,
.detach = msm_iommu_detach,
.map = msm_iommu_map,
.destroy = msm_iommu_destroy,
};
+static const struct msm_mmu_funcs user_funcs = {
+ .attach = msm_iommu_attach_user,
+ .detach = msm_iommu_detach,
+ .map = msm_iommu_map,
+ .unmap = msm_iommu_unmap,
+ .destroy = msm_iommu_destroy,
+ .enable = msm_iommu_clocks_enable,
+ .disable = msm_iommu_clocks_disable,
+};
+
+static const struct msm_mmu_funcs secure_funcs = {
+ .attach = msm_iommu_attach_secure,
+ .detach = msm_iommu_detach,
+ .map = msm_iommu_map,
+ .unmap = msm_iommu_unmap,
+ .destroy = msm_iommu_destroy,
+};
+
static const struct msm_mmu_funcs dynamic_funcs = {
.attach = msm_iommu_attach_dynamic,
.detach = msm_iommu_detach_dynamic,
.destroy = msm_iommu_destroy,
};
-struct msm_mmu *_msm_iommu_new(struct device *dev, struct iommu_domain *domain,
- const struct msm_mmu_funcs *funcs)
+static const struct {
+ const char *cbname;
+ const struct msm_mmu_funcs *funcs;
+} msm_iommu_domains[] = {
+ [MSM_IOMMU_DOMAIN_DEFAULT] = {
+ .cbname = NULL,
+ .funcs = &default_funcs,
+ },
+ [MSM_IOMMU_DOMAIN_USER] = {
+ .cbname = "gfx3d_user",
+ .funcs = &user_funcs,
+ },
+ [MSM_IOMMU_DOMAIN_SECURE] = {
+ .cbname = "gfx3d_secure",
+ .funcs = &secure_funcs
+ },
+};
+
+static struct msm_mmu *iommu_create(struct device *dev,
+ struct iommu_domain *domain, const struct msm_mmu_funcs *funcs)
{
struct msm_iommu *iommu;
return &iommu->base;
}
-struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain)
+
+struct msm_mmu *msm_iommu_new(struct device *parent,
+ enum msm_iommu_domain_type type, struct iommu_domain *domain)
{
- return _msm_iommu_new(dev, domain, &funcs);
+ struct device *dev = parent;
+
+ if (type >= ARRAY_SIZE(msm_iommu_domains) ||
+ !msm_iommu_domains[type].funcs)
+ return ERR_PTR(-ENODEV);
+
+ if (msm_iommu_domains[type].cbname) {
+ dev = find_context_bank(msm_iommu_domains[type].cbname);
+ if (IS_ERR(dev))
+ return ERR_CAST(dev);
+ }
+
+ return iommu_create(dev, domain, msm_iommu_domains[type].funcs);
}
/*
if (!domain)
return ERR_PTR(-ENODEV);
- mmu = _msm_iommu_new(base->dev, domain, &dynamic_funcs);
+ mmu = iommu_create(base->dev, domain, &dynamic_funcs);
if (IS_ERR(mmu)) {
if (domain)
MSM_SMMU_DOMAIN_MAX,
};
+enum msm_iommu_domain_type {
+ MSM_IOMMU_DOMAIN_DEFAULT,
+ MSM_IOMMU_DOMAIN_USER,
+ MSM_IOMMU_DOMAIN_SECURE,
+};
+
struct msm_mmu_funcs {
int (*attach)(struct msm_mmu *mmu, const char **names, int cnt);
void (*detach)(struct msm_mmu *mmu);
int (*map)(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt,
- int prot);
- int (*unmap)(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt);
- int (*map_sg)(struct msm_mmu *mmu, struct sg_table *sgt,
- enum dma_data_direction dir);
- void (*unmap_sg)(struct msm_mmu *mmu, struct sg_table *sgt,
- enum dma_data_direction dir);
- int (*map_dma_buf)(struct msm_mmu *mmu, struct sg_table *sgt,
- struct dma_buf *dma_buf, int dir);
- void (*unmap_dma_buf)(struct msm_mmu *mmu, struct sg_table *sgt,
- struct dma_buf *dma_buf, int dir);
+ u32 flags, void *priv);
+ void (*unmap)(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt,
+ void *priv);
void (*destroy)(struct msm_mmu *mmu);
+ void (*enable)(struct msm_mmu *mmu);
+ void (*disable)(struct msm_mmu *mmu);
};
struct msm_mmu {
mmu->funcs = funcs;
}
-struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain);
+/* Create a new SDE mmu device */
struct msm_mmu *msm_smmu_new(struct device *dev,
enum msm_mmu_domain_type domain);
+
+/* Create a new legacy MDP4 or GPU mmu device */
+struct msm_mmu *msm_iommu_new(struct device *parent,
+ enum msm_iommu_domain_type type, struct iommu_domain *domain);
+
+/* Create a new dynamic domain for GPU */
struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *orig);
+static inline void msm_mmu_enable(struct msm_mmu *mmu)
+{
+ if (mmu->funcs->enable)
+ mmu->funcs->enable(mmu);
+}
+
+static inline void msm_mmu_disable(struct msm_mmu *mmu)
+{
+ if (mmu->funcs->disable)
+ mmu->funcs->disable(mmu);
+}
+
#endif /* __MSM_MMU_H__ */
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
}
}
+/**
+ * _msm_property_install_integer - install signed drm range property
+ * @info: Pointer to property info container struct
+ * @name: Property name
+ * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
+ * @min: Min property value
+ * @max: Max property value
+ * @init: Default Property value
+ * @property_idx: Property index
+ * @force_dirty: Whether or not to filter 'dirty' status on unchanged values
+ */
+static void _msm_property_install_signed_integer(struct msm_property_info *info,
+ const char *name, int flags, int64_t min, int64_t max,
+ int64_t init, uint32_t property_idx, bool force_dirty)
+{
+ struct drm_property **prop;
+
+ if (!info)
+ return;
+
+ ++info->install_request;
+
+ if (!name || (property_idx >= info->property_count)) {
+ DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
+ } else {
+ prop = &info->property_array[property_idx];
+ /*
+ * Properties need to be attached to each drm object that
+ * uses them, but only need to be created once
+ */
+ if (*prop == 0) {
+ *prop = drm_property_create_signed_range(info->dev,
+ flags, name, min, max);
+ if (*prop == 0)
+ DRM_ERROR("create %s property failed\n", name);
+ }
+
+ /* save init value for later */
+ info->property_data[property_idx].default_value = I642U64(init);
+ info->property_data[property_idx].force_dirty = force_dirty;
+
+ /* always attach property, if created */
+ if (*prop) {
+ drm_object_attach_property(info->base, *prop, init);
+ ++info->install_count;
+ }
+ }
+}
+
void msm_property_install_range(struct msm_property_info *info,
const char *name, int flags, uint64_t min, uint64_t max,
uint64_t init, uint32_t property_idx)
min, max, init, property_idx, true);
}
+void msm_property_install_signed_range(struct msm_property_info *info,
+ const char *name, int flags, int64_t min, int64_t max,
+ int64_t init, uint32_t property_idx)
+{
+ _msm_property_install_signed_integer(info, name, flags,
+ min, max, init, property_idx, false);
+}
+
+void msm_property_install_volatile_signed_range(struct msm_property_info *info,
+ const char *name, int flags, int64_t min, int64_t max,
+ int64_t init, uint32_t property_idx)
+{
+ _msm_property_install_signed_integer(info, name, flags,
+ min, max, init, property_idx, true);
+}
+
void msm_property_install_rotation(struct msm_property_info *info,
unsigned int supported_rotations, uint32_t property_idx)
{
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
uint32_t property_idx);
/**
+ * msm_property_install_signed_range - install signed drm range property
+ * @info: Pointer to property info container struct
+ * @name: Property name
+ * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
+ * @min: Min property value
+ * @max: Max property value
+ * @init: Default Property value
+ * @property_idx: Property index
+ */
+void msm_property_install_signed_range(struct msm_property_info *info,
+ const char *name,
+ int flags,
+ int64_t min,
+ int64_t max,
+ int64_t init,
+ uint32_t property_idx);
+
+/**
+ * msm_property_install_volatile_signed_range - install signed range property
+ * This function is similar to msm_property_install_range, but assumes
+ * that the property is meant for holding user pointers or descriptors
+ * that may reference volatile data without having an updated value.
+ * @info: Pointer to property info container struct
+ * @name: Property name
+ * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
+ * @min: Min property value
+ * @max: Max property value
+ * @init: Default Property value
+ * @property_idx: Property index
+ */
+void msm_property_install_volatile_signed_range(struct msm_property_info *info,
+ const char *name,
+ int flags,
+ int64_t min,
+ int64_t max,
+ int64_t init,
+ uint32_t property_idx);
+
+/**
* msm_property_install_rotation - install standard drm rotation property
* @info: Pointer to property info container struct
* @supported_rotations: Bitmask of supported rotation values (see
#define to_msm_smmu(x) container_of(x, struct msm_smmu, base)
#define msm_smmu_to_client(smmu) (smmu->client)
+
+static int msm_smmu_fault_handler(struct iommu_domain *iommu,
+ struct device *dev, unsigned long iova, int flags, void *arg)
+{
+
+ dev_info(dev, "%s: iova=0x%08lx, flags=0x%x, iommu=%pK\n", __func__,
+ iova, flags, iommu);
+ return 0;
+}
+
+
static int _msm_smmu_create_mapping(struct msm_smmu_client *client,
const struct msm_smmu_domain *domain);
}
static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova,
- struct sg_table *sgt, int prot)
+ struct sg_table *sgt, u32 flags, void *priv)
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
- struct iommu_domain *domain;
- struct scatterlist *sg;
- uint64_t da = iova;
- unsigned int i, j;
int ret;
- if (!client)
- return -ENODEV;
-
- domain = client->mmu_mapping->domain;
- if (!domain || !sgt)
- return -EINVAL;
-
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
- u32 pa = sg_phys(sg) - sg->offset;
- size_t bytes = sg->length + sg->offset;
-
- VERB("map[%d]: %16llx %08x(%zx)", i, iova, pa, bytes);
-
- ret = iommu_map(domain, da, pa, bytes, prot);
- if (ret)
- goto fail;
-
- da += bytes;
- }
-
- return 0;
-
-fail:
- da = iova;
+ if (priv)
+ ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL, priv);
+ else
+ ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL);
- for_each_sg(sgt->sgl, sg, i, j) {
- size_t bytes = sg->length + sg->offset;
-
- iommu_unmap(domain, da, bytes);
- da += bytes;
- }
- return ret;
+ return (ret != sgt->nents) ? -ENOMEM : 0;
}
-static int msm_smmu_map_sg(struct msm_mmu *mmu, struct sg_table *sgt,
- enum dma_data_direction dir)
+static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
+ struct sg_table *sgt, void *priv)
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
- int ret;
- ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents, dir);
- if (ret != sgt->nents)
- return -ENOMEM;
-
- return 0;
-}
-
-static void msm_smmu_unmap_sg(struct msm_mmu *mmu, struct sg_table *sgt,
- enum dma_data_direction dir)
-{
- struct msm_smmu *smmu = to_msm_smmu(mmu);
- struct msm_smmu_client *client = msm_smmu_to_client(smmu);
-
- dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, dir);
-}
-
-static int msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
- struct sg_table *sgt)
-{
- struct msm_smmu *smmu = to_msm_smmu(mmu);
- struct msm_smmu_client *client = msm_smmu_to_client(smmu);
- struct iommu_domain *domain;
- struct scatterlist *sg;
- uint64_t da = iova;
- int i;
-
- if (!client)
- return -ENODEV;
-
- domain = client->mmu_mapping->domain;
- if (!domain || !sgt)
- return -EINVAL;
-
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
- size_t bytes = sg->length + sg->offset;
- size_t unmapped;
-
- unmapped = iommu_unmap(domain, da, bytes);
- if (unmapped < bytes)
- return unmapped;
-
- VERB("unmap[%d]: %16llx(%zx)", i, iova, bytes);
-
- WARN_ON(!PAGE_ALIGNED(bytes));
-
- da += bytes;
- }
-
- return 0;
+ if (priv)
+ msm_dma_unmap_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL, priv);
+ else
+ dma_unmap_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL);
}
static void msm_smmu_destroy(struct msm_mmu *mmu)
kfree(smmu);
}
-static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt,
- struct dma_buf *dma_buf, int dir)
-{
- struct msm_smmu *smmu = to_msm_smmu(mmu);
- struct msm_smmu_client *client = msm_smmu_to_client(smmu);
- int ret;
-
- ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents, dir,
- dma_buf);
- if (ret != sgt->nents) {
- DRM_ERROR("dma map sg failed\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-
-static void msm_smmu_unmap_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt,
- struct dma_buf *dma_buf, int dir)
-{
- struct msm_smmu *smmu = to_msm_smmu(mmu);
- struct msm_smmu_client *client = msm_smmu_to_client(smmu);
-
- msm_dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, dir, dma_buf);
-}
-
static const struct msm_mmu_funcs funcs = {
.attach = msm_smmu_attach,
.detach = msm_smmu_detach,
.map = msm_smmu_map,
- .map_sg = msm_smmu_map_sg,
- .unmap_sg = msm_smmu_unmap_sg,
.unmap = msm_smmu_unmap,
- .map_dma_buf = msm_smmu_map_dma_buf,
- .unmap_dma_buf = msm_smmu_unmap_dma_buf,
.destroy = msm_smmu_destroy,
};
{
struct msm_smmu *smmu;
struct device *client_dev;
+ struct msm_smmu_client *client;
smmu = kzalloc(sizeof(*smmu), GFP_KERNEL);
if (!smmu)
smmu->client_dev = client_dev;
msm_mmu_init(&smmu->base, dev, &funcs);
+ client = msm_smmu_to_client(smmu);
+ if (client)
+ iommu_set_fault_handler(client->mmu_mapping->domain,
+ msm_smmu_fault_handler, dev);
+
return &smmu->base;
}
*/
#define SNAPSHOT_HEADER(_snapshot, _header, _id, _dwords) \
_snapshot_header((_snapshot), \
- (struct msm_snapshot_section_header *) &(header), \
- sizeof(header), (_dwords) << 2, (_id))
+ (struct msm_snapshot_section_header *) &(_header), \
+ sizeof(_header), (_dwords) << 2, (_id))
struct msm_gpu;
__u32 size;
} __packed;
+#define SNAPSHOT_GPU_OBJECT_SHADER 1
+#define SNAPSHOT_GPU_OBJECT_IB 2
+#define SNAPSHOT_GPU_OBJECT_GENERIC 3
+#define SNAPSHOT_GPU_OBJECT_DRAW 4
+#define SNAPSHOT_GPU_OBJECT_GLOBAL 5
+
+struct msm_snapshot_gpu_object {
+ struct msm_snapshot_section_header header;
+ __u32 type;
+ __u64 gpuaddr;
+ __u64 pt_base;
+ __u64 size;
+} __packed;
#endif
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
connector = bl_get_data(bd);
c_conn = to_sde_connector(connector);
display = (struct dsi_display *) c_conn->display;
- if (brightness > display->panel->bl_config.bl_max_level)
- brightness = display->panel->bl_config.bl_max_level;
+ if (brightness > display->panel[0]->bl_config.bl_max_level)
+ brightness = display->panel[0]->bl_config.bl_max_level;
/* This maps UI brightness into driver backlight level with
* rounding
*/
SDE_BRIGHT_TO_BL(bl_lvl, brightness,
- display->panel->bl_config.bl_max_level,
- display->panel->bl_config.brightness_max_level);
+ display->panel[0]->bl_config.bl_max_level,
+ display->panel[0]->bl_config.brightness_max_level);
if (!bl_lvl && brightness)
bl_lvl = 1;
switch (c_conn->connector_type) {
case DRM_MODE_CONNECTOR_DSI:
display = (struct dsi_display *) c_conn->display;
- bl_config = &display->panel->bl_config;
+ bl_config = &display->panel[0]->bl_config;
props.max_brightness = bl_config->brightness_max_level;
props.brightness = bl_config->brightness_max_level;
bd = backlight_device_register("sde-backlight",
goto error_unregister_conn;
}
- if (c_conn->ops.set_backlight) {
- rc = sde_backlight_setup(&c_conn->base);
- if (rc) {
- pr_err("failed to setup backlight, rc=%d\n", rc);
- goto error_unregister_conn;
- }
- }
-
/* create properties */
msm_property_init(&c_conn->property_info, &c_conn->base.base, dev,
priv->conn_property, c_conn->property_data,
msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE);
+ msm_property_install_volatile_signed_range(&c_conn->property_info,
+ "PLL_DELTA", 0x0, INT_MIN, INT_MAX, 0,
+ CONNECTOR_PROP_PLL_DELTA);
+
/* enum/bitmask properties */
msm_property_install_enum(&c_conn->property_info, "topology_name",
DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name,
{
unsigned int i;
- /* Can now check the pitches given vs pitches expected */
+ /* Update layout pitches from fb */
for (i = 0; i < layout->num_planes; ++i) {
if (layout->plane_pitch[i] != fb->pitches[i]) {
- DRM_ERROR("plane %u expected pitch %u, fb %u\n",
+ SDE_DEBUG("plane %u expected pitch %u, fb %u\n",
i, layout->plane_pitch[i], fb->pitches[i]);
- return -EINVAL;
+ layout->plane_pitch[i] = fb->pitches[i];
}
}
#define CREATE_TRACE_POINTS
#include "sde_trace.h"
-static const char * const iommu_ports[] = {
- "mdp_0",
-};
-
/**
* Controls size of event log buffer. Specified as a power of 2.
*/
return;
}
+ ret = drm_crtc_vblank_get(crtc);
+ if (ret)
+ return;
+
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc)
continue;
break;
}
}
+
+ drm_crtc_vblank_put(crtc);
}
static void sde_kms_prepare_fence(struct msm_kms *kms,
.get_modes = sde_hdmi_connector_get_modes,
.mode_valid = sde_hdmi_mode_valid,
.get_info = sde_hdmi_get_info,
+ .set_property = sde_hdmi_set_property,
};
struct msm_display_info info = {0};
struct drm_encoder *encoder;
sde_kms->aspace[i] = aspace;
- ret = mmu->funcs->attach(mmu, (const char **)iommu_ports,
- ARRAY_SIZE(iommu_ports));
+ ret = mmu->funcs->attach(mmu, NULL, 0);
if (ret) {
SDE_ERROR("failed to attach iommu %d: %d\n", i, ret);
msm_gem_address_space_put(aspace);
.i2c = nv04_i2c_new,
.imem = nv40_instmem_new,
.mc = nv44_mc_new,
- .mmu = nv44_mmu_new,
+ .mmu = nv04_mmu_new,
.pci = nv40_pci_new,
.therm = nv40_therm_new,
.timer = nv41_timer_new,
}
if (type == 0x00000010) {
- if (!nv31_mpeg_mthd(mpeg, mthd, data))
+ if (nv31_mpeg_mthd(mpeg, mthd, data))
show &= ~0x01000000;
}
}
}
if (type == 0x00000010) {
- if (!nv44_mpeg_mthd(subdev->device, mthd, data))
+ if (nv44_mpeg_mthd(subdev->device, mthd, data))
show &= ~0x01000000;
}
}
rbo->placement.num_busy_placement = 0;
for (i = 0; i < rbo->placement.num_placement; i++) {
if (rbo->placements[i].flags & TTM_PL_FLAG_VRAM) {
- if (rbo->placements[0].fpfn < fpfn)
- rbo->placements[0].fpfn = fpfn;
+ if (rbo->placements[i].fpfn < fpfn)
+ rbo->placements[i].fpfn = fpfn;
} else {
rbo->placement.busy_placement =
&rbo->placements[i];
if (unlikely(ret != 0))
goto out_err0;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
if (unlikely(ret != 0))
goto out_err1;
int ttm_ref_object_add(struct ttm_object_file *tfile,
struct ttm_base_object *base,
- enum ttm_ref_type ref_type, bool *existed)
+ enum ttm_ref_type ref_type, bool *existed,
+ bool require_existed)
{
struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
struct ttm_ref_object *ref;
}
rcu_read_unlock();
+ if (require_existed)
+ return -EPERM;
+
ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
false, false);
if (unlikely(ret != 0))
prime = (struct ttm_prime_object *) dma_buf->priv;
base = &prime->base;
*handle = base->hash.key;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
dma_buf_put(dma_buf);
struct vmw_fence_obj **p_fence)
{
struct vmw_fence_obj *fence;
- int ret;
+ int ret;
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
if (unlikely(fence == NULL))
}
+/**
+ * vmw_fence_obj_lookup - Look up a user-space fence object
+ *
+ * @tfile: A struct ttm_object_file identifying the caller.
+ * @handle: A handle identifying the fence object.
+ * @return: A struct vmw_user_fence base ttm object on success or
+ * an error pointer on failure.
+ *
+ * The fence object is looked up and type-checked. The caller needs
+ * to have opened the fence object first, but since that happens on
+ * creation and fence objects aren't shareable, that's not an
+ * issue currently.
+ */
+static struct ttm_base_object *
+vmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle)
+{
+ struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle);
+
+ if (!base) {
+ pr_err("Invalid fence object handle 0x%08lx.\n",
+ (unsigned long)handle);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (base->refcount_release != vmw_user_fence_base_release) {
+ pr_err("Invalid fence object handle 0x%08lx.\n",
+ (unsigned long)handle);
+ ttm_base_object_unref(&base);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return base;
+}
+
+
int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
arg->kernel_cookie = jiffies + wait_timeout;
}
- base = ttm_base_object_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- printk(KERN_ERR "Wait invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ base = vmw_fence_obj_lookup(tfile, arg->handle);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
fence = &(container_of(base, struct vmw_user_fence, base)->fence);
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_private *dev_priv = vmw_priv(dev);
- base = ttm_base_object_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- printk(KERN_ERR "Fence signaled invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ base = vmw_fence_obj_lookup(tfile, arg->handle);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
fence = &(container_of(base, struct vmw_user_fence, base)->fence);
fman = fman_from_fence(fence);
(struct drm_vmw_fence_event_arg *) data;
struct vmw_fence_obj *fence = NULL;
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+ struct ttm_object_file *tfile = vmw_fp->tfile;
struct drm_vmw_fence_rep __user *user_fence_rep =
(struct drm_vmw_fence_rep __user *)(unsigned long)
arg->fence_rep;
*/
if (arg->handle) {
struct ttm_base_object *base =
- ttm_base_object_lookup_for_ref(dev_priv->tdev,
- arg->handle);
-
- if (unlikely(base == NULL)) {
- DRM_ERROR("Fence event invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ vmw_fence_obj_lookup(tfile, arg->handle);
+
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
fence = &(container_of(base, struct vmw_user_fence,
base)->fence);
(void) vmw_fence_obj_reference(fence);
if (user_fence_rep != NULL) {
- bool existed;
-
ret = ttm_ref_object_add(vmw_fp->tfile, base,
- TTM_REF_USAGE, &existed);
+ TTM_REF_USAGE, NULL, false);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed to reference a fence "
"object.\n");
return 0;
out_no_create:
if (user_fence_rep != NULL)
- ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
- handle, TTM_REF_USAGE);
+ ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE);
out_no_ref_obj:
vmw_fence_obj_unreference(&fence);
return ret;
param->value = dev_priv->has_dx;
break;
default:
- DRM_ERROR("Illegal vmwgfx get param request: %d\n",
- param->param);
return -EINVAL;
}
bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
- if (unlikely(arg->pad64 != 0)) {
+ if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) {
DRM_ERROR("Illegal GET_3D_CAP argument.\n");
return -EINVAL;
}
return ret;
ret = ttm_ref_object_add(tfile, &user_bo->prime.base,
- TTM_REF_SYNCCPU_WRITE, &existed);
+ TTM_REF_SYNCCPU_WRITE, &existed, false);
if (ret != 0 || existed)
ttm_bo_synccpu_write_release(&user_bo->dma.base);
*handle = user_bo->prime.base.hash.key;
return ttm_ref_object_add(tfile, &user_bo->prime.base,
- TTM_REF_USAGE, NULL);
+ TTM_REF_USAGE, NULL, false);
}
/*
128;
num_sizes = 0;
- for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
+ if (req->mip_levels[i] > DRM_VMW_MAX_MIP_LEVELS)
+ return -EINVAL;
num_sizes += req->mip_levels[i];
+ }
- if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
- DRM_VMW_MAX_MIP_LEVELS)
+ if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS ||
+ num_sizes == 0)
return -EINVAL;
size = vmw_user_surface_size + 128 +
uint32_t handle;
struct ttm_base_object *base;
int ret;
+ bool require_exist = false;
if (handle_type == DRM_VMW_HANDLE_PRIME) {
ret = ttm_prime_fd_to_handle(tfile, u_handle, &handle);
if (unlikely(ret != 0))
return ret;
} else {
- if (unlikely(drm_is_render_client(file_priv))) {
- DRM_ERROR("Render client refused legacy "
- "surface reference.\n");
- return -EACCES;
- }
+ if (unlikely(drm_is_render_client(file_priv)))
+ require_exist = true;
+
if (ACCESS_ONCE(vmw_fpriv(file_priv)->locked_master)) {
DRM_ERROR("Locked master refused legacy "
"surface reference.\n");
/*
* Make sure the surface creator has the same
- * authenticating master.
+ * authenticating master, or is already registered with us.
*/
if (drm_is_primary_client(file_priv) &&
- user_srf->master != file_priv->master) {
- DRM_ERROR("Trying to reference surface outside of"
- " master domain.\n");
- ret = -EACCES;
- goto out_bad_resource;
- }
+ user_srf->master != file_priv->master)
+ require_exist = true;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL,
+ require_exist);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not add a reference to a surface.\n");
goto out_bad_resource;
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-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
}
#endif
-int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
- uint64_t size, unsigned int op)
+static int kgsl_do_cache_op(struct page *page, void *addr,
+ uint64_t offset, uint64_t size, unsigned int op)
{
+ void (*cache_op)(const void *, const void *);
+
/*
- * If the buffer is mapped in the kernel operate on that address
- * otherwise use the user address
+ * The dmac_xxx_range functions handle addresses and sizes that
+ * are not aligned to the cacheline size correctly.
*/
+ switch (_fixup_cache_range_op(op)) {
+ case KGSL_CACHE_OP_FLUSH:
+ cache_op = dmac_flush_range;
+ break;
+ case KGSL_CACHE_OP_CLEAN:
+ cache_op = dmac_clean_range;
+ break;
+ case KGSL_CACHE_OP_INV:
+ cache_op = dmac_inv_range;
+ break;
+ default:
+ return -EINVAL;
+ }
- void *addr = (memdesc->hostptr) ?
- memdesc->hostptr : (void *) memdesc->useraddr;
+ if (page != NULL) {
+ unsigned long pfn = page_to_pfn(page) + offset / PAGE_SIZE;
+ /*
+ * page_address() returns the kernel virtual address of page.
+ * For high memory kernel virtual address exists only if page
+ * has been mapped. So use a version of kmap rather than
+ * page_address() for high memory.
+ */
+ if (PageHighMem(page)) {
+ offset &= ~PAGE_MASK;
+
+ do {
+ unsigned int len = size;
+
+ if (len + offset > PAGE_SIZE)
+ len = PAGE_SIZE - offset;
+
+ page = pfn_to_page(pfn++);
+ addr = kmap_atomic(page);
+ cache_op(addr + offset, addr + offset + len);
+ kunmap_atomic(addr);
+
+ size -= len;
+ offset = 0;
+ } while (size);
+
+ return 0;
+ }
+
+ addr = page_address(page);
+ }
+
+ cache_op(addr + offset, addr + offset + (size_t) size);
+ return 0;
+}
+
+int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
+ uint64_t size, unsigned int op)
+{
+ void *addr = NULL;
+ int ret = 0;
if (size == 0 || size > UINT_MAX)
return -EINVAL;
if ((offset + size < offset) || (offset + size < size))
return -ERANGE;
- /* Make sure the offset + size do not overflow the address */
- if (addr + ((size_t) offset + (size_t) size) < addr)
- return -ERANGE;
-
/* Check that offset+length does not exceed memdesc->size */
if (offset + size > memdesc->size)
return -ERANGE;
- /* Return quietly if the buffer isn't mapped on the CPU */
- if (addr == NULL)
- return 0;
+ if (memdesc->hostptr) {
+ addr = memdesc->hostptr;
+ /* Make sure the offset + size do not overflow the address */
+ if (addr + ((size_t) offset + (size_t) size) < addr)
+ return -ERANGE;
- addr = addr + offset;
+ ret = kgsl_do_cache_op(NULL, addr, offset, size, op);
+ return ret;
+ }
/*
- * The dmac_xxx_range functions handle addresses and sizes that
- * are not aligned to the cacheline size correctly.
+ * If the buffer is not to mapped to kernel, perform cache
+ * operations after mapping to kernel.
*/
+ if (memdesc->sgt != NULL) {
+ struct scatterlist *sg;
+ unsigned int i, pos = 0;
- switch (_fixup_cache_range_op(op)) {
- case KGSL_CACHE_OP_FLUSH:
- dmac_flush_range(addr, addr + (size_t) size);
- break;
- case KGSL_CACHE_OP_CLEAN:
- dmac_clean_range(addr, addr + (size_t) size);
- break;
- case KGSL_CACHE_OP_INV:
- dmac_inv_range(addr, addr + (size_t) size);
- break;
- }
+ for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) {
+ uint64_t sg_offset, sg_left;
- return 0;
+ if (offset >= (pos + sg->length)) {
+ pos += sg->length;
+ continue;
+ }
+ sg_offset = offset > pos ? offset - pos : 0;
+ sg_left = (sg->length - sg_offset > size) ? size :
+ sg->length - sg_offset;
+ ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset,
+ sg_left, op);
+ size -= sg_left;
+ if (size == 0)
+ break;
+ pos += sg->length;
+ }
+ } else if (memdesc->pages != NULL) {
+ addr = vmap(memdesc->pages, memdesc->page_count,
+ VM_IOREMAP, pgprot_writecombine(PAGE_KERNEL));
+ if (addr == NULL)
+ return -ENOMEM;
+
+ /* Make sure the offset + size do not overflow the address */
+ if (addr + ((size_t) offset + (size_t) size) < addr)
+ return -ERANGE;
+
+ ret = kgsl_do_cache_op(NULL, addr, offset, size, op);
+ vunmap(addr);
+ }
+ return ret;
}
EXPORT_SYMBOL(kgsl_cache_range_op);
void *in, *out;
unsigned long flags;
int ret, err = 0;
- unsigned long t;
struct page *page;
spin_lock_irqsave(&newchannel->lock, flags);
goto error1;
}
- t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ);
- if (t == 0) {
- err = -ETIMEDOUT;
- goto error1;
- }
+ wait_for_completion(&open_info->waitevent);
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&open_info->msglistentry);
struct vmbus_channel_gpadl_header *gpadlmsg;
struct vmbus_channel_gpadl_body *gpadl_body;
struct vmbus_channel_msginfo *msginfo = NULL;
- struct vmbus_channel_msginfo *submsginfo;
+ struct vmbus_channel_msginfo *submsginfo, *tmp;
u32 msgcount;
struct list_head *curr;
u32 next_gpadl_handle;
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
+ if (msgcount > 1) {
+ list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist,
+ msglistentry) {
+ kfree(submsginfo);
+ }
+ }
+
kfree(msginfo);
return ret;
}
union hv_connection_id conn_id;
int ret = 0;
int retries = 0;
- u32 msec = 1;
+ u32 usec = 1;
conn_id.asu32 = 0;
conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
}
retries++;
- msleep(msec);
- if (msec < 2048)
- msec *= 2;
+ udelay(usec);
+ if (usec < 2048)
+ usec *= 2;
}
return ret;
}
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64);
- if (!crash)
+ if (!crash) {
vfree(hv_context.tsc_page);
- hv_context.tsc_page = NULL;
+ hv_context.tsc_page = NULL;
+ }
}
#endif
}
* currently hot added. We hot add in multiples of 128M
* chunks; it is possible that we may not be able to bring
* online all the pages in the region. The range
- * covered_end_pfn defines the pages that can
+ * covered_start_pfn:covered_end_pfn defines the pages that can
* be brough online.
*/
struct hv_hotadd_state {
struct list_head list;
unsigned long start_pfn;
+ unsigned long covered_start_pfn;
unsigned long covered_end_pfn;
unsigned long ha_end_pfn;
unsigned long end_pfn;
+ /*
+ * A list of gaps.
+ */
+ struct list_head gap_list;
+};
+
+struct hv_hotadd_gap {
+ struct list_head list;
+ unsigned long start_pfn;
+ unsigned long end_pfn;
};
struct balloon_state {
.priority = 0
};
+/* Check if the particular page is backed and can be onlined and online it. */
+static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
+{
+ unsigned long cur_start_pgp;
+ unsigned long cur_end_pgp;
+ struct hv_hotadd_gap *gap;
+
+ cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
+ cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
+
+ /* The page is not backed. */
+ if (((unsigned long)pg < cur_start_pgp) ||
+ ((unsigned long)pg >= cur_end_pgp))
+ return;
+
+ /* Check for gaps. */
+ list_for_each_entry(gap, &has->gap_list, list) {
+ cur_start_pgp = (unsigned long)
+ pfn_to_page(gap->start_pfn);
+ cur_end_pgp = (unsigned long)
+ pfn_to_page(gap->end_pfn);
+ if (((unsigned long)pg >= cur_start_pgp) &&
+ ((unsigned long)pg < cur_end_pgp)) {
+ return;
+ }
+ }
-static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
+ /* This frame is currently backed; online the page. */
+ __online_page_set_limits(pg);
+ __online_page_increment_counters(pg);
+ __online_page_free(pg);
+}
+
+static void hv_bring_pgs_online(struct hv_hotadd_state *has,
+ unsigned long start_pfn, unsigned long size)
{
int i;
- for (i = 0; i < size; i++) {
- struct page *pg;
- pg = pfn_to_page(start_pfn + i);
- __online_page_set_limits(pg);
- __online_page_increment_counters(pg);
- __online_page_free(pg);
- }
+ for (i = 0; i < size; i++)
+ hv_page_online_one(has, pfn_to_page(start_pfn + i));
}
static void hv_mem_hot_add(unsigned long start, unsigned long size,
list_for_each(cur, &dm_device.ha_region_list) {
has = list_entry(cur, struct hv_hotadd_state, list);
- cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn);
- cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
+ cur_start_pgp = (unsigned long)
+ pfn_to_page(has->start_pfn);
+ cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
- if (((unsigned long)pg >= cur_start_pgp) &&
- ((unsigned long)pg < cur_end_pgp)) {
- /*
- * This frame is currently backed; online the
- * page.
- */
- __online_page_set_limits(pg);
- __online_page_increment_counters(pg);
- __online_page_free(pg);
- }
+ /* The page belongs to a different HAS. */
+ if (((unsigned long)pg < cur_start_pgp) ||
+ ((unsigned long)pg >= cur_end_pgp))
+ continue;
+
+ hv_page_online_one(has, pg);
+ break;
}
}
-static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
+static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
{
struct list_head *cur;
struct hv_hotadd_state *has;
+ struct hv_hotadd_gap *gap;
unsigned long residual, new_inc;
if (list_empty(&dm_device.ha_region_list))
*/
if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
+
+ /*
+ * If the current start pfn is not where the covered_end
+ * is, create a gap and update covered_end_pfn.
+ */
+ if (has->covered_end_pfn != start_pfn) {
+ gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC);
+ if (!gap)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&gap->list);
+ gap->start_pfn = has->covered_end_pfn;
+ gap->end_pfn = start_pfn;
+ list_add_tail(&gap->list, &has->gap_list);
+
+ has->covered_end_pfn = start_pfn;
+ }
+
/*
* If the current hot add-request extends beyond
* our current limit; extend it.
has->end_pfn += new_inc;
}
- /*
- * If the current start pfn is not where the covered_end
- * is, update it.
- */
-
- if (has->covered_end_pfn != start_pfn)
- has->covered_end_pfn = start_pfn;
-
- return true;
-
+ return 1;
}
- return false;
+ return 0;
}
static unsigned long handle_pg_range(unsigned long pg_start,
if (pgs_ol > pfn_cnt)
pgs_ol = pfn_cnt;
+ has->covered_end_pfn += pgs_ol;
+ pfn_cnt -= pgs_ol;
/*
* Check if the corresponding memory block is already
* online by checking its last previously backed page.
*/
if (start_pfn > has->start_pfn &&
!PageReserved(pfn_to_page(start_pfn - 1)))
- hv_bring_pgs_online(start_pfn, pgs_ol);
+ hv_bring_pgs_online(has, start_pfn, pgs_ol);
- has->covered_end_pfn += pgs_ol;
- pfn_cnt -= pgs_ol;
}
if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) {
unsigned long rg_size)
{
struct hv_hotadd_state *ha_region = NULL;
+ int covered;
if (pfn_cnt == 0)
return 0;
- if (!dm_device.host_specified_ha_region)
- if (pfn_covered(pg_start, pfn_cnt))
+ if (!dm_device.host_specified_ha_region) {
+ covered = pfn_covered(pg_start, pfn_cnt);
+ if (covered < 0)
+ return 0;
+
+ if (covered)
goto do_pg_range;
+ }
/*
* If the host has specified a hot-add range; deal with it first.
return 0;
INIT_LIST_HEAD(&ha_region->list);
+ INIT_LIST_HEAD(&ha_region->gap_list);
list_add_tail(&ha_region->list, &dm_device.ha_region_list);
ha_region->start_pfn = rg_start;
ha_region->ha_end_pfn = rg_start;
+ ha_region->covered_start_pfn = pg_start;
ha_region->covered_end_pfn = pg_start;
ha_region->end_pfn = rg_start + rg_size;
}
struct hv_dynmem_device *dm = hv_get_drvdata(dev);
struct list_head *cur, *tmp;
struct hv_hotadd_state *has;
+ struct hv_hotadd_gap *gap, *tmp_gap;
if (dm->num_pages_ballooned != 0)
pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned);
#endif
list_for_each_safe(cur, tmp, &dm->ha_region_list) {
has = list_entry(cur, struct hv_hotadd_state, list);
+ list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) {
+ list_del(&gap->list);
+ kfree(gap);
+ }
list_del(&has->list);
kfree(has);
}
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regmap.h>
+#include <linux/delay.h>
#include "bmg160.h"
#define BMG160_IRQ_NAME "bmg160_event"
#define BMG160_NO_FILTER 0
#define BMG160_DEF_BW 100
+#define BMG160_GYRO_REG_RESET 0x14
+#define BMG160_GYRO_RESET_VAL 0xb6
+
#define BMG160_REG_INT_MAP_0 0x17
#define BMG160_INT_MAP_0_BIT_ANY BIT(1)
int ret;
unsigned int val;
+ /*
+ * Reset chip to get it in a known good state. A delay of 30ms after
+ * reset is required according to the datasheet.
+ */
+ regmap_write(data->regmap, BMG160_GYRO_REG_RESET,
+ BMG160_GYRO_RESET_VAL);
+ usleep_range(30000, 30700);
+
ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val);
if (ret < 0) {
dev_err(data->dev, "Error reading reg_chip_id\n");
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+ { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
{ 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 },
XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */
XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */
+ XPAD_XBOXONE_VENDOR(0x1532), /* Razer Wildcat */
XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */
{ }
u32 ts_pinctrl_seq_delay;
u32 ddic_pinctrl_seq_delay[HBTP_PINCTRL_DDIC_SEQ_NUM];
u32 fb_resume_seq_delay;
- bool lcd_on;
+ int lcd_state;
bool power_suspended;
bool power_sync_enabled;
bool power_sig_enabled;
unsigned long event, void *data)
{
int blank;
+ int lcd_state;
struct fb_event *evdata = data;
struct fb_info *fbi = NULL;
struct hbtp_data *hbtp_data =
(event == FB_EARLY_EVENT_BLANK ||
event == FB_R_EARLY_EVENT_BLANK)) {
blank = *(int *)(evdata->data);
+ lcd_state = hbtp->lcd_state;
if (event == FB_EARLY_EVENT_BLANK) {
- if (blank == FB_BLANK_UNBLANK) {
+ if (blank <= FB_BLANK_NORMAL &&
+ lcd_state == FB_BLANK_POWERDOWN) {
pr_debug("%s: receives EARLY_BLANK:UNBLANK\n",
__func__);
- hbtp_data->lcd_on = true;
hbtp_fb_early_resume(hbtp_data);
- } else if (blank == FB_BLANK_POWERDOWN) {
+ } else if (blank == FB_BLANK_POWERDOWN &&
+ lcd_state <= FB_BLANK_NORMAL) {
pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n",
__func__);
- hbtp_data->lcd_on = false;
+ } else {
+ pr_debug("%s: receives EARLY_BLANK:%d in %d state\n",
+ __func__, blank, lcd_state);
}
} else if (event == FB_R_EARLY_EVENT_BLANK) {
- if (blank == FB_BLANK_UNBLANK) {
+ if (blank <= FB_BLANK_NORMAL) {
pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n",
__func__);
- hbtp_data->lcd_on = false;
hbtp_fb_suspend(hbtp_data);
} else if (blank == FB_BLANK_POWERDOWN) {
pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n",
__func__);
- hbtp_data->lcd_on = true;
+ } else {
+ pr_debug("%s: receives R_EARLY_BALNK:%d in %d state\n",
+ __func__, blank, lcd_state);
}
}
}
if (evdata->data && hbtp_data &&
event == FB_EVENT_BLANK) {
blank = *(int *)(evdata->data);
- if (blank == FB_BLANK_POWERDOWN) {
+ lcd_state = hbtp->lcd_state;
+ if (blank == FB_BLANK_POWERDOWN &&
+ lcd_state <= FB_BLANK_NORMAL) {
pr_debug("%s: receives BLANK:POWERDOWN\n", __func__);
hbtp_fb_suspend(hbtp_data);
- } else if (blank == FB_BLANK_UNBLANK) {
+ } else if (blank <= FB_BLANK_NORMAL &&
+ lcd_state == FB_BLANK_POWERDOWN) {
pr_debug("%s: receives BLANK:UNBLANK\n", __func__);
hbtp_fb_resume(hbtp_data);
+ } else {
+ pr_debug("%s: receives BLANK:%d in %d state\n",
+ __func__, blank, lcd_state);
}
+ hbtp_data->lcd_state = blank;
}
return 0;
}
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
* Avatar AVIU-145A2 0x361f00 ? clickpad
* Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons
+ * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons
* Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons
* Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons
* Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**)
},
},
{
+ /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"),
+ },
+ },
+ {
/* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
return -EBADF;
}
- if (count == 0)
- return 0;
+ mutex_lock(&(dev_data->file_mutex));
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
- tmpbuf = kzalloc(count + 1, GFP_KERNEL);
- if (!tmpbuf)
- return -ENOMEM;
-
- mutex_lock(&(dev_data->file_mutex));
+ if (count == 0) {
+ retval = 0;
+ goto unlock;
+ }
+ if (*f_pos > REG_ADDR_LIMIT) {
+ retval = -EFAULT;
+ goto unlock;
+ }
+ tmpbuf = kzalloc(count + 1, GFP_KERNEL);
+ if (!tmpbuf) {
+ retval = -ENOMEM;
+ goto unlock;
+ }
retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
*f_pos,
tmpbuf,
*f_pos += retval;
clean_up:
- mutex_unlock(&(dev_data->file_mutex));
kfree(tmpbuf);
+unlock:
+ mutex_unlock(&(dev_data->file_mutex));
return retval;
}
return -EBADF;
}
- if (count == 0)
- return 0;
+ mutex_lock(&(dev_data->file_mutex));
+
+ if (*f_pos > REG_ADDR_LIMIT) {
+ retval = -EFAULT;
+ goto unlock;
+ }
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
+ if (count == 0) {
+ retval = 0;
+ goto unlock;
+ }
+
tmpbuf = kzalloc(count + 1, GFP_KERNEL);
- if (!tmpbuf)
- return -ENOMEM;
+ if (!tmpbuf) {
+ retval = -ENOMEM;
+ goto unlock;
+ }
if (copy_from_user(tmpbuf, buf, count)) {
- kfree(tmpbuf);
- return -EFAULT;
+ retval = -EFAULT;
+ goto clean_up;
}
- mutex_lock(&(dev_data->file_mutex));
retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
*f_pos,
if (retval >= 0)
*f_pos += retval;
- mutex_unlock(&(dev_data->file_mutex));
+clean_up:
kfree(tmpbuf);
+unlock:
+ mutex_unlock(&(dev_data->file_mutex));
return retval;
}
config IOMMU_IO_PGTABLE_FAST
bool "Fast ARMv7/v8 Long Descriptor Format"
- select IOMMU_IO_PGTABLE
+ depends on ARM64_DMA_USE_IOMMU
help
Enable support for a subset of the ARM long descriptor pagetable
format. This allocator achieves fast performance by
static bool arm_smmu_has_secure_vmid(struct arm_smmu_domain *smmu_domain);
static bool arm_smmu_is_iova_coherent(struct iommu_domain *domain,
dma_addr_t iova);
+static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain,
+ dma_addr_t iova);
static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain);
smmu->num_context_banks + 2,
MAX_ASID + 1, GFP_KERNEL);
if (ret < 0) {
- dev_err(smmu->dev, "dynamic ASID allocation failed: %d\n",
- ret);
+ dev_err_ratelimited(smmu->dev,
+ "dynamic ASID allocation failed: %d\n", ret);
goto out;
}
return ret;
}
+static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain,
+ dma_addr_t iova)
+{
+ uint64_t ret;
+ unsigned long flags;
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+ if (!ops)
+ return 0;
+
+ flags = arm_smmu_pgtbl_lock(smmu_domain);
+ ret = ops->iova_to_pte(ops, iova);
+ arm_smmu_pgtbl_unlock(smmu_domain, flags);
+ return ret;
+}
+
static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents, int prot)
{
.enable_config_clocks = arm_smmu_enable_config_clocks,
.disable_config_clocks = arm_smmu_disable_config_clocks,
.is_iova_coherent = arm_smmu_is_iova_coherent,
+ .iova_to_pte = arm_smmu_iova_to_pte,
};
static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
return 0;
}
+static uint64_t arm_lpae_iova_get_pte(struct io_pgtable_ops *ops,
+ unsigned long iova)
+{
+ struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ arm_lpae_iopte pte;
+ int lvl;
+
+ if (!arm_lpae_iova_to_pte(data, iova, &lvl, &pte))
+ return pte;
+
+ return 0;
+}
+
static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
unsigned long iova)
{
.unmap = arm_lpae_unmap,
.iova_to_phys = arm_lpae_iova_to_phys,
.is_iova_coherent = arm_lpae_is_iova_coherent,
+ .iova_to_pte = arm_lpae_iova_get_pte,
};
return data;
unsigned long iova);
bool (*is_iova_coherent)(struct io_pgtable_ops *ops,
unsigned long iova);
+ uint64_t (*iova_to_pte)(struct io_pgtable_ops *ops,
+ unsigned long iova);
};
static LIST_HEAD(iommu_debug_devices);
static struct dentry *debugfs_tests_dir;
static u32 iters_per_op = 1;
+static void *virt_addr;
struct iommu_debug_device {
struct device *dev;
return -EIO;
}
+static ssize_t __iommu_debug_dma_attach_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ struct iommu_debug_device *ddev = file->private_data;
+ struct device *dev = ddev->dev;
+ struct dma_iommu_mapping *dma_mapping;
+ ssize_t retval = -EINVAL;
+ int val;
+
+ if (kstrtoint_from_user(ubuf, count, 0, &val)) {
+ pr_err("Invalid format. Expected a hex or decimal integer");
+ retval = -EFAULT;
+ goto out;
+ }
+
+ if (val) {
+ if (dev->archdata.mapping)
+ if (dev->archdata.mapping->domain) {
+ pr_err("Already attached.\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ if (WARN(dev->archdata.iommu,
+ "Attachment tracking out of sync with device\n")) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ dma_mapping = arm_iommu_create_mapping(&platform_bus_type, 0,
+ (SZ_1G * 4ULL));
+
+ if (!dma_mapping)
+ goto out;
+
+ if (arm_iommu_attach_device(dev, dma_mapping))
+ goto out_release_mapping;
+ pr_err("Attached\n");
+ } else {
+ if (!dev->archdata.mapping) {
+ pr_err("No mapping. Did you already attach?\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ if (!dev->archdata.mapping->domain) {
+ pr_err("No domain. Did you already attach?\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ arm_iommu_detach_device(dev);
+ arm_iommu_release_mapping(dev->archdata.mapping);
+ pr_err("Detached\n");
+ }
+ retval = count;
+ return retval;
+
+out_release_mapping:
+ arm_iommu_release_mapping(dma_mapping);
+out:
+ return retval;
+}
+
static ssize_t __iommu_debug_attach_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *offset,
return retval;
}
+static ssize_t iommu_debug_dma_attach_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ return __iommu_debug_dma_attach_write(file, ubuf, count, offset);
+
+}
+
+static ssize_t iommu_debug_dma_attach_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ struct iommu_debug_device *ddev = file->private_data;
+ struct device *dev = ddev->dev;
+ char c[2];
+
+ if (*offset)
+ return 0;
+
+ if (!dev->archdata.mapping)
+ c[0] = '0';
+ else
+ c[0] = dev->archdata.mapping->domain ? '1' : '0';
+
+ c[1] = '\n';
+ if (copy_to_user(ubuf, &c, 2)) {
+ pr_err("copy_to_user failed\n");
+ return -EFAULT;
+ }
+ *offset = 1; /* non-zero means we're done */
+
+ return 2;
+}
+
+static const struct file_operations iommu_debug_dma_attach_fops = {
+ .open = simple_open,
+ .write = iommu_debug_dma_attach_write,
+ .read = iommu_debug_dma_attach_read,
+};
+
+static ssize_t iommu_debug_virt_addr_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ char buf[100];
+ ssize_t retval;
+ size_t buflen;
+
+ if (*offset)
+ return 0;
+
+ memset(buf, 0, 100);
+
+ if (!virt_addr)
+ strlcpy(buf, "FAIL\n", 100);
+ else
+ snprintf(buf, 100, "0x%pK\n", virt_addr);
+
+ buflen = strlen(buf);
+ if (copy_to_user(ubuf, buf, buflen)) {
+ pr_err("Couldn't copy_to_user\n");
+ retval = -EFAULT;
+ } else {
+ *offset = 1; /* non-zero means we're done */
+ retval = buflen;
+ }
+
+ return retval;
+}
+
+static const struct file_operations iommu_debug_virt_addr_fops = {
+ .open = simple_open,
+ .read = iommu_debug_virt_addr_read,
+};
+
static ssize_t iommu_debug_attach_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *offset)
.read = iommu_debug_attach_read,
};
+static ssize_t iommu_debug_pte_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ struct iommu_debug_device *ddev = file->private_data;
+ dma_addr_t iova;
+
+ if (kstrtox_from_user(ubuf, count, 0, &iova)) {
+ pr_err("Invalid format for iova\n");
+ ddev->iova = 0;
+ return -EINVAL;
+ }
+
+ ddev->iova = iova;
+ pr_err("Saved iova=%pa for future PTE commands\n", &iova);
+ return count;
+}
+
+
+static ssize_t iommu_debug_pte_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ struct iommu_debug_device *ddev = file->private_data;
+ struct device *dev = ddev->dev;
+ uint64_t pte;
+ char buf[100];
+ ssize_t retval;
+ size_t buflen;
+
+ if (!dev->archdata.mapping) {
+ pr_err("No mapping. Did you already attach?\n");
+ return -EINVAL;
+ }
+ if (!dev->archdata.mapping->domain) {
+ pr_err("No domain. Did you already attach?\n");
+ return -EINVAL;
+ }
+
+ if (*offset)
+ return 0;
+
+ memset(buf, 0, 100);
+
+ pte = iommu_iova_to_pte(dev->archdata.mapping->domain,
+ ddev->iova);
+
+ if (!pte)
+ strlcpy(buf, "FAIL\n", 100);
+ else
+ snprintf(buf, 100, "pte=%016llx\n", pte);
+
+ buflen = strlen(buf);
+ if (copy_to_user(ubuf, buf, buflen)) {
+ pr_err("Couldn't copy_to_user\n");
+ retval = -EFAULT;
+ } else {
+ *offset = 1; /* non-zero means we're done */
+ retval = buflen;
+ }
+
+ return retval;
+}
+
+static const struct file_operations iommu_debug_pte_fops = {
+ .open = simple_open,
+ .write = iommu_debug_pte_write,
+ .read = iommu_debug_pte_read,
+};
+
static ssize_t iommu_debug_atos_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *offset)
memset(buf, 0, 100);
phys = iommu_iova_to_phys_hard(ddev->domain, ddev->iova);
- if (!phys)
+ if (!phys) {
strlcpy(buf, "FAIL\n", 100);
- else
+ phys = iommu_iova_to_phys(ddev->domain, ddev->iova);
+ dev_err(ddev->dev, "ATOS for %pa failed. Software walk returned: %pa\n",
+ &ddev->iova, &phys);
+ } else {
snprintf(buf, 100, "%pa\n", &phys);
+ }
buflen = strlen(buf);
if (copy_to_user(ubuf, buf, buflen)) {
.read = iommu_debug_atos_read,
};
+static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ struct iommu_debug_device *ddev = file->private_data;
+ struct device *dev = ddev->dev;
+ phys_addr_t phys;
+ char buf[100];
+ ssize_t retval;
+ size_t buflen;
+
+ if (!dev->archdata.mapping) {
+ pr_err("No mapping. Did you already attach?\n");
+ return -EINVAL;
+ }
+ if (!dev->archdata.mapping->domain) {
+ pr_err("No domain. Did you already attach?\n");
+ return -EINVAL;
+ }
+
+ if (*offset)
+ return 0;
+
+ memset(buf, 0, 100);
+
+ phys = iommu_iova_to_phys_hard(dev->archdata.mapping->domain,
+ ddev->iova);
+ if (!phys)
+ strlcpy(buf, "FAIL\n", 100);
+ else
+ snprintf(buf, 100, "%pa\n", &phys);
+
+ buflen = strlen(buf);
+ if (copy_to_user(ubuf, buf, buflen)) {
+ pr_err("Couldn't copy_to_user\n");
+ retval = -EFAULT;
+ } else {
+ *offset = 1; /* non-zero means we're done */
+ retval = buflen;
+ }
+
+ return retval;
+}
+
+static const struct file_operations iommu_debug_dma_atos_fops = {
+ .open = simple_open,
+ .write = iommu_debug_atos_write,
+ .read = iommu_debug_dma_atos_read,
+};
+
static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *offset)
{
.write = iommu_debug_map_write,
};
+static ssize_t iommu_debug_dma_map_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *offset)
+{
+ ssize_t retval = -EINVAL;
+ int ret;
+ char *comma1, *comma2;
+ char buf[100];
+ unsigned long addr;
+ void *v_addr;
+ dma_addr_t iova;
+ size_t size;
+ unsigned int attr;
+ struct dma_attrs coherent_attr;
+ struct dma_attrs *dma_attrs = &coherent_attr;
+ struct iommu_debug_device *ddev = file->private_data;
+ struct device *dev = ddev->dev;
+
+ init_dma_attrs(dma_attrs);
+
+ if (count >= 100) {
+ pr_err("Value too large\n");
+ return -EINVAL;
+ }
+
+ if (!dev->archdata.mapping) {
+ pr_err("No mapping. Did you already attach?\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ if (!dev->archdata.mapping->domain) {
+ pr_err("No domain. Did you already attach?\n");
+ retval = -EINVAL;
+ goto out;
+ }
+
+ memset(buf, 0, 100);
+
+ if (copy_from_user(buf, ubuf, count)) {
+ pr_err("Couldn't copy from user\n");
+ retval = -EFAULT;
+ goto out;
+ }
+
+ comma1 = strnchr(buf, count, ',');
+ if (!comma1)
+ goto invalid_format;
+
+ comma2 = strnchr(comma1 + 1, count, ',');
+ if (!comma2)
+ goto invalid_format;
+
+ *comma1 = *comma2 = '\0';
+
+ if (kstrtoul(buf, 0, &addr))
+ goto invalid_format;
+ v_addr = (void *)addr;
+
+ if (kstrtosize_t(comma1 + 1, 0, &size))
+ goto invalid_format;
+
+ if (kstrtouint(comma2 + 1, 0, &attr))
+ goto invalid_format;
+
+ if (v_addr < virt_addr || v_addr > (virt_addr + SZ_1M - 1))
+ goto invalid_addr;
+
+ if (attr == 0)
+ dma_attrs = NULL;
+ else if (attr == 1)
+ dma_set_attr(DMA_ATTR_FORCE_COHERENT, dma_attrs);
+ else if (attr == 2)
+ dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT, dma_attrs);
+ else
+ goto invalid_format;
+
+ iova = dma_map_single_attrs(dev, v_addr, size,
+ DMA_TO_DEVICE, dma_attrs);
+
+ if (dma_mapping_error(dev, iova)) {
+ pr_err("Failed to perform dma_map_single\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ retval = count;
+ pr_err("Mapped 0x%p to %pa (len=0x%zx)\n",
+ v_addr, &iova, size);
+ ddev->iova = iova;
+ pr_err("Saved iova=%pa for future PTE commands\n", &iova);
+out:
+ return retval;
+
+invalid_format:
+ pr_err("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n");
+ return retval;
+
+invalid_addr:
+ pr_err("Invalid addr given! Address should be within 1MB size from start addr returned by doing 'cat virt_addr'.\n");
+ return retval;
+}
+
+static ssize_t iommu_debug_dma_map_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ struct iommu_debug_device *ddev = file->private_data;
+ struct device *dev = ddev->dev;
+ char buf[100];
+ ssize_t retval;
+ size_t buflen;
+ dma_addr_t iova;
+
+ if (!dev->archdata.mapping) {
+ pr_err("No mapping. Did you already attach?\n");
+ return -EINVAL;
+ }
+ if (!dev->archdata.mapping->domain) {
+ pr_err("No domain. Did you already attach?\n");
+ return -EINVAL;
+ }
+
+ if (*offset)
+ return 0;
+
+ memset(buf, 0, 100);
+
+ iova = ddev->iova;
+ snprintf(buf, 100, "%pa\n", &iova);
+
+ buflen = strlen(buf);
+ if (copy_to_user(ubuf, buf, buflen)) {
+ pr_err("Couldn't copy_to_user\n");
+ retval = -EFAULT;
+ } else {
+ *offset = 1; /* non-zero means we're done */
+ retval = buflen;
+ }
+
+ return retval;
+}
+
+static const struct file_operations iommu_debug_dma_map_fops = {
+ .open = simple_open,
+ .write = iommu_debug_dma_map_write,
+ .read = iommu_debug_dma_map_read,
+};
+
static ssize_t iommu_debug_unmap_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *offset)
.write = iommu_debug_unmap_write,
};
+static ssize_t iommu_debug_dma_unmap_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *offset)
+{
+ ssize_t retval = 0;
+ char *comma1, *comma2;
+ char buf[100];
+ size_t size;
+ unsigned int attr;
+ dma_addr_t iova;
+ struct dma_attrs coherent_attr;
+ struct dma_attrs *dma_attrs = &coherent_attr;
+ struct iommu_debug_device *ddev = file->private_data;
+ struct device *dev = ddev->dev;
+
+ init_dma_attrs(dma_attrs);
+
+ if (count >= 100) {
+ pr_err("Value too large\n");
+ return -EINVAL;
+ }
+
+ if (!dev->archdata.mapping) {
+ pr_err("No mapping. Did you already attach?\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ if (!dev->archdata.mapping->domain) {
+ pr_err("No domain. Did you already attach?\n");
+ retval = -EINVAL;
+ goto out;
+ }
+
+ memset(buf, 0, 100);
+
+ if (copy_from_user(buf, ubuf, count)) {
+ pr_err("Couldn't copy from user\n");
+ retval = -EFAULT;
+ goto out;
+ }
+
+ comma1 = strnchr(buf, count, ',');
+ if (!comma1)
+ goto invalid_format;
+
+ comma2 = strnchr(comma1 + 1, count, ',');
+ if (!comma2)
+ goto invalid_format;
+
+ *comma1 = *comma2 = '\0';
+
+ if (kstrtoux(buf, 0, &iova))
+ goto invalid_format;
+
+ if (kstrtosize_t(comma1 + 1, 0, &size))
+ goto invalid_format;
+
+ if (kstrtouint(comma2 + 1, 0, &attr))
+ goto invalid_format;
+
+ if (attr == 0)
+ dma_attrs = NULL;
+ else if (attr == 1)
+ dma_set_attr(DMA_ATTR_FORCE_COHERENT, dma_attrs);
+ else if (attr == 2)
+ dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT, dma_attrs);
+ else
+ goto invalid_format;
+
+ dma_unmap_single_attrs(dev, iova, size, DMA_TO_DEVICE, dma_attrs);
+
+ retval = count;
+ pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size);
+out:
+ return retval;
+
+invalid_format:
+ pr_err("Invalid format. Expected: iova,len, dma attr\n");
+ return retval;
+}
+
+static const struct file_operations iommu_debug_dma_unmap_fops = {
+ .open = simple_open,
+ .write = iommu_debug_dma_unmap_write,
+};
+
static ssize_t iommu_debug_config_clocks_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *offset)
goto err_rmdir;
}
+ if (!debugfs_create_file("virt_addr", S_IRUSR, dir, ddev,
+ &iommu_debug_virt_addr_fops)) {
+ pr_err("Couldn't create iommu/devices/%s/virt_addr debugfs file\n",
+ name);
+ goto err_rmdir;
+ }
+
if (!debugfs_create_file("profiling", S_IRUSR, dir, ddev,
&iommu_debug_profiling_fops)) {
pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n",
goto err_rmdir;
}
+ if (!debugfs_create_file("dma_attach", S_IRUSR, dir, ddev,
+ &iommu_debug_dma_attach_fops)) {
+ pr_err("Couldn't create iommu/devices/%s/dma_attach debugfs file\n",
+ name);
+ goto err_rmdir;
+ }
+
if (!debugfs_create_file("attach", S_IRUSR, dir, ddev,
&iommu_debug_attach_fops)) {
pr_err("Couldn't create iommu/devices/%s/attach debugfs file\n",
goto err_rmdir;
}
+ if (!debugfs_create_file("dma_atos", S_IWUSR, dir, ddev,
+ &iommu_debug_dma_atos_fops)) {
+ pr_err("Couldn't create iommu/devices/%s/dma_atos debugfs file\n",
+ name);
+ goto err_rmdir;
+ }
+
if (!debugfs_create_file("map", S_IWUSR, dir, ddev,
&iommu_debug_map_fops)) {
pr_err("Couldn't create iommu/devices/%s/map debugfs file\n",
goto err_rmdir;
}
+ if (!debugfs_create_file("dma_map", S_IWUSR, dir, ddev,
+ &iommu_debug_dma_map_fops)) {
+ pr_err("Couldn't create iommu/devices/%s/dma_map debugfs file\n",
+ name);
+ goto err_rmdir;
+ }
+
if (!debugfs_create_file("unmap", S_IWUSR, dir, ddev,
&iommu_debug_unmap_fops)) {
pr_err("Couldn't create iommu/devices/%s/unmap debugfs file\n",
goto err_rmdir;
}
+ if (!debugfs_create_file("dma_unmap", S_IWUSR, dir, ddev,
+ &iommu_debug_dma_unmap_fops)) {
+ pr_err("Couldn't create iommu/devices/%s/dma_unmap debugfs file\n",
+ name);
+ goto err_rmdir;
+ }
+
+ if (!debugfs_create_file("pte", S_IWUSR, dir, ddev,
+ &iommu_debug_pte_fops)) {
+ pr_err("Couldn't create iommu/devices/%s/pte debugfs file\n",
+ name);
+ goto err_rmdir;
+ }
+
if (!debugfs_create_file("config_clocks", S_IWUSR, dir, ddev,
&iommu_debug_config_clocks_fops)) {
pr_err("Couldn't create iommu/devices/%s/config_clocks debugfs file\n",
return -ENODEV;
}
+ virt_addr = kzalloc(SZ_1M, GFP_KERNEL);
+
+ if (!virt_addr)
+ return -ENOMEM;
+
return iommu_debug_populate_devices();
}
return domain->ops->iova_to_phys_hard(domain, iova);
}
+uint64_t iommu_iova_to_pte(struct iommu_domain *domain,
+ dma_addr_t iova)
+{
+ if (unlikely(domain->ops->iova_to_pte == NULL))
+ return 0;
+
+ return domain->ops->iova_to_pte(domain, iova);
+}
+
bool iommu_is_iova_coherent(struct iommu_domain *domain, dma_addr_t iova)
{
if (unlikely(domain->ops->is_iova_coherent == NULL))
return -ENOMEM;
}
+ raw_spin_lock_init(&cd->rlock);
+
cd->gpc_base = of_iomap(node, 0);
if (!cd->gpc_base) {
pr_err("fsl-gpcv2: unable to map gpc registers\n");
dm-verity-objs += dm-verity-fec.o
endif
-ifeq ($(CONFIG_DM_ANDROID_VERITY),y)
-dm-verity-objs += dm-android-verity.o
-endif
-
ifeq ($(CONFIG_DM_VERITY_AVB),y)
dm-verity-objs += dm-verity-avb.o
endif
struct dm_offload *o = container_of(cb, struct dm_offload, cb);
struct bio_list list;
struct bio *bio;
+ int i;
INIT_LIST_HEAD(&o->cb.list);
if (unlikely(!current->bio_list))
return;
- list = *current->bio_list;
- bio_list_init(current->bio_list);
-
- while ((bio = bio_list_pop(&list))) {
- struct bio_set *bs = bio->bi_pool;
- if (unlikely(!bs) || bs == fs_bio_set) {
- bio_list_add(current->bio_list, bio);
- continue;
+ for (i = 0; i < 2; i++) {
+ list = current->bio_list[i];
+ bio_list_init(¤t->bio_list[i]);
+
+ while ((bio = bio_list_pop(&list))) {
+ struct bio_set *bs = bio->bi_pool;
+ if (unlikely(!bs) || bs == fs_bio_set) {
+ bio_list_add(¤t->bio_list[i], bio);
+ continue;
+ }
+
+ spin_lock(&bs->rescue_lock);
+ bio_list_add(&bs->rescue_list, bio);
+ queue_work(bs->rescue_workqueue, &bs->rescue_work);
+ spin_unlock(&bs->rescue_lock);
}
-
- spin_lock(&bs->rescue_lock);
- bio_list_add(&bs->rescue_list, bio);
- queue_work(bs->rescue_workqueue, &bs->rescue_work);
- spin_unlock(&bs->rescue_lock);
}
}
((conf->start_next_window <
conf->next_resync + RESYNC_SECTORS) &&
current->bio_list &&
- !bio_list_empty(current->bio_list))),
+ (!bio_list_empty(¤t->bio_list[0]) ||
+ !bio_list_empty(¤t->bio_list[1])))),
conf->resync_lock);
conf->nr_waiting--;
}
!conf->barrier ||
(conf->nr_pending &&
current->bio_list &&
- !bio_list_empty(current->bio_list)),
+ (!bio_list_empty(¤t->bio_list[0]) ||
+ !bio_list_empty(¤t->bio_list[1]))),
conf->resync_lock);
conf->nr_waiting--;
}
spin_lock_irqsave(&tasklet->tasklet_lock, flags);
queue_cmd = &tasklet->tasklet_queue_cmd[tasklet->taskletq_idx];
if (queue_cmd->cmd_used) {
- pr_err("%s: Tasklet queue overflow: %d\n",
+ pr_err_ratelimited("%s: Tasklet queue overflow: %d\n",
__func__, vfe_dev->pdev->id);
spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
return;
#define CDBG(fmt, args...)
#endif
+/* Backward interface compatibility for 3D THRESHOLD calculation */
+#define ISPIF_USE_DEFAULT_THRESHOLD (0)
+#define ISPIF_CALCULATE_THRESHOLD (1)
+
static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable);
static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd,
/* This is set when device is 8974 */
ispif->clk_idx = 1;
}
-
+ memset(ispif->stereo_configured, 0, sizeof(ispif->stereo_configured));
atomic_set(&ispif->reset_trig[VFE0], 1);
/* initiate reset of ISPIF */
msm_camera_io_w(ISPIF_RST_CMD_MASK,
}
static void msm_ispif_config_stereo(struct ispif_device *ispif,
- struct msm_ispif_param_data_ext *params) {
+ struct msm_ispif_param_data_ext *params, int use_line_width) {
int i;
enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t stereo_3d_threshold = STEREO_DEFAULT_3D_THRESHOLD;
for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
if (params->entries[i].intftype == PIX0 &&
- params->stereo_enable &&
- params->right_entries[i].csid < CSID_MAX) {
- vfe_intf = params->entries[i].vfe_intf;
+ params->stereo_enable &&
+ params->right_entries[i].csid < CSID_MAX &&
+ !ispif->stereo_configured[vfe_intf]) {
msm_camera_io_w_mb(0x3,
ispif->base + ISPIF_VFE_m_OUTPUT_SEL(vfe_intf));
- msm_camera_io_w_mb(STEREO_DEFAULT_3D_THRESHOLD,
+ if (use_line_width &&
+ (params->line_width[vfe_intf] > 0))
+ stereo_3d_threshold =
+ (params->line_width[vfe_intf] +
+ 2 * 6 - 1) / (2 * 6);
+ msm_camera_io_w_mb(stereo_3d_threshold,
ispif->base +
ISPIF_VFE_m_3D_THRESHOLD(vfe_intf));
+ ispif->stereo_configured[vfe_intf] = 1;
}
}
}
msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
cid_mask, params->entries[i].vfe_intf, 0);
if (params->stereo_enable) {
+ ispif->stereo_configured[
+ params->entries[i].vfe_intf] = 0;
cid_mask = msm_ispif_get_right_cids_mask_from_cfg(
¶ms->right_entries[i],
params->entries[i].num_cids);
rc = -EINVAL;
return rc;
}
- msm_ispif_config_stereo(ispif, params);
+
+ msm_ispif_config_stereo(ispif, params, ISPIF_USE_DEFAULT_THRESHOLD);
msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
return rc;
if (rc < 0)
goto end;
if (cid_right_mask) {
+ ispif->stereo_configured[
+ params->entries[i].vfe_intf] = 0;
intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1);
rc = readl_poll_timeout(ispif->base + intf_addr,
stop_flag,
rc = msm_ispif_config2(ispif, params);
msm_ispif_io_dump_reg(ispif);
break;
+ case ISPIF_CFG_STEREO:
+ msm_ispif_config_stereo(ispif, params,
+ ISPIF_CALCULATE_THRESHOLD);
+ break;
default:
pr_err("%s: invalid cfg_type\n", __func__);
rc = -EINVAL;
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-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
int ispif_vdd_count;
struct regulator *vfe_vdd[ISPIF_VFE_VDD_INFO_MAX];
int vfe_vdd_count;
+ int stereo_configured[VFE_MAX];
};
#endif
return 0;
}
+ if (!event_data)
+ return -EINVAL;
+
memset(&event, 0, sizeof(struct v4l2_event));
session_id = event_data->session_id;
stream_id = event_data->stream_id;
BUG_ON(!pvdev);
/* !!! only ONE open is allowed !!! */
- if (atomic_read(&pvdev->opened))
+ if (atomic_cmpxchg(&pvdev->opened, 0, 1))
return -EBUSY;
- atomic_set(&pvdev->opened, 1);
-
spin_lock_irqsave(&msm_pid_lock, flags);
msm_pid = get_pid(task_pid(current));
spin_unlock_irqrestore(&msm_pid_lock, flags);
return -EINVAL;
}
- if (stripe_base == UINT_MAX || new_frame->num_strips >
- (UINT_MAX - 1 - stripe_base) / stripe_size) {
- pr_err("Invalid frame message,num_strips %d is large\n",
+ /* Stripe index starts at zero */
+ if ((!new_frame->num_strips) ||
+ (new_frame->first_stripe_index >= new_frame->num_strips) ||
+ (new_frame->last_stripe_index >= new_frame->num_strips) ||
+ (new_frame->first_stripe_index >
+ new_frame->last_stripe_index)) {
+ pr_err("Invalid frame message, #stripes=%d, stripe indices=[%d,%d]\n",
+ new_frame->num_strips,
+ new_frame->first_stripe_index,
+ new_frame->last_stripe_index);
+ return -EINVAL;
+ }
+
+ if (!stripe_size) {
+ pr_err("Invalid frame message, invalid stripe_size (%d)!\n",
+ stripe_size);
+ return -EINVAL;
+ }
+
+ if ((stripe_base == UINT_MAX) ||
+ (new_frame->num_strips >
+ (UINT_MAX - 1 - stripe_base) / stripe_size)) {
+ pr_err("Invalid frame message, num_strips %d is large\n",
new_frame->num_strips);
return -EINVAL;
}
struct msm_cpp_frame_info_t *frame = NULL;
struct msm_cpp_frame_info_t k_frame_info;
int32_t rc = 0;
- int32_t i = 0;
- int32_t num_buff = sizeof(k_frame_info.output_buffer_info)/
+ uint32_t i = 0;
+ uint32_t num_buff = sizeof(k_frame_info.output_buffer_info) /
sizeof(struct msm_cpp_buffer_info_t);
+
if (copy_from_user(&k_frame_info,
(void __user *)ioctl_ptr->ioctl_ptr,
sizeof(k_frame_info)))
- return -EFAULT;
+ return -EFAULT;
frame = msm_cpp_get_frame(ioctl_ptr);
if (!frame) {
}
*ioctl_ptr = arg;
- if ((*ioctl_ptr == NULL) ||
- ((*ioctl_ptr)->ioctl_ptr == NULL)) {
+ if (((*ioctl_ptr) == NULL) ||
+ ((*ioctl_ptr)->ioctl_ptr == NULL) ||
+ ((*ioctl_ptr)->len == 0)) {
pr_err("Error invalid ioctl argument cmd %u", cmd);
return -EINVAL;
}
if (cpp_dev->iommu_state == CPP_IOMMU_STATE_DETACHED) {
struct msm_camera_smmu_attach_type cpp_attach_info;
+ if (ioctl_ptr->len !=
+ sizeof(struct msm_camera_smmu_attach_type)) {
+ rc = -EINVAL;
+ break;
+ }
+
memset(&cpp_attach_info, 0, sizeof(cpp_attach_info));
rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info,
ioctl_ptr);
if (rc < 0) {
pr_err("CPP_IOMMU_ATTACH copy from user fail");
- ERR_COPY_FROM_USER();
- return -EINVAL;
+ break;
}
cpp_dev->security_mode = cpp_attach_info.attach;
case VIDIOC_MSM_CPP_IOMMU_DETACH: {
if ((cpp_dev->iommu_state == CPP_IOMMU_STATE_ATTACHED) &&
(cpp_dev->stream_cnt == 0)) {
-
struct msm_camera_smmu_attach_type cpp_attach_info;
+ if (ioctl_ptr->len !=
+ sizeof(struct msm_camera_smmu_attach_type)) {
+ rc = -EINVAL;
+ break;
+ }
+
memset(&cpp_attach_info, 0, sizeof(cpp_attach_info));
rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info,
ioctl_ptr);
if (rc < 0) {
pr_err("CPP_IOMMU_DETTACH copy from user fail");
- ERR_COPY_FROM_USER();
- return -EINVAL;
+ break;
}
cpp_dev->security_mode = cpp_attach_info.attach;
} else {
pr_err("%s:%d IOMMMU attach triggered in invalid state\n",
__func__, __LINE__);
+ rc = -EINVAL;
}
break;
}
struct msm_cpp_stream_buff_info_t k_cpp_buff_info;
struct msm_cpp_frame_info32_t k32_frame_info;
struct msm_cpp_frame_info_t k64_frame_info;
+ struct msm_camera_smmu_attach_type kb_cpp_smmu_attach_info;
uint32_t identity_k = 0;
bool is_copytouser_req = true;
void __user *up = (void __user *)arg;
break;
}
case VIDIOC_MSM_CPP_IOMMU_ATTACH32:
- cmd = VIDIOC_MSM_CPP_IOMMU_ATTACH;
- break;
case VIDIOC_MSM_CPP_IOMMU_DETACH32:
- cmd = VIDIOC_MSM_CPP_IOMMU_DETACH;
+ {
+ if ((kp_ioctl.len != sizeof(struct msm_camera_smmu_attach_type))
+ || (copy_from_user(&kb_cpp_smmu_attach_info,
+ (void __user *)kp_ioctl.ioctl_ptr,
+ sizeof(kb_cpp_smmu_attach_info)))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ kp_ioctl.ioctl_ptr = (void *)&kb_cpp_smmu_attach_info;
+ is_copytouser_req = false;
+ cmd = (cmd == VIDIOC_MSM_CPP_IOMMU_ATTACH32) ?
+ VIDIOC_MSM_CPP_IOMMU_ATTACH :
+ VIDIOC_MSM_CPP_IOMMU_DETACH;
break;
+ }
case MSM_SD_NOTIFY_FREEZE:
break;
case MSM_SD_UNNOTIFY_FREEZE:
default:
pr_err_ratelimited("%s: unsupported compat type :%x LOAD %lu\n",
__func__, cmd, VIDIOC_MSM_CPP_LOAD_FIRMWARE);
- break;
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
}
mutex_unlock(&cpp_dev->mutex);
default:
pr_err_ratelimited("%s: unsupported compat type :%d\n",
__func__, cmd);
- break;
+ return -EINVAL;
}
if (is_copytouser_req) {
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
int sde_mdp_get_pipe_flush_bits(struct sde_mdp_pipe *pipe)
{
- u32 flush_bits;
+ u32 flush_bits = 0;
if (pipe->type == SDE_MDP_PIPE_TYPE_DMA)
flush_bits |= BIT(pipe->num) << 5;
void dvb_usbv2_disconnect(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
- const char *name = d->name;
- struct device dev = d->udev->dev;
+ const char *devname = kstrdup(dev_name(&d->udev->dev), GFP_KERNEL);
+ const char *drvname = d->name;
dev_dbg(&d->udev->dev, "%s: bInterfaceNumber=%d\n", __func__,
intf->cur_altsetting->desc.bInterfaceNumber);
dvb_usbv2_exit(d);
- dev_info(&dev, "%s: '%s' successfully deinitialized and disconnected\n",
- KBUILD_MODNAME, name);
+ pr_info("%s: '%s:%s' successfully deinitialized and disconnected\n",
+ KBUILD_MODNAME, drvname, devname);
+ kfree(devname);
}
EXPORT_SYMBOL(dvb_usbv2_disconnect);
int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type)
{
- struct hexline hx;
- u8 reset;
- int ret,pos=0;
+ struct hexline *hx;
+ u8 *buf;
+ int ret, pos = 0;
+ u16 cpu_cs_register = cypress[type].cpu_cs_register;
+
+ buf = kmalloc(sizeof(*hx), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ hx = (struct hexline *)buf;
/* stop the CPU */
- reset = 1;
- if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1)
+ buf[0] = 1;
+ if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1)
err("could not stop the USB controller CPU.");
- while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) {
- deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk);
- ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len);
+ while ((ret = dvb_usb_get_hexline(fw, hx, &pos)) > 0) {
+ deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n", hx->addr, hx->len, hx->chk);
+ ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
- if (ret != hx.len) {
+ if (ret != hx->len) {
err("error while transferring firmware "
"(transferred size: %d, block size: %d)",
- ret,hx.len);
+ ret, hx->len);
ret = -EINVAL;
break;
}
}
if (ret < 0) {
err("firmware download failed at %d with %d",pos,ret);
+ kfree(buf);
return ret;
}
if (ret == 0) {
/* restart the CPU */
- reset = 0;
- if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) {
+ buf[0] = 0;
+ if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
} else
ret = -EIO;
+ kfree(buf);
+
return ret;
}
EXPORT_SYMBOL(usb_cypress_load_firmware);
#include "q6audio_common.h"
#include "audio_utils_aio.h"
#include <sound/msm-audio-effects-q6-v2.h>
-#include <sound/msm-dts-eagle.h>
#define MAX_CHANNELS_SUPPORTED 8
#define WAIT_TIMEDOUT_DURATION_SECS 1
pr_err("%s: audio client null to init pp\n", __func__);
return;
}
- switch (ac->topology) {
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
-
- ret = q6asm_set_softvolume_v2(ac, &softvol,
- SOFT_VOLUME_INSTANCE_1);
- if (ret < 0)
- pr_err("%s: Send SoftVolume1 Param failed ret=%d\n",
- __func__, ret);
- ret = q6asm_set_softvolume_v2(ac, &softvol,
- SOFT_VOLUME_INSTANCE_2);
- if (ret < 0)
- pr_err("%s: Send SoftVolume2 Param failed ret=%d\n",
- __func__, ret);
-
- msm_dts_eagle_init_master_module(ac);
-
- break;
- default:
- ret = q6asm_set_softvolume_v2(ac, &softvol,
- SOFT_VOLUME_INSTANCE_1);
- if (ret < 0)
- pr_err("%s: Send SoftVolume Param failed ret=%d\n",
- __func__, ret);
- break;
- }
+ ret = q6asm_set_softvolume_v2(ac, &softvol,
+ SOFT_VOLUME_INSTANCE_1);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
}
static void audio_effects_deinit_pp(struct audio_client *ac)
pr_err("%s: audio client null to deinit pp\n", __func__);
return;
}
- switch (ac->topology) {
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
- msm_dts_eagle_deinit_master_module(ac);
- break;
- default:
- break;
- }
}
static void audio_effects_event_handler(uint32_t opcode, uint32_t token,
&(effects->audio_effects.topo_switch_vol),
(long *)&values[1], SOFT_VOLUME_INSTANCE_2);
break;
- case DTS_EAGLE_MODULE_ENABLE:
- pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__);
- if (msm_audio_effects_is_effmodule_supp_in_top(
- effects_module, effects->ac->topology)) {
- /*
- * HPX->OFF: first disable HPX and then
- * enable SA+
- * HPX->ON: first disable SA+ and then
- * enable HPX
- */
- bool hpx_state = (bool)values[1];
- if (hpx_state)
- msm_audio_effects_enable_extn(effects->ac,
- &(effects->audio_effects),
- false);
- msm_dts_eagle_enable_asm(effects->ac,
- hpx_state,
- AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
- msm_dts_eagle_enable_asm(effects->ac,
- hpx_state,
- AUDPROC_MODULE_ID_DTS_HPX_POSTMIX);
- if (!hpx_state)
- msm_audio_effects_enable_extn(effects->ac,
- &(effects->audio_effects),
- true);
- }
- break;
default:
pr_err("%s: Invalid effects config module\n", __func__);
rc = -EINVAL;
long rc;
struct msm_audio_event32 usr_evt_32;
struct msm_audio_event usr_evt;
+ memset(&usr_evt, 0, sizeof(struct msm_audio_event));
if (copy_from_user(&usr_evt_32, arg,
sizeof(struct msm_audio_event32))) {
usr_evt.timeout_ms = usr_evt_32.timeout_ms;
rc = audio_aio_process_event_req_common(audio, &usr_evt);
+ if (rc < 0) {
+ pr_err("%s: audio process event failed, rc = %ld",
+ __func__, rc);
+ return rc;
+ }
usr_evt_32.event_type = usr_evt.event_type;
switch (usr_evt_32.event_type) {
{
struct uid_entry *uid_entry;
struct task_struct *task, *temp;
+ struct user_namespace *user_ns = current_user_ns();
cputime_t utime;
cputime_t stime;
unsigned long bkt;
+ uid_t uid;
rt_mutex_lock(&uid_lock);
read_lock(&tasklist_lock);
do_each_thread(temp, task) {
- uid_entry = find_or_register_uid(from_kuid_munged(
- current_user_ns(), task_uid(task)));
+ uid = from_kuid_munged(user_ns, task_uid(task));
+ uid_entry = find_or_register_uid(uid);
if (!uid_entry) {
read_unlock(&tasklist_lock);
rt_mutex_unlock(&uid_lock);
pr_err("%s: failed to find the uid_entry for uid %d\n",
- __func__, from_kuid_munged(current_user_ns(),
- task_uid(task)));
+ __func__, uid);
return -ENOMEM;
}
task_cputime_adjusted(task, &utime, &stime);
io_last->fsync -= task->ioac.syscfs;
}
-static void update_io_stats_locked(void)
+static void update_io_stats_all_locked(void)
{
struct uid_entry *uid_entry;
struct task_struct *task, *temp;
struct io_stats *io_bucket, *io_curr, *io_last;
+ struct user_namespace *user_ns = current_user_ns();
unsigned long bkt;
-
- BUG_ON(!rt_mutex_is_locked(&uid_lock));
+ uid_t uid;
hash_for_each(hash_table, bkt, uid_entry, hash)
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
sizeof(struct io_stats));
- read_lock(&tasklist_lock);
+ rcu_read_lock();
do_each_thread(temp, task) {
- uid_entry = find_or_register_uid(from_kuid_munged(
- current_user_ns(), task_uid(task)));
+ uid = from_kuid_munged(user_ns, task_uid(task));
+ uid_entry = find_or_register_uid(uid);
if (!uid_entry)
continue;
add_uid_io_curr_stats(uid_entry, task);
} while_each_thread(temp, task);
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
hash_for_each(hash_table, bkt, uid_entry, hash) {
io_bucket = &uid_entry->io[uid_entry->state];
}
}
+static void update_io_stats_uid_locked(uid_t target_uid)
+{
+ struct uid_entry *uid_entry;
+ struct task_struct *task, *temp;
+ struct io_stats *io_bucket, *io_curr, *io_last;
+ struct user_namespace *user_ns = current_user_ns();
+
+ uid_entry = find_or_register_uid(target_uid);
+ if (!uid_entry)
+ return;
+
+ memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
+ sizeof(struct io_stats));
+
+ rcu_read_lock();
+ do_each_thread(temp, task) {
+ if (from_kuid_munged(user_ns, task_uid(task)) != target_uid)
+ continue;
+ add_uid_io_curr_stats(uid_entry, task);
+ } while_each_thread(temp, task);
+ rcu_read_unlock();
+
+ io_bucket = &uid_entry->io[uid_entry->state];
+ io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
+ io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
+
+ io_bucket->read_bytes +=
+ io_curr->read_bytes - io_last->read_bytes;
+ io_bucket->write_bytes +=
+ io_curr->write_bytes - io_last->write_bytes;
+ io_bucket->rchar += io_curr->rchar - io_last->rchar;
+ io_bucket->wchar += io_curr->wchar - io_last->wchar;
+ io_bucket->fsync += io_curr->fsync - io_last->fsync;
+
+ io_last->read_bytes = io_curr->read_bytes;
+ io_last->write_bytes = io_curr->write_bytes;
+ io_last->rchar = io_curr->rchar;
+ io_last->wchar = io_curr->wchar;
+ io_last->fsync = io_curr->fsync;
+}
+
static int uid_io_show(struct seq_file *m, void *v)
{
struct uid_entry *uid_entry;
rt_mutex_lock(&uid_lock);
- update_io_stats_locked();
+ update_io_stats_all_locked();
hash_for_each(hash_table, bkt, uid_entry, hash) {
seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
return count;
}
- update_io_stats_locked();
+ update_io_stats_uid_locked(uid);
uid_entry->state = state;
uid_entry->utime += utime;
uid_entry->stime += stime;
- update_io_stats_locked();
+ update_io_stats_uid_locked(uid);
clean_uid_io_last_stats(uid_entry, task);
exit:
ret = mq->cmdq_issue_fn(mq, mq->cmdq_req_peeked);
/*
- * Don't requeue if issue_fn fails, just bug on.
- * We don't expect failure here and there is no recovery other
- * than fixing the actual issue if there is any.
+ * Don't requeue if issue_fn fails.
+ * Recovery will be come by completion softirq
* Also we end the request if there is a partition switch error,
* so we should not requeue the request here.
*/
- if (ret)
- BUG_ON(1);
} /* loop */
return 0;
*/
int mmc_cmdq_hw_reset(struct mmc_host *host)
{
- if (!host->bus_ops->power_restore)
- return -EOPNOTSUPP;
+ if (!host->bus_ops->reset)
+ return -EOPNOTSUPP;
- mmc_power_cycle(host, host->ocr_avail);
- mmc_select_voltage(host, host->card->ocr);
- return host->bus_ops->power_restore(host);
+ return host->bus_ops->reset(host);
}
EXPORT_SYMBOL(mmc_cmdq_hw_reset);
ret = host->bus_ops->reset(host);
mmc_bus_put(host);
- if (ret != -EOPNOTSUPP)
- pr_warn("%s: tried to reset card\n", mmc_hostname(host));
+ if (ret)
+ pr_warn("%s: tried to reset card, got error %d\n",
+ mmc_hostname(host), ret);
return ret;
}
mmc_get_card(host->card);
if (!value) {
- /*turning off clock scaling*/
- mmc_exit_clk_scaling(host);
+ /* Suspend the clock scaling and mask host capability */
+ if (host->clk_scaling.enable)
+ mmc_suspend_clk_scaling(host);
host->caps2 &= ~MMC_CAP2_CLK_SCALE;
host->clk_scaling.state = MMC_LOAD_HIGH;
/* Set to max. frequency when disabling */
mmc_clk_update_freq(host, host->card->clk_scaling_highest,
host->clk_scaling.state);
} else if (value) {
- /* starting clock scaling, will restart in case started */
+ /* Unmask host capability and resume scaling */
host->caps2 |= MMC_CAP2_CLK_SCALE;
- if (host->clk_scaling.enable)
- mmc_exit_clk_scaling(host);
- mmc_init_clk_scaling(host);
+ if (!host->clk_scaling.enable)
+ mmc_resume_clk_scaling(host);
}
mmc_put_card(host->card);
static int mmc_reset(struct mmc_host *host)
{
struct mmc_card *card = host->card;
+ int ret;
+
+ if ((host->caps & MMC_CAP_HW_RESET) && host->ops->hw_reset &&
+ mmc_can_reset(card)) {
+ /* If the card accept RST_n signal, send it. */
+ mmc_set_clock(host, host->f_init);
+ host->ops->hw_reset(host);
+ /* Set initial state and call mmc_set_ios */
+ mmc_set_initial_state(host);
+ } else {
+ /* Do a brute force power cycle */
+ mmc_power_cycle(host, card->ocr);
+ }
- if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
- return -EOPNOTSUPP;
-
- if (!mmc_can_reset(card))
- return -EOPNOTSUPP;
+ /* Suspend clk scaling to avoid switching frequencies intermittently */
- mmc_host_clk_hold(host);
- mmc_set_clock(host, host->f_init);
+ ret = mmc_suspend_clk_scaling(host);
+ if (ret) {
+ pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
+ mmc_hostname(host), __func__, ret);
+ return ret;
+ }
- host->ops->hw_reset(host);
+ ret = mmc_init_card(host, host->card->ocr, host->card);
+ if (ret) {
+ pr_err("%s: %s: mmc_init_card failed (%d)\n",
+ mmc_hostname(host), __func__, ret);
+ return ret;
+ }
- /* Set initial state and call mmc_set_ios */
- mmc_set_initial_state(host);
- mmc_host_clk_release(host);
+ ret = mmc_resume_clk_scaling(host);
+ if (ret)
+ pr_err("%s: %s: fail to resume clock scaling (%d)\n",
+ mmc_hostname(host), __func__, ret);
- return mmc_init_card(host, card->ocr, card);
+ return ret;
}
static const struct mmc_bus_ops mmc_ops = {
* before setting doorbell, hence one is not needed here.
*/
for_each_set_bit(tag, &comp_status, cq_host->num_slots) {
- /* complete the corresponding mrq */
- pr_debug("%s: completing tag -> %lu\n",
- mmc_hostname(mmc), tag);
- MMC_TRACE(mmc, "%s: completing tag -> %lu\n",
- __func__, tag);
+ mrq = get_req_by_tag(cq_host, tag);
+ if (!((mrq->cmd && mrq->cmd->error) ||
+ mrq->cmdq_req->resp_err ||
+ (mrq->data && mrq->data->error))) {
+ /* complete the corresponding mrq */
+ pr_debug("%s: completing tag -> %lu\n",
+ mmc_hostname(mmc), tag);
+ MMC_TRACE(mmc, "%s: completing tag -> %lu\n",
+ __func__, tag);
cmdq_finish_data(mmc, tag);
+ }
}
}
switch (uhs) {
case MMC_TIMING_UHS_SDR50:
+ case MMC_TIMING_UHS_DDR50:
pinctrl = imx_data->pins_100mhz;
break;
case MMC_TIMING_UHS_SDR104:
last_trx_part = curr_part - 1;
- /*
- * We have whole TRX scanned, skip to the next part. Use
- * roundown (not roundup), as the loop will increase
- * offset in next step.
- */
- offset = rounddown(offset + trx->length, blocksize);
+ /* Jump to the end of TRX */
+ offset = roundup(offset + trx->length, blocksize);
+ /* Next loop iteration will increase the offset */
+ offset -= blocksize;
continue;
}
return err;
}
- if (bytes == 0) {
- err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
- if (err)
- return err;
+ err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
+ if (err)
+ return err;
+ if (bytes == 0) {
err = clear_update_marker(ubi, vol, 0);
if (err)
return err;
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
#include <linux/spi/spi.h>
#include <linux/can.h>
#include <linux/can/dev.h>
+#include <linux/completion.h>
#define DEBUG_RH850 0
#if DEBUG_RH850 == 1
#define MAX_TX_BUFFERS 1
#define XFER_BUFFER_SIZE 64
#define RX_ASSEMBLY_BUFFER_SIZE 128
-#define RH850_CLOCK 80000000
+#define RH850_CLOCK 16000000
#define RH850_MAX_CHANNELS 4
+#define DRIVER_MODE_RAW_FRAMES 0
+#define DRIVER_MODE_PROPERTIES 1
+#define DRIVER_MODE_AMB 2
struct rh850_can {
struct net_device *netdev[RH850_MAX_CHANNELS];
char *assembly_buffer;
u8 assembly_buffer_size;
atomic_t netif_queue_stop;
+ struct completion response_completion;
+ int wait_cmd;
+ int cmd_result;
+ int driver_mode;
};
struct rh850_netdev_privdata {
#define CMD_CAN_ADD_FILTER 0x83
#define CMD_CAN_REMOVE_FILTER 0x84
#define CMD_CAN_RECEIVE_FRAME 0x85
+#define CMD_CAN_CONFIG_BIT_TIMING 0x86
+
+#define CMD_CAN_DATA_BUFF_ADD 0x87
+#define CMD_CAN_DATA_BUFF_REMOVE 0X88
+#define CMD_CAN_RELEASE_BUFFER 0x89
+#define CMD_CAN_DATA_BUFF_REMOVE_ALL 0x8A
+#define CMD_PROPERTY_WRITE 0x8B
+#define CMD_PROPERTY_READ 0x8C
+
+#define CMD_GET_FW_BR_VERSION 0x95
+#define CMD_BEGIN_FIRMWARE_UPGRADE 0x96
+#define CMD_FIRMWARE_UPGRADE_DATA 0x97
+#define CMD_END_FIRMWARE_UPGRADE 0x98
+#define CMD_BEGIN_BOOT_ROM_UPGRADE 0x99
+#define CMD_BOOT_ROM_UPGRADE_DATA 0x9A
+#define CMD_END_BOOT_ROM_UPGRADE 0x9B
+
+#define IOCTL_RELEASE_CAN_BUFFER (SIOCDEVPRIVATE + 0)
+#define IOCTL_ENABLE_BUFFERING (SIOCDEVPRIVATE + 1)
+#define IOCTL_ADD_FRAME_FILTER (SIOCDEVPRIVATE + 2)
+#define IOCTL_REMOVE_FRAME_FILTER (SIOCDEVPRIVATE + 3)
+#define IOCTL_DISABLE_BUFFERING (SIOCDEVPRIVATE + 5)
+#define IOCTL_DISABLE_ALL_BUFFERING (SIOCDEVPRIVATE + 6)
+#define IOCTL_GET_FW_BR_VERSION (SIOCDEVPRIVATE + 7)
+#define IOCTL_BEGIN_FIRMWARE_UPGRADE (SIOCDEVPRIVATE + 8)
+#define IOCTL_FIRMWARE_UPGRADE_DATA (SIOCDEVPRIVATE + 9)
+#define IOCTL_END_FIRMWARE_UPGRADE (SIOCDEVPRIVATE + 10)
+#define IOCTL_BEGIN_BOOT_ROM_UPGRADE (SIOCDEVPRIVATE + 11)
+#define IOCTL_BOOT_ROM_UPGRADE_DATA (SIOCDEVPRIVATE + 12)
+#define IOCTL_END_BOOT_ROM_UPGRADE (SIOCDEVPRIVATE + 13)
struct can_fw_resp {
u8 maj;
u8 data[];
} __packed;
+struct can_config_bit_timing {
+ u8 can_if;
+ u32 brp;
+ u32 tseg1;
+ u32 tseg2;
+ u32 sjw;
+} __packed;
+
+struct vehicle_property {
+ int id;
+ u64 ts;
+ int zone;
+ int val_type;
+ u32 data_len;
+ union {
+ u8 bval;
+ int val;
+ int val_arr[4];
+ float f_value;
+ float float_arr[4];
+ u8 str[36];
+ };
+} __packed;
+
+/* IOCTL messages */
+struct rh850_release_can_buffer {
+ u8 enable;
+} __packed;
+
+struct rh850_add_can_buffer {
+ u8 can_if;
+ u32 mid;
+ u32 mask;
+} __packed;
+
+struct rh850_delete_can_buffer {
+ u8 can_if;
+ u32 mid;
+ u32 mask;
+} __packed;
+
+struct can_fw_br_resp {
+ u8 maj;
+ u8 min;
+ u8 ver[32];
+ u8 br_maj;
+ u8 br_min;
+ u8 curr_exec_mode;
+} __packed;
+
+struct rh850_ioctl_req {
+ u8 len;
+ u8 data[];
+} __packed;
+
static struct can_bittiming_const rh850_bittiming_const = {
.name = "rh850",
- .tseg1_min = 4,
+ .tseg1_min = 1,
.tseg1_max = 16,
- .tseg2_min = 2,
- .tseg2_max = 8,
+ .tseg2_min = 1,
+ .tseg2_max = 16,
.sjw_max = 4,
- .brp_min = 4,
- .brp_max = 1023,
+ .brp_min = 1,
+ .brp_max = 70,
+ .brp_inc = 1,
+};
+
+static struct can_bittiming_const rh850_data_bittiming_const = {
+ .name = "rh850",
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 16,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 70,
.brp_inc = 1,
};
}
netdev = priv_data->netdev[frame->can_if];
skb = alloc_can_skb(netdev, &cf);
- if (skb == NULL) {
+ if (!skb) {
LOGDE("skb alloc failed. frame->can_if %d\n", frame->can_if);
return;
}
netdev->stats.rx_packets++;
}
-static void rh850_process_response(struct rh850_can *priv_data,
- struct spi_miso *resp, int length)
+static void rh850_receive_property(struct rh850_can *priv_data,
+ struct vehicle_property *property)
{
+ struct canfd_frame *cfd;
+ u8 *p;
+ struct sk_buff *skb;
+ struct skb_shared_hwtstamps *skt;
+ struct timeval tv;
+ static u64 nanosec;
+ struct net_device *netdev;
+ int i;
+
+ /* can0 as the channel with properties */
+ netdev = priv_data->netdev[0];
+ skb = alloc_canfd_skb(netdev, &cfd);
+ if (!skb) {
+ LOGDE("skb alloc failed. frame->can_if %d\n", 0);
+ return;
+ }
+
+ LOGDI("rcv property:0x%x data:%2x %2x %2x %2x",
+ property->id, property->str[0], property->str[1],
+ property->str[2], property->str[3]);
+ cfd->can_id = 0x00;
+ cfd->len = sizeof(struct vehicle_property);
+
+ p = (u8 *)property;
+ for (i = 0; i < cfd->len; i++)
+ cfd->data[i] = p[i];
+
+ nanosec = le64_to_cpu(property->ts);
+ tv.tv_sec = (int)(nanosec / 1000000000);
+ tv.tv_usec = (int)(nanosec - (u64)tv.tv_sec * 1000000000) / 1000;
+ skt = skb_hwtstamps(skb);
+ skt->hwtstamp = timeval_to_ktime(tv);
+ LOGDI(" hwtstamp %lld\n", ktime_to_ms(skt->hwtstamp));
+ skb->tstamp = timeval_to_ktime(tv);
+ netif_rx(skb);
+ netdev->stats.rx_packets++;
+}
+
+static int rh850_process_response(struct rh850_can *priv_data,
+ struct spi_miso *resp, int length)
+{
+ int ret = 0;
LOGDI("<%x %2d [%d]\n", resp->cmd, resp->len, resp->seq);
if (resp->cmd == CMD_CAN_RECEIVE_FRAME) {
struct can_receive_frame *frame =
} else {
rh850_receive_frame(priv_data, frame);
}
+ } else if (resp->cmd == CMD_PROPERTY_READ) {
+ struct vehicle_property *property =
+ (struct vehicle_property *)&resp->data;
+ if (resp->len > length) {
+ LOGDE("Error. This should never happen\n");
+ LOGDE("process_response: Saving %d bytes\n",
+ length);
+ memcpy(priv_data->assembly_buffer, (char *)resp,
+ length);
+ priv_data->assembly_buffer_size = length;
+ } else {
+ rh850_receive_property(priv_data, property);
+ }
} else if (resp->cmd == CMD_GET_FW_VERSION) {
struct can_fw_resp *fw_resp = (struct can_fw_resp *)resp->data;
dev_info(&priv_data->spidev->dev, "fw %d.%d",
fw_resp->maj, fw_resp->min);
dev_info(&priv_data->spidev->dev, "fw string %s",
fw_resp->ver);
+ } else if (resp->cmd == CMD_GET_FW_BR_VERSION) {
+ struct can_fw_br_resp *fw_resp =
+ (struct can_fw_br_resp *)resp->data;
+
+ dev_info(&priv_data->spidev->dev, "fw_can %d.%d",
+ fw_resp->maj, fw_resp->min);
+ dev_info(&priv_data->spidev->dev, "fw string %s",
+ fw_resp->ver);
+ dev_info(&priv_data->spidev->dev, "fw_br %d.%d exec_mode %d",
+ fw_resp->br_maj, fw_resp->br_min,
+ fw_resp->curr_exec_mode);
+ ret = fw_resp->curr_exec_mode << 28;
+ ret |= (fw_resp->br_maj & 0xF) << 24;
+ ret |= (fw_resp->br_min & 0xFF) << 16;
+ ret |= (fw_resp->maj & 0xF) << 8;
+ ret |= (fw_resp->min & 0xFF);
+ }
+
+ if (resp->cmd == priv_data->wait_cmd) {
+ priv_data->cmd_result = ret;
+ complete(&priv_data->response_completion);
}
+ return ret;
}
-static void rh850_process_rx(struct rh850_can *priv_data, char *rx_buf)
+static int rh850_process_rx(struct rh850_can *priv_data, char *rx_buf)
{
struct spi_miso *resp;
int length_processed = 0, actual_length = priv_data->xfer_length;
+ int ret = 0;
while (length_processed < actual_length) {
int length_left = actual_length - length_processed;
rx_buf, 2);
data = priv_data->assembly_buffer;
resp = (struct spi_miso *)data;
- length = resp->len - priv_data->assembly_buffer_size;
+ length = resp->len + sizeof(*resp)
+ - priv_data->assembly_buffer_size;
if (length > 0)
memcpy(priv_data->assembly_buffer +
priv_data->assembly_buffer_size,
length_processed, length_left, priv_data->xfer_length);
length_processed += length;
if (length_left >= sizeof(*resp) &&
- resp->len <= length_left) {
+ resp->len + sizeof(*resp) <= length_left) {
struct spi_miso *resp =
(struct spi_miso *)data;
- if (resp->len < sizeof(struct spi_miso)) {
- LOGDE("Error resp->len is %d). Abort.\n",
- resp->len);
- break;
- }
- rh850_process_response(priv_data, resp, length_left);
+ ret = rh850_process_response(priv_data, resp,
+ length_left);
} else if (length_left > 0) {
/* Not full message. Store however much we have for */
/* later assembly */
break;
}
}
+ return ret;
}
static int rh850_do_spi_transaction(struct rh850_can *priv_data)
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
if (xfer == 0 || msg == 0)
return -ENOMEM;
+ LOGDI(">%x %2d [%d]\n", priv_data->tx_buf[0],
+ priv_data->tx_buf[1], priv_data->tx_buf[2]);
spi_message_init(msg);
spi_message_add_tail(xfer, msg);
xfer->tx_buf = priv_data->tx_buf;
xfer->rx_buf = priv_data->rx_buf;
xfer->len = priv_data->xfer_length;
ret = spi_sync(spi, msg);
- LOGDI("spi_sync ret %d\n", ret);
+ LOGDI("spi_sync ret %d data %x %x %x %x %x %x %x %x\n", ret,
+ priv_data->rx_buf[0], priv_data->rx_buf[1], priv_data->rx_buf[2],
+ priv_data->rx_buf[3], priv_data->rx_buf[4], priv_data->rx_buf[5],
+ priv_data->rx_buf[6], priv_data->rx_buf[7]);
if (ret == 0)
- rh850_process_rx(priv_data, priv_data->rx_buf);
+ ret = rh850_process_rx(priv_data, priv_data->rx_buf);
kfree(msg);
kfree(xfer);
return ret;
return ret;
}
+static int rh850_set_bitrate(struct net_device *netdev)
+{
+ char *tx_buf, *rx_buf;
+ int ret;
+ struct spi_mosi *req;
+ struct can_config_bit_timing *req_d;
+ struct rh850_can *priv_data;
+ struct can_priv *priv = netdev_priv(netdev);
+ struct rh850_netdev_privdata *rh850_priv;
+
+ rh850_priv = netdev_priv(netdev);
+ priv_data = rh850_priv->rh850_can;
+
+ netdev_info(netdev, "ch%i, bitrate setting>%i",
+ rh850_priv->netdev_index, priv->bittiming.bitrate);
+ LOGNI("sjw>%i brp>%i ph_sg1>%i ph_sg2>%i smpl_pt>%i tq>%i pr_seg>%i",
+ priv->bittiming.sjw, priv->bittiming.brp,
+ priv->bittiming.phase_seg1,
+ priv->bittiming.phase_seg2,
+ priv->bittiming.sample_point,
+ priv->bittiming.tq, priv->bittiming.prop_seg);
+
+ mutex_lock(&priv_data->spi_lock);
+ tx_buf = priv_data->tx_buf;
+ rx_buf = priv_data->rx_buf;
+ memset(tx_buf, 0, XFER_BUFFER_SIZE);
+ memset(rx_buf, 0, XFER_BUFFER_SIZE);
+ priv_data->xfer_length = XFER_BUFFER_SIZE;
+
+ req = (struct spi_mosi *)tx_buf;
+ req->cmd = CMD_CAN_CONFIG_BIT_TIMING;
+ req->len = sizeof(struct can_config_bit_timing);
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+ req_d = (struct can_config_bit_timing *)req->data;
+ req_d->can_if = rh850_priv->netdev_index;
+ req_d->brp = priv->bittiming.brp;
+ req_d->tseg1 = priv->bittiming.phase_seg1 + priv->bittiming.prop_seg;
+ req_d->tseg2 = priv->bittiming.phase_seg2;
+ req_d->sjw = priv->bittiming.sjw;
+
+ ret = rh850_do_spi_transaction(priv_data);
+ mutex_unlock(&priv_data->spi_lock);
+
+ return ret;
+}
+
static int rh850_can_write(struct rh850_can *priv_data,
- int can_channel, struct can_frame *cf)
+ int can_channel, struct canfd_frame *cf)
{
char *tx_buf, *rx_buf;
int ret, i;
priv_data->xfer_length = XFER_BUFFER_SIZE;
req = (struct spi_mosi *)tx_buf;
- req->cmd = CMD_CAN_SEND_FRAME;
- req->len = sizeof(struct can_write_req) + 8;
- req->seq = atomic_inc_return(&priv_data->msg_seq);
-
- req_d = (struct can_write_req *)req->data;
- req_d->can_if = can_channel;
- req_d->mid = cf->can_id;
- req_d->dlc = cf->can_dlc;
- for (i = 0; i < cf->can_dlc; i++)
- req_d->data[i] = cf->data[i];
+ if (priv_data->driver_mode == DRIVER_MODE_RAW_FRAMES) {
+ req->cmd = CMD_CAN_SEND_FRAME;
+ req->len = sizeof(struct can_write_req) + 8;
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+
+ req_d = (struct can_write_req *)req->data;
+ req_d->can_if = can_channel;
+ req_d->mid = cf->can_id;
+ req_d->dlc = cf->len;
+
+ for (i = 0; i < cf->len; i++)
+ req_d->data[i] = cf->data[i];
+ } else if (priv_data->driver_mode == DRIVER_MODE_PROPERTIES ||
+ priv_data->driver_mode == DRIVER_MODE_AMB) {
+ req->cmd = CMD_PROPERTY_WRITE;
+ req->len = sizeof(struct vehicle_property);
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+ for (i = 0; i < cf->len; i++)
+ req->data[i] = cf->data[i];
+ } else {
+ LOGDE("rh850_can_write: wrong driver mode %i",
+ priv_data->driver_mode);
+ }
ret = rh850_do_spi_transaction(priv_data);
netdev = priv_data->netdev[can_channel];
static void rh850_send_can_frame(struct work_struct *ws)
{
struct rh850_tx_work *tx_work;
- struct can_frame *cf;
+ struct canfd_frame *cf;
struct rh850_can *priv_data;
struct net_device *netdev;
struct rh850_netdev_privdata *netdev_priv_data;
LOGDI("send_can_frame ws %p\n", ws);
LOGDI("send_can_frame tx %p\n", tx_work);
- cf = (struct can_frame *)tx_work->skb->data;
+ cf = (struct canfd_frame *)tx_work->skb->data;
rh850_can_write(priv_data, can_channel, cf);
dev_kfree_skb(tx_work->skb);
return NETDEV_TX_OK;
}
+static int rh850_send_release_can_buffer_cmd(struct net_device *netdev)
+{
+ char *tx_buf, *rx_buf;
+ int ret;
+ struct spi_mosi *req;
+ struct rh850_can *priv_data;
+ struct rh850_netdev_privdata *netdev_priv_data;
+ int *mode;
+
+ netdev_priv_data = netdev_priv(netdev);
+ priv_data = netdev_priv_data->rh850_can;
+ mutex_lock(&priv_data->spi_lock);
+ tx_buf = priv_data->tx_buf;
+ rx_buf = priv_data->rx_buf;
+ memset(tx_buf, 0, XFER_BUFFER_SIZE);
+ memset(rx_buf, 0, XFER_BUFFER_SIZE);
+ priv_data->xfer_length = XFER_BUFFER_SIZE;
+
+ req = (struct spi_mosi *)tx_buf;
+ req->cmd = CMD_CAN_RELEASE_BUFFER;
+ req->len = sizeof(int);
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+ mode = (int *)req->data;
+ *mode = priv_data->driver_mode;
+
+ ret = rh850_do_spi_transaction(priv_data);
+ mutex_unlock(&priv_data->spi_lock);
+
+ return ret;
+}
+
+static int rh850_data_buffering(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ char *tx_buf, *rx_buf;
+ int ret;
+ struct spi_mosi *req;
+ struct rh850_add_can_buffer *enable_buffering;
+ struct rh850_add_can_buffer *add_request;
+ struct rh850_can *priv_data;
+ struct rh850_netdev_privdata *netdev_priv_data;
+
+ netdev_priv_data = netdev_priv(netdev);
+ priv_data = netdev_priv_data->rh850_can;
+
+ mutex_lock(&priv_data->spi_lock);
+ tx_buf = priv_data->tx_buf;
+ rx_buf = priv_data->rx_buf;
+ memset(tx_buf, 0, XFER_BUFFER_SIZE);
+ memset(rx_buf, 0, XFER_BUFFER_SIZE);
+ priv_data->xfer_length = XFER_BUFFER_SIZE;
+
+ add_request = ifr->ifr_data;
+ req = (struct spi_mosi *)tx_buf;
+
+ if (cmd == IOCTL_ENABLE_BUFFERING)
+ req->cmd = CMD_CAN_DATA_BUFF_ADD;
+ else
+ req->cmd = CMD_CAN_DATA_BUFF_REMOVE;
+
+ req->len = sizeof(struct rh850_add_can_buffer);
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+
+ enable_buffering = (struct rh850_add_can_buffer *)req->data;
+ enable_buffering->can_if = add_request->can_if;
+ enable_buffering->mid = add_request->mid;
+ enable_buffering->mask = add_request->mask;
+
+ ret = rh850_do_spi_transaction(priv_data);
+ mutex_unlock(&priv_data->spi_lock);
+
+ return ret;
+}
+
+static int rh850_remove_all_buffering(struct net_device *netdev)
+{
+ char *tx_buf, *rx_buf;
+ int ret;
+ struct spi_mosi *req;
+ struct rh850_can *priv_data;
+ struct rh850_netdev_privdata *netdev_priv_data;
+
+ netdev_priv_data = netdev_priv(netdev);
+ priv_data = netdev_priv_data->rh850_can;
+
+ mutex_lock(&priv_data->spi_lock);
+ tx_buf = priv_data->tx_buf;
+ rx_buf = priv_data->rx_buf;
+ memset(tx_buf, 0, XFER_BUFFER_SIZE);
+ memset(rx_buf, 0, XFER_BUFFER_SIZE);
+ priv_data->xfer_length = XFER_BUFFER_SIZE;
+
+ req = (struct spi_mosi *)tx_buf;
+ req->cmd = CMD_CAN_DATA_BUFF_REMOVE_ALL;
+ req->len = 0;
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+
+ ret = rh850_do_spi_transaction(priv_data);
+ mutex_unlock(&priv_data->spi_lock);
+
+ return ret;
+}
+
+static int rh850_frame_filter(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ char *tx_buf, *rx_buf;
+ int ret;
+ struct spi_mosi *req;
+ struct can_add_filter_req *add_filter;
+ struct can_add_filter_req *filter_request;
+ struct rh850_can *priv_data;
+ struct rh850_netdev_privdata *netdev_priv_data;
+
+ netdev_priv_data = netdev_priv(netdev);
+ priv_data = netdev_priv_data->rh850_can;
+
+ mutex_lock(&priv_data->spi_lock);
+ tx_buf = priv_data->tx_buf;
+ rx_buf = priv_data->rx_buf;
+ memset(tx_buf, 0, XFER_BUFFER_SIZE);
+ memset(rx_buf, 0, XFER_BUFFER_SIZE);
+ priv_data->xfer_length = XFER_BUFFER_SIZE;
+
+ filter_request = ifr->ifr_data;
+ req = (struct spi_mosi *)tx_buf;
+
+ if (cmd == IOCTL_ADD_FRAME_FILTER)
+ req->cmd = CMD_CAN_ADD_FILTER;
+ else
+ req->cmd = CMD_CAN_REMOVE_FILTER;
+
+ req->len = sizeof(struct can_add_filter_req);
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+
+ add_filter = (struct can_add_filter_req *)req->data;
+ add_filter->can_if = filter_request->can_if;
+ add_filter->mid = filter_request->mid;
+ add_filter->mask = filter_request->mask;
+
+ ret = rh850_do_spi_transaction(priv_data);
+ mutex_unlock(&priv_data->spi_lock);
+
+ return ret;
+}
+
+static int rh850_send_spi_locked(struct rh850_can *priv_data, int cmd, int len,
+ u8 *data)
+{
+ char *tx_buf, *rx_buf;
+ struct spi_mosi *req;
+ int ret;
+
+ LOGDI("rh850_send_spi_locked\n");
+
+ tx_buf = priv_data->tx_buf;
+ rx_buf = priv_data->rx_buf;
+ memset(tx_buf, 0, XFER_BUFFER_SIZE);
+ memset(rx_buf, 0, XFER_BUFFER_SIZE);
+ priv_data->xfer_length = XFER_BUFFER_SIZE;
+
+ req = (struct spi_mosi *)tx_buf;
+ req->cmd = cmd;
+ req->len = len;
+ req->seq = atomic_inc_return(&priv_data->msg_seq);
+
+ if (unlikely(len > 64))
+ return -EINVAL;
+ memcpy(req->data, data, len);
+
+ ret = rh850_do_spi_transaction(priv_data);
+ return ret;
+}
+
+static int rh850_convert_ioctl_cmd_to_spi_cmd(int ioctl_cmd)
+{
+ switch (ioctl_cmd) {
+ case IOCTL_GET_FW_BR_VERSION:
+ return CMD_GET_FW_BR_VERSION;
+ case IOCTL_BEGIN_FIRMWARE_UPGRADE:
+ return CMD_BEGIN_FIRMWARE_UPGRADE;
+ case IOCTL_FIRMWARE_UPGRADE_DATA:
+ return CMD_FIRMWARE_UPGRADE_DATA;
+ case IOCTL_END_FIRMWARE_UPGRADE:
+ return CMD_END_FIRMWARE_UPGRADE;
+ case IOCTL_BEGIN_BOOT_ROM_UPGRADE:
+ return CMD_BEGIN_BOOT_ROM_UPGRADE;
+ case IOCTL_BOOT_ROM_UPGRADE_DATA:
+ return CMD_BOOT_ROM_UPGRADE_DATA;
+ case IOCTL_END_BOOT_ROM_UPGRADE:
+ return CMD_END_BOOT_ROM_UPGRADE;
+ }
+ return -EINVAL;
+}
+
+static int rh850_do_blocking_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ int spi_cmd, ret;
+
+ struct rh850_can *priv_data;
+ struct rh850_netdev_privdata *netdev_priv_data;
+ struct rh850_ioctl_req *ioctl_data;
+ int len = 0;
+ u8 *data = NULL;
+
+ netdev_priv_data = netdev_priv(netdev);
+ priv_data = netdev_priv_data->rh850_can;
+
+ spi_cmd = rh850_convert_ioctl_cmd_to_spi_cmd(cmd);
+ LOGDI("rh850_do_blocking_ioctl spi_cmd %x\n", spi_cmd);
+ if (spi_cmd < 0) {
+ LOGDE("rh850_do_blocking_ioctl wrong command %d\n", cmd);
+ return spi_cmd;
+ }
+ if (!ifr)
+ return -EINVAL;
+ ioctl_data = ifr->ifr_data;
+ /* Regular NULL check fails here as ioctl_data is at some offset */
+ if ((void *)ioctl_data > (void *)0x100) {
+ len = ioctl_data->len;
+ data = ioctl_data->data;
+ }
+ LOGDI("rh850_do_blocking_ioctl len %d\n", len);
+ mutex_lock(&priv_data->spi_lock);
+
+ priv_data->wait_cmd = spi_cmd;
+ priv_data->cmd_result = -1;
+ reinit_completion(&priv_data->response_completion);
+
+ ret = rh850_send_spi_locked(priv_data, spi_cmd, len, data);
+ mutex_unlock(&priv_data->spi_lock);
+
+ if (ret == 0) {
+ LOGDI("rh850_do_blocking_ioctl ready to wait for response\n");
+ wait_for_completion_interruptible_timeout(
+ &priv_data->response_completion, 5 * HZ);
+ ret = priv_data->cmd_result;
+ }
+ return ret;
+}
+
+static int rh850_netdev_do_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ struct rh850_can *priv_data;
+ struct rh850_netdev_privdata *netdev_priv_data;
+ int *mode;
+ int ret = -EINVAL;
+
+ netdev_priv_data = netdev_priv(netdev);
+ priv_data = netdev_priv_data->rh850_can;
+ LOGDI("rh850_netdev_do_ioctl %x\n", cmd);
+
+ switch (cmd) {
+ case IOCTL_RELEASE_CAN_BUFFER:
+ if (ifr->ifr_data > (void *)0x100) {
+ mode = ifr->ifr_data;
+ priv_data->driver_mode = *mode;
+ }
+ LOGDE("rh850_driver_mode %d\n", priv_data->driver_mode);
+ rh850_send_release_can_buffer_cmd(netdev);
+ ret = 0;
+ break;
+ case IOCTL_ENABLE_BUFFERING:
+ case IOCTL_DISABLE_BUFFERING:
+ rh850_data_buffering(netdev, ifr, cmd);
+ ret = 0;
+ break;
+ case IOCTL_DISABLE_ALL_BUFFERING:
+ rh850_remove_all_buffering(netdev);
+ ret = 0;
+ break;
+ case IOCTL_ADD_FRAME_FILTER:
+ case IOCTL_REMOVE_FRAME_FILTER:
+ rh850_frame_filter(netdev, ifr, cmd);
+ ret = 0;
+ break;
+ case IOCTL_GET_FW_BR_VERSION:
+ case IOCTL_BEGIN_FIRMWARE_UPGRADE:
+ case IOCTL_FIRMWARE_UPGRADE_DATA:
+ case IOCTL_END_FIRMWARE_UPGRADE:
+ case IOCTL_BEGIN_BOOT_ROM_UPGRADE:
+ case IOCTL_BOOT_ROM_UPGRADE_DATA:
+ case IOCTL_END_BOOT_ROM_UPGRADE:
+ ret = rh850_do_blocking_ioctl(netdev, ifr, cmd);
+ break;
+ }
+ LOGDI("rh850_netdev_do_ioctl ret %d\n", ret);
+
+ return ret;
+}
+
static const struct net_device_ops rh850_netdev_ops = {
.ndo_open = rh850_netdev_open,
.ndo_stop = rh850_netdev_close,
.ndo_start_xmit = rh850_netdev_start_xmit,
+ .ndo_do_ioctl = rh850_netdev_do_ioctl,
};
static int rh850_create_netdev(struct spi_device *spi,
netdev->netdev_ops = &rh850_netdev_ops;
SET_NETDEV_DEV(netdev, &spi->dev);
netdev_priv_data->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
- CAN_CTRLMODE_LISTENONLY;
+ CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_FD;
netdev_priv_data->can.bittiming_const = &rh850_bittiming_const;
+ netdev_priv_data->can.data_bittiming_const =
+ &rh850_data_bittiming_const;
netdev_priv_data->can.clock.freq = RH850_CLOCK;
+ netdev_priv_data->can.do_set_bittiming = rh850_set_bitrate;
return 0;
}
goto cleanup_privdata;
}
priv_data->xfer_length = 0;
+ priv_data->driver_mode = DRIVER_MODE_RAW_FRAMES;
mutex_init(&priv_data->spi_lock);
atomic_set(&priv_data->msg_seq, 0);
+ init_completion(&priv_data->response_completion);
return priv_data;
cleanup_privdata:
static const char ibmveth_driver_name[] = "ibmveth";
static const char ibmveth_driver_string[] = "IBM Power Virtual Ethernet Driver";
-#define ibmveth_driver_version "1.05"
+#define ibmveth_driver_version "1.06"
MODULE_AUTHOR("Santiago Leon <santil@linux.vnet.ibm.com>");
MODULE_DESCRIPTION("IBM Power Virtual Ethernet Driver");
return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_OFF_MASK;
}
+static inline int ibmveth_rxq_large_packet(struct ibmveth_adapter *adapter)
+{
+ return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_LRG_PKT;
+}
+
static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter)
{
return be32_to_cpu(adapter->rx_queue.queue_addr[adapter->rx_queue.index].length);
goto retry_bounce;
}
+static void ibmveth_rx_mss_helper(struct sk_buff *skb, u16 mss, int lrg_pkt)
+{
+ struct tcphdr *tcph;
+ int offset = 0;
+ int hdr_len;
+
+ /* only TCP packets will be aggregated */
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct iphdr *iph = (struct iphdr *)skb->data;
+
+ if (iph->protocol == IPPROTO_TCP) {
+ offset = iph->ihl * 4;
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+ } else {
+ return;
+ }
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ struct ipv6hdr *iph6 = (struct ipv6hdr *)skb->data;
+
+ if (iph6->nexthdr == IPPROTO_TCP) {
+ offset = sizeof(struct ipv6hdr);
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ /* if mss is not set through Large Packet bit/mss in rx buffer,
+ * expect that the mss will be written to the tcp header checksum.
+ */
+ tcph = (struct tcphdr *)(skb->data + offset);
+ if (lrg_pkt) {
+ skb_shinfo(skb)->gso_size = mss;
+ } else if (offset) {
+ skb_shinfo(skb)->gso_size = ntohs(tcph->check);
+ tcph->check = 0;
+ }
+
+ if (skb_shinfo(skb)->gso_size) {
+ hdr_len = offset + tcph->doff * 4;
+ skb_shinfo(skb)->gso_segs =
+ DIV_ROUND_UP(skb->len - hdr_len,
+ skb_shinfo(skb)->gso_size);
+ }
+}
+
static int ibmveth_poll(struct napi_struct *napi, int budget)
{
struct ibmveth_adapter *adapter =
int frames_processed = 0;
unsigned long lpar_rc;
struct iphdr *iph;
+ u16 mss = 0;
restart_poll:
while (frames_processed < budget) {
int length = ibmveth_rxq_frame_length(adapter);
int offset = ibmveth_rxq_frame_offset(adapter);
int csum_good = ibmveth_rxq_csum_good(adapter);
+ int lrg_pkt = ibmveth_rxq_large_packet(adapter);
skb = ibmveth_rxq_get_buffer(adapter);
+ /* if the large packet bit is set in the rx queue
+ * descriptor, the mss will be written by PHYP eight
+ * bytes from the start of the rx buffer, which is
+ * skb->data at this stage
+ */
+ if (lrg_pkt) {
+ __be64 *rxmss = (__be64 *)(skb->data + 8);
+
+ mss = (u16)be64_to_cpu(*rxmss);
+ }
+
new_skb = NULL;
if (length < rx_copybreak)
new_skb = netdev_alloc_skb(netdev, length);
if (iph->check == 0xffff) {
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
- adapter->rx_large_packets++;
}
}
}
+ if (length > netdev->mtu + ETH_HLEN) {
+ ibmveth_rx_mss_helper(skb, mss, lrg_pkt);
+ adapter->rx_large_packets++;
+ }
+
napi_gro_receive(napi, skb); /* send it up */
netdev->stats.rx_packets++;
#define IBMVETH_RXQ_TOGGLE 0x80000000
#define IBMVETH_RXQ_TOGGLE_SHIFT 31
#define IBMVETH_RXQ_VALID 0x40000000
+#define IBMVETH_RXQ_LRG_PKT 0x04000000
#define IBMVETH_RXQ_NO_CSUM 0x02000000
#define IBMVETH_RXQ_CSUM_GOOD 0x01000000
#define IBMVETH_RXQ_OFF_MASK 0x0000FFFF
{
struct mlx4_cq *cq;
+ rcu_read_lock();
cq = radix_tree_lookup(&mlx4_priv(dev)->cq_table.tree,
cqn & (dev->caps.num_cqs - 1));
+ rcu_read_unlock();
+
if (!cq) {
mlx4_dbg(dev, "Completion event for bogus CQ %08x\n", cqn);
return;
}
+ /* Acessing the CQ outside of rcu_read_lock is safe, because
+ * the CQ is freed only after interrupt handling is completed.
+ */
++cq->arm_sn;
cq->comp(cq);
struct mlx4_cq_table *cq_table = &mlx4_priv(dev)->cq_table;
struct mlx4_cq *cq;
- spin_lock(&cq_table->lock);
-
+ rcu_read_lock();
cq = radix_tree_lookup(&cq_table->tree, cqn & (dev->caps.num_cqs - 1));
- if (cq)
- atomic_inc(&cq->refcount);
-
- spin_unlock(&cq_table->lock);
+ rcu_read_unlock();
if (!cq) {
- mlx4_warn(dev, "Async event for bogus CQ %08x\n", cqn);
+ mlx4_dbg(dev, "Async event for bogus CQ %08x\n", cqn);
return;
}
+ /* Acessing the CQ outside of rcu_read_lock is safe, because
+ * the CQ is freed only after interrupt handling is completed.
+ */
cq->event(cq, event_type);
-
- if (atomic_dec_and_test(&cq->refcount))
- complete(&cq->free);
}
static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
if (err)
return err;
- spin_lock_irq(&cq_table->lock);
+ spin_lock(&cq_table->lock);
err = radix_tree_insert(&cq_table->tree, cq->cqn, cq);
- spin_unlock_irq(&cq_table->lock);
+ spin_unlock(&cq_table->lock);
if (err)
goto err_icm;
return 0;
err_radix:
- spin_lock_irq(&cq_table->lock);
+ spin_lock(&cq_table->lock);
radix_tree_delete(&cq_table->tree, cq->cqn);
- spin_unlock_irq(&cq_table->lock);
+ spin_unlock(&cq_table->lock);
err_icm:
mlx4_cq_free_icm(dev, cq->cqn);
if (err)
mlx4_warn(dev, "HW2SW_CQ failed (%d) for CQN %06x\n", err, cq->cqn);
+ spin_lock(&cq_table->lock);
+ radix_tree_delete(&cq_table->tree, cq->cqn);
+ spin_unlock(&cq_table->lock);
+
synchronize_irq(priv->eq_table.eq[MLX4_CQ_TO_EQ_VECTOR(cq->vector)].irq);
if (priv->eq_table.eq[MLX4_CQ_TO_EQ_VECTOR(cq->vector)].irq !=
priv->eq_table.eq[MLX4_EQ_ASYNC].irq)
synchronize_irq(priv->eq_table.eq[MLX4_EQ_ASYNC].irq);
- spin_lock_irq(&cq_table->lock);
- radix_tree_delete(&cq_table->tree, cq->cqn);
- spin_unlock_irq(&cq_table->lock);
-
if (atomic_dec_and_test(&cq->refcount))
complete(&cq->free);
wait_for_completion(&cq->free);
ring->cqn = priv->rx_cq[ring_ind]->mcq.cqn;
ring->stride = stride;
- if (ring->stride <= TXBB_SIZE)
+ if (ring->stride <= TXBB_SIZE) {
+ /* Stamp first unused send wqe */
+ __be32 *ptr = (__be32 *)ring->buf;
+ __be32 stamp = cpu_to_be32(1 << STAMP_SHIFT);
+ *ptr = stamp;
+ /* Move pointer to start of rx section */
ring->buf += TXBB_SIZE;
+ }
ring->log_stride = ffs(ring->stride) - 1;
ring->buf_size = ring->size * ring->stride;
put_res(dev, slave, srqn, RES_SRQ);
qp->srq = srq;
}
+
+ /* Save param3 for dynamic changes from VST back to VGT */
+ qp->param3 = qpc->param3;
put_res(dev, slave, rcqn, RES_CQ);
put_res(dev, slave, mtt_base, RES_MTT);
res_end_move(dev, slave, RES_QP, qpn);
int qpn = vhcr->in_modifier & 0x7fffff;
struct res_qp *qp;
u8 orig_sched_queue;
- __be32 orig_param3 = qpc->param3;
u8 orig_vlan_control = qpc->pri_path.vlan_control;
u8 orig_fvl_rx = qpc->pri_path.fvl_rx;
u8 orig_pri_path_fl = qpc->pri_path.fl;
*/
if (!err) {
qp->sched_queue = orig_sched_queue;
- qp->param3 = orig_param3;
qp->vlan_control = orig_vlan_control;
qp->fvl_rx = orig_fvl_rx;
qp->pri_path_fl = orig_pri_path_fl;
struct net_device *netdev;
struct catc *catc;
u8 broadcast[ETH_ALEN];
- int i, pktsz;
+ int pktsz, ret;
if (usb_set_interface(usbdev,
intf->altsetting->desc.bInterfaceNumber, 1)) {
if ((!catc->ctrl_urb) || (!catc->tx_urb) ||
(!catc->rx_urb) || (!catc->irq_urb)) {
dev_err(&intf->dev, "No free urbs available.\n");
- usb_free_urb(catc->ctrl_urb);
- usb_free_urb(catc->tx_urb);
- usb_free_urb(catc->rx_urb);
- usb_free_urb(catc->irq_urb);
- free_netdev(netdev);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail_free;
}
/* The F5U011 has the same vendor/product as the netmate but a device version of 0x130 */
catc->irq_buf, 2, catc_irq_done, catc, 1);
if (!catc->is_f5u011) {
+ u32 *buf;
+ int i;
+
dev_dbg(dev, "Checking memory size\n");
- i = 0x12345678;
- catc_write_mem(catc, 0x7a80, &i, 4);
- i = 0x87654321;
- catc_write_mem(catc, 0xfa80, &i, 4);
- catc_read_mem(catc, 0x7a80, &i, 4);
+ buf = kmalloc(4, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto fail_free;
+ }
+
+ *buf = 0x12345678;
+ catc_write_mem(catc, 0x7a80, buf, 4);
+ *buf = 0x87654321;
+ catc_write_mem(catc, 0xfa80, buf, 4);
+ catc_read_mem(catc, 0x7a80, buf, 4);
- switch (i) {
+ switch (*buf) {
case 0x12345678:
catc_set_reg(catc, TxBufCount, 8);
catc_set_reg(catc, RxBufCount, 32);
dev_dbg(dev, "32k Memory\n");
break;
}
+
+ kfree(buf);
dev_dbg(dev, "Getting MAC from SEEROM.\n");
usb_set_intfdata(intf, catc);
SET_NETDEV_DEV(netdev, &intf->dev);
- if (register_netdev(netdev) != 0) {
- usb_set_intfdata(intf, NULL);
- usb_free_urb(catc->ctrl_urb);
- usb_free_urb(catc->tx_urb);
- usb_free_urb(catc->rx_urb);
- usb_free_urb(catc->irq_urb);
- free_netdev(netdev);
- return -EIO;
- }
+ ret = register_netdev(netdev);
+ if (ret)
+ goto fail_clear_intfdata;
+
return 0;
+
+fail_clear_intfdata:
+ usb_set_intfdata(intf, NULL);
+fail_free:
+ usb_free_urb(catc->ctrl_urb);
+ usb_free_urb(catc->tx_urb);
+ usb_free_urb(catc->rx_urb);
+ usb_free_urb(catc->irq_urb);
+ free_netdev(netdev);
+ return ret;
}
static void catc_disconnect(struct usb_interface *intf)
static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
{
+ u8 *buf;
int ret;
+ buf = kmalloc(size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0),
PEGASUS_REQ_GET_REGS, PEGASUS_REQT_READ, 0,
- indx, data, size, 1000);
+ indx, buf, size, 1000);
if (ret < 0)
netif_dbg(pegasus, drv, pegasus->net,
"%s returned %d\n", __func__, ret);
+ else if (ret <= size)
+ memcpy(data, buf, ret);
+ kfree(buf);
return ret;
}
-static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
+static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size,
+ const void *data)
{
+ u8 *buf;
int ret;
+ buf = kmemdup(data, size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
PEGASUS_REQ_SET_REGS, PEGASUS_REQT_WRITE, 0,
- indx, data, size, 100);
+ indx, buf, size, 100);
if (ret < 0)
netif_dbg(pegasus, drv, pegasus->net,
"%s returned %d\n", __func__, ret);
+ kfree(buf);
return ret;
}
static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data)
{
+ u8 *buf;
int ret;
+ buf = kmemdup(&data, 1, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
PEGASUS_REQ_SET_REG, PEGASUS_REQT_WRITE, data,
- indx, &data, 1, 1000);
+ indx, buf, 1, 1000);
if (ret < 0)
netif_dbg(pegasus, drv, pegasus->net,
"%s returned %d\n", __func__, ret);
+ kfree(buf);
return ret;
}
*/
static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
{
- return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
- RTL8150_REQ_GET_REGS, RTL8150_REQT_READ,
- indx, 0, data, size, 500);
+ void *buf;
+ int ret;
+
+ buf = kmalloc(size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ RTL8150_REQ_GET_REGS, RTL8150_REQT_READ,
+ indx, 0, buf, size, 500);
+ if (ret > 0 && ret <= size)
+ memcpy(data, buf, ret);
+ kfree(buf);
+ return ret;
}
-static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
+static int set_registers(rtl8150_t * dev, u16 indx, u16 size, const void *data)
{
- return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
- RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE,
- indx, 0, data, size, 500);
+ void *buf;
+ int ret;
+
+ buf = kmemdup(data, size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE,
+ indx, 0, buf, size, 500);
+ kfree(buf);
+ return ret;
}
static void async_set_reg_cb(struct urb *urb)
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/service-notifier.h>
#include <soc/qcom/msm_qmi_interface.h>
+#include <soc/qcom/icnss.h>
#include <soc/qcom/service-locator.h>
#include "core.h"
#include "qmi.h"
int ret;
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+ unsigned long time_left;
ath10k_dbg(ar, ATH10K_DBG_SNOC,
"Mode: %d, config: %p, host_version: %s\n",
return ret;
}
- wait_event_timeout(ath10k_fw_ready_wait_event,
+ time_left = wait_event_timeout(
+ ath10k_fw_ready_wait_event,
(atomic_read(&qmi_cfg->fw_ready) &&
atomic_read(&qmi_cfg->server_connected)),
msecs_to_jiffies(ATH10K_SNOC_WLAN_FW_READY_TIMEOUT));
+ if (time_left == 0) {
+ ath10k_err(ar, "Wait for FW ready and server connect timed out\n");
+ return -ETIMEDOUT;
+ }
req.host_version_valid = 1;
strlcpy(req.host_version, host_version,
goto out_destroy_wq;
}
+ if (!icnss_is_fw_ready()) {
+ ath10k_err(ar, "failed to get fw ready indication\n");
+ ret = -EFAULT;
+ goto err_fw_ready;
+ }
+
+ atomic_set(&qmi_cfg->fw_ready, 1);
ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI service started successfully\n");
return 0;
+err_fw_ready:
+ qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01,
+ WLFW_SERVICE_INS_ID_V01,
+ &qmi_cfg->wlfw_clnt_nb);
out_destroy_wq:
destroy_workqueue(qmi_cfg->event_wq);
return ret;
if (!ar_snoc)
return -EINVAL;
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 removed\n", __func__);
+
ath10k_core_unregister(ar);
ath10k_snoc_pdr_unregister_notifier(ar);
ath10k_snoc_modem_ssr_unregister_notifier(ar);
ath10k_snoc_stop_qmi_service(ar);
ath10k_core_destroy(ar);
- ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 removed\n", __func__);
-
return 0;
}
if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
return 0;
+ if (!spec_priv->rfs_chan_spec_scan)
+ return 1;
+
/* Output buffers are full, no need to process anything
* since there is no space to put the result anyway
*/
void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)
{
- if (config_enabled(CONFIG_ATH9K_DEBUGFS)) {
+ if (config_enabled(CONFIG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) {
relay_close(spec_priv->rfs_chan_spec_scan);
spec_priv->rfs_chan_spec_scan = NULL;
}
debugfs_phy,
1024, 256, &rfs_spec_scan_cb,
NULL);
+ if (!spec_priv->rfs_chan_spec_scan)
+ return;
+
debugfs_create_file("spectral_scan_ctl",
S_IRUSR | S_IWUSR,
debugfs_phy, spec_priv,
module_param(disable_ap_sme, bool, 0444);
MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
+static bool ignore_reg_hints = true;
+module_param(ignore_reg_hints, bool, 0444);
+MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)");
+
#define CHAN60G(_channel, _flags) { \
.band = IEEE80211_BAND_60GHZ, \
.center_freq = 56160 + (2160 * (_channel)), \
wiphy->vendor_commands = wil_nl80211_vendor_commands;
wiphy->vendor_events = wil_nl80211_vendor_events;
wiphy->n_vendor_events = ARRAY_SIZE(wil_nl80211_vendor_events);
+
+ if (ignore_reg_hints) {
+ wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
+ wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
+ }
}
struct wireless_dev *wil_cfg80211_init(struct device *dev)
#else
void wcnss_prealloc_check_memory_leak(void) {}
#endif
+EXPORT_SYMBOL(wcnss_prealloc_check_memory_leak);
int wcnss_pre_alloc_reset(void)
{
return n;
}
+EXPORT_SYMBOL(wcnss_pre_alloc_reset);
int prealloc_memory_stats_show(struct seq_file *fp, void *data)
{
spin_lock_bh(&local->baplock);
res = hfa384x_setup_bap(dev, BAP0, rid, 0);
- if (!res)
- res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec));
+ if (res)
+ goto unlock;
+
+ res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec));
+ if (res)
+ goto unlock;
if (le16_to_cpu(rec.len) == 0) {
/* RID not available */
res = -ENODATA;
+ goto unlock;
}
rlen = (le16_to_cpu(rec.len) - 1) * 2;
- if (!res && exact_len && rlen != len) {
+ if (exact_len && rlen != len) {
printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: "
"rid=0x%04x, len=%d (expected %d)\n",
dev->name, rid, rlen, len);
res = -ENODATA;
}
- if (!res)
- res = hfa384x_from_bap(dev, BAP0, buf, len);
+ res = hfa384x_from_bap(dev, BAP0, buf, len);
+unlock:
spin_unlock_bh(&local->baplock);
mutex_unlock(&local->rid_bap_mtx);
rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len);
if (rc < 0)
goto out_unlock;
+ nvdimm_bus_unlock(&nvdimm_bus->dev);
+
if (copy_to_user(p, buf, buf_len))
rc = -EFAULT;
+
+ vfree(buf);
+ return rc;
+
out_unlock:
nvdimm_bus_unlock(&nvdimm_bus->dev);
out:
PCIE_DBG(pcie_dev, "RC%d: PM_Enter_L23 is NOT received\n",
pcie_dev->rc_idx);
- msm_pcie_disable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG);
-
if (pcie_dev->use_pinctrl && pcie_dev->pins_sleep)
pinctrl_select_state(pcie_dev->pinctrl,
pcie_dev->pins_sleep);
+ msm_pcie_disable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG);
+
PCIE_DBG(pcie_dev, "RC%d: exit\n", pcie_dev->rc_idx);
return ret;
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015,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
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
-#include <linux/power_supply.h>
+#include <linux/extcon.h>
#include <linux/regulator/consumer.h>
struct gpio_usbdetect {
struct platform_device *pdev;
struct regulator *vin;
- struct power_supply *usb_psy;
int vbus_det_irq;
+ int id_det_irq;
int gpio;
+ struct extcon_dev *extcon_dev;
+ int vbus_state;
+ bool id_state;
+};
+
+static const unsigned int gpio_usb_extcon_table[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_USB_CC,
+ EXTCON_USB_SPEED,
+ EXTCON_NONE,
};
static irqreturn_t gpio_usbdetect_vbus_irq(int irq, void *data)
{
struct gpio_usbdetect *usb = data;
- int vbus;
- union power_supply_propval pval = {0,};
- vbus = gpio_get_value(usb->gpio);
- if (vbus)
- pval.intval = POWER_SUPPLY_TYPE_USB;
- else
- pval.intval = POWER_SUPPLY_TYPE_UNKNOWN;
+ usb->vbus_state = gpio_get_value(usb->gpio);
+ if (usb->vbus_state) {
+ dev_dbg(&usb->pdev->dev, "setting vbus notification\n");
+ extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB, 1);
+ } else {
+ dev_dbg(&usb->pdev->dev, "setting vbus removed notification\n");
+ extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t gpio_usbdetect_id_irq(int irq, void *data)
+{
+ struct gpio_usbdetect *usb = data;
+ int ret;
+
+ ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL,
+ &usb->id_state);
+ if (ret < 0) {
+ dev_err(&usb->pdev->dev, "unable to read ID IRQ LINE\n");
+ return IRQ_HANDLED;
+ }
- power_supply_set_property(usb->usb_psy,
- POWER_SUPPLY_PROP_TYPE, &pval);
+ return IRQ_WAKE_THREAD;
+}
- pval.intval = vbus;
- power_supply_set_property(usb->usb_psy, POWER_SUPPLY_PROP_PRESENT,
- &pval);
+static irqreturn_t gpio_usbdetect_id_irq_thread(int irq, void *data)
+{
+ struct gpio_usbdetect *usb = data;
+
+ if (usb->id_state) {
+ dev_dbg(&usb->pdev->dev, "stopping usb host\n");
+ extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB_HOST, 0);
+ enable_irq(usb->vbus_det_irq);
+ } else {
+ dev_dbg(&usb->pdev->dev, "starting usb HOST\n");
+ disable_irq(usb->vbus_det_irq);
+ extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB_HOST, 1);
+ }
return IRQ_HANDLED;
}
+static const u32 gpio_usb_extcon_exclusive[] = {0x3, 0};
+
static int gpio_usbdetect_probe(struct platform_device *pdev)
{
struct gpio_usbdetect *usb;
- struct power_supply *usb_psy;
int rc;
- unsigned long flags;
-
- usb_psy = power_supply_get_by_name("usb");
- if (!usb_psy) {
- dev_dbg(&pdev->dev, "USB power_supply not found, deferring probe\n");
- return -EPROBE_DEFER;
- }
usb = devm_kzalloc(&pdev->dev, sizeof(*usb), GFP_KERNEL);
if (!usb)
return -ENOMEM;
usb->pdev = pdev;
- usb->usb_psy = usb_psy;
+
+ usb->extcon_dev = devm_extcon_dev_allocate(&pdev->dev,
+ gpio_usb_extcon_table);
+ if (IS_ERR(usb->extcon_dev)) {
+ dev_err(&pdev->dev, "failed to allocate a extcon device\n");
+ return PTR_ERR(usb->extcon_dev);
+ }
+
+ usb->extcon_dev->mutually_exclusive = gpio_usb_extcon_exclusive;
+ rc = devm_extcon_dev_register(&pdev->dev, usb->extcon_dev);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to register extcon device\n");
+ return rc;
+ }
if (of_get_property(pdev->dev.of_node, "vin-supply", NULL)) {
usb->vin = devm_regulator_get(&pdev->dev, "vin");
"qcom,vbus-det-gpio", 0);
if (usb->gpio < 0) {
dev_err(&pdev->dev, "Failed to get gpio: %d\n", usb->gpio);
- return usb->gpio;
+ rc = usb->gpio;
+ goto error;
}
rc = gpio_request(usb->gpio, "vbus-det-gpio");
if (rc < 0) {
dev_err(&pdev->dev, "Failed to request gpio: %d\n", rc);
- return rc;
+ goto error;
}
usb->vbus_det_irq = gpio_to_irq(usb->gpio);
if (usb->vbus_det_irq < 0) {
- if (usb->vin)
- regulator_disable(usb->vin);
- return usb->vbus_det_irq;
+ dev_err(&pdev->dev, "get vbus_det_irq failed\n");
+ rc = usb->vbus_det_irq;
+ goto error;
}
- rc = devm_request_irq(&pdev->dev, usb->vbus_det_irq,
- gpio_usbdetect_vbus_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "vbus_det_irq", usb);
+ rc = devm_request_threaded_irq(&pdev->dev, usb->vbus_det_irq,
+ NULL, gpio_usbdetect_vbus_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT, "vbus_det_irq", usb);
if (rc) {
dev_err(&pdev->dev, "request for vbus_det_irq failed: %d\n",
rc);
- if (usb->vin)
- regulator_disable(usb->vin);
- return rc;
+ goto error;
+ }
+
+ usb->id_det_irq = platform_get_irq_byname(pdev, "pmic_id_irq");
+ if (usb->id_det_irq < 0) {
+ dev_err(&pdev->dev, "get id_det_irq failed\n");
+ rc = usb->id_det_irq;
+ goto error;
+ }
+
+ rc = devm_request_threaded_irq(&pdev->dev, usb->id_det_irq,
+ gpio_usbdetect_id_irq,
+ gpio_usbdetect_id_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT, "id_det_irq", usb);
+ if (rc) {
+ dev_err(&pdev->dev, "request for id_det_irq failed: %d\n", rc);
+ goto error;
}
enable_irq_wake(usb->vbus_det_irq);
+ enable_irq_wake(usb->id_det_irq);
dev_set_drvdata(&pdev->dev, usb);
/* Read and report initial VBUS state */
- local_irq_save(flags);
gpio_usbdetect_vbus_irq(usb->vbus_det_irq, usb);
- local_irq_restore(flags);
return 0;
+
+error:
+ if (usb->vin)
+ regulator_disable(usb->vin);
+ return rc;
}
static int gpio_usbdetect_remove(struct platform_device *pdev)
disable_irq_wake(usb->vbus_det_irq);
disable_irq(usb->vbus_det_irq);
+ disable_irq_wake(usb->id_det_irq);
+ disable_irq(usb->id_det_irq);
if (usb->vin)
regulator_disable(usb->vin);
* pipe will be unsuspended as part of
* enabling IPA clocks
*/
+ mutex_lock(&ipa_ctx->sps_pm.sps_pm_lock);
if (!atomic_read(
&ipa_ctx->sps_pm.dec_clients)
) {
1);
ipa_sps_process_irq_schedule_rel();
}
+ mutex_unlock(&ipa_ctx->sps_pm.sps_pm_lock);
} else {
resource = ipa2_get_rm_resource_from_ep(i);
res = ipa_rm_request_resource_with_timer(
* pipe will be unsuspended as part of
* enabling IPA clocks
*/
+ mutex_lock(&ipa3_ctx->transport_pm.
+ transport_pm_mutex);
if (!atomic_read(
&ipa3_ctx->transport_pm.dec_clients)
) {
1);
ipa3_sps_process_irq_schedule_rel();
}
+ mutex_unlock(&ipa3_ctx->transport_pm.
+ transport_pm_mutex);
} else {
resource = ipa3_get_rm_resource_from_ep(i);
res =
struct ipa3_ep_context *ep;
int empty;
int result;
+ int i;
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
cancel_delayed_work_sync(&ep->sys->replenish_rx_work);
flush_workqueue(ep->sys->wq);
if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- result = ipa3_stop_gsi_channel(clnt_hdl);
+ /* channel stop might fail on timeout if IPA is busy */
+ for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
+ result = ipa3_stop_gsi_channel(clnt_hdl);
+ if (result == GSI_STATUS_SUCCESS)
+ break;
+
+ if (result != -GSI_STATUS_AGAIN &&
+ result != -GSI_STATUS_TIMED_OUT)
+ break;
+ }
+
if (result != GSI_STATUS_SUCCESS) {
IPAERR("GSI stop chan err: %d.\n", result);
BUG();
static bool workqueues_stopped;
static bool ipa3_modem_init_cmplt;
static bool first_time_handshake;
+struct mutex ipa3_qmi_lock;
+
/* QMI A5 service */
static struct msg_desc ipa3_indication_reg_req_desc = {
req->filter_spec_ex_list_len);
}
- /* cache the qmi_filter_request */
- memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_msg_cache[
- ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg]),
- req, sizeof(struct ipa_install_fltr_rule_req_msg_v01));
- ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg++;
- ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg %= 10;
+ mutex_lock(&ipa3_qmi_lock);
+ if (ipa3_qmi_ctx != NULL) {
+ /* cache the qmi_filter_request */
+ memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_msg_cache[
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg]),
+ req,
+ sizeof(struct ipa_install_fltr_rule_req_msg_v01));
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg++;
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg %= 10;
+ }
+ mutex_unlock(&ipa3_qmi_lock);
req_desc.max_msg_len = QMI_IPA_INSTALL_FILTER_RULE_REQ_MAX_MSG_LEN_V01;
req_desc.msg_id = QMI_IPA_INSTALL_FILTER_RULE_REQ_V01;
req->filter_spec_ex_list_len);
}
- /* cache the qmi_filter_request */
- memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_ex_msg_cache[
- ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg]),
- req, sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01));
- ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg++;
- ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg %= 10;
+ mutex_lock(&ipa3_qmi_lock);
+ if (ipa3_qmi_ctx != NULL) {
+ /* cache the qmi_filter_request */
+ memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_ex_msg_cache[
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg]),
+ req,
+ sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01));
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg++;
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg %= 10;
+ }
+ mutex_unlock(&ipa3_qmi_lock);
req_desc.max_msg_len =
QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01;
return -EINVAL;
}
- /* cache the qmi_filter_request */
- memcpy(&(ipa3_qmi_ctx->ipa_fltr_installed_notif_req_msg_cache[
- ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg]),
- req, sizeof(struct ipa_fltr_installed_notif_req_msg_v01));
- ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg++;
- ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg %= 10;
+ mutex_lock(&ipa3_qmi_lock);
+ if (ipa3_qmi_ctx != NULL) {
+ /* cache the qmi_filter_request */
+ memcpy(&(ipa3_qmi_ctx->ipa_fltr_installed_notif_req_msg_cache[
+ ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg]),
+ req,
+ sizeof(struct ipa_fltr_installed_notif_req_msg_v01));
+ ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg++;
+ ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg %= 10;
+ }
+ mutex_unlock(&ipa3_qmi_lock);
req_desc.max_msg_len =
QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01;
resp.resp.error, "ipa_stop_data_usage_quota_req_msg_v01");
}
+void ipa3_qmi_init(void)
+{
+ mutex_init(&ipa3_qmi_lock);
+}
+
+void ipa3_qmi_cleanup(void)
+{
+ mutex_destroy(&ipa3_qmi_lock);
+}
+
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-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
void ipa3_q6_handshake_complete(bool ssr_bootup);
+void ipa3_qmi_init(void);
+
+void ipa3_qmi_cleanup(void);
+
#else /* CONFIG_RMNET_IPA3 */
static inline int ipa3_qmi_service_init(uint32_t wan_platform_type)
static inline void ipa3_q6_handshake_complete(bool ssr_bootup) { }
+static inline void ipa3_qmi_init(void)
+{
+}
+
+static inline void ipa3_qmi_cleanup(void)
+{
+}
+
#endif /* CONFIG_RMNET_IPA3 */
#endif /* IPA_QMI_SERVICE_H */
IPADBG("Skipping endpoint configuration.\n");
}
+ ipa3_enable_data_path(ipa_ep_idx);
+
out->clnt_hdl = ipa_ep_idx;
if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client))
memset(&mem, 0, sizeof(mem));
+ if (IPA_CLIENT_IS_PROD(ep->client)) {
+ IPADBG("Calling gsi_stop_channel ch:%lu\n",
+ ep->gsi_chan_hdl);
+ res = gsi_stop_channel(ep->gsi_chan_hdl);
+ IPADBG("gsi_stop_channel ch: %lu returned %d\n",
+ ep->gsi_chan_hdl, res);
+ goto end_sequence;
+ }
+
for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
- IPADBG("Calling gsi_stop_channel\n");
+ IPADBG("Calling gsi_stop_channel ch:%lu\n",
+ ep->gsi_chan_hdl);
res = gsi_stop_channel(ep->gsi_chan_hdl);
- IPADBG("gsi_stop_channel returned %d\n", res);
+ IPADBG("gsi_stop_channel ch: %lu returned %d\n",
+ ep->gsi_chan_hdl, res);
if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT)
goto end_sequence;
- if (IPA_CLIENT_IS_CONS(ep->client)) {
- IPADBG("Inject a DMA_TASK with 1B packet to IPA\n");
- /* Send a 1B packet DMA_TASK to IPA and try again */
- res = ipa3_inject_dma_task_for_gsi();
- if (res) {
- IPAERR("Failed to inject DMA TASk for GSI\n");
- goto end_sequence;
- }
+ IPADBG("Inject a DMA_TASK with 1B packet to IPA\n");
+ /* Send a 1B packet DMA_TASK to IPA and try again */
+ res = ipa3_inject_dma_task_for_gsi();
+ if (res) {
+ IPAERR("Failed to inject DMA TASk for GSI\n");
+ goto end_sequence;
}
/* sleep for short period to flush IPA */
mutex_init(&rmnet_ipa3_ctx->pipe_handle_guard);
rmnet_ipa3_ctx->ipa3_to_apps_hdl = -1;
rmnet_ipa3_ctx->apps_to_ipa3_hdl = -1;
+
+ ipa3_qmi_init();
+
/* Register for Modem SSR */
rmnet_ipa3_ctx->subsys_notify_handle = subsys_notif_register_notifier(
SUBSYS_MODEM,
static void __exit ipa3_wwan_cleanup(void)
{
int ret;
+ ipa3_qmi_cleanup();
mutex_destroy(&rmnet_ipa3_ctx->pipe_handle_guard);
ret = subsys_notif_unregister_notifier(
rmnet_ipa3_ctx->subsys_notify_handle, &ipa3_ssr_notifier);
return status;
}
+#define ACER_WMID_ACCEL_HID "BST0001"
+
static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
void *ctx, void **retval)
{
+ struct acpi_device *dev;
+
+ if (!strcmp(ctx, "SENR")) {
+ if (acpi_bus_get_device(ah, &dev))
+ return AE_OK;
+ if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev)))
+ return AE_OK;
+ } else
+ return AE_OK;
+
*(acpi_handle *)retval = ah;
- return AE_OK;
+
+ return AE_CTRL_TERMINATE;
}
static int __init acer_wmi_get_handle(const char *name, const char *prop,
{
int err;
- err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
+ err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle);
if (err)
return err;
err = acer_wmi_input_setup();
if (err)
return err;
+ err = acer_wmi_accel_setup();
+ if (err)
+ return err;
}
- acer_wmi_accel_setup();
-
err = platform_driver_register(&acer_platform_driver);
if (err) {
pr_err("Unable to register platform driver\n");
return sprintf(buf, "%s\n", technology_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
- else if (off == POWER_SUPPLY_PROP_TYPE)
+ else if (off == POWER_SUPPLY_PROP_TYPE ||
+ off == POWER_SUPPLY_PROP_REAL_TYPE)
return sprintf(buf, "%s\n", type_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_SCOPE)
return sprintf(buf, "%s\n", scope_text[value.intval]);
POWER_SUPPLY_ATTR(connector_health),
POWER_SUPPLY_ATTR(ctm_current_max),
POWER_SUPPLY_ATTR(hw_current_max),
+ POWER_SUPPLY_ATTR(real_type),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
+#include <soc/at91/at91sam9_ddrsdr.h>
+
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
static void __iomem *at91_shdwc_base;
static struct clk *sclk;
+static void __iomem *mpddrc_base;
static void __init at91_wakeup_status(void)
{
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
}
+static void at91_lpddr_poweroff(void)
+{
+ asm volatile(
+ /* Align to cache lines */
+ ".balign 32\n\t"
+
+ /* Ensure AT91_SHDW_CR is in the TLB by reading it */
+ " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+ /* Power down SDRAM0 */
+ " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
+ /* Shutdown CPU */
+ " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+ " b .\n\t"
+ :
+ : "r" (mpddrc_base),
+ "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
+ "r" (at91_shdwc_base),
+ "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
+ : "r0");
+}
+
static int at91_poweroff_get_wakeup_mode(struct device_node *np)
{
const char *pm;
static int __init at91_poweroff_probe(struct platform_device *pdev)
{
struct resource *res;
+ struct device_node *np;
+ u32 ddr_type;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pm_power_off = at91_poweroff;
+ np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
+ if (!np)
+ return 0;
+
+ mpddrc_base = of_iomap(np, 0);
+ of_node_put(np);
+
+ if (!mpddrc_base)
+ return 0;
+
+ ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
+ if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
+ (ddr_type == AT91_DDRSDRC_MD_LPDDR3))
+ pm_power_off = at91_lpddr_poweroff;
+ else
+ iounmap(mpddrc_base);
+
return 0;
}
static int __exit at91_poweroff_remove(struct platform_device *pdev)
{
- if (pm_power_off == at91_poweroff)
+ if (pm_power_off == at91_poweroff ||
+ pm_power_off == at91_lpddr_poweroff)
pm_power_off = NULL;
clk_disable_unprepare(sclk);
return 0;
}
+static const struct of_device_id at91_ramc_of_match[] = {
+ { .compatible = "atmel,sama5d3-ddramc", },
+ { /* sentinel */ }
+};
+
static const struct of_device_id at91_poweroff_of_match[] = {
{ .compatible = "atmel,at91sam9260-shdwc", },
{ .compatible = "atmel,at91sam9rl-shdwc", },
if (sysfs_streq(buf, "full")) {
dload_type = SCM_DLOAD_FULLDUMP;
} else if (sysfs_streq(buf, "mini")) {
- if (!msm_minidump_enabled()) {
- pr_info("Minidump is not enabled\n");
+ if (!minidump_enabled) {
+ pr_err("Minidump is not enabled\n");
return -ENODEV;
}
dload_type = SCM_DLOAD_MINIDUMP;
} else {
- pr_info("Invalid value. Use 'full' or 'mini'\n");
+ pr_err("Invalid value. Use 'full' or 'mini'\n");
return -EINVAL;
}
if (!chip->main_psy)
return 0;
- if (chip->batt_psy) {
- rc = power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CURRENT_QNOVO,
- &pval);
- if (rc < 0) {
- pr_err("Couldn't get qnovo fcc, rc=%d\n", rc);
- return rc;
- }
-
- if (pval.intval != -EINVAL)
- total_fcc_ua = pval.intval;
- }
-
if (chip->pl_mode == POWER_SUPPLY_PL_NONE
|| get_effective_result_locked(chip->pl_disable_votable)) {
pval.intval = total_fcc_ua;
struct pl_data *chip = data;
union power_supply_propval pval = {0, };
int rc = 0;
- int effective_fv_uv = fv_uv;
if (fv_uv < 0)
return 0;
if (!chip->main_psy)
return 0;
- if (chip->batt_psy) {
- rc = power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
- &pval);
- if (rc < 0) {
- pr_err("Couldn't get qnovo fv, rc=%d\n", rc);
- return rc;
- }
-
- if (pval.intval != -EINVAL)
- effective_fv_uv = pval.intval;
- }
-
- pval.intval = effective_fv_uv;
+ pval.intval = fv_uv;
rc = power_supply_set_property(chip->main_psy,
POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
}
#define DEFAULT_RESTRICTED_CURRENT_UA 1000000
-static int pl_init(void)
+int qcom_batt_init(void)
{
struct pl_data *chip;
int rc = 0;
+ /* initialize just once */
+ if (the_chip) {
+ pr_err("was initialized earlier Failing now\n");
+ return -EINVAL;
+ }
+
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
goto unreg_notifier;
}
- return rc;
+ the_chip = chip;
+
+ return 0;
unreg_notifier:
power_supply_unreg_notifier(&chip->nb);
return rc;
}
-static void pl_deinit(void)
+void qcom_batt_deinit(void)
{
struct pl_data *chip = the_chip;
+ if (chip == NULL)
+ return;
+
+ cancel_work_sync(&chip->status_change_work);
+ cancel_delayed_work_sync(&chip->pl_taper_work);
+ cancel_work_sync(&chip->pl_disable_forever_work);
+
power_supply_unreg_notifier(&chip->nb);
destroy_votable(chip->pl_awake_votable);
destroy_votable(chip->pl_disable_votable);
destroy_votable(chip->fv_votable);
destroy_votable(chip->fcc_votable);
wakeup_source_unregister(chip->pl_ws);
+ the_chip = NULL;
kfree(chip);
}
-
-module_init(pl_init);
-module_exit(pl_deinit)
-
-MODULE_DESCRIPTION("");
-MODULE_LICENSE("GPL v2");
--- /dev/null
+/* 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 __BATTERY_H
+#define __BATTERY_H
+int qcom_batt_init(void);
+void qcom_batt_deinit(void);
+#endif /* __BATTERY_H */
#define STEP_CHARGING_MAX_STEPS 5
struct smb_dt_props {
- int fcc_ua;
int usb_icl_ua;
int dc_icl_ua;
int boost_threshold_ua;
- int fv_uv;
int wipower_max_uw;
int min_freq_khz;
int max_freq_khz;
"qcom,external-vconn");
rc = of_property_read_u32(node,
- "qcom,fcc-max-ua", &chip->dt.fcc_ua);
+ "qcom,fcc-max-ua", &chg->batt_profile_fcc_ua);
if (rc < 0)
- chip->dt.fcc_ua = -EINVAL;
+ chg->batt_profile_fcc_ua = -EINVAL;
rc = of_property_read_u32(node,
- "qcom,fv-max-uv", &chip->dt.fv_uv);
+ "qcom,fv-max-uv", &chg->batt_profile_fv_uv);
if (rc < 0)
- chip->dt.fv_uv = -EINVAL;
+ chg->batt_profile_fv_uv = -EINVAL;
rc = of_property_read_u32(node,
"qcom,usb-icl-ua", &chip->dt.usb_icl_ua);
POWER_SUPPLY_PROP_PE_START,
POWER_SUPPLY_PROP_CTM_CURRENT_MAX,
POWER_SUPPLY_PROP_HW_CURRENT_MAX,
+ POWER_SUPPLY_PROP_REAL_TYPE,
};
static int smb2_usb_get_prop(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_ONLINE:
rc = smblib_get_prop_usb_online(chg, val);
+ if (!val->intval)
+ break;
+
+ rc = smblib_get_prop_typec_mode(chg, val);
+ if ((val->intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT ||
+ chg->micro_usb_mode) &&
+ chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
+ val->intval = 0;
+ else
+ val->intval = 1;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
val->intval = chg->voltage_min_uv;
rc = smblib_get_prop_usb_current_max(chg, val);
break;
case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB_PD;
+ break;
+ case POWER_SUPPLY_PROP_REAL_TYPE:
if (chip->bad_part)
- val->intval = POWER_SUPPLY_TYPE_USB;
+ val->intval = POWER_SUPPLY_TYPE_USB_PD;
else
- val->intval = chg->usb_psy_desc.type;
+ val->intval = chg->real_charger_type;
break;
case POWER_SUPPLY_PROP_TYPEC_MODE:
if (chg->micro_usb_mode)
struct smb_charger *chg = &chip->chg;
chg->usb_psy_desc.name = "usb";
- chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
+ chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
chg->usb_psy_desc.properties = smb2_usb_props;
chg->usb_psy_desc.num_properties = ARRAY_SIZE(smb2_usb_props);
chg->usb_psy_desc.get_property = smb2_usb_get_prop;
usb_cfg.drv_data = chip;
usb_cfg.of_node = chg->dev->of_node;
- chg->usb_psy = devm_power_supply_register(chg->dev,
+ chg->usb_psy = power_supply_register(chg->dev,
&chg->usb_psy_desc,
&usb_cfg);
if (IS_ERR(chg->usb_psy)) {
return 0;
}
+/********************************
+ * USB PC_PORT PSY REGISTRATION *
+ ********************************/
+static enum power_supply_property smb2_usb_port_props[] = {
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int smb2_usb_port_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct smb2 *chip = power_supply_get_drvdata(psy);
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ rc = smblib_get_prop_usb_online(chg, val);
+ if (!val->intval)
+ break;
+
+ rc = smblib_get_prop_typec_mode(chg, val);
+ if ((val->intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT ||
+ chg->micro_usb_mode) &&
+ chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ default:
+ pr_err_ratelimited("Get prop %d is not supported in pc_port\n",
+ psp);
+ return -EINVAL;
+ }
+
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", psp, rc);
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int smb2_usb_port_set_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+
+ switch (psp) {
+ default:
+ pr_err_ratelimited("Set prop %d is not supported in pc_port\n",
+ psp);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const struct power_supply_desc usb_port_psy_desc = {
+ .name = "pc_port",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = smb2_usb_port_props,
+ .num_properties = ARRAY_SIZE(smb2_usb_port_props),
+ .get_property = smb2_usb_port_get_prop,
+ .set_property = smb2_usb_port_set_prop,
+};
+
+static int smb2_init_usb_port_psy(struct smb2 *chip)
+{
+ struct power_supply_config usb_port_cfg = {};
+ struct smb_charger *chg = &chip->chg;
+
+ usb_port_cfg.drv_data = chip;
+ usb_port_cfg.of_node = chg->dev->of_node;
+ chg->usb_port_psy = power_supply_register(chg->dev,
+ &usb_port_psy_desc,
+ &usb_port_cfg);
+ if (IS_ERR(chg->usb_port_psy)) {
+ pr_err("Couldn't register USB pc_port power supply\n");
+ return PTR_ERR(chg->usb_port_psy);
+ }
+
+ return 0;
+}
+
/*****************************
* USB MAIN PSY REGISTRATION *
*****************************/
rc = smblib_get_prop_fcc_delta(chg, val);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = get_effective_result(chg->usb_icl_votable);
+ rc = smblib_get_icl_current(chg, &val->intval);
break;
default:
pr_debug("get prop %d is not supported in usb-main\n", psp);
usb_main_cfg.drv_data = chip;
usb_main_cfg.of_node = chg->dev->of_node;
- chg->usb_main_psy = devm_power_supply_register(chg->dev,
+ chg->usb_main_psy = power_supply_register(chg->dev,
&usb_main_psy_desc,
&usb_main_cfg);
if (IS_ERR(chg->usb_main_psy)) {
dc_cfg.drv_data = chip;
dc_cfg.of_node = chg->dev->of_node;
- chg->dc_psy = devm_power_supply_register(chg->dev,
+ chg->dc_psy = power_supply_register(chg->dev,
&dc_psy_desc,
&dc_cfg);
if (IS_ERR(chg->dc_psy)) {
rc = smblib_get_prop_charge_qnovo_enable(chg, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
- val->intval = chg->qnovo_fv_uv;
+ val->intval = get_client_vote_locked(chg->fv_votable,
+ QNOVO_VOTER);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
rc = smblib_get_prop_batt_current_now(chg, val);
break;
case POWER_SUPPLY_PROP_CURRENT_QNOVO:
- val->intval = chg->qnovo_fcc_ua;
+ val->intval = get_client_vote_locked(chg->fcc_votable,
+ QNOVO_VOTER);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
val->intval = get_client_vote(chg->fcc_votable,
vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval);
+ chg->batt_profile_fv_uv = val->intval;
+ vote(chg->fv_votable, BATT_PROFILE_VOTER, true, val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
rc = smblib_set_prop_charge_qnovo_enable(chg, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
- chg->qnovo_fv_uv = val->intval;
- rc = rerun_election(chg->fv_votable);
+ if (val->intval == -EINVAL) {
+ vote(chg->fv_votable, BATT_PROFILE_VOTER,
+ true, chg->batt_profile_fv_uv);
+ vote(chg->fv_votable, QNOVO_VOTER, false, 0);
+ } else {
+ vote(chg->fv_votable, QNOVO_VOTER, true, val->intval);
+ vote(chg->fv_votable, BATT_PROFILE_VOTER, false, 0);
+ }
break;
case POWER_SUPPLY_PROP_CURRENT_QNOVO:
- chg->qnovo_fcc_ua = val->intval;
vote(chg->pl_disable_votable, PL_QNOVO_VOTER,
val->intval != -EINVAL && val->intval < 2000000, 0);
- rc = rerun_election(chg->fcc_votable);
+ if (val->intval == -EINVAL) {
+ vote(chg->fcc_votable, BATT_PROFILE_VOTER,
+ true, chg->batt_profile_fcc_ua);
+ vote(chg->fcc_votable, QNOVO_VOTER, false, 0);
+ } else {
+ vote(chg->fcc_votable, QNOVO_VOTER, true, val->intval);
+ vote(chg->fcc_votable, BATT_PROFILE_VOTER, false, 0);
+ }
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- vote(chg->fcc_votable, DEFAULT_VOTER, true, val->intval);
+ chg->batt_profile_fcc_ua = val->intval;
+ vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval);
break;
case POWER_SUPPLY_PROP_SET_SHIP_MODE:
/* Not in ship mode as long as the device is active */
case POWER_SUPPLY_PROP_DP_DM:
rc = smblib_dp_dm(chg, val->intval);
break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
+ rc = smblib_set_prop_input_current_limited(chg, val);
+ break;
default:
rc = -EINVAL;
}
case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
case POWER_SUPPLY_PROP_DP_DM:
case POWER_SUPPLY_PROP_RERUN_AICL:
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
return 1;
default:
break;
batt_cfg.drv_data = chg;
batt_cfg.of_node = chg->dev->of_node;
- chg->batt_psy = devm_power_supply_register(chg->dev,
+ chg->batt_psy = power_supply_register(chg->dev,
&batt_psy_desc,
&batt_cfg);
if (IS_ERR(chg->batt_psy)) {
if (chip->dt.no_battery)
chg->fake_capacity = 50;
- if (chip->dt.fcc_ua < 0)
- smblib_get_charge_param(chg, &chg->param.fcc, &chip->dt.fcc_ua);
+ if (chg->batt_profile_fcc_ua < 0)
+ smblib_get_charge_param(chg, &chg->param.fcc,
+ &chg->batt_profile_fcc_ua);
- if (chip->dt.fv_uv < 0)
- smblib_get_charge_param(chg, &chg->param.fv, &chip->dt.fv_uv);
+ if (chg->batt_profile_fv_uv < 0)
+ smblib_get_charge_param(chg, &chg->param.fv,
+ &chg->batt_profile_fv_uv);
smblib_get_charge_param(chg, &chg->param.usb_icl,
&chg->default_icl_ua);
vote(chg->dc_suspend_votable,
DEFAULT_VOTER, chip->dt.no_battery, 0);
vote(chg->fcc_votable,
- DEFAULT_VOTER, true, chip->dt.fcc_ua);
+ BATT_PROFILE_VOTER, true, chg->batt_profile_fcc_ua);
vote(chg->fv_votable,
- DEFAULT_VOTER, true, chip->dt.fv_uv);
+ BATT_PROFILE_VOTER, true, chg->batt_profile_fv_uv);
vote(chg->dc_icl_votable,
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
return rc;
}
+static void smb2_free_interrupts(struct smb_charger *chg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(smb2_irqs); i++) {
+ if (smb2_irqs[i].irq > 0) {
+ if (smb2_irqs[i].wake)
+ disable_irq_wake(smb2_irqs[i].irq);
+
+ devm_free_irq(chg->dev, smb2_irqs[i].irq,
+ smb2_irqs[i].irq_data);
+ }
+ }
+}
+
static void smb2_disable_interrupts(struct smb_charger *chg)
{
int i;
rc = smb2_init_usb_main_psy(chip);
if (rc < 0) {
- pr_err("Couldn't initialize usb psy rc=%d\n", rc);
+ pr_err("Couldn't initialize usb main psy rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ rc = smb2_init_usb_port_psy(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize usb pc_port psy rc=%d\n", rc);
goto cleanup;
}
device_init_wakeup(chg->dev, true);
pr_info("QPNP SMB2 probed successfully usb:present=%d type=%d batt:present = %d health = %d charge = %d\n",
- usb_present, chg->usb_psy_desc.type,
+ usb_present, chg->real_charger_type,
batt_present, batt_health, batt_charge_type);
return rc;
cleanup:
- smblib_deinit(chg);
- if (chg->usb_psy)
- power_supply_unregister(chg->usb_psy);
+ smb2_free_interrupts(chg);
if (chg->batt_psy)
power_supply_unregister(chg->batt_psy);
+ if (chg->usb_main_psy)
+ power_supply_unregister(chg->usb_main_psy);
+ if (chg->usb_psy)
+ power_supply_unregister(chg->usb_psy);
+ if (chg->usb_port_psy)
+ power_supply_unregister(chg->usb_port_psy);
+ if (chg->dc_psy)
+ power_supply_unregister(chg->dc_psy);
if (chg->vconn_vreg && chg->vconn_vreg->rdev)
- regulator_unregister(chg->vconn_vreg->rdev);
+ devm_regulator_unregister(chg->dev, chg->vconn_vreg->rdev);
if (chg->vbus_vreg && chg->vbus_vreg->rdev)
- regulator_unregister(chg->vbus_vreg->rdev);
+ devm_regulator_unregister(chg->dev, chg->vbus_vreg->rdev);
+
+ smblib_deinit(chg);
+
platform_set_drvdata(pdev, NULL);
return rc;
}
power_supply_unregister(chg->batt_psy);
power_supply_unregister(chg->usb_psy);
+ power_supply_unregister(chg->usb_port_psy);
regulator_unregister(chg->vconn_vreg->rdev);
regulator_unregister(chg->vbus_vreg->rdev);
#include <linux/qpnp/qpnp-revid.h>
#include <linux/input/qpnp-power-on.h>
#include <linux/irq.h>
+#include <linux/pmic-voter.h>
#include "smb-lib.h"
#include "smb-reg.h"
+#include "battery.h"
#include "storm-watch.h"
-#include <linux/pmic-voter.h>
#define smblib_err(chg, fmt, ...) \
pr_err("%s: %s: " fmt, chg->name, \
/* if PD is active, APSD is disabled so won't have a valid result */
if (chg->pd_active)
- chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
+ chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD;
else
- chg->usb_psy_desc.type = apsd_result->pst;
+ chg->real_charger_type = apsd_result->pst;
smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n",
apsd_result->name, chg->pd_active);
return rc;
}
+static int get_sdp_current(struct smb_charger *chg, int *icl_ua)
+{
+ int rc;
+ u8 icl_options;
+ bool usb3 = false;
+
+ rc = smblib_read(chg, USBIN_ICL_OPTIONS_REG, &icl_options);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get ICL options rc=%d\n", rc);
+ return rc;
+ }
+
+ usb3 = (icl_options & CFG_USB3P0_SEL_BIT);
+
+ if (icl_options & USB51_MODE_BIT)
+ *icl_ua = usb3 ? USBIN_900MA : USBIN_500MA;
+ else
+ *icl_ua = usb3 ? USBIN_150MA : USBIN_100MA;
+
+ return rc;
+}
+
int smblib_set_icl_current(struct smb_charger *chg, int icl_ua)
{
int rc = 0;
/* configure current */
if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
- && (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) {
+ && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) {
rc = set_sdp_current(chg, icl_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc);
/* remove override if no voters - hw defaults is desired */
override = false;
} else if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
- if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
/* For std cable with type = SDP never override */
override = false;
- else if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP
+ else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_CDP
&& icl_ua == 1500000)
/*
* For std cable with type = CDP override only if
return rc;
}
+int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua)
+{
+ int rc = 0;
+ u8 load_cfg;
+ bool override;
+ union power_supply_propval pval;
+
+ rc = smblib_get_prop_typec_mode(chg, &pval);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get typeC mode rc = %d\n", rc);
+ return rc;
+ }
+
+ if ((pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
+ || chg->micro_usb_mode)
+ && (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) {
+ rc = get_sdp_current(chg, icl_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get SDP ICL rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = smblib_read(chg, USBIN_LOAD_CFG_REG, &load_cfg);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get load cfg rc=%d\n", rc);
+ return rc;
+ }
+ override = load_cfg & ICL_OVERRIDE_AFTER_APSD_BIT;
+ if (!override)
+ return INT_MAX;
+
+ /* override is set */
+ rc = smblib_get_charge_param(chg, &chg->param.usb_icl, icl_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get HC ICL rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
/*********************
* VOTABLE CALLBACKS *
*********************/
u8 stat;
int rc;
+ if (chg->fake_input_current_limited >= 0) {
+ val->intval = chg->fake_input_current_limited;
+ return 0;
+ }
+
rc = smblib_read(chg, AICL_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc);
return rc;
}
+int smblib_set_prop_input_current_limited(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ chg->fake_input_current_limited = val->intval;
+ return 0;
+}
+
int smblib_rerun_aicl(struct smb_charger *chg)
{
int rc, settled_icl_ua;
int smblib_get_charge_current(struct smb_charger *chg,
int *total_current_ua)
{
- const struct apsd_result *apsd_result = smblib_update_usb_type(chg);
+ const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
union power_supply_propval val = {0, };
int rc = 0, typec_source_rd, current_ua;
bool non_compliant;
}
}
-static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising)
+void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg)
{
+ int rc;
+ u8 stat;
+ bool vbus_rising;
+
+ rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
+ return;
+ }
+
+ vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
+
if (vbus_rising)
smblib_cc2_sink_removal_exit(chg);
else
smblib_cc2_sink_removal_enter(chg);
+
+ power_supply_changed(chg->usb_psy);
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
+ vbus_rising ? "attached" : "detached");
}
#define PL_DELAY_MS 30000
if (chg->micro_usb_mode)
smblib_micro_usb_plugin(chg, vbus_rising);
- else
- smblib_typec_usb_plugin(chg, vbus_rising);
power_supply_changed(chg->usb_psy);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
struct smb_charger *chg = irq_data->parent_data;
mutex_lock(&chg->lock);
- smblib_usb_plugin_locked(chg);
+ if (chg->pd_hard_reset)
+ smblib_usb_plugin_hard_reset_locked(chg);
+ else
+ smblib_usb_plugin_locked(chg);
mutex_unlock(&chg->lock);
return IRQ_HANDLED;
}
int pulses;
power_supply_changed(chg->usb_main_psy);
- if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) {
rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg,
}
}
- if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP_3) {
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) {
rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg,
static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
bool rising, bool qc_charger)
{
- const struct apsd_result *apsd_result = smblib_update_usb_type(chg);
+ const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
/* Hold off PD only until hvdcp 2.0 detection timeout */
if (rising) {
int rc = 0;
chg->fcc_votable = find_votable("FCC");
- if (!chg->fcc_votable) {
- rc = -EPROBE_DEFER;
+ if (chg->fcc_votable == NULL) {
+ rc = -EINVAL;
+ smblib_err(chg, "Couldn't find FCC votable rc=%d\n", rc);
return rc;
}
chg->fv_votable = find_votable("FV");
- if (!chg->fv_votable) {
- rc = -EPROBE_DEFER;
+ if (chg->fv_votable == NULL) {
+ rc = -EINVAL;
+ smblib_err(chg, "Couldn't find FV votable rc=%d\n", rc);
return rc;
}
chg->usb_icl_votable = find_votable("USB_ICL");
if (!chg->usb_icl_votable) {
- rc = -EPROBE_DEFER;
+ rc = -EINVAL;
+ smblib_err(chg, "Couldn't find USB_ICL votable rc=%d\n", rc);
return rc;
}
chg->pl_disable_votable = find_votable("PL_DISABLE");
- if (!chg->pl_disable_votable) {
- rc = -EPROBE_DEFER;
+ if (chg->pl_disable_votable == NULL) {
+ rc = -EINVAL;
+ smblib_err(chg, "Couldn't find votable PL_DISABLE rc=%d\n", rc);
return rc;
}
vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
chg->fake_capacity = -EINVAL;
+ chg->fake_input_current_limited = -EINVAL;
switch (chg->mode) {
case PARALLEL_MASTER:
- chg->qnovo_fcc_ua = -EINVAL;
- chg->qnovo_fv_uv = -EINVAL;
+ rc = qcom_batt_init();
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't init qcom_batt_init rc=%d\n",
+ rc);
+ return rc;
+ }
+
rc = smblib_create_votables(chg);
if (rc < 0) {
smblib_err(chg, "Couldn't create votables rc=%d\n",
{
switch (chg->mode) {
case PARALLEL_MASTER:
+ cancel_work_sync(&chg->bms_update_work);
+ cancel_work_sync(&chg->rdstd_cc2_detach_work);
+ cancel_delayed_work_sync(&chg->hvdcp_detect_work);
+ cancel_delayed_work_sync(&chg->step_soc_req_work);
+ cancel_delayed_work_sync(&chg->clear_hdc_work);
+ cancel_work_sync(&chg->otg_oc_work);
+ cancel_work_sync(&chg->vconn_oc_work);
+ cancel_delayed_work_sync(&chg->otg_ss_done_work);
+ cancel_delayed_work_sync(&chg->icl_change_work);
+ cancel_delayed_work_sync(&chg->pl_enable_work);
+ cancel_work_sync(&chg->legacy_detection_work);
power_supply_unreg_notifier(&chg->nb);
smblib_destroy_votables(chg);
+ qcom_batt_deinit();
break;
case PARALLEL_SLAVE:
break;
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
#define CC2_WA_VOTER "CC2_WA_VOTER"
+#define QNOVO_VOTER "QNOVO_VOTER"
+#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
struct power_supply *bms_psy;
struct power_supply_desc usb_psy_desc;
struct power_supply *usb_main_psy;
+ struct power_supply *usb_port_psy;
+ enum power_supply_type real_charger_type;
/* notifiers */
struct notifier_block nb;
bool typec_present;
u8 typec_status[5];
bool typec_legacy_valid;
+ int fake_input_current_limited;
/* workaround flag */
u32 wa_flags;
/* extcon for VBUS / ID notification to USB for uUSB */
struct extcon_dev *extcon;
+ /* battery profile */
+ int batt_profile_fcc_ua;
+ int batt_profile_fv_uv;
+
/* qnovo */
- int qnovo_fcc_ua;
- int qnovo_fv_uv;
int usb_icl_delta_ua;
int pulse_cnt;
};
const union power_supply_propval *val);
int smblib_set_prop_system_temp_level(struct smb_charger *chg,
const union power_supply_propval *val);
+int smblib_set_prop_input_current_limited(struct smb_charger *chg,
+ const union power_supply_propval *val);
int smblib_get_prop_dc_present(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_dp_dm(struct smb_charger *chg, int val);
int smblib_rerun_aicl(struct smb_charger *chg);
int smblib_set_icl_current(struct smb_charger *chg, int icl_ua);
+int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua);
int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua);
int smblib_init(struct smb_charger *chg);
/* CHGR FREQ Peripheral registers */
#define FREQ_CLK_DIV_REG (CHGR_FREQ_BASE + 0x50)
-/* SMB1355 specific registers */
-#define SMB1355_TEMP_COMP_STATUS_REG (MISC_BASE + 0x07)
-#define SKIN_TEMP_RST_HOT_BIT BIT(6)
-#define SKIN_TEMP_UB_HOT_BIT BIT(5)
-#define SKIN_TEMP_LB_HOT_BIT BIT(4)
-#define DIE_TEMP_TSD_HOT_BIT BIT(3)
-#define DIE_TEMP_RST_HOT_BIT BIT(2)
-#define DIE_TEMP_UB_HOT_BIT BIT(1)
-#define DIE_TEMP_LB_HOT_BIT BIT(0)
-
#endif /* __SMB2_CHARGER_REG_H */
struct smb_dt_props dt;
struct power_supply *parallel_psy;
u32 wa_flags;
- struct pmic_revid_data *pmic_rev_id;
- char *name;
};
static int __debug_mask;
if (rc < 0)
chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID;
- /* check that smb1355 is configured to run in mid-mid mode */
- if (chip->pmic_rev_id->pmic_subtype == SMB1355_SUBTYPE
- && chip->dt.pl_mode != POWER_SUPPLY_PL_USBMID_USBMID) {
- pr_err("Smb1355 can only run in MID-MID mode, saw = %d mode\n",
- chip->dt.pl_mode);
- return -EINVAL;
- }
-
chip->dt.suspend_input = of_property_read_bool(node,
"qcom,suspend-input");
* PARALLEL PSY REGISTRATION *
*****************************/
-static int smb1355_get_prop_connector_health(struct smb138x *chip)
-{
- struct smb_charger *chg = &chip->chg;
- u8 temp;
- int rc;
-
- rc = smblib_read(chg, SMB1355_TEMP_COMP_STATUS_REG, &temp);
- if (rc < 0) {
- pr_err("Couldn't read comp stat reg rc = %d\n", rc);
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- }
-
- if (temp & SKIN_TEMP_RST_HOT_BIT)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
-
- if (temp & SKIN_TEMP_UB_HOT_BIT)
- return POWER_SUPPLY_HEALTH_HOT;
-
- if (temp & SKIN_TEMP_LB_HOT_BIT)
- return POWER_SUPPLY_HEALTH_WARM;
-
- return POWER_SUPPLY_HEALTH_COOL;
-}
-
static int smb138x_get_prop_connector_health(struct smb138x *chip)
{
struct smb_charger *chg = &chip->chg;
POWER_SUPPLY_PROP_PIN_ENABLED,
POWER_SUPPLY_PROP_INPUT_SUSPEND,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
- POWER_SUPPLY_PROP_MODEL_NAME,
- POWER_SUPPLY_PROP_PARALLEL_MODE,
- POWER_SUPPLY_PROP_CONNECTOR_HEALTH,
- POWER_SUPPLY_PROP_SET_SHIP_MODE,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGER_TEMP,
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_CURRENT_MAX,
-};
-
-static enum power_supply_property smb1355_parallel_props[] = {
- POWER_SUPPLY_PROP_CHARGE_TYPE,
- POWER_SUPPLY_PROP_CHARGING_ENABLED,
- POWER_SUPPLY_PROP_PIN_ENABLED,
- POWER_SUPPLY_PROP_INPUT_SUSPEND,
- POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
- POWER_SUPPLY_PROP_VOLTAGE_MAX,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_PARALLEL_MODE,
POWER_SUPPLY_PROP_CONNECTOR_HEALTH,
POWER_SUPPLY_PROP_SET_SHIP_MODE,
- POWER_SUPPLY_PROP_CHARGER_TEMP,
- POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
};
static int smb138x_parallel_get_prop(struct power_supply *psy,
else
val->intval = 0;
break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ rc = smblib_get_charge_param(chg, &chg->param.usb_icl,
+ &val->intval);
+ else
+ val->intval = 0;
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval);
break;
rc = smblib_get_charge_param(chg, &chg->param.fcc,
&val->intval);
break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ rc = smblib_get_prop_slave_current_now(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP:
+ rc = smb138x_get_prop_charger_temp(chip, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
+ rc = smblib_get_prop_charger_temp_max(chg, val);
+ break;
case POWER_SUPPLY_PROP_MODEL_NAME:
- val->strval = chip->name;
+ val->strval = "smb138x";
break;
case POWER_SUPPLY_PROP_PARALLEL_MODE:
val->intval = chip->dt.pl_mode;
break;
case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
- if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
- val->intval = smb138x_get_prop_connector_health(chip);
- else
- val->intval = smb1355_get_prop_connector_health(chip);
+ val->intval = smb138x_get_prop_connector_health(chip);
break;
case POWER_SUPPLY_PROP_SET_SHIP_MODE:
/* Not in ship mode as long as device is active */
val->intval = 0;
break;
- case POWER_SUPPLY_PROP_CHARGER_TEMP:
- if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
- rc = smb138x_get_prop_charger_temp(chip, val);
- else
- rc = smblib_get_prop_charger_temp(chg, val);
- break;
- case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
- rc = smblib_get_prop_charger_temp_max(chg, val);
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
- rc = smblib_get_prop_slave_current_now(chg, val);
- else
- rc = -ENODATA;
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- if ((chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE)
- && ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
- || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)))
- rc = smblib_get_charge_param(chg, &chg->param.usb_icl,
- &val->intval);
- else
- rc = -ENODATA;
- break;
default:
pr_err("parallel power supply get prop %d not supported\n",
prop);
return 0;
}
-static struct power_supply_desc parallel_psy_desc = {
+static const struct power_supply_desc parallel_psy_desc = {
.name = "parallel",
.type = POWER_SUPPLY_TYPE_PARALLEL,
.properties = smb138x_parallel_props,
return 0;
}
-static int smb1355_init_parallel_psy(struct smb138x *chip)
-{
- struct power_supply_config parallel_cfg = {};
- struct smb_charger *chg = &chip->chg;
-
- parallel_cfg.drv_data = chip;
- parallel_cfg.of_node = chg->dev->of_node;
-
- /* change to smb1355's property list */
- parallel_psy_desc.properties = smb1355_parallel_props;
- parallel_psy_desc.num_properties = ARRAY_SIZE(smb1355_parallel_props);
- chip->parallel_psy = devm_power_supply_register(chg->dev,
- ¶llel_psy_desc,
- ¶llel_cfg);
- if (IS_ERR(chip->parallel_psy)) {
- pr_err("Couldn't register parallel power supply\n");
- return PTR_ERR(chip->parallel_psy);
- }
-
- return 0;
-}
-
/******************************
* VBUS REGULATOR REGISTRATION *
******************************/
static int smb138x_setup_wa_flags(struct smb138x *chip)
{
+ struct pmic_revid_data *pmic_rev_id;
struct device_node *revid_dev_node;
revid_dev_node = of_parse_phandle(chip->chg.dev->of_node,
return -EINVAL;
}
- chip->pmic_rev_id = get_revid_data(revid_dev_node);
- if (IS_ERR_OR_NULL(chip->pmic_rev_id)) {
+ pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR_OR_NULL(pmic_rev_id)) {
/*
* the revid peripheral must be registered, any failure
* here only indicates that the rev-id module has not
return -EPROBE_DEFER;
}
- switch (chip->pmic_rev_id->pmic_subtype) {
+ switch (pmic_rev_id->pmic_subtype) {
case SMB1381_SUBTYPE:
- if (chip->pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */
+ if (pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */
chip->wa_flags |= OOB_COMP_WA_BIT;
break;
default:
pr_err("PMIC subtype %d not supported\n",
- chip->pmic_rev_id->pmic_subtype);
+ pmic_rev_id->pmic_subtype);
return -EINVAL;
}
chg->param = v1_params;
- chip->name = "smb1381";
rc = smblib_init(chg);
if (rc < 0) {
pr_err("Couldn't initialize smblib rc=%d\n", rc);
return rc;
}
-static int smb1355_slave_probe(struct smb138x *chip)
+static int smb138x_slave_probe(struct smb138x *chip)
{
struct smb_charger *chg = &chip->chg;
int rc = 0;
goto cleanup;
}
- rc = smb138x_parse_dt(chip);
- if (rc < 0) {
- pr_err("Couldn't parse device tree rc=%d\n", rc);
- goto cleanup;
- }
-
- rc = smb138x_init_slave_hw(chip);
- if (rc < 0) {
- pr_err("Couldn't initialize hardware rc=%d\n", rc);
- goto cleanup;
- }
-
- rc = smb1355_init_parallel_psy(chip);
- if (rc < 0) {
- pr_err("Couldn't initialize parallel psy rc=%d\n", rc);
- goto cleanup;
- }
-
- rc = smb138x_determine_initial_slave_status(chip);
- if (rc < 0) {
- pr_err("Couldn't determine initial status rc=%d\n", rc);
- goto cleanup;
- }
-
- rc = smb138x_request_interrupts(chip);
- if (rc < 0) {
- pr_err("Couldn't request interrupts rc=%d\n", rc);
- goto cleanup;
- }
-
- return 0;
-
-cleanup:
- smblib_deinit(chg);
- return rc;
-}
-
-static int smb1381_slave_probe(struct smb138x *chip)
-{
- struct smb_charger *chg = &chip->chg;
- int rc = 0;
-
- chg->param = v1_params;
-
- rc = smblib_init(chg);
- if (rc < 0) {
- pr_err("Couldn't initialize smblib rc=%d\n", rc);
- goto cleanup;
- }
chg->iio.temp_max_chan = iio_channel_get(chg->dev, "charger_temp_max");
if (IS_ERR(chg->iio.temp_max_chan)) {
rc = PTR_ERR(chg->iio.temp_max_chan);
goto cleanup;
}
- return 0;
+ return rc;
cleanup:
smblib_deinit(chg);
+ if (chip->parallel_psy)
+ power_supply_unregister(chip->parallel_psy);
+ if (chg->vbus_vreg && chg->vbus_vreg->rdev)
+ regulator_unregister(chg->vbus_vreg->rdev);
return rc;
}
-static int slave_probe(struct smb138x *chip)
-{
- struct device_node *revid_dev_node;
- int rc = 0;
-
- revid_dev_node = of_parse_phandle(chip->chg.dev->of_node,
- "qcom,pmic-revid", 0);
- if (!revid_dev_node) {
- pr_err("Missing qcom,pmic-revid property\n");
- return -EINVAL;
- }
-
- chip->pmic_rev_id = get_revid_data(revid_dev_node);
- if (IS_ERR_OR_NULL(chip->pmic_rev_id)) {
- /*
- * the revid peripheral must be registered, any failure
- * here only indicates that the rev-id module has not
- * probed yet.
- */
- return -EPROBE_DEFER;
- }
-
- switch (chip->pmic_rev_id->pmic_subtype) {
- case SMB1355_SUBTYPE:
- chip->name = "smb1355";
- rc = smb1355_slave_probe(chip);
- break;
- case SMB1381_SUBTYPE:
- chip->name = "smb1381";
- rc = smb1381_slave_probe(chip);
- break;
- default:
- pr_err("Unsupported pmic subtype = 0x%02x\n",
- chip->pmic_rev_id->pmic_subtype);
- rc = -EINVAL;
- }
-
- if (rc < 0) {
- if (rc != -EPROBE_DEFER)
- pr_err("Couldn't probe SMB138X rc=%d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
static const struct of_device_id match_table[] = {
{
- .compatible = "qcom,smb138x-charger",
- .data = (void *) PARALLEL_MASTER,
- },
- {
- .compatible = "qcom,smb138x-parallel-slave",
- .data = (void *) PARALLEL_SLAVE,
+ .compatible = "qcom,smb138x-charger",
+ .data = (void *) PARALLEL_MASTER
},
{
- .compatible = "qcom,smb1355-parallel-slave",
- .data = (void *) PARALLEL_SLAVE,
+ .compatible = "qcom,smb138x-parallel-slave",
+ .data = (void *) PARALLEL_SLAVE
},
{ },
};
rc = smb138x_master_probe(chip);
break;
case PARALLEL_SLAVE:
- rc = slave_probe(chip);
+ rc = smb138x_slave_probe(chip);
break;
default:
pr_err("Couldn't find a matching mode %d\n", chip->chg.mode);
goto cleanup;
}
- pr_info("%s probed successfully mode=%d pl_mode = %d\n",
- chip->name, chip->chg.mode, chip->dt.pl_mode);
+ pr_info("SMB138X probed successfully mode=%d\n", chip->chg.mode);
return rc;
cleanup:
This driver supports the control of different power rails of device
through regulator interface.
+config REGULATOR_ONSEMI_NCP6335D
+ tristate "OnSemi NCP6335D regulator support"
+ depends on I2C
+ help
+ This driver supports the OnSemi NCP6335D switching voltage regulator
+ (buck converter). The regulator is controlled using an I2C interface
+ and supports a programmable voltage range from 0.6V to 1.4V in steps
+ of 6.25mV.
+
config REGULATOR_PALMAS
tristate "TI Palmas PMIC Regulators"
depends on MFD_PALMAS
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
+obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
--- /dev/null
+/* Copyright (c) 2012-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.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
+#include <linux/string.h>
+
+/* registers */
+#define REG_NCP6335D_PID 0x03
+#define REG_NCP6335D_PROGVSEL1 0x10
+#define REG_NCP6335D_PROGVSEL0 0x11
+#define REG_NCP6335D_PGOOD 0x12
+#define REG_NCP6335D_TIMING 0x13
+#define REG_NCP6335D_COMMAND 0x14
+
+/* constraints */
+#define NCP6335D_MIN_VOLTAGE_UV 600000
+#define NCP6335D_STEP_VOLTAGE_UV 6250
+#define NCP6335D_VOLTAGE_STEPS 128
+#define NCP6335D_MIN_SLEW_NS 166
+#define NCP6335D_MAX_SLEW_NS 1333
+
+/* bits */
+#define NCP6335D_ENABLE BIT(7)
+#define NCP6335D_DVS_PWM_MODE BIT(5)
+#define NCP6335D_PWM_MODE1 BIT(6)
+#define NCP6335D_PWM_MODE0 BIT(7)
+#define NCP6335D_PGOOD_DISCHG BIT(4)
+#define NCP6335D_SLEEP_MODE BIT(4)
+
+#define NCP6335D_VOUT_SEL_MASK 0x7F
+#define NCP6335D_SLEW_MASK 0x18
+#define NCP6335D_SLEW_SHIFT 0x3
+
+struct ncp6335d_info {
+ struct regulator_dev *regulator;
+ struct regulator_init_data *init_data;
+ struct regmap *regmap;
+ struct device *dev;
+ unsigned int vsel_reg;
+ unsigned int vsel_backup_reg;
+ unsigned int mode_bit;
+ int curr_voltage;
+ int slew_rate;
+
+ unsigned int step_size;
+ unsigned int min_voltage;
+ unsigned int min_slew_ns;
+ unsigned int max_slew_ns;
+ unsigned int peek_poke_address;
+
+ struct dentry *debug_root;
+};
+
+static int delay_array[] = {10, 20, 30, 40, 50};
+
+static int ncp6335x_read(struct ncp6335d_info *dd, unsigned int reg,
+ unsigned int *val)
+{
+ int i = 0, rc = 0;
+
+ rc = regmap_read(dd->regmap, reg, val);
+ for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) {
+ pr_debug("Failed reading reg=%u - retry(%d)\n", reg, i);
+ msleep(delay_array[i]);
+ rc = regmap_read(dd->regmap, reg, val);
+ }
+
+ if (rc)
+ pr_err("Failed reading reg=%u rc=%d\n", reg, rc);
+
+ return rc;
+}
+
+static int ncp6335x_write(struct ncp6335d_info *dd, unsigned int reg,
+ unsigned int val)
+{
+ int i = 0, rc = 0;
+
+ rc = regmap_write(dd->regmap, reg, val);
+ for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) {
+ pr_debug("Failed writing reg=%u - retry(%d)\n", reg, i);
+ msleep(delay_array[i]);
+ rc = regmap_write(dd->regmap, reg, val);
+ }
+
+ if (rc)
+ pr_err("Failed writing reg=%u rc=%d\n", reg, rc);
+
+ return rc;
+}
+
+static int ncp6335x_update_bits(struct ncp6335d_info *dd, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ int i = 0, rc = 0;
+
+ rc = regmap_update_bits(dd->regmap, reg, mask, val);
+ for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) {
+ pr_debug("Failed updating reg=%u- retry(%d)\n", reg, i);
+ msleep(delay_array[i]);
+ rc = regmap_update_bits(dd->regmap, reg, mask, val);
+ }
+
+ if (rc)
+ pr_err("Failed updating reg=%u rc=%d\n", reg, rc);
+
+ return rc;
+}
+
+static void dump_registers(struct ncp6335d_info *dd,
+ unsigned int reg, const char *func)
+{
+ unsigned int val = 0;
+
+ ncp6335x_read(dd, reg, &val);
+ dev_dbg(dd->dev, "%s: NCP6335D: Reg = %x, Val = %x\n", func, reg, val);
+}
+
+static void ncp633d_slew_delay(struct ncp6335d_info *dd,
+ int prev_uV, int new_uV)
+{
+ u8 val;
+ int delay;
+
+ val = abs(prev_uV - new_uV) / dd->step_size;
+ delay = ((val * dd->slew_rate) / 1000) + 1;
+
+ dev_dbg(dd->dev, "Slew Delay = %d\n", delay);
+
+ udelay(delay);
+}
+
+static int ncp6335d_is_enabled(struct regulator_dev *rdev)
+{
+ int rc, val = 0;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = ncp6335x_read(dd, dd->vsel_reg, &val);
+ if (rc)
+ dev_err(dd->dev, "Unable to read enable register rc(%d)", rc);
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return ((val & NCP6335D_ENABLE) ? 1 : 0);
+}
+
+static int ncp6335d_enable(struct regulator_dev *rdev)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = ncp6335x_update_bits(dd, dd->vsel_reg,
+ NCP6335D_ENABLE, NCP6335D_ENABLE);
+ if (rc)
+ dev_err(dd->dev, "Unable to enable regualtor rc(%d)", rc);
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_disable(struct regulator_dev *rdev)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = ncp6335x_update_bits(dd, dd->vsel_reg,
+ NCP6335D_ENABLE, 0);
+ if (rc)
+ dev_err(dd->dev, "Unable to disable regualtor rc(%d)", rc);
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_get_voltage(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = ncp6335x_read(dd, dd->vsel_reg, &val);
+ if (rc) {
+ dev_err(dd->dev, "Unable to get volatge rc(%d)", rc);
+ return rc;
+ }
+ dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * dd->step_size) +
+ dd->min_voltage;
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return dd->curr_voltage;
+}
+
+static int ncp6335d_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ int rc, set_val, new_uV;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ set_val = DIV_ROUND_UP(min_uV - dd->min_voltage, dd->step_size);
+ new_uV = (set_val * dd->step_size) + dd->min_voltage;
+ if (new_uV > max_uV) {
+ dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ rc = ncp6335x_update_bits(dd, dd->vsel_reg,
+ NCP6335D_VOUT_SEL_MASK, (set_val & NCP6335D_VOUT_SEL_MASK));
+ if (rc) {
+ dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+ min_uV, max_uV);
+ } else {
+ ncp633d_slew_delay(dd, dd->curr_voltage, new_uV);
+ dd->curr_voltage = new_uV;
+ }
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ if (selector >= NCP6335D_VOLTAGE_STEPS)
+ return 0;
+
+ return selector * dd->step_size + dd->min_voltage;
+}
+
+static int ncp6335d_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ /* only FAST and NORMAL mode types are supported */
+ if (mode != REGULATOR_MODE_FAST && mode != REGULATOR_MODE_NORMAL) {
+ dev_err(dd->dev, "Mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND, dd->mode_bit,
+ (mode == REGULATOR_MODE_FAST) ? dd->mode_bit : 0);
+ if (rc) {
+ dev_err(dd->dev, "Unable to set operating mode rc(%d)", rc);
+ return rc;
+ }
+
+ rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND,
+ NCP6335D_DVS_PWM_MODE,
+ (mode == REGULATOR_MODE_FAST) ?
+ NCP6335D_DVS_PWM_MODE : 0);
+ if (rc)
+ dev_err(dd->dev, "Unable to set DVS trans. mode rc(%d)", rc);
+
+ dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+ return rc;
+}
+
+static unsigned int ncp6335d_get_mode(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = ncp6335x_read(dd, REG_NCP6335D_COMMAND, &val);
+ if (rc) {
+ dev_err(dd->dev, "Unable to get regulator mode rc(%d)\n", rc);
+ return rc;
+ }
+
+ dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+ if (val & dd->mode_bit)
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops ncp6335d_ops = {
+ .set_voltage = ncp6335d_set_voltage,
+ .get_voltage = ncp6335d_get_voltage,
+ .list_voltage = ncp6335d_list_voltage,
+ .is_enabled = ncp6335d_is_enabled,
+ .enable = ncp6335d_enable,
+ .disable = ncp6335d_disable,
+ .set_mode = ncp6335d_set_mode,
+ .get_mode = ncp6335d_get_mode,
+};
+
+static struct regulator_desc rdesc = {
+ .name = "ncp6335d",
+ .supply_name = "vin",
+ .owner = THIS_MODULE,
+ .n_voltages = NCP6335D_VOLTAGE_STEPS,
+ .ops = &ncp6335d_ops,
+};
+
+static int ncp6335d_restore_working_reg(struct device_node *node,
+ struct ncp6335d_info *dd)
+{
+ int ret;
+ unsigned int val;
+
+ /* Restore register from back up register */
+ ret = ncp6335x_read(dd, dd->vsel_backup_reg, &val);
+ if (ret < 0) {
+ dev_err(dd->dev, "Failed to get backup data from reg %d, ret = %d\n",
+ dd->vsel_backup_reg, ret);
+ return ret;
+ }
+
+ ret = ncp6335x_update_bits(dd, dd->vsel_reg,
+ NCP6335D_VOUT_SEL_MASK, val);
+ if (ret < 0) {
+ dev_err(dd->dev, "Failed to update working reg %d, ret = %d\n",
+ dd->vsel_reg, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ncp6335d_parse_gpio(struct device_node *node,
+ struct ncp6335d_info *dd)
+{
+ int ret = 0, gpio;
+ enum of_gpio_flags flags;
+
+ if (!of_find_property(node, "onnn,vsel-gpio", NULL))
+ return ret;
+
+ /* Get GPIO connected to vsel and set its output */
+ gpio = of_get_named_gpio_flags(node,
+ "onnn,vsel-gpio", 0, &flags);
+ if (!gpio_is_valid(gpio)) {
+ if (gpio != -EPROBE_DEFER)
+ dev_err(dd->dev, "Could not get vsel, ret = %d\n",
+ gpio);
+ return gpio;
+ }
+
+ ret = devm_gpio_request(dd->dev, gpio, "ncp6335d_vsel");
+ if (ret) {
+ dev_err(dd->dev, "Failed to obtain gpio %d ret = %d\n",
+ gpio, ret);
+ return ret;
+ }
+
+ ret = gpio_direction_output(gpio, flags & OF_GPIO_ACTIVE_LOW ? 0 : 1);
+ if (ret) {
+ dev_err(dd->dev, "Failed to set GPIO %d to: %s, ret = %d",
+ gpio, flags & OF_GPIO_ACTIVE_LOW ?
+ "GPIO_LOW" : "GPIO_HIGH", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ncp6335d_init(struct i2c_client *client, struct ncp6335d_info *dd,
+ const struct ncp6335d_platform_data *pdata)
+{
+ int rc;
+ unsigned int val;
+
+ switch (pdata->default_vsel) {
+ case NCP6335D_VSEL0:
+ dd->vsel_reg = REG_NCP6335D_PROGVSEL0;
+ dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL1;
+ dd->mode_bit = NCP6335D_PWM_MODE0;
+ break;
+ case NCP6335D_VSEL1:
+ dd->vsel_reg = REG_NCP6335D_PROGVSEL1;
+ dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL0;
+ dd->mode_bit = NCP6335D_PWM_MODE1;
+ break;
+ default:
+ dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel);
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(client->dev.of_node, "onnn,restore-reg")) {
+ rc = ncp6335d_restore_working_reg(client->dev.of_node, dd);
+ if (rc)
+ return rc;
+ }
+
+ rc = ncp6335d_parse_gpio(client->dev.of_node, dd);
+ if (rc)
+ return rc;
+
+ /* get the current programmed voltage */
+ rc = ncp6335x_read(dd, dd->vsel_reg, &val);
+ if (rc) {
+ dev_err(dd->dev, "Unable to get volatge rc(%d)", rc);
+ return rc;
+ }
+ dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) *
+ dd->step_size) + dd->min_voltage;
+
+ /* set discharge */
+ rc = ncp6335x_update_bits(dd, REG_NCP6335D_PGOOD,
+ NCP6335D_PGOOD_DISCHG,
+ (pdata->discharge_enable ?
+ NCP6335D_PGOOD_DISCHG : 0));
+ if (rc) {
+ dev_err(dd->dev, "Unable to set Active Discharge rc(%d)\n", rc);
+ return -EINVAL;
+ }
+
+ /* set slew rate */
+ if (pdata->slew_rate_ns < dd->min_slew_ns ||
+ pdata->slew_rate_ns > dd->max_slew_ns) {
+ dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns);
+ return -EINVAL;
+ }
+
+ dd->slew_rate = pdata->slew_rate_ns;
+ val = DIV_ROUND_UP(pdata->slew_rate_ns, dd->min_slew_ns);
+ val = ilog2(val);
+
+ rc = ncp6335x_update_bits(dd, REG_NCP6335D_TIMING,
+ NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT);
+ if (rc)
+ dev_err(dd->dev, "Unable to set slew rate rc(%d)\n", rc);
+
+ /* Set Sleep mode bit */
+ rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND,
+ NCP6335D_SLEEP_MODE, pdata->sleep_enable ?
+ NCP6335D_SLEEP_MODE : 0);
+ if (rc)
+ dev_err(dd->dev, "Unable to set sleep mode (%d)\n", rc);
+
+ dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+ dump_registers(dd, REG_NCP6335D_PROGVSEL0, __func__);
+ dump_registers(dd, REG_NCP6335D_TIMING, __func__);
+ dump_registers(dd, REG_NCP6335D_PGOOD, __func__);
+
+ return rc;
+}
+
+static struct regmap_config ncp6335d_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int ncp6335d_parse_dt(struct i2c_client *client,
+ struct ncp6335d_info *dd)
+{
+ int rc;
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,step-size", &dd->step_size);
+ if (rc < 0) {
+ dev_err(&client->dev, "step size missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,min-slew-ns", &dd->min_slew_ns);
+ if (rc < 0) {
+ dev_err(&client->dev, "min slew us missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,max-slew-ns", &dd->max_slew_ns);
+ if (rc < 0) {
+ dev_err(&client->dev, "max slew us missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,min-setpoint", &dd->min_voltage);
+ if (rc < 0) {
+ dev_err(&client->dev, "min set point missing: rc = %d.\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static struct ncp6335d_platform_data *
+ ncp6335d_get_of_platform_data(struct i2c_client *client)
+{
+ struct ncp6335d_platform_data *pdata = NULL;
+ struct regulator_init_data *init_data;
+ const char *mode_name;
+ int rc;
+
+ init_data = of_get_regulator_init_data(&client->dev,
+ client->dev.of_node, &rdesc);
+ if (!init_data) {
+ dev_err(&client->dev, "regulator init data is missing\n");
+ return pdata;
+ }
+
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct ncp6335d_platform_data), GFP_KERNEL);
+ if (!pdata)
+ return pdata;
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,vsel", &pdata->default_vsel);
+ if (rc < 0) {
+ dev_err(&client->dev, "onnn,vsel property missing: rc = %d.\n",
+ rc);
+ return NULL;
+ }
+
+ rc = of_property_read_u32(client->dev.of_node,
+ "onnn,slew-ns", &pdata->slew_rate_ns);
+ if (rc < 0) {
+ dev_err(&client->dev, "onnn,slew-ns property missing: rc = %d.\n",
+ rc);
+ return NULL;
+ }
+
+ pdata->discharge_enable = of_property_read_bool(client->dev.of_node,
+ "onnn,discharge-enable");
+
+ pdata->sleep_enable = of_property_read_bool(client->dev.of_node,
+ "onnn,sleep-enable");
+
+ pdata->init_data = init_data;
+
+ init_data->constraints.input_uV = init_data->constraints.max_uV;
+ init_data->constraints.valid_ops_mask =
+ REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE;
+ init_data->constraints.valid_modes_mask =
+ REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_FAST;
+
+ rc = of_property_read_string(client->dev.of_node, "onnn,mode",
+ &mode_name);
+ if (!rc) {
+ if (strcmp("pwm", mode_name) == 0) {
+ init_data->constraints.initial_mode =
+ REGULATOR_MODE_FAST;
+ } else if (strcmp("auto", mode_name) == 0) {
+ init_data->constraints.initial_mode =
+ REGULATOR_MODE_NORMAL;
+ } else {
+ dev_err(&client->dev, "onnn,mode, unknown regulator mode: %s\n",
+ mode_name);
+ return NULL;
+ }
+ }
+
+ return pdata;
+}
+
+static int get_reg(void *data, u64 *val)
+{
+ struct ncp6335d_info *dd = data;
+ int rc;
+ unsigned int temp = 0;
+
+ rc = ncp6335x_read(dd, dd->peek_poke_address, &temp);
+ if (rc < 0)
+ dev_err(dd->dev, "Couldn't read reg %x rc = %d\n",
+ dd->peek_poke_address, rc);
+ else
+ *val = temp;
+
+ return rc;
+}
+
+static int set_reg(void *data, u64 val)
+{
+ struct ncp6335d_info *dd = data;
+ int rc;
+ unsigned int temp = 0;
+
+ temp = (unsigned int) val;
+ rc = ncp6335x_write(dd, dd->peek_poke_address, temp);
+ if (rc < 0)
+ dev_err(dd->dev, "Couldn't write 0x%02x to 0x%02x rc= %d\n",
+ dd->peek_poke_address, temp, rc);
+
+ return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");
+
+static int ncp6335d_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc;
+ unsigned int val = 0;
+ struct ncp6335d_info *dd;
+ const struct ncp6335d_platform_data *pdata;
+ struct regulator_config config = { };
+
+ if (client->dev.of_node)
+ pdata = ncp6335d_get_of_platform_data(client);
+ else
+ pdata = client->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&client->dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
+
+ dd = devm_kzalloc(&client->dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ if (client->dev.of_node) {
+ rc = ncp6335d_parse_dt(client, dd);
+ if (rc)
+ return rc;
+ } else {
+ dd->step_size = NCP6335D_STEP_VOLTAGE_UV;
+ dd->min_voltage = NCP6335D_MIN_VOLTAGE_UV;
+ dd->min_slew_ns = NCP6335D_MIN_SLEW_NS;
+ dd->max_slew_ns = NCP6335D_MAX_SLEW_NS;
+ }
+
+ dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ dev_err(&client->dev, "Error allocating regmap\n");
+ return PTR_ERR(dd->regmap);
+ }
+
+ rc = ncp6335x_read(dd, REG_NCP6335D_PID, &val);
+ if (rc) {
+ dev_err(&client->dev, "Unable to identify NCP6335D, rc(%d)\n",
+ rc);
+ return rc;
+ }
+ dev_info(&client->dev, "Detected Regulator NCP6335D PID = %d\n", val);
+
+ dd->init_data = pdata->init_data;
+ dd->dev = &client->dev;
+ i2c_set_clientdata(client, dd);
+
+ rc = ncp6335d_init(client, dd, pdata);
+ if (rc) {
+ dev_err(&client->dev, "Unable to initialize the regulator\n");
+ return -EINVAL;
+ }
+
+ config.dev = &client->dev;
+ config.init_data = dd->init_data;
+ config.regmap = dd->regmap;
+ config.driver_data = dd;
+ config.of_node = client->dev.of_node;
+
+ dd->regulator = regulator_register(&rdesc, &config);
+
+ if (IS_ERR(dd->regulator)) {
+ dev_err(&client->dev, "Unable to register regulator rc(%ld)",
+ PTR_ERR(dd->regulator));
+
+ return PTR_ERR(dd->regulator);
+ }
+
+ dd->debug_root = debugfs_create_dir("ncp6335x", NULL);
+ if (!dd->debug_root)
+ dev_err(&client->dev, "Couldn't create debug dir\n");
+
+ if (dd->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO,
+ dd->debug_root,
+ &(dd->peek_poke_address));
+ if (!ent)
+ dev_err(&client->dev, "Couldn't create address debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO,
+ dd->debug_root, dd,
+ &poke_poke_debug_ops);
+ if (!ent)
+ dev_err(&client->dev, "Couldn't create data debug file rc = %d\n",
+ rc);
+ }
+
+ return 0;
+}
+
+static int ncp6335d_regulator_remove(struct i2c_client *client)
+{
+ struct ncp6335d_info *dd = i2c_get_clientdata(client);
+
+ regulator_unregister(dd->regulator);
+
+ debugfs_remove_recursive(dd->debug_root);
+
+ return 0;
+}
+
+static const struct of_device_id ncp6335d_match_table[] = {
+ { .compatible = "onnn,ncp6335d-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ncp6335d_match_table);
+
+static const struct i2c_device_id ncp6335d_id[] = {
+ {"ncp6335d", -1},
+ { },
+};
+
+static struct i2c_driver ncp6335d_regulator_driver = {
+ .driver = {
+ .name = "ncp6335d-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = ncp6335d_match_table,
+ },
+ .probe = ncp6335d_regulator_probe,
+ .remove = ncp6335d_regulator_remove,
+ .id_table = ncp6335d_id,
+};
+
+/**
+ * ncp6335d_regulator_init() - initialized ncp6335d regulator driver
+ * This function registers the ncp6335d regulator platform driver.
+ *
+ * Returns 0 on success or errno on failure.
+ */
+int __init ncp6335d_regulator_init(void)
+{
+ static bool initialized;
+
+ if (initialized)
+ return 0;
+
+ initialized = true;
+
+ return i2c_add_driver(&ncp6335d_regulator_driver);
+}
+EXPORT_SYMBOL(ncp6335d_regulator_init);
+subsys_initcall(ncp6335d_regulator_init);
+
+static void __exit ncp6335d_regulator_exit(void)
+{
+ i2c_del_driver(&ncp6335d_regulator_driver);
+}
+module_exit(ncp6335d_regulator_exit);
+MODULE_DESCRIPTION("OnSemi-NCP6335D regulator driver");
+MODULE_LICENSE("GPL v2");
#include <linux/bitrev.h>
#include <linux/bcd.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#define S35390A_CMD_STATUS1 0
#define S35390A_CMD_STATUS2 1
#define S35390A_ALRM_BYTE_HOURS 1
#define S35390A_ALRM_BYTE_MINS 2
+/* flags for STATUS1 */
#define S35390A_FLAG_POC 0x01
#define S35390A_FLAG_BLD 0x02
+#define S35390A_FLAG_INT2 0x04
#define S35390A_FLAG_24H 0x40
#define S35390A_FLAG_RESET 0x80
+
+/* flag for STATUS2 */
#define S35390A_FLAG_TEST 0x01
#define S35390A_INT2_MODE_MASK 0xF0
return 0;
}
-static int s35390a_reset(struct s35390a *s35390a)
+/*
+ * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset.
+ * To keep the information if an irq is pending, pass the value read from
+ * STATUS1 to the caller.
+ */
+static int s35390a_reset(struct s35390a *s35390a, char *status1)
{
- char buf[1];
-
- if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0)
- return -EIO;
-
- if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD)))
+ char buf;
+ int ret;
+ unsigned initcount = 0;
+
+ ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (*status1 & S35390A_FLAG_POC)
+ /*
+ * Do not communicate for 0.5 seconds since the power-on
+ * detection circuit is in operation.
+ */
+ msleep(500);
+ else if (!(*status1 & S35390A_FLAG_BLD))
+ /*
+ * If both POC and BLD are unset everything is fine.
+ */
return 0;
- buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H);
- buf[0] &= 0xf0;
- return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
+ /*
+ * At least one of POC and BLD are set, so reinitialise chip. Keeping
+ * this information in the hardware to know later that the time isn't
+ * valid is unfortunately not possible because POC and BLD are cleared
+ * on read. So the reset is best done now.
+ *
+ * The 24H bit is kept over reset, so set it already here.
+ */
+initialize:
+ *status1 = S35390A_FLAG_24H;
+ buf = S35390A_FLAG_RESET | S35390A_FLAG_24H;
+ ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
+
+ if (ret < 0)
+ return ret;
+
+ ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
+ if (ret < 0)
+ return ret;
+
+ if (buf & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) {
+ /* Try up to five times to reset the chip */
+ if (initcount < 5) {
+ ++initcount;
+ goto initialize;
+ } else
+ return -EIO;
+ }
+
+ return 1;
}
static int s35390a_disable_test_mode(struct s35390a *s35390a)
if (alm->time.tm_wday != -1)
buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80;
+ else
+ buf[S35390A_ALRM_BYTE_WDAY] = 0;
buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a,
alm->time.tm_hour) | 0x80;
char buf[3], sts;
int i, err;
+ /*
+ * initialize all members to -1 to signal the core that they are not
+ * defined by the hardware.
+ */
+ alm->time.tm_sec = -1;
+ alm->time.tm_min = -1;
+ alm->time.tm_hour = -1;
+ alm->time.tm_mday = -1;
+ alm->time.tm_mon = -1;
+ alm->time.tm_year = -1;
+ alm->time.tm_wday = -1;
+ alm->time.tm_yday = -1;
+ alm->time.tm_isdst = -1;
+
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
if (err < 0)
return err;
- if (bitrev8(sts) != S35390A_INT2_MODE_ALARM)
- return -EINVAL;
+ if ((bitrev8(sts) & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) {
+ /*
+ * When the alarm isn't enabled, the register to configure
+ * the alarm time isn't accessible.
+ */
+ alm->enabled = 0;
+ return 0;
+ } else {
+ alm->enabled = 1;
+ }
err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
if (err < 0)
return err;
/* This chip returns the bits of each byte in reverse order */
- for (i = 0; i < 3; ++i) {
+ for (i = 0; i < 3; ++i)
buf[i] = bitrev8(buf[i]);
- buf[i] &= ~0x80;
- }
- alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]);
- alm->time.tm_hour = s35390a_reg2hr(s35390a,
- buf[S35390A_ALRM_BYTE_HOURS]);
- alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]);
+ /*
+ * B0 of the three matching registers is an enable flag. Iff it is set
+ * the configured value is used for matching.
+ */
+ if (buf[S35390A_ALRM_BYTE_WDAY] & 0x80)
+ alm->time.tm_wday =
+ bcd2bin(buf[S35390A_ALRM_BYTE_WDAY] & ~0x80);
+
+ if (buf[S35390A_ALRM_BYTE_HOURS] & 0x80)
+ alm->time.tm_hour =
+ s35390a_reg2hr(s35390a,
+ buf[S35390A_ALRM_BYTE_HOURS] & ~0x80);
+
+ if (buf[S35390A_ALRM_BYTE_MINS] & 0x80)
+ alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS] & ~0x80);
+
+ /* alarm triggers always at s=0 */
+ alm->time.tm_sec = 0;
dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n",
__func__, alm->time.tm_min, alm->time.tm_hour,
static int s35390a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- int err;
+ int err, err_reset;
unsigned int i;
struct s35390a *s35390a;
struct rtc_time tm;
- char buf[1];
+ char buf, status1;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
}
}
- err = s35390a_reset(s35390a);
- if (err < 0) {
+ err_reset = s35390a_reset(s35390a, &status1);
+ if (err_reset < 0) {
+ err = err_reset;
dev_err(&client->dev, "error resetting chip\n");
goto exit_dummy;
}
- err = s35390a_disable_test_mode(s35390a);
- if (err < 0) {
- dev_err(&client->dev, "error disabling test mode\n");
- goto exit_dummy;
- }
-
- err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
- if (err < 0) {
- dev_err(&client->dev, "error checking 12/24 hour mode\n");
- goto exit_dummy;
- }
- if (buf[0] & S35390A_FLAG_24H)
+ if (status1 & S35390A_FLAG_24H)
s35390a->twentyfourhour = 1;
else
s35390a->twentyfourhour = 0;
- if (s35390a_get_datetime(client, &tm) < 0)
+ if (status1 & S35390A_FLAG_INT2) {
+ /* disable alarm (and maybe test mode) */
+ buf = 0;
+ err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1);
+ if (err < 0) {
+ dev_err(&client->dev, "error disabling alarm");
+ goto exit_dummy;
+ }
+ } else {
+ err = s35390a_disable_test_mode(s35390a);
+ if (err < 0) {
+ dev_err(&client->dev, "error disabling test mode\n");
+ goto exit_dummy;
+ }
+ }
+
+ if (err_reset > 0 || s35390a_get_datetime(client, &tm) < 0)
dev_warn(&client->dev, "clock needs to be set\n");
device_set_wakeup_capable(&client->dev, 1);
err = PTR_ERR(s35390a->rtc);
goto exit_dummy;
}
+
+ if (status1 & S35390A_FLAG_INT2)
+ rtc_update_irq(s35390a->rtc, 1, RTC_AF);
+
return 0;
exit_dummy:
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/kernel.h>
+#include <linux/clk.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
struct platform_device *pdev;
struct rtc_device *rtc_dev;
void __iomem *rtc_base; /* NULL if not initialized. */
+ struct clk *clk;
int tegra_rtc_irq; /* alarm and periodic irq */
spinlock_t tegra_rtc_lock;
};
if (info->tegra_rtc_irq <= 0)
return -EBUSY;
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk))
+ return PTR_ERR(info->clk);
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret < 0)
+ return ret;
+
/* set context info. */
info->pdev = pdev;
spin_lock_init(&info->tegra_rtc_lock);
ret = PTR_ERR(info->rtc_dev);
dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
ret);
- return ret;
+ goto disable_clk;
}
ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
dev_err(&pdev->dev,
"Unable to request interrupt for device (err=%d).\n",
ret);
- return ret;
+ goto disable_clk;
}
dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
return 0;
+
+disable_clk:
+ clk_disable_unprepare(info->clk);
+ return ret;
+}
+
+static int tegra_rtc_remove(struct platform_device *pdev)
+{
+ struct tegra_rtc_info *info = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(info->clk);
+
+ return 0;
}
#ifdef CONFIG_PM_SLEEP
MODULE_ALIAS("platform:tegra_rtc");
static struct platform_driver tegra_rtc_driver = {
+ .remove = tegra_rtc_remove,
.shutdown = tegra_rtc_shutdown,
.driver = {
.name = "tegra_rtc",
task->num_scatter = qc->n_elem;
} else {
for_each_sg(qc->sg, sg, qc->n_elem, si)
- xfer += sg->length;
+ xfer += sg_dma_len(sg);
task->total_xfer_len = xfer;
task->num_scatter = si;
* @eedp_enable: eedp support enable bit
* @eedp_type: 0(type_1), 1(type_2), 2(type_3)
* @eedp_block_length: block size
+ * @ata_command_pending: SATL passthrough outstanding for device
*/
struct MPT3SAS_DEVICE {
struct MPT3SAS_TARGET *sas_target;
u8 configured_lun;
u8 block;
u8 tlr_snoop_check;
+ /*
+ * Bug workaround for SATL handling: the mpt2/3sas firmware
+ * doesn't return BUSY or TASK_SET_FULL for subsequent
+ * commands while a SATL pass through is in operation as the
+ * spec requires, it simply does nothing with them until the
+ * pass through completes, causing them possibly to timeout if
+ * the passthrough is a long executing command (like format or
+ * secure erase). This variable allows us to do the right
+ * thing while a SATL command is pending.
+ */
+ unsigned long ata_command_pending;
};
#define MPT3_CMD_NOT_USED 0x8000 /* free */
}
}
-static inline bool ata_12_16_cmd(struct scsi_cmnd *scmd)
+static int _scsih_set_satl_pending(struct scsi_cmnd *scmd, bool pending)
{
- return (scmd->cmnd[0] == ATA_12 || scmd->cmnd[0] == ATA_16);
+ struct MPT3SAS_DEVICE *priv = scmd->device->hostdata;
+
+ if (scmd->cmnd[0] != ATA_12 && scmd->cmnd[0] != ATA_16)
+ return 0;
+
+ if (pending)
+ return test_and_set_bit(0, &priv->ata_command_pending);
+
+ clear_bit(0, &priv->ata_command_pending);
+ return 0;
}
/**
if (!scmd)
continue;
count++;
- if (ata_12_16_cmd(scmd))
- scsi_internal_device_unblock(scmd->device,
- SDEV_RUNNING);
+ _scsih_set_satl_pending(scmd, false);
mpt3sas_base_free_smid(ioc, smid);
scsi_dma_unmap(scmd);
if (ioc->pci_error_recovery)
if (ioc->logging_level & MPT_DEBUG_SCSI)
scsi_print_command(scmd);
- /*
- * Lock the device for any subsequent command until command is
- * done.
- */
- if (ata_12_16_cmd(scmd))
- scsi_internal_device_block(scmd->device);
-
sas_device_priv_data = scmd->device->hostdata;
if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
scmd->result = DID_NO_CONNECT << 16;
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 */
if (scmd == NULL)
return 1;
- if (ata_12_16_cmd(scmd))
- scsi_internal_device_unblock(scmd->device, SDEV_RUNNING);
+ _scsih_set_satl_pending(scmd, false);
mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
#define READ_CAPACITY_RETRIES_ON_RESET 10
+/*
+ * Ensure that we don't overflow sector_t when CONFIG_LBDAF is not set
+ * and the reported logical block size is bigger than 512 bytes. Note
+ * that last_sector is a u64 and therefore logical_to_sectors() is not
+ * applicable.
+ */
+static bool sd_addressable_capacity(u64 lba, unsigned int sector_size)
+{
+ u64 last_sector = (lba + 1ULL) << (ilog2(sector_size) - 9);
+
+ if (sizeof(sector_t) == 4 && last_sector > U32_MAX)
+ return false;
+
+ return true;
+}
+
static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
unsigned char *buffer)
{
return -ENODEV;
}
- if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
+ if (!sd_addressable_capacity(lba, sector_size)) {
sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
"kernel compiled with support for large block "
"devices.\n");
return sector_size;
}
- if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
+ if (!sd_addressable_capacity(lba, sector_size)) {
sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
"kernel compiled with support for large block "
"devices.\n");
rw_max = q->limits.io_opt =
sdkp->opt_xfer_blocks * sdp->sector_size;
else
- rw_max = BLK_DEF_MAX_SECTORS;
+ rw_max = min_not_zero(logical_to_sectors(sdp, dev_max),
+ (sector_t)BLK_DEF_MAX_SECTORS);
/* Combine with controller limits */
q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q));
result = get_user(val, ip);
if (result)
return result;
+ if (val > SG_MAX_CDB_SIZE)
+ return -ENOMEM;
sfp->next_cmd_len = (val > 0) ? val : 0;
return 0;
case SG_GET_VERSION_NUM:
unsigned char *buffer;
struct scsi_mode_data data;
struct scsi_sense_hdr sshdr;
+ unsigned int ms_len = 128;
int rc, n;
static const char *loadmech[] =
scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
/* ask for mode page 0x2a */
- rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
+ rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, ms_len,
SR_TIMEOUT, 3, &data, NULL);
- if (!scsi_status_is_good(rc)) {
+ if (!scsi_status_is_good(rc) || data.length > ms_len ||
+ data.header_length + data.block_descriptor_length > data.length) {
/* failed, drive doesn't have capabilities mode page */
cd->cdi.speed = 1;
cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R |
driver allows configuring SPM to allow different low power modes for
both core and L2.
+config MSM_L2_SPM
+ bool "SPM support for L2 cache"
+ help
+ Enable SPM driver support for L2 cache. Some MSM chipsets allow
+ control of L2 cache low power mode with a Subsystem Power manager.
+ Enabling this driver allows configuring L2 SPM for low power modes
+ on supported chipsets
+
config QCOM_SCM
bool "Secure Channel Manager (SCM) support"
default n
This figures are reported in mpm sleep clock cycles and have a
resolution of 31 bits as 1 bit is used as an overflow check.
+config MSM_BOOT_TIME_MARKER
+ bool "Use MSM boot time marker reporting"
+ depends on MSM_BOOT_STATS
+ help
+ Use this to mark msm boot kpi for measurement.
+ An instrumentation for boot time measurement.
+ To create an entry, call "place_marker" function.
+ At userspace, write marker name to "/sys/kernel/debug/bootkpi/kpi_values"
+ If unsure, say N
+
config QCOM_CPUSS_DUMP
bool "CPU Subsystem Dumping support"
help
clients are going to cross their thresholds then Cx ipeak hw module will raise
an interrupt to cDSP block to throttle cDSP fmax.
+config MSM_CACHE_M4M_ERP64
+ bool "Cache and M4M error report"
+ depends on ARCH_MSM8996
+ help
+ Say 'Y' here to enable reporting of cache and M4M errors to the kernel
+ log. The kernel log contains collected error syndrome and address
+ registers. These register dumps can be used as useful information
+ to find out possible hardware problems.
+
+config MSM_CACHE_M4M_ERP64_PANIC_ON_CE
+ bool "Panic on correctable cache/M4M errors"
+ help
+ Say 'Y' here to cause kernel panic when correctable cache/M4M errors
+ are detected. Enabling this is useful when you want to dump memory
+ and system state close to the time when the error occured.
+
+ If unsure, say N.
+
+config MSM_CACHE_M4M_ERP64_PANIC_ON_UE
+ bool "Panic on uncorrectable cache/M4M errors"
+ help
+ Say 'Y' here to cause kernel panic when uncorrectable cache/M4M errors
+ are detected.
+
source "drivers/soc/qcom/memshare/Kconfig"
obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o
obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o
obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
+obj-$(CONFIG_MSM_BOOT_TIME_MARKER) += boot_marker.o
obj-$(CONFIG_MSM_AVTIMER) += avtimer.o
ifdef CONFIG_ARCH_MSM8996
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_kryo.o
obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o
obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o
obj-$(CONFIG_QCOM_CX_IPEAK) += cx_ipeak.o
+obj-$(CONFIG_MSM_CACHE_M4M_ERP64) += cache_m4m_erp64.o
--- /dev/null
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <soc/qcom/boot_stats.h>
+
+#define MAX_STRING_LEN 256
+#define BOOT_MARKER_MAX_LEN 40
+static struct dentry *dent_bkpi, *dent_bkpi_status;
+static struct boot_marker boot_marker_list;
+
+struct boot_marker {
+ char marker_name[BOOT_MARKER_MAX_LEN];
+ unsigned long long int timer_value;
+ struct list_head list;
+ struct mutex lock;
+};
+
+static void _create_boot_marker(const char *name,
+ unsigned long long int timer_value)
+{
+ struct boot_marker *new_boot_marker;
+
+ pr_debug("%-41s:%llu.%03llu seconds\n", name,
+ timer_value/TIMER_KHZ,
+ ((timer_value % TIMER_KHZ)
+ * 1000) / TIMER_KHZ);
+
+ new_boot_marker = kmalloc(sizeof(*new_boot_marker), GFP_KERNEL);
+ if (!new_boot_marker)
+ return;
+
+ strlcpy(new_boot_marker->marker_name, name,
+ sizeof(new_boot_marker->marker_name));
+ new_boot_marker->timer_value = timer_value;
+
+ mutex_lock(&boot_marker_list.lock);
+ list_add_tail(&(new_boot_marker->list), &(boot_marker_list.list));
+ mutex_unlock(&boot_marker_list.lock);
+}
+
+static void set_bootloader_stats(void)
+{
+ _create_boot_marker("M - APPSBL Start - ",
+ readl_relaxed(&boot_stats->bootloader_start));
+ _create_boot_marker("M - APPSBL Display Init - ",
+ readl_relaxed(&boot_stats->bootloader_display));
+ _create_boot_marker("M - APPSBL Early-Domain Start - ",
+ readl_relaxed(&boot_stats->bootloader_early_domain_start));
+ _create_boot_marker("D - APPSBL Kernel Load Time - ",
+ readl_relaxed(&boot_stats->bootloader_load_kernel));
+ _create_boot_marker("D - APPSBL Kernel Auth Time - ",
+ readl_relaxed(&boot_stats->bootloader_checksum));
+ _create_boot_marker("M - APPSBL End - ",
+ readl_relaxed(&boot_stats->bootloader_end));
+}
+
+void place_marker(const char *name)
+{
+ _create_boot_marker((char *) name, msm_timer_get_sclk_ticks());
+}
+EXPORT_SYMBOL(place_marker);
+
+static ssize_t bootkpi_reader(struct file *fp, char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ int rc = 0;
+ char *buf;
+ int temp = 0;
+ struct boot_marker *marker;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&boot_marker_list.lock);
+ list_for_each_entry(marker, &boot_marker_list.list, list) {
+ temp += scnprintf(buf + temp, PAGE_SIZE - temp,
+ "%-41s:%llu.%03llu seconds\n",
+ marker->marker_name,
+ marker->timer_value/TIMER_KHZ,
+ (((marker->timer_value % TIMER_KHZ)
+ * 1000) / TIMER_KHZ));
+ }
+ mutex_unlock(&boot_marker_list.lock);
+ rc = simple_read_from_buffer(user_buffer, count, position, buf, temp);
+ kfree(buf);
+ return rc;
+}
+
+static ssize_t bootkpi_writer(struct file *fp, const char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ int rc = 0;
+ char buf[MAX_STRING_LEN];
+
+ if (count > MAX_STRING_LEN)
+ return -EINVAL;
+ rc = simple_write_to_buffer(buf,
+ sizeof(buf) - 1, position, user_buffer, count);
+ if (rc < 0)
+ return rc;
+ buf[rc] = '\0';
+ place_marker(buf);
+ return rc;
+}
+
+static int bootkpi_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations fops_bkpi = {
+ .owner = THIS_MODULE,
+ .open = bootkpi_open,
+ .read = bootkpi_reader,
+ .write = bootkpi_writer,
+};
+
+static int __init init_bootkpi(void)
+{
+ dent_bkpi = debugfs_create_dir("bootkpi", NULL);
+ if (IS_ERR_OR_NULL(dent_bkpi))
+ return -ENODEV;
+
+ dent_bkpi_status = debugfs_create_file("kpi_values",
+ (S_IRUGO|S_IWUGO), dent_bkpi, 0, &fops_bkpi);
+ if (IS_ERR_OR_NULL(dent_bkpi_status)) {
+ debugfs_remove(dent_bkpi);
+ dent_bkpi = NULL;
+ pr_err("boot_marker: Could not create 'kpi_values' debugfs file\n");
+ return -ENODEV;
+ }
+
+ INIT_LIST_HEAD(&boot_marker_list.list);
+ mutex_init(&boot_marker_list.lock);
+ set_bootloader_stats();
+ return 0;
+}
+subsys_initcall(init_bootkpi);
+
+static void __exit exit_bootkpi(void)
+{
+ struct boot_marker *marker;
+ struct boot_marker *temp_addr;
+
+ debugfs_remove_recursive(dent_bkpi);
+ mutex_lock(&boot_marker_list.lock);
+ list_for_each_entry_safe(marker, temp_addr, &boot_marker_list.list,
+ list) {
+ list_del(&marker->list);
+ kfree(marker);
+ }
+ mutex_unlock(&boot_marker_list.lock);
+ boot_stats_exit();
+}
+module_exit(exit_bootkpi);
+
+MODULE_DESCRIPTION("MSM boot key performance indicators");
+MODULE_LICENSE("GPL v2");
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
#include <linux/io.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/sched.h>
#include <linux/of.h>
#include <linux/of_address.h>
-
-struct boot_stats {
- uint32_t bootloader_start;
- uint32_t bootloader_end;
- uint32_t bootloader_display;
- uint32_t bootloader_load_kernel;
-};
+#include <linux/export.h>
+#include <linux/types.h>
+#include <soc/qcom/boot_stats.h>
static void __iomem *mpm_counter_base;
static uint32_t mpm_counter_freq;
-static struct boot_stats __iomem *boot_stats;
+struct boot_stats __iomem *boot_stats;
static int mpm_parse_dt(void)
{
mpm_counter_freq);
}
+unsigned long long int msm_timer_get_sclk_ticks(void)
+{
+ unsigned long long int t1, t2;
+ int loop_count = 10;
+ int loop_zero_count = 3;
+ int tmp = USEC_PER_SEC;
+ void __iomem *sclk_tick;
+
+ do_div(tmp, TIMER_KHZ);
+ tmp /= (loop_zero_count-1);
+ sclk_tick = mpm_counter_base;
+ if (!sclk_tick)
+ return -EINVAL;
+ while (loop_zero_count--) {
+ t1 = __raw_readl_no_log(sclk_tick);
+ do {
+ udelay(1);
+ t2 = t1;
+ t1 = __raw_readl_no_log(sclk_tick);
+ } while ((t2 != t1) && --loop_count);
+ if (!loop_count) {
+ pr_err("boot_stats: SCLK did not stabilize\n");
+ return 0;
+ }
+ if (t1)
+ break;
+
+ udelay(tmp);
+ }
+ if (!loop_zero_count) {
+ pr_err("boot_stats: SCLK reads zero\n");
+ return 0;
+ }
+ return t1;
+}
+
int boot_stats_init(void)
{
int ret;
print_boot_stats();
+ if (!(boot_marker_enabled()))
+ boot_stats_exit();
+ return 0;
+}
+
+int boot_stats_exit(void)
+{
iounmap(boot_stats);
iounmap(mpm_counter_base);
-
return 0;
}
-
--- /dev/null
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This 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) "msm_cache_erp64: " fmt
+
+#include <linux/printk.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/cpu.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+#include <linux/cpu_pm.h>
+#include <linux/smp.h>
+
+#include <soc/qcom/kryo-l2-accessors.h>
+
+/* Instruction cache */
+#define ICECR_EL1 S3_1_c11_c1_0
+#define ICECR_IRQ_EN (BIT(1) | BIT(3) | BIT(5) | BIT(7))
+#define ICESR_EL1 S3_1_c11_c1_1
+#define ICESR_BIT_L1DPE BIT(3)
+#define ICESR_BIT_L1TPE BIT(2)
+#define ICESR_BIT_L0DPE BIT(1)
+#define ICESR_BIT_L0TPE BIT(0)
+#define ICESYNR0_EL1 S3_1_c11_c1_3
+#define ICESYNR1_EL1 S3_1_c11_c1_4
+#define ICEAR0_EL1 S3_1_c11_c1_5
+#define ICEAR1_EL1 S3_1_c11_c1_6
+#define ICESRS_EL1 S3_1_c11_c1_2
+
+/* Data cache */
+#define DCECR_EL1 S3_1_c11_c5_0
+#define DCECR_IRQ_EN (BIT(1) | BIT(3) | BIT(5) | BIT(7) | \
+ BIT(9))
+#define DCESR_EL1 S3_1_c11_c5_1
+#define DCESR_BIT_S1FTLBDPE BIT(4)
+#define DCESR_BIT_S1FTLBTPE BIT(3)
+#define DCESR_BIT_L1DPE BIT(2)
+#define DCESR_BIT_L1PTPE BIT(1)
+#define DCESR_BIT_L1VTPE BIT(0)
+#define DCESYNR0_EL1 S3_1_c11_c5_3
+#define DCESYNR1_EL1 S3_1_c11_c5_4
+#define DCESRS_EL1 S3_1_c11_c5_2
+#define DCEAR0_EL1 S3_1_c11_c5_5
+#define DCEAR1_EL1 S3_1_c11_c5_6
+
+/* L2 cache */
+#define L2CPUSRSELR_EL1I S3_3_c15_c0_6
+#define L2CPUSRDR_EL1 S3_3_c15_c0_7
+#define L2ECR0_IA 0x200
+#define L2ECR0_IRQ_EN (BIT(1) | BIT(3) | BIT(6) | BIT(9) | \
+ BIT(11) | BIT(13) | BIT(16) | \
+ BIT(19) | BIT(21) | BIT(23) | \
+ BIT(26) | BIT(29))
+
+#define L2ECR1_IA 0x201
+#define L2ECR1_IRQ_EN (BIT(1) | BIT(3) | BIT(6) | BIT(9) | \
+ BIT(11) | BIT(13) | BIT(16) | \
+ BIT(19) | BIT(21) | BIT(23) | BIT(29))
+#define L2ECR2_IA 0x202
+#define L2ECR2_IRQ_EN_MASK 0x3FFFFFF
+#define L2ECR2_IRQ_EN (BIT(1) | BIT(3) | BIT(6) | BIT(9) | \
+ BIT(12) | BIT(15) | BIT(17) | \
+ BIT(19) | BIT(22) | BIT(25))
+#define L2ESR0_IA 0x204
+#define L2ESR0_MASK 0x00FFFFFF
+#define L2ESR0_CE ((BIT(0) | BIT(1) | BIT(2) | BIT(3) | \
+ BIT(4) | BIT(5) | BIT(12) | BIT(13) | \
+ BIT(14) | BIT(15) | BIT(16) | BIT(17)) \
+ & L2ESR0_MASK)
+#define L2ESR0_UE (~L2ESR0_CE & L2ESR0_MASK)
+#define L2ESRS0_IA 0x205
+#define L2ESR1_IA 0x206
+#define L2ESR1_MASK 0x80FFFBFF
+#define L2ESRS1_IA 0x207
+#define L2ESYNR0_IA 0x208
+#define L2ESYNR1_IA 0x209
+#define L2ESYNR2_IA 0x20A
+#define L2ESYNR3_IA 0x20B
+#define L2ESYNR4_IA 0x20C
+#define L2EAR0_IA 0x20E
+#define L2EAR1_IA 0x20F
+
+#define L3_QLL_HML3_FIRA 0x3000
+#define L3_QLL_HML3_FIRA_CE (BIT(1) | BIT(3) | BIT(5))
+#define L3_QLL_HML3_FIRA_UE (BIT(2) | BIT(4) | BIT(6))
+#define L3_QLL_HML3_FIRAC 0x3008
+#define L3_QLL_HML3_FIRAS 0x3010
+#define L3_QLL_HML3_FIRAT0C 0x3020
+#define L3_QLL_HML3_FIRAT0C_IRQ_EN 0xFFFFFFFF
+#define L3_QLL_HML3_FIRAT1C 0x3024
+#define L3_QLL_HML3_FIRAT1S 0x302C
+#define L3_QLL_HML3_FIRAT1S_IRQ_EN 0x01EFC8FE
+#define L3_QLL_HML3_FIRSYNA 0x3100
+#define L3_QLL_HML3_FIRSYNB 0x3104
+#define L3_QLL_HML3_FIRSYNC 0x3108
+#define L3_QLL_HML3_FIRSYND 0x310C
+
+#define M4M_ERR_STATUS 0x10000
+#define M4M_ERR_STATUS_MASK 0x1FF
+#define M4M_ERR_Q22SIB_RET_DEC_ERR (BIT(7))
+#define M4M_ERR_Q22SIB_RET_SLV_ERR (BIT(6))
+#define M4M_ERR_CLR 0x10008
+#define M4M_INT_CTRL 0x10010
+#define M4M_INT_CTRL_IRQ_EN 0x1FF
+#define M4M_ERR_CTRL 0x10018
+#define M4M_ERR_INJ 0x10020
+#define M4M_ERR_CAP_0 0x10030
+#define M4M_ERR_CAP_1 0x10038
+#define M4M_ERR_CAP_2 0x10040
+#define M4M_ERR_CAP_3 0x10048
+
+#define AFFINITY_LEVEL_L3 3
+
+#ifdef CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_CE
+static bool __read_mostly panic_on_ce = true;
+#else
+static bool __read_mostly panic_on_ce;
+#endif
+
+#ifdef CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_UE
+static bool __read_mostly panic_on_ue = true;
+#else
+static bool __read_mostly panic_on_ue;
+#endif
+
+module_param(panic_on_ce, bool, false);
+module_param(panic_on_ue, bool, false);
+
+static void __iomem *hml3_base;
+static void __iomem *m4m_base;
+
+enum erp_irq_index { IRQ_L1, IRQ_L2_INFO0, IRQ_L2_INFO1, IRQ_L2_ERR0,
+ IRQ_L2_ERR1, IRQ_L3, IRQ_M4M, IRQ_MAX };
+static const char * const erp_irq_names[] = {
+ "l1_irq", "l2_irq_info_0", "l2_irq_info_1", "l2_irq_err_0",
+ "l2_irq_err_1", "l3_irq", "m4m_irq"
+};
+static int erp_irqs[IRQ_MAX];
+
+struct msm_l1_err_stats {
+ /* nothing */
+};
+
+static DEFINE_PER_CPU(struct msm_l1_err_stats, msm_l1_erp_stats);
+static DEFINE_PER_CPU(struct call_single_data, handler_csd);
+
+#define erp_mrs(reg) ({ \
+ u64 __val; \
+ asm volatile("mrs %0, " __stringify(reg) : "=r" (__val)); \
+ __val; \
+})
+
+#define erp_msr(reg, val) { \
+ asm volatile("msr " __stringify(reg) ", %0" : : "r" (val)); \
+}
+
+static void msm_erp_show_icache_error(void)
+{
+ u64 icesr;
+ int cpu = raw_smp_processor_id();
+
+ icesr = erp_mrs(ICESR_EL1);
+ if (!(icesr & (ICESR_BIT_L0TPE | ICESR_BIT_L0DPE | ICESR_BIT_L1TPE |
+ ICESR_BIT_L1DPE))) {
+ pr_debug("CPU%d: No I-cache error detected ICESR 0x%llx\n",
+ cpu, icesr);
+ goto clear_out;
+ }
+
+ pr_alert("CPU%d: I-cache error\n", cpu);
+ pr_alert("CPU%d: ICESR_EL1 0x%llx ICESYNR0 0x%llx ICESYNR1 0x%llx ICEAR0 0x%llx IECAR1 0x%llx\n",
+ cpu, icesr, erp_mrs(ICESYNR0_EL1), erp_mrs(ICESYNR1_EL1),
+ erp_mrs(ICEAR0_EL1), erp_mrs(ICEAR1_EL1));
+
+ /*
+ * all detectable I-cache erros are recoverable as
+ * corrupted lines are refetched
+ */
+ if (panic_on_ce)
+ BUG_ON(1);
+ else
+ WARN_ON(1);
+
+clear_out:
+ erp_msr(ICESR_EL1, icesr);
+}
+
+static void msm_erp_show_dcache_error(void)
+{
+ u64 dcesr;
+ int cpu = raw_smp_processor_id();
+
+ dcesr = erp_mrs(DCESR_EL1);
+ if (!(dcesr & (DCESR_BIT_L1VTPE | DCESR_BIT_L1PTPE | DCESR_BIT_L1DPE |
+ DCESR_BIT_S1FTLBTPE | DCESR_BIT_S1FTLBDPE))) {
+ pr_debug("CPU%d: No D-cache error detected DCESR 0x%llx\n",
+ cpu, dcesr);
+ goto clear_out;
+ }
+
+ pr_alert("CPU%d: D-cache error detected\n", cpu);
+ pr_alert("CPU%d: L1 DCESR 0x%llx, DCESYNR0 0x%llx, DCESYNR1 0x%llx, DCEAR0 0x%llx, DCEAR1 0x%llx\n",
+ cpu, dcesr, erp_mrs(DCESYNR0_EL1), erp_mrs(DCESYNR1_EL1),
+ erp_mrs(DCEAR0_EL1), erp_mrs(DCEAR1_EL1));
+
+ /* all D-cache erros are correctable */
+ if (panic_on_ce)
+ BUG_ON(1);
+ else
+ WARN_ON(1);
+
+clear_out:
+ erp_msr(DCESR_EL1, dcesr);
+}
+
+static irqreturn_t msm_l1_erp_irq(int irq, void *dev_id)
+{
+ msm_erp_show_icache_error();
+ msm_erp_show_dcache_error();
+ return IRQ_HANDLED;
+}
+
+static DEFINE_SPINLOCK(local_handler_lock);
+static void msm_l2_erp_local_handler(void *force)
+{
+ unsigned long flags;
+ u64 esr0, esr1;
+ bool parity_ue, parity_ce, misc_ue;
+ int cpu;
+
+ spin_lock_irqsave(&local_handler_lock, flags);
+
+ esr0 = get_l2_indirect_reg(L2ESR0_IA);
+ esr1 = get_l2_indirect_reg(L2ESR1_IA);
+ parity_ue = esr0 & L2ESR0_UE;
+ parity_ce = esr0 & L2ESR0_CE;
+ misc_ue = esr1;
+ cpu = raw_smp_processor_id();
+
+ if (force || parity_ue || parity_ce || misc_ue) {
+ if (parity_ue)
+ pr_alert("CPU%d: L2 uncorrectable parity error\n", cpu);
+ if (parity_ce)
+ pr_alert("CPU%d: L2 correctable parity error\n", cpu);
+ if (misc_ue)
+ pr_alert("CPU%d: L2 (non-parity) error\n", cpu);
+ pr_alert("CPU%d: L2ESR0 0x%llx, L2ESR1 0x%llx\n",
+ cpu, esr0, esr1);
+ pr_alert("CPU%d: L2ESYNR0 0x%llx, L2ESYNR1 0x%llx, L2ESYNR2 0x%llx\n",
+ cpu, get_l2_indirect_reg(L2ESYNR0_IA),
+ get_l2_indirect_reg(L2ESYNR1_IA),
+ get_l2_indirect_reg(L2ESYNR2_IA));
+ pr_alert("CPU%d: L2EAR0 0x%llx, L2EAR1 0x%llx\n", cpu,
+ get_l2_indirect_reg(L2EAR0_IA),
+ get_l2_indirect_reg(L2EAR1_IA));
+ } else {
+ pr_info("CPU%d: No L2 error detected in L2ESR0 0x%llx, L2ESR1 0x%llx)\n",
+ cpu, esr0, esr1);
+ }
+
+ /* clear */
+ set_l2_indirect_reg(L2ESR0_IA, esr0);
+ set_l2_indirect_reg(L2ESR1_IA, esr1);
+
+ if (panic_on_ue)
+ BUG_ON(parity_ue || misc_ue);
+ else
+ WARN_ON(parity_ue || misc_ue);
+
+ if (panic_on_ce)
+ BUG_ON(parity_ce);
+ else
+ WARN_ON(parity_ce);
+
+ spin_unlock_irqrestore(&local_handler_lock, flags);
+}
+
+static irqreturn_t msm_l2_erp_irq(int irq, void *dev_id)
+{
+ int cpu;
+ struct call_single_data *csd;
+
+ for_each_online_cpu(cpu) {
+ csd = &per_cpu(handler_csd, cpu);
+ csd->func = msm_l2_erp_local_handler;
+ smp_call_function_single_async(cpu, csd);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_l3_erp_irq(int irq, void *dev_id)
+{
+ u32 hml3_fira;
+ bool parity_ue, parity_ce, misc_ue;
+
+ hml3_fira = readl_relaxed(hml3_base + L3_QLL_HML3_FIRA);
+ parity_ue = (hml3_fira & L3_QLL_HML3_FIRAT1S_IRQ_EN) &
+ L3_QLL_HML3_FIRA_UE;
+ parity_ce = (hml3_fira & L3_QLL_HML3_FIRAT1S_IRQ_EN) &
+ L3_QLL_HML3_FIRA_CE;
+ misc_ue = (hml3_fira & L3_QLL_HML3_FIRAT1S_IRQ_EN) &
+ ~(L3_QLL_HML3_FIRA_UE | L3_QLL_HML3_FIRA_CE);
+ if (parity_ue)
+ pr_alert("L3 uncorrectable parity error\n");
+ if (parity_ce)
+ pr_alert("L3 correctable parity error\n");
+ if (misc_ue)
+ pr_alert("L3 (non-parity) error\n");
+
+ pr_alert("HML3_FIRA 0x%0x\n", hml3_fira);
+ pr_alert("HML3_FIRSYNA 0x%0x, HML3_FIRSYNB 0x%0x\n",
+ readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYNA),
+ readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYNB));
+ pr_alert("HML3_FIRSYNC 0x%0x, HML3_FIRSYND 0x%0x\n",
+ readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYNC),
+ readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYND));
+
+ if (panic_on_ue)
+ BUG_ON(parity_ue || misc_ue);
+ else
+ WARN_ON(parity_ue || misc_ue);
+
+ if (panic_on_ce)
+ BUG_ON(parity_ce);
+ else
+ WARN_ON(parity_ce);
+
+ writel_relaxed(hml3_fira, hml3_base + L3_QLL_HML3_FIRAC);
+ /* ensure of irq clear */
+ wmb();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t msm_m4m_erp_irq(int irq, void *dev_id)
+{
+ u32 m4m_status;
+
+ pr_alert("CPU%d: M4M error detected\n", raw_smp_processor_id());
+ m4m_status = readl_relaxed(m4m_base + M4M_ERR_STATUS);
+ pr_alert("M4M_ERR_STATUS 0x%0x\n", m4m_status);
+ if ((m4m_status & M4M_ERR_STATUS_MASK) &
+ ~(M4M_ERR_Q22SIB_RET_DEC_ERR | M4M_ERR_Q22SIB_RET_SLV_ERR)) {
+ pr_alert("M4M_ERR_CAP_0 0x%0x, M4M_ERR_CAP_1 0x%x\n",
+ readl_relaxed(m4m_base + M4M_ERR_CAP_0),
+ readl_relaxed(m4m_base + M4M_ERR_CAP_1));
+ pr_alert("M4M_ERR_CAP_2 0x%0x, M4M_ERR_CAP_3 0x%x\n",
+ readl_relaxed(m4m_base + M4M_ERR_CAP_2),
+ readl_relaxed(m4m_base + M4M_ERR_CAP_3));
+ } else {
+ /*
+ * M4M error-capture registers not valid when error detected
+ * due to DEC_ERR or SLV_ERR. L2E registers are still valid.
+ */
+ pr_alert("Omit dumping M4M_ERR_CAP\n");
+ }
+
+ /*
+ * On QSB errors, the L2 captures the bad address and syndrome in
+ * L2E error registers. Therefore dump L2E always whenever M4M error
+ * detected.
+ */
+ on_each_cpu(msm_l2_erp_local_handler, (void *)1, 1);
+ writel_relaxed(1, m4m_base + M4M_ERR_CLR);
+ /* ensure of irq clear */
+ wmb();
+
+ if (panic_on_ue)
+ BUG_ON(1);
+ else
+ WARN_ON(1);
+
+ return IRQ_HANDLED;
+}
+
+static void enable_erp_irq_callback(void *info)
+{
+ enable_percpu_irq(erp_irqs[IRQ_L1], IRQ_TYPE_NONE);
+}
+
+static void disable_erp_irq_callback(void *info)
+{
+ disable_percpu_irq(erp_irqs[IRQ_L1]);
+}
+
+static void msm_cache_erp_irq_init(void *param)
+{
+ u64 v;
+ /* Enable L0/L1 I/D cache error reporting. */
+ erp_msr(ICECR_EL1, ICECR_IRQ_EN);
+ erp_msr(DCECR_EL1, DCECR_IRQ_EN);
+ /*
+ * Enable L2 data, tag, QSB and possion error reporting.
+ */
+ set_l2_indirect_reg(L2ECR0_IA, L2ECR0_IRQ_EN);
+ set_l2_indirect_reg(L2ECR1_IA, L2ECR1_IRQ_EN);
+ v = (get_l2_indirect_reg(L2ECR2_IA) & ~L2ECR2_IRQ_EN_MASK)
+ | L2ECR2_IRQ_EN;
+ set_l2_indirect_reg(L2ECR2_IA, v);
+}
+
+static void msm_cache_erp_l3_init(void)
+{
+ writel_relaxed(L3_QLL_HML3_FIRAT0C_IRQ_EN,
+ hml3_base + L3_QLL_HML3_FIRAT0C);
+ writel_relaxed(L3_QLL_HML3_FIRAT1S_IRQ_EN,
+ hml3_base + L3_QLL_HML3_FIRAT1S);
+}
+
+static int cache_erp_cpu_pm_callback(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ unsigned long aff_level = (unsigned long) v;
+
+ switch (cmd) {
+ case CPU_CLUSTER_PM_EXIT:
+ msm_cache_erp_irq_init(NULL);
+
+ if (aff_level >= AFFINITY_LEVEL_L3)
+ msm_cache_erp_l3_init();
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cache_erp_cpu_pm_notifier = {
+ .notifier_call = cache_erp_cpu_pm_callback,
+};
+
+static int cache_erp_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_STARTING:
+ msm_cache_erp_irq_init(NULL);
+ enable_erp_irq_callback(NULL);
+ break;
+ case CPU_DYING:
+ disable_erp_irq_callback(NULL);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cache_erp_cpu_notifier = {
+ .notifier_call = cache_erp_cpu_callback,
+};
+
+static int msm_cache_erp_probe(struct platform_device *pdev)
+{
+ int i, ret = 0;
+ struct resource *r;
+
+ dev_dbg(&pdev->dev, "enter\n");
+
+ /* L3 */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hml3_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(hml3_base)) {
+ dev_err(&pdev->dev, "failed to ioremap (0x%pK)\n", hml3_base);
+ return PTR_ERR(hml3_base);
+ }
+
+ for (i = 0; i <= IRQ_L3; i++) {
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ erp_irq_names[i]);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get %s\n",
+ erp_irq_names[i]);
+ return -ENODEV;
+ }
+ erp_irqs[i] = r->start;
+ }
+
+ msm_cache_erp_l3_init();
+
+ /* L0/L1 erp irq per cpu */
+ dev_info(&pdev->dev, "Registering for L1 error interrupts\n");
+ ret = request_percpu_irq(erp_irqs[IRQ_L1], msm_l1_erp_irq,
+ erp_irq_names[IRQ_L1], &msm_l1_erp_stats);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request L0/L1 ERP irq %s (%d)\n",
+ erp_irq_names[IRQ_L1], ret);
+ return ret;
+ } else {
+ dev_dbg(&pdev->dev, "requested L0/L1 ERP irq %s\n",
+ erp_irq_names[IRQ_L1]);
+ }
+
+ get_online_cpus();
+ register_hotcpu_notifier(&cache_erp_cpu_notifier);
+ cpu_pm_register_notifier(&cache_erp_cpu_pm_notifier);
+
+ /* Perform L1/L2 cache error detection init on online cpus */
+ on_each_cpu(msm_cache_erp_irq_init, NULL, 1);
+ /* Enable irqs */
+ on_each_cpu(enable_erp_irq_callback, NULL, 1);
+ put_online_cpus();
+
+ /* L2 erp irq per cluster */
+ dev_info(&pdev->dev, "Registering for L2 error interrupts\n");
+ for (i = IRQ_L2_INFO0; i <= IRQ_L2_ERR1; i++) {
+ ret = devm_request_irq(&pdev->dev, erp_irqs[i],
+ msm_l2_erp_irq,
+ IRQF_ONESHOT |
+ IRQF_TRIGGER_HIGH,
+ erp_irq_names[i], NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %s (%d)\n",
+ erp_irq_names[i], ret);
+ goto cleanup;
+ }
+ }
+
+ /* L3 erp irq */
+ dev_info(&pdev->dev, "Registering for L3 error interrupts\n");
+ ret = devm_request_irq(&pdev->dev, erp_irqs[IRQ_L3], msm_l3_erp_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+ erp_irq_names[IRQ_L3], NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request L3 irq %s (%d)\n",
+ erp_irq_names[IRQ_L3], ret);
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ free_percpu_irq(erp_irqs[IRQ_L1], NULL);
+ return ret;
+}
+
+static void msm_m4m_erp_irq_init(void)
+{
+ writel_relaxed(M4M_INT_CTRL_IRQ_EN, m4m_base + M4M_INT_CTRL);
+ writel_relaxed(0, m4m_base + M4M_ERR_CTRL);
+}
+
+static int msm_m4m_erp_m4m_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct resource *r;
+
+ dev_dbg(&pdev->dev, "enter\n");
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ m4m_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(m4m_base)) {
+ dev_err(&pdev->dev, "failed to ioremap (0x%pK)\n", m4m_base);
+ return PTR_ERR(m4m_base);
+ }
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ erp_irq_names[IRQ_M4M]);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get %s\n",
+ erp_irq_names[IRQ_M4M]);
+ ret = -ENODEV;
+ goto exit;
+ }
+ erp_irqs[IRQ_M4M] = r->start;
+
+ dev_info(&pdev->dev, "Registering for M4M error interrupts\n");
+ ret = devm_request_irq(&pdev->dev, erp_irqs[IRQ_M4M],
+ msm_m4m_erp_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+ erp_irq_names[IRQ_M4M], NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %s (%d)\n",
+ erp_irq_names[IRQ_M4M], ret);
+ goto exit;
+ }
+
+ msm_m4m_erp_irq_init();
+
+exit:
+ return ret;
+}
+
+static struct of_device_id cache_erp_dt_ids[] = {
+ { .compatible = "qcom,kryo_cache_erp64", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cache_erp_dt_ids);
+
+static struct platform_driver msm_cache_erp_driver = {
+ .probe = msm_cache_erp_probe,
+ .driver = {
+ .name = "msm_cache_erp64",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cache_erp_dt_ids),
+ },
+};
+
+static struct of_device_id m4m_erp_dt_ids[] = {
+ { .compatible = "qcom,m4m_erp", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, m4m_erp_dt_ids);
+static struct platform_driver msm_m4m_erp_driver = {
+ .probe = msm_m4m_erp_m4m_probe,
+ .driver = {
+ .name = "msm_m4m_erp",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4m_erp_dt_ids),
+ },
+};
+
+static int __init msm_cache_erp_init(void)
+{
+ int r;
+
+ r = platform_driver_register(&msm_cache_erp_driver);
+ if (!r)
+ r = platform_driver_register(&msm_m4m_erp_driver);
+ if (r)
+ pr_err("failed to register driver %d\n", r);
+ return r;
+}
+
+arch_initcall(msm_cache_erp_init);
#include <soc/qcom/socinfo.h>
#include <soc/qcom/ramdump.h>
-#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
-#include <net/cnss_prealloc.h>
-#endif
-
-
#include "wlan_firmware_service_v01.h"
#ifdef CONFIG_ICNSS_DEBUG
ICNSS_MSA0_ASSIGNED,
ICNSS_WLFW_EXISTS,
ICNSS_WDOG_BITE,
+ ICNSS_SHUTDOWN_DONE,
};
struct ce_irq_list {
goto out;
}
+ memset(&ind_msg, 0, sizeof(ind_msg));
+
ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
if (ret < 0) {
icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
ret, priv->state);
- wcnss_prealloc_check_memory_leak();
- wcnss_pre_alloc_reset();
goto out;
}
if (!priv->ops || !priv->ops->shutdown)
goto out;
+ if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state))
+ goto out;
+
icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
priv->ops->shutdown(&priv->pdev->dev);
+ set_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
out:
return 0;
}
out:
+ clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state);
return 0;
call_probe:
if (ret) {
icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
ret, penv->state);
- wcnss_prealloc_check_memory_leak();
- wcnss_pre_alloc_reset();
goto power_off;
}
penv->ops->remove(&penv->pdev->dev);
clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
- wcnss_prealloc_check_memory_leak();
- wcnss_pre_alloc_reset();
penv->ops = NULL;
penv->ops->remove(&priv->pdev->dev);
clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
- wcnss_prealloc_check_memory_leak();
- wcnss_pre_alloc_reset();
icnss_hw_power_off(penv);
case ICNSS_WDOG_BITE:
seq_puts(s, "MODEM WDOG BITE");
continue;
+ case ICNSS_SHUTDOWN_DONE:
+ seq_puts(s, "SHUTDOWN DONE");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
-/* 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
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/types.h>
-
+#include <linux/spinlock.h>
static int ipc_router_mhi_xprt_debug_mask;
module_param_named(debug_mask, ipc_router_mhi_xprt_debug_mask,
struct completion sft_close_complete;
unsigned xprt_version;
unsigned xprt_option;
- struct mutex tx_addr_map_list_lock;
+ spinlock_t tx_addr_map_list_lock;
struct list_head tx_addr_map_list;
- struct mutex rx_addr_map_list_lock;
+ spinlock_t rx_addr_map_list_lock;
struct list_head rx_addr_map_list;
};
* Return: The mapped virtual Address if found, NULL otherwise.
*/
void *ipc_router_mhi_xprt_find_addr_map(struct list_head *addr_map_list,
- struct mutex *addr_map_list_lock,
- void *addr)
+ spinlock_t *addr_map_list_lock, void *addr)
{
struct ipc_router_mhi_addr_map *addr_mapping;
struct ipc_router_mhi_addr_map *tmp_addr_mapping;
+ unsigned long flags;
void *virt_addr;
if (!addr_map_list || !addr_map_list_lock)
return NULL;
- mutex_lock(addr_map_list_lock);
+ spin_lock_irqsave(addr_map_list_lock, flags);
list_for_each_entry_safe(addr_mapping, tmp_addr_mapping,
addr_map_list, list_node) {
if (addr_mapping->virt_addr == addr) {
kref_put(&addr_mapping->pkt->ref,
ipc_router_mhi_release_pkt);
kfree(addr_mapping);
- mutex_unlock(addr_map_list_lock);
+ spin_unlock_irqrestore(addr_map_list_lock, flags);
return virt_addr;
}
}
- mutex_unlock(addr_map_list_lock);
+ spin_unlock_irqrestore(addr_map_list_lock, flags);
IPC_RTR_ERR(
"%s: Virtual address mapping [%p] not found\n",
__func__, (void *)addr);
* Return: 0 on success, standard Linux error code otherwise.
*/
int ipc_router_mhi_xprt_add_addr_map(struct list_head *addr_map_list,
- struct mutex *addr_map_list_lock,
+ spinlock_t *addr_map_list_lock,
struct rr_packet *pkt, void *virt_addr)
{
struct ipc_router_mhi_addr_map *addr_mapping;
+ unsigned long flags;
if (!addr_map_list || !addr_map_list_lock)
return -EINVAL;
return -ENOMEM;
addr_mapping->virt_addr = virt_addr;
addr_mapping->pkt = pkt;
- mutex_lock(addr_map_list_lock);
+ spin_lock_irqsave(addr_map_list_lock, flags);
if (addr_mapping->pkt)
kref_get(&addr_mapping->pkt->ref);
list_add_tail(&addr_mapping->list_node, addr_map_list);
- mutex_unlock(addr_map_list_lock);
+ spin_unlock_irqrestore(addr_map_list_lock, flags);
return 0;
}
mhi_xprtp = (struct ipc_router_mhi_xprt *)(cb_info->result->user_data);
if (cb_info->chan == mhi_xprtp->ch_hndl.out_chan_id) {
out_addr = cb_info->result->buf_addr;
- mutex_lock(&mhi_xprtp->ch_hndl.state_lock);
- ipc_router_mhi_xprt_find_addr_map(&mhi_xprtp->tx_addr_map_list,
+ ipc_router_mhi_xprt_find_addr_map(
+ &mhi_xprtp->tx_addr_map_list,
&mhi_xprtp->tx_addr_map_list_lock,
out_addr);
wake_up(&mhi_xprtp->write_wait_q);
- mutex_unlock(&mhi_xprtp->ch_hndl.state_lock);
} else if (cb_info->chan == mhi_xprtp->ch_hndl.in_chan_id) {
queue_work(mhi_xprtp->wq, &mhi_xprtp->read_work);
} else {
mhi_xprtp->ch_hndl.num_trbs = IPC_ROUTER_MHI_XPRT_NUM_TRBS;
mhi_xprtp->ch_hndl.mhi_xprtp = mhi_xprtp;
INIT_LIST_HEAD(&mhi_xprtp->tx_addr_map_list);
- mutex_init(&mhi_xprtp->tx_addr_map_list_lock);
+ spin_lock_init(&mhi_xprtp->tx_addr_map_list_lock);
INIT_LIST_HEAD(&mhi_xprtp->rx_addr_map_list);
- mutex_init(&mhi_xprtp->rx_addr_map_list_lock);
+ spin_lock_init(&mhi_xprtp->rx_addr_map_list_lock);
rc = ipc_router_mhi_driver_register(mhi_xprtp);
return rc;
data = (struct msm_dump_data *)(phys_to_virt(entry->addr));
if (!strcmp(data->name, "")) {
- pr_info("Entry name is NULL, Use ID %d for minidump\n",
+ 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);
dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table));
if (msm_dump_data_add_minidump(entry))
- pr_info("Failed to add entry in Minidump table\n");
+ pr_err("Failed to add entry in Minidump table\n");
return 0;
}
struct md_region entry[MAX_NUM_ENTRIES];
};
+/*
+ * md_elfhdr: Minidump table elf header
+ * @md_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 *md_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 bool minidump_enabled;
-static struct md_table minidump_table;
+static struct md_table minidump_table;
+static struct md_elfhdr minidump_elfheader;
+
+bool minidump_enabled;
static unsigned int pendings;
static unsigned int region_idx = 1; /* First entry is ELF header*/
-/* ELF Header */
-static struct elfhdr *md_ehdr;
-/* ELF Program header */
-static struct elf_phdr *phdr;
-/* ELF Section header */
-static struct elf_shdr *shdr;
-/* Section offset in elf image */
-static u64 elf_offset;
-/* String table index, first byte must be '\0' */
-static unsigned int stringtable_idx = 1;
-
static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
{
return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
return &elf_sheader(hdr)[idx];
}
+static inline struct elf_phdr *elf_pheader(struct elfhdr *hdr)
+{
+ return (struct elf_phdr *)((size_t)hdr + (size_t)hdr->e_phoff);
+}
+
+static inline struct elf_phdr *elf_program(struct elfhdr *hdr, int idx)
+{
+ return &elf_pheader(hdr)[idx];
+}
+
static inline char *elf_str_table(struct elfhdr *hdr)
{
if (hdr->e_shstrndx == SHN_UNDEF)
{
char *strtab = elf_str_table(hdr);
- if ((strtab == NULL) | (stringtable_idx < offset))
+ 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(md_ehdr);
+ char *strtab = elf_str_table(minidump_elfheader.md_ehdr);
+ int idx = minidump_elfheader.strtable_idx;
int ret = 0;
- if ((strtab == NULL) | (name == NULL))
+ if ((strtab == NULL) || (name == NULL))
return 0;
- ret = stringtable_idx;
- stringtable_idx += strlcpy((strtab + stringtable_idx),
- name, MAX_NAME_LENGTH);
- stringtable_idx += 1;
+ ret = idx;
+ idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH);
+ minidump_elfheader.strtable_idx = idx + 1;
+
return ret;
}
static int md_update_smem_table(const struct md_region *entry)
{
struct md_smem_region *mdr;
-
- if (!minidump_enabled) {
- pr_info("Table in smem is not setup\n");
- return -ENODEV;
- }
+ struct elfhdr *hdr = minidump_elfheader.md_ehdr;
+ struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++);
+ struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++);
mdr = &minidump_table.region[region_idx++];
shdr->sh_addr = (elf_addr_t)entry->virt_addr;
shdr->sh_size = mdr->size;
shdr->sh_flags = SHF_WRITE;
- shdr->sh_offset = elf_offset;
+ shdr->sh_offset = minidump_elfheader.elf_offset;
shdr->sh_entsize = 0;
phdr->p_type = PT_LOAD;
- phdr->p_offset = elf_offset;
+ 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->size;
phdr->p_flags = PF_R | PF_W;
- md_ehdr->e_shnum += 1;
- md_ehdr->e_phnum += 1;
- elf_offset += shdr->sh_size;
- shdr++;
- phdr++;
+ minidump_elfheader.elf_offset += shdr->sh_size;
return 0;
}
-bool msm_minidump_enabled(void)
-{
- bool ret;
-
- spin_lock(&mdt_lock);
- ret = minidump_enabled;
- spin_unlock(&mdt_lock);
- return ret;
-}
-EXPORT_SYMBOL(msm_minidump_enabled);
-
int msm_minidump_add_region(const struct md_region *entry)
{
u32 entries;
if (((strlen(entry->name) > MAX_NAME_LENGTH) ||
md_check_name(entry->name)) && !entry->virt_addr) {
- pr_info("Invalid entry details\n");
+ pr_err("Invalid entry details\n");
return -EINVAL;
}
if (!IS_ALIGNED(entry->size, 4)) {
- pr_info("size should be 4 byte aligned\n");
+ 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_info("Maximum entries reached.\n");
+ pr_err("Maximum entries reached.\n");
spin_unlock(&mdt_lock);
return -ENOMEM;
}
static int msm_minidump_add_header(void)
{
struct md_smem_region *mdreg = &minidump_table.region[0];
- char *banner;
+ struct elfhdr *md_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+1 of section and program elf headers,
+ * string table section and linux banner.
+ */
elfh_size = sizeof(*md_ehdr) + MAX_STRTBL_SIZE + MAX_MEM_LENGTH +
((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 1));
- md_ehdr = kzalloc(elfh_size, GFP_KERNEL);
- if (!md_ehdr)
+ minidump_elfheader.md_ehdr = kzalloc(elfh_size, GFP_KERNEL);
+ if (!minidump_elfheader.md_ehdr)
return -ENOMEM;
strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name));
- mdreg->address = virt_to_phys(md_ehdr);
+ mdreg->address = virt_to_phys(minidump_elfheader.md_ehdr);
mdreg->size = elfh_size;
- /* Section headers*/
- shdr = (struct elf_shdr *)(md_ehdr + 1);
- phdr = (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
+ md_ehdr = minidump_elfheader.md_ehdr;
+ /* Assign section/program headers offset */
+ minidump_elfheader.shdr = shdr = (struct elf_shdr *)(md_ehdr + 1);
+ minidump_elfheader.phdr = phdr =
+ (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
phdr_off = sizeof(*md_ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);
memcpy(md_ehdr->e_ident, ELFMAG, SELFMAG);
md_ehdr->e_ehsize = sizeof(*md_ehdr);
md_ehdr->e_phoff = phdr_off;
md_ehdr->e_phentsize = sizeof(*phdr);
- md_ehdr->e_phnum = 1;
md_ehdr->e_shoff = sizeof(*md_ehdr);
md_ehdr->e_shentsize = sizeof(*shdr);
- md_ehdr->e_shnum = 3; /* NULL, STR TABLE, Linux banner */
md_ehdr->e_shstrndx = 1;
- elf_offset = elfh_size;
+ 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(*md_ehdr) +
((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES);
- /* First section header should be NULL
- * 2nd entry for string table
- */
shdr++;
shdr->sh_type = SHT_STRTAB;
shdr->sh_offset = (elf_addr_t)strtbl_off;
shdr->sh_name = set_section_name("STR_TBL");
shdr++;
- /* 3rd entry for linux banner */
+ /* 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 *)md_ehdr + strtbl_off + MAX_STRTBL_SIZE;
strlcpy(banner, linux_banner, MAX_MEM_LENGTH);
shdr->sh_entsize = 0;
shdr->sh_flags = SHF_WRITE;
shdr->sh_name = set_section_name("linux_banner");
- shdr++;
phdr->p_type = PT_LOAD;
phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1;
phdr->p_flags = PF_R | PF_W;
- md_ehdr->e_phnum += 1;
- phdr++;
+ /* Update headers count*/
+ md_ehdr->e_phnum = 1;
+ md_ehdr->e_shnum = 4;
return 0;
}
smem_table = smem_get_entry(SMEM_MINIDUMP_TABLE_ID, &size, 0,
SMEM_ANY_HOST_FLAG);
if (IS_ERR_OR_NULL(smem_table)) {
- pr_info("SMEM is not initialized.\n");
+ pr_err("SMEM is not initialized.\n");
return -ENODEV;
}
if ((smem_table->next_avail_offset + MAX_MEM_LENGTH) >
smem_table->smem_length) {
- pr_info("SMEM memory not available.\n");
+ pr_err("SMEM memory not available.\n");
return -ENOMEM;
}
for (i = 0; i < pendings; i++) {
mdr = &minidump_table.entry[i];
if (md_update_smem_table(mdr)) {
- pr_info("Unable to add entry %s to smem table\n",
+ pr_err("Unable to add entry %s to smem table\n",
mdr->name);
spin_unlock(&mdt_lock);
- return -ENODEV;
+ return -ENOENT;
}
}
static u32 kryo_read_pmresr(int reg, int l_h)
{
- u32 val;
-
- if (reg > KRYO_MAX_L1_REG) {
- pr_err("Invalid read of RESR reg %d\n", reg);
- return 0;
- }
+ u32 val = 0;
if (l_h == RESR_L) {
switch (reg) {
case 2:
asm volatile("mrs %0, " pmresr2l_el0 : "=r" (val));
break;
+ default:
+ WARN_ONCE(1, "Invalid read of RESR reg %d\n", reg);
+ break;
}
} else {
switch (reg) {
case 2:
asm volatile("mrs %0," pmresr2h_el0 : "=r" (val));
break;
+ default:
+ WARN_ONCE(1, "Invalid read of RESR reg %d\n", reg);
+ break;
}
}
priv->region_start),
VMID_HLOS);
}
+ if (desc->clear_fw_region && priv->region_start)
+ pil_clear_segment(desc);
dma_free_attrs(desc->dev, priv->region_size,
priv->region, priv->region_start,
&desc->attrs);
priv->region = NULL;
}
- if (desc->clear_fw_region && priv->region_start)
- pil_clear_segment(desc);
pil_release_mmap(desc);
}
return ret;
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("qbt1000_ioctl %d\n", cmd);
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__);
}
pr_debug("app %s load before\n", app.name);
+ app.name[MAX_NAME_SIZE - 1] = '\0';
/* start the TZ app */
rc = qseecom_start_app(
pr_err("App %s failed to set bw\n", app.name);
}
} else {
- pr_err("app %s failed to load\n", app.name);
+ dev_err(drvdata->dev, "%s: Fingerprint Trusted App failed to load\n",
+ __func__);
goto end;
}
pr_debug("app %s load after\n", app.name);
- if (!strcmp(app.name, FP_APP_NAME))
- drvdata->fp_app_handle = drvdata->app_handle;
-
+ drvdata->fp_app_handle = drvdata->app_handle;
break;
}
case QBT1000_UNLOAD_APP:
mutex_unlock(&svc->m_lock);
return NULL;
}
- if (!svc->port_cnt && !svc->svc_cnt)
+ if (!svc->svc_cnt)
clnt->svc_cnt++;
svc->port_cnt++;
svc->port_fn[temp_port] = svc_fn;
svc->port_priv[temp_port] = priv;
+ svc->svc_cnt++;
} else {
if (!svc->fn) {
- if (!svc->port_cnt && !svc->svc_cnt)
+ if (!svc->svc_cnt)
clnt->svc_cnt++;
svc->fn = svc_fn;
- if (svc->port_cnt)
- svc->svc_cnt++;
svc->priv = priv;
+ svc->svc_cnt++;
}
}
if (!handle)
return -EINVAL;
+ if (!svc->svc_cnt) {
+ pr_err("%s: svc already deregistered. svc = %pK\n",
+ __func__, svc);
+ return -EINVAL;
+ }
+
mutex_lock(&svc->m_lock);
dest_id = svc->dest_id;
client_id = svc->client_id;
clnt = &client[dest_id][client_id];
- if (svc->port_cnt > 0 || svc->svc_cnt > 0) {
+ if (svc->svc_cnt > 0) {
if (svc->port_cnt)
svc->port_cnt--;
- else if (svc->svc_cnt)
- svc->svc_cnt--;
- if (!svc->port_cnt && !svc->svc_cnt) {
+ svc->svc_cnt--;
+ if (!svc->svc_cnt) {
client[dest_id][client_id].svc_cnt--;
- svc->need_reset = 0x0;
- }
- } else if (client[dest_id][client_id].svc_cnt > 0) {
- client[dest_id][client_id].svc_cnt--;
- if (!client[dest_id][client_id].svc_cnt) {
- svc->need_reset = 0x0;
pr_debug("%s: service is reset %pK\n", __func__, svc);
}
}
- if (!svc->port_cnt && !svc->svc_cnt) {
+ if (!svc->svc_cnt) {
svc->priv = NULL;
svc->id = 0;
svc->fn = NULL;
-/* Copyright (c) 2010, 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010, 2014, 2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
u32 flags;
u32 addr;
} cmd;
+ struct scm_desc desc = {0};
+
+ if (!is_scm_armv8()) {
+ cmd.addr = addr;
+ cmd.flags = flags;
+ return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
+ &cmd, sizeof(cmd), NULL, 0);
+ }
+
+ desc.args[0] = addr;
+ desc.args[1] = flags;
+ desc.arginfo = SCM_ARGS(2);
- cmd.addr = addr;
- cmd.flags = flags;
- return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
- &cmd, sizeof(cmd), NULL, 0);
+ return scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, SCM_BOOT_ADDR), &desc);
}
EXPORT_SYMBOL(scm_set_boot_addr);
mutex_unlock(&qmi_client_release_lock);
pr_info("Connection established between QMI handle and %d service\n",
data->instance_id);
- /* Register for indication messages about service */
- rc = qmi_register_ind_cb(data->clnt_handle, root_service_service_ind_cb,
- (void *)data);
- if (rc < 0)
- pr_err("Indication callback register failed(instance-id: %d) rc:%d\n",
- data->instance_id, rc);
-
mutex_lock(¬if_add_lock);
mutex_lock(&service_list_lock);
list_for_each_entry(service_notif, &service_list, list) {
}
mutex_unlock(&service_list_lock);
mutex_unlock(¬if_add_lock);
+ /* Register for indication messages about service */
+ rc = qmi_register_ind_cb(data->clnt_handle,
+ root_service_service_ind_cb, (void *)data);
+ if (rc < 0)
+ pr_err("Indication callback register failed(instance-id: %d) rc:%d\n",
+ data->instance_id, rc);
}
static void root_service_service_exit(struct qmi_client_info *data,
HW_PLATFORM_RCM = 21,
HW_PLATFORM_STP = 23,
HW_PLATFORM_SBC = 24,
+ HW_PLATFORM_ADP = 25,
HW_PLATFORM_INVALID
};
[HW_PLATFORM_DTV] = "DTV",
[HW_PLATFORM_STP] = "STP",
[HW_PLATFORM_SBC] = "SBC",
+ [HW_PLATFORM_ADP] = "ADP",
};
enum {
};
enum {
+ PLATFORM_SUBTYPE_MOJAVE_V1 = 0x0,
+ PLATFORM_SUBTYPE_MMX = 0x1,
+ PLATFORM_SUBTYPE_MOJAVE_FULL_V2 = 0x2,
+ PLATFORM_SUBTYPE_MOJAVE_BARE_V2 = 0x3,
+ PLATFORM_SUBTYPE_ADP_INVALID,
+};
+
+const char *adp_hw_platform_subtype[] = {
+ [PLATFORM_SUBTYPE_MOJAVE_V1] = "MOJAVE_V1",
+ [PLATFORM_SUBTYPE_MMX] = "MMX",
+ [PLATFORM_SUBTYPE_MOJAVE_FULL_V2] = "_MOJAVE_V2_FULL",
+ [PLATFORM_SUBTYPE_MOJAVE_BARE_V2] = "_MOJAVE_V2_BARE",
+ [PLATFORM_SUBTYPE_ADP_INVALID] = "INVALID",
+};
+
+enum {
PLATFORM_SUBTYPE_UNKNOWN = 0x0,
PLATFORM_SUBTYPE_CHARM = 0x1,
PLATFORM_SUBTYPE_STRANGE = 0x2,
/* 8996 IDs */
[246] = {MSM_CPU_8996, "MSM8996"},
- [310] = {MSM_CPU_8996, "MSM8996"},
- [311] = {MSM_CPU_8996, "APQ8096"},
[291] = {MSM_CPU_8996, "APQ8096"},
[305] = {MSM_CPU_8996, "MSM8996pro"},
+ [310] = {MSM_CPU_8996, "MSM8996"},
+ [311] = {MSM_CPU_8996, "APQ8096"},
[312] = {MSM_CPU_8996, "APQ8096pro"},
+ [315] = {MSM_CPU_8996, "MSM8996pro"},
+ [316] = {MSM_CPU_8996, "APQ8096pro"},
/* 8976 ID */
[266] = {MSM_CPU_8976, "MSM8976"},
}
return snprintf(buf, PAGE_SIZE, "%-.32s\n",
qrd_hw_platform_subtype[hw_subtype]);
+ }
+ if (socinfo_get_platform_type() == HW_PLATFORM_ADP) {
+ if (hw_subtype >= PLATFORM_SUBTYPE_ADP_INVALID) {
+ pr_err("Invalid hardware platform sub type for adp found\n");
+ hw_subtype = PLATFORM_SUBTYPE_ADP_INVALID;
+ }
+ return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+ adp_hw_platform_subtype[hw_subtype]);
} else {
if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) {
pr_err("Invalid hardware platform subtype\n");
dummy_socinfo.id = 246;
strlcpy(dummy_socinfo.build_id, "msm8996 - ",
sizeof(dummy_socinfo.build_id));
- } else if (early_machine_is_msm8996_auto()) {
- dummy_socinfo.id = 310;
- strlcpy(dummy_socinfo.build_id, "msm8996-auto - ",
- sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_msm8929()) {
dummy_socinfo.id = 268;
strlcpy(dummy_socinfo.build_id, "msm8929 - ",
mutex_unlock(&subsys_list_lock);
}
-static void for_each_subsys_device(struct subsys_device **list, unsigned count,
- void *data, void (*fn)(struct subsys_device *, void *))
+static int for_each_subsys_device(struct subsys_device **list, unsigned count,
+ void *data, int (*fn)(struct subsys_device *, void *))
{
+ int ret;
while (count--) {
struct subsys_device *dev = *list++;
if (!dev)
continue;
- fn(dev, data);
+ ret = fn(dev, data);
+ if (ret)
+ return ret;
}
+ return 0;
}
static void notify_each_subsys_device(struct subsys_device **list,
return 0;
}
-static void subsystem_shutdown(struct subsys_device *dev, void *data)
+static int subsystem_shutdown(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
+ int ret;
pr_info("[%s:%d]: Shutting down %s\n",
current->comm, current->pid, name);
- if (dev->desc->shutdown(dev->desc, true) < 0)
- panic("subsys-restart: [%s:%d]: Failed to shutdown %s!",
- current->comm, current->pid, name);
+ ret = dev->desc->shutdown(dev->desc, true);
+ if (ret < 0) {
+ if (!dev->desc->ignore_ssr_failure) {
+ panic("subsys-restart: [%s:%d]: Failed to shutdown %s!",
+ current->comm, current->pid, name);
+ } else {
+ pr_err("Shutdown failure on %s\n", name);
+ return ret;
+ }
+ }
dev->crash_count++;
subsys_set_state(dev, SUBSYS_OFFLINE);
disable_all_irqs(dev);
+
+ return 0;
}
-static void subsystem_ramdump(struct subsys_device *dev, void *data)
+static int subsystem_ramdump(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
pr_warn("%s[%s:%d]: Ramdump failed.\n",
name, current->comm, current->pid);
dev->do_ramdump_on_put = false;
+ return 0;
}
-static void subsystem_free_memory(struct subsys_device *dev, void *data)
+static int subsystem_free_memory(struct subsys_device *dev, void *data)
{
if (dev->desc->free_memory)
dev->desc->free_memory(dev->desc);
+ return 0;
}
-static void subsystem_powerup(struct subsys_device *dev, void *data)
+static int subsystem_powerup(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
int ret;
pr_info("[%s:%d]: Powering up %s\n", current->comm, current->pid, name);
init_completion(&dev->err_ready);
- if (dev->desc->powerup(dev->desc) < 0) {
+ ret = dev->desc->powerup(dev->desc);
+ if (ret < 0) {
notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
NULL);
- panic("[%s:%d]: Powerup error: %s!",
- current->comm, current->pid, name);
+ if (!dev->desc->ignore_ssr_failure) {
+ panic("[%s:%d]: Powerup error: %s!",
+ current->comm, current->pid, name);
+ } else {
+ pr_err("Powerup failure on %s\n", name);
+ return ret;
+ }
}
enable_all_irqs(dev);
if (ret) {
notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
NULL);
- panic("[%s:%d]: Timed out waiting for error ready: %s!",
- current->comm, current->pid, name);
+ if (!dev->desc->ignore_ssr_failure)
+ panic("[%s:%d]: Timed out waiting for error ready: %s!",
+ current->comm, current->pid, name);
+ else
+ return ret;
}
subsys_set_state(dev, SUBSYS_ONLINE);
subsys_set_crash_status(dev, CRASH_STATUS_NO_CRASH);
+
+ return 0;
}
static int __find_subsys(struct device *dev, void *data)
struct subsys_tracking *track;
unsigned count;
unsigned long flags;
+ int ret;
/*
* It's OK to not take the registration lock at this point.
pr_debug("[%s:%d]: Starting restart sequence for %s\n",
current->comm, current->pid, desc->name);
notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
- for_each_subsys_device(list, count, NULL, subsystem_shutdown);
+ ret = for_each_subsys_device(list, count, NULL, subsystem_shutdown);
+ if (ret)
+ goto err;
notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
for_each_subsys_device(list, count, NULL, subsystem_free_memory);
notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
- for_each_subsys_device(list, count, NULL, subsystem_powerup);
+ ret = for_each_subsys_device(list, count, NULL, subsystem_powerup);
+ if (ret)
+ goto err;
notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
pr_info("[%s:%d]: Restart sequence for %s completed.\n",
current->comm, current->pid, desc->name);
+err:
+ /* Reset subsys count */
+ if (ret)
+ dev->count = 0;
+
mutex_unlock(&soc_order_reg_lock);
mutex_unlock(&track->lock);
desc->generic_irq = ret;
}
+ desc->ignore_ssr_failure = of_property_read_bool(pdev->dev.of_node,
+ "qcom,ignore-ssr-failure");
+
order = ssr_parse_restart_orders(desc);
if (IS_ERR(order)) {
pr_err("Could not initialize SSR restart order, err = %ld\n",
* drivers/staging/android/ion/ion.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, 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
int id;
};
-static struct ion_device *ion_dev;
-
bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer)
{
return (buffer->flags & ION_FLAG_CACHED) &&
}
EXPORT_SYMBOL(ion_unmap_kernel);
-static int ion_debug_client_show(struct seq_file *s, void *unused)
+static struct mutex debugfs_mutex;
+static struct rb_root *ion_root_client;
+static int is_client_alive(struct ion_client *client)
{
- struct ion_client *client = s->private;
- struct rb_node *n, *cnode;
- bool found = false;
-
- down_write(&ion_dev->lock);
+ struct rb_node *node;
+ struct ion_client *tmp;
+ struct ion_device *dev;
- if (!client || (client->dev != ion_dev)) {
- up_write(&ion_dev->lock);
- return -EINVAL;
- }
+ node = ion_root_client->rb_node;
+ dev = container_of(ion_root_client, struct ion_device, clients);
- cnode = rb_first(&ion_dev->clients);
- for ( ; cnode; cnode = rb_next(cnode)) {
- struct ion_client *c = rb_entry(cnode,
- struct ion_client, node);
- if (client == c) {
- found = true;
- break;
+ down_read(&dev->lock);
+ while (node) {
+ tmp = rb_entry(node, struct ion_client, node);
+ if (client < tmp) {
+ node = node->rb_left;
+ } else if (client > tmp) {
+ node = node->rb_right;
+ } else {
+ up_read(&dev->lock);
+ return 1;
}
}
- if (!found) {
- up_write(&ion_dev->lock);
- return -EINVAL;
+ up_read(&dev->lock);
+ return 0;
+}
+
+static int ion_debug_client_show(struct seq_file *s, void *unused)
+{
+ struct ion_client *client = s->private;
+ struct rb_node *n;
+
+ mutex_lock(&debugfs_mutex);
+ if (!is_client_alive(client)) {
+ seq_printf(s, "ion_client 0x%pK dead, can't dump its buffers\n",
+ client);
+ mutex_unlock(&debugfs_mutex);
+ return 0;
}
seq_printf(s, "%16.16s: %16.16s : %16.16s : %12.12s\n",
seq_printf(s, "\n");
}
mutex_unlock(&client->lock);
- up_write(&ion_dev->lock);
+ mutex_unlock(&debugfs_mutex);
return 0;
}
struct rb_node *n;
pr_debug("%s: %d\n", __func__, __LINE__);
- mutex_lock(&client->lock);
+ mutex_lock(&debugfs_mutex);
while ((n = rb_first(&client->handles))) {
struct ion_handle *handle = rb_entry(n, struct ion_handle,
node);
}
idr_destroy(&client->idr);
- mutex_unlock(&client->lock);
down_write(&dev->lock);
if (client->task)
kfree(client->display_name);
kfree(client->name);
kfree(client);
+ mutex_unlock(&debugfs_mutex);
}
EXPORT_SYMBOL(ion_client_destroy);
seq_printf(s, "%16s %16s %16s\n", "client", "pid", "size");
seq_puts(s, "----------------------------------------------------\n");
- down_read(&dev->lock);
+ mutex_lock(&debugfs_mutex);
for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
struct ion_client *client = rb_entry(n, struct ion_client,
node);
client->pid, size);
}
}
- up_read(&dev->lock);
+ mutex_unlock(&debugfs_mutex);
+
seq_puts(s, "----------------------------------------------------\n");
seq_puts(s, "orphaned allocations (info is from last known client):\n");
mutex_lock(&dev->buffer_lock);
init_rwsem(&idev->lock);
plist_head_init(&idev->heaps);
idev->clients = RB_ROOT;
- ion_dev = idev;
+ ion_root_client = &idev->clients;
+ mutex_init(&debugfs_mutex);
return idev;
}
EXPORT_SYMBOL(ion_device_create);
if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH))
SET_PSTATE_REPLY_OPTIONAL(param);
/*
- * The GlobalSAN iSCSI Initiator for MacOSX does
- * not respond to MaxBurstLength, FirstBurstLength,
- * DefaultTime2Wait or DefaultTime2Retain parameter keys.
- * So, we set them to 'reply optional' here, and assume the
- * the defaults from iscsi_parameters.h if the initiator
- * is not RFC compliant and the keys are not negotiated.
- */
- if (!strcmp(param->name, MAXBURSTLENGTH))
- SET_PSTATE_REPLY_OPTIONAL(param);
- if (!strcmp(param->name, FIRSTBURSTLENGTH))
- SET_PSTATE_REPLY_OPTIONAL(param);
- if (!strcmp(param->name, DEFAULTTIME2WAIT))
- SET_PSTATE_REPLY_OPTIONAL(param);
- if (!strcmp(param->name, DEFAULTTIME2RETAIN))
- SET_PSTATE_REPLY_OPTIONAL(param);
- /*
* Required for gPXE iSCSI boot client
*/
if (!strcmp(param->name, MAXCONNECTIONS))
{
struct se_cmd *se_cmd = NULL;
int rc;
+ bool op_scsi = false;
/*
* Determine if a struct se_cmd is associated with
* this struct iscsi_cmd.
*/
switch (cmd->iscsi_opcode) {
case ISCSI_OP_SCSI_CMD:
- se_cmd = &cmd->se_cmd;
- __iscsit_free_cmd(cmd, true, shutdown);
+ op_scsi = true;
/*
* Fallthrough
*/
case ISCSI_OP_SCSI_TMFUNC:
- rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
- if (!rc && shutdown && se_cmd && se_cmd->se_sess) {
- __iscsit_free_cmd(cmd, true, shutdown);
+ se_cmd = &cmd->se_cmd;
+ __iscsit_free_cmd(cmd, op_scsi, shutdown);
+ rc = transport_generic_free_cmd(se_cmd, shutdown);
+ if (!rc && shutdown && se_cmd->se_sess) {
+ __iscsit_free_cmd(cmd, op_scsi, shutdown);
target_put_sess_cmd(se_cmd);
}
break;
struct tty_struct *tty = tty_port_tty_get(&port->port);
int i, ret;
- read_mem32((u32 *) &size, addr, 4);
+ size = __le32_to_cpu(readl(addr));
/* DBG1( "%d bytes port: %d", size, index); */
if (tty && test_bit(TTY_THROTTLED, &tty->flags)) {
*/
if (!uart_circ_empty(xmit))
tasklet_schedule(&atmel_port->tasklet);
+ else if ((port->rs485.flags & SER_RS485_ENABLED) &&
+ !(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
+ /* DMA done, stop TX, start RX for RS485 */
+ atmel_start_rx(port);
+ }
spin_unlock_irqrestore(&port->lock, flags);
}
desc->callback = atmel_complete_tx_dma;
desc->callback_param = atmel_port;
atmel_port->cookie_tx = dmaengine_submit(desc);
-
- } else {
- if (port->rs485.flags & SER_RS485_ENABLED) {
- /* DMA done, stop TX, start RX for RS485 */
- atmel_start_rx(port);
- }
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
atmel_uart_writel(port, ATMEL_PDC_TCR, 0);
atmel_port->pdc_tx.ofs = 0;
}
+ /*
+ * in uart_flush_buffer(), the xmit circular buffer has just
+ * been cleared, so we have to reset tx_len accordingly.
+ */
+ atmel_port->tx_len = 0;
}
/*
pdc_tx = atmel_uart_readl(port, ATMEL_PDC_PTSR) & ATMEL_PDC_TXTEN;
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);
+ /* Make sure that tx path is actually able to send characters */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+
uart_console_write(port, s, count, atmel_console_putchar);
/*
struct device *dev = msm_port->uart.dev;
struct dma_slave_config conf;
struct msm_dma *dma;
+ struct dma_chan *dma_chan;
u32 crci = 0;
int ret;
dma = &msm_port->tx_dma;
/* allocate DMA resources, if available */
- dma->chan = dma_request_slave_channel_reason(dev, "tx");
- if (IS_ERR(dma->chan))
+ dma_chan = dma_request_slave_channel_reason(dev, "tx");
+ if (IS_ERR(dma_chan))
goto no_tx;
+ dma->chan = dma_chan;
of_property_read_u32(dev->of_node, "qcom,tx-crci", &crci);
struct device *dev = msm_port->uart.dev;
struct dma_slave_config conf;
struct msm_dma *dma;
+ struct dma_chan *dma_chan;
u32 crci = 0;
int ret;
dma = &msm_port->rx_dma;
/* allocate DMA resources, if available */
- dma->chan = dma_request_slave_channel_reason(dev, "rx");
- if (IS_ERR(dma->chan))
+ dma_chan = dma_request_slave_channel_reason(dev, "rx");
+ if (IS_ERR(dma_chan))
goto no_rx;
+ dma->chan = dma_chan;
of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci);
snprintf(msm_port->name, sizeof(msm_port->name),
"msm_serial%d", port->line);
- ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH,
- msm_port->name, port);
- if (unlikely(ret))
- return ret;
-
/*
* UART clk must be kept enabled to
* avoid losing received character
*/
ret = clk_prepare_enable(msm_port->clk);
- if (ret) {
- goto err_clk;
+ if (ret)
return ret;
- }
ret = clk_prepare_enable(msm_port->pclk);
- if (ret) {
+ if (ret)
goto err_pclk;
- return ret;
- }
msm_serial_set_mnd_regs(port);
msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
}
+ ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH,
+ msm_port->name, port);
+ if (unlikely(ret))
+ goto err_irq;
+
return 0;
+err_irq:
+ if (msm_port->is_uartdm)
+ msm_release_dma(msm_port);
+
+ clk_disable_unprepare(msm_port->pclk);
+
err_pclk:
clk_disable_unprepare(msm_port->clk);
-err_clk:
- free_irq(port->irq, port);
return ret;
}
*/
tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength);
tbuf = kzalloc(tbuf_size, GFP_KERNEL);
- if (!tbuf)
- return -ENOMEM;
+ if (!tbuf) {
+ status = -ENOMEM;
+ goto err_alloc;
+ }
bufp = tbuf;
}
kfree(tbuf);
+ err_alloc:
/* any errors get returned through the urb completion */
spin_lock_irq(&hcd_root_hub_lock);
if (ret < 0)
return ret;
- /* The port state is unknown until the reset completes. */
- if (!(portstatus & USB_PORT_STAT_RESET))
+ /*
+ * The port state is unknown until the reset completes.
+ *
+ * On top of that, some chips may require additional time
+ * to re-establish a connection after the reset is complete,
+ * so also wait for the connection to be re-established.
+ */
+ if (!(portstatus & USB_PORT_STAT_RESET) &&
+ (portstatus & USB_PORT_STAT_CONNECTION))
break;
/* switch to the long delay after two short delay failures */
}
}
- power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPE, &pval);
+ power_supply_get_property(mdwc->usb_psy,
+ POWER_SUPPLY_PROP_REAL_TYPE, &pval);
if (pval.intval != POWER_SUPPLY_TYPE_USB)
return 0;
int status)
{
struct dwc3 *dwc = dep->dwc;
+ unsigned int unmap_after_complete = false;
int i;
if (req->queued) {
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number <= 1)
+ /*
+ * NOTICE we don't want to unmap before calling ->complete() if we're
+ * dealing with a bounced ep0 request. If we unmap it here, we would end
+ * up overwritting the contents of req->buf and this could confuse the
+ * gadget driver.
+ */
+ if (dwc->ep0_bounced && dep->number <= 1) {
dwc->ep0_bounced = false;
-
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+ unmap_after_complete = true;
+ } else {
+ usb_gadget_unmap_request(&dwc->gadget,
+ &req->request, req->direction);
+ }
dev_dbg(dwc->dev, "request %pK from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
+
+ if (unmap_after_complete)
+ usb_gadget_unmap_request(&dwc->gadget,
+ &req->request, req->direction);
}
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+ .bLength = sizeof(ss_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */
static struct usb_ms_endpoint_descriptor_16 ms_in_desc = {
/* .bLength = DYNAMIC */
static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_descriptor_header **midi_function;
+ struct usb_descriptor_header **midi_ss_function;
struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS];
struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS];
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
struct usb_string *us;
- int status, n, jack = 1, i = 0;
+ int status, n, jack = 1, i = 0, j = 0;
midi->gadget = cdev->gadget;
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
if (!midi->out_ep)
goto fail;
+ /* allocate temporary function list for ss */
+ midi_ss_function = kcalloc((MAX_PORTS * 4) + 11,
+ sizeof(*midi_ss_function), GFP_KERNEL);
+ if (!midi_ss_function) {
+ status = -ENOMEM;
+ goto fail;
+ }
+
/* allocate temporary function list */
midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function),
GFP_KERNEL);
if (!midi_function) {
status = -ENOMEM;
+ kfree(midi_ss_function);
goto fail;
}
midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc;
midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc;
midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) &ac_interface_desc;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) &ac_header_desc;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) &ms_interface_desc;
/* calculate the header's wTotalLength */
n = USB_DT_MS_HEADER_SIZE
ms_header_desc.wTotalLength = cpu_to_le16(n);
midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) &ms_header_desc;
/* configure the external IN jacks, each linked to an embedded OUT jack */
for (n = 0; n < midi->in_ports; n++) {
in_ext->bJackID = jack++;
in_ext->iJack = 0;
midi_function[i++] = (struct usb_descriptor_header *) in_ext;
+ midi_ss_function[j++] = (struct usb_descriptor_header *) in_ext;
out_emb->bLength = USB_DT_MIDI_OUT_SIZE(1);
out_emb->bDescriptorType = USB_DT_CS_INTERFACE;
out_emb->pins[0].baSourceID = in_ext->bJackID;
out_emb->iJack = 0;
midi_function[i++] = (struct usb_descriptor_header *) out_emb;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) out_emb;
/* link it to the endpoint */
ms_in_desc.baAssocJackID[n] = out_emb->bJackID;
in_emb->bJackID = jack++;
in_emb->iJack = 0;
midi_function[i++] = (struct usb_descriptor_header *) in_emb;
+ midi_ss_function[j++] = (struct usb_descriptor_header *) in_emb;
out_ext->bLength = USB_DT_MIDI_OUT_SIZE(1);
out_ext->bDescriptorType = USB_DT_CS_INTERFACE;
out_ext->pins[0].baSourceID = in_emb->bJackID;
out_ext->pins[0].baSourcePin = 1;
midi_function[i++] = (struct usb_descriptor_header *) out_ext;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) out_ext;
/* link it to the endpoint */
ms_out_desc.baAssocJackID[n] = in_emb->bJackID;
midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc;
midi_function[i++] = NULL;
+ midi_ss_function[j++] = (struct usb_descriptor_header *) &bulk_out_desc;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc;
+ midi_ss_function[j++] = (struct usb_descriptor_header *) &ms_out_desc;
+ midi_ss_function[j++] = (struct usb_descriptor_header *) &bulk_in_desc;
+ midi_ss_function[j++] =
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc;
+ midi_ss_function[j++] = (struct usb_descriptor_header *) &ms_in_desc;
+ midi_ss_function[j++] = NULL;
+
/*
* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
goto fail_f_midi;
}
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024);
+ bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024);
+ f->ss_descriptors = usb_copy_descriptors(midi_ss_function);
+ if (!f->ss_descriptors)
+ goto fail_f_midi;
+ }
+
kfree(midi_function);
+ kfree(midi_ss_function);
return 0;
fail_f_midi:
kfree(midi_function);
usb_free_descriptors(f->hs_descriptors);
+ kfree(midi_ss_function);
fail:
f_midi_unregister_card(midi);
fail_register:
spinlock_t req_lock; /* guard {rx,tx}_reqs */
struct list_head tx_reqs, rx_reqs;
- unsigned tx_qlen;
+ atomic_t tx_qlen;
/* Minimum number of TX USB request queued to UDC */
#define TX_REQ_THRESHOLD 5
int no_tx_req_used;
dev_kfree_skb_any(skb);
}
+ atomic_dec(&dev->tx_qlen);
if (netif_carrier_ok(dev->net))
netif_wake_queue(dev->net);
}
req->length = length;
- /* throttle highspeed IRQ rate back slightly */
- if (gadget_is_dualspeed(dev->gadget) &&
- (dev->gadget->speed == USB_SPEED_HIGH) &&
- !list_empty(&dev->tx_reqs)) {
- dev->tx_qlen++;
- if (dev->tx_qlen == (dev->qmult/2)) {
- req->no_interrupt = 0;
- dev->tx_qlen = 0;
- } else {
- req->no_interrupt = 1;
- }
- } else {
- req->no_interrupt = 0;
- }
+ /* throttle high/super speed IRQ rate back slightly */
+ if (gadget_is_dualspeed(dev->gadget))
+ req->no_interrupt = (((dev->gadget->speed == USB_SPEED_HIGH ||
+ dev->gadget->speed == USB_SPEED_SUPER)) &&
+ !list_empty(&dev->tx_reqs))
+ ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0)
+ : 0;
retval = usb_ep_queue(in, req, GFP_ATOMIC);
switch (retval) {
break;
case 0:
net->trans_start = jiffies;
+ atomic_inc(&dev->tx_qlen);
}
if (retval) {
rx_fill(dev, gfp_flags);
/* and open the tx floodgates */
- dev->tx_qlen = 0;
+ atomic_set(&dev->tx_qlen, 0);
netif_wake_queue(dev->net);
}
To compile this driver as a module, choose M here: the
module will be called chaoskey.
+
+config USB_QTI_KS_BRIDGE
+ tristate "USB QTI kick start bridge"
+ depends on USB
+ help
+ Say Y here if you have a QTI modem device connected via USB that
+ will be bridged in kernel space. This driver works as a bridge to pass
+ boot images, ram-dumps and efs sync.
+ To compile this driver as a module, choose M here: the module
+ will be called ks_bridge. If unsure, choose N.
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
+
+obj-$(CONFIG_USB_QTI_KS_BRIDGE) += ks_bridge.o
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, 2017, Linux Foundation. All rights reserved.
+ *
+ * This 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.
+ */
+
+/* add additional information to our printk's */
+#define pr_fmt(fmt) "%s: " fmt "\n", __func__
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/platform_device.h>
+#include <linux/ratelimit.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+
+#define DRIVER_DESC "USB host ks bridge driver"
+
+enum bus_id {
+ BUS_HSIC,
+ BUS_USB,
+ BUS_UNDEF,
+};
+
+#define BUSNAME_LEN 20
+
+static enum bus_id str_to_busid(const char *name)
+{
+ if (!strncasecmp("msm_hsic_host", name, BUSNAME_LEN))
+ return BUS_HSIC;
+ if (!strncasecmp("msm_ehci_host.0", name, BUSNAME_LEN))
+ return BUS_USB;
+ if (!strncasecmp("xhci-hcd.0.auto", name, BUSNAME_LEN))
+ return BUS_USB;
+
+ return BUS_UNDEF;
+}
+
+struct data_pkt {
+ int n_read;
+ char *buf;
+ size_t len;
+ struct list_head list;
+ void *ctxt;
+};
+
+#define FILE_OPENED BIT(0)
+#define USB_DEV_CONNECTED BIT(1)
+#define NO_RX_REQS 10
+#define NO_BRIDGE_INSTANCES 4
+#define EFS_HSIC_BRIDGE_INDEX 2
+#define EFS_USB_BRIDGE_INDEX 3
+#define MAX_DATA_PKT_SIZE 16384
+#define PENDING_URB_TIMEOUT 10
+
+struct ksb_dev_info {
+ const char *name;
+};
+
+struct ks_bridge {
+ char *name;
+ spinlock_t lock;
+ struct workqueue_struct *wq;
+ struct work_struct to_mdm_work;
+ struct work_struct start_rx_work;
+ struct list_head to_mdm_list;
+ struct list_head to_ks_list;
+ wait_queue_head_t ks_wait_q;
+ wait_queue_head_t pending_urb_wait;
+ atomic_t tx_pending_cnt;
+ atomic_t rx_pending_cnt;
+
+ struct ksb_dev_info id_info;
+
+ /* cdev interface */
+ dev_t cdev_start_no;
+ struct cdev cdev;
+ struct class *class;
+ struct device *device;
+
+ /* usb specific */
+ struct usb_device *udev;
+ struct usb_interface *ifc;
+ __u8 in_epAddr;
+ __u8 out_epAddr;
+ unsigned int in_pipe;
+ unsigned int out_pipe;
+ struct usb_anchor submitted;
+
+ unsigned long flags;
+
+ /* to handle INT IN ep */
+ unsigned int period;
+
+#define DBG_MSG_LEN 40
+#define DBG_MAX_MSG 500
+ unsigned int dbg_idx;
+ rwlock_t dbg_lock;
+
+ char (dbgbuf[DBG_MAX_MSG])[DBG_MSG_LEN]; /* buffer */
+};
+
+struct ks_bridge *__ksb[NO_BRIDGE_INSTANCES];
+
+/* by default debugging is enabled */
+static unsigned int enable_dbg = 1;
+module_param(enable_dbg, uint, S_IRUGO | S_IWUSR);
+
+static void
+dbg_log_event(struct ks_bridge *ksb, char *event, int d1, int d2)
+{
+ unsigned long flags;
+ unsigned long long t;
+ unsigned long nanosec;
+
+ if (!enable_dbg)
+ return;
+
+ write_lock_irqsave(&ksb->dbg_lock, flags);
+ t = cpu_clock(smp_processor_id());
+ nanosec = do_div(t, 1000000000)/1000;
+ scnprintf(ksb->dbgbuf[ksb->dbg_idx], DBG_MSG_LEN, "%5lu.%06lu:%s:%x:%x",
+ (unsigned long)t, nanosec, event, d1, d2);
+
+ ksb->dbg_idx++;
+ ksb->dbg_idx = ksb->dbg_idx % DBG_MAX_MSG;
+ write_unlock_irqrestore(&ksb->dbg_lock, flags);
+}
+
+static
+struct data_pkt *ksb_alloc_data_pkt(size_t count, gfp_t flags, void *ctxt)
+{
+ struct data_pkt *pkt;
+
+ pkt = kzalloc(sizeof(struct data_pkt), flags);
+ if (!pkt)
+ return ERR_PTR(-ENOMEM);
+
+ pkt->buf = kmalloc(count, flags);
+ if (!pkt->buf) {
+ kfree(pkt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pkt->len = count;
+ INIT_LIST_HEAD(&pkt->list);
+ pkt->ctxt = ctxt;
+
+ return pkt;
+}
+
+static void ksb_free_data_pkt(struct data_pkt *pkt)
+{
+ kfree(pkt->buf);
+ kfree(pkt);
+}
+
+
+static void
+submit_one_urb(struct ks_bridge *ksb, gfp_t flags, struct data_pkt *pkt);
+static ssize_t ksb_fs_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ int ret;
+ unsigned long flags;
+ struct ks_bridge *ksb = fp->private_data;
+ struct data_pkt *pkt = NULL;
+ size_t space, copied;
+
+read_start:
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return -ENODEV;
+
+ spin_lock_irqsave(&ksb->lock, flags);
+ if (list_empty(&ksb->to_ks_list)) {
+ spin_unlock_irqrestore(&ksb->lock, flags);
+ ret = wait_event_interruptible(ksb->ks_wait_q,
+ !list_empty(&ksb->to_ks_list) ||
+ !test_bit(USB_DEV_CONNECTED, &ksb->flags));
+ if (ret < 0)
+ return ret;
+
+ goto read_start;
+ }
+
+ space = count;
+ copied = 0;
+ while (!list_empty(&ksb->to_ks_list) && space &&
+ test_bit(USB_DEV_CONNECTED, &ksb->flags)) {
+ size_t len;
+
+ pkt = list_first_entry(&ksb->to_ks_list, struct data_pkt, list);
+ list_del_init(&pkt->list);
+ len = min_t(size_t, space, pkt->len - pkt->n_read);
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ ret = copy_to_user(buf + copied, pkt->buf + pkt->n_read, len);
+ if (ret) {
+ dev_err(ksb->device,
+ "copy_to_user failed err:%d\n", ret);
+ ksb_free_data_pkt(pkt);
+ return -EFAULT;
+ }
+
+ pkt->n_read += len;
+ space -= len;
+ copied += len;
+
+ if (pkt->n_read == pkt->len) {
+ /*
+ * re-init the packet and queue it
+ * for more data.
+ */
+ pkt->n_read = 0;
+ pkt->len = MAX_DATA_PKT_SIZE;
+ submit_one_urb(ksb, GFP_KERNEL, pkt);
+ pkt = NULL;
+ }
+ spin_lock_irqsave(&ksb->lock, flags);
+ }
+
+ /* put the partial packet back in the list */
+ if (!space && pkt && pkt->n_read != pkt->len) {
+ if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ list_add(&pkt->list, &ksb->to_ks_list);
+ else
+ ksb_free_data_pkt(pkt);
+ }
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ dbg_log_event(ksb, "KS_READ", copied, 0);
+
+ dev_dbg(ksb->device, "count:%zu space:%zu copied:%zu", count,
+ space, copied);
+
+ return copied;
+}
+
+static void ksb_tx_cb(struct urb *urb)
+{
+ struct data_pkt *pkt = urb->context;
+ struct ks_bridge *ksb = pkt->ctxt;
+
+ dbg_log_event(ksb, "C TX_URB", urb->status, 0);
+ dev_dbg(&ksb->udev->dev, "status:%d", urb->status);
+
+ if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ usb_autopm_put_interface_async(ksb->ifc);
+
+ if (urb->status < 0)
+ pr_err_ratelimited("%s: urb failed with err:%d",
+ ksb->id_info.name, urb->status);
+
+ ksb_free_data_pkt(pkt);
+
+ atomic_dec(&ksb->tx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
+}
+
+static void ksb_tomdm_work(struct work_struct *w)
+{
+ struct ks_bridge *ksb = container_of(w, struct ks_bridge, to_mdm_work);
+ struct data_pkt *pkt;
+ unsigned long flags;
+ struct urb *urb;
+ int ret;
+
+ spin_lock_irqsave(&ksb->lock, flags);
+ while (!list_empty(&ksb->to_mdm_list)
+ && test_bit(USB_DEV_CONNECTED, &ksb->flags)) {
+ pkt = list_first_entry(&ksb->to_mdm_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dbg_log_event(ksb, "TX_URB_MEM_FAIL", -ENOMEM, 0);
+ pr_err_ratelimited("%s: unable to allocate urb",
+ ksb->id_info.name);
+ ksb_free_data_pkt(pkt);
+ return;
+ }
+
+ ret = usb_autopm_get_interface(ksb->ifc);
+ if (ret < 0 && ret != -EAGAIN && ret != -EACCES) {
+ dbg_log_event(ksb, "TX_URB_AUTOPM_FAIL", ret, 0);
+ pr_err_ratelimited("%s: autopm_get failed:%d",
+ ksb->id_info.name, ret);
+ usb_free_urb(urb);
+ ksb_free_data_pkt(pkt);
+ return;
+ }
+ usb_fill_bulk_urb(urb, ksb->udev, ksb->out_pipe,
+ pkt->buf, pkt->len, ksb_tx_cb, pkt);
+ usb_anchor_urb(urb, &ksb->submitted);
+
+ dbg_log_event(ksb, "S TX_URB", pkt->len, 0);
+
+ atomic_inc(&ksb->tx_pending_cnt);
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ dev_err(&ksb->udev->dev, "out urb submission failed");
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ ksb_free_data_pkt(pkt);
+ usb_autopm_put_interface(ksb->ifc);
+ atomic_dec(&ksb->tx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
+ return;
+ }
+
+ usb_free_urb(urb);
+
+ spin_lock_irqsave(&ksb->lock, flags);
+ }
+ spin_unlock_irqrestore(&ksb->lock, flags);
+}
+
+static ssize_t ksb_fs_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ int ret;
+ struct data_pkt *pkt;
+ unsigned long flags;
+ struct ks_bridge *ksb = fp->private_data;
+
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return -ENODEV;
+
+ if (count > MAX_DATA_PKT_SIZE)
+ count = MAX_DATA_PKT_SIZE;
+
+ pkt = ksb_alloc_data_pkt(count, GFP_KERNEL, ksb);
+ if (IS_ERR(pkt)) {
+ dev_err(ksb->device,
+ "unable to allocate data packet");
+ return PTR_ERR(pkt);
+ }
+
+ ret = copy_from_user(pkt->buf, buf, count);
+ if (ret) {
+ dev_err(ksb->device,
+ "copy_from_user failed: err:%d", ret);
+ ksb_free_data_pkt(pkt);
+ return ret;
+ }
+
+ spin_lock_irqsave(&ksb->lock, flags);
+ list_add_tail(&pkt->list, &ksb->to_mdm_list);
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ queue_work(ksb->wq, &ksb->to_mdm_work);
+
+ dbg_log_event(ksb, "KS_WRITE", count, 0);
+
+ return count;
+}
+
+static int ksb_fs_open(struct inode *ip, struct file *fp)
+{
+ struct ks_bridge *ksb =
+ container_of(ip->i_cdev, struct ks_bridge, cdev);
+
+ if (IS_ERR(ksb)) {
+ pr_err("ksb device not found");
+ return -ENODEV;
+ }
+
+ dev_dbg(ksb->device, ":%s", ksb->id_info.name);
+ dbg_log_event(ksb, "FS-OPEN", 0, 0);
+
+ fp->private_data = ksb;
+ set_bit(FILE_OPENED, &ksb->flags);
+
+ if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ queue_work(ksb->wq, &ksb->start_rx_work);
+
+ return 0;
+}
+
+static unsigned int ksb_fs_poll(struct file *file, poll_table *wait)
+{
+ struct ks_bridge *ksb = file->private_data;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return POLLERR;
+
+ poll_wait(file, &ksb->ks_wait_q, wait);
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ return POLLERR;
+
+ spin_lock_irqsave(&ksb->lock, flags);
+ if (!list_empty(&ksb->to_ks_list))
+ ret = POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ return ret;
+}
+
+static int ksb_fs_release(struct inode *ip, struct file *fp)
+{
+ struct ks_bridge *ksb = fp->private_data;
+
+ if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ dev_dbg(ksb->device, ":%s", ksb->id_info.name);
+ dbg_log_event(ksb, "FS-RELEASE", 0, 0);
+
+ clear_bit(FILE_OPENED, &ksb->flags);
+ fp->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations ksb_fops = {
+ .owner = THIS_MODULE,
+ .read = ksb_fs_read,
+ .write = ksb_fs_write,
+ .open = ksb_fs_open,
+ .release = ksb_fs_release,
+ .poll = ksb_fs_poll,
+};
+
+static struct ksb_dev_info ksb_fboot_dev[] = {
+ {
+ .name = "ks_hsic_bridge",
+ },
+ {
+ .name = "ks_usb_bridge",
+ },
+};
+
+static struct ksb_dev_info ksb_efs_hsic_dev = {
+ .name = "efs_hsic_bridge",
+};
+
+static struct ksb_dev_info ksb_efs_usb_dev = {
+ .name = "efs_usb_bridge",
+};
+static const struct usb_device_id ksb_usb_ids[] = {
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9008, 0),
+ .driver_info = (unsigned long)&ksb_fboot_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9025, 0), },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9091, 0), },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x901D, 0), },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x900E, 0), },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904C, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 2),
+ .driver_info = (unsigned long)&ksb_efs_usb_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x908A, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x908E, 3),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909C, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909D, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909E, 3),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909F, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x90A0, 2),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x90A4, 3),
+ .driver_info = (unsigned long)&ksb_efs_hsic_dev, },
+
+ {} /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, ksb_usb_ids);
+
+static void ksb_rx_cb(struct urb *urb);
+static void
+submit_one_urb(struct ks_bridge *ksb, gfp_t flags, struct data_pkt *pkt)
+{
+ struct urb *urb;
+ int ret;
+
+ urb = usb_alloc_urb(0, flags);
+ if (!urb) {
+ dev_err(&ksb->udev->dev, "unable to allocate urb");
+ ksb_free_data_pkt(pkt);
+ return;
+ }
+
+ if (ksb->period)
+ usb_fill_int_urb(urb, ksb->udev, ksb->in_pipe,
+ pkt->buf, pkt->len,
+ ksb_rx_cb, pkt, ksb->period);
+ else
+ usb_fill_bulk_urb(urb, ksb->udev, ksb->in_pipe,
+ pkt->buf, pkt->len,
+ ksb_rx_cb, pkt);
+
+ usb_anchor_urb(urb, &ksb->submitted);
+
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) {
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ ksb_free_data_pkt(pkt);
+ return;
+ }
+
+ atomic_inc(&ksb->rx_pending_cnt);
+ ret = usb_submit_urb(urb, flags);
+ if (ret) {
+ dev_err(&ksb->udev->dev, "in urb submission failed");
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ ksb_free_data_pkt(pkt);
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
+ return;
+ }
+
+ dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+
+ usb_free_urb(urb);
+}
+static void ksb_rx_cb(struct urb *urb)
+{
+ struct data_pkt *pkt = urb->context;
+ struct ks_bridge *ksb = pkt->ctxt;
+ bool wakeup = true;
+
+ dbg_log_event(ksb, "C RX_URB", urb->status, urb->actual_length);
+
+ dev_dbg(&ksb->udev->dev, "status:%d actual:%d", urb->status,
+ urb->actual_length);
+
+ /*non zero len of data received while unlinking urb*/
+ if (urb->status == -ENOENT && (urb->actual_length > 0)) {
+ /*
+ * If we wakeup the reader process now, it may
+ * queue the URB before its reject flag gets
+ * cleared.
+ */
+ wakeup = false;
+ goto add_to_list;
+ }
+
+ if (urb->status < 0) {
+ if (urb->status != -ESHUTDOWN && urb->status != -ENOENT
+ && urb->status != -EPROTO)
+ pr_err_ratelimited("%s: urb failed with err:%d",
+ ksb->id_info.name, urb->status);
+
+ if (!urb->actual_length) {
+ ksb_free_data_pkt(pkt);
+ goto done;
+ }
+ }
+
+ usb_mark_last_busy(ksb->udev);
+
+ if (urb->actual_length == 0) {
+ submit_one_urb(ksb, GFP_ATOMIC, pkt);
+ goto done;
+ }
+
+add_to_list:
+ spin_lock(&ksb->lock);
+ pkt->len = urb->actual_length;
+ list_add_tail(&pkt->list, &ksb->to_ks_list);
+ spin_unlock(&ksb->lock);
+ /* wake up read thread */
+ if (wakeup)
+ wake_up(&ksb->ks_wait_q);
+done:
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
+}
+
+static void ksb_start_rx_work(struct work_struct *w)
+{
+ struct ks_bridge *ksb =
+ container_of(w, struct ks_bridge, start_rx_work);
+ struct data_pkt *pkt;
+ struct urb *urb;
+ int i = 0;
+ int ret;
+ bool put = true;
+
+ ret = usb_autopm_get_interface(ksb->ifc);
+ if (ret < 0) {
+ if (ret != -EAGAIN && ret != -EACCES) {
+ pr_err_ratelimited("%s: autopm_get failed:%d",
+ ksb->id_info.name, ret);
+ return;
+ }
+ put = false;
+ }
+ for (i = 0; i < NO_RX_REQS; i++) {
+
+ if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ break;
+
+ pkt = ksb_alloc_data_pkt(MAX_DATA_PKT_SIZE, GFP_KERNEL, ksb);
+ if (IS_ERR(pkt)) {
+ dev_err(&ksb->udev->dev, "unable to allocate data pkt");
+ break;
+ }
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&ksb->udev->dev, "unable to allocate urb");
+ ksb_free_data_pkt(pkt);
+ break;
+ }
+
+ if (ksb->period)
+ usb_fill_int_urb(urb, ksb->udev, ksb->in_pipe,
+ pkt->buf, pkt->len,
+ ksb_rx_cb, pkt, ksb->period);
+ else
+ usb_fill_bulk_urb(urb, ksb->udev, ksb->in_pipe,
+ pkt->buf, pkt->len,
+ ksb_rx_cb, pkt);
+
+ usb_anchor_urb(urb, &ksb->submitted);
+
+ dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+
+ atomic_inc(&ksb->rx_pending_cnt);
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ dev_err(&ksb->udev->dev, "in urb submission failed");
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ ksb_free_data_pkt(pkt);
+ atomic_dec(&ksb->rx_pending_cnt);
+ wake_up(&ksb->pending_urb_wait);
+ break;
+ }
+
+ usb_free_urb(urb);
+ }
+ if (put)
+ usb_autopm_put_interface_async(ksb->ifc);
+}
+
+static int
+ksb_usb_probe(struct usb_interface *ifc, const struct usb_device_id *id)
+{
+ __u8 ifc_num, ifc_count, ksb_port_num;
+ struct usb_host_interface *ifc_desc;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i;
+ struct ks_bridge *ksb;
+ unsigned long flags;
+ struct data_pkt *pkt;
+ struct ksb_dev_info *mdev, *fbdev;
+ struct usb_device *udev;
+ unsigned int bus_id;
+ int ret;
+ bool free_mdev = false;
+
+ ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
+
+ udev = interface_to_usbdev(ifc);
+ ifc_count = udev->actconfig->desc.bNumInterfaces;
+ fbdev = mdev = (struct ksb_dev_info *)id->driver_info;
+
+ bus_id = str_to_busid(udev->bus->bus_name);
+ if (bus_id == BUS_UNDEF) {
+ dev_err(&udev->dev, "unknown usb bus %s, probe failed\n",
+ udev->bus->bus_name);
+ return -ENODEV;
+ }
+
+ switch (id->idProduct) {
+ case 0x900E:
+ case 0x9025:
+ case 0x9091:
+ case 0x901D:
+ /* 1-1 mapping between ksb and udev port which starts with 1 */
+ ksb_port_num = udev->portnum - 1;
+ dev_dbg(&udev->dev, "ifc_count: %u, port_num:%u\n", ifc_count,
+ ksb_port_num);
+ if (ifc_count > 1)
+ return -ENODEV;
+ if (ksb_port_num >= NO_BRIDGE_INSTANCES) {
+ dev_err(&udev->dev, "port-num:%u invalid. Try first\n",
+ ksb_port_num);
+ ksb_port_num = 0;
+ }
+ ksb = __ksb[ksb_port_num];
+ if (ksb->ifc) {
+ dev_err(&udev->dev, "port already in use\n");
+ return -ENODEV;
+ }
+ mdev = kzalloc(sizeof(struct ksb_dev_info), GFP_KERNEL);
+ if (!mdev)
+ return -ENOMEM;
+ free_mdev = true;
+ mdev->name = ksb->name;
+ break;
+ case 0x9008:
+ ksb = __ksb[bus_id];
+ mdev = &fbdev[bus_id];
+ break;
+ case 0x9048:
+ case 0x904C:
+ case 0x9075:
+ case 0x908A:
+ case 0x908E:
+ case 0x90A0:
+ case 0x909C:
+ case 0x909D:
+ case 0x909E:
+ case 0x909F:
+ case 0x90A4:
+ ksb = __ksb[EFS_HSIC_BRIDGE_INDEX];
+ break;
+ case 0x9079:
+ if (ifc_num != 2)
+ return -ENODEV;
+ ksb = __ksb[EFS_USB_BRIDGE_INDEX];
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ if (!ksb) {
+ pr_err("ksb is not initialized");
+ return -ENODEV;
+ }
+
+ ksb->udev = usb_get_dev(interface_to_usbdev(ifc));
+ ksb->ifc = ifc;
+ ifc_desc = ifc->cur_altsetting;
+ ksb->id_info = *mdev;
+
+ for (i = 0; i < ifc_desc->desc.bNumEndpoints; i++) {
+ ep_desc = &ifc_desc->endpoint[i].desc;
+
+ if (!ksb->in_epAddr && (usb_endpoint_is_bulk_in(ep_desc))) {
+ ksb->in_epAddr = ep_desc->bEndpointAddress;
+ ksb->period = 0;
+ }
+
+ if (!ksb->in_epAddr && (usb_endpoint_is_int_in(ep_desc))) {
+ ksb->in_epAddr = ep_desc->bEndpointAddress;
+ ksb->period = ep_desc->bInterval;
+ }
+
+ if (!ksb->out_epAddr && usb_endpoint_is_bulk_out(ep_desc))
+ ksb->out_epAddr = ep_desc->bEndpointAddress;
+ }
+
+ if (!(ksb->in_epAddr && ksb->out_epAddr)) {
+ dev_err(&udev->dev,
+ "could not find bulk in and bulk out endpoints");
+ usb_put_dev(ksb->udev);
+ ksb->ifc = NULL;
+ if (free_mdev)
+ kfree(mdev);
+ return -ENODEV;
+ }
+
+ ksb->in_pipe = ksb->period ?
+ usb_rcvintpipe(ksb->udev, ksb->in_epAddr) :
+ usb_rcvbulkpipe(ksb->udev, ksb->in_epAddr);
+
+ ksb->out_pipe = usb_sndbulkpipe(ksb->udev, ksb->out_epAddr);
+
+ usb_set_intfdata(ifc, ksb);
+ set_bit(USB_DEV_CONNECTED, &ksb->flags);
+ atomic_set(&ksb->tx_pending_cnt, 0);
+ atomic_set(&ksb->rx_pending_cnt, 0);
+
+ dbg_log_event(ksb, "PID-ATT", id->idProduct, 0);
+
+ /*free up stale buffers if any from previous disconnect*/
+ spin_lock_irqsave(&ksb->lock, flags);
+ while (!list_empty(&ksb->to_ks_list)) {
+ pkt = list_first_entry(&ksb->to_ks_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ }
+ while (!list_empty(&ksb->to_mdm_list)) {
+ pkt = list_first_entry(&ksb->to_mdm_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ }
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ ret = alloc_chrdev_region(&ksb->cdev_start_no, 0, 1, mdev->name);
+ if (ret < 0) {
+ dbg_log_event(ksb, "chr reg failed", ret, 0);
+ goto fail_chrdev_region;
+ }
+
+ ksb->class = class_create(THIS_MODULE, mdev->name);
+ if (IS_ERR(ksb->class)) {
+ dbg_log_event(ksb, "clscr failed", PTR_ERR(ksb->class), 0);
+ goto fail_class_create;
+ }
+
+ cdev_init(&ksb->cdev, &ksb_fops);
+ ksb->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&ksb->cdev, ksb->cdev_start_no, 1);
+ if (ret < 0) {
+ dbg_log_event(ksb, "cdev_add failed", ret, 0);
+ goto fail_class_create;
+ }
+
+ ksb->device = device_create(ksb->class, &udev->dev, ksb->cdev_start_no,
+ NULL, mdev->name);
+ if (IS_ERR(ksb->device)) {
+ dbg_log_event(ksb, "devcrfailed", PTR_ERR(ksb->device), 0);
+ goto fail_device_create;
+ }
+
+ if (device_can_wakeup(&ksb->udev->dev))
+ ifc->needs_remote_wakeup = 1;
+
+ if (free_mdev)
+ kfree(mdev);
+ dev_dbg(&udev->dev, "usb dev connected");
+
+ return 0;
+
+fail_device_create:
+ cdev_del(&ksb->cdev);
+fail_class_create:
+ unregister_chrdev_region(ksb->cdev_start_no, 1);
+fail_chrdev_region:
+ usb_set_intfdata(ifc, NULL);
+ clear_bit(USB_DEV_CONNECTED, &ksb->flags);
+
+ if (free_mdev)
+ kfree(mdev);
+
+ return -ENODEV;
+
+}
+
+static int ksb_usb_suspend(struct usb_interface *ifc, pm_message_t message)
+{
+ struct ks_bridge *ksb = usb_get_intfdata(ifc);
+ unsigned long flags;
+
+ dbg_log_event(ksb, "SUSPEND", 0, 0);
+
+ if (pm_runtime_autosuspend_expiration(&ksb->udev->dev)) {
+ dbg_log_event(ksb, "SUSP ABORT-TimeCheck", 0, 0);
+ return -EBUSY;
+ }
+
+ usb_kill_anchored_urbs(&ksb->submitted);
+
+ spin_lock_irqsave(&ksb->lock, flags);
+ if (!list_empty(&ksb->to_ks_list)) {
+ spin_unlock_irqrestore(&ksb->lock, flags);
+ dbg_log_event(ksb, "SUSPEND ABORT", 0, 0);
+ /*
+ * Now wakeup the reader process and queue
+ * Rx URBs for more data.
+ */
+ wake_up(&ksb->ks_wait_q);
+ queue_work(ksb->wq, &ksb->start_rx_work);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ return 0;
+}
+
+static int ksb_usb_resume(struct usb_interface *ifc)
+{
+ struct ks_bridge *ksb = usb_get_intfdata(ifc);
+
+ dbg_log_event(ksb, "RESUME", 0, 0);
+
+ if (test_bit(FILE_OPENED, &ksb->flags))
+ queue_work(ksb->wq, &ksb->start_rx_work);
+
+ return 0;
+}
+
+static void ksb_usb_disconnect(struct usb_interface *ifc)
+{
+ struct ks_bridge *ksb = usb_get_intfdata(ifc);
+ unsigned long flags;
+ struct data_pkt *pkt;
+
+ dbg_log_event(ksb, "PID-DETACH", 0, 0);
+
+ clear_bit(USB_DEV_CONNECTED, &ksb->flags);
+ wake_up(&ksb->ks_wait_q);
+ cancel_work_sync(&ksb->to_mdm_work);
+ cancel_work_sync(&ksb->start_rx_work);
+
+ device_destroy(ksb->class, ksb->cdev_start_no);
+ cdev_del(&ksb->cdev);
+ class_destroy(ksb->class);
+ unregister_chrdev_region(ksb->cdev_start_no, 1);
+
+ usb_kill_anchored_urbs(&ksb->submitted);
+
+ wait_event_interruptible_timeout(
+ ksb->pending_urb_wait,
+ !atomic_read(&ksb->tx_pending_cnt) &&
+ !atomic_read(&ksb->rx_pending_cnt),
+ msecs_to_jiffies(PENDING_URB_TIMEOUT));
+
+ spin_lock_irqsave(&ksb->lock, flags);
+ while (!list_empty(&ksb->to_ks_list)) {
+ pkt = list_first_entry(&ksb->to_ks_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ }
+ while (!list_empty(&ksb->to_mdm_list)) {
+ pkt = list_first_entry(&ksb->to_mdm_list,
+ struct data_pkt, list);
+ list_del_init(&pkt->list);
+ ksb_free_data_pkt(pkt);
+ }
+ spin_unlock_irqrestore(&ksb->lock, flags);
+
+ ifc->needs_remote_wakeup = 0;
+ usb_put_dev(ksb->udev);
+ ksb->ifc = NULL;
+ usb_set_intfdata(ifc, NULL);
+}
+
+static struct usb_driver ksb_usb_driver = {
+ .name = "ks_bridge",
+ .probe = ksb_usb_probe,
+ .disconnect = ksb_usb_disconnect,
+ .suspend = ksb_usb_suspend,
+ .resume = ksb_usb_resume,
+ .reset_resume = ksb_usb_resume,
+ .id_table = ksb_usb_ids,
+ .supports_autosuspend = 1,
+};
+
+static int ksb_debug_show(struct seq_file *s, void *unused)
+{
+ unsigned long flags;
+ struct ks_bridge *ksb = s->private;
+ int i;
+
+ read_lock_irqsave(&ksb->dbg_lock, flags);
+ for (i = 0; i < DBG_MAX_MSG; i++) {
+ if (i == (ksb->dbg_idx - 1))
+ seq_printf(s, "-->%s\n", ksb->dbgbuf[i]);
+ else
+ seq_printf(s, "%s\n", ksb->dbgbuf[i]);
+ }
+ read_unlock_irqrestore(&ksb->dbg_lock, flags);
+
+ return 0;
+}
+
+static int ksb_debug_open(struct inode *ip, struct file *fp)
+{
+ return single_open(fp, ksb_debug_show, ip->i_private);
+
+ return 0;
+}
+
+static const struct file_operations dbg_fops = {
+ .open = ksb_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *dbg_dir;
+
+static int __init ksb_init(void)
+{
+ struct ks_bridge *ksb;
+ int num_instances = 0;
+ int ret = 0;
+ int i;
+
+ dbg_dir = debugfs_create_dir("ks_bridge", NULL);
+ if (IS_ERR(dbg_dir))
+ pr_err("unable to create debug dir");
+
+ for (i = 0; i < NO_BRIDGE_INSTANCES; i++) {
+ ksb = kzalloc(sizeof(struct ks_bridge), GFP_KERNEL);
+ if (!ksb) {
+ pr_err("unable to allocat mem for ks_bridge");
+ ret = -ENOMEM;
+ goto dev_free;
+ }
+ __ksb[i] = ksb;
+
+ ksb->name = kasprintf(GFP_KERNEL, "ks_usb_bridge.%i", i);
+ if (!ksb->name) {
+ pr_info("unable to allocate name");
+ kfree(ksb);
+ ret = -ENOMEM;
+ goto dev_free;
+ }
+
+ spin_lock_init(&ksb->lock);
+ INIT_LIST_HEAD(&ksb->to_mdm_list);
+ INIT_LIST_HEAD(&ksb->to_ks_list);
+ init_waitqueue_head(&ksb->ks_wait_q);
+ init_waitqueue_head(&ksb->pending_urb_wait);
+ ksb->wq = create_singlethread_workqueue(ksb->name);
+ if (!ksb->wq) {
+ pr_err("unable to allocate workqueue");
+ kfree(ksb->name);
+ kfree(ksb);
+ ret = -ENOMEM;
+ goto dev_free;
+ }
+
+ INIT_WORK(&ksb->to_mdm_work, ksb_tomdm_work);
+ INIT_WORK(&ksb->start_rx_work, ksb_start_rx_work);
+ init_usb_anchor(&ksb->submitted);
+
+ ksb->dbg_idx = 0;
+ ksb->dbg_lock = __RW_LOCK_UNLOCKED(lck);
+
+ if (!IS_ERR(dbg_dir))
+ debugfs_create_file(ksb->name, S_IRUGO, dbg_dir,
+ ksb, &dbg_fops);
+
+ num_instances++;
+ }
+
+ ret = usb_register(&ksb_usb_driver);
+ if (ret) {
+ pr_err("unable to register ks bridge driver");
+ goto dev_free;
+ }
+
+ pr_info("init done");
+
+ return 0;
+
+dev_free:
+ if (!IS_ERR(dbg_dir))
+ debugfs_remove_recursive(dbg_dir);
+
+ for (i = 0; i < num_instances; i++) {
+ ksb = __ksb[i];
+
+ destroy_workqueue(ksb->wq);
+ kfree(ksb->name);
+ kfree(ksb);
+ }
+
+ return ret;
+
+}
+
+static void __exit ksb_exit(void)
+{
+ struct ks_bridge *ksb;
+ int i;
+
+ if (!IS_ERR(dbg_dir))
+ debugfs_remove_recursive(dbg_dir);
+
+ usb_deregister(&ksb_usb_driver);
+
+ for (i = 0; i < NO_BRIDGE_INSTANCES; i++) {
+ ksb = __ksb[i];
+
+ destroy_workqueue(ksb->wq);
+ kfree(ksb->name);
+ kfree(ksb);
+ }
+}
+
+module_init(ksb_init);
+module_exit(ksb_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
pd->vbus_present = val.intval;
ret = power_supply_get_property(pd->usb_psy,
- POWER_SUPPLY_PROP_TYPE, &val);
+ POWER_SUPPLY_PROP_REAL_TYPE, &val);
if (ret) {
usbpd_err(&pd->dev, "Unable to read USB TYPE: %d\n", ret);
return ret;
} else if (cmd == VFIO_DEVICE_SET_IRQS) {
struct vfio_irq_set hdr;
+ size_t size;
u8 *data = NULL;
- int ret = 0;
+ int max, ret = 0;
minsz = offsetofend(struct vfio_irq_set, count);
return -EFAULT;
if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS ||
+ hdr.count >= (U32_MAX - hdr.start) ||
hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
VFIO_IRQ_SET_ACTION_TYPE_MASK))
return -EINVAL;
- if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
- size_t size;
- int max = vfio_pci_get_irq_count(vdev, hdr.index);
+ max = vfio_pci_get_irq_count(vdev, hdr.index);
+ if (hdr.start >= max || hdr.start + hdr.count > max)
+ return -EINVAL;
- if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
- size = sizeof(uint8_t);
- else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
- size = sizeof(int32_t);
- else
- return -EINVAL;
+ switch (hdr.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
+ case VFIO_IRQ_SET_DATA_NONE:
+ size = 0;
+ break;
+ case VFIO_IRQ_SET_DATA_BOOL:
+ size = sizeof(uint8_t);
+ break;
+ case VFIO_IRQ_SET_DATA_EVENTFD:
+ size = sizeof(int32_t);
+ break;
+ default:
+ return -EINVAL;
+ }
- if (hdr.argsz - minsz < hdr.count * size ||
- hdr.start >= max || hdr.start + hdr.count > max)
+ if (size) {
+ if (hdr.argsz - minsz < hdr.count * size)
return -EINVAL;
data = memdup_user((void __user *)(arg + minsz),
if (!is_irq_none(vdev))
return -EINVAL;
- vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+ vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
if (!vdev->ctx)
return -ENOMEM;
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
obj-$(CONFIG_FB_CARMINE) += carminefb.o
obj-$(CONFIG_FB_MB862XX) += mb862xx/
-obj-$(CONFIG_FB_MSM) += msm/
+ifeq ($(CONFIG_FB_MSM),y)
+obj-y += msm/
+else
+obj-$(CONFIG_MSM_DBA) += msm/msm_dba/
+endif
obj-$(CONFIG_FB_NUC900) += nuc900fb.o
obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o
obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
* assumes that the link and core clocks are already on.
*/
int mdss_dsi_phy_v3_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, bool enable);
+
+/**
+ * mdss_dsi_phy_v3_idle_pc_exit() - Called after Idle Power Collapse exit
+ * @ctrl: pointer to DSI controller structure
+ *
+ * This function is called after Idle Power Collapse, so driver
+ * can perform any sequence required after the Idle PC exit.
+ */
+void mdss_dsi_phy_v3_idle_pc_exit(struct mdss_dsi_ctrl_pdata *ctrl);
#endif /* MDSS_DSI_PHY_H */
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
*/
DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL3,
active_lanes);
+ usleep_range(5, 15);
DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL3, 0);
return rc;
}
+void mdss_dsi_phy_v3_idle_pc_exit(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ u32 val = BIT(5);
+ u32 data;
+
+ /* Reset phy pll after idle pc exit */
+ data = DSI_PHY_R32(ctrl->phy_io.base, CMN_CTRL_1);
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_CTRL_1, data | val);
+ usleep_range(10, 15);
+
+ data = DSI_PHY_R32(ctrl->phy_io.base, CMN_CTRL_1);
+ data &= ~(BIT(5));
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_CTRL_1, data);
+}
int mdss_dsi_phy_v3_shutdown(struct mdss_dsi_ctrl_pdata *ctrl)
{
wfd = mdp5_data->wfd;
output_layer = commit->output_layer;
+ if (output_layer->buffer.plane_count > MAX_PLANES) {
+ pr_err("Output buffer plane_count exceeds MAX_PLANES limit:%d\n",
+ output_layer->buffer.plane_count);
+ return -EINVAL;
+ }
+
data = mdss_mdp_wfd_add_data(wfd, output_layer);
if (IS_ERR_OR_NULL(data))
return PTR_ERR(data);
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
if (wfd_data->layer.flags & MDP_LAYER_SECURE_SESSION)
flags = MDP_SECURE_OVERLAY_SESSION;
+ if (buffer->plane_count > MAX_PLANES) {
+ pr_err("buffer plane_count exceeds MAX_PLANES limit:%d",
+ buffer->plane_count);
+ return -EINVAL;
+ }
+
memset(planes, 0, sizeof(planes));
for (i = 0; i < buffer->plane_count; i++) {
memset(planes, 0, sizeof(planes));
+ if (buffer->plane_count > MAX_PLANES) {
+ pr_err("buffer plane_count exceeds MAX_PLANES limit:%d\n",
+ buffer->plane_count);
+ return -EINVAL;
+ }
+
for (i = 0; i < buffer->plane_count; i++) {
planes[i].memory_id = buffer->planes[i].fd;
planes[i].offset = buffer->planes[i].offset;
struct mdss_rot_entry_container *req;
int size, i;
+ /*
+ * Check input and output plane_count from each given item
+ * are within the MAX_PLANES limit
+ */
+ for (i = 0 ; i < count; i++) {
+ if ((items[i].input.plane_count > MAX_PLANES) ||
+ (items[i].output.plane_count > MAX_PLANES)) {
+ pr_err("Input/Output plane_count exceeds MAX_PLANES limit, input:%d, output:%d\n",
+ items[i].input.plane_count,
+ items[i].output.plane_count);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
size = sizeof(struct mdss_rot_entry_container);
size += sizeof(struct mdss_rot_entry) * count;
req = devm_kzalloc(&mgr->pdev->dev, size, GFP_KERNEL);
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
#define ADV7533_REG_CHIP_REVISION (0x00)
#define ADV7533_DSI_CEC_I2C_ADDR_REG (0xE1)
-#define ADV7533_RESET_DELAY (100)
+#define ADV7533_RESET_DELAY (10)
#define PINCTRL_STATE_ACTIVE "pmx_adv7533_active"
#define PINCTRL_STATE_SUSPEND "pmx_adv7533_suspend"
static int adv7533_video_on(void *client, bool on,
struct msm_dba_video_cfg *cfg, u32 flags)
{
- int ret = -EINVAL;
+ int ret = 0;
u8 lanes;
u8 reg_val = 0;
struct adv7533 *pdata = adv7533_get_platform_data(client);
if (!pdata || !cfg) {
pr_err("%s: invalid platform data\n", __func__);
- return ret;
+ return -EINVAL;
}
mutex_lock(&pdata->ops_mutex);
/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 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
return -EINVAL;
}
- pr_debug("%s: [%s:0x02%x] : W[0x%02x, 0x%02x]\n", __func__,
+ pr_debug("%s: [%s:0x%02x] : W[0x%02x, 0x%02x]\n", __func__,
client->name, addr, reg, val);
client->addr = addr;
}
/**
+ * mdss_dsi_phy_idle_pc_exit() - Called after exit Idle PC
+ * @ctrl: pointer to DSI controller structure
+ *
+ * Perform any programming needed after Idle PC exit.
+ */
+static int mdss_dsi_phy_idle_pc_exit(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ if (ctrl->shared_data->phy_rev == DSI_PHY_REV_30)
+ mdss_dsi_phy_v3_idle_pc_exit(ctrl);
+
+ return 0;
+}
+
+/**
* mdss_dsi_clamp_ctrl_default() - Program DSI clamps
* @ctrl: pointer to DSI controller structure
* @enable: true to enable clamps, false to disable clamps
}
}
+ if ((clk_type & MDSS_DSI_LINK_CLK) &&
+ (new_state == MDSS_DSI_CLK_ON) &&
+ !ctrl->panel_data.panel_info.cont_splash_enabled)
+ mdss_dsi_phy_idle_pc_exit(ctrl);
+
return rc;
}
break;
case XenbusStateInitWait:
-InitWait:
xenbus_switch_state(dev, XenbusStateConnected);
break;
* get Connected twice here.
*/
if (dev->state != XenbusStateConnected)
- goto InitWait; /* no InitWait seen yet, fudge it */
+ /* no InitWait seen yet, fudge it */
+ xenbus_switch_state(dev, XenbusStateConnected);
if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
"request-update", "%d", &val) < 0)
obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o
obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o
-obj-$(CONFIG_FS_MBCACHE) += mbcache.o
+obj-$(CONFIG_FS_MBCACHE) += mbcache.o mbcache2.o
obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o
obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_COREDUMP) += coredump.o
bool use_persistent:1; /* use persistent instead of durable handles */
#ifdef CONFIG_CIFS_SMB2
bool print:1; /* set if connection to printer share */
- bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */
__le32 capabilities;
__u32 share_flags;
__u32 maximal_access;
wdata->credits = credits;
if (!wdata->cfile->invalidHandle ||
- !cifs_reopen_file(wdata->cfile, false))
+ !(rc = cifs_reopen_file(wdata->cfile, false)))
rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release);
if (rc) {
rdata->credits = credits;
if (!rdata->cfile->invalidHandle ||
- !cifs_reopen_file(rdata->cfile, true))
+ !(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata);
error:
if (rc) {
}
if (!rdata->cfile->invalidHandle ||
- !cifs_reopen_file(rdata->cfile, true))
+ !(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata);
if (rc) {
add_credits_and_wake_if(server, rdata->credits, 0);
return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle;
}
+static bool
+cifs_can_echo(struct TCP_Server_Info *server)
+{
+ if (server->tcpStatus == CifsGood)
+ return true;
+
+ return false;
+}
+
struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids,
.get_dfs_refer = CIFSGetDFSRefer,
.qfs_tcon = cifs_qfs_tcon,
.is_path_accessible = cifs_is_path_accessible,
+ .can_echo = cifs_can_echo,
.query_path_info = cifs_query_path_info,
.query_file_info = cifs_query_file_info,
.get_srv_inum = cifs_get_srv_inum,
else
return -EIO;
- if (tcon && tcon->bad_network_name)
- return -ENOENT;
-
if ((tcon && tcon->seal) &&
((ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) == 0)) {
cifs_dbg(VFS, "encryption requested but no server support");
return -EINVAL;
}
+ /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
+ if (tcon)
+ tcon->tid = 0;
+
rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req);
if (rc) {
kfree(unc_path);
tcon_error_exit:
if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) {
cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
- if (tcon)
- tcon->bad_network_name = true;
}
goto tcon_exit;
}
get_events()->is_hw_crypt_cb())
clear_cache_needed = true;
+ filemap_write_and_wait(inode->i_mapping);
if (clear_cache_needed) {
ret = vfs_fsync(inode_info->lower_file, false);
if (ret)
pr_err("failed to sync file ret = %d.\n", ret);
}
-
- filemap_write_and_wait(inode->i_mapping);
fput(inode_info->lower_file);
inode_info->lower_file = NULL;
mutex_unlock(&inode_info->lower_file_mutex);
#define rsv_start rsv_window._rsv_start
#define rsv_end rsv_window._rsv_end
+struct mb2_cache;
+
/*
* second extended-fs super-block data in memory
*/
* of the mount options.
*/
spinlock_t s_lock;
+ struct mb2_cache *s_mb_cache;
};
static inline spinlock_t *
dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
- ext2_xattr_put_super(sb);
+ if (sbi->s_mb_cache) {
+ ext2_xattr_destroy_cache(sbi->s_mb_cache);
+ sbi->s_mb_cache = NULL;
+ }
if (!(sb->s_flags & MS_RDONLY)) {
struct ext2_super_block *es = sbi->s_es;
ext2_msg(sb, KERN_ERR, "error: insufficient memory");
goto failed_mount3;
}
+
+#ifdef CONFIG_EXT2_FS_XATTR
+ sbi->s_mb_cache = ext2_xattr_create_cache();
+ if (!sbi->s_mb_cache) {
+ ext2_msg(sb, KERN_ERR, "Failed to create an mb_cache");
+ goto failed_mount3;
+ }
+#endif
/*
* set up enough so that it can read an inode
*/
sb->s_id);
goto failed_mount;
failed_mount3:
+ if (sbi->s_mb_cache)
+ ext2_xattr_destroy_cache(sbi->s_mb_cache);
percpu_counter_destroy(&sbi->s_freeblocks_counter);
percpu_counter_destroy(&sbi->s_freeinodes_counter);
percpu_counter_destroy(&sbi->s_dirs_counter);
static int __init init_ext2_fs(void)
{
- int err = init_ext2_xattr();
- if (err)
- return err;
+ int err;
+
err = init_inodecache();
if (err)
- goto out1;
+ return err;
err = register_filesystem(&ext2_fs_type);
if (err)
goto out;
return 0;
out:
destroy_inodecache();
-out1:
- exit_ext2_xattr();
return err;
}
{
unregister_filesystem(&ext2_fs_type);
destroy_inodecache();
- exit_ext2_xattr();
}
MODULE_AUTHOR("Remy Card and others");
#include <linux/buffer_head.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/mbcache.h>
+#include <linux/mbcache2.h>
#include <linux/quotaops.h>
#include <linux/rwsem.h>
#include <linux/security.h>
static int ext2_xattr_set2(struct inode *, struct buffer_head *,
struct ext2_xattr_header *);
-static int ext2_xattr_cache_insert(struct buffer_head *);
+static int ext2_xattr_cache_insert(struct mb2_cache *, struct buffer_head *);
static struct buffer_head *ext2_xattr_cache_find(struct inode *,
struct ext2_xattr_header *);
static void ext2_xattr_rehash(struct ext2_xattr_header *,
struct ext2_xattr_entry *);
-static struct mb_cache *ext2_xattr_cache;
-
static const struct xattr_handler *ext2_xattr_handler_map[] = {
[EXT2_XATTR_INDEX_USER] = &ext2_xattr_user_handler,
#ifdef CONFIG_EXT2_FS_POSIX_ACL
size_t name_len, size;
char *end;
int error;
+ struct mb2_cache *ext2_mb_cache = EXT2_SB(inode->i_sb)->s_mb_cache;
ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
name_index, name, buffer, (long)buffer_size);
goto found;
entry = next;
}
- if (ext2_xattr_cache_insert(bh))
+ if (ext2_xattr_cache_insert(ext2_mb_cache, bh))
ea_idebug(inode, "cache insert failed");
error = -ENODATA;
goto cleanup;
le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
goto bad_block;
- if (ext2_xattr_cache_insert(bh))
+ if (ext2_xattr_cache_insert(ext2_mb_cache, bh))
ea_idebug(inode, "cache insert failed");
if (buffer) {
error = -ERANGE;
char *end;
size_t rest = buffer_size;
int error;
+ struct mb2_cache *ext2_mb_cache = EXT2_SB(inode->i_sb)->s_mb_cache;
ea_idebug(inode, "buffer=%p, buffer_size=%ld",
buffer, (long)buffer_size);
goto bad_block;
entry = next;
}
- if (ext2_xattr_cache_insert(bh))
+ if (ext2_xattr_cache_insert(ext2_mb_cache, bh))
ea_idebug(inode, "cache insert failed");
/* list the attribute names */
/* Here we know that we can set the new attribute. */
if (header) {
- struct mb_cache_entry *ce;
-
/* assert(header == HDR(bh)); */
- ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev,
- bh->b_blocknr);
lock_buffer(bh);
if (header->h_refcount == cpu_to_le32(1)) {
+ __u32 hash = le32_to_cpu(header->h_hash);
+
ea_bdebug(bh, "modifying in-place");
- if (ce)
- mb_cache_entry_free(ce);
+ /*
+ * This must happen under buffer lock for
+ * ext2_xattr_set2() to reliably detect modified block
+ */
+ mb2_cache_entry_delete_block(EXT2_SB(sb)->s_mb_cache,
+ hash, bh->b_blocknr);
+
/* keep the buffer locked while modifying it. */
} else {
int offset;
- if (ce)
- mb_cache_entry_release(ce);
unlock_buffer(bh);
ea_bdebug(bh, "cloning");
header = kmalloc(bh->b_size, GFP_KERNEL);
struct super_block *sb = inode->i_sb;
struct buffer_head *new_bh = NULL;
int error;
+ struct mb2_cache *ext2_mb_cache = EXT2_SB(sb)->s_mb_cache;
if (header) {
new_bh = ext2_xattr_cache_find(inode, header);
don't need to change the reference count. */
new_bh = old_bh;
get_bh(new_bh);
- ext2_xattr_cache_insert(new_bh);
+ ext2_xattr_cache_insert(ext2_mb_cache, new_bh);
} else {
/* We need to allocate a new block */
ext2_fsblk_t goal = ext2_group_first_block_no(sb,
memcpy(new_bh->b_data, header, new_bh->b_size);
set_buffer_uptodate(new_bh);
unlock_buffer(new_bh);
- ext2_xattr_cache_insert(new_bh);
+ ext2_xattr_cache_insert(ext2_mb_cache, new_bh);
ext2_xattr_update_super_block(sb);
}
error = 0;
if (old_bh && old_bh != new_bh) {
- struct mb_cache_entry *ce;
-
/*
* If there was an old block and we are no longer using it,
* release the old block.
*/
- ce = mb_cache_entry_get(ext2_xattr_cache, old_bh->b_bdev,
- old_bh->b_blocknr);
lock_buffer(old_bh);
if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) {
+ __u32 hash = le32_to_cpu(HDR(old_bh)->h_hash);
+
+ /*
+ * This must happen under buffer lock for
+ * ext2_xattr_set2() to reliably detect freed block
+ */
+ mb2_cache_entry_delete_block(ext2_mb_cache,
+ hash, old_bh->b_blocknr);
/* Free the old block. */
- if (ce)
- mb_cache_entry_free(ce);
ea_bdebug(old_bh, "freeing");
ext2_free_blocks(inode, old_bh->b_blocknr, 1);
mark_inode_dirty(inode);
} else {
/* Decrement the refcount only. */
le32_add_cpu(&HDR(old_bh)->h_refcount, -1);
- if (ce)
- mb_cache_entry_release(ce);
dquot_free_block_nodirty(inode, 1);
mark_inode_dirty(inode);
mark_buffer_dirty(old_bh);
ext2_xattr_delete_inode(struct inode *inode)
{
struct buffer_head *bh = NULL;
- struct mb_cache_entry *ce;
down_write(&EXT2_I(inode)->xattr_sem);
if (!EXT2_I(inode)->i_file_acl)
EXT2_I(inode)->i_file_acl);
goto cleanup;
}
- ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev, bh->b_blocknr);
lock_buffer(bh);
if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
- if (ce)
- mb_cache_entry_free(ce);
+ __u32 hash = le32_to_cpu(HDR(bh)->h_hash);
+
+ /*
+ * This must happen under buffer lock for ext2_xattr_set2() to
+ * reliably detect freed block
+ */
+ mb2_cache_entry_delete_block(EXT2_SB(inode->i_sb)->s_mb_cache,
+ hash, bh->b_blocknr);
ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1);
get_bh(bh);
bforget(bh);
unlock_buffer(bh);
} else {
le32_add_cpu(&HDR(bh)->h_refcount, -1);
- if (ce)
- mb_cache_entry_release(ce);
ea_bdebug(bh, "refcount now=%d",
le32_to_cpu(HDR(bh)->h_refcount));
unlock_buffer(bh);
}
/*
- * ext2_xattr_put_super()
- *
- * This is called when a file system is unmounted.
- */
-void
-ext2_xattr_put_super(struct super_block *sb)
-{
- mb_cache_shrink(sb->s_bdev);
-}
-
-
-/*
* ext2_xattr_cache_insert()
*
* Create a new entry in the extended attribute cache, and insert
* Returns 0, or a negative error number on failure.
*/
static int
-ext2_xattr_cache_insert(struct buffer_head *bh)
+ext2_xattr_cache_insert(struct mb2_cache *cache, struct buffer_head *bh)
{
__u32 hash = le32_to_cpu(HDR(bh)->h_hash);
- struct mb_cache_entry *ce;
int error;
- ce = mb_cache_entry_alloc(ext2_xattr_cache, GFP_NOFS);
- if (!ce)
- return -ENOMEM;
- error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash);
+ error = mb2_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr);
if (error) {
- mb_cache_entry_free(ce);
if (error == -EBUSY) {
ea_bdebug(bh, "already in cache (%d cache entries)",
atomic_read(&ext2_xattr_cache->c_entry_count));
error = 0;
}
- } else {
- ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash,
- atomic_read(&ext2_xattr_cache->c_entry_count));
- mb_cache_entry_release(ce);
- }
+ } else
+ ea_bdebug(bh, "inserting [%x]", (int)hash);
return error;
}
ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header)
{
__u32 hash = le32_to_cpu(header->h_hash);
- struct mb_cache_entry *ce;
+ struct mb2_cache_entry *ce;
+ struct mb2_cache *ext2_mb_cache = EXT2_SB(inode->i_sb)->s_mb_cache;
if (!header->h_hash)
return NULL; /* never share */
ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
again:
- ce = mb_cache_entry_find_first(ext2_xattr_cache, inode->i_sb->s_bdev,
- hash);
+ ce = mb2_cache_entry_find_first(ext2_mb_cache, hash);
while (ce) {
struct buffer_head *bh;
- if (IS_ERR(ce)) {
- if (PTR_ERR(ce) == -EAGAIN)
- goto again;
- break;
- }
-
bh = sb_bread(inode->i_sb, ce->e_block);
if (!bh) {
ext2_error(inode->i_sb, "ext2_xattr_cache_find",
inode->i_ino, (unsigned long) ce->e_block);
} else {
lock_buffer(bh);
- if (le32_to_cpu(HDR(bh)->h_refcount) >
+ /*
+ * We have to be careful about races with freeing or
+ * rehashing of xattr block. Once we hold buffer lock
+ * xattr block's state is stable so we can check
+ * whether the block got freed / rehashed or not.
+ * Since we unhash mbcache entry under buffer lock when
+ * freeing / rehashing xattr block, checking whether
+ * entry is still hashed is reliable.
+ */
+ if (hlist_bl_unhashed(&ce->e_hash_list)) {
+ mb2_cache_entry_put(ext2_mb_cache, ce);
+ unlock_buffer(bh);
+ brelse(bh);
+ goto again;
+ } else if (le32_to_cpu(HDR(bh)->h_refcount) >
EXT2_XATTR_REFCOUNT_MAX) {
ea_idebug(inode, "block %ld refcount %d>%d",
(unsigned long) ce->e_block,
} else if (!ext2_xattr_cmp(header, HDR(bh))) {
ea_bdebug(bh, "b_count=%d",
atomic_read(&(bh->b_count)));
- mb_cache_entry_release(ce);
+ mb2_cache_entry_touch(ext2_mb_cache, ce);
+ mb2_cache_entry_put(ext2_mb_cache, ce);
return bh;
}
unlock_buffer(bh);
brelse(bh);
}
- ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash);
+ ce = mb2_cache_entry_find_next(ext2_mb_cache, ce);
}
return NULL;
}
#undef BLOCK_HASH_SHIFT
-int __init
-init_ext2_xattr(void)
+#define HASH_BUCKET_BITS 10
+
+struct mb2_cache *ext2_xattr_create_cache(void)
{
- ext2_xattr_cache = mb_cache_create("ext2_xattr", 6);
- if (!ext2_xattr_cache)
- return -ENOMEM;
- return 0;
+ return mb2_cache_create(HASH_BUCKET_BITS);
}
-void
-exit_ext2_xattr(void)
+void ext2_xattr_destroy_cache(struct mb2_cache *cache)
{
- mb_cache_destroy(ext2_xattr_cache);
+ if (cache)
+ mb2_cache_destroy(cache);
}
#define EXT2_XATTR_SIZE(size) \
(((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND)
+struct mb2_cache;
+
# ifdef CONFIG_EXT2_FS_XATTR
extern const struct xattr_handler ext2_xattr_user_handler;
extern int ext2_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
extern void ext2_xattr_delete_inode(struct inode *);
-extern void ext2_xattr_put_super(struct super_block *);
-extern int init_ext2_xattr(void);
-extern void exit_ext2_xattr(void);
+extern struct mb2_cache *ext2_xattr_create_cache(void);
+extern void ext2_xattr_destroy_cache(struct mb2_cache *cache);
extern const struct xattr_handler *ext2_xattr_handlers[];
{
}
-static inline void
-ext2_xattr_put_super(struct super_block *sb)
-{
-}
-
-static inline int
-init_ext2_xattr(void)
-{
- return 0;
-}
-
-static inline void
-exit_ext2_xattr(void)
+static inline void ext2_xattr_destroy_cache(struct mb2_cache *cache)
{
}
struct list_head s_es_list; /* List of inodes with reclaimable extents */
long s_es_nr_inode;
struct ext4_es_stats s_es_stats;
- struct mb_cache *s_mb_cache;
+ struct mb2_cache *s_mb_cache;
spinlock_t s_es_lock ____cacheline_aligned_in_smp;
/* Ratelimit ext4 messages. */
csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum,
csum_size);
offset += csum_size;
- csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
- EXT4_INODE_SIZE(inode->i_sb) -
- offset);
}
+ csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
+ EXT4_INODE_SIZE(inode->i_sb) - offset);
}
return csum;
ext4_release_system_zone(sb);
ext4_mb_release(sb);
ext4_ext_release(sb);
- ext4_xattr_put_super(sb);
if (!(sb->s_flags & MS_RDONLY) && !aborted) {
ext4_clear_feature_journal_needs_recovery(sb);
no_journal:
if (ext4_mballoc_ready) {
- sbi->s_mb_cache = ext4_xattr_create_cache(sb->s_id);
+ sbi->s_mb_cache = ext4_xattr_create_cache();
if (!sbi->s_mb_cache) {
ext4_msg(sb, KERN_ERR, "Failed to create an mb_cache");
goto failed_mount_wq;
if (EXT4_SB(sb)->rsv_conversion_wq)
destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq);
failed_mount_wq:
+ if (sbi->s_mb_cache) {
+ ext4_xattr_destroy_cache(sbi->s_mb_cache);
+ sbi->s_mb_cache = NULL;
+ }
if (sbi->s_journal) {
jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
-#include <linux/mbcache.h>
+#include <linux/mbcache2.h>
#include <linux/quotaops.h>
#include "ext4_jbd2.h"
#include "ext4.h"
# define ea_bdebug(bh, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif
-static void ext4_xattr_cache_insert(struct mb_cache *, struct buffer_head *);
+static void ext4_xattr_cache_insert(struct mb2_cache *, struct buffer_head *);
static struct buffer_head *ext4_xattr_cache_find(struct inode *,
struct ext4_xattr_header *,
- struct mb_cache_entry **);
+ struct mb2_cache_entry **);
static void ext4_xattr_rehash(struct ext4_xattr_header *,
struct ext4_xattr_entry *);
static int ext4_xattr_list(struct dentry *dentry, char *buffer,
struct ext4_xattr_entry *entry;
size_t size;
int error;
- struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+ struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
name_index, name, buffer, (long)buffer_size);
struct inode *inode = d_inode(dentry);
struct buffer_head *bh = NULL;
int error;
- struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+ struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
ea_idebug(inode, "buffer=%p, buffer_size=%ld",
buffer, (long)buffer_size);
ext4_xattr_release_block(handle_t *handle, struct inode *inode,
struct buffer_head *bh)
{
- struct mb_cache_entry *ce = NULL;
int error = 0;
- struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
- ce = mb_cache_entry_get(ext4_mb_cache, bh->b_bdev, bh->b_blocknr);
BUFFER_TRACE(bh, "get_write_access");
error = ext4_journal_get_write_access(handle, bh);
if (error)
lock_buffer(bh);
if (BHDR(bh)->h_refcount == cpu_to_le32(1)) {
+ __u32 hash = le32_to_cpu(BHDR(bh)->h_hash);
+
ea_bdebug(bh, "refcount now=0; freeing");
- if (ce)
- mb_cache_entry_free(ce);
+ /*
+ * This must happen under buffer lock for
+ * ext4_xattr_block_set() to reliably detect freed block
+ */
+ mb2_cache_entry_delete_block(EXT4_GET_MB_CACHE(inode), hash,
+ bh->b_blocknr);
get_bh(bh);
unlock_buffer(bh);
ext4_free_blocks(handle, inode, bh, 0, 1,
EXT4_FREE_BLOCKS_FORGET);
} else {
le32_add_cpu(&BHDR(bh)->h_refcount, -1);
- if (ce)
- mb_cache_entry_release(ce);
/*
* Beware of this ugliness: Releasing of xattr block references
* from different inodes can race and so we have to protect
struct super_block *sb = inode->i_sb;
struct buffer_head *new_bh = NULL;
struct ext4_xattr_search *s = &bs->s;
- struct mb_cache_entry *ce = NULL;
+ struct mb2_cache_entry *ce = NULL;
int error = 0;
- struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+ struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
#define header(x) ((struct ext4_xattr_header *)(x))
if (i->value && i->value_len > sb->s_blocksize)
return -ENOSPC;
if (s->base) {
- ce = mb_cache_entry_get(ext4_mb_cache, bs->bh->b_bdev,
- bs->bh->b_blocknr);
BUFFER_TRACE(bs->bh, "get_write_access");
error = ext4_journal_get_write_access(handle, bs->bh);
if (error)
lock_buffer(bs->bh);
if (header(s->base)->h_refcount == cpu_to_le32(1)) {
- if (ce) {
- mb_cache_entry_free(ce);
- ce = NULL;
- }
+ __u32 hash = le32_to_cpu(BHDR(bs->bh)->h_hash);
+
+ /*
+ * This must happen under buffer lock for
+ * ext4_xattr_block_set() to reliably detect modified
+ * block
+ */
+ mb2_cache_entry_delete_block(ext4_mb_cache, hash,
+ bs->bh->b_blocknr);
ea_bdebug(bs->bh, "modifying in-place");
error = ext4_xattr_set_entry(i, s);
if (!error) {
int offset = (char *)s->here - bs->bh->b_data;
unlock_buffer(bs->bh);
- if (ce) {
- mb_cache_entry_release(ce);
- ce = NULL;
- }
ea_bdebug(bs->bh, "cloning");
s->base = kmalloc(bs->bh->b_size, GFP_NOFS);
error = -ENOMEM;
if (error)
goto cleanup_dquot;
lock_buffer(new_bh);
+ /*
+ * We have to be careful about races with
+ * freeing or rehashing of xattr block. Once we
+ * hold buffer lock xattr block's state is
+ * stable so we can check whether the block got
+ * freed / rehashed or not. Since we unhash
+ * mbcache entry under buffer lock when freeing
+ * / rehashing xattr block, checking whether
+ * entry is still hashed is reliable.
+ */
+ if (hlist_bl_unhashed(&ce->e_hash_list)) {
+ /*
+ * Undo everything and check mbcache
+ * again.
+ */
+ unlock_buffer(new_bh);
+ dquot_free_block(inode,
+ EXT4_C2B(EXT4_SB(sb),
+ 1));
+ brelse(new_bh);
+ mb2_cache_entry_put(ext4_mb_cache, ce);
+ ce = NULL;
+ new_bh = NULL;
+ goto inserted;
+ }
le32_add_cpu(&BHDR(new_bh)->h_refcount, 1);
ea_bdebug(new_bh, "reusing; refcount now=%d",
le32_to_cpu(BHDR(new_bh)->h_refcount));
if (error)
goto cleanup_dquot;
}
- mb_cache_entry_release(ce);
+ mb2_cache_entry_touch(ext4_mb_cache, ce);
+ mb2_cache_entry_put(ext4_mb_cache, ce);
ce = NULL;
} else if (bs->bh && s->base == bs->bh->b_data) {
/* We were modifying this block in-place. */
cleanup:
if (ce)
- mb_cache_entry_release(ce);
+ mb2_cache_entry_put(ext4_mb_cache, ce);
brelse(new_bh);
if (!(bs->bh && s->base == bs->bh->b_data))
kfree(s->base);
}
/*
- * ext4_xattr_put_super()
- *
- * This is called when a file system is unmounted.
- */
-void
-ext4_xattr_put_super(struct super_block *sb)
-{
- mb_cache_shrink(sb->s_bdev);
-}
-
-/*
* ext4_xattr_cache_insert()
*
* Create a new entry in the extended attribute cache, and insert
* Returns 0, or a negative error number on failure.
*/
static void
-ext4_xattr_cache_insert(struct mb_cache *ext4_mb_cache, struct buffer_head *bh)
+ext4_xattr_cache_insert(struct mb2_cache *ext4_mb_cache, struct buffer_head *bh)
{
__u32 hash = le32_to_cpu(BHDR(bh)->h_hash);
- struct mb_cache_entry *ce;
int error;
- ce = mb_cache_entry_alloc(ext4_mb_cache, GFP_NOFS);
- if (!ce) {
- ea_bdebug(bh, "out of memory");
- return;
- }
- error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash);
+ error = mb2_cache_entry_create(ext4_mb_cache, GFP_NOFS, hash,
+ bh->b_blocknr);
if (error) {
- mb_cache_entry_free(ce);
- if (error == -EBUSY) {
+ if (error == -EBUSY)
ea_bdebug(bh, "already in cache");
- error = 0;
- }
- } else {
+ } else
ea_bdebug(bh, "inserting [%x]", (int)hash);
- mb_cache_entry_release(ce);
- }
}
/*
*/
static struct buffer_head *
ext4_xattr_cache_find(struct inode *inode, struct ext4_xattr_header *header,
- struct mb_cache_entry **pce)
+ struct mb2_cache_entry **pce)
{
__u32 hash = le32_to_cpu(header->h_hash);
- struct mb_cache_entry *ce;
- struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+ struct mb2_cache_entry *ce;
+ struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
if (!header->h_hash)
return NULL; /* never share */
ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
-again:
- ce = mb_cache_entry_find_first(ext4_mb_cache, inode->i_sb->s_bdev,
- hash);
+ ce = mb2_cache_entry_find_first(ext4_mb_cache, hash);
while (ce) {
struct buffer_head *bh;
- if (IS_ERR(ce)) {
- if (PTR_ERR(ce) == -EAGAIN)
- goto again;
- break;
- }
bh = sb_bread(inode->i_sb, ce->e_block);
if (!bh) {
EXT4_ERROR_INODE(inode, "block %lu read error",
return bh;
}
brelse(bh);
- ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash);
+ ce = mb2_cache_entry_find_next(ext4_mb_cache, ce);
}
return NULL;
}
#define HASH_BUCKET_BITS 10
-struct mb_cache *
-ext4_xattr_create_cache(char *name)
+struct mb2_cache *
+ext4_xattr_create_cache(void)
{
- return mb_cache_create(name, HASH_BUCKET_BITS);
+ return mb2_cache_create(HASH_BUCKET_BITS);
}
-void ext4_xattr_destroy_cache(struct mb_cache *cache)
+void ext4_xattr_destroy_cache(struct mb2_cache *cache)
{
if (cache)
- mb_cache_destroy(cache);
+ mb2_cache_destroy(cache);
}
extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int);
extern void ext4_xattr_delete_inode(handle_t *, struct inode *);
-extern void ext4_xattr_put_super(struct super_block *);
extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
struct ext4_inode *raw_inode, handle_t *handle);
struct ext4_xattr_info *i,
struct ext4_xattr_ibody_find *is);
-extern struct mb_cache *ext4_xattr_create_cache(char *name);
-extern void ext4_xattr_destroy_cache(struct mb_cache *);
+extern struct mb2_cache *ext4_xattr_create_cache(void);
+extern void ext4_xattr_destroy_cache(struct mb2_cache *);
#ifdef CONFIG_EXT4_FS_SECURITY
extern int ext4_init_security(handle_t *handle, struct inode *inode,
return 1;
}
+ /* check log blocks per segment */
+ if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) {
+ f2fs_msg(sb, KERN_INFO,
+ "Invalid log blocks per segment (%u)\n",
+ le32_to_cpu(raw_super->log_blocks_per_seg));
+ return 1;
+ }
+
/* Currently, support 512/1024/2048/4096 bytes sector size */
if (le32_to_cpu(raw_super->log_sectorsize) >
F2FS_MAX_LOG_SECTOR_SIZE ||
int error;
error = get_leaf_nr(dip, index, &leaf_no);
- if (!error)
+ if (!IS_ERR_VALUE(error))
error = get_leaf(dip, leaf_no, bh_out);
return error;
index = name->hash >> (32 - dip->i_depth);
error = get_leaf_nr(dip, index, &leaf_no);
- if (error)
+ if (IS_ERR_VALUE(error))
return error;
/* Get the old leaf block */
list_del_init(&ce->e_lru_list);
if (ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt))
continue;
- spin_unlock(&mb_cache_spinlock);
/* Prevent any find or get operation on the entry */
hlist_bl_lock(ce->e_block_hash_p);
hlist_bl_lock(ce->e_index_hash_p);
!list_empty(&ce->e_lru_list)) {
hlist_bl_unlock(ce->e_index_hash_p);
hlist_bl_unlock(ce->e_block_hash_p);
- spin_lock(&mb_cache_spinlock);
continue;
}
__mb_cache_entry_unhash_unlock(ce);
+ spin_unlock(&mb_cache_spinlock);
list_add_tail(&ce->e_lru_list, &free_list);
spin_lock(&mb_cache_spinlock);
}
if (ce->e_used || ce->e_queued ||
atomic_read(&ce->e_refcnt))
continue;
- spin_unlock(&mb_cache_spinlock);
/*
* Prevent any find or get operation on the
* entry.
hlist_bl_unlock(ce->e_index_hash_p);
hlist_bl_unlock(ce->e_block_hash_p);
l = &mb_cache_lru_list;
- spin_lock(&mb_cache_spinlock);
continue;
}
mb_assert(list_empty(&ce->e_lru_list));
mb_assert(!(ce->e_used || ce->e_queued ||
atomic_read(&ce->e_refcnt)));
__mb_cache_entry_unhash_unlock(ce);
+ spin_unlock(&mb_cache_spinlock);
goto found;
}
}
cache->c_bucket_bits);
block_hash_p = &cache->c_block_hash[bucket];
/* First serialize access to the block corresponding hash chain. */
+ spin_lock(&mb_cache_spinlock);
hlist_bl_lock(block_hash_p);
hlist_bl_for_each_entry(ce, l, block_hash_p, e_block_list) {
mb_assert(ce->e_block_hash_p == block_hash_p);
* Prevent a free from removing the entry.
*/
atomic_inc(&ce->e_refcnt);
+ if (!list_empty(&ce->e_lru_list))
+ list_del_init(&ce->e_lru_list);
hlist_bl_unlock(block_hash_p);
+ spin_unlock(&mb_cache_spinlock);
__spin_lock_mb_cache_entry(ce);
- atomic_dec(&ce->e_refcnt);
if (ce->e_used > 0) {
DEFINE_WAIT(wait);
while (ce->e_used > 0) {
finish_wait(&mb_cache_queue, &wait);
}
ce->e_used += 1 + MB_CACHE_WRITER;
+ atomic_dec(&ce->e_refcnt);
__spin_unlock_mb_cache_entry(ce);
- if (!list_empty(&ce->e_lru_list)) {
- spin_lock(&mb_cache_spinlock);
- list_del_init(&ce->e_lru_list);
- spin_unlock(&mb_cache_spinlock);
- }
if (!__mb_cache_entry_is_block_hashed(ce)) {
__mb_cache_entry_release(ce);
return NULL;
}
}
hlist_bl_unlock(block_hash_p);
+ spin_unlock(&mb_cache_spinlock);
return NULL;
}
--- /dev/null
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/list_bl.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mbcache2.h>
+
+/*
+ * Mbcache is a simple key-value store. Keys need not be unique, however
+ * key-value pairs are expected to be unique (we use this fact in
+ * mb2_cache_entry_delete_block()).
+ *
+ * Ext2 and ext4 use this cache for deduplication of extended attribute blocks.
+ * They use hash of a block contents as a key and block number as a value.
+ * That's why keys need not be unique (different xattr blocks may end up having
+ * the same hash). However block number always uniquely identifies a cache
+ * entry.
+ *
+ * We provide functions for creation and removal of entries, search by key,
+ * and a special "delete entry with given key-value pair" operation. Fixed
+ * size hash table is used for fast key lookups.
+ */
+
+struct mb2_cache {
+ /* Hash table of entries */
+ struct hlist_bl_head *c_hash;
+ /* log2 of hash table size */
+ int c_bucket_bits;
+ /* Protects c_lru_list, c_entry_count */
+ spinlock_t c_lru_list_lock;
+ struct list_head c_lru_list;
+ /* Number of entries in cache */
+ unsigned long c_entry_count;
+ struct shrinker c_shrink;
+};
+
+static struct kmem_cache *mb2_entry_cache;
+
+/*
+ * mb2_cache_entry_create - create entry in cache
+ * @cache - cache where the entry should be created
+ * @mask - gfp mask with which the entry should be allocated
+ * @key - key of the entry
+ * @block - block that contains data
+ *
+ * Creates entry in @cache with key @key and records that data is stored in
+ * block @block. The function returns -EBUSY if entry with the same key
+ * and for the same block already exists in cache. Otherwise 0 is returned.
+ */
+int mb2_cache_entry_create(struct mb2_cache *cache, gfp_t mask, u32 key,
+ sector_t block)
+{
+ struct mb2_cache_entry *entry, *dup;
+ struct hlist_bl_node *dup_node;
+ struct hlist_bl_head *head;
+
+ entry = kmem_cache_alloc(mb2_entry_cache, mask);
+ if (!entry)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&entry->e_lru_list);
+ /* One ref for hash, one ref returned */
+ atomic_set(&entry->e_refcnt, 1);
+ entry->e_key = key;
+ entry->e_block = block;
+ head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)];
+ entry->e_hash_list_head = head;
+ hlist_bl_lock(head);
+ hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) {
+ if (dup->e_key == key && dup->e_block == block) {
+ hlist_bl_unlock(head);
+ kmem_cache_free(mb2_entry_cache, entry);
+ return -EBUSY;
+ }
+ }
+ hlist_bl_add_head(&entry->e_hash_list, head);
+ hlist_bl_unlock(head);
+
+ spin_lock(&cache->c_lru_list_lock);
+ list_add_tail(&entry->e_lru_list, &cache->c_lru_list);
+ /* Grab ref for LRU list */
+ atomic_inc(&entry->e_refcnt);
+ cache->c_entry_count++;
+ spin_unlock(&cache->c_lru_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mb2_cache_entry_create);
+
+void __mb2_cache_entry_free(struct mb2_cache_entry *entry)
+{
+ kmem_cache_free(mb2_entry_cache, entry);
+}
+EXPORT_SYMBOL(__mb2_cache_entry_free);
+
+static struct mb2_cache_entry *__entry_find(struct mb2_cache *cache,
+ struct mb2_cache_entry *entry,
+ u32 key)
+{
+ struct mb2_cache_entry *old_entry = entry;
+ struct hlist_bl_node *node;
+ struct hlist_bl_head *head;
+
+ if (entry)
+ head = entry->e_hash_list_head;
+ else
+ head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)];
+ hlist_bl_lock(head);
+ if (entry && !hlist_bl_unhashed(&entry->e_hash_list))
+ node = entry->e_hash_list.next;
+ else
+ node = hlist_bl_first(head);
+ while (node) {
+ entry = hlist_bl_entry(node, struct mb2_cache_entry,
+ e_hash_list);
+ if (entry->e_key == key) {
+ atomic_inc(&entry->e_refcnt);
+ goto out;
+ }
+ node = node->next;
+ }
+ entry = NULL;
+out:
+ hlist_bl_unlock(head);
+ if (old_entry)
+ mb2_cache_entry_put(cache, old_entry);
+
+ return entry;
+}
+
+/*
+ * mb2_cache_entry_find_first - find the first entry in cache with given key
+ * @cache: cache where we should search
+ * @key: key to look for
+ *
+ * Search in @cache for entry with key @key. Grabs reference to the first
+ * entry found and returns the entry.
+ */
+struct mb2_cache_entry *mb2_cache_entry_find_first(struct mb2_cache *cache,
+ u32 key)
+{
+ return __entry_find(cache, NULL, key);
+}
+EXPORT_SYMBOL(mb2_cache_entry_find_first);
+
+/*
+ * mb2_cache_entry_find_next - find next entry in cache with the same
+ * @cache: cache where we should search
+ * @entry: entry to start search from
+ *
+ * Finds next entry in the hash chain which has the same key as @entry.
+ * If @entry is unhashed (which can happen when deletion of entry races
+ * with the search), finds the first entry in the hash chain. The function
+ * drops reference to @entry and returns with a reference to the found entry.
+ */
+struct mb2_cache_entry *mb2_cache_entry_find_next(struct mb2_cache *cache,
+ struct mb2_cache_entry *entry)
+{
+ return __entry_find(cache, entry, entry->e_key);
+}
+EXPORT_SYMBOL(mb2_cache_entry_find_next);
+
+/* mb2_cache_entry_delete_block - remove information about block from cache
+ * @cache - cache we work with
+ * @key - key of the entry to remove
+ * @block - block containing data for @key
+ *
+ * Remove entry from cache @cache with key @key with data stored in @block.
+ */
+void mb2_cache_entry_delete_block(struct mb2_cache *cache, u32 key,
+ sector_t block)
+{
+ struct hlist_bl_node *node;
+ struct hlist_bl_head *head;
+ struct mb2_cache_entry *entry;
+
+ head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)];
+ hlist_bl_lock(head);
+ hlist_bl_for_each_entry(entry, node, head, e_hash_list) {
+ if (entry->e_key == key && entry->e_block == block) {
+ /* We keep hash list reference to keep entry alive */
+ hlist_bl_del_init(&entry->e_hash_list);
+ hlist_bl_unlock(head);
+ spin_lock(&cache->c_lru_list_lock);
+ if (!list_empty(&entry->e_lru_list)) {
+ list_del_init(&entry->e_lru_list);
+ cache->c_entry_count--;
+ atomic_dec(&entry->e_refcnt);
+ }
+ spin_unlock(&cache->c_lru_list_lock);
+ mb2_cache_entry_put(cache, entry);
+ return;
+ }
+ }
+ hlist_bl_unlock(head);
+}
+EXPORT_SYMBOL(mb2_cache_entry_delete_block);
+
+/* mb2_cache_entry_touch - cache entry got used
+ * @cache - cache the entry belongs to
+ * @entry - entry that got used
+ *
+ * Move entry in lru list to reflect the fact that it was used.
+ */
+void mb2_cache_entry_touch(struct mb2_cache *cache,
+ struct mb2_cache_entry *entry)
+{
+ spin_lock(&cache->c_lru_list_lock);
+ if (!list_empty(&entry->e_lru_list))
+ list_move_tail(&cache->c_lru_list, &entry->e_lru_list);
+ spin_unlock(&cache->c_lru_list_lock);
+}
+EXPORT_SYMBOL(mb2_cache_entry_touch);
+
+static unsigned long mb2_cache_count(struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ struct mb2_cache *cache = container_of(shrink, struct mb2_cache,
+ c_shrink);
+
+ return cache->c_entry_count;
+}
+
+/* Shrink number of entries in cache */
+static unsigned long mb2_cache_scan(struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ int nr_to_scan = sc->nr_to_scan;
+ struct mb2_cache *cache = container_of(shrink, struct mb2_cache,
+ c_shrink);
+ struct mb2_cache_entry *entry;
+ struct hlist_bl_head *head;
+ unsigned int shrunk = 0;
+
+ spin_lock(&cache->c_lru_list_lock);
+ while (nr_to_scan-- && !list_empty(&cache->c_lru_list)) {
+ entry = list_first_entry(&cache->c_lru_list,
+ struct mb2_cache_entry, e_lru_list);
+ list_del_init(&entry->e_lru_list);
+ cache->c_entry_count--;
+ /*
+ * We keep LRU list reference so that entry doesn't go away
+ * from under us.
+ */
+ spin_unlock(&cache->c_lru_list_lock);
+ head = entry->e_hash_list_head;
+ hlist_bl_lock(head);
+ if (!hlist_bl_unhashed(&entry->e_hash_list)) {
+ hlist_bl_del_init(&entry->e_hash_list);
+ atomic_dec(&entry->e_refcnt);
+ }
+ hlist_bl_unlock(head);
+ if (mb2_cache_entry_put(cache, entry))
+ shrunk++;
+ cond_resched();
+ spin_lock(&cache->c_lru_list_lock);
+ }
+ spin_unlock(&cache->c_lru_list_lock);
+
+ return shrunk;
+}
+
+/*
+ * mb2_cache_create - create cache
+ * @bucket_bits: log2 of the hash table size
+ *
+ * Create cache for keys with 2^bucket_bits hash entries.
+ */
+struct mb2_cache *mb2_cache_create(int bucket_bits)
+{
+ struct mb2_cache *cache;
+ int bucket_count = 1 << bucket_bits;
+ int i;
+
+ if (!try_module_get(THIS_MODULE))
+ return NULL;
+
+ cache = kzalloc(sizeof(struct mb2_cache), GFP_KERNEL);
+ if (!cache)
+ goto err_out;
+ cache->c_bucket_bits = bucket_bits;
+ INIT_LIST_HEAD(&cache->c_lru_list);
+ spin_lock_init(&cache->c_lru_list_lock);
+ cache->c_hash = kmalloc(bucket_count * sizeof(struct hlist_bl_head),
+ GFP_KERNEL);
+ if (!cache->c_hash) {
+ kfree(cache);
+ goto err_out;
+ }
+ for (i = 0; i < bucket_count; i++)
+ INIT_HLIST_BL_HEAD(&cache->c_hash[i]);
+
+ cache->c_shrink.count_objects = mb2_cache_count;
+ cache->c_shrink.scan_objects = mb2_cache_scan;
+ cache->c_shrink.seeks = DEFAULT_SEEKS;
+ register_shrinker(&cache->c_shrink);
+
+ return cache;
+
+err_out:
+ module_put(THIS_MODULE);
+ return NULL;
+}
+EXPORT_SYMBOL(mb2_cache_create);
+
+/*
+ * mb2_cache_destroy - destroy cache
+ * @cache: the cache to destroy
+ *
+ * Free all entries in cache and cache itself. Caller must make sure nobody
+ * (except shrinker) can reach @cache when calling this.
+ */
+void mb2_cache_destroy(struct mb2_cache *cache)
+{
+ struct mb2_cache_entry *entry, *next;
+
+ unregister_shrinker(&cache->c_shrink);
+
+ /*
+ * We don't bother with any locking. Cache must not be used at this
+ * point.
+ */
+ list_for_each_entry_safe(entry, next, &cache->c_lru_list, e_lru_list) {
+ if (!hlist_bl_unhashed(&entry->e_hash_list)) {
+ hlist_bl_del_init(&entry->e_hash_list);
+ atomic_dec(&entry->e_refcnt);
+ } else
+ WARN_ON(1);
+ list_del(&entry->e_lru_list);
+ WARN_ON(atomic_read(&entry->e_refcnt) != 1);
+ mb2_cache_entry_put(cache, entry);
+ }
+ kfree(cache->c_hash);
+ kfree(cache);
+ module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL(mb2_cache_destroy);
+
+static int __init mb2cache_init(void)
+{
+ mb2_entry_cache = kmem_cache_create("mbcache",
+ sizeof(struct mb2_cache_entry), 0,
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL);
+ BUG_ON(!mb2_entry_cache);
+ return 0;
+}
+
+static void __exit mb2cache_exit(void)
+{
+ kmem_cache_destroy(mb2_entry_cache);
+}
+
+module_init(mb2cache_init)
+module_exit(mb2cache_exit)
+
+MODULE_AUTHOR("Jan Kara <jack@suse.cz>");
+MODULE_DESCRIPTION("Meta block cache (for extended attributes)");
+MODULE_LICENSE("GPL");
u64 seq; /* Sequence number to prevent loops */
wait_queue_head_t poll;
u64 event;
+ unsigned int mounts; /* # of mounts in the namespace */
+ unsigned int pending_mounts;
};
struct mnt_pcp {
#include "pnode.h"
#include "internal.h"
+/* Maximum number of mounts in a mount namespace */
+unsigned int sysctl_mount_max __read_mostly = 100000;
+
static unsigned int m_hash_mask __read_mostly;
static unsigned int m_hash_shift __read_mostly;
static unsigned int mp_hash_mask __read_mostly;
list_splice(&head, n->list.prev);
+ n->mounts += n->pending_mounts;
+ n->pending_mounts = 0;
+
__attach_mnt(mnt, parent);
touch_mnt_namespace(n);
}
propagate_umount(&tmp_list);
while (!list_empty(&tmp_list)) {
+ struct mnt_namespace *ns;
bool disconnect;
p = list_first_entry(&tmp_list, struct mount, mnt_list);
list_del_init(&p->mnt_expire);
list_del_init(&p->mnt_list);
- __touch_mnt_namespace(p->mnt_ns);
+ ns = p->mnt_ns;
+ if (ns) {
+ ns->mounts--;
+ __touch_mnt_namespace(ns);
+ }
p->mnt_ns = NULL;
if (how & UMOUNT_SYNC)
p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
return 0;
}
+int count_mounts(struct mnt_namespace *ns, struct mount *mnt)
+{
+ unsigned int max = READ_ONCE(sysctl_mount_max);
+ unsigned int mounts = 0, old, pending, sum;
+ struct mount *p;
+
+ for (p = mnt; p; p = next_mnt(p, mnt))
+ mounts++;
+
+ old = ns->mounts;
+ pending = ns->pending_mounts;
+ sum = old + pending;
+ if ((old > sum) ||
+ (pending > sum) ||
+ (max < sum) ||
+ (mounts > (max - sum)))
+ return -ENOSPC;
+
+ ns->pending_mounts = pending + mounts;
+ return 0;
+}
+
/*
* @source_mnt : mount tree to be attached
* @nd : place the mount tree @source_mnt is attached
struct path *parent_path)
{
HLIST_HEAD(tree_list);
+ struct mnt_namespace *ns = dest_mnt->mnt_ns;
struct mountpoint *smp;
struct mount *child, *p;
struct hlist_node *n;
if (IS_ERR(smp))
return PTR_ERR(smp);
+ /* Is there space to add these mounts to the mount namespace? */
+ if (!parent_path) {
+ err = count_mounts(ns, source_mnt);
+ if (err)
+ goto out;
+ }
+
if (IS_MNT_SHARED(dest_mnt)) {
err = invent_group_ids(source_mnt, true);
if (err)
out_cleanup_ids:
while (!hlist_empty(&tree_list)) {
child = hlist_entry(tree_list.first, struct mount, mnt_hash);
+ child->mnt_parent->mnt_ns->pending_mounts = 0;
umount_tree(child, UMOUNT_SYNC);
}
unlock_mount_hash();
cleanup_group_ids(source_mnt, NULL);
out:
+ ns->pending_mounts = 0;
+
read_seqlock_excl(&mount_lock);
put_mountpoint(smp);
read_sequnlock_excl(&mount_lock);
init_waitqueue_head(&new_ns->poll);
new_ns->event = 0;
new_ns->user_ns = get_user_ns(user_ns);
+ new_ns->mounts = 0;
+ new_ns->pending_mounts = 0;
return new_ns;
}
q = new;
while (p) {
q->mnt_ns = new_ns;
+ new_ns->mounts++;
if (new_fs) {
if (&p->mnt == new_fs->root.mnt) {
new_fs->root.mnt = mntget(&q->mnt);
struct mount *mnt = real_mount(m);
mnt->mnt_ns = new_ns;
new_ns->root = mnt;
+ new_ns->mounts++;
list_add(&mnt->mnt_list, &new_ns->list);
} else {
mntput(m);
read_sequnlock_excl(&mount_lock);
}
hlist_add_head(&child->mnt_hash, list);
- return 0;
+ return count_mounts(m->mnt_ns, child);
}
/*
struct mount *copy_tree(struct mount *, struct dentry *, int);
bool is_path_reachable(struct mount *, struct dentry *,
const struct path *root);
+int count_mounts(struct mnt_namespace *ns, struct mount *mnt);
#endif /* _LINUX_PNODE_H */
static inline void clear_soft_dirty_pmd(struct vm_area_struct *vma,
unsigned long addr, pmd_t *pmdp)
{
- pmd_t pmd = pmdp_huge_get_and_clear(vma->vm_mm, addr, pmdp);
+ pmd_t pmd = *pmdp;
+
+ /* See comment in change_huge_pmd() */
+ pmdp_invalidate(vma, addr, pmdp);
+ if (pmd_dirty(*pmdp))
+ pmd = pmd_mkdirty(pmd);
+ if (pmd_young(*pmdp))
+ pmd = pmd_mkyoung(pmd);
pmd = pmd_wrprotect(pmd);
pmd = pmd_clear_soft_dirty(pmd);
lower_dentry = lower_path.dentry;
lower_cur_parent_dentry = dget_parent(lower_dentry);
+ if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
+ err = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
+ if (err == 0) {
+ d_drop(dentry);
+ goto out;
+ }
+ }
+
spin_lock(&lower_dentry->d_lock);
if (d_unhashed(lower_dentry)) {
spin_unlock(&lower_dentry->d_lock);
gid = AID_MEDIA_OBB;
break;
case PERM_ANDROID_PACKAGE:
- if (info->d_uid != 0)
+ if (uid_is_app(info->d_uid))
gid = multiuser_get_ext_gid(info->d_uid);
else
- gid = multiuser_get_uid(info->userid, uid);
+ gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
break;
case PERM_ANDROID_PACKAGE_CACHE:
- if (info->d_uid != 0)
- gid = multiuser_get_cache_gid(info->d_uid);
+ if (uid_is_app(info->d_uid))
+ gid = multiuser_get_ext_cache_gid(info->d_uid);
else
- gid = multiuser_get_uid(info->userid, uid);
+ gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
break;
case PERM_PRE_ROOT:
default:
goto retry_deleg;
}
if (error)
- pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n");
+ pr_debug("sdcardfs: Failed to touch up lower fs gid/uid for %s\n", name);
}
sdcardfs_put_lower_path(dentry, &path);
}
file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
+ vma->vm_private_data = file;
+ get_file(lower_file);
+ vma->vm_file = lower_file;
out:
return err;
get_file(lower_file); /* prevent lower_file from being released */
iocb->ki_filp = lower_file;
err = lower_file->f_op->read_iter(iocb, iter);
- /* ? wait IO finish to update atime as ecryptfs ? */
iocb->ki_filp = file;
fput(lower_file);
+ /* update upper inode atime as needed */
+ if (err >= 0 || err == -EIOCBQUEUED)
+ fsstack_copy_attr_atime(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
out:
return err;
}
err = lower_file->f_op->write_iter(iocb, iter);
iocb->ki_filp = file;
fput(lower_file);
+ /* update upper inode times/sizes as needed */
+ if (err >= 0 || err == -EIOCBQUEUED) {
+ fsstack_copy_inode_size(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
+ fsstack_copy_attr_times(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
+ }
out:
return err;
}
struct sdcardfs_inode_info *info;
struct inode_data data;
struct inode *inode; /* the new inode to return */
- int err;
+
+ if (!igrab(lower_inode))
+ return ERR_PTR(-ESTALE);
data.id = id;
data.lower_inode = lower_inode;
sdcardfs_inode_set, /* inode init function */
&data); /* data passed to test+set fxns */
if (!inode) {
- err = -EACCES;
iput(lower_inode);
- return ERR_PTR(err);
+ return ERR_PTR(-ENOMEM);
}
- /* if found a cached inode, then just return it */
- if (!(inode->i_state & I_NEW))
+ /* if found a cached inode, then just return it (after iput) */
+ if (!(inode->i_state & I_NEW)) {
+ iput(lower_inode);
return inode;
+ }
/* initialize new inode */
info = SDCARDFS_I(inode);
inode->i_ino = lower_inode->i_ino;
- if (!igrab(lower_inode)) {
- err = -ESTALE;
- return ERR_PTR(err);
- }
sdcardfs_set_lower_inode(inode, lower_inode);
inode->i_version++;
/* instatiate a new negative dentry */
dname.name = name->name;
dname.len = name->len;
- dname.hash = full_name_hash(dname.name, dname.len);
- lower_dentry = d_lookup(lower_dir_dentry, &dname);
- if (lower_dentry)
- goto setup_lower;
- lower_dentry = d_alloc(lower_dir_dentry, &dname);
+ /* See if the low-level filesystem might want
+ * to use its own hash
+ */
+ lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname);
+ if (IS_ERR(lower_dentry))
+ return lower_dentry;
if (!lower_dentry) {
- err = -ENOMEM;
+ /* We called vfs_path_lookup earlier, and did not get a negative
+ * dentry then. Don't confuse the lower filesystem by forcing
+ * one on it now...
+ */
+ err = -ENOENT;
goto out;
}
- d_add(lower_dentry, NULL); /* instantiate and hash */
-setup_lower:
lower_path.dentry = lower_dentry;
lower_path.mnt = mntget(lower_dir_mnt);
sdcardfs_set_lower_path(dentry, &lower_path);
static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
int err;
- struct file *file, *lower_file;
+ struct file *file;
const struct vm_operations_struct *lower_vm_ops;
- struct vm_area_struct lower_vma;
- memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
- file = lower_vma.vm_file;
+ file = (struct file *)vma->vm_private_data;
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
BUG_ON(!lower_vm_ops);
- lower_file = sdcardfs_lower_file(file);
- /*
- * XXX: vm_ops->fault may be called in parallel. Because we have to
- * resort to temporarily changing the vma->vm_file to point to the
- * lower file, a concurrent invocation of sdcardfs_fault could see a
- * different value. In this workaround, we keep a different copy of
- * the vma structure in our stack, so we never expose a different
- * value of the vma->vm_file called to us, even temporarily. A
- * better fix would be to change the calling semantics of ->fault to
- * take an explicit file pointer.
- */
- lower_vma.vm_file = lower_file;
- err = lower_vm_ops->fault(&lower_vma, vmf);
+ err = lower_vm_ops->fault(vma, vmf);
return err;
}
+static void sdcardfs_vm_open(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ get_file(file);
+}
+
+static void sdcardfs_vm_close(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ fput(file);
+}
+
static int sdcardfs_page_mkwrite(struct vm_area_struct *vma,
struct vm_fault *vmf)
{
int err = 0;
- struct file *file, *lower_file;
+ struct file *file;
const struct vm_operations_struct *lower_vm_ops;
- struct vm_area_struct lower_vma;
- memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
- file = lower_vma.vm_file;
+ file = (struct file *)vma->vm_private_data;
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
BUG_ON(!lower_vm_ops);
if (!lower_vm_ops->page_mkwrite)
goto out;
- lower_file = sdcardfs_lower_file(file);
- /*
- * XXX: vm_ops->page_mkwrite may be called in parallel.
- * Because we have to resort to temporarily changing the
- * vma->vm_file to point to the lower file, a concurrent
- * invocation of sdcardfs_page_mkwrite could see a different
- * value. In this workaround, we keep a different copy of the
- * vma structure in our stack, so we never expose a different
- * value of the vma->vm_file called to us, even temporarily.
- * A better fix would be to change the calling semantics of
- * ->page_mkwrite to take an explicit file pointer.
- */
- lower_vma.vm_file = lower_file;
- err = lower_vm_ops->page_mkwrite(&lower_vma, vmf);
+ err = lower_vm_ops->page_mkwrite(vma, vmf);
out:
return err;
}
const struct vm_operations_struct sdcardfs_vm_ops = {
.fault = sdcardfs_fault,
.page_mkwrite = sdcardfs_page_mkwrite,
+ .open = sdcardfs_vm_open,
+ .close = sdcardfs_vm_close,
};
#define AID_APP_END 19999 /* last app user */
#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
+#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
+#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
typedef uid_t userid_t;
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
-static inline gid_t multiuser_get_cache_gid(uid_t uid)
+static inline bool uid_is_app(uid_t uid)
{
- return uid - AID_APP_START + AID_CACHE_GID_START;
+ appid_t appid = uid % AID_USER_OFFSET;
+
+ return appid >= AID_APP_START && appid <= AID_APP_END;
+}
+
+static inline gid_t multiuser_get_ext_cache_gid(uid_t uid)
+{
+ return uid - AID_APP_START + AID_EXT_CACHE_GID_START;
}
static inline gid_t multiuser_get_ext_gid(uid_t uid)
{
const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
struct kobject *kobj = of->kn->parent->priv;
- size_t len;
+ ssize_t len;
/*
* If buf != of->prealloc_buf, we don't know how
if (WARN_ON_ONCE(buf != of->prealloc_buf))
return 0;
len = ops->show(kobj, of->kn->priv, buf);
+ if (len < 0)
+ return len;
if (pos) {
if (len <= pos)
return 0;
len -= pos;
memmove(buf, buf + pos, len);
}
- return min(count, len);
+ return min_t(ssize_t, count, len);
}
/* kernfs write callback for regular sysfs files */
return crypto_alloc_instance2(name, alg, ahash_instance_headroom());
}
+static inline void ahash_request_complete(struct ahash_request *req, int err)
+{
+ req->base.complete(&req->base, err);
+}
+
+static inline u32 ahash_request_flags(struct ahash_request *req)
+{
+ return req->base.flags;
+}
+
static inline struct crypto_ahash *crypto_spawn_ahash(
struct crypto_ahash_spawn *spawn)
{
* @ref_type: The type of reference.
* @existed: Upon completion, indicates that an identical reference object
* already existed, and the refcount was upped on that object instead.
+ * @require_existed: Fail with -EPERM if an identical ref object didn't
+ * already exist.
*
* Checks that the base object is shareable and adds a ref object to it.
*
*/
extern int ttm_ref_object_add(struct ttm_object_file *tfile,
struct ttm_base_object *base,
- enum ttm_ref_type ref_type, bool *existed);
+ enum ttm_ref_type ref_type, bool *existed,
+ bool require_existed);
extern bool ttm_ref_object_exists(struct ttm_object_file *tfile,
struct ttm_base_object *base);
pr_cont_kernfs_path(cgrp->kn);
}
+static inline void cgroup_init_kthreadd(void)
+{
+ /*
+ * kthreadd is inherited by all kthreads, keep it in the root so
+ * that the new kthreads are guaranteed to stay in the root until
+ * initialization is finished.
+ */
+ current->no_cgroup_migration = 1;
+}
+
+static inline void cgroup_kthread_ready(void)
+{
+ /*
+ * This kthread finished initialization. The creator should have
+ * set PF_NO_SETAFFINITY if this kthread should stay in the root.
+ */
+ current->no_cgroup_migration = 0;
+}
+
#else /* !CONFIG_CGROUPS */
struct cgroup_subsys_state;
static inline int cgroup_init_early(void) { return 0; }
static inline int cgroup_init(void) { return 0; }
+static inline void cgroup_init_kthreadd(void) {}
+static inline void cgroup_kthread_ready(void) {}
#endif /* !CONFIG_CGROUPS */
WLAN_STATUS_REJECT_DSE_BAND = 96,
WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99,
WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103,
+ /* 802.11ai */
+ WLAN_STATUS_FILS_AUTHENTICATION_FAILURE = 108,
+ WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER = 109,
};
#define IEEE80211_GCMP_MIC_LEN 16
#define IEEE80211_GCMP_PN_LEN 6
+#define FILS_NONCE_LEN 16
+#define FILS_MAX_KEK_LEN 64
+
+#define FILS_ERP_MAX_USERNAME_LEN 16
+#define FILS_ERP_MAX_REALM_LEN 253
+#define FILS_ERP_MAX_RRK_LEN 64
+
+#define PMK_MAX_LEN 48
+
/* Public action codes */
enum ieee80211_pub_actioncode {
WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4,
};
+#define SUITE(oui, id) (((oui) << 8) | (id))
+
/* cipher suite selectors */
-#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
-#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01
-#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02
-/* reserved: 0x000FAC03 */
-#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04
-#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
-#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
-#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08
-#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09
-#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A
-#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B
-#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C
-#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D
-
-#define WLAN_CIPHER_SUITE_SMS4 0x00147201
+#define WLAN_CIPHER_SUITE_USE_GROUP SUITE(0x000FAC, 0)
+#define WLAN_CIPHER_SUITE_WEP40 SUITE(0x000FAC, 1)
+#define WLAN_CIPHER_SUITE_TKIP SUITE(0x000FAC, 2)
+/* reserved: SUITE(0x000FAC, 3) */
+#define WLAN_CIPHER_SUITE_CCMP SUITE(0x000FAC, 4)
+#define WLAN_CIPHER_SUITE_WEP104 SUITE(0x000FAC, 5)
+#define WLAN_CIPHER_SUITE_AES_CMAC SUITE(0x000FAC, 6)
+#define WLAN_CIPHER_SUITE_GCMP SUITE(0x000FAC, 8)
+#define WLAN_CIPHER_SUITE_GCMP_256 SUITE(0x000FAC, 9)
+#define WLAN_CIPHER_SUITE_CCMP_256 SUITE(0x000FAC, 10)
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128 SUITE(0x000FAC, 11)
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256 SUITE(0x000FAC, 12)
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256 SUITE(0x000FAC, 13)
+
+#define WLAN_CIPHER_SUITE_SMS4 SUITE(0x001472, 1)
/* AKM suite selectors */
-#define WLAN_AKM_SUITE_8021X 0x000FAC01
-#define WLAN_AKM_SUITE_PSK 0x000FAC02
-#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
-#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
-#define WLAN_AKM_SUITE_TDLS 0x000FAC07
-#define WLAN_AKM_SUITE_SAE 0x000FAC08
-#define WLAN_AKM_SUITE_FT_OVER_SAE 0x000FAC09
+#define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1)
+#define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2)
+#define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5)
+#define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6)
+#define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7)
+#define WLAN_AKM_SUITE_SAE SUITE(0x000FAC, 8)
+#define WLAN_AKM_SUITE_FT_OVER_SAE SUITE(0x000FAC, 9)
+#define WLAN_AKM_SUITE_FILS_SHA256 SUITE(0x000FAC, 14)
+#define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15)
+#define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16)
+#define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17)
#define WLAN_MAX_KEY_LEN 32
void (*tlbi_domain)(struct iommu_domain *domain);
int (*enable_config_clocks)(struct iommu_domain *domain);
void (*disable_config_clocks)(struct iommu_domain *domain);
+ uint64_t (*iova_to_pte)(struct iommu_domain *domain,
+ dma_addr_t iova);
#ifdef CONFIG_OF_IOMMU
int (*of_xlate)(struct device *dev, struct of_phandle_args *args);
phys_addr_t offset, u64 size,
int prot);
extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr);
+
+extern uint64_t iommu_iova_to_pte(struct iommu_domain *domain,
+ dma_addr_t iova);
/**
* report_iommu_fault() - report about an IOMMU fault to the IOMMU framework
* @domain: the iommu domain where the fault has happened
#ifndef _LINUX_KASAN_H
#define _LINUX_KASAN_H
+#include <linux/sched.h>
#include <linux/types.h>
struct kmem_cache;
#include <asm/kasan.h>
#include <asm/pgtable.h>
-#include <linux/sched.h>
extern unsigned char kasan_zero_page[PAGE_SIZE];
extern pte_t kasan_zero_pte[PTRS_PER_PTE];
void kasan_unpoison_shadow(const void *address, size_t size);
+void kasan_unpoison_task_stack(struct task_struct *task);
+
void kasan_alloc_pages(struct page *page, unsigned int order);
void kasan_free_pages(struct page *page, unsigned int order);
static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
+static inline void kasan_unpoison_task_stack(struct task_struct *task) {}
+
static inline void kasan_enable_current(void) {}
static inline void kasan_disable_current(void) {}
int len, void *val);
int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
int len, struct kvm_io_device *dev);
-int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
- struct kvm_io_device *dev);
+void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+ struct kvm_io_device *dev);
#ifdef CONFIG_KVM_ASYNC_PF
struct kvm_async_pf {
--- /dev/null
+#ifndef _LINUX_MB2CACHE_H
+#define _LINUX_MB2CACHE_H
+
+#include <linux/hash.h>
+#include <linux/list_bl.h>
+#include <linux/list.h>
+#include <linux/atomic.h>
+#include <linux/fs.h>
+
+struct mb2_cache;
+
+struct mb2_cache_entry {
+ /* LRU list - protected by cache->c_lru_list_lock */
+ struct list_head e_lru_list;
+ /* Hash table list - protected by bitlock in e_hash_list_head */
+ struct hlist_bl_node e_hash_list;
+ atomic_t e_refcnt;
+ /* Key in hash - stable during lifetime of the entry */
+ u32 e_key;
+ /* Block number of hashed block - stable during lifetime of the entry */
+ sector_t e_block;
+ /* Head of hash list (for list bit lock) - stable */
+ struct hlist_bl_head *e_hash_list_head;
+};
+
+struct mb2_cache *mb2_cache_create(int bucket_bits);
+void mb2_cache_destroy(struct mb2_cache *cache);
+
+int mb2_cache_entry_create(struct mb2_cache *cache, gfp_t mask, u32 key,
+ sector_t block);
+void __mb2_cache_entry_free(struct mb2_cache_entry *entry);
+static inline int mb2_cache_entry_put(struct mb2_cache *cache,
+ struct mb2_cache_entry *entry)
+{
+ if (!atomic_dec_and_test(&entry->e_refcnt))
+ return 0;
+ __mb2_cache_entry_free(entry);
+ return 1;
+}
+
+void mb2_cache_entry_delete_block(struct mb2_cache *cache, u32 key,
+ sector_t block);
+struct mb2_cache_entry *mb2_cache_entry_find_first(struct mb2_cache *cache,
+ u32 key);
+struct mb2_cache_entry *mb2_cache_entry_find_next(struct mb2_cache *cache,
+ struct mb2_cache_entry *entry);
+void mb2_cache_entry_touch(struct mb2_cache *cache,
+ struct mb2_cache_entry *entry);
+
+#endif /* _LINUX_MB2CACHE_H */
#ifdef CONFIG_HUGETLB_PAGE
atomic_long_t hugetlb_usage;
#endif
+#ifdef CONFIG_MSM_APP_SETTINGS
+ int app_setting;
+#endif
+
};
static inline void mm_init_cpumask(struct mm_struct *mm)
extern dev_t name_to_dev_t(const char *name);
+extern unsigned int sysctl_mount_max;
+
#endif /* _LINUX_MOUNT_H */
POWER_SUPPLY_PROP_CONNECTOR_HEALTH,
POWER_SUPPLY_PROP_CTM_CURRENT_MAX,
POWER_SUPPLY_PROP_HW_CURRENT_MAX,
+ POWER_SUPPLY_PROP_REAL_TYPE,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
--- /dev/null
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 __NCP6335D_H__
+#define __NCP6335D_H__
+
+enum {
+ NCP6335D_VSEL0,
+ NCP6335D_VSEL1,
+};
+
+struct ncp6335d_platform_data {
+ struct regulator_init_data *init_data;
+ int default_vsel;
+ int slew_rate_ns;
+ int discharge_enable;
+ bool sleep_enable;
+};
+
+#ifdef CONFIG_REGULATOR_ONSEMI_NCP6335D
+int __init ncp6335d_regulator_init(void);
+#else
+static inline int __init ncp6335d_regulator_init(void) { return 0; }
+#endif
+
+#endif
#ifdef CONFIG_COMPAT_BRK
unsigned brk_randomized:1;
#endif
+#ifdef CONFIG_CGROUPS
+ /* disallow userland-initiated cgroup migration */
+ unsigned no_cgroup_migration:1;
+#endif
unsigned long atomic_flags; /* Flags needing atomic access. */
#define CFG80211_CONNECT_TIMEOUT 1
#define CFG80211_CONNECT_TIMEOUT_REASON_CODE 1
+/* Indicate backport support for the new connect done api */
+#define CFG80211_CONNECT_DONE 1
+/* Indicate backport support for FILS SK offload in cfg80211 */
+#define CFG80211_FILS_SK_OFFLOAD_SUPPORT 1
+
/*
* wireless hardware capability structures
*/
* @ht_capa_mask: The bits of ht_capa which are to be used.
* @vht_capa: VHT capability override
* @vht_capa_mask: VHT capability mask indicating which fields to use
+ * @fils_kek: FILS KEK for protecting (Re)Association Request/Response frame or
+ * %NULL if FILS is not used.
+ * @fils_kek_len: Length of fils_kek in octets
+ * @fils_nonces: FILS nonces (part of AAD) for protecting (Re)Association
+ * Request/Response frame or %NULL if FILS is not used. This field starts
+ * with 16 octets of STA Nonce followed by 16 octets of AP Nonce.
*/
struct cfg80211_assoc_request {
struct cfg80211_bss *bss;
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
struct ieee80211_vht_cap vht_capa, vht_capa_mask;
+ const u8 *fils_kek;
+ size_t fils_kek_len;
+ const u8 *fils_nonces;
};
/**
* networks.
* @bss_select: criteria to be used for BSS selection.
* @prev_bssid: previous BSSID, if not %NULL use reassociate frame
- */
+ * @fils_erp_username: EAP re-authentication protocol (ERP) username part of the
+ * NAI or %NULL if not specified. This is used to construct FILS wrapped
+ * data IE.
+ * @fils_erp_username_len: Length of @fils_erp_username in octets.
+ * @fils_erp_realm: EAP re-authentication protocol (ERP) realm part of NAI or
+ * %NULL if not specified. This specifies the domain name of ER server and
+ * is used to construct FILS wrapped data IE.
+ * @fils_erp_realm_len: Length of @fils_erp_realm in octets.
+ * @fils_erp_next_seq_num: The next sequence number to use in the FILS ERP
+ * messages. This is also used to construct FILS wrapped data IE.
+ * @fils_erp_rrk: ERP re-authentication Root Key (rRK) used to derive additional
+ * keys in FILS or %NULL if not specified.
+ * @fils_erp_rrk_len: Length of @fils_erp_rrk in octets.
+*/
struct cfg80211_connect_params {
struct ieee80211_channel *channel;
struct ieee80211_channel *channel_hint;
bool pbss;
struct cfg80211_bss_selection bss_select;
const u8 *prev_bssid;
+ const u8 *fils_erp_username;
+ size_t fils_erp_username_len;
+ const u8 *fils_erp_realm;
+ size_t fils_erp_realm_len;
+ u16 fils_erp_next_seq_num;
+ const u8 *fils_erp_rrk;
+ size_t fils_erp_rrk_len;
};
/**
* This structure is passed to the set/del_pmksa() method for PMKSA
* caching.
*
- * @bssid: The AP's BSSID.
- * @pmkid: The PMK material itself.
+ * @bssid: The AP's BSSID (may be %NULL).
+ * @pmkid: The identifier to refer a PMKSA.
+ * @pmk: The PMK for the PMKSA identified by @pmkid. This is used for key
+ * derivation by a FILS STA. Otherwise, %NULL.
+ * @pmk_len: Length of the @pmk. The length of @pmk can differ depending on
+ * the hash algorithm used to generate this.
+ * @ssid: SSID to specify the ESS within which a PMKSA is valid when using FILS
+ * cache identifier (may be %NULL).
+ * @ssid_len: Length of the @ssid in octets.
+ * @cache_id: 2-octet cache identifier advertized by a FILS AP identifying the
+ * scope of PMKSA. This is valid only if @ssid_len is non-zero (may be
+ * %NULL).
*/
struct cfg80211_pmksa {
const u8 *bssid;
const u8 *pmkid;
+ const u8 *pmk;
+ size_t pmk_len;
+ const u8 *ssid;
+ size_t ssid_len;
+ const u8 *cache_id;
};
/**
#endif
/**
+ * struct cfg80211_connect_resp_params - Connection response params
+ * @status: Status code, %WLAN_STATUS_SUCCESS for successful connection, use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ * the real status code for failures. If this call is used to report a
+ * failure due to a timeout (e.g., not receiving an Authentication frame
+ * from the AP) instead of an explicit rejection by the AP, -1 is used to
+ * indicate that this is a failure, but without a status code.
+ * @timeout_reason is used to report the reason for the timeout in that
+ * case.
+ * @bssid: The BSSID of the AP (may be %NULL)
+ * @bss: Entry of bss to which STA got connected to, can be obtained through
+ * cfg80211_get_bss() (may be %NULL). Only one parameter among @bssid and
+ * @bss needs to be specified.
+ * @req_ie: Association request IEs (may be %NULL)
+ * @req_ie_len: Association request IEs length
+ * @resp_ie: Association response IEs (may be %NULL)
+ * @resp_ie_len: Association response IEs length
+ * @fils_kek: KEK derived from a successful FILS connection (may be %NULL)
+ * @fils_kek_len: Length of @fils_kek in octets
+ * @update_erp_next_seq_num: Boolean value to specify whether the value in
+ * @fils_erp_next_seq_num is valid.
+ * @fils_erp_next_seq_num: The next sequence number to use in ERP message in
+ * FILS Authentication. This value should be specified irrespective of the
+ * status for a FILS connection.
+ * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL).
+ * @pmk_len: Length of @pmk in octets
+ * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID
+ * used for this FILS connection (may be %NULL).
+ * @timeout_reason: Reason for connection timeout. This is used when the
+ * connection fails due to a timeout instead of an explicit rejection from
+ * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is
+ * not known. This value is used only if @status < 0 to indicate that the
+ * failure is due to a timeout and not due to explicit rejection by the AP.
+ * This value is ignored in other cases (@status >= 0).
+ */
+struct cfg80211_connect_resp_params {
+ int status;
+ const u8 *bssid;
+ struct cfg80211_bss *bss;
+ const u8 *req_ie;
+ size_t req_ie_len;
+ const u8 *resp_ie;
+ size_t resp_ie_len;
+ const u8 *fils_kek;
+ size_t fils_kek_len;
+ bool update_erp_next_seq_num;
+ u16 fils_erp_next_seq_num;
+ const u8 *pmk;
+ size_t pmk_len;
+ const u8 *pmkid;
+ enum nl80211_timeout_reason timeout_reason;
+};
+
+/**
+ * cfg80211_connect_done - notify cfg80211 of connection result
+ *
+ * @dev: network device
+ * @params: connection response parameters
+ * @gfp: allocation flags
+ *
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_bss(), but takes a structure pointer for connection response
+ * parameters. Only one of the functions among cfg80211_connect_bss(),
+ * cfg80211_connect_result(), cfg80211_connect_timeout(),
+ * and cfg80211_connect_done() should be called.
+ */
+void cfg80211_connect_done(struct net_device *dev,
+ struct cfg80211_connect_resp_params *params,
+ gfp_t gfp);
+
+/**
* cfg80211_connect_bss - notify cfg80211 of connection result
*
* @dev: network device
* @req_ie_len: association request IEs length
* @resp_ie: association response IEs (may be %NULL)
* @resp_ie_len: assoc response IEs length
- * @status: status code, 0 for successful connection, use
- * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
- * the real status code for failures.
+ * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ * the real status code for failures. If this call is used to report a
+ * failure due to a timeout (e.g., not receiving an Authentication frame
+ * from the AP) instead of an explicit rejection by the AP, -1 is used to
+ * indicate that this is a failure, but without a status code.
+ * @timeout_reason is used to report the reason for the timeout in that
+ * case.
* @gfp: allocation flags
* @timeout_reason: reason for connection timeout. This is used when the
* connection fails due to a timeout instead of an explicit rejection from
* failure is due to a timeout and not due to explicit rejection by the AP.
* This value is ignored in other cases (@status >= 0).
*
- * It should be called by the underlying driver whenever connect() has
- * succeeded. This is similar to cfg80211_connect_result(), but with the
- * option of identifying the exact bss entry for the connection. Only one of
- * these functions should be called.
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_result(), but with the option of identifying the exact bss
+ * entry for the connection. Only one of the functions among
+ * cfg80211_connect_bss(), cfg80211_connect_result(),
+ * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called.
*/
-void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
- struct cfg80211_bss *bss, const u8 *req_ie,
- size_t req_ie_len, const u8 *resp_ie,
- size_t resp_ie_len, int status, gfp_t gfp,
- enum nl80211_timeout_reason timeout_reason);
+static inline void
+cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
+ struct cfg80211_bss *bss, const u8 *req_ie,
+ size_t req_ie_len, const u8 *resp_ie,
+ size_t resp_ie_len, int status, gfp_t gfp,
+ enum nl80211_timeout_reason timeout_reason)
+{
+ struct cfg80211_connect_resp_params params;
+
+ memset(¶ms, 0, sizeof(params));
+ params.status = status;
+ params.bssid = bssid;
+ params.bss = bss;
+ params.req_ie = req_ie;
+ params.req_ie_len = req_ie_len;
+ params.resp_ie = resp_ie;
+ params.resp_ie_len = resp_ie_len;
+ params.timeout_reason = timeout_reason;
+
+ cfg80211_connect_done(dev, ¶ms, gfp);
+}
/**
* cfg80211_connect_result - notify cfg80211 of connection result
* @req_ie_len: association request IEs length
* @resp_ie: association response IEs (may be %NULL)
* @resp_ie_len: assoc response IEs length
- * @status: status code, 0 for successful connection, use
+ * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
* %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
* the real status code for failures.
* @gfp: allocation flags
*
- * It should be called by the underlying driver whenever connect() has
- * succeeded.
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_bss() which allows the exact bss entry to be specified. Only
+ * one of the functions among cfg80211_connect_bss(), cfg80211_connect_result(),
+ * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called.
*/
static inline void
cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
* in a sequence where no explicit authentication/association rejection was
* received from the AP. This could happen, e.g., due to not being able to send
* out the Authentication or Association Request frame or timing out while
- * waiting for the response.
+ * waiting for the response. Only one of the functions among
+ * cfg80211_connect_bss(), cfg80211_connect_result(),
+ * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called.
*/
static inline void
cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid,
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
*/
#ifdef CONFIG_MSM_BOOT_STATS
+
+#define TIMER_KHZ 32768
+extern struct boot_stats __iomem *boot_stats;
+
+struct boot_stats {
+ uint32_t bootloader_start;
+ uint32_t bootloader_end;
+ uint32_t bootloader_display;
+ uint32_t bootloader_load_kernel;
+ uint32_t load_kernel_start;
+ uint32_t load_kernel_end;
+#ifdef CONFIG_MSM_BOOT_TIME_MARKER
+ uint32_t bootloader_early_domain_start;
+ uint32_t bootloader_checksum;
+#endif
+};
+
int boot_stats_init(void);
+int boot_stats_exit(void);
+unsigned long long int msm_timer_get_sclk_ticks(void);
#else
static inline int boot_stats_init(void) { return 0; }
+unsigned long long int msm_timer_get_sclk_ticks(void) { return 0; }
+#endif
+
+#ifdef CONFIG_MSM_BOOT_TIME_MARKER
+
+static inline int boot_marker_enabled(void) { return 1; }
+void place_marker(const char *name);
+#else
+inline void place_marker(char *name);
+static inline int boot_marker_enabled(void) { return 0; }
#endif
*/
#ifdef CONFIG_QCOM_MINIDUMP
extern int msm_minidump_add_region(const struct md_region *entry);
-extern bool msm_minidump_enabled(void);
+extern bool minidump_enabled;
#else
static inline int msm_minidump_add_region(const struct md_region *entry)
{
struct profiler_bw_cntrs_req {
uint32_t total;
- uint32_t gpu;
uint32_t cpu;
+ uint32_t gpu;
uint32_t cmd;
};
struct compat_profiler_bw_cntrs_req {
compat_uint_t total;
- compat_uint_t gpu;
compat_uint_t cpu;
+ compat_uint_t gpu;
compat_uint_t cmd;
};
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmtellurium")
#define early_machine_is_msm8996() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8996")
-#define early_machine_is_msm8996_auto() \
- of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8996-cdp")
#define early_machine_is_msm8929() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8929")
#define early_machine_is_msm8998() \
* @sysmon_shutdown_ret: Return value for the call to sysmon_send_shutdown
* @system_debug: If "set", triggers a device restart when the
* subsystem's wdog bite handler is invoked.
+ * @ignore_ssr_failure: SSR failures are usually fatal and results in panic. If
+ * set will ignore failure.
* @edge: GLINK logical name of the subsystem
*/
struct subsys_desc {
u32 sysmon_pid;
int sysmon_shutdown_ret;
bool system_debug;
+ bool ignore_ssr_failure;
const char *edge;
};
+++ /dev/null
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __MSM_DTS_EAGLE_H__
-#define __MSM_DTS_EAGLE_H__
-
-#include <linux/compat.h>
-#include <sound/soc.h>
-#include <sound/devdep_params.h>
-#include <sound/q6asm-v2.h>
-
-#ifdef CONFIG_COMPAT
-enum {
- DTS_EAGLE_IOCTL_GET_CACHE_SIZE32 = _IOR(0xF2, 0, __s32),
- DTS_EAGLE_IOCTL_SET_CACHE_SIZE32 = _IOW(0xF2, 1, __s32),
- DTS_EAGLE_IOCTL_GET_PARAM32 = _IOR(0xF2, 2, compat_uptr_t),
- DTS_EAGLE_IOCTL_SET_PARAM32 = _IOW(0xF2, 3, compat_uptr_t),
- DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32 =
- _IOW(0xF2, 4, compat_uptr_t),
- DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32 =
- _IOW(0xF2, 5, compat_uptr_t),
- DTS_EAGLE_IOCTL_GET_LICENSE32 =
- _IOR(0xF2, 6, compat_uptr_t),
- DTS_EAGLE_IOCTL_SET_LICENSE32 =
- _IOW(0xF2, 7, compat_uptr_t),
- DTS_EAGLE_IOCTL_SEND_LICENSE32 = _IOW(0xF2, 8, __s32),
- DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32 = _IOW(0xF2, 9,
- compat_uptr_t),
-};
-#endif
-
-#ifdef CONFIG_DTS_EAGLE
-void msm_dts_ion_memmap(struct param_outband *po_);
-int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module);
-int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable);
-void msm_dts_eagle_add_controls(struct snd_soc_platform *platform);
-int msm_dts_eagle_set_stream_gain(struct audio_client *ac,
- int lgain, int rgain);
-int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf,
- bool for_pre, bool get, struct audio_client *ac,
- struct param_outband *po);
-int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf,
- bool for_pre, bool get);
-int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg);
-int msm_dts_eagle_is_hpx_on(void);
-int msm_dts_eagle_init_pre(struct audio_client *ac);
-int msm_dts_eagle_deinit_pre(struct audio_client *ac);
-int msm_dts_eagle_init_post(int port_id, int copp_id);
-int msm_dts_eagle_deinit_post(int port_id, int topology);
-int msm_dts_eagle_init_master_module(struct audio_client *ac);
-int msm_dts_eagle_deinit_master_module(struct audio_client *ac);
-int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime);
-void msm_dts_eagle_pcm_free(struct snd_pcm *pcm);
-int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg);
-#else
-static inline void msm_dts_ion_memmap(struct param_outband *po_)
-{
- pr_debug("%s\n", __func__);
-}
-static inline int msm_dts_eagle_enable_asm(struct audio_client *ac,
- u32 enable, int module)
-{
- return 0;
-}
-static inline int msm_dts_eagle_enable_adm(int port_id, int copp_idx,
- u32 enable)
-{
- return 0;
-}
-static inline void msm_dts_eagle_add_controls(struct snd_soc_platform *platform)
-{
-}
-static inline int msm_dts_eagle_set_stream_gain(struct audio_client *ac,
- int lgain, int rgain)
-{
- pr_debug("%s\n", __func__);
- return 0;
-}
-static inline int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd,
- char *buf, bool for_pre, bool get,
- struct audio_client *ac,
- struct param_outband *po)
-{
- return 0;
-}
-static inline int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd,
- char *buf, bool for_pre, bool get)
-{
- return 0;
-}
-static inline int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg)
-{
- return -EPERM;
-}
-static inline int msm_dts_eagle_is_hpx_on(void)
-{
- return 0;
-}
-static inline int msm_dts_eagle_init_pre(struct audio_client *ac)
-{
- return 0;
-}
-static inline int msm_dts_eagle_deinit_pre(struct audio_client *ac)
-{
- return 0;
-}
-static inline int msm_dts_eagle_init_post(int port_id, int coppid)
-{
- return 0;
-}
-static inline int msm_dts_eagle_deinit_post(int port_id, int topology)
-{
- return 0;
-}
-static inline int msm_dts_eagle_init_master_module(struct audio_client *ac)
-{
- return 0;
-}
-static inline int msm_dts_eagle_deinit_master_module(struct audio_client *ac)
-{
- return 0;
-}
-static inline int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime)
-{
- pr_debug("%s\n", __func__);
- return 0;
-}
-static inline void msm_dts_eagle_pcm_free(struct snd_pcm *pcm)
-{
- pr_debug("%s\n", __func__);
-}
-static inline int msm_dts_eagle_compat_ioctl(unsigned int cmd,
- unsigned long arg)
-{
- return 0;
-}
-#endif
-
-#endif
ADM_AUDVOL_CAL,
ADM_RTAC_INFO_CAL,
ADM_RTAC_APR_CAL,
- ADM_DTS_EAGLE,
ADM_SRS_TRUMEDIA,
ADM_RTAC_AUDVOL_CAL,
ADM_MAX_CAL_TYPES
/*
- * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015, 2017, 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
TP_ARGS(core, phase)
);
+DECLARE_EVENT_CLASS(clk_state_dump,
+
+ TP_PROTO(const char *name, unsigned int prepare_count,
+ unsigned int enable_count, unsigned long rate, unsigned int vdd_level),
+
+ TP_ARGS(name, prepare_count, enable_count, rate, vdd_level),
+
+ TP_STRUCT__entry(
+ __string(name, name)
+ __field(unsigned int, prepare_count)
+ __field(unsigned int, enable_count)
+ __field(unsigned long, rate)
+ __field(unsigned int, vdd_level)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __entry->prepare_count = prepare_count;
+ __entry->enable_count = enable_count;
+ __entry->rate = rate;
+ __entry->vdd_level = vdd_level;
+ ),
+
+ TP_printk("%s\tprepare:enable cnt [%u:%u]\trate: vdd_level [%lu:%u]",
+ __get_str(name), __entry->prepare_count, __entry->enable_count,
+ __entry->rate, __entry->vdd_level)
+);
+
+DEFINE_EVENT(clk_state_dump, clk_state,
+
+ TP_PROTO(const char *name, unsigned int prepare_count,
+ unsigned int enable_count, unsigned long rate, unsigned int vdd_level),
+
+ TP_ARGS(name, prepare_count, enable_count, rate, vdd_level)
+);
+
#endif /* _TRACE_CLK_H */
/* This part must be outside protection */
__u32 nr_ops;
};
+#define MSM_GEM_SYNC_TO_DEV 0
+#define MSM_GEM_SYNC_TO_CPU 1
+
+struct drm_msm_gem_syncop {
+ __u32 handle;
+ __u32 op;
+};
+
+struct drm_msm_gem_sync {
+ __u32 nr_ops;
+ __u64 __user ops;
+};
#define DRM_MSM_GET_PARAM 0x00
/* placeholder:
#define DRM_MSM_COUNTER_GET 0x43
#define DRM_MSM_COUNTER_PUT 0x44
#define DRM_MSM_COUNTER_READ 0x45
+#define DRM_MSM_GEM_SYNC 0x46
/**
* Currently DRM framework supports only VSYNC event.
#define DRM_IOCTL_MSM_COUNTER_READ \
DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_COUNTER_READ, \
struct drm_msm_counter_read)
-
+#define DRM_IOCTL_MSM_GEM_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_GEM_SYNC,\
+ struct drm_msm_gem_sync)
#endif /* __MSM_DRM_H__ */
#define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, unsigned int)
#define ESOC_NOTIFY _IOW(ESOC_CODE, 3, unsigned int)
#define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, unsigned int)
+#define ESOC_GET_ERR_FATAL _IOR(ESOC_CODE, 5, unsigned int)
#define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, unsigned int)
#define ESOC_REG_REQ_ENG _IO(ESOC_CODE, 7)
#define ESOC_REG_CMD_ENG _IO(ESOC_CODE, 8)
#define HSIC "HSIC"
#define HSICPCIe "HSIC+PCIe"
#define PCIe "PCIe"
+#define ESOC_REQ_SEND_SHUTDOWN ESOC_REQ_SEND_SHUTDOWN
enum esoc_evt {
ESOC_RUN_STATE = 0x1,
ESOC_REQ_IMG = 1,
ESOC_REQ_DEBUG,
ESOC_REQ_SHUTDOWN,
+ ESOC_REQ_SEND_SHUTDOWN,
};
#ifdef __KERNEL__
*/
/**
+ * DOC: FILS shared key authentication offload
+ *
+ * FILS shared key authentication offload can be advertized by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+ * eventually do a key derivation as per IEEE 802.11ai. The below additional
+ * parameters should be given to driver in %NL80211_CMD_CONNECT.
+ * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
+ * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK
+ * rIK should be used to generate an authentication tag on the ERP message and
+ * rMSK should be used to derive a PMKSA.
+ * rIK, rMSK should be generated and keyname_nai, sequence number should be used
+ * as specified in IETF RFC 6696.
+ *
+ * When FILS shared key authentication is completed, driver needs to provide the
+ * below additional parameters to userspace.
+ * %NL80211_ATTR_FILS_KEK - used for key renewal
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
+ * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
+ * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+ * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with
+ * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to
+ * use in a FILS shared key connection with PMKSA caching.
+ */
+
+/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_UNSPEC: unspecified command to catch errors
* @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
* NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
*
- * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC
- * (for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC
+ * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK
+ * (PMK is used for PTKSA derivation in case of FILS shared key offload) or
+ * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+ * advertized by a FILS capable AP identifying the scope of PMKSA in an
+ * ESS.
* @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
- * (for the BSSID) and %NL80211_ATTR_PMKID.
+ * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+ * authentication.
* @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
*
* @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
* u32 attribute with an &enum nl80211_timeout_reason value. This is used,
* e.g., with %NL80211_CMD_CONNECT event.
*
+ * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP)
+ * username part of NAI used to refer keys rRK and rIK. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part
+ * of NAI specifying the domain name of the ER server. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number
+ * to use in ERP messages. This is used in generating the FILS wrapped data
+ * for FILS authentication and is used with %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the
+ * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and
+ * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK
+ * from successful FILS authentication and is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ * identifying the scope of PMKSAs. This is used with
+ * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+ * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID.
+ * This is used with @NL80211_CMD_SET_PMKSA.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
NL80211_ATTR_TIMEOUT_REASON,
+ NL80211_ATTR_FILS_ERP_USERNAME,
+ NL80211_ATTR_FILS_ERP_REALM,
+ NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ NL80211_ATTR_FILS_ERP_RRK,
+ NL80211_ATTR_FILS_CACHE_ID,
+
+ NL80211_ATTR_PMK,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
* @__NL80211_ATTR_CQM_INVALID: invalid
* @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
* the threshold for the RSSI level at which an event will be sent. Zero
- * to disable.
+ * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
+ * set, multiple values can be supplied as a low-to-high sorted array of
+ * threshold values in dBm. Events will be sent when the RSSI value
+ * crosses any of the thresholds.
* @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
* the minimum amount the RSSI level must change after an event before a
* new event may be issued (to reduce effects of RSSI oscillation).
* @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
* for reporting BSSs with better RSSI than the current connected BSS
* (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
+ * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
+ * RSSI threshold values to monitor rather than exactly one threshold.
+ * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
+ * authentication with %NL80211_CMD_CONNECT.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_EXT_FEATURE_CQM_RSSI_LIST,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
struct msm_ispif_pack_cfg pack_cfg[CID_MAX];
struct msm_ispif_right_param_entry right_entries[MAX_PARAM_ENTRIES];
uint32_t stereo_enable;
+ uint16_t line_width[VFE_MAX];
};
struct msm_ispif_param_data {
ISPIF_ENABLE_REG_DUMP,
ISPIF_SET_VFE_INFO,
ISPIF_CFG2,
+ ISPIF_CFG_STEREO,
};
struct ispif_cfg_data {
#define ISPIF_3D_SUPPORT 1
+#define ISPIF_LINE_WIDTH_SUPPORT 1
+
#define VIDIOC_MSM_ISPIF_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data)
if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) &&
!uid_eq(cred->euid, tcred->uid) &&
!uid_eq(cred->euid, tcred->suid) &&
- !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
+ !ns_capable(tcred->user_ns, CAP_SYS_NICE))
ret = -EACCES;
if (!ret && cgroup_on_dfl(dst_cgrp)) {
tsk = tsk->group_leader;
/*
- * Workqueue threads may acquire PF_NO_SETAFFINITY and become
- * trapped in a cpuset, or RT worker may be born in a cgroup
- * with no rt_runtime allocated. Just say no.
+ * kthreads may acquire PF_NO_SETAFFINITY during initialization.
+ * If userland migrates such a kthread to a non-root cgroup, it can
+ * become trapped in a cpuset, or RT kthread may be born in a
+ * cgroup with no rt_runtime allocated. Just say no.
*/
- if (tsk == kthreadd_task || (tsk->flags & PF_NO_SETAFFINITY)) {
+ if (tsk->no_cgroup_migration || (tsk->flags & PF_NO_SETAFFINITY)) {
ret = -EINVAL;
goto out_unlock_rcu;
}
return 0;
}
+/*
+ * Variation on perf_event_ctx_lock_nested(), except we take two context
+ * mutexes.
+ */
+static struct perf_event_context *
+__perf_event_ctx_lock_double(struct perf_event *group_leader,
+ struct perf_event_context *ctx)
+{
+ struct perf_event_context *gctx;
+
+again:
+ rcu_read_lock();
+ gctx = READ_ONCE(group_leader->ctx);
+ if (!atomic_inc_not_zero(&gctx->refcount)) {
+ rcu_read_unlock();
+ goto again;
+ }
+ rcu_read_unlock();
+
+ mutex_lock_double(&gctx->mutex, &ctx->mutex);
+
+ if (group_leader->ctx != gctx) {
+ mutex_unlock(&ctx->mutex);
+ mutex_unlock(&gctx->mutex);
+ put_ctx(gctx);
+ goto again;
+ }
+
+ return gctx;
+}
+
/**
* sys_perf_event_open - open a performance event, associate it to a task/cpu
*
}
if (move_group) {
- gctx = group_leader->ctx;
- mutex_lock_double(&gctx->mutex, &ctx->mutex);
+ gctx = __perf_event_ctx_lock_double(group_leader, ctx);
+
+ /*
+ * Check if we raced against another sys_perf_event_open() call
+ * moving the software group underneath us.
+ */
+ if (!(group_leader->group_flags & PERF_GROUP_SOFTWARE)) {
+ /*
+ * If someone moved the group out from under us, check
+ * if this new event wound up on the same ctx, if so
+ * its the regular !move_group case, otherwise fail.
+ */
+ if (gctx != ctx) {
+ err = -EINVAL;
+ goto err_locked;
+ } else {
+ perf_event_ctx_unlock(group_leader, gctx);
+ move_group = 0;
+ }
+ }
} else {
mutex_lock(&ctx->mutex);
}
perf_unpin_context(ctx);
if (move_group)
- mutex_unlock(&gctx->mutex);
+ perf_event_ctx_unlock(group_leader, gctx);
mutex_unlock(&ctx->mutex);
if (group_leader)
mutex_unlock(&group_leader->group_leader_mutex);
err_locked:
if (move_group)
- mutex_unlock(&gctx->mutex);
+ perf_event_ctx_unlock(group_leader, gctx);
mutex_unlock(&ctx->mutex);
/* err_file: */
fput(event_file);
#include <linux/freezer.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
+#include <linux/cgroup.h>
#include <trace/events/sched.h>
static DEFINE_SPINLOCK(kthread_create_lock);
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
+ cgroup_kthread_ready();
__kthread_parkme(&self);
ret = threadfn(data);
}
set_mems_allowed(node_states[N_MEMORY]);
current->flags |= PF_NOFREEZE;
+ cgroup_init_kthreadd();
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
reorder = &next_queue->reorder;
+ spin_lock(&reorder->lock);
if (!list_empty(&reorder->list)) {
padata = list_entry(reorder->list.next,
struct padata_priv, list);
- spin_lock(&reorder->lock);
list_del_init(&padata->list);
atomic_dec(&pd->reorder_objects);
- spin_unlock(&reorder->lock);
pd->processed++;
+ spin_unlock(&reorder->lock);
goto out;
}
+ spin_unlock(&reorder->lock);
if (__this_cpu_read(pd->pqueue->cpu_index) == next_queue->cpu_index) {
padata = ERR_PTR(-ENODATA);
*/
int hibernate(void)
{
- int error;
+ int error, nr_calls = 0;
if (!hibernation_available()) {
pr_debug("PM: Hibernation not available.\n");
}
pm_prepare_console();
- error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
- if (error)
+ error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
+ if (error) {
+ nr_calls--;
goto Exit;
+ }
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
/* Don't bother checking whether freezer_test_done is true */
freezer_test_done = false;
Exit:
- pm_notifier_call_chain(PM_POST_HIBERNATION);
+ __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
pm_restore_console();
atomic_inc(&snapshot_device_available);
Unlock:
*/
static int software_resume(void)
{
- int error;
+ int error, nr_calls = 0;
unsigned int flags;
/*
}
pm_prepare_console();
- error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
- if (error)
+ error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
+ if (error) {
+ nr_calls--;
goto Close_Finish;
+ }
pr_debug("PM: Preparing processes for restore.\n");
error = freeze_processes();
unlock_device_hotplug();
thaw_processes();
Finish:
- pm_notifier_call_chain(PM_POST_RESTORE);
+ __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
pm_restore_console();
atomic_inc(&snapshot_device_available);
/* For success case, the suspend path will release the lock */
}
EXPORT_SYMBOL_GPL(unregister_pm_notifier);
-int pm_notifier_call_chain(unsigned long val)
+int __pm_notifier_call_chain(unsigned long val, int nr_to_call, int *nr_calls)
{
- int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL);
+ int ret;
+
+ ret = __blocking_notifier_call_chain(&pm_chain_head, val, NULL,
+ nr_to_call, nr_calls);
return notifier_to_errno(ret);
}
+int pm_notifier_call_chain(unsigned long val)
+{
+ return __pm_notifier_call_chain(val, -1, NULL);
+}
/* If set, devices may be suspended and resumed asynchronously. */
int pm_async_enabled = 1;
#ifdef CONFIG_PM_SLEEP
/* kernel/power/main.c */
+extern int __pm_notifier_call_chain(unsigned long val, int nr_to_call,
+ int *nr_calls);
extern int pm_notifier_call_chain(unsigned long val);
#endif
*/
static int suspend_prepare(suspend_state_t state)
{
- int error;
+ int error, nr_calls = 0;
if (!sleep_state_supported(state))
return -EPERM;
pm_prepare_console();
- error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
- if (error)
+ error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
+ if (error) {
+ nr_calls--;
goto Finish;
+ }
trace_suspend_resume(TPS("freeze_processes"), 0, true);
error = suspend_freeze_processes();
suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE);
Finish:
- pm_notifier_call_chain(PM_POST_SUSPEND);
+ __pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL);
pm_restore_console();
return error;
}
static int snapshot_open(struct inode *inode, struct file *filp)
{
struct snapshot_data *data;
- int error;
+ int error, nr_calls = 0;
if (!hibernation_available())
return -EPERM;
swap_type_of(swsusp_resume_device, 0, NULL) : -1;
data->mode = O_RDONLY;
data->free_bitmaps = false;
- error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
+ error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
if (error)
- pm_notifier_call_chain(PM_POST_HIBERNATION);
+ __pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL);
} else {
/*
* Resuming. We may need to wait for the image device to
data->swap = -1;
data->mode = O_WRONLY;
- error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
+ error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
if (!error) {
error = create_basic_memory_bitmaps();
data->free_bitmaps = !error;
- }
+ } else
+ nr_calls--;
+
if (error)
- pm_notifier_call_chain(PM_POST_RESTORE);
+ __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
}
if (error)
atomic_inc(&snapshot_device_available);
WARN_ON(!task->ptrace || task->parent != current);
+ /*
+ * PTRACE_LISTEN can allow ptrace_trap_notify to wake us up remotely.
+ * Recheck state under the lock to close this race.
+ */
spin_lock_irq(&task->sighand->siglock);
- if (__fatal_signal_pending(task))
- wake_up_state(task, __TASK_TRACED);
- else
- task->state = TASK_TRACED;
+ if (task->state == __TASK_TRACED) {
+ if (__fatal_signal_pending(task))
+ wake_up_state(task, __TASK_TRACED);
+ else
+ task->state = TASK_TRACED;
+ }
spin_unlock_irq(&task->sighand->siglock);
}
* Thomas Gleixner, Mike Kravetz
*/
+#include <linux/kasan.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/nmi.h>
#ifdef CONFIG_PARAVIRT
#include <asm/paravirt.h>
#endif
+#ifdef CONFIG_MSM_APP_SETTINGS
+#include <asm/app_api.h>
+#endif
#include "sched.h"
#include "../workqueue_internal.h"
fire_sched_out_preempt_notifiers(prev, next);
prepare_lock_switch(rq, next);
prepare_arch_switch(next);
+
+#ifdef CONFIG_MSM_APP_SETTINGS
+ if (use_app_setting)
+ switch_app_setting_bit(prev, next);
+
+ if (use_32bit_app_setting || use_32bit_app_setting_pro)
+ switch_32bit_app_setting_bit(prev, next);
+#endif
}
/**
idle->state = TASK_RUNNING;
idle->se.exec_start = sched_clock();
+ kasan_unpoison_task_stack(idle);
+
#ifdef CONFIG_SMP
/*
* Its possible that init_idle() gets called multiple times on a task,
#include <linux/sched/sysctl.h>
#include <linux/kexec.h>
#include <linux/bpf.h>
+#include <linux/mount.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
.mode = 0644,
.proc_handler = proc_doulongvec_minmax,
},
+ {
+ .procname = "mount-max",
+ .data = &sysctl_mount_max,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &one,
+ },
{ }
};
ftrace_probe_registered = 1;
}
-static void __disable_ftrace_function_probe(void)
+static bool __disable_ftrace_function_probe(void)
{
int i;
if (!ftrace_probe_registered)
- return;
+ return false;
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
struct hlist_head *hhd = &ftrace_func_hash[i];
if (hhd->first)
- return;
+ return false;
}
/* no more funcs left */
ftrace_shutdown(&trace_probe_ops, 0);
ftrace_probe_registered = 0;
+ return true;
}
__unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
void *data, int flags)
{
+ struct ftrace_ops_hash old_hash_ops;
struct ftrace_func_entry *rec_entry;
struct ftrace_func_probe *entry;
struct ftrace_func_probe *p;
struct hlist_node *tmp;
char str[KSYM_SYMBOL_LEN];
int i, ret;
+ bool disabled;
if (glob && (strcmp(glob, "*") == 0 || !strlen(glob)))
func_g.search = NULL;
mutex_lock(&trace_probe_ops.func_hash->regex_lock);
+ old_hash_ops.filter_hash = old_hash;
+ /* Probes only have filters */
+ old_hash_ops.notrace_hash = NULL;
+
hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash);
if (!hash)
/* Hmm, should report this somehow */
}
}
mutex_lock(&ftrace_lock);
- __disable_ftrace_function_probe();
+ disabled = __disable_ftrace_function_probe();
/*
* Remove after the disable is called. Otherwise, if the last
* probe is removed, a null hash means *all enabled*.
*/
ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash);
+
+ /* still need to update the function call sites */
+ if (ftrace_enabled && !disabled)
+ ftrace_run_modify_code(&trace_probe_ops, FTRACE_UPDATE_CALLS,
+ &old_hash_ops);
synchronize_sched();
if (!ret)
free_ftrace_hash_rcu(old_hash);
int ring_buffer_iter_empty(struct ring_buffer_iter *iter)
{
struct ring_buffer_per_cpu *cpu_buffer;
+ struct buffer_page *reader;
+ struct buffer_page *head_page;
+ struct buffer_page *commit_page;
+ unsigned commit;
cpu_buffer = iter->cpu_buffer;
- return iter->head_page == cpu_buffer->commit_page &&
- iter->head == rb_commit_index(cpu_buffer);
+ /* Remember, trace recording is off when iterator is in use */
+ reader = cpu_buffer->reader_page;
+ head_page = cpu_buffer->head_page;
+ commit_page = cpu_buffer->commit_page;
+ commit = rb_page_commit(commit_page);
+
+ return ((iter->head_page == commit_page && iter->head == commit) ||
+ (iter->head_page == reader && commit_page == head_page &&
+ head_page->read == commit &&
+ iter->head == rb_page_commit(cpu_buffer->reader_page)));
}
EXPORT_SYMBOL_GPL(ring_buffer_iter_empty);
rb_data[cpu].cnt = cpu;
rb_threads[cpu] = kthread_create(rb_test, &rb_data[cpu],
"rbtester/%d", cpu);
- if (WARN_ON(!rb_threads[cpu])) {
+ if (WARN_ON(IS_ERR(rb_threads[cpu]))) {
pr_cont("FAILED\n");
- ret = -1;
+ ret = PTR_ERR(rb_threads[cpu]);
goto out_free;
}
/* Now create the rb hammer! */
rb_hammer = kthread_run(rb_hammer_test, NULL, "rbhammer");
- if (WARN_ON(!rb_hammer)) {
+ if (WARN_ON(IS_ERR(rb_hammer))) {
pr_cont("FAILED\n");
- ret = -1;
+ ret = PTR_ERR(rb_hammer);
goto out_free;
}
return ret;
out_reg:
- ret = register_ftrace_function_probe(glob, ops, count);
+ ret = alloc_snapshot(&global_trace);
+ if (ret < 0)
+ goto out;
- if (ret >= 0)
- alloc_snapshot(&global_trace);
+ ret = register_ftrace_function_probe(glob, ops, count);
+ out:
return ret < 0 ? ret : 0;
}
{
struct page *page = NULL;
spinlock_t *ptl;
+ pte_t pte;
retry:
ptl = pmd_lockptr(mm, pmd);
spin_lock(ptl);
*/
if (!pmd_huge(*pmd))
goto out;
- if (pmd_present(*pmd)) {
+ pte = huge_ptep_get((pte_t *)pmd);
+ if (pte_present(pte)) {
page = pmd_page(*pmd) + ((address & ~PMD_MASK) >> PAGE_SHIFT);
if (flags & FOLL_GET)
get_page(page);
} else {
- if (is_hugetlb_entry_migration(huge_ptep_get((pte_t *)pmd))) {
+ if (is_hugetlb_entry_migration(pte)) {
spin_unlock(ptl);
__migration_entry_wait(mm, (pte_t *)pmd, ptl);
goto retry;
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kmemleak.h>
+#include <linux/linkage.h>
#include <linux/memblock.h>
#include <linux/memory.h>
#include <linux/mm.h>
}
}
+static void __kasan_unpoison_stack(struct task_struct *task, void *sp)
+{
+ void *base = task_stack_page(task);
+ size_t size = sp - base;
+
+ kasan_unpoison_shadow(base, size);
+}
+
+/* Unpoison the entire stack for a task. */
+void kasan_unpoison_task_stack(struct task_struct *task)
+{
+ __kasan_unpoison_stack(task, task_stack_page(task) + THREAD_SIZE);
+}
+
+/* Unpoison the stack for the current task beyond a watermark sp value. */
+asmlinkage void kasan_unpoison_remaining_stack(void *sp)
+{
+ __kasan_unpoison_stack(current, sp);
+}
/*
* All functions below always inlined so compiler could
COMPAT_SYSCALL_DEFINE3(set_mempolicy, int, mode, compat_ulong_t __user *, nmask,
compat_ulong_t, maxnode)
{
- long err = 0;
unsigned long __user *nm = NULL;
unsigned long nr_bits, alloc_size;
DECLARE_BITMAP(bm, MAX_NUMNODES);
alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
if (nmask) {
- err = compat_get_bitmap(bm, nmask, nr_bits);
+ if (compat_get_bitmap(bm, nmask, nr_bits))
+ return -EFAULT;
nm = compat_alloc_user_space(alloc_size);
- err |= copy_to_user(nm, bm, alloc_size);
+ if (copy_to_user(nm, bm, alloc_size))
+ return -EFAULT;
}
- if (err)
- return -EFAULT;
-
return sys_set_mempolicy(mode, nm, nr_bits+1);
}
compat_ulong_t, mode, compat_ulong_t __user *, nmask,
compat_ulong_t, maxnode, compat_ulong_t, flags)
{
- long err = 0;
unsigned long __user *nm = NULL;
unsigned long nr_bits, alloc_size;
nodemask_t bm;
alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
if (nmask) {
- err = compat_get_bitmap(nodes_addr(bm), nmask, nr_bits);
+ if (compat_get_bitmap(nodes_addr(bm), nmask, nr_bits))
+ return -EFAULT;
nm = compat_alloc_user_space(alloc_size);
- err |= copy_to_user(nm, nodes_addr(bm), alloc_size);
+ if (copy_to_user(nm, nodes_addr(bm), alloc_size))
+ return -EFAULT;
}
- if (err)
- return -EFAULT;
-
return sys_mbind(start, len, mode, nm, nr_bits+1, flags);
}
#include <asm/tlb.h>
#include <asm/mmu_context.h>
+#ifdef CONFIG_MSM_APP_SETTINGS
+#include <asm/app_api.h>
+#endif
+
#include "internal.h"
#ifndef arch_mmap_check
if (!len)
return -EINVAL;
+#ifdef CONFIG_MSM_APP_SETTINGS
+ if (use_app_setting)
+ apply_app_setting_bit(file);
+#endif
+
/*
* Does the application expect PROT_READ to imply PROT_EXEC?
*
#include <linux/kthread.h>
#include <linux/net.h>
#include <linux/nsproxy.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/socket.h>
#include <linux/string.h>
{
struct sockaddr_storage *paddr = &con->peer_addr.in_addr;
struct socket *sock;
+ unsigned int noio_flag;
int ret;
BUG_ON(con->sock);
+
+ /* sock_create_kern() allocates with GFP_KERNEL */
+ noio_flag = memalloc_noio_save();
ret = sock_create_kern(read_pnet(&con->msgr->net), paddr->ss_family,
SOCK_STREAM, IPPROTO_TCP, &sock);
+ memalloc_noio_restore(noio_flag);
if (ret)
return ret;
sock->sk->sk_allocation = GFP_NOFS;
void ping_unhash(struct sock *sk)
{
struct inet_sock *isk = inet_sk(sk);
+
pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
+ write_lock_bh(&ping_table.lock);
if (sk_hashed(sk)) {
- write_lock_bh(&ping_table.lock);
hlist_nulls_del(&sk->sk_nulls_node);
sk_nulls_node_init(&sk->sk_nulls_node);
sock_put(sk);
isk->inet_num = 0;
isk->inet_sport = 0;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
- write_unlock_bh(&ping_table.lock);
}
+ write_unlock_bh(&ping_table.lock);
}
EXPORT_SYMBOL_GPL(ping_unhash);
struct rtable *rt;
struct flowi4 fl4;
const struct iphdr *iph = (const struct iphdr *) skb->data;
+ struct net *net = dev_net(skb->dev);
int oif = skb->dev->ifindex;
u8 tos = RT_TOS(iph->tos);
u8 prot = iph->protocol;
rt = (struct rtable *) dst;
- __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(net, &fl4, sk, iph, oif, tos, prot, mark, 0);
__ip_do_redirect(rt, skb, &fl4, true);
}
continue;
if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
continue;
+ if (cfg->fc_protocol && cfg->fc_protocol != rt->rt6i_protocol)
+ continue;
dst_hold(&rt->dst);
read_unlock_bh(&table->tb6_lock);
!ether_addr_equal(bssid, hdr->addr1))
return false;
}
+
+ /*
+ * 802.11-2016 Table 9-26 says that for data frames, A1 must be
+ * the BSSID - we've checked that already but may have accepted
+ * the wildcard (ff:ff:ff:ff:ff:ff).
+ *
+ * It also says:
+ * The BSSID of the Data frame is determined as follows:
+ * a) If the STA is contained within an AP or is associated
+ * with an AP, the BSSID is the address currently in use
+ * by the STA contained in the AP.
+ *
+ * So we should not accept data frames with an address that's
+ * multicast.
+ *
+ * Accepting it also opens a security problem because stations
+ * could encrypt it with the GTK and inject traffic that way.
+ */
+ if (ieee80211_is_data(hdr->frame_control) && multicast)
+ return false;
+
return true;
case NL80211_IFTYPE_WDS:
if (bssid || !ieee80211_is_data(hdr->frame_control))
if (unlikely(!PAGE_ALIGNED(req->tp_block_size)))
goto out;
if (po->tp_version >= TPACKET_V3 &&
- (int)(req->tp_block_size -
- BLK_PLUS_PRIV(req_u->req3.tp_sizeof_priv)) <= 0)
+ req->tp_block_size <=
+ BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv))
goto out;
if (unlikely(req->tp_frame_size < po->tp_hdrlen +
po->tp_reserve))
if (!asoc)
return -EINVAL;
+ /* If there is a thread waiting on more sndbuf space for
+ * sending on this asoc, it cannot be peeled.
+ */
+ if (waitqueue_active(&asoc->wait))
+ return -EBUSY;
+
/* An association cannot be branched off from an already peeled-off
* socket, nor is this supported for tcp style sockets.
*/
*/
release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
- if (sk != asoc->base.sk)
- goto do_error;
lock_sock(sk);
*timeo_p = current_timeo;
return used;
}
-int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
+static int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
{
int err = simple_setattr(dentry, iattr);
return gss_new;
gss_msg = gss_add_msg(gss_new);
if (gss_msg == gss_new) {
- int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
+ int res;
+ atomic_inc(&gss_msg->count);
+ res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
if (res) {
gss_unhash_msg(gss_new);
+ atomic_dec(&gss_msg->count);
+ gss_release_msg(gss_new);
gss_msg = ERR_PTR(res);
}
} else
warn_gssd();
gss_release_msg(gss_msg);
}
+ gss_release_msg(gss_msg);
}
static void gss_pipe_dentry_destroy(struct dentry *dir,
dev = dev_get_by_name(net, driver_name);
if (!dev)
return -ENODEV;
+ if (tipc_mtu_bad(dev, 0)) {
+ dev_put(dev);
+ return -EINVAL;
+ }
/* Associate TIPC bearer with L2 bearer */
rcu_assign_pointer(b->media_ptr, dev);
if (!b_ptr)
return NOTIFY_DONE;
- b_ptr->mtu = dev->mtu;
-
switch (evt) {
case NETDEV_CHANGE:
if (netif_carrier_ok(dev))
break;
case NETDEV_GOING_DOWN:
+ tipc_reset_bearer(net, b_ptr);
+ break;
case NETDEV_CHANGEMTU:
+ if (tipc_mtu_bad(dev, 0)) {
+ bearer_disable(net, b_ptr);
+ break;
+ }
+ b_ptr->mtu = dev->mtu;
tipc_reset_bearer(net, b_ptr);
break;
case NETDEV_CHANGEADDR:
#include "netlink.h"
#include "core.h"
+#include "msg.h"
#include <net/genetlink.h>
#define MAX_MEDIA 3
#define TIPC_MEDIA_TYPE_IB 2
#define TIPC_MEDIA_TYPE_UDP 3
+/* minimum bearer MTU */
+#define TIPC_MIN_BEARER_MTU (MAX_H_SIZE + INT_H_SIZE)
+
/**
* struct tipc_node_map - set of node identifiers
* @count: # of nodes in set
void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
struct sk_buff_head *xmitq);
+/* check if device MTU is too low for tipc headers */
+static inline bool tipc_mtu_bad(struct net_device *dev, unsigned int reserve)
+{
+ if (dev->mtu >= TIPC_MIN_BEARER_MTU + reserve)
+ return false;
+ netdev_warn(dev, "MTU too low for tipc bearer\n");
+ return true;
+}
+
#endif /* _TIPC_BEARER_H */
if (err)
goto out_nametbl;
+ INIT_LIST_HEAD(&tn->dist_queue);
err = tipc_topsrv_start(net);
if (err)
goto out_subscr;
spinlock_t nametbl_lock;
struct name_table *nametbl;
+ /* Name dist queue */
+ struct list_head dist_queue;
+
/* Topology subscription server */
struct tipc_server *topsrv;
atomic_t subscription_count;
int sysctl_tipc_named_timeout __read_mostly = 2000;
-/**
- * struct tipc_dist_queue - queue holding deferred name table updates
- */
-static struct list_head tipc_dist_queue = LIST_HEAD_INIT(tipc_dist_queue);
-
struct distr_queue_item {
struct distr_item i;
u32 dtype;
/**
* named_prepare_buf - allocate & initialize a publication message
+ *
+ * The buffer returned is of size INT_H_SIZE + payload size
*/
static struct sk_buff *named_prepare_buf(struct net *net, u32 type, u32 size,
u32 dest)
struct publication *publ;
struct sk_buff *skb = NULL;
struct distr_item *item = NULL;
- uint msg_dsz = (tipc_node_get_mtu(net, dnode, 0) / ITEM_SIZE) *
- ITEM_SIZE;
- uint msg_rem = msg_dsz;
+ u32 msg_dsz = ((tipc_node_get_mtu(net, dnode, 0) - INT_H_SIZE) /
+ ITEM_SIZE) * ITEM_SIZE;
+ u32 msg_rem = msg_dsz;
list_for_each_entry(publ, pls, local_list) {
/* Prepare next buffer: */
* tipc_named_add_backlog - add a failed name table update to the backlog
*
*/
-static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node)
+static void tipc_named_add_backlog(struct net *net, struct distr_item *i,
+ u32 type, u32 node)
{
struct distr_queue_item *e;
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
unsigned long now = get_jiffies_64();
e = kzalloc(sizeof(*e), GFP_ATOMIC);
e->node = node;
e->expires = now + msecs_to_jiffies(sysctl_tipc_named_timeout);
memcpy(e, i, sizeof(*i));
- list_add_tail(&e->next, &tipc_dist_queue);
+ list_add_tail(&e->next, &tn->dist_queue);
}
/**
void tipc_named_process_backlog(struct net *net)
{
struct distr_queue_item *e, *tmp;
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
char addr[16];
unsigned long now = get_jiffies_64();
- list_for_each_entry_safe(e, tmp, &tipc_dist_queue, next) {
+ list_for_each_entry_safe(e, tmp, &tn->dist_queue, next) {
if (time_after(e->expires, now)) {
if (!tipc_update_nametbl(net, &e->i, e->node, e->dtype))
continue;
node = msg_orignode(msg);
while (count--) {
if (!tipc_update_nametbl(net, item, node, mtype))
- tipc_named_add_backlog(item, mtype, node);
+ tipc_named_add_backlog(net, item, mtype, node);
item++;
}
kfree_skb(skb);
static void tipc_node_kref_release(struct kref *kref)
{
- struct tipc_node *node = container_of(kref, struct tipc_node, kref);
+ struct tipc_node *n = container_of(kref, struct tipc_node, kref);
- tipc_node_delete(node);
+ kfree(n->bc_entry.link);
+ kfree_rcu(n, rcu);
}
void tipc_node_put(struct tipc_node *node)
{
list_del_rcu(&node->list);
hlist_del_rcu(&node->hash);
- kfree(node->bc_entry.link);
- kfree_rcu(node, rcu);
+ tipc_node_put(node);
+
+ del_timer_sync(&node->timer);
+ tipc_node_put(node);
}
void tipc_node_stop(struct net *net)
{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct tipc_net *tn = tipc_net(net);
struct tipc_node *node, *t_node;
spin_lock_bh(&tn->node_list_lock);
- list_for_each_entry_safe(node, t_node, &tn->node_list, list) {
- if (del_timer(&node->timer))
- tipc_node_put(node);
- tipc_node_put(node);
- }
+ list_for_each_entry_safe(node, t_node, &tn->node_list, list)
+ tipc_node_delete(node);
spin_unlock_bh(&tn->node_list_lock);
}
if (rc & TIPC_LINK_DOWN_EVT)
tipc_node_link_down(n, bearer_id, false);
}
- if (!mod_timer(&n->timer, jiffies + n->keepalive_intv))
- tipc_node_get(n);
- tipc_node_put(n);
+ mod_timer(&n->timer, jiffies + n->keepalive_intv);
}
/**
state = SELF_UP_PEER_UP;
break;
case SELF_LOST_CONTACT_EVT:
- state = SELF_DOWN_PEER_LEAVING;
+ state = SELF_DOWN_PEER_DOWN;
break;
case SELF_ESTABL_CONTACT_EVT:
case PEER_LOST_CONTACT_EVT:
state = SELF_UP_PEER_UP;
break;
case PEER_LOST_CONTACT_EVT:
- state = SELF_LEAVING_PEER_DOWN;
+ state = SELF_DOWN_PEER_DOWN;
break;
case SELF_LOST_CONTACT_EVT:
case PEER_ESTABL_CONTACT_EVT:
* @tsk: receiving socket
* @skb: pointer to message buffer.
*/
-static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb)
+static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb,
+ struct sk_buff_head *xmitq)
{
struct sock *sk = &tsk->sk;
+ u32 onode = tsk_own_node(tsk);
struct tipc_msg *hdr = buf_msg(skb);
int mtyp = msg_type(hdr);
int conn_cong;
if (mtyp == CONN_PROBE) {
msg_set_type(hdr, CONN_PROBE_REPLY);
- tipc_sk_respond(sk, skb, TIPC_OK);
+ if (tipc_msg_reverse(onode, &skb, TIPC_OK))
+ __skb_queue_tail(xmitq, skb);
return;
} else if (mtyp == CONN_ACK) {
conn_cong = tsk_conn_cong(tsk);
*
* Returns true if message was added to socket receive queue, otherwise false
*/
-static bool filter_rcv(struct sock *sk, struct sk_buff *skb)
+static bool filter_rcv(struct sock *sk, struct sk_buff *skb,
+ struct sk_buff_head *xmitq)
{
struct socket *sock = sk->sk_socket;
struct tipc_sock *tsk = tipc_sk(sk);
int usr = msg_user(hdr);
if (unlikely(msg_user(hdr) == CONN_MANAGER)) {
- tipc_sk_proto_rcv(tsk, skb);
+ tipc_sk_proto_rcv(tsk, skb, xmitq);
return false;
}
return true;
reject:
- tipc_sk_respond(sk, skb, err);
+ if (tipc_msg_reverse(tsk_own_node(tsk), &skb, err))
+ __skb_queue_tail(xmitq, skb);
return false;
}
static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
unsigned int truesize = skb->truesize;
+ struct sk_buff_head xmitq;
+ u32 dnode, selector;
- if (likely(filter_rcv(sk, skb)))
+ __skb_queue_head_init(&xmitq);
+
+ if (likely(filter_rcv(sk, skb, &xmitq))) {
atomic_add(truesize, &tipc_sk(sk)->dupl_rcvcnt);
+ return 0;
+ }
+
+ if (skb_queue_empty(&xmitq))
+ return 0;
+
+ /* Send response/rejected message */
+ skb = __skb_dequeue(&xmitq);
+ dnode = msg_destnode(buf_msg(skb));
+ selector = msg_origport(buf_msg(skb));
+ tipc_node_xmit_skb(sock_net(sk), skb, dnode, selector);
return 0;
}
* Caller must hold socket lock
*/
static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
- u32 dport)
+ u32 dport, struct sk_buff_head *xmitq)
{
+ unsigned long time_limit = jiffies + 2;
+ struct sk_buff *skb;
unsigned int lim;
atomic_t *dcnt;
- struct sk_buff *skb;
- unsigned long time_limit = jiffies + 2;
+ u32 onode;
while (skb_queue_len(inputq)) {
if (unlikely(time_after_eq(jiffies, time_limit)))
/* Add message directly to receive queue if possible */
if (!sock_owned_by_user(sk)) {
- filter_rcv(sk, skb);
+ filter_rcv(sk, skb, xmitq);
continue;
}
/* Try backlog, compensating for double-counted bytes */
dcnt = &tipc_sk(sk)->dupl_rcvcnt;
- if (sk->sk_backlog.len)
+ if (!sk->sk_backlog.len)
atomic_set(dcnt, 0);
lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt);
if (likely(!sk_add_backlog(sk, skb, lim)))
continue;
/* Overload => reject message back to sender */
- tipc_sk_respond(sk, skb, TIPC_ERR_OVERLOAD);
+ onode = tipc_own_addr(sock_net(sk));
+ if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD))
+ __skb_queue_tail(xmitq, skb);
break;
}
}
*/
void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq)
{
+ struct sk_buff_head xmitq;
u32 dnode, dport = 0;
int err;
struct tipc_sock *tsk;
struct sock *sk;
struct sk_buff *skb;
+ __skb_queue_head_init(&xmitq);
while (skb_queue_len(inputq)) {
dport = tipc_skb_peek_port(inputq, dport);
tsk = tipc_sk_lookup(net, dport);
if (likely(tsk)) {
sk = &tsk->sk;
if (likely(spin_trylock_bh(&sk->sk_lock.slock))) {
- tipc_sk_enqueue(inputq, sk, dport);
+ tipc_sk_enqueue(inputq, sk, dport, &xmitq);
spin_unlock_bh(&sk->sk_lock.slock);
}
+ /* Send pending response/rejected messages, if any */
+ while ((skb = __skb_dequeue(&xmitq))) {
+ dnode = msg_destnode(buf_msg(skb));
+ tipc_node_xmit_skb(net, skb, dnode, dport);
+ }
sock_put(sk);
continue;
}
/* IANA assigned UDP port */
#define UDP_PORT_DEFAULT 6118
-#define UDP_MIN_HEADROOM 28
+#define UDP_MIN_HEADROOM 48
static const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = {
[TIPC_NLA_UDP_UNSPEC] = {.type = NLA_UNSPEC},
udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
udp_conf.use_udp_checksums = false;
ub->ifindex = dev->ifindex;
+ if (tipc_mtu_bad(dev, sizeof(struct iphdr) +
+ sizeof(struct udphdr))) {
+ err = -EINVAL;
+ goto err;
+ }
b->mtu = dev->mtu - sizeof(struct iphdr)
- sizeof(struct udphdr);
#if IS_ENABLED(CONFIG_IPV6)
* qp_handle.
*/
if (vmci_handle_is_invalid(e_payload->handle) ||
- vmci_handle_is_equal(trans->qp_handle, e_payload->handle))
+ !vmci_handle_is_equal(trans->qp_handle, e_payload->handle))
return;
/* We don't ask for delayed CBs when we subscribe to this event (we
MODULE_AUTHOR("VMware, Inc.");
MODULE_DESCRIPTION("VMCI transport for Virtual Sockets");
-MODULE_VERSION("1.0.2.0-k");
+MODULE_VERSION("1.0.3.0-k");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("vmware_vsock");
MODULE_ALIAS_NETPROTO(PF_VSOCK);
enum cfg80211_event_type type;
union {
- struct {
- u8 bssid[ETH_ALEN];
- const u8 *req_ie;
- const u8 *resp_ie;
- size_t req_ie_len;
- size_t resp_ie_len;
- struct cfg80211_bss *bss;
- int status; /* -1 = failed; 0..65535 = status code */
- enum nl80211_timeout_reason timeout_reason;
- } cr;
+ struct cfg80211_connect_resp_params cr;
struct {
const u8 *req_ie;
const u8 *resp_ie;
struct cfg80211_connect_params *connect,
struct cfg80211_cached_keys *connkeys,
const u8 *prev_bssid);
-void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len,
- int status, bool wextev,
- struct cfg80211_bss *bss,
- enum nl80211_timeout_reason timeout_reason);
+void __cfg80211_connect_result(struct net_device *dev,
+ struct cfg80211_connect_resp_params *params,
+ bool wextev);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap);
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
(5490 - 5590 @ 80), (36)
(5650 - 5730 @ 80), (36)
(5735 - 5835 @ 80), (36)
+ # 60 gHz band channels 1-3
+ (57240 - 63720 @ 2160), (40), NO-OUTDOOR
country AS: DFS-FCC
(2402 - 2472 @ 40), (30)
(5650 - 5730 @ 80), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4
+ (57240 - 65880 @ 2160), (43), NO-OUTDOOR
+
country AW: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (40), NO-OUTDOOR
country BB: DFS-FCC
(2402 - 2482 @ 40), (20)
(5900 - 5920 @ 10), (30)
(5910 - 5930 @ 10), (30)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+ (57240 - 65880 @ 2160), (40)
country BF: DFS-FCC
(2402 - 2482 @ 40), (20)
(5250 - 5330 @ 80), (30), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country BR: DFS-FCC
(2402 - 2482 @ 40), (30)
(5170 - 5250 @ 80), (24), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3
+ (57240 - 63720 @ 2160), (40)
+
country BS: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
(5650 - 5730 @ 80), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3
+ (57240 - 63720 @ 2160), (40)
+
country CF: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 40), (24)
(5900 - 5920 @ 10), (30)
(5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country CI: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
(5170 - 5330 @ 160), (20)
(5735 - 5835 @ 80), (20)
+ # 60 gHz band channels 1-3
+ (57240 - 63720 @ 2160), (50), NO-OUTDOOR
+
country CN: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5735 - 5835 @ 80), (33)
- # 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm
- # ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
- (57240 - 59400 @ 2160), (28)
+ # 60 gHz band channels 2,3: 44dBm
(59400 - 63720 @ 2160), (44)
- (63720 - 65880 @ 2160), (28)
country CO: DFS-FCC
(2402 - 2482 @ 40), (20)
(5490 - 5730 @ 20), (24), DFS
(5735 - 5835 @ 20), (30)
+ # 60 gHz band channels 1-3
+ (57240 - 63720 @ 2160), (30)
+
country CX: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
(5490 - 5730 @ 20), (24), DFS
(5735 - 5835 @ 20), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country EE: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (18), AUTO-BW
(5250 - 5330 @ 80), (18), DFS, AUTO-BW
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (40), NO-OUTDOOR
country GF: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country GY:
(2402 - 2482 @ 40), (30)
(5735 - 5835 @ 80), (30)
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4, ref: FCC/EU
+ (57240 - 65880 @ 2160), (40)
+
country HN:
(2402 - 2482 @ 40), (20)
(5170 - 5330 @ 160), (24)
(5490 - 5730 @ 160), (24)
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country HR: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
# 5.9ghz band
# reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
(5850 - 5870 @ 10), (30)
(5890 - 5910 @ 10), (30)
(5900 - 5920 @ 10), (30)
(5910 - 5930 @ 10), (30)
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
(57240 - 65880 @ 2160), (40), NO-OUTDOOR
country IL: DFS-ETSI
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ # 60 gHz band channels 1-4, base on Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country IN:
(2402 - 2482 @ 40), (20)
(5170 - 5330 @ 160), (23)
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country JO:
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23)
(5735 - 5835 @ 80), (23)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country JP: DFS-JP
(2402 - 2482 @ 40), (20)
(2474 - 2494 @ 20), (20), NO-OFDM
(5170 - 5250 @ 80), (20), AUTO-BW, NO-OUTDOOR
(5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR
(5490 - 5710 @ 160), (20), DFS
- # 60 GHz band channels 2-4 at 10mW,
- # ref: http://www.arib.or.jp/english/html/overview/doc/1-STD-T74v1_1.pdf
- (59000 - 66000 @ 2160), (10 mW)
+ # 60 gHz band channels 1-4
+ (57240 - 65880 @ 2160), (40)
country KE: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5735 - 5835 @ 80), (30)
# 60 GHz band channels 1-4,
# ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
- (57000 - 66000 @ 2160), (43)
+ (57240 - 65880 @ 2160), (43)
country KP: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5900 - 5920 @ 10), (30)
(5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country LK: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 20), (24)
(5890 - 5910 @ 10), (30)
(5900 - 5920 @ 10), (30)
(5910 - 5930 @ 10), (30)
+
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57240 - 65880 @ 2160), (40), NO-OUTDOOR
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country MC: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (40), NO-OUTDOOR
country MN: DFS-FCC
(2402 - 2482 @ 40), (20)
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country MY: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
(5490 - 5650 @ 160), (24), DFS
(5735 - 5815 @ 80), (24)
+ # 60 gHz band channels 1-3
+ (57240 - 63720 @ 2160), (40)
+
country NA: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country NL: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country OM: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country PK:
(2402 - 2482 @ 40), (30)
(5735 - 5835 @ 80), (30)
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country QA:
(2402 - 2482 @ 40), (20)
(5735 - 5835 @ 80), (30)
(5490 - 5730 @ 160), (30)
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4
+ (57240 - 65880 @ 2160), (40)
+
country RW: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country SI: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4
+ (57240 - 65880 @ 2160), (40)
+
country TN: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (40), NO-OUTDOOR
country TT:
(2402 - 2482 @ 40), (20)
(5490 - 5730 @ 160), (36)
(5735 - 5835 @ 80), (36)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country TW: DFS-FCC
(2402 - 2472 @ 40), (30)
(5170 - 5250 @ 80), (24), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-3, FCC
+ (57240 - 63720 @ 2160), (40)
+
country TZ:
(2402 - 2482 @ 40), (20)
(5735 - 5835 @ 80), (30)
(5490 - 5670 @ 160), (20), DFS
(5735 - 5835 @ 80), (20)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+ (57240 - 65880 @ 2160), (20)
country UG: DFS-FCC
(2402 - 2482 @ 40), (20)
(5910 - 5930 @ 10), (30)
# 60g band
# reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
- # channels 1,2,3, EIRP=40dBm(43dBm peak)
- (57240 - 63720 @ 2160), (40)
+ # channels 1,2,3,4,5,6 EIRP=40dBm(43dBm peak)
+ (57240 - 70200 @ 2160), (40)
country UY: DFS-FCC
(2402 - 2482 @ 40), (20)
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4
+ (57240 - 65880 @ 2160), (40)
+
country UZ: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4
+ (57240 - 65880 @ 2160), (40)
+
country VU: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (24), AUTO-BW
(5170 - 5250 @ 80), (20), NO-IR, AUTO-BW, NO-OUTDOOR
(5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR
(5490 - 5710 @ 160), (20), DFS
- (59000 - 66000 @ 2160), (10 mW)
country YE:
(2402 - 2482 @ 40), (20)
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
+ # 60 gHz band channels 1-4
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
country ZW: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
- u8 *ie = mgmt->u.assoc_resp.variable;
- int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
- u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+ struct cfg80211_connect_resp_params cr;
+
+ memset(&cr, 0, sizeof(cr));
+ cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code);
+ cr.bssid = mgmt->bssid;
+ cr.bss = bss;
+ cr.resp_ie = mgmt->u.assoc_resp.variable;
+ cr.resp_ie_len =
+ len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+ cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED;
trace_cfg80211_send_rx_assoc(dev, bss);
* and got a reject -- we only try again with an assoc
* frame instead of reassoc.
*/
- if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
+ if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) {
cfg80211_unhold_bss(bss_from_pub(bss));
cfg80211_put_bss(wiphy, bss);
return;
nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues);
/* update current_bss etc., consumes the bss reference */
- __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
- status_code,
- status_code == WLAN_STATUS_SUCCESS, bss,
- NL80211_TIMEOUT_UNSPECIFIED);
+ __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS);
}
EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
.len = sizeof(struct nl80211_bss_select_rssi_adjust)
},
+ [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY,
+ .len = FILS_MAX_KEK_LEN },
+ [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN },
[NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 },
+ [NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY,
+ .len = FILS_ERP_MAX_USERNAME_LEN },
+ [NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY,
+ .len = FILS_ERP_MAX_REALM_LEN },
+ [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 },
+ [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY,
+ .len = FILS_ERP_MAX_RRK_LEN },
+ [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 },
+ [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
};
/* policy for the key attributes */
return false;
return true;
case NL80211_CMD_CONNECT:
+ /* SAE not supported yet */
+ if (auth_type == NL80211_AUTHTYPE_SAE)
+ return false;
+ /* FILS with SK PFS or PK not supported yet */
+ if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS ||
+ auth_type == NL80211_AUTHTYPE_FILS_PK)
+ return false;
+ if (!wiphy_ext_feature_isset(
+ &rdev->wiphy,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) &&
+ auth_type == NL80211_AUTHTYPE_FILS_SK)
+ return false;
+ return true;
case NL80211_CMD_START_AP:
/* SAE not supported yet */
if (auth_type == NL80211_AUTHTYPE_SAE)
req.flags |= ASSOC_REQ_USE_RRM;
}
+ if (info->attrs[NL80211_ATTR_FILS_KEK]) {
+ req.fils_kek = nla_data(info->attrs[NL80211_ATTR_FILS_KEK]);
+ req.fils_kek_len = nla_len(info->attrs[NL80211_ATTR_FILS_KEK]);
+ if (!info->attrs[NL80211_ATTR_FILS_NONCES])
+ return -EINVAL;
+ req.fils_nonces =
+ nla_data(info->attrs[NL80211_ATTR_FILS_NONCES]);
+ }
+
err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
if (!err) {
wdev_lock(dev->ieee80211_ptr);
}
}
+ if (wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) &&
+ info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] &&
+ info->attrs[NL80211_ATTR_FILS_ERP_REALM] &&
+ info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] &&
+ info->attrs[NL80211_ATTR_FILS_ERP_RRK]) {
+ connect.fils_erp_username =
+ nla_data(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]);
+ connect.fils_erp_username_len =
+ nla_len(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]);
+ connect.fils_erp_realm =
+ nla_data(info->attrs[NL80211_ATTR_FILS_ERP_REALM]);
+ connect.fils_erp_realm_len =
+ nla_len(info->attrs[NL80211_ATTR_FILS_ERP_REALM]);
+ connect.fils_erp_next_seq_num =
+ nla_get_u16(
+ info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]);
+ connect.fils_erp_rrk =
+ nla_data(info->attrs[NL80211_ATTR_FILS_ERP_RRK]);
+ connect.fils_erp_rrk_len =
+ nla_len(info->attrs[NL80211_ATTR_FILS_ERP_RRK]);
+ } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] ||
+ info->attrs[NL80211_ATTR_FILS_ERP_REALM] ||
+ info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] ||
+ info->attrs[NL80211_ATTR_FILS_ERP_RRK]) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+
wdev_lock(dev->ieee80211_ptr);
err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
wdev_unlock(dev->ieee80211_ptr);
memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
- if (!info->attrs[NL80211_ATTR_MAC])
- return -EINVAL;
-
if (!info->attrs[NL80211_ATTR_PMKID])
return -EINVAL;
pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
- pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (info->attrs[NL80211_ATTR_MAC]) {
+ pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ } else if (info->attrs[NL80211_ATTR_SSID] &&
+ info->attrs[NL80211_ATTR_FILS_CACHE_ID] &&
+ (info->genlhdr->cmd == NL80211_CMD_DEL_PMKSA ||
+ info->attrs[NL80211_ATTR_PMK])) {
+ pmksa.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ pmksa.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+ pmksa.cache_id =
+ nla_data(info->attrs[NL80211_ATTR_FILS_CACHE_ID]);
+ } else {
+ return -EINVAL;
+ }
+ if (info->attrs[NL80211_ATTR_PMK]) {
+ pmksa.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]);
+ pmksa.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]);
+ }
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
struct sk_buff *msg;
void *hdr;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ msg = nlmsg_new(100 + len, gfp);
if (!msg)
return;
}
void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len,
- int status,
- enum nl80211_timeout_reason timeout_reason,
+ struct net_device *netdev,
+ struct cfg80211_connect_resp_params *cr,
gfp_t gfp)
{
struct sk_buff *msg;
void *hdr;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len +
+ cr->fils_kek_len + cr->pmk_len +
+ (cr->pmkid ? WLAN_PMKID_LEN : 0), gfp);
if (!msg)
return;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
- (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
+ (cr->bssid &&
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE,
- status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE :
- status) ||
- (status < 0 &&
+ cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE :
+ cr->status) ||
+ (cr->status < 0 &&
(nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
- nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, timeout_reason))) ||
- (req_ie &&
- nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
- (resp_ie &&
- nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
+ nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON,
+ cr->timeout_reason))) ||
+ (cr->req_ie &&
+ nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) ||
+ (cr->resp_ie &&
+ nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len,
+ cr->resp_ie)) ||
+ (cr->update_erp_next_seq_num &&
+ nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ cr->fils_erp_next_seq_num)) ||
+ (cr->status == WLAN_STATUS_SUCCESS &&
+ ((cr->fils_kek &&
+ nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils_kek_len,
+ cr->fils_kek)) ||
+ (cr->pmk &&
+ nla_put(msg, NL80211_ATTR_PMK, cr->pmk_len, cr->pmk)) ||
+ (cr->pmkid &&
+ nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->pmkid)))))
goto nla_put_failure;
genlmsg_end(msg, hdr);
struct sk_buff *msg;
void *hdr;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp);
if (!msg)
return;
struct sk_buff *msg;
void *hdr;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ msg = nlmsg_new(100 + ie_len, GFP_KERNEL);
if (!msg)
return;
trace_cfg80211_notify_new_peer_candidate(dev, addr);
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ msg = nlmsg_new(100 + ie_len, gfp);
if (!msg)
return;
struct sk_buff *msg;
void *hdr;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ msg = nlmsg_new(100 + len, gfp);
if (!msg)
return -ENOMEM;
trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ msg = nlmsg_new(100 + len, gfp);
if (!msg)
return;
if (!ft_event->target_ap)
return;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
if (!msg)
return;
struct net_device *netdev,
const u8 *addr, gfp_t gfp);
void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len,
- int status,
- enum nl80211_timeout_reason timeout_reason,
+ struct net_device *netdev,
+ struct cfg80211_connect_resp_params *params,
gfp_t gfp);
void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
}
treason = NL80211_TIMEOUT_UNSPECIFIED;
if (cfg80211_conn_do_work(wdev, &treason)) {
- __cfg80211_connect_result(
- wdev->netdev, bssid,
- NULL, 0, NULL, 0, -1, false, NULL,
- treason);
+ struct cfg80211_connect_resp_params cr;
+
+ memset(&cr, 0, sizeof(cr));
+ cr.status = -1;
+ cr.bssid = bssid;
+ cr.timeout_reason = treason;
+ __cfg80211_connect_result(wdev->netdev, &cr, false);
}
wdev_unlock(wdev);
}
wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
schedule_work(&rdev->conn_work);
} else if (status_code != WLAN_STATUS_SUCCESS) {
- __cfg80211_connect_result(wdev->netdev, mgmt->bssid,
- NULL, 0, NULL, 0,
- status_code, false, NULL,
- NL80211_TIMEOUT_UNSPECIFIED);
+ struct cfg80211_connect_resp_params cr;
+
+ memset(&cr, 0, sizeof(cr));
+ cr.status = status_code;
+ cr.bssid = mgmt->bssid;
+ cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED;
+ __cfg80211_connect_result(wdev->netdev, &cr, false);
} else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
schedule_work(&rdev->conn_work);
*/
/* This method must consume bss one way or another */
-void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len,
- int status, bool wextev,
- struct cfg80211_bss *bss,
- enum nl80211_timeout_reason timeout_reason)
+void __cfg80211_connect_result(struct net_device *dev,
+ struct cfg80211_connect_resp_params *cr,
+ bool wextev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
const u8 *country_ie;
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) {
- cfg80211_put_bss(wdev->wiphy, bss);
+ cfg80211_put_bss(wdev->wiphy, cr->bss);
return;
}
- nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,
- bssid, req_ie, req_ie_len,
- resp_ie, resp_ie_len,
- status, timeout_reason, GFP_KERNEL);
+ nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr,
+ GFP_KERNEL);
#ifdef CONFIG_CFG80211_WEXT
if (wextev) {
- if (req_ie && status == WLAN_STATUS_SUCCESS) {
+ if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) {
memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = req_ie_len;
- wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
+ wrqu.data.length = cr->req_ie_len;
+ wireless_send_event(dev, IWEVASSOCREQIE, &wrqu,
+ cr->req_ie);
}
- if (resp_ie && status == WLAN_STATUS_SUCCESS) {
+ if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) {
memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = resp_ie_len;
- wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
+ wrqu.data.length = cr->resp_ie_len;
+ wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu,
+ cr->resp_ie);
}
memset(&wrqu, 0, sizeof(wrqu));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- if (bssid && status == WLAN_STATUS_SUCCESS) {
- memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
- memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
+ if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) {
+ memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN);
+ memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN);
wdev->wext.prev_bssid_valid = true;
}
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
}
#endif
- if (!bss && (status == WLAN_STATUS_SUCCESS)) {
+ if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) {
WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
- bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
- wdev->ssid, wdev->ssid_len,
- wdev->conn_bss_type,
- IEEE80211_PRIVACY_ANY);
- if (bss)
- cfg80211_hold_bss(bss_from_pub(bss));
+ cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid,
+ wdev->ssid, wdev->ssid_len,
+ wdev->conn_bss_type,
+ IEEE80211_PRIVACY_ANY);
+ if (cr->bss)
+ cfg80211_hold_bss(bss_from_pub(cr->bss));
}
if (wdev->current_bss) {
wdev->current_bss = NULL;
}
- if (status != WLAN_STATUS_SUCCESS) {
+ if (cr->status != WLAN_STATUS_SUCCESS) {
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
wdev->ssid_len = 0;
- if (bss) {
- cfg80211_unhold_bss(bss_from_pub(bss));
- cfg80211_put_bss(wdev->wiphy, bss);
+ if (cr->bss) {
+ cfg80211_unhold_bss(bss_from_pub(cr->bss));
+ cfg80211_put_bss(wdev->wiphy, cr->bss);
}
cfg80211_sme_free(wdev);
return;
}
- if (WARN_ON(!bss))
+ if (WARN_ON(!cr->bss))
return;
- wdev->current_bss = bss_from_pub(bss);
+ wdev->current_bss = bss_from_pub(cr->bss);
cfg80211_upload_connect_keys(wdev);
rcu_read_lock();
- country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+ country_ie = ieee80211_bss_get_ie(cr->bss, WLAN_EID_COUNTRY);
if (!country_ie) {
rcu_read_unlock();
return;
* - country_ie + 2, the start of the country ie data, and
* - and country_ie[1] which is the IE length
*/
- regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
+ regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band,
country_ie + 2, country_ie[1]);
kfree(country_ie);
}
/* Consumes bss object one way or another */
-void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
- struct cfg80211_bss *bss, const u8 *req_ie,
- size_t req_ie_len, const u8 *resp_ie,
- size_t resp_ie_len, int status, gfp_t gfp,
- enum nl80211_timeout_reason timeout_reason)
+void cfg80211_connect_done(struct net_device *dev,
+ struct cfg80211_connect_resp_params *params,
+ gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_event *ev;
unsigned long flags;
+ u8 *next;
- if (bss) {
+ if (params->bss) {
/* Make sure the bss entry provided by the driver is valid. */
- struct cfg80211_internal_bss *ibss = bss_from_pub(bss);
+ struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss);
if (WARN_ON(list_empty(&ibss->list))) {
- cfg80211_put_bss(wdev->wiphy, bss);
+ cfg80211_put_bss(wdev->wiphy, params->bss);
return;
}
}
- ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
+ ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) +
+ params->req_ie_len + params->resp_ie_len +
+ params->fils_kek_len + params->pmk_len +
+ (params->pmkid ? WLAN_PMKID_LEN : 0), gfp);
if (!ev) {
- cfg80211_put_bss(wdev->wiphy, bss);
+ cfg80211_put_bss(wdev->wiphy, params->bss);
return;
}
ev->type = EVENT_CONNECT_RESULT;
- if (bssid)
- memcpy(ev->cr.bssid, bssid, ETH_ALEN);
- if (req_ie_len) {
- ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
- ev->cr.req_ie_len = req_ie_len;
- memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
+ next = ((u8 *)ev) + sizeof(*ev);
+ if (params->bssid) {
+ ev->cr.bssid = next;
+ memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN);
+ next += ETH_ALEN;
}
- if (resp_ie_len) {
- ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
- ev->cr.resp_ie_len = resp_ie_len;
- memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
+ if (params->req_ie_len) {
+ ev->cr.req_ie = next;
+ ev->cr.req_ie_len = params->req_ie_len;
+ memcpy((void *)ev->cr.req_ie, params->req_ie,
+ params->req_ie_len);
+ next += params->req_ie_len;
}
- if (bss)
- cfg80211_hold_bss(bss_from_pub(bss));
- ev->cr.bss = bss;
- ev->cr.status = status;
- ev->cr.timeout_reason = timeout_reason;
+ if (params->resp_ie_len) {
+ ev->cr.resp_ie = next;
+ ev->cr.resp_ie_len = params->resp_ie_len;
+ memcpy((void *)ev->cr.resp_ie, params->resp_ie,
+ params->resp_ie_len);
+ next += params->resp_ie_len;
+ }
+ if (params->fils_kek_len) {
+ ev->cr.fils_kek = next;
+ ev->cr.fils_kek_len = params->fils_kek_len;
+ memcpy((void *)ev->cr.fils_kek, params->fils_kek,
+ params->fils_kek_len);
+ next += params->fils_kek_len;
+ }
+ if (params->pmk_len) {
+ ev->cr.pmk = next;
+ ev->cr.pmk_len = params->pmk_len;
+ memcpy((void *)ev->cr.pmk, params->pmk, params->pmk_len);
+ next += params->pmk_len;
+ }
+ if (params->pmkid) {
+ ev->cr.pmkid = next;
+ memcpy((void *)ev->cr.pmkid, params->pmkid, WLAN_PMKID_LEN);
+ next += WLAN_PMKID_LEN;
+ }
+ ev->cr.update_erp_next_seq_num = params->update_erp_next_seq_num;
+ if (params->update_erp_next_seq_num)
+ ev->cr.fils_erp_next_seq_num = params->fils_erp_next_seq_num;
+ if (params->bss)
+ cfg80211_hold_bss(bss_from_pub(params->bss));
+ ev->cr.bss = params->bss;
+ ev->cr.status = params->status;
+ ev->cr.timeout_reason = params->timeout_reason;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
spin_unlock_irqrestore(&wdev->event_lock, flags);
queue_work(cfg80211_wq, &rdev->event_work);
}
-EXPORT_SYMBOL(cfg80211_connect_bss);
+EXPORT_SYMBOL(cfg80211_connect_done);
/* Consumes bss object one way or another */
void __cfg80211_roamed(struct wireless_dev *wdev,
{
struct cfg80211_event *ev;
unsigned long flags;
- const u8 *bssid = NULL;
spin_lock_irqsave(&wdev->event_lock, flags);
while (!list_empty(&wdev->event_list)) {
wdev_lock(wdev);
switch (ev->type) {
case EVENT_CONNECT_RESULT:
- if (!is_zero_ether_addr(ev->cr.bssid))
- bssid = ev->cr.bssid;
__cfg80211_connect_result(
- wdev->netdev, bssid,
- ev->cr.req_ie, ev->cr.req_ie_len,
- ev->cr.resp_ie, ev->cr.resp_ie_len,
- ev->cr.status,
- ev->cr.status == WLAN_STATUS_SUCCESS,
- ev->cr.bss, ev->cr.timeout_reason);
+ wdev->netdev,
+ &ev->cr,
+ ev->cr.status == WLAN_STATUS_SUCCESS);
break;
case EVENT_ROAMED:
__cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie,
# Check for git id commit length and improperly formed commit descriptions
if ($in_commit_log && !$commit_log_possible_stack_dump &&
+ $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
($line =~ /\b[0-9a-f]{12,40}\b/i &&
$line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
* immediately unlinked.
*/
struct key_type key_type_dead = {
- .name = "dead",
+ .name = ".dead",
};
/*
* Create and join an anonymous session keyring or join a named session
* keyring, creating it if necessary. A named session keyring must have Search
* permission for it to be joined. Session keyrings without this permit will
- * be skipped over.
+ * be skipped over. It is not permitted for userspace to create or join
+ * keyrings whose name begin with a dot.
*
* If successful, the ID of the joined session keyring will be returned.
*/
ret = PTR_ERR(name);
goto error;
}
+
+ ret = -EPERM;
+ if (name[0] == '.')
+ goto error_name;
}
/* join the session */
ret = join_session_keyring(name);
+error_name:
kfree(name);
-
error:
return ret;
}
* Read or set the default keyring in which request_key() will cache keys and
* return the old setting.
*
- * If a process keyring is specified then this will be created if it doesn't
- * yet exist. The old setting will be returned if successful.
+ * If a thread or process keyring is specified then it will be created if it
+ * doesn't yet exist. The old setting will be returned if successful.
*/
long keyctl_set_reqkey_keyring(int reqkey_defl)
{
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
ret = install_process_keyring_to_cred(new);
- if (ret < 0) {
- if (ret != -EEXIST)
- goto error;
- ret = 0;
- }
+ if (ret < 0)
+ goto error;
goto set;
case KEY_REQKEY_DEFL_DEFAULT:
}
/*
- * Install a fresh thread keyring directly to new credentials. This keyring is
- * allowed to overrun the quota.
+ * Install a thread keyring to the given credentials struct if it didn't have
+ * one already. This is allowed to overrun the quota.
+ *
+ * Return: 0 if a thread keyring is now present; -errno on failure.
*/
int install_thread_keyring_to_cred(struct cred *new)
{
struct key *keyring;
+ if (new->thread_keyring)
+ return 0;
+
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
}
/*
- * Install a fresh thread keyring, discarding the old one.
+ * Install a thread keyring to the current task if it didn't have one already.
+ *
+ * Return: 0 if a thread keyring is now present; -errno on failure.
*/
static int install_thread_keyring(void)
{
if (!new)
return -ENOMEM;
- BUG_ON(new->thread_keyring);
-
ret = install_thread_keyring_to_cred(new);
if (ret < 0) {
abort_creds(new);
}
/*
- * Install a process keyring directly to a credentials struct.
+ * Install a process keyring to the given credentials struct if it didn't have
+ * one already. This is allowed to overrun the quota.
*
- * Returns -EEXIST if there was already a process keyring, 0 if one installed,
- * and other value on any other error
+ * Return: 0 if a process keyring is now present; -errno on failure.
*/
int install_process_keyring_to_cred(struct cred *new)
{
struct key *keyring;
if (new->process_keyring)
- return -EEXIST;
+ return 0;
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
}
/*
- * Make sure a process keyring is installed for the current process. The
- * existing process keyring is not replaced.
+ * Install a process keyring to the current task if it didn't have one already.
*
- * Returns 0 if there is a process keyring by the end of this function, some
- * error otherwise.
+ * Return: 0 if a process keyring is now present; -errno on failure.
*/
static int install_process_keyring(void)
{
ret = install_process_keyring_to_cred(new);
if (ret < 0) {
abort_creds(new);
- return ret != -EEXIST ? ret : 0;
+ return ret;
}
return commit_creds(new);
}
/*
- * Install a session keyring directly to a credentials struct.
+ * Install the given keyring as the session keyring of the given credentials
+ * struct, replacing the existing one if any. If the given keyring is NULL,
+ * then install a new anonymous session keyring.
+ *
+ * Return: 0 on success; -errno on failure.
*/
int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
{
}
/*
- * Install a session keyring, discarding the old one. If a keyring is not
- * supplied, an empty one is invented.
+ * Install the given keyring as the session keyring of the current task,
+ * replacing the existing one if any. If the given keyring is NULL, then
+ * install a new anonymous session keyring.
+ *
+ * Return: 0 on success; -errno on failure.
*/
static int install_session_keyring(struct key *keyring)
{
/* NOTE: overflow flag is not cleared */
spin_unlock_irqrestore(&f->lock, flags);
+ /* close the old pool and wait until all users are gone */
+ snd_seq_pool_mark_closing(oldpool);
+ snd_use_lock_sync(&f->use_lock);
+
/* release cells in old pool */
for (cell = oldhead; cell; cell = next) {
next = cell->next;
ALC292_FIXUP_DISABLE_AAMIX,
ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK,
ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
ALC275_FIXUP_DELL_XPS,
ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE,
ALC293_FIXUP_LENOVO_SPK_NOISE,
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE
},
+ [ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
[ALC275_FIXUP_DELL_XPS] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
.type = HDA_FIXUP_FUNC,
.v.func = alc298_fixup_speaker_volume,
.chained = true,
- .chain_id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
},
[ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
.type = HDA_FIXUP_PINS,
}
#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
-#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
+#define CLASSD_ACLK_RATE_12M288_MPY_8 (12288 * 1000 * 8)
static struct {
int rate;
#include "msm-digital-cdc.h"
#include "msm-cdc-common.h"
#include "../../msm/sdm660-common.h"
+#include "../../../../drivers/base/regmap/internal.h"
#define DRV_NAME "msm_digital_codec"
#define MCLK_RATE_9P6MHZ 9600000
{
int ret = -EINVAL;
struct msm_asoc_mach_data *pdata = NULL;
+ struct msm_dig_priv *msm_dig_cdc =
+ snd_soc_codec_get_drvdata(registered_digcodec);
pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card);
if (ret < 0) {
pr_err("%s:failed to enable the MCLK\n",
__func__);
+ /*
+ * Avoid access to lpass register
+ * as clock enable failed during SSR.
+ */
+ if (ret == -ENODEV)
+ msm_dig_cdc->regmap->cache_only = true;
mutex_unlock(&pdata->cdc_int_mclk0_mutex);
return ret;
}
bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- /* cache bypass for initial version */
- default:
+ case MSM89XX_CDC_CORE_RX1_B1_CTL:
+ case MSM89XX_CDC_CORE_RX2_B1_CTL:
+ case MSM89XX_CDC_CORE_RX3_B1_CTL:
+ case MSM89XX_CDC_CORE_RX1_B6_CTL:
+ case MSM89XX_CDC_CORE_RX2_B6_CTL:
+ case MSM89XX_CDC_CORE_RX3_B6_CTL:
+ case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL:
+ case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL:
+ case MSM89XX_CDC_CORE_CLK_MCLK_CTL:
+ case MSM89XX_CDC_CORE_CLK_PDM_CTL:
return true;
+ default:
+ return false;
}
}
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \
msm-dai-stub-v2.o
obj-$(CONFIG_SND_HWDEP) += msm-pcm-routing-devdep.o
-obj-$(CONFIG_DTS_EAGLE) += msm-dts-eagle.o
+obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o
obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o
obj-$(CONFIG_DOLBY_LICENSE) += msm-ds2-dap-config.o
obj-$(CONFIG_DTS_SRS_TM) += msm-dts-srs-tm-config.o
case ULP_LSM_CAL_TYPE:
size = sizeof(struct audio_cal_info_lsm);
break;
- case DTS_EAGLE_CAL_TYPE:
- size = 0;
- break;
case AUDIO_CORE_METAINFO_CAL_TYPE:
size = sizeof(struct audio_cal_info_metainfo);
break;
case ULP_LSM_CAL_TYPE:
size = sizeof(struct audio_cal_type_lsm);
break;
- case DTS_EAGLE_CAL_TYPE:
- size = 0;
- break;
case AUDIO_CORE_METAINFO_CAL_TYPE:
size = sizeof(struct audio_cal_type_metainfo);
break;
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-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
#include <sound/q6asm-v2.h>
#include <sound/compress_params.h>
#include <sound/msm-audio-effects-q6-v2.h>
-#include <sound/msm-dts-eagle.h>
#include <sound/devdep_params.h>
#define MAX_ENABLE_CMD_SIZE 32
case EQ_MODULE:
switch (topology) {
case ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS:
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS:
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
- return true;
- default:
- return false;
- }
- case DTS_EAGLE_MODULE:
- switch (topology) {
- case ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX:
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS:
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
- return true;
- default:
- return false;
- }
- case SOFT_VOLUME2_MODULE:
- case DTS_EAGLE_MODULE_ENABLE:
- switch (topology) {
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS:
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
return true;
default:
return false;
break;
}
}
- if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+ if (params_length && (rc == 0))
q6asm_send_audio_effects_params(ac, params,
params_length);
else
break;
}
}
- if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+ if (params_length && (rc == 0))
q6asm_send_audio_effects_params(ac, params,
params_length);
else
break;
}
}
- if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+ if (params_length && (rc == 0))
q6asm_send_audio_effects_params(ac, params,
params_length);
else
break;
}
}
- if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+ if (params_length && (rc == 0))
q6asm_send_audio_effects_params(ac, params,
params_length);
else
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
#include <sound/msm-audio-effects-q6-v2.h>
-#include <sound/msm-dts-eagle.h>
-
#include "msm-pcm-routing-v2.h"
#include "msm-qti-pp-config.h"
#define MAX_NUMBER_OF_STREAMS 2
-/*
- * Max size for getting DTS EAGLE Param through kcontrol
- * Safe for both 32 and 64 bit platforms
- * 64 = size of kcontrol value array on 64 bit platform
- * 4 = size of parameters Eagle expects before cast to 64 bits
- * 40 = size of dts_eagle_param_desc + module_id cast to 64 bits
- */
-#define DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA ((64 * 4) - 40)
-
struct msm_compr_gapless_state {
bool set_next_stream_id;
int32_t stream_opened[MAX_NUMBER_OF_STREAMS];
if (rc < 0)
pr_err("%s: Send vol gain command failed rc=%d\n",
__func__, rc);
- else
- if (msm_dts_eagle_set_stream_gain(prtd->audio_client,
- volume_l, volume_r))
- pr_debug("%s: DTS_EAGLE send stream gain failed\n",
- __func__);
return rc;
}
};
switch (ac->topology) {
- case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: /* HPX + SA+ topology */
-
- ret = q6asm_set_softvolume_v2(ac, &softvol,
- SOFT_VOLUME_INSTANCE_1);
- if (ret < 0)
- pr_err("%s: Send SoftVolume Param failed ret=%d\n",
- __func__, ret);
-
- ret = q6asm_set_softvolume_v2(ac, &softvol,
- SOFT_VOLUME_INSTANCE_2);
- if (ret < 0)
- pr_err("%s: Send SoftVolume2 Param failed ret=%d\n",
- __func__, ret);
- /*
- * HPX module init is trigerred from HAL using ioctl
- * DTS_EAGLE_MODULE_ENABLE when stream starts
- */
- break;
- case ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX: /* HPX topology */
- break;
default:
ret = q6asm_set_softvolume_v2(ac, &softvol,
SOFT_VOLUME_INSTANCE_1);
&(audio_effects->equalizer),
values);
break;
- case DTS_EAGLE_MODULE:
- pr_debug("%s: DTS_EAGLE_MODULE\n", __func__);
- if (!msm_audio_effects_is_effmodule_supp_in_top(effects_module,
- prtd->audio_client->topology))
- return 0;
- msm_dts_eagle_handle_asm(NULL, (void *)values, true,
- false, prtd->audio_client, NULL);
- break;
- case DTS_EAGLE_MODULE_ENABLE:
- pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__);
- if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
- prtd->audio_client->topology))
- msm_dts_eagle_enable_asm(prtd->audio_client,
- (bool)values[0],
- AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
-
- break;
case SOFT_VOLUME_MODULE:
pr_debug("%s: SOFT_VOLUME_MODULE\n", __func__);
break;
struct msm_compr_audio_effects *audio_effects = NULL;
struct snd_compr_stream *cstream = NULL;
struct msm_compr_audio *prtd = NULL;
- long *values = &(ucontrol->value.integer.value[0]);
pr_debug("%s\n", __func__);
if (fe_id >= MSM_FRONTEND_DAI_MAX) {
return -EINVAL;
}
- switch (audio_effects->query.mod_id) {
- case DTS_EAGLE_MODULE:
- pr_debug("%s: DTS_EAGLE_MODULE handling queued get\n",
- __func__);
- values[0] = (long)audio_effects->query.mod_id;
- values[1] = (long)audio_effects->query.parm_id;
- values[2] = (long)audio_effects->query.size;
- values[3] = (long)audio_effects->query.offset;
- values[4] = (long)audio_effects->query.device;
- if (values[2] > DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA) {
- pr_err("%s: DTS_EAGLE_MODULE parameter's requested size (%li) too large (max size is %i)\n",
- __func__, values[2],
- DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA);
- return -EINVAL;
- }
- msm_dts_eagle_handle_asm(NULL, (void *)&values[1],
- true, true, prtd->audio_client, NULL);
- break;
- default:
- pr_err("%s: Invalid effects config module\n", __func__);
- return -EINVAL;
- }
return 0;
}
+++ /dev/null
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/msm_ion.h>
-#include <linux/mm.h>
-#include <linux/msm_audio_ion.h>
-#include <linux/vmalloc.h>
-#include <sound/core.h>
-#include <sound/soc.h>
-#include <sound/pcm.h>
-#include <sound/q6adm-v2.h>
-#include <sound/q6asm-v2.h>
-#include <sound/apr_audio-v2.h>
-#include <sound/q6audio-v2.h>
-#include <sound/audio_effects.h>
-#include <sound/hwdep.h>
-#include <sound/msm-dts-eagle.h>
-#include <sound/q6core.h>
-
-#include "msm-pcm-routing-v2.h"
-
-#define ION_MEM_SIZE 131072
-#define DEPC_MAX_SIZE 524288
-
-#define MPST AUDPROC_MODULE_ID_DTS_HPX_POSTMIX
-#define MPRE AUDPROC_MODULE_ID_DTS_HPX_PREMIX
-
-#define eagle_vol_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__)
-#define eagle_vol_err(fmt, ...) \
- pr_err("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__)
-#define eagle_drv_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__)
-#define eagle_drv_err(fmt, ...) \
- pr_err("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__)
-#define eagle_precache_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__)
-#define eagle_precache_err(fmt, ...) \
- pr_err("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__)
-#define eagle_postcache_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__)
-#define eagle_postcache_err(fmt, ...) \
- pr_err("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__)
-#define eagle_ioctl_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__)
-#define eagle_ioctl_err(fmt, ...) \
- pr_err("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__)
-#define eagle_asm_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__)
-#define eagle_asm_err(fmt, ...) \
- pr_err("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__)
-#define eagle_adm_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__)
-#define eagle_adm_err(fmt, ...) \
- pr_err("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__)
-#define eagle_enable_dbg(fmt, ...) \
- pr_debug("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__)
-#define eagle_enable_err(fmt, ...) \
- pr_err("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__)
-#define eagle_ioctl_info(fmt, ...) \
- pr_err("DTS_EAGLE_IOCTL: " fmt "\n", ##__VA_ARGS__)
-
-enum {
- AUDIO_DEVICE_OUT_EARPIECE = 0,
- AUDIO_DEVICE_OUT_SPEAKER,
- AUDIO_DEVICE_OUT_WIRED_HEADSET,
- AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO,
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
- AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT,
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
- AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
- AUDIO_DEVICE_OUT_AUX_DIGITAL,
- AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET,
- AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET,
- AUDIO_DEVICE_OUT_USB_ACCESSORY,
- AUDIO_DEVICE_OUT_USB_DEVICE,
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
- AUDIO_DEVICE_OUT_ANC_HEADSET,
- AUDIO_DEVICE_OUT_ANC_HEADPHONE,
- AUDIO_DEVICE_OUT_PROXY,
- AUDIO_DEVICE_OUT_FM,
- AUDIO_DEVICE_OUT_FM_TX,
-
- AUDIO_DEVICE_OUT_COUNT
-};
-
-#define AUDIO_DEVICE_COMBO 0x400000 /* bit 23 */
-
-enum { /* cache block */
- CB_0 = 0,
- CB_1,
- CB_2,
- CB_3,
- CB_4,
- CB_5,
- CB_6,
- CB_7,
-
- CB_COUNT
-};
-
-enum { /* cache block description */
- CBD_DEV_MASK = 0,
- CBD_OFFSG,
- CBD_CMD0,
- CBD_SZ0,
- CBD_OFFS1,
- CBD_CMD1,
- CBD_SZ1,
- CBD_OFFS2,
- CBD_CMD2,
- CBD_SZ2,
- CBD_OFFS3,
- CBD_CMD3,
- CBD_SZ3,
-
- CBD_COUNT,
-};
-
-static s32 _fx_logN(s32 x)
-{
- s32 t, y = 0xa65af;
- if (x < 0x00008000) {
- x <<= 16; y -= 0xb1721; }
- if (x < 0x00800000) {
- x <<= 8; y -= 0x58b91; }
- if (x < 0x08000000) {
- x <<= 4; y -= 0x2c5c8; }
- if (x < 0x20000000) {
- x <<= 2; y -= 0x162e4; }
- if (x < 0x40000000) {
- x <<= 1; y -= 0x0b172; }
- t = x + (x >> 1);
- if ((t & 0x80000000) == 0) {
- x = t; y -= 0x067cd; }
- t = x + (x >> 2);
- if ((t & 0x80000000) == 0) {
- x = t; y -= 0x03920; }
- t = x + (x >> 3);
- if ((t & 0x80000000) == 0) {
- x = t; y -= 0x01e27; }
- t = x + (x >> 4);
- if ((t & 0x80000000) == 0) {
- x = t; y -= 0x00f85; }
- t = x + (x >> 5);
- if ((t & 0x80000000) == 0) {
- x = t; y -= 0x007e1; }
- t = x + (x >> 6);
- if ((t & 0x80000000) == 0) {
- x = t; y -= 0x003f8; }
- t = x + (x >> 7);
- if ((t & 0x80000000) == 0) {
- x = t; y -= 0x001fe; }
- x = 0x80000000 - x;
- y -= x >> 15;
- return y;
-}
-
-static inline void *_getd(struct dts_eagle_param_desc *depd)
-{
- return (void *)(((char *)depd) + sizeof(struct dts_eagle_param_desc));
-}
-
-static int _ref_cnt;
-/* dts eagle parameter cache */
-static char *_depc;
-static u32 _depc_size;
-static s32 _c_bl[CB_COUNT][CBD_COUNT];
-static u32 _device_primary;
-static u32 _device_all;
-/* ION states */
-static struct ion_client *_ion_client;
-static struct ion_handle *_ion_handle;
-static struct param_outband _po;
-static struct audio_client *_ac_NT;
-static struct ion_client *_ion_client_NT;
-static struct ion_handle *_ion_handle_NT;
-static struct param_outband _po_NT;
-
-#define SEC_BLOB_MAX_CNT 10
-#define SEC_BLOB_MAX_SIZE 0x4004 /*extra 4 for size*/
-static char *_sec_blob[SEC_BLOB_MAX_CNT];
-
-/* multi-copp support */
-static int _cidx[AFE_MAX_PORTS] = {-1};
-
-/* volume controls */
-#define VOL_CMD_CNT_MAX 10
-static u32 _vol_cmd_cnt;
-static s32 **_vol_cmds;
-struct vol_cmds_d {
- s32 d[4];
-};
-static struct vol_cmds_d *_vol_cmds_d;
-static const s32 _log10_10_inv_x20 = 0x0008af84;
-
-/* hpx master control */
-static u32 _is_hpx_enabled;
-
-static void _volume_cmds_free(void)
-{
- int i;
- for (i = 0; i < _vol_cmd_cnt; i++)
- kfree(_vol_cmds[i]);
- _vol_cmd_cnt = 0;
- kfree(_vol_cmds);
- kfree(_vol_cmds_d);
- _vol_cmds = NULL;
- _vol_cmds_d = NULL;
-}
-
-static s32 _volume_cmds_alloc1(s32 size)
-{
- _volume_cmds_free();
- _vol_cmd_cnt = size;
- _vol_cmds = kzalloc(_vol_cmd_cnt * sizeof(int *), GFP_KERNEL);
- if (_vol_cmds) {
- _vol_cmds_d = kzalloc(_vol_cmd_cnt * sizeof(struct vol_cmds_d),
- GFP_KERNEL);
- } else
- _vol_cmd_cnt = 0;
- if (_vol_cmds_d)
- return 0;
- _volume_cmds_free();
- return -ENOMEM;
-}
-
-/* assumes size is equal or less than 0xFFF */
-static s32 _volume_cmds_alloc2(s32 idx, s32 size)
-{
- kfree(_vol_cmds[idx]);
- _vol_cmds[idx] = kzalloc(size, GFP_KERNEL);
- if (_vol_cmds[idx])
- return 0;
- _vol_cmds_d[idx].d[0] = 0;
- return -ENOMEM;
-}
-
-static void _init_cb_descs(void)
-{
- int i;
- for (i = 0; i < CB_COUNT; i++) {
- _c_bl[i][CBD_DEV_MASK] = 0;
- _c_bl[i][CBD_OFFSG] = _c_bl[i][CBD_OFFS1] =
- _c_bl[i][CBD_OFFS2] = _c_bl[i][CBD_OFFS3] =
- 0xFFFFFFFF;
- _c_bl[i][CBD_CMD0] = _c_bl[i][CBD_SZ0] =
- _c_bl[i][CBD_CMD1] = _c_bl[i][CBD_SZ1] =
- _c_bl[i][CBD_CMD2] = _c_bl[i][CBD_SZ2] =
- _c_bl[i][CBD_CMD3] = _c_bl[i][CBD_SZ3] = 0;
- }
-}
-
-static u32 _get_dev_mask_for_pid(int pid)
-{
- switch (pid) {
- case SLIMBUS_0_RX:
- return (1 << AUDIO_DEVICE_OUT_EARPIECE) |
- (1 << AUDIO_DEVICE_OUT_SPEAKER) |
- (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) |
- (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) |
- (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) |
- (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE);
- case INT_BT_SCO_RX:
- return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT);
- case RT_PROXY_PORT_001_RX:
- return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) |
- (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) |
- (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) |
- (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) |
- (1 << AUDIO_DEVICE_OUT_USB_DEVICE) |
- (1 << AUDIO_DEVICE_OUT_PROXY);
- case HDMI_RX:
- return 1 << AUDIO_DEVICE_OUT_AUX_DIGITAL;
- case INT_FM_RX:
- return 1 << AUDIO_DEVICE_OUT_FM;
- case INT_FM_TX:
- return 1 << AUDIO_DEVICE_OUT_FM_TX;
- default:
- return 0;
- }
-}
-
-static int _get_pid_from_dev(u32 device)
-{
- if (device & (1 << AUDIO_DEVICE_OUT_EARPIECE) ||
- device & (1 << AUDIO_DEVICE_OUT_SPEAKER) ||
- device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) ||
- device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) ||
- device & (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) ||
- device & (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE)) {
- return SLIMBUS_0_RX;
- } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) ||
- device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) ||
- device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) {
- return INT_BT_SCO_RX;
- } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) ||
- device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) ||
- device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) ||
- device & (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) ||
- device & (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) ||
- device & (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) ||
- device & (1 << AUDIO_DEVICE_OUT_USB_DEVICE) ||
- device & (1 << AUDIO_DEVICE_OUT_PROXY)) {
- return RT_PROXY_PORT_001_RX;
- } else if (device & (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
- return HDMI_RX;
- } else if (device & (1 << AUDIO_DEVICE_OUT_FM)) {
- return INT_FM_RX;
- } else if (device & (1 << AUDIO_DEVICE_OUT_FM_TX)) {
- return INT_FM_TX;
- }
- return 0;
-}
-
-static s32 _get_cb_for_dev(int device)
-{
- s32 i;
- if (device & AUDIO_DEVICE_COMBO) {
- for (i = 0; i < CB_COUNT; i++) {
- if ((_c_bl[i][CBD_DEV_MASK] & device) == device)
- return i;
- }
- } else {
- for (i = 0; i < CB_COUNT; i++) {
- if ((_c_bl[i][CBD_DEV_MASK] & device) &&
- !(_c_bl[i][CBD_DEV_MASK] & AUDIO_DEVICE_COMBO))
- return i;
- }
- }
- eagle_drv_err("%s: device %i not found", __func__, device);
- return -EINVAL;
-}
-
-static int _is_port_open_and_eagle(int pid)
-{
- if (msm_routing_check_backend_enabled(pid))
- return 1;
- return 1;
-}
-
-static int _isNTDevice(u32 device)
-{
- if (device &
- ((1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) |
- (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) |
- (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL)))
- return 1;
- return 0;
-}
-
-static void _reg_ion_mem(void)
-{
- int rc;
- rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client, &_ion_handle,
- ION_MEM_SIZE, &_po.paddr, &_po.size, &_po.kvaddr);
- if (rc)
- eagle_drv_err("%s: msm audio ion alloc failed with %i",
- __func__, rc);
-}
-
-static void _unreg_ion_mem(void)
-{
- int rc;
- rc = msm_audio_ion_free(_ion_client, _ion_handle);
- if (rc)
- eagle_drv_err("%s: msm audio ion alloc failed with %i",
- __func__, rc);
-}
-
-static void _reg_ion_mem_NT(void)
-{
- int rc;
- eagle_drv_dbg("%s: NT ion mem", __func__);
- rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client_NT,
- &_ion_handle_NT, ION_MEM_SIZE,
- &_po_NT.paddr, &_po_NT.size, &_po_NT.kvaddr);
- if (rc) {
- eagle_drv_err("%s: msm audio ion alloc failed", __func__);
- return;
- }
- rc = q6asm_memory_map(_ac_NT, _po_NT.paddr,
- IN, _po_NT.size, 1);
- if (rc < 0) {
- eagle_drv_err("%s: memory map failed", __func__);
- msm_audio_ion_free(_ion_client_NT, _ion_handle_NT);
- _ion_client_NT = NULL;
- _ion_handle_NT = NULL;
- }
-}
-
-static void _unreg_ion_mem_NT(void)
-{
- int rc;
- rc = q6asm_memory_unmap(_ac_NT, _po_NT.paddr, IN);
- if (rc < 0)
- eagle_drv_err("%s: mem unmap failed", __func__);
- rc = msm_audio_ion_free(_ion_client_NT, _ion_handle_NT);
- if (rc < 0)
- eagle_drv_err("%s: mem free failed", __func__);
-
- _ion_client_NT = NULL;
- _ion_handle_NT = NULL;
-}
-
-static struct audio_client *_getNTDeviceAC(void)
-{
- return _ac_NT;
-}
-
-static void _set_audioclient(struct audio_client *ac)
-{
- _ac_NT = ac;
- _reg_ion_mem_NT();
-}
-
-static void _clear_audioclient(void)
-{
- _unreg_ion_mem_NT();
- _ac_NT = NULL;
-}
-
-
-static int _sendcache_pre(struct audio_client *ac)
-{
- uint32_t offset, size;
- int32_t cidx, cmd, err = 0;
- cidx = _get_cb_for_dev(_device_primary);
- if (cidx < 0) {
- eagle_precache_err("%s: no cache for primary device %i found",
- __func__, _device_primary);
- return -EINVAL;
- }
- offset = _c_bl[cidx][CBD_OFFSG];
- cmd = _c_bl[cidx][CBD_CMD0];
- size = _c_bl[cidx][CBD_SZ0];
- /* check for integer overflow */
- if (offset > (UINT_MAX - size))
- err = -EINVAL;
- if ((_depc_size == 0) || !_depc || (size == 0) ||
- cmd == 0 || ((offset + size) > _depc_size) || (err != 0)) {
- eagle_precache_err("%s: primary device %i cache index %i general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i",
- __func__, _device_primary, cidx, _depc_size, _depc,
- offset, size, cmd);
- return -EINVAL;
- }
-
- if ((offset < (UINT_MAX - 124)) && ((offset + 124) < _depc_size))
- eagle_precache_dbg("%s: first 6 integers %i %i %i %i %i %i (30th %i)",
- __func__, *((int *)&_depc[offset]),
- *((int *)&_depc[offset+4]),
- *((int *)&_depc[offset+8]),
- *((int *)&_depc[offset+12]),
- *((int *)&_depc[offset+16]),
- *((int *)&_depc[offset+20]),
- *((int *)&_depc[offset+120]));
- eagle_precache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, param = 0x%X, offset = %u, and size = %u",
- __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], cmd, offset, size);
-
- if (q6asm_dts_eagle_set(ac, cmd, size, (void *)&_depc[offset],
- NULL, MPRE))
- eagle_precache_err("%s: q6asm_dts_eagle_set failed with id = %d and size = %u",
- __func__, cmd, size);
- else
- eagle_precache_dbg("%s: q6asm_dts_eagle_set succeeded with id = %d and size = %u",
- __func__, cmd, size);
- return 0;
-}
-
-static int _sendcache_post(int port_id, int copp_idx, int topology)
-{
- int cidx = -1, cmd, mask, index, err = 0;
- uint32_t offset, size;
-
- if (port_id == -1) {
- cidx = _get_cb_for_dev(_device_primary);
- if (cidx < 0) {
- eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X",
- __func__, _device_primary, port_id);
- return -EINVAL;
- }
- goto NT_MODE_GOTO;
- }
-
- index = adm_validate_and_get_port_index(port_id);
- if (index < 0) {
- eagle_postcache_err("%s: Invalid port idx %d port_id %#x",
- __func__, index, port_id);
- return -EINVAL;
- }
- eagle_postcache_dbg("%s: valid port idx %d for port_id %#x set to %i",
- __func__, index, port_id, copp_idx);
- _cidx[index] = copp_idx;
-
- mask = _get_dev_mask_for_pid(port_id);
- if (mask & _device_primary) {
- cidx = _get_cb_for_dev(_device_primary);
- if (cidx < 0) {
- eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X",
- __func__, _device_primary, port_id);
- return -EINVAL;
- }
- } else if (mask & _device_all) {
- cidx = _get_cb_for_dev(_device_all);
- if (cidx < 0) {
- eagle_postcache_err("%s: no cache for combo device %i found. Port id was 0x%X",
- __func__, _device_all, port_id);
- return -EINVAL;
- }
- } else {
- eagle_postcache_err("%s: port id 0x%X not for primary or combo device %i",
- __func__, port_id, _device_primary);
- return -EINVAL;
- }
-
-NT_MODE_GOTO:
- offset = _c_bl[cidx][CBD_OFFSG] + _c_bl[cidx][CBD_OFFS2];
- cmd = _c_bl[cidx][CBD_CMD2];
- size = _c_bl[cidx][CBD_SZ2];
-
- /* check for integer overflow */
- if (offset > (UINT_MAX - size))
- err = -EINVAL;
- if ((_depc_size == 0) || !_depc || (err != 0) || (size == 0) ||
- (cmd == 0) || (offset + size) > _depc_size) {
- eagle_postcache_err("%s: primary device %i cache index %i port_id 0x%X general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i",
- __func__, _device_primary, cidx, port_id,
- _depc_size, _depc, offset, size, cmd);
- return -EINVAL;
- }
-
- if ((offset < (UINT_MAX - 24)) && ((offset + 24) < _depc_size))
- eagle_postcache_dbg("%s: first 6 integers %i %i %i %i %i %i",
- __func__, *((int *)&_depc[offset]),
- *((int *)&_depc[offset+4]),
- *((int *)&_depc[offset+8]),
- *((int *)&_depc[offset+12]),
- *((int *)&_depc[offset+16]),
- *((int *)&_depc[offset+20]));
- eagle_postcache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, port_id = 0x%X, param = 0x%X, offset = %u, and size = %u",
- __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], port_id, cmd,
- offset, size);
-
- if (_ac_NT) {
- eagle_postcache_dbg("%s: NT Route detected", __func__);
- if (q6asm_dts_eagle_set(_getNTDeviceAC(), cmd, size,
- (void *)&_depc[offset],
- &_po_NT, MPST))
- eagle_postcache_err("%s: q6asm_dts_eagle_set failed with id = 0x%X and size = %u",
- __func__, cmd, size);
- } else if (adm_dts_eagle_set(port_id, copp_idx, cmd,
- (void *)&_depc[offset], size) < 0)
- eagle_postcache_err("%s: adm_dts_eagle_set failed with id = 0x%X and size = %u",
- __func__, cmd, size);
- else
- eagle_postcache_dbg("%s: adm_dts_eagle_set succeeded with id = 0x%X and size = %u",
- __func__, cmd, size);
- return 0;
-}
-
-static int _enable_post_get_control(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = _is_hpx_enabled;
- return 0;
-}
-
-static int _enable_post_put_control(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int idx = 0, be_index = 0, port_id, topology;
- int flag = ucontrol->value.integer.value[0];
- struct msm_pcm_routing_bdai_data msm_bedai;
- eagle_drv_dbg("%s: flag %d", __func__, flag);
-
- _is_hpx_enabled = flag ? true : false;
- msm_pcm_routing_acquire_lock();
- /* send cache postmix params when hpx is set On */
- for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
- msm_pcm_routing_get_bedai_info(be_index, &msm_bedai);
- port_id = msm_bedai.port_id;
- if (!(((port_id == SLIMBUS_0_RX) ||
- (port_id == RT_PROXY_PORT_001_RX)) &&
- msm_bedai.active))
- continue;
- for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
- topology = adm_get_topology_for_port_copp_idx(
- port_id, idx);
- if (topology ==
- ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) {
- msm_dts_eagle_enable_adm(port_id, idx,
- _is_hpx_enabled);
- }
- }
- }
- msm_pcm_routing_release_lock();
- return 0;
-}
-
-static const struct snd_kcontrol_new _hpx_enabled_controls[] = {
- SOC_SINGLE_EXT("Set HPX OnOff", SND_SOC_NOPM, 0, 1, 0,
- _enable_post_get_control, _enable_post_put_control)
-};
-
-/**
- * msm_dts_ion_memmap() - helper function to map ION memory
- * @po_: Out of band memory structure used as memory.
- *
- * Assign already allocated ION memory for mapping it to dsp.
- *
- * Return: No return value.
- */
-void msm_dts_ion_memmap(struct param_outband *po_)
-{
- po_->size = ION_MEM_SIZE;
- po_->kvaddr = _po.kvaddr;
- po_->paddr = _po.paddr;
-}
-
-/**
- * msm_dts_eagle_enable_asm() - Enable/disable dts module
- * @ac: Enable/disable module in ASM session associated with this audio client.
- * @enable: Enable/disable the dts module.
- * @module: module id.
- *
- * Enable/disable specified dts module id in asm.
- *
- * Return: Return failure if any.
- */
-int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module)
-{
- int ret = 0;
- eagle_enable_dbg("%s: enable = %i on module %i",
- __func__, enable, module);
- _is_hpx_enabled = enable;
- ret = q6asm_dts_eagle_set(ac, AUDPROC_PARAM_ID_ENABLE,
- sizeof(enable), &enable,
- NULL, module);
- if (_is_hpx_enabled) {
- if (module == MPRE)
- _sendcache_pre(ac);
- else if (module == MPST)
- _sendcache_post(-1, 0, 0);
- }
- return ret;
-}
-
-/**
- * msm_dts_eagle_enable_adm() - Enable/disable dts module in adm
- * @port_id: Send enable/disable param to this port id.
- * @copp_idx: Send enable/disable param to the relevant copp.
- * @enable: Enable/disable the dts module.
- *
- * Enable/disable dts module in adm.
- *
- * Return: Return failure if any.
- */
-int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable)
-{
- int ret = 0;
- eagle_enable_dbg("%s: enable = %i", __func__, enable);
- _is_hpx_enabled = enable;
- ret = adm_dts_eagle_set(port_id, copp_idx, AUDPROC_PARAM_ID_ENABLE,
- (char *)&enable, sizeof(enable));
- if (_is_hpx_enabled)
- _sendcache_post(port_id, copp_idx, MPST);
- return ret;
-}
-
-/**
- * msm_dts_eagle_add_controls() - Add mixer control to Enable/Disable DTS HPX
- * @platform: Add mixer controls to this platform.
- *
- * Add mixer control to Enable/Disable DTS HPX module in ADM.
- *
- * Return: No return value.
- */
-void msm_dts_eagle_add_controls(struct snd_soc_platform *platform)
-{
- snd_soc_add_platform_controls(platform, _hpx_enabled_controls,
- ARRAY_SIZE(_hpx_enabled_controls));
-}
-
-/**
- * msm_dts_eagle_set_stream_gain() - Set stream gain to DTS Premix module
- * @ac: Set stream gain to ASM session associated with this audio client.
- * @lgain: Left gain value.
- * @rgain: Right gain value.
- *
- * Set stream gain to DTS Premix module in ASM.
- *
- * Return: failure or success.
- */
-int msm_dts_eagle_set_stream_gain(struct audio_client *ac, int lgain, int rgain)
-{
- u32 i, val;
- s32 idx, err = 0;
-
- eagle_vol_dbg("%s: - entry: vol_cmd_cnt = %u, lgain = %i, rgain = %i",
- __func__, _vol_cmd_cnt, lgain, rgain);
-
- if (_depc_size == 0) {
- eagle_vol_dbg("%s: driver cache not initialized", __func__);
- return -EINVAL;
- }
-
- for (i = 0; i < _vol_cmd_cnt; i++) {
- if (_vol_cmds_d[i].d[0] & 0x8000) {
- idx = (sizeof(struct dts_eagle_param_desc)/sizeof(int))
- + (_vol_cmds_d[i].d[0] & 0x3FF);
- val = _fx_logN(((s32)(lgain+rgain)) << 2);
- val = ((long long)val * _log10_10_inv_x20) >> 16;
- _vol_cmds[i][idx] = (s32)clamp((int)(((long long)val *
- _vol_cmds_d[i].d[1]) >> 16),
- _vol_cmds_d[i].d[2],
- _vol_cmds_d[i].d[3]);
- eagle_vol_dbg("%s: loop %u cmd desc found %i, idx = %i. volume info: lgain = %i, rgain = %i, volume = %i (scale %i, min %i, max %i)",
- __func__, i, _vol_cmds_d[i].d[0], idx, lgain,
- rgain, _vol_cmds[i][idx], _vol_cmds_d[i].d[1],
- _vol_cmds_d[i].d[2], _vol_cmds_d[i].d[3]);
- }
- idx = _get_cb_for_dev(_device_primary);
- if (idx < 0) {
- eagle_vol_err("%s: no cache for primary device %i found",
- __func__, _device_primary);
- return -EINVAL;
- }
- val = _c_bl[idx][CBD_OFFSG] + _vol_cmds[i][2];
- /* check for integer overflow */
- if (val > (UINT_MAX - _vol_cmds[i][1]))
- err = -EINVAL;
- if ((err != 0) || ((val + _vol_cmds[i][1]) > _depc_size)) {
- eagle_vol_err("%s: volume size (%u) + offset (%i) out of bounds %i",
- __func__, val, _vol_cmds[i][1], _depc_size);
- return -EINVAL;
- }
- memcpy((void *)&_depc[val], &_vol_cmds[i][4], _vol_cmds[i][1]);
- if (q6asm_dts_eagle_set(ac, _vol_cmds[i][0],
- _vol_cmds[i][1], (void *)&_depc[val], NULL, MPRE))
- eagle_vol_err("%s: loop %u - volume set failed with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i",
- __func__, i, _vol_cmds[i][0], _vol_cmds[i][1],
- _vol_cmds[i][2], _vol_cmds_d[i].d[0],
- _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2],
- _vol_cmds_d[i].d[3], _vol_cmds[i][4]);
- else
- eagle_vol_dbg("%s: loop %u - volume set succeeded with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i",
- __func__, i, _vol_cmds[i][0], _vol_cmds[i][1],
- _vol_cmds[i][2], _vol_cmds_d[i].d[0],
- _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2],
- _vol_cmds_d[i].d[3], _vol_cmds[i][4]);
- }
- return 0;
-}
-
-/**
- * msm_dts_eagle_handle_asm() - Set or Get params from ASM
- * @depd: DTS Eagle Params structure.
- * @buf: Buffer to get queried param value.
- * @for_pre: For premix module or postmix module.
- * @get: Getting param from DSP or setting param.
- * @ac: Set/Get from ASM session associated with this audio client.
- * @po: Out of band memory to set or get postmix params.
- *
- * Set or Get params from modules in ASM session.
- *
- * Return: Return failure if any.
- */
-int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf,
- bool for_pre, bool get, struct audio_client *ac,
- struct param_outband *po)
-{
- struct dts_eagle_param_desc depd_ = {0};
- s32 ret = 0, isALSA = 0, err = 0, i, mod = for_pre ? MPRE : MPST;
- u32 offset;
-
- eagle_asm_dbg("%s: set/get asm", __func__);
-
- /* special handling for ALSA route, to accommodate 64 bit platforms */
- if (depd == NULL) {
- long *arg_ = (long *)buf;
- depd = &depd_;
- depd->id = (u32)*arg_++;
- depd->size = (u32)*arg_++;
- depd->offset = (s32)*arg_++;
- depd->device = (u32)*arg_++;
- buf = (char *)arg_;
- isALSA = 1;
- }
-
- if (depd->size & 1) {
- eagle_asm_err("%s: parameter size %u is not a multiple of 2",
- __func__, depd->size);
- return -EINVAL;
- }
-
- if (get) {
- void *buf_, *buf_m = NULL;
- eagle_asm_dbg("%s: get requested", __func__);
- if (depd->offset == -1) {
- eagle_asm_dbg("%s: get from dsp requested", __func__);
- if (depd->size > 0 && depd->size <= DEPC_MAX_SIZE) {
- buf_ = buf_m = vzalloc(depd->size);
- } else {
- eagle_asm_err("%s: get size %u invalid",
- __func__, depd->size);
- return -EINVAL;
- }
- if (!buf_m) {
- eagle_asm_err("%s: out of memory", __func__);
- return -ENOMEM;
- } else if (q6asm_dts_eagle_get(ac, depd->id,
- depd->size, buf_m,
- po, mod) < 0) {
- eagle_asm_err("%s: asm get failed", __func__);
- ret = -EFAULT;
- goto DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT;
- }
- eagle_asm_dbg("%s: get result: param id 0x%x value %d size %u",
- __func__, depd->id, *(int *)buf_m, depd->size);
- } else {
- s32 tgt = _get_cb_for_dev(depd->device);
- if (tgt < 0) {
- eagle_asm_err("%s: no cache for device %u found",
- __func__, depd->device);
- return -EINVAL;
- }
- offset = _c_bl[tgt][CBD_OFFSG] + depd->offset;
- /* check for integer overflow */
- if (offset > (UINT_MAX - depd->size))
- err = -EINVAL;
- if ((err != 0) || (offset + depd->size) > _depc_size) {
- eagle_asm_err("%s: invalid size %u and/or offset %u",
- __func__, depd->size, offset);
- return -EINVAL;
- }
- buf_ = (u32 *)&_depc[offset];
- }
- if (isALSA) {
- if (depd->size == 2) {
- *(long *)buf = (long)*(__u16 *)buf_;
- eagle_asm_dbg("%s: asm out 16 bit value %li",
- __func__, *(long *)buf);
- } else {
- s32 *pbuf = (s32 *)buf_;
- long *bufl = (long *)buf;
- for (i = 0; i < (depd->size >> 2); i++) {
- *bufl++ = (long)*pbuf++;
- eagle_asm_dbg("%s: asm out value %li",
- __func__, *(bufl-1));
- }
- }
- } else {
- memcpy(buf, buf_, depd->size);
- }
-DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT:
- vfree(buf_m);
- return (int)ret;
- } else {
- s32 tgt = _get_cb_for_dev(depd->device);
- if (tgt < 0) {
- eagle_asm_err("%s: no cache for device %u found",
- __func__, depd->device);
- return -EINVAL;
- }
- offset = _c_bl[tgt][CBD_OFFSG] + depd->offset;
- /* check for integer overflow */
- if (offset > (UINT_MAX - depd->size))
- err = -EINVAL;
- if ((err != 0) || ((offset + depd->size) > _depc_size)) {
- eagle_asm_err("%s: invalid size %u and/or offset %u for parameter (cache is size %u)",
- __func__, depd->size, offset, _depc_size);
- return -EINVAL;
- }
- if (isALSA) {
- if (depd->size == 2) {
- *(__u16 *)&_depc[offset] = (__u16)*(long *)buf;
- eagle_asm_dbg("%s: asm in 16 bit value %li",
- __func__, *(long *)buf);
- } else {
- s32 *pbuf = (s32 *)&_depc[offset];
- long *bufl = (long *)buf;
- for (i = 0; i < (depd->size >> 2); i++) {
- *pbuf++ = (s32)*bufl++;
- eagle_asm_dbg("%s: asm in value %i",
- __func__, *(pbuf-1));
- }
- }
- } else {
- memcpy(&_depc[offset], buf, depd->size);
- }
- eagle_asm_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i",
- __func__, depd->id, depd->size, depd->offset,
- depd->device,
- tgt, offset, *(int *)&_depc[offset]);
- if (q6asm_dts_eagle_set(ac, depd->id, depd->size,
- (void *)&_depc[offset], po, mod))
- eagle_asm_err("%s: q6asm_dts_eagle_set failed with id = 0x%X, size = %u, offset = %d",
- __func__, depd->id, depd->size, depd->offset);
- else
- eagle_asm_dbg("%s: q6asm_dts_eagle_set succeeded with id = 0x%X, size = %u, offset = %d",
- __func__, depd->id, depd->size, depd->offset);
- }
- return (int)ret;
-}
-
-/**
- * msm_dts_eagle_handle_adm() - Set or Get params from ADM
- * @depd: DTS Eagle Params structure used to set or get.
- * @buf: Buffer to get queried param value in NT mode.
- * @for_pre: For premix module or postmix module.
- * @get: Getting param from DSP or setting param.
- *
- * Set or Get params from modules in ADM session.
- *
- * Return: Return failure if any.
- */
-int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf,
- bool for_pre, bool get)
-{
- u32 pid = _get_pid_from_dev(depd->device), cidx;
- s32 ret = 0;
-
- eagle_adm_dbg("%s: set/get adm", __func__);
-
- if (_isNTDevice(depd->device)) {
- eagle_adm_dbg("%s: NT Route detected", __func__);
- ret = msm_dts_eagle_handle_asm(depd, buf, for_pre, get,
- _getNTDeviceAC(), &_po_NT);
- if (ret < 0)
- eagle_adm_err("%s: NT Route set failed with id = 0x%X, size = %u, offset = %i, device = %u",
- __func__, depd->id, depd->size, depd->offset,
- depd->device);
- } else if (get) {
- cidx = adm_validate_and_get_port_index(pid);
- eagle_adm_dbg("%s: get from qdsp requested (port id 0x%X)",
- __func__, pid);
- if (adm_dts_eagle_get(pid, _cidx[cidx], depd->id,
- buf, depd->size) < 0) {
- eagle_adm_err("%s: get from qdsp via adm with port id 0x%X failed",
- __func__, pid);
- return -EFAULT;
- }
- } else if (_is_port_open_and_eagle(pid)) {
- cidx = adm_validate_and_get_port_index(pid);
- eagle_adm_dbg("%s: adm_dts_eagle_set called with id = 0x%X, size = %u, offset = %i, device = %u, port id = %u, copp index = %u",
- __func__, depd->id, depd->size, depd->offset,
- depd->device, pid, cidx);
- ret = adm_dts_eagle_set(pid, _cidx[cidx], depd->id,
- (void *)buf, depd->size);
- if (ret < 0)
- eagle_adm_err("%s: adm_dts_eagle_set failed", __func__);
- else
- eagle_adm_dbg("%s: adm_dts_eagle_set succeeded",
- __func__);
- } else {
- ret = -EINVAL;
- eagle_adm_dbg("%s: port id 0x%X not active or not Eagle",
- __func__, pid);
- }
- return (int)ret;
-}
-
-/**
- * msm_dts_eagle_ioctl() - ioctl handler function
- * @cmd: cmd to handle.
- * @arg: argument to the cmd.
- *
- * Handle DTS Eagle ioctl cmds.
- *
- * Return: Return failure if any.
- */
-int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg)
-{
- s32 ret = 0;
- switch (cmd) {
- case DTS_EAGLE_IOCTL_GET_CACHE_SIZE: {
- eagle_ioctl_info("%s: called with control 0x%X (get param cache size)",
- __func__, cmd);
- if (copy_to_user((void *)arg, &_depc_size,
- sizeof(_depc_size))) {
- eagle_ioctl_err("%s: error writing size", __func__);
- return -EFAULT;
- }
- break;
- }
- case DTS_EAGLE_IOCTL_SET_CACHE_SIZE: {
- u32 size = 0;
- eagle_ioctl_info("%s: called with control 0x%X (allocate param cache)",
- __func__, cmd);
- if (copy_from_user((void *)&size, (void *)arg, sizeof(size))) {
- eagle_ioctl_err("%s: error copying size (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, &size, sizeof(size));
- return -EFAULT;
- } else if (size > DEPC_MAX_SIZE) {
- eagle_ioctl_err("%s: cache size %u not allowed (min 0, max %u)",
- __func__, size, DEPC_MAX_SIZE);
- return -EINVAL;
- }
- if (_depc) {
- eagle_ioctl_dbg("%s: previous param cache of size %u freed",
- __func__, _depc_size);
- _depc_size = 0;
- vfree(_depc);
- _depc = NULL;
- }
- if (size)
- _depc = vzalloc(size);
- else
- eagle_ioctl_dbg("%s: %u bytes requested for param cache, nothing allocated",
- __func__, size);
- if (_depc) {
- eagle_ioctl_dbg("%s: %u bytes allocated for param cache",
- __func__, size);
- _depc_size = size;
- } else {
- eagle_ioctl_err("%s: error allocating param cache (vzalloc failed on %u bytes)",
- __func__, size);
- _depc_size = 0;
- return -ENOMEM;
- }
- break;
- }
- case DTS_EAGLE_IOCTL_GET_PARAM: {
- struct dts_eagle_param_desc depd;
- s32 for_pre = 0, get_from_core = 0, err = 0;
- u32 offset;
- void *buf, *buf_m = NULL;
- eagle_ioctl_info("%s: control 0x%X (get param)",
- __func__, cmd);
- if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) {
- eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, &depd, sizeof(depd));
- return -EFAULT;
- }
- if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) {
- eagle_ioctl_dbg("%s: using for premix", __func__);
- for_pre = 1;
- }
- if (depd.device & DTS_EAGLE_FLAG_IOCTL_GETFROMCORE) {
- eagle_ioctl_dbg("%s: 'get from core' requested",
- __func__);
- get_from_core = 1;
- depd.offset = -1;
- }
- depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK;
- if (depd.offset == -1) {
- if (depd.size > 0 && depd.size <= DEPC_MAX_SIZE) {
- buf = buf_m = vzalloc(depd.size);
- } else {
- eagle_ioctl_err("%s: get size %u invalid",
- __func__, depd.size);
- return -EINVAL;
- }
- if (!buf_m) {
- eagle_ioctl_err("%s: out of memory", __func__);
- return -ENOMEM;
- }
- if (get_from_core)
- ret = core_dts_eagle_get(depd.id, depd.size,
- buf);
- else
- ret = msm_dts_eagle_handle_adm(&depd, buf,
- for_pre, true);
- } else {
- s32 cb = _get_cb_for_dev(depd.device);
- if (cb < 0) {
- eagle_ioctl_err("%s: no cache for device %u found",
- __func__, depd.device);
- return -EINVAL;
- }
- offset = _c_bl[cb][CBD_OFFSG] + depd.offset;
- /* check for integer overflow */
- if (offset > (UINT_MAX - depd.size))
- err = -EINVAL;
- if ((err != 0) ||
- ((offset + depd.size) > _depc_size)) {
- eagle_ioctl_err("%s: invalid size %u and/or offset %u",
- __func__, depd.size, offset);
- return -EINVAL;
- }
- buf = (void *)&_depc[offset];
- }
- if (ret < 0)
- eagle_ioctl_err("%s: error %i getting data", __func__,
- ret);
- else if (copy_to_user((void *)(((char *)arg)+sizeof(depd)),
- buf, depd.size)) {
- eagle_ioctl_err("%s: error copying get data", __func__);
- ret = -EFAULT;
- }
- vfree(buf_m);
- break;
- }
- case DTS_EAGLE_IOCTL_SET_PARAM: {
- struct dts_eagle_param_desc depd;
- s32 just_set_cache = 0, for_pre = 0, err = 0;
- u32 offset;
- s32 tgt;
- eagle_ioctl_info("%s: control 0x%X (set param)",
- __func__, cmd);
- if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) {
- eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, &depd, sizeof(depd));
- return -EFAULT;
- }
- if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) {
- eagle_ioctl_dbg("%s: using for premix", __func__);
- for_pre = 1;
- }
- if (depd.device & DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE) {
- eagle_ioctl_dbg("%s: 'just set cache' requested",
- __func__);
- just_set_cache = 1;
- }
- depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK;
- tgt = _get_cb_for_dev(depd.device);
- if (tgt < 0) {
- eagle_ioctl_err("%s: no cache for device %u found",
- __func__, depd.device);
- return -EINVAL;
- }
- offset = _c_bl[tgt][CBD_OFFSG] + depd.offset;
- /* check for integer overflow */
- if (offset > (UINT_MAX - depd.size))
- err = -EINVAL;
- if ((err != 0) || ((offset + depd.size) > _depc_size)) {
- eagle_ioctl_err("%s: invalid size %u and/or offset %u for parameter (target cache block %i with offset %i, global cache is size %u)",
- __func__, depd.size, offset, tgt,
- _c_bl[tgt][CBD_OFFSG], _depc_size);
- return -EINVAL;
- }
- if (copy_from_user((void *)&_depc[offset],
- (void *)(((char *)arg)+sizeof(depd)),
- depd.size)) {
- eagle_ioctl_err("%s: error copying param to cache (src:%pK, tgt:%pK, size:%u)",
- __func__, ((char *)arg)+sizeof(depd),
- &_depc[offset], depd.size);
- return -EFAULT;
- }
- eagle_ioctl_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i",
- __func__, depd.id, depd.size, depd.offset,
- depd.device, tgt, offset, *(int *)&_depc[offset]);
- if (!just_set_cache) {
- ret = msm_dts_eagle_handle_adm(&depd, &_depc[offset],
- for_pre, false);
- }
- break;
- }
- case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK: {
- u32 b_[CBD_COUNT+1], *b = &b_[1], cb;
- eagle_ioctl_info("%s: with control 0x%X (set param cache block)",
- __func__, cmd);
- if (copy_from_user((void *)b_, (void *)arg, sizeof(b_))) {
- eagle_ioctl_err("%s: error copying cache block data (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, b_, sizeof(b_));
- return -EFAULT;
- }
- cb = b_[0];
- if (cb >= CB_COUNT) {
- eagle_ioctl_err("%s: cache block %u out of range (max %u)",
- __func__, cb, CB_COUNT-1);
- return -EINVAL;
- }
- eagle_ioctl_dbg("%s: cache block %i set: devices 0x%X, global offset %i, offsets 1:%u 2:%u 3:%u, cmds/sizes 0:0x%X %u 1:0x%X %u 2:0x%X %u 3:0x%X %u",
- __func__, cb, _c_bl[cb][CBD_DEV_MASK], _c_bl[cb][CBD_OFFSG],
- _c_bl[cb][CBD_OFFS1], _c_bl[cb][CBD_OFFS2],
- _c_bl[cb][CBD_OFFS3], _c_bl[cb][CBD_CMD0], _c_bl[cb][CBD_SZ0],
- _c_bl[cb][CBD_CMD1], _c_bl[cb][CBD_SZ1], _c_bl[cb][CBD_CMD2],
- _c_bl[cb][CBD_SZ2], _c_bl[cb][CBD_CMD3], _c_bl[cb][CBD_SZ3]);
- if ((b[CBD_OFFSG]+b[CBD_OFFS1]+b[CBD_SZ1]) > _depc_size ||
- (b[CBD_OFFSG]+b[CBD_OFFS2]+b[CBD_SZ2]) > _depc_size ||
- (b[CBD_OFFSG]+b[CBD_OFFS3]+b[CBD_SZ3]) > _depc_size) {
- eagle_ioctl_err("%s: cache block bounds out of range",
- __func__);
- return -EINVAL;
- }
- memcpy(_c_bl[cb], b, sizeof(_c_bl[cb]));
- break;
- }
- case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE: {
- u32 data[2];
- eagle_ioctl_dbg("%s: with control 0x%X (set active device)",
- __func__, cmd);
- if (copy_from_user((void *)data, (void *)arg, sizeof(data))) {
- eagle_ioctl_err("%s: error copying active device data (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, data, sizeof(data));
- return -EFAULT;
- }
- if (data[1] != 0) {
- _device_primary = data[0];
- eagle_ioctl_dbg("%s: primary device %i", __func__,
- data[0]);
- } else {
- _device_all = data[0];
- eagle_ioctl_dbg("%s: all devices 0x%X", __func__,
- data[0]);
- }
- break;
- }
- case DTS_EAGLE_IOCTL_GET_LICENSE: {
- u32 target = 0, size = 0;
- s32 size_only;
- eagle_ioctl_dbg("%s: with control 0x%X (get license)",
- __func__, cmd);
- if (copy_from_user((void *)&target, (void *)arg,
- sizeof(target))) {
- eagle_ioctl_err("%s: error reading license index. (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, &target, sizeof(target));
- return -EFAULT;
- }
- size_only = target & (1<<31) ? 1 : 0;
- target &= 0x7FFFFFFF;
- if (target >= SEC_BLOB_MAX_CNT) {
- eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)",
- __func__, target, SEC_BLOB_MAX_CNT);
- return -EINVAL;
- }
- if (_sec_blob[target] == NULL) {
- eagle_ioctl_err("%s: license index %u never initialized",
- __func__, target);
- return -EINVAL;
- }
- size = ((u32 *)_sec_blob[target])[0];
- if ((size == 0) || (size > SEC_BLOB_MAX_SIZE)) {
- eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)",
- __func__, size, target, SEC_BLOB_MAX_SIZE);
- return -EINVAL;
- }
- if (size_only) {
- eagle_ioctl_dbg("%s: reporting size of license data only",
- __func__);
- if (copy_to_user((void *)(((char *)arg)+sizeof(target)),
- (void *)&size, sizeof(size))) {
- eagle_ioctl_err("%s: error copying license size",
- __func__);
- return -EFAULT;
- }
- } else if (copy_to_user((void *)(((char *)arg)+sizeof(target)),
- (void *)&(((s32 *)_sec_blob[target])[1]), size)) {
- eagle_ioctl_err("%s: error copying license data",
- __func__);
- return -EFAULT;
- } else
- eagle_ioctl_info("%s: license file %u bytes long from license index %u returned to user",
- __func__, size, target);
- break;
- }
- case DTS_EAGLE_IOCTL_SET_LICENSE: {
- u32 target[2] = {0, 0};
- eagle_ioctl_dbg("%s: control 0x%X (set license)", __func__,
- cmd);
- if (copy_from_user((void *)target, (void *)arg,
- sizeof(target))) {
- eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, target, sizeof(target));
- return -EFAULT;
- }
- if (target[0] >= SEC_BLOB_MAX_CNT) {
- eagle_ioctl_err("%s: license index %u out of bounds (max index is %u)",
- __func__, target[0], SEC_BLOB_MAX_CNT-1);
- return -EINVAL;
- }
- if (target[1] == 0) {
- eagle_ioctl_dbg("%s: request to free license index %u",
- __func__, target[0]);
- kfree(_sec_blob[target[0]]);
- _sec_blob[target[0]] = NULL;
- break;
- }
- if ((target[1] == 0) || (target[1] >= SEC_BLOB_MAX_SIZE)) {
- eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)",
- __func__, target[1], target[0],
- SEC_BLOB_MAX_SIZE);
- return -EINVAL;
- }
- if (_sec_blob[target[0]] != NULL) {
- if (((u32 *)_sec_blob[target[0]])[1] != target[1]) {
- eagle_ioctl_dbg("%s: request new size for already allocated license index %u",
- __func__, target[0]);
- }
- kfree(_sec_blob[target[0]]);
- _sec_blob[target[0]] = NULL;
- }
- eagle_ioctl_dbg("%s: allocating %u bytes for license index %u",
- __func__, target[1], target[0]);
- _sec_blob[target[0]] = kzalloc(target[1] + 4, GFP_KERNEL);
- if (!_sec_blob[target[0]]) {
- eagle_ioctl_err("%s: error allocating license index %u (kzalloc failed on %u bytes)",
- __func__, target[0], target[1]);
- return -ENOMEM;
- }
- ((u32 *)_sec_blob[target[0]])[0] = target[1];
- if (copy_from_user(
- (void *)&(((u32 *)_sec_blob[target[0]])[1]),
- (void *)(((char *)arg)+sizeof(target)),
- target[1])) {
- eagle_ioctl_err("%s: error copying license to index %u, size %u (src:%pK, tgt:%pK, size:%u)",
- __func__, target[0], target[1],
- ((char *)arg)+sizeof(target),
- &(((u32 *)_sec_blob[target[0]])[1]),
- target[1]);
- return -EFAULT;
- } else
- eagle_ioctl_info("%s: license file %u bytes long copied to index license index %u",
- __func__, target[1], target[0]);
- break;
- }
- case DTS_EAGLE_IOCTL_SEND_LICENSE: {
- u32 target = 0;
- eagle_ioctl_dbg("%s: control 0x%X (send license)", __func__,
- cmd);
- if (copy_from_user((void *)&target, (void *)arg,
- sizeof(target))) {
- eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, &target, sizeof(target));
- return -EFAULT;
- }
- if (target >= SEC_BLOB_MAX_CNT) {
- eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)",
- __func__, target, SEC_BLOB_MAX_CNT-1);
- return -EINVAL;
- }
- if (!_sec_blob[target] ||
- ((u32 *)_sec_blob[target])[0] == 0) {
- eagle_ioctl_err("%s: license index %u is invalid",
- __func__, target);
- return -EINVAL;
- }
- if (core_dts_eagle_set(((s32 *)_sec_blob[target])[0],
- (char *)&((s32 *)_sec_blob[target])[1]) < 0)
- eagle_ioctl_err("%s: core_dts_eagle_set failed with id = %u",
- __func__, target);
- else
- eagle_ioctl_info("%s: core_dts_eagle_set succeeded with id = %u",
- __func__, target);
- break;
- }
- case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS: {
- s32 spec = 0;
- eagle_ioctl_info("%s: control 0x%X (set volume commands)",
- __func__, cmd);
- if (copy_from_user((void *)&spec, (void *)arg,
- sizeof(spec))) {
- eagle_ioctl_err("%s: error reading volume command specifier (src:%pK, tgt:%pK, size:%zu)",
- __func__, (void *)arg, &spec, sizeof(spec));
- return -EFAULT;
- }
- if (spec & 0x80000000) {
- u32 idx = (spec & 0x0000F000) >> 12;
- s32 size = spec & 0x00000FFF;
- eagle_ioctl_dbg("%s: setting volume command %i size: %i",
- __func__, idx, size);
- if (idx >= _vol_cmd_cnt) {
- eagle_ioctl_err("%s: volume command index %u out of bounds (only %u allocated)",
- __func__, idx, _vol_cmd_cnt);
- return -EINVAL;
- }
- if (_volume_cmds_alloc2(idx, size) < 0) {
- eagle_ioctl_err("%s: error allocating memory for volume controls",
- __func__);
- return -ENOMEM;
- }
- if (copy_from_user((void *)&_vol_cmds_d[idx],
- (void *)(((char *)arg) + sizeof(int)),
- sizeof(struct vol_cmds_d))) {
- eagle_ioctl_err("%s: error reading volume command descriptor (src:%pK, tgt:%pK, size:%zu)",
- __func__, ((char *)arg) + sizeof(int),
- &_vol_cmds_d[idx],
- sizeof(struct vol_cmds_d));
- return -EFAULT;
- }
- eagle_ioctl_dbg("%s: setting volume command %i spec (size %zu): %i %i %i %i",
- __func__, idx, sizeof(struct vol_cmds_d),
- _vol_cmds_d[idx].d[0], _vol_cmds_d[idx].d[1],
- _vol_cmds_d[idx].d[2], _vol_cmds_d[idx].d[3]);
- if (copy_from_user((void *)_vol_cmds[idx],
- (void *)(((char *)arg) + (sizeof(int) +
- sizeof(struct vol_cmds_d))), size)) {
- eagle_ioctl_err("%s: error reading volume command string (src:%pK, tgt:%pK, size:%i)",
- __func__, ((char *)arg) + (sizeof(int) +
- sizeof(struct vol_cmds_d)),
- _vol_cmds[idx], size);
- return -EFAULT;
- }
- } else {
- eagle_ioctl_dbg("%s: setting volume command size",
- __func__);
- if (spec < 0 || spec > VOL_CMD_CNT_MAX) {
- eagle_ioctl_err("%s: volume command count %i out of bounds (min 0, max %i)",
- __func__, spec, VOL_CMD_CNT_MAX);
- return -EINVAL;
- } else if (spec == 0) {
- eagle_ioctl_dbg("%s: request to free volume commands",
- __func__);
- _volume_cmds_free();
- break;
- }
- eagle_ioctl_dbg("%s: setting volume command size requested = %i",
- __func__, spec);
- if (_volume_cmds_alloc1(spec) < 0) {
- eagle_ioctl_err("%s: error allocating memory for volume controls",
- __func__);
- return -ENOMEM;
- }
- }
- break;
- }
- default: {
- eagle_ioctl_err("%s: control 0x%X (invalid control)",
- __func__, cmd);
- ret = -EINVAL;
- }
- }
- return (int)ret;
-}
-
-/**
- * msm_dts_eagle_compat_ioctl() - To handle 32bit to 64bit ioctl compatibility
- * @cmd: cmd to handle.
- * @arg: argument to the cmd.
- *
- * Handle DTS Eagle ioctl cmds from 32bit userspace.
- *
- * Return: Return failure if any.
- */
-#ifdef CONFIG_COMPAT
-int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32:
- cmd = DTS_EAGLE_IOCTL_GET_CACHE_SIZE;
- break;
- case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32:
- cmd = DTS_EAGLE_IOCTL_SET_CACHE_SIZE;
- break;
- case DTS_EAGLE_IOCTL_GET_PARAM32:
- cmd = DTS_EAGLE_IOCTL_GET_PARAM;
- break;
- case DTS_EAGLE_IOCTL_SET_PARAM32:
- cmd = DTS_EAGLE_IOCTL_SET_PARAM;
- break;
- case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32:
- cmd = DTS_EAGLE_IOCTL_SET_CACHE_BLOCK;
- break;
- case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32:
- cmd = DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE;
- break;
- case DTS_EAGLE_IOCTL_GET_LICENSE32:
- cmd = DTS_EAGLE_IOCTL_GET_LICENSE;
- break;
- case DTS_EAGLE_IOCTL_SET_LICENSE32:
- cmd = DTS_EAGLE_IOCTL_SET_LICENSE;
- break;
- case DTS_EAGLE_IOCTL_SEND_LICENSE32:
- cmd = DTS_EAGLE_IOCTL_SEND_LICENSE;
- break;
- case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32:
- cmd = DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS;
- break;
- default:
- break;
- }
- return msm_dts_eagle_ioctl(cmd, arg);
-}
-#endif
-/**
- * msm_dts_eagle_init_pre() - Initialize DTS premix module
- * @ac: Initialize premix module in the ASM session.
- *
- * Initialize DTS premix module on provided ASM session
- *
- * Return: Return failure if any.
- */
-int msm_dts_eagle_init_pre(struct audio_client *ac)
-{
- return msm_dts_eagle_enable_asm(ac, _is_hpx_enabled,
- AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
-}
-
-/**
- * msm_dts_eagle_deinit_pre() - Deinitialize DTS premix module
- * @ac: Deinitialize premix module in the ASM session.
- *
- * Deinitialize DTS premix module on provided ASM session
- *
- * Return: Currently does nothing so 0.
- */
-int msm_dts_eagle_deinit_pre(struct audio_client *ac)
-{
- return 0;
-}
-
-/**
- * msm_dts_eagle_init_post() - Initialize DTS postmix module
- * @port_id: Port id for the ADM session.
- * @copp_idx: Copp idx for the ADM session.
- *
- * Initialize DTS postmix module on ADM session
- *
- * Return: Return failure if any.
- */
-int msm_dts_eagle_init_post(int port_id, int copp_idx)
-{
- return msm_dts_eagle_enable_adm(port_id, copp_idx, _is_hpx_enabled);
-}
-
-/**
- * msm_dts_eagle_deinit_post() - Deinitialize DTS postmix module
- * @port_id: Port id for the ADM session.
- * @topology: Topology in use.
- *
- * Deinitialize DTS postmix module on ADM session
- *
- * Return: Currently does nothing so 0.
- */
-int msm_dts_eagle_deinit_post(int port_id, int topology)
-{
- return 0;
-}
-
-/**
- * msm_dts_eagle_init_master_module() - Initialize both DTS modules
- * @ac: Initialize modules in the ASM session.
- *
- * Initialize DTS modules on ASM session
- *
- * Return: Success.
- */
-int msm_dts_eagle_init_master_module(struct audio_client *ac)
-{
- _set_audioclient(ac);
- msm_dts_eagle_enable_asm(ac, _is_hpx_enabled,
- AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
- msm_dts_eagle_enable_asm(ac, _is_hpx_enabled,
- AUDPROC_MODULE_ID_DTS_HPX_POSTMIX);
- return 0;
-}
-
-/**
- * msm_dts_eagle_deinit_master_module() - Deinitialize both DTS modules
- * @ac: Deinitialize modules in the ASM session.
- *
- * Deinitialize DTS modules on ASM session
- *
- * Return: Success.
- */
-int msm_dts_eagle_deinit_master_module(struct audio_client *ac)
-{
- msm_dts_eagle_deinit_pre(ac);
- msm_dts_eagle_deinit_post(-1, 0);
- _clear_audioclient();
- return 0;
-}
-
-/**
- * msm_dts_eagle_is_hpx_on() - Check if HPX effects are On
- *
- * Check if HPX effects are On
- *
- * Return: On/Off.
- */
-int msm_dts_eagle_is_hpx_on(void)
-{
- return _is_hpx_enabled;
-}
-
-/**
- * msm_dts_eagle_pcm_new() - Create hwdep node
- * @runtime: snd_soc_pcm_runtime structure.
- *
- * Create hwdep node
- *
- * Return: Success.
- */
-int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime)
-{
- if (!_ref_cnt++) {
- _init_cb_descs();
- _reg_ion_mem();
- }
- return 0;
-}
-
-/**
- * msm_dts_eagle_pcm_free() - remove hwdep node
- * @runtime: snd_soc_pcm_runtime structure.
- *
- * Remove hwdep node
- *
- * Return: void.
- */
-void msm_dts_eagle_pcm_free(struct snd_pcm *pcm)
-{
- if (!--_ref_cnt)
- _unreg_ion_mem();
- vfree(_depc);
-}
-
-MODULE_DESCRIPTION("DTS EAGLE platform driver");
-MODULE_LICENSE("GPL v2");
-/* Copyright (c) 2012-2014, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 2016-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
#include <sound/control.h>
#include <sound/q6adm-v2.h>
#include <sound/asound.h>
-#include <sound/msm-dts-eagle.h>
#include "msm-dts-srs-tm-config.h"
#include "msm-pcm-routing-v2.h"
}
size = sizeof(*user) + userarg32.payload_size;
- user = kmalloc(size, GFP_KERNEL);
+ user = kzalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
err = -EFAULT;
}
if (!err) {
- user32 = kmalloc(size, GFP_KERNEL);
+ user32 = kzalloc(size, GFP_KERNEL);
if (!user32) {
dev_err(rtd->dev,
"%s: Allocation event user status size %d\n",
}
size = sizeof(*user) + userarg32.payload_size;
- user = kmalloc(size, GFP_KERNEL);
+ user = kzalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
err = -EFAULT;
}
if (!err) {
- user32 = kmalloc(size, GFP_KERNEL);
+ user32 = kzalloc(size, GFP_KERNEL);
if (!user32) {
dev_err(rtd->dev,
"%s: Allocation event user status size %d\n",
size = sizeof(struct snd_lsm_event_status) +
userarg.payload_size;
- user = kmalloc(size, GFP_KERNEL);
+ user = kzalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
size = sizeof(struct snd_lsm_event_status_v3) +
userarg.payload_size;
- user = kmalloc(size, GFP_KERNEL);
+ user = kzalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
-/* Copyright (c) 2014, 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
#include <linux/module.h>
#include <sound/hwdep.h>
#include <sound/devdep_params.h>
-#include <sound/msm-dts-eagle.h>
-
#include "msm-pcm-routing-devdep.h"
#include "msm-ds2-dap-config.h"
case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER:
ret = msm_ds2_dap_ioctl(hw, file, cmd, argp);
break;
- case DTS_EAGLE_IOCTL_GET_CACHE_SIZE:
- case DTS_EAGLE_IOCTL_SET_CACHE_SIZE:
- case DTS_EAGLE_IOCTL_GET_PARAM:
- case DTS_EAGLE_IOCTL_SET_PARAM:
- case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK:
- case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE:
- case DTS_EAGLE_IOCTL_GET_LICENSE:
- case DTS_EAGLE_IOCTL_SET_LICENSE:
- case DTS_EAGLE_IOCTL_SEND_LICENSE:
- case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS:
- ret = msm_dts_eagle_ioctl(cmd, arg);
- if (ret == -EPERM) {
- pr_err("%s called with invalid control 0x%X\n",
- __func__, cmd);
- ret = -EINVAL;
- }
- break;
default:
pr_err("%s called with invalid control 0x%X\n", __func__, cmd);
ret = -EINVAL;
void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm)
{
pr_debug("%s\n", __func__);
- msm_dts_eagle_pcm_free(pcm);
}
#ifdef CONFIG_COMPAT
case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32:
ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp);
break;
- case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32:
- case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32:
- case DTS_EAGLE_IOCTL_GET_PARAM32:
- case DTS_EAGLE_IOCTL_SET_PARAM32:
- case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32:
- case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32:
- case DTS_EAGLE_IOCTL_GET_LICENSE32:
- case DTS_EAGLE_IOCTL_SET_LICENSE32:
- case DTS_EAGLE_IOCTL_SEND_LICENSE32:
- case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32:
- ret = msm_dts_eagle_compat_ioctl(cmd, arg);
- if (ret == -EPERM) {
- pr_err("%s called with invalid control 0x%X\n",
- __func__, cmd);
- ret = -EINVAL;
- }
- break;
default:
pr_err("%s called with invalid control 0x%X\n", __func__, cmd);
ret = -EINVAL;
#ifdef CONFIG_COMPAT
hwdep->ops.ioctl_compat = msm_pcm_routing_hwdep_compat_ioctl;
#endif
- return msm_dts_eagle_pcm_new(runtime);
+ return rc;
}
#endif
#include <sound/pcm_params.h>
#include <sound/q6core.h>
#include <sound/audio_cal_utils.h>
-#include <sound/msm-dts-eagle.h>
#include <sound/audio_effects.h>
#include <sound/hwdep.h>
__func__, topology, port_id, rc);
}
break;
- case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX:
- pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__);
- msm_dts_eagle_init_post(port_id, copp_idx);
- break;
case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE:
pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__);
rc = msm_qti_pp_asphere_init(port_id, copp_idx);
msm_dolby_dap_deinit(port_id);
}
break;
- case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX:
- pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__);
- msm_dts_eagle_deinit_post(port_id, topology);
- break;
case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE:
pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__);
msm_qti_pp_asphere_deinit(port_id);
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new usb_rx_port_mixer_controls[] = {
SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new pri_tdm_rx_0_port_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = {
SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new lsm1_mixer_controls[] = {
{"PRI_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"PRI_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"PRI_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"PRI_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"},
{"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"SEC_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"SEC_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SEC_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"},
{"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"TERT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"TERT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"TERT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"TERT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Port Mixer"},
{"QUAT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"QUAT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"QUAT_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"QUAT_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"},
/* Backend Enablement */
bedai->active = 0;
bedai->sample_rate = 0;
bedai->channel = 0;
+ bedai->passthr_mode = LEGACY_PCM;
mutex_unlock(&routing_lock);
return 0;
msm_routing_be_dai_name_table_mixer_controls,
ARRAY_SIZE(msm_routing_be_dai_name_table_mixer_controls));
- msm_dts_eagle_add_controls(platform);
-
snd_soc_add_platform_controls(platform, msm_source_tracking_controls,
ARRAY_SIZE(msm_source_tracking_controls));
snd_soc_add_platform_controls(platform, adm_channel_config_controls,
#include <sound/q6afe-v2.h>
#include <sound/audio_cal_utils.h>
#include <sound/asound.h>
-#include <sound/msm-dts-eagle.h>
#include "msm-dts-srs-tm-config.h"
#include <sound/adsp_err.h>
return idx;
}
-int adm_dts_eagle_set(int port_id, int copp_idx, int param_id,
- void *data, uint32_t size)
-{
- struct adm_cmd_set_pp_params_v5 admp;
- int p_idx, ret = 0, *ob_params;
-
- pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X size %u\n",
- __func__, port_id, copp_idx, param_id, size);
-
- port_id = afe_convert_virtual_to_portid(port_id);
- p_idx = adm_validate_and_get_port_index(port_id);
- pr_debug("DTS_EAGLE_ADM: %s - after lookup, port id %i, port idx %i\n",
- __func__, port_id, p_idx);
-
- if (p_idx < 0) {
- pr_err("DTS_EAGLE_ADM: %s: invalid port index 0x%x, port id 0x%x\n",
- __func__, p_idx, port_id);
- return -EINVAL;
- }
-
- if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
- pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__,
- copp_idx);
- return -EINVAL;
- }
-
- ob_params = (int *)this_adm.outband_memmap.kvaddr;
- if (ob_params == NULL) {
- pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?\n",
- __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
- /* check for integer overflow */
- if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
- ret = -EINVAL;
- if ((ret < 0) ||
- (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) {
- pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n",
- __func__, this_adm.outband_memmap.size,
- size + APR_CMD_OB_HDR_SZ);
- ret = -EINVAL;
- goto fail_cmd;
- }
- *ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX;
- *ob_params++ = param_id;
- *ob_params++ = size;
- memcpy(ob_params, data, size);
-
- admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
- admp.hdr.pkt_size = sizeof(admp);
- admp.hdr.src_svc = APR_SVC_ADM;
- admp.hdr.src_domain = APR_DOMAIN_APPS;
- admp.hdr.src_port = port_id;
- admp.hdr.dest_svc = APR_SVC_ADM;
- admp.hdr.dest_domain = APR_DOMAIN_ADSP;
- admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]);
- admp.hdr.token = p_idx << 16 | copp_idx;
- admp.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
- admp.payload_addr_lsw = lower_32_bits(this_adm.outband_memmap.paddr);
- admp.payload_addr_msw = msm_audio_populate_upper_32_bits(
- this_adm.outband_memmap.paddr);
- admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[
- ADM_DTS_EAGLE]);
- admp.payload_size = size + sizeof(struct adm_param_data_v5);
-
- pr_debug("DTS_EAGLE_ADM: %s - Command was sent now check Q6 - port id = %d, size %d, module id %x, param id %x.\n",
- __func__, admp.hdr.dest_port,
- admp.payload_size, AUDPROC_MODULE_ID_DTS_HPX_POSTMIX,
- param_id);
- atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1);
- ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp);
- if (ret < 0) {
- pr_err("DTS_EAGLE_ADM: %s - ADM enable for port %d failed\n",
- __func__, port_id);
- ret = -EINVAL;
- goto fail_cmd;
- }
- ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx],
- atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx]) >= 0,
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("DTS_EAGLE_ADM: %s - set params timed out port = %d\n",
- __func__, port_id);
- ret = -EINVAL;
- } else if (atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx]) > 0) {
- pr_err("%s: DSP returned error[%s]\n",
- __func__, adsp_err_get_err_str(
- atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx])));
- ret = adsp_err_get_lnx_err_code(
- atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx]));
- } else {
- ret = 0;
- }
-
-fail_cmd:
- return ret;
-}
-
-int adm_dts_eagle_get(int port_id, int copp_idx, int param_id,
- void *data, uint32_t size)
-{
- struct adm_cmd_get_pp_params_v5 admp;
- int p_idx, ret = 0, *ob_params;
- uint32_t orig_size = size;
- pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X\n",
- __func__, port_id, copp_idx, param_id);
-
- port_id = afe_convert_virtual_to_portid(port_id);
- p_idx = adm_validate_and_get_port_index(port_id);
- if (p_idx < 0) {
- pr_err("DTS_EAGLE_ADM: %s - invalid port index %i, port id %i, copp idx %i\n",
- __func__, p_idx, port_id, copp_idx);
- return -EINVAL;
- }
-
- if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
- pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__,
- copp_idx);
- return -EINVAL;
- }
-
- if ((size == 0) || !data) {
- pr_err("DTS_EAGLE_ADM: %s - invalid size %u or pointer %pK.\n",
- __func__, size, data);
- return -EINVAL;
- }
-
- size = (size+3) & 0xFFFFFFFC;
-
- ob_params = (int *)(this_adm.outband_memmap.kvaddr);
- if (ob_params == NULL) {
- pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?",
- __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
- /* check for integer overflow */
- if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
- ret = -EINVAL;
- if ((ret < 0) ||
- (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) {
- pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n",
- __func__, this_adm.outband_memmap.size,
- size + APR_CMD_OB_HDR_SZ);
- ret = -EINVAL;
- goto fail_cmd;
- }
- *ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX;
- *ob_params++ = param_id;
- *ob_params++ = size;
-
- admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
- admp.hdr.pkt_size = sizeof(admp);
- admp.hdr.src_svc = APR_SVC_ADM;
- admp.hdr.src_domain = APR_DOMAIN_APPS;
- admp.hdr.src_port = port_id;
- admp.hdr.dest_svc = APR_SVC_ADM;
- admp.hdr.dest_domain = APR_DOMAIN_ADSP;
- admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]);
- admp.hdr.token = p_idx << 16 | copp_idx;
- admp.hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5;
- admp.data_payload_addr_lsw =
- lower_32_bits(this_adm.outband_memmap.paddr);
- admp.data_payload_addr_msw =
- msm_audio_populate_upper_32_bits(
- this_adm.outband_memmap.paddr);
- admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[
- ADM_DTS_EAGLE]);
- admp.module_id = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX;
- admp.param_id = param_id;
- admp.param_max_size = size + sizeof(struct adm_param_data_v5);
- admp.reserved = 0;
-
- atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1);
-
- ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp);
- if (ret < 0) {
- pr_err("DTS_EAGLE_ADM: %s - Failed to get EAGLE Params on port %d\n",
- __func__, port_id);
- ret = -EINVAL;
- goto fail_cmd;
- }
- ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx],
- atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx]) >= 0,
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("DTS_EAGLE_ADM: %s - EAGLE get params timed out port = %d\n",
- __func__, port_id);
- ret = -EINVAL;
- goto fail_cmd;
- } else if (atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx]) > 0) {
- pr_err("%s: DSP returned error[%s]\n",
- __func__, adsp_err_get_err_str(
- atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx])));
- ret = adsp_err_get_lnx_err_code(
- atomic_read(&this_adm.copp.stat
- [p_idx][copp_idx]));
- goto fail_cmd;
- }
-
- memcpy(data, ob_params, orig_size);
- ret = 0;
-fail_cmd:
- return ret;
-}
-
int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id,
void *srs_params)
{
__func__, port_id, path, rate, channel_mode, perf_mode,
topology);
- /* For DTS EAGLE only, force 24 bit */
- if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) &&
- (perf_mode == LEGACY_PCM_MODE)) {
- bit_width = 24;
- pr_debug("%s: Force open adm in 24-bit for DTS HPX topology 0x%x\n",
- __func__, topology);
- }
port_id = q6audio_convert_virtual_to_portid(port_id);
port_idx = adm_validate_and_get_port_index(port_id);
if (port_idx < 0) {
flags = ADM_ULL_POST_PROCESSING_DEVICE_SESSION;
if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) ||
(topology == DS2_ADM_COPP_TOPOLOGY_ID) ||
- (topology == SRS_TRUMEDIA_TOPOLOGY_ID) ||
- (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX))
+ (topology == SRS_TRUMEDIA_TOPOLOGY_ID))
topology = DEFAULT_COPP_TOPOLOGY;
} else if (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) {
flags = ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION;
flags = ADM_LOW_LATENCY_DEVICE_SESSION;
if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) ||
(topology == DS2_ADM_COPP_TOPOLOGY_ID) ||
- (topology == SRS_TRUMEDIA_TOPOLOGY_ID) ||
- (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX))
+ (topology == SRS_TRUMEDIA_TOPOLOGY_ID))
topology = DEFAULT_COPP_TOPOLOGY;
} else {
if (path == ADM_PATH_COMPRESSED_RX)
(uint32_t)this_adm.outband_memmap.size);
}
}
- if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) &&
- (perf_mode == LEGACY_PCM_MODE)) {
- int res = 0;
- atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE);
- msm_dts_ion_memmap(&this_adm.outband_memmap);
- res = adm_memory_map_regions(
- &this_adm.outband_memmap.paddr,
- 0,
- (uint32_t *)&this_adm.outband_memmap.size,
- 1);
- if (res < 0)
- pr_err("%s: DTS_EAGLE mmap did not work!",
- __func__);
- }
- memset(&open, 0, sizeof(struct adm_cmd_device_open_v5));
open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
APR_PKT_VER);
__func__, port_idx, copp_idx);
continue;
}
- if (atomic_read(
- &this_adm.copp.topology[port_idx][copp_idx]) ==
- ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)
- continue;
rtac_add_adm_device(payload_map.port_id[i],
atomic_read(&this_adm.copp.id
[port_idx][copp_idx]),
}
}
- if ((perf_mode == LEGACY_PCM_MODE) &&
- (this_adm.outband_memmap.paddr != 0) &&
- (atomic_read(
- &this_adm.copp.topology[port_idx][copp_idx]) ==
- ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) {
- atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE);
- ret = adm_memory_unmap_regions();
- if (ret < 0) {
- pr_err("%s: adm mem unmmap err %d",
- __func__, ret);
- } else {
- atomic_set(&this_adm.mem_map_handles
- [ADM_DTS_EAGLE], 0);
- }
- }
if ((afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) &&
this_adm.sourceTrackingData.memmap.paddr) {
{NULL, NULL, NULL, NULL, NULL, NULL} },
{NULL, NULL, cal_utils_match_buf_num} },
- {{DTS_EAGLE_CAL_TYPE,
- {NULL, NULL, NULL, NULL, NULL, NULL} },
- {NULL, NULL, cal_utils_match_buf_num} },
-
{{SRS_TRUMEDIA_CAL_TYPE,
{NULL, NULL, NULL, NULL, NULL, NULL} },
{NULL, NULL, cal_utils_match_buf_num} },
#include <sound/q6asm-v2.h>
#include <sound/q6audio-v2.h>
#include <sound/audio_cal_utils.h>
-#include <sound/msm-dts-eagle.h>
#include <sound/adsp_err.h>
+#include <sound/compress_params.h>
#define TRUE 0x01
#define FALSE 0x00
}
rtac_set_asm_handle(ac->session, NULL);
- apr_deregister(ac->apr2);
- apr_deregister(ac->apr);
- q6asm_mmap_apr_dereg();
- ac->apr2 = NULL;
- ac->apr = NULL;
- ac->mmap_apr = NULL;
+ if (!atomic_read(&ac->reset)) {
+ apr_deregister(ac->apr2);
+ apr_deregister(ac->apr);
+ q6asm_mmap_apr_dereg();
+ ac->apr2 = NULL;
+ ac->apr = NULL;
+ ac->mmap_apr = NULL;
+ }
q6asm_session_free(ac);
pr_debug("%s: APR De-Register\n", __func__);
}
pr_debug("%s: Clearing custom topology\n", __func__);
}
- this_mmap.apr = NULL;
cal_utils_clear_cal_block_q6maps(ASM_MAX_CAL_TYPES, cal_data);
common_client.mmap_apr = NULL;
if (data->opcode == RESET_EVENTS) {
mutex_lock(&ac->cmd_lock);
atomic_set(&ac->reset, 1);
- if (ac->apr == NULL)
+ if (ac->apr == NULL) {
ac->apr = ac->apr2;
+ ac->apr2 = NULL;
+ }
pr_debug("%s: Reset event is received: %d %d apr[%pK]\n",
__func__,
data->reset_event, data->reset_proc, ac->apr);
open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX;
open.preprocopo_id = q6asm_get_asm_topology_cal();
- if ((open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
- (open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS))
- open.preprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE;
open.bits_per_sample = bits_per_sample;
open.mode_flags = 0x0;
open.bits_per_sample = bits_per_sample;
open.postprocopo_id = q6asm_get_asm_topology_cal();
- if ((ac->perf_mode != LEGACY_PCM_MODE) &&
- ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
- (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS)))
+ if (ac->perf_mode != LEGACY_PCM_MODE)
open.postprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE;
- /* For DTS EAGLE only, force 24 bit */
- if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
- (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS))
- open.bits_per_sample = 24;
-
pr_debug("%s: perf_mode %d asm_topology 0x%x bps %d\n", __func__,
ac->perf_mode, open.postprocopo_id, open.bits_per_sample);
ac->topology = open.postprocopo_id;
ac->app_type = q6asm_get_asm_app_type_cal();
- /* For DTS EAGLE only, force 24 bit */
- if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
- (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER))
- open.bits_per_sample = 24;
switch (wr_format) {
case FORMAT_LINEAR_PCM:
return rc;
}
-int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size,
- void *data, struct param_outband *po, int m_id)
-{
- int rc = 0, *ob_params = NULL;
- uint32_t sz = sizeof(struct asm_dts_eagle_param) + (po ? 0 : size);
- struct asm_dts_eagle_param *ad;
-
- if (!ac || ac->apr == NULL || (size == 0) || !data) {
- pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK.\n",
- __func__, size, data);
- return -EINVAL;
- }
-
- ad = kzalloc(sz, GFP_KERNEL);
- if (!ad) {
- pr_err("DTS_EAGLE_ASM - %s: error allocating mem of size %u\n",
- __func__, sz);
- return -ENOMEM;
- }
- pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n",
- __func__, ac, param_id, size, data, m_id);
- q6asm_add_hdr_async(ac, &ad->hdr, sz, 1);
- ad->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
- ad->param.data_payload_addr_lsw = 0;
- ad->param.data_payload_addr_msw = 0;
-
- ad->param.mem_map_handle = 0;
- ad->param.data_payload_size = size +
- sizeof(struct asm_stream_param_data_v2);
- ad->data.module_id = m_id;
- ad->data.param_id = param_id;
- ad->data.param_size = size;
- ad->data.reserved = 0;
- atomic_set(&ac->cmd_state_pp, -1);
-
- if (po) {
- struct list_head *ptr, *next;
- struct asm_buffer_node *node;
- pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n",
- __func__, po->kvaddr, (long)po->paddr);
- ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr);
- ad->param.data_payload_addr_msw =
- msm_audio_populate_upper_32_bits(po->paddr);
- list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
- node = list_entry(ptr, struct asm_buffer_node, list);
- if (node->buf_phys_addr == po->paddr) {
- ad->param.mem_map_handle = node->mmap_hdl;
- break;
- }
- }
- if (ad->param.mem_map_handle == 0) {
- pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n",
- __func__);
- rc = -EINVAL;
- goto fail_cmd;
- }
- /* check for integer overflow */
- if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
- rc = -EINVAL;
- if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) {
- pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n",
- __func__, po->size, size + APR_CMD_OB_HDR_SZ);
- rc = -EINVAL;
- goto fail_cmd;
- }
- ob_params = (int *)po->kvaddr;
- *ob_params++ = m_id;
- *ob_params++ = param_id;
- *ob_params++ = size;
- memcpy(ob_params, data, size);
- } else {
- pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__);
- memcpy(((char *)ad) + sizeof(struct asm_dts_eagle_param),
- data, size);
- }
- rc = apr_send_pkt(ac->apr, (uint32_t *)ad);
- if (rc < 0) {
- pr_err("DTS_EAGLE_ASM - %s: set-params send failed paramid[0x%x]\n",
- __func__, ad->data.param_id);
- rc = -EINVAL;
- goto fail_cmd;
- }
-
- rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state_pp) >= 0), 1*HZ);
- if (!rc) {
- pr_err("DTS_EAGLE_ASM - %s: timeout, set-params paramid[0x%x]\n",
- __func__, ad->data.param_id);
- rc = -ETIMEDOUT;
- goto fail_cmd;
- }
-
- if (atomic_read(&ac->cmd_state_pp) > 0) {
- pr_err("%s: DSP returned error[%s]\n",
- __func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state_pp)));
- rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state_pp));
- goto fail_cmd;
- }
- rc = 0;
-fail_cmd:
- kfree(ad);
- return rc;
-}
-
-int q6asm_dts_eagle_get(struct audio_client *ac, int param_id, uint32_t size,
- void *data, struct param_outband *po, int m_id)
-{
- struct asm_dts_eagle_param_get *ad;
- int rc = 0, *ob_params = NULL;
- uint32_t sz = sizeof(struct asm_dts_eagle_param) + APR_CMD_GET_HDR_SZ +
- (po ? 0 : size);
-
- if (!ac || ac->apr == NULL || (size == 0) || !data) {
- pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK\n",
- __func__, size, data);
- return -EINVAL;
- }
- ad = kzalloc(sz, GFP_KERNEL);
- if (!ad) {
- pr_err("DTS_EAGLE_ASM - %s: error allocating memory of size %u\n",
- __func__, sz);
- return -ENOMEM;
- }
- pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n",
- __func__, ac, param_id, size, data, m_id);
- q6asm_add_hdr(ac, &ad->hdr, sz, TRUE);
- ad->hdr.opcode = ASM_STREAM_CMD_GET_PP_PARAMS_V2;
- ad->param.data_payload_addr_lsw = 0;
- ad->param.data_payload_addr_msw = 0;
- ad->param.mem_map_handle = 0;
- ad->param.module_id = m_id;
- ad->param.param_id = param_id;
- ad->param.param_max_size = size + APR_CMD_GET_HDR_SZ;
- ad->param.reserved = 0;
- atomic_set(&ac->cmd_state, -1);
-
- generic_get_data = kzalloc(size + sizeof(struct generic_get_data_),
- GFP_KERNEL);
- if (!generic_get_data) {
- pr_err("DTS_EAGLE_ASM - %s: error allocating mem of size %u\n",
- __func__, size);
- rc = -ENOMEM;
- goto fail_cmd;
- }
-
- if (po) {
- struct list_head *ptr, *next;
- struct asm_buffer_node *node;
- pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n",
- __func__, po->kvaddr, (long)po->paddr);
- ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr);
- ad->param.data_payload_addr_msw =
- msm_audio_populate_upper_32_bits(po->paddr);
- list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
- node = list_entry(ptr, struct asm_buffer_node, list);
- if (node->buf_phys_addr == po->paddr) {
- ad->param.mem_map_handle = node->mmap_hdl;
- break;
- }
- }
- if (ad->param.mem_map_handle == 0) {
- pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n",
- __func__);
- rc = -EINVAL;
- goto fail_cmd;
- }
- /* check for integer overflow */
- if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
- rc = -EINVAL;
- if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) {
- pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n",
- __func__, po->size, size + APR_CMD_OB_HDR_SZ);
- rc = -EINVAL;
- goto fail_cmd;
- }
- ob_params = (int *)po->kvaddr;
- *ob_params++ = m_id;
- *ob_params++ = param_id;
- *ob_params++ = size;
- generic_get_data->is_inband = 0;
- } else {
- pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__);
- generic_get_data->is_inband = 1;
- }
-
- rc = apr_send_pkt(ac->apr, (uint32_t *)ad);
- if (rc < 0) {
- pr_err("DTS_EAGLE_ASM - %s: Commmand 0x%x failed\n", __func__,
- ad->hdr.opcode);
- goto fail_cmd;
- }
-
- rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 1*HZ);
- if (!rc) {
- pr_err("DTS_EAGLE_ASM - %s: timeout in get\n",
- __func__);
- rc = -ETIMEDOUT;
- goto fail_cmd;
- }
-
- if (atomic_read(&ac->cmd_state) > 0) {
- pr_err("%s: DSP returned error[%s]\n",
- __func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)));
- rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
- goto fail_cmd;
- }
-
- if (generic_get_data->valid) {
- rc = 0;
- memcpy(data, po ? ob_params : generic_get_data->ints, size);
- } else {
- rc = -EINVAL;
- pr_err("DTS_EAGLE_ASM - %s: EAGLE get params problem getting data - check callback error value\n",
- __func__);
- }
-fail_cmd:
- kfree(ad);
- kfree(generic_get_data);
- generic_get_data = NULL;
- return rc;
-}
-
static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance)
{
struct asm_volume_ctrl_master_gain vol;
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* 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
generic_get_data->valid = 1;
generic_get_data->size_in_ints =
data->payload_size/sizeof(int);
- pr_debug("DTS_EAGLE_CORE callback size = %i\n",
+ pr_debug("callback size = %i\n",
data->payload_size);
memcpy(generic_get_data->ints, data->payload,
data->payload_size);
return ret;
}
-int core_dts_eagle_set(int size, char *data)
-{
- struct adsp_dts_eagle *payload = NULL;
- int rc = 0, size_aligned4byte;
-
- pr_debug("DTS_EAGLE_CORE - %s\n", __func__);
- if (size <= 0 || !data) {
- pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n",
- __func__, size, data);
- return -EINVAL;
- }
-
- size_aligned4byte = (size+3) & 0xFFFFFFFC;
- mutex_lock(&(q6core_lcl.cmd_lock));
- ocm_core_open();
- if (q6core_lcl.core_handle_q) {
- payload = kzalloc(sizeof(struct adsp_dts_eagle) +
- size_aligned4byte, GFP_KERNEL);
- if (!payload) {
- rc = -ENOMEM;
- goto exit;
- }
- payload->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
- APR_HDR_LEN(APR_HDR_SIZE),
- APR_PKT_VER);
- payload->hdr.pkt_size = sizeof(struct adsp_dts_eagle) +
- size_aligned4byte;
- payload->hdr.src_port = 0;
- payload->hdr.dest_port = 0;
- payload->hdr.token = 0;
- payload->hdr.opcode = ADSP_CMD_SET_DTS_EAGLE_DATA_ID;
- payload->id = DTS_EAGLE_LICENSE_ID;
- payload->overwrite = 1;
- payload->size = size;
- memcpy(payload->data, data, size);
- rc = apr_send_pkt(q6core_lcl.core_handle_q,
- (uint32_t *)payload);
- if (rc < 0) {
- pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n",
- __func__, payload->hdr.opcode, rc);
- }
- kfree(payload);
- }
-
-exit:
- mutex_unlock(&(q6core_lcl.cmd_lock));
- return rc;
-}
-
-int core_dts_eagle_get(int id, int size, char *data)
-{
- struct apr_hdr ah;
- int rc = 0;
-
- pr_debug("DTS_EAGLE_CORE - %s\n", __func__);
- if (size <= 0 || !data) {
- pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n",
- __func__, size, data);
- return -EINVAL;
- }
- mutex_lock(&(q6core_lcl.cmd_lock));
- ocm_core_open();
- if (q6core_lcl.core_handle_q) {
- ah.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
- APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
- ah.pkt_size = sizeof(struct apr_hdr);
- ah.src_port = 0;
- ah.dest_port = 0;
- ah.token = 0;
- ah.opcode = id;
-
- q6core_lcl.bus_bw_resp_received = 0;
- generic_get_data = kzalloc(sizeof(struct generic_get_data_)
- + size, GFP_KERNEL);
- if (!generic_get_data) {
- rc = -ENOMEM;
- goto exit;
- }
-
- rc = apr_send_pkt(q6core_lcl.core_handle_q,
- (uint32_t *)&ah);
- if (rc < 0) {
- pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n",
- __func__, ah.opcode, rc);
- goto exit;
- }
-
- rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
- (q6core_lcl.bus_bw_resp_received == 1),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!rc) {
- pr_err("DTS_EAGLE_CORE - %s: EAGLE get params timed out\n",
- __func__);
- rc = -EINVAL;
- goto exit;
- }
- if (generic_get_data->valid) {
- rc = 0;
- memcpy(data, generic_get_data->ints, size);
- } else {
- rc = -EINVAL;
- pr_err("DTS_EAGLE_CORE - %s: EAGLE get params problem getting data - check callback error value\n",
- __func__);
- }
- }
-
-exit:
- kfree(generic_get_data);
- generic_get_data = NULL;
- mutex_unlock(&(q6core_lcl.cmd_lock));
- return rc;
-}
-
uint32_t core_set_dolby_manufacturer_id(int manufacturer_id)
{
struct adsp_dolby_manufacturer_id payload;
mutex_lock(&(q6core_lcl.cmd_lock));
ocm_core_open();
- q6core_lcl.bus_bw_resp_received = 0;
- rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr);
- if (rc < 0) {
- pr_err("%s: Get ADSP state APR packet send event %d\n",
- __func__, rc);
- goto bail;
- }
+ if (q6core_lcl.core_handle_q) {
+ q6core_lcl.bus_bw_resp_received = 0;
+ rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr);
+ if (rc < 0) {
+ pr_err("%s: Get ADSP state APR packet send event %d\n",
+ __func__, rc);
+ goto bail;
+ }
- rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
- (q6core_lcl.bus_bw_resp_received == 1),
- msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
- if (rc > 0 && q6core_lcl.bus_bw_resp_received) {
- /* ensure to read updated param by callback thread */
- rmb();
- ret = !!q6core_lcl.param;
+ rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+ if (rc > 0 && q6core_lcl.bus_bw_resp_received) {
+ /* ensure to read updated param by callback thread */
+ rmb();
+ ret = !!q6core_lcl.param;
+ }
}
bail:
pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret);
openlog("KVP", 0, LOG_USER);
syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
- kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR);
+ kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC);
if (kvp_fd < 0) {
syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s",
continue;
kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
- kvm->buses[bus_idx]->ioeventfd_count--;
+ if (kvm->buses[bus_idx])
+ kvm->buses[bus_idx]->ioeventfd_count--;
ioeventfd_release(p);
ret = 0;
break;
list_del(&kvm->vm_list);
spin_unlock(&kvm_lock);
kvm_free_irq_routing(kvm);
- for (i = 0; i < KVM_NR_BUSES; i++)
- kvm_io_bus_destroy(kvm->buses[i]);
+ for (i = 0; i < KVM_NR_BUSES; i++) {
+ if (kvm->buses[i])
+ kvm_io_bus_destroy(kvm->buses[i]);
+ kvm->buses[i] = NULL;
+ }
kvm_coalesced_mmio_free(kvm);
#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
mmu_notifier_unregister(&kvm->mmu_notifier, kvm->mm);
};
bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu);
+ if (!bus)
+ return -ENOMEM;
r = __kvm_io_bus_write(vcpu, bus, &range, val);
return r < 0 ? r : 0;
}
};
bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu);
+ if (!bus)
+ return -ENOMEM;
/* First try the device referenced by cookie. */
if ((cookie >= 0) && (cookie < bus->dev_count) &&
};
bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu);
+ if (!bus)
+ return -ENOMEM;
r = __kvm_io_bus_read(vcpu, bus, &range, val);
return r < 0 ? r : 0;
}
struct kvm_io_bus *new_bus, *bus;
bus = kvm->buses[bus_idx];
+ if (!bus)
+ return -ENOMEM;
+
/* exclude ioeventfd which is limited by maximum fd */
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
}
/* Caller must hold slots_lock. */
-int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
- struct kvm_io_device *dev)
+void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+ struct kvm_io_device *dev)
{
- int i, r;
+ int i;
struct kvm_io_bus *new_bus, *bus;
bus = kvm->buses[bus_idx];
- r = -ENOENT;
+ if (!bus)
+ return;
+
for (i = 0; i < bus->dev_count; i++)
if (bus->range[i].dev == dev) {
- r = 0;
break;
}
- if (r)
- return r;
+ if (i == bus->dev_count)
+ return;
new_bus = kmalloc(sizeof(*bus) + ((bus->dev_count - 1) *
sizeof(struct kvm_io_range)), GFP_KERNEL);
- if (!new_bus)
- return -ENOMEM;
+ if (!new_bus) {
+ pr_err("kvm: failed to shrink bus, removing it completely\n");
+ goto broken;
+ }
memcpy(new_bus, bus, sizeof(*bus) + i * sizeof(struct kvm_io_range));
new_bus->dev_count--;
memcpy(new_bus->range + i, bus->range + i + 1,
(new_bus->dev_count - i) * sizeof(struct kvm_io_range));
+broken:
rcu_assign_pointer(kvm->buses[bus_idx], new_bus);
synchronize_srcu_expedited(&kvm->srcu);
kfree(bus);
- return r;
+ return;
}
static struct notifier_block kvm_cpu_notifier = {