compatible "qcom,rpmcc" should be also included.
"qcom,rpmcc-msm8916", "qcom,rpmcc"
- "qcom,rpmcc-msm8974", "qcom,rpmcc"
"qcom,rpmcc-apq8064", "qcom,rpmcc"
- "qcom,rpmcc-apq8084", "qcom,rpmcc"
- #clock-cells : shall contain 1
compatible = "qcom,rpm-msm8916";
qcom,smd-channels = "rpm_requests";
- rpmcc: qcom,rpmcc {
+ rpmcc: clock-controller {
compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
#clock-cells = <1>;
};
bias-disable;
};
};
+
+ wcnss_pin_a: wcnss {
+ bt {
+ function = "riva_bt";
+ pins = "gpio16", "gpio17";
+ };
+
+ fm {
+ function = "riva_fm";
+ pins = "gpio14", "gpio15";
+ };
+
+ wlan {
+ function = "riva_wlan";
+ pins = "gpio64", "gpio65", "gpio66", "gpio67", "gpio68";
+ drive-strength = <6>;
+ bias-pull-up;
+ };
+ };
};
&pm8921_mpps {
};
+ smd {
+ q6@1 {
+ status = "ok";
+ };
+
+ riva@6 {
+ status = "ok";
+ };
+ };
+
soc {
rpm@108000 {
regulators {
bias-pull-down;
};
+ s2 {
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ qcom,switch-mode-frequency = <1600000>;
+ bias-pull-down;
+ regulator-always-on;
+ };
+
s3 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1400000>;
bias-pull-down;
};
+ l26 {
+ regulator-min-microvolt = < 375000>;
+ regulator-max-microvolt = <1050000>;
+ bias-pull-down;
+ };
lvs6 {
bias-pull-down;
};
lvs7 {
bias-pull-down;
+ regulator-always-on;
};
};
};
target-supply = <&pm8921_lvs7>;
};
+ wcnss@3204000 {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&wcnss_pin_a>;
+ };
+
/* OTG */
phy@12500000 {
status = "okay";
usb@12530000 {
status = "okay";
};
+ pil_q6v4: pil@28800000 {
+ qcom,pll-supply = <&pm8921_l26>;
+ qcom,pll-uV = <1050000>;
+ };
hdmi: qcom,hdmi-tx@4a00000 {
status = "okay";
};
};
+ smd {
+ q6@1 {
+ status = "okay";
+ };
+ };
+
soc {
pinctrl@800000 {
card_detect: card_detect {
reg = <0x80000000 0x200000>;
no-map;
};
+
+ wcnss_mem: wcnss@8f000000 {
+ reg = <0x8f000000 0x700000>;
+ no-map;
+ };
};
cpus {
};
};
+ smd {
+ compatible = "qcom,smd";
+
+ modem@0 {
+ interrupts = <0 37 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,ipc = <&l2cc 8 3>;
+ qcom,smd-edge = <0>;
+
+ status = "disabled";
+ };
+
+ q6@1 {
+ interrupts = <0 90 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,ipc = <&l2cc 8 15>;
+ qcom,smd-edge = <1>;
+
+ status = "disabled";
+
+ apr {
+ compatible = "qcom,apr";
+ qcom,smd-channels = "apr_audio_svc";
+ rproc = <&pil_q6v4>;
+ };
+ };
+
+ dsps@3 {
+ interrupts = <0 138 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,ipc = <&sps_sic_non_secure 0x4080 0>;
+ qcom,smd-edge = <3>;
+
+ status = "disabled";
+ };
+
+ riva@6 {
+ interrupts = <0 198 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,ipc = <&l2cc 8 25>;
+ qcom,smd-edge = <6>;
+
+ status = "disabled";
+
+ wcnss {
+ compatible = "qcom,wcnss";
+ qcom,smd-channels = "WCNSS_CTRL";
+
+ qcom,mmio = <&riva>;
+
+ bt {
+ compatible = "qcom,wcnss-bt";
+ };
+
+ wifi {
+ compatible = "qcom,wcnss-wlan";
+
+ interrupts = <0 203 0>, <0 202 0>;
+ interrupt-names = "tx", "rx";
+
+ qcom,state = <&apps_smsm 10>, <&apps_smsm 9>;
+ qcom,state-names = "tx-enable", "tx-rings-empty";
+
+ local-mac-address = [ 18 00 2d 88 9c a9 ];
+ };
+ };
+ };
+ };
+
+ smsm {
+ compatible = "qcom,smsm";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ipc-1 = <&l2cc 8 4>;
+ qcom,ipc-2 = <&l2cc 8 14>;
+ qcom,ipc-3 = <&l2cc 8 23>;
+ qcom,ipc-4 = <&sps_sic_non_secure 0x4094 0>;
+
+ apps_smsm: apps@0 {
+ reg = <0>;
+ #qcom,state-cells = <1>;
+ };
+
+ modem_smsm: modem@1 {
+ reg = <1>;
+ interrupts = <0 38 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ q6_smsm: q6@2 {
+ reg = <2>;
+ interrupts = <0 89 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ wcnss_smsm: wcnss@3 {
+ reg = <3>;
+ interrupts = <0 204 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ dsps_smsm: dsps@4 {
+ reg = <4>;
+ interrupts = <0 137 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
regulator-max-microvolt = <1250000>;
};
+ sps_sic_non_secure: sps-sic-non-secure@12100000 {
+ compatible = "syscon";
+ reg = <0x12100000 0x10000>;
+ };
+
gsbi1: gsbi@12440000 {
status = "disabled";
compatible = "qcom,gsbi-v1.0.0";
};
};
+ riva: wcnss@3204000 {
+ compatible = "qcom,riva-pil", "qcom,riva";
+ reg = <0x03200800 0x1000>, <0x03202000 0x2000>, <0x03204000 0x100>;
+ reg-names = "ccu", "dxe", "pmu";
+
+ interrupts-extended = <&intc 0 199 IRQ_TYPE_EDGE_RISING>,
+ <&wcnss_smsm 6 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "wdog", "fatal";
+
+ memory-region = <&wcnss_mem>;
+
+ vddcx-supply = <&pm8921_s3>;
+ vddmx-supply = <&pm8921_l24>;
+ vddpx-supply = <&pm8921_s4>;
+
+ status = "disabled";
+
+ iris {
+ compatible = "qcom,wcn3660";
+
+ clocks = <&rpmcc 9>;
+ clock-names = "xo";
+
+ vddxo-supply = <&pm8921_l4>;
+ vddrfa-supply = <&pm8921_s2>;
+ vddpa-supply = <&pm8921_l10>;
+ vdddig-supply = <&pm8921_lvs2>;
+ };
+ };
+
+
usb1_phy: phy@12500000 {
compatible = "qcom,usb-otg-ci";
reg = <0x12500000 0x400>;
qcom,pas-id = <1>; /* PAS_Q6 */
};
- smd {
- compatible = "qcom,smd";
- adsp_a11 {
- interrupts = <0 90 IRQ_TYPE_EDGE_RISING>;
- qcom,ipc = <&l2cc 8 15>;
- qcom,smd-edge = <1>;
- qcom,remote-pid = <0x2>;
- q6_requests {
- compatible = "qcom,apr";
- qcom,smd-channels = "apr_audio_svc";
- rproc = <&pil_q6v4>;
- };
- };
- };
-
dai_fe: dai_fe {
compatible = "qcom,msm-dai-fe";
#sound-dai-cells = <0>;
CONFIG_BT_HIDP=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_QCOMSMD=y
CONFIG_CFG80211=m
CONFIG_CFG80211_WEXT=y
CONFIG_MAC80211=m
CONFIG_ATH_CARDS=m
CONFIG_ATH6KL=m
CONFIG_ATH6KL_SDIO=m
+CONFIG_WCN36XX=m
+CONFIG_WCN36XX_DEBUGFS=y
CONFIG_INPUT_EVDEV=y
# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_MOUSE_PS2 is not set
CONFIG_KRAITCC=y
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_MSM_IOMMU=y
+CONFIG_QCOM_Q6V5_PIL=y
CONFIG_QCOM_TZ_PIL=y
+CONFIG_QCOM_WCNSS_PIL=y
CONFIG_QCOM_GSBI=y
CONFIG_QCOM_PM=y
CONFIG_QCOM_SMEM=y
CONFIG_QCOM_SMD=y
CONFIG_QCOM_SMD_RPM=y
+CONFIG_QCOM_SMP2P=y
+CONFIG_QCOM_SMSM=y
+CONFIG_QCOM_WCNSS_CTRL=y
CONFIG_PHY_QCOM_APQ8064_SATA=y
CONFIG_PHY_QCOM_IPQ806X_SATA=y
CONFIG_NVMEM=y
qcom,qos-off = <0x1000>;
qcom,bus-type = <1>;
clock-names = "bus_clk", "bus_a_clk";
- clocks = <&rpmcc RPM_SNOC_CLK>,
- <&rpmcc RPM_SNOC_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_SNOC_CLK>,
+ <&rpmcc RPM_SMD_SNOC_A_CLK>;
};
fab_bimc: fab-bimc {
qcom,base-name = "bimc-base";
qcom,bus-type = <2>;
clock-names = "bus_clk", "bus_a_clk";
- clocks = <&rpmcc RPM_BIMC_CLK>,
- <&rpmcc RPM_BIMC_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_BIMC_CLK>,
+ <&rpmcc RPM_SMD_BIMC_A_CLK>;
};
fab_pnoc: fab-pnoc {
qcom,qos-delta = <0x1000>;
qcom,bus-type = <1>;
clock-names = "bus_clk", "bus_a_clk";
- clocks = <&rpmcc RPM_PCNOC_CLK>,
- <&rpmcc RPM_PCNOC_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_PCNOC_CLK>,
+ <&rpmcc RPM_SMD_PCNOC_A_CLK>;
};
/* SNOC Devices */
compatible = "arm,coresight-tpiu", "arm,primecell";
reg = <0x820000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
port {
compatible = "arm,coresight-funnel", "arm,primecell";
reg = <0x821000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
ports {
compatible = "qcom,coresight-replicator1x", "arm,primecell";
reg = <0x824000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
ports {
compatible = "arm,coresight-tmc", "arm,primecell";
reg = <0x825000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
ports {
compatible = "arm,coresight-tmc", "arm,primecell";
reg = <0x826000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
port {
compatible = "arm,coresight-funnel", "arm,primecell";
reg = <0x841000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
ports {
compatible = "arm,coresight-etm4x", "arm,primecell";
reg = <0x85c000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
cpu = <&CPU0>;
compatible = "arm,coresight-etm4x", "arm,primecell";
reg = <0x85d000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
cpu = <&CPU1>;
compatible = "arm,coresight-etm4x", "arm,primecell";
reg = <0x85e000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
cpu = <&CPU2>;
compatible = "arm,coresight-etm4x", "arm,primecell";
reg = <0x85f000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>;
+ clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>;
clock-names = "apb_pclk", "atclk";
cpu = <&CPU3>;
reg = <0x0 0x8b600000 0x0 0x0600000>;
};
+ wcnss_mem: wcnss@89300000 {
+ reg = <0x0 0x89300000 0x0 0x600000>;
+ no-map;
+ };
+
vidc_mem: vidc_region@8f800000 {
no-map;
reg = <0 0x8f800000 0 0x800000>;
};
+
+ mba_mem: mba@8ea00000 {
+ no-map;
+ reg = <0 0x8ea00000 0 0x100000>;
+ };
};
cpus {
firmware {
compatible = "simple-bus";
- scm {
+ scm: scm {
compatible = "qcom,scm";
clocks = <&gcc GCC_CRYPTO_CLK> , <&gcc GCC_CRYPTO_AXI_CLK>, <&gcc GCC_CRYPTO_AHB_CLK>;
clock-names = "core", "bus", "iface";
+ #reset-cells = <1>;
};
};
#thermal-sensor-cells = <1>;
};
- q6-smp2p {
- compatible = "qcom,smp2p";
- qcom,smem = <435>, <428>;
- interrupts = <0 27 1>;
- qcom,ipc = <&apcs 8 14>;
-
- qcom,local-pid = <0>;
- qcom,remote-pid = <1>;
-
- q6_smp2p_out: master-kernel {
- qcom,entry-name = "master-kernel";
- qcom,outbound;
-
- gpio-controller;
- #gpio-cells = <2>;
- };
+ hexagon@4080000 {
+ compatible = "qcom,pil-q6v56-mss", "qcom,q6v5-pil";
+ reg = <0x04080000 0x100>,
+ <0x04020000 0x040>;
- q6_smp2p_in: slave-kernel {
- qcom,entry-name = "slave-kernel";
- qcom,inbound;
+ reg-names = "qdsp6", "rmb";
- interrupt-controller;
- #interrupt-cells = <2>;
- };
- };
+ interrupts-extended = <&intc 0 24 1>,
+ <&hexagon_smp2p_in 0 0>,
+ <&hexagon_smp2p_in 1 0>,
+ <&hexagon_smp2p_in 2 0>,
+ <&hexagon_smp2p_in 3 0>;
+ interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack";
- wcnss-smp2p {
- compatible = "qcom,smp2p";
- qcom,smem = <451>, <431>;
+ clocks = <&gcc GCC_MSS_CFG_AHB_CLK>, <&gcc GCC_MSS_Q6_BIMC_AXI_CLK>, <&gcc GCC_BOOT_ROM_AHB_CLK>;
+ clock-names = "iface", "bus", "mem";
- interrupts = <0 143 1>;
+ qcom,state = <&hexagon_smp2p_out 0>;
+ qcom,state-names = "stop";
- qcom,ipc = <&apcs 8 18>;
+ resets = <&scm 0>;
+ reset-names = "mss_restart";
- qcom,local-pid = <0>;
- qcom,remote-pid = <4>;
+ mx-supply = <&pm8916_l3>;
+ pll-supply = <&pm8916_l7>;
- wcnss_smp2p_out: master-kernel {
- qcom,entry-name = "master-kernel";
- qcom,outbound;
+ qcom,halt-regs = <&tcsr 0x18000 0x19000 0x1a000>;
- gpio-controller;
- #gpio-cells = <2>;
+ mba {
+ memory-region = <&mba_mem>;
};
- wcnss_smp2p_in: slave-kernel {
- qcom,entry-name = "slave-kernel";
- qcom,inbound;
-
- interrupt-controller;
- #interrupt-cells = <2>;
+ mpss {
+ memory-region = <&modem_adsp_mem>;
};
};
- qcom,mss@4080000 {
- compatible = "qcom,pil-q6v56-mss", "qcom,q6v5-pil";
- reg = <0x04080000 0x100>,
- <0x04020000 0x040>,
- <0x01810000 0x004>,
- <0x01810000 0x004>,
- <0x0194f000 0x010>,
- <0x01950000 0x008>,
- <0x01951000 0x008>;
-
- reg-names = "qdsp6_base", "rmb_base", "restart_reg_sec",
- "halt_q6", "halt_modem", "halt_nc";
-
- interrupts-extended = <&intc 0 24 1>,
- <&q6_smp2p_in 0 0>,
- <&q6_smp2p_in 1 0>,
- <&q6_smp2p_in 2 0>,
- <&q6_smp2p_in 3 0>;
- interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack";
-
- clocks = <&gcc GCC_MSS_CFG_AHB_CLK>, <&gcc GCC_MSS_Q6_BIMC_AXI_CLK>, <&gcc GCC_BOOT_ROM_AHB_CLK>;
-
- clock-names = "iface", "bus", "mem";
+ pronto: wcnss@a21b000 {
+ compatible = "qcom,pronto-v2-pil", "qcom,pronto";
+ reg = <0x0a204000 0x2000>, <0x0a202000 0x1000>, <0x0a21b000 0x3000>;
+ reg-names = "ccu", "dxe", "pmu";
- qcom,mx-supply = <&pm8916_l3>;
- qcom,mx-uV = <1050000>;
- qcom,pll-supply = <&pm8916_l7>;
- qcom,pll-uV = <1800000>;
- qcom,proxy-clock-names = "xo";
- qcom,active-clock-names = "iface_clk", "bus_clk", "mem_clk";
- qcom,is-loadable;
- qcom,firmware-name = "modem";
- qcom,pil-self-auth;
-
-
- /* GPIO inputs from mss */
- qcom,gpio-err-fatal = <&q6_smp2p_in 0 0>;
- qcom,gpio-err-ready = <&q6_smp2p_in 1 0>;
- qcom,gpio-proxy-unvote = <&q6_smp2p_in 2 0>;
- qcom,gpio-stop-ack = <&q6_smp2p_in 3 0>;
- qcom,gpio-ramdump-disable = <&q6_smp2p_in 15 0>;
- /* GPIO output to mss */
- qcom,gpio-force-stop = <&q6_smp2p_out 0 0>;
- qcom,stop-gpio = <&q6_smp2p_out 0 0>;
- memory-region = <&modem_adsp_mem>;
- };
-
- pronto_rproc:pronto_rproc {
- compatible = "qcom,tz-pil";
+ memory-region = <&wcnss_mem>;
- interrupts-extended = <&intc 0 149 1>,
- <&wcnss_smp2p_in 0 0>,
- <&wcnss_smp2p_in 1 0>,
- <&wcnss_smp2p_in 2 0>,
- <&wcnss_smp2p_in 3 0>;
+ interrupts-extended = <&intc 0 149 IRQ_TYPE_EDGE_RISING>,
+ <&wcnss_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
+ <&wcnss_smp2p_in 1 IRQ_TYPE_EDGE_RISING>,
+ <&wcnss_smp2p_in 2 IRQ_TYPE_EDGE_RISING>,
+ <&wcnss_smp2p_in 3 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack";
- clocks = <&gcc GCC_CRYPTO_CLK>,
- <&gcc GCC_CRYPTO_AHB_CLK>,
- <&gcc GCC_CRYPTO_AXI_CLK>,
- <&gcc CRYPTO_CLK_SRC>;
- clock-names = "scm_core_clk", "scm_iface_clk", "scm_bus_clk", "scm_src_clk";
-
- qcom,firmware-name = "wcnss";
- qcom,pas-id = <6>;
+ vddmx-supply = <&pm8916_l3>;
+ vddpx-supply = <&pm8916_l7>;
- qcom,crash-reason = <422>;
- qcom,smd-edges = <&pronto_smd_edge>;
-
- qcom,pll-supply = <&pm8916_l7>;
- qcom,pll-uV = <1800000>;
- qcom,pll-uA = <18000>;
-
- qcom,stop-gpio = <&wcnss_smp2p_out 0 0>;
+ qcom,state = <&wcnss_smp2p_out 0>;
+ qcom,state-names = "stop";
pinctrl-names = "default";
pinctrl-0 = <&wcnss_default>;
- memory-region = <&peripheral_mem>;
- };
-
- qcom,wcn36xx@0a000000 {
- compatible = "qcom,wcn3620";
- reg = <0x0a000000 0x280000>,
- <0xb011008 0x04>,
- <0x0a21b000 0x3000>,
- <0x03204000 0x00000100>,
- <0x03200800 0x00000200>,
- <0x0A100400 0x00000200>,
- <0x0A205050 0x00000200>,
- <0x0A219000 0x00000020>,
- <0x0A080488 0x00000008>,
- <0x0A080fb0 0x00000008>,
- <0x0A08040c 0x00000008>,
- <0x0A0120a8 0x00000008>,
- <0x0A012448 0x00000008>,
- <0x0A080c00 0x00000001>;
-
- reg-names = "wcnss_mmio", "wcnss_fiq",
- "pronto_phy_base", "riva_phy_base",
- "riva_ccu_base", "pronto_a2xb_base",
- "pronto_ccpu_base", "pronto_saw2_base",
- "wlan_tx_phy_aborts","wlan_brdg_err_source",
- "wlan_tx_status", "alarms_txctl",
- "alarms_tactl", "pronto_mcu_base";
-
- interrupts = <0 145 0 0 146 0>;
- interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq";
-
- // qcom,pronto-vddmx-supply = <&pm8916_l3>;
- // qcom,pronto-vddcx-supply = <&pm8916_s1_corner>;
- // qcom,pronto-vddpx-supply = <&pm8916_l7>;
- // qcom,iris-vddxo-supply = <&pm8916_l7>;
- // qcom,iris-vddrfa-supply = <&pm8916_s3>;
- // qcom,iris-vddpa-supply = <&pm8916_l9>;
- // qcom,iris-vdddig-supply = <&pm8916_l5>;
-
- pinctrl-names = "wcnss_default";
- // pinctrl-names = "wcnss_default", "wcnss_sleep",
- // "wcnss_gpio_default";
- pinctrl-0 = <&wcnss_default>;
- // pinctrl-1 = <&wcnss_sleep>;
- // pinctrl-2 = <&wcnss_gpio_default>;
-
- // clocks = <&rpmcc RPM_XO_CLK_SRC>,
- // <&rpmcc RPM_RF_CLK2>;
- //clock-names = "xo", "rf_clk";
-
- rproc = <&pronto_rproc>;
- qcom,has-autodetect-xo;
- qcom,wlan-rx-buff-count = <512>;
- qcom,is-pronto-vt;
- qcom,has-pronto-hw;
- // qcom,wcnss-adc_tm = <&pm8916_adc_tm>;
- };
+ iris {
+ compatible = "qcom,wcn3620";
+ clocks = <&rpmcc RPM_SMD_RF_CLK2>;
+ clock-names = "xo";
+ vddxo-supply = <&pm8916_l7>;
+ vddrfa-supply = <&pm8916_s3>;
+ vddpa-supply = <&pm8916_l9>;
+ vdddig-supply = <&pm8916_l5>;
+ };
+ };
qcom,rpm-log@29dc00 {
compatible = "qcom,rpm-log";
compatible = "qcom,rpm-msm8916";
qcom,smd-channels = "rpm_requests";
rpmcc: qcom,rpmcc {
- compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
+ compatible = "qcom,rpmcc-msm8916";
#clock-cells = <1>;
};
};
};
- pronto_smd_edge: pronto {
+ pronto {
interrupts = <0 142 1>;
qcom,ipc = <&apcs 8 17>;
qcom,smd-edge = <6>;
qcom,remote-pid = <4>;
- bt {
- compatible = "qcom,hci-smd";
- qcom,smd-channels = "APPS_RIVA_BT_CMD", "APPS_RIVA_BT_ACL";
- qcom,smd-channel-names = "event", "data";
- };
+ wcnss {
+ compatible = "qcom,wcnss";
+ qcom,smd-channels = "WCNSS_CTRL";
- ipcrtr {
- compatible = "qcom,ipcrtr";
- qcom,smd-channels = "IPCRTR";
- };
+ qcom,mmio = <&pronto>;
- wifi {
- compatible = "qcom,wlan-ctrl";
- qcom,smd-channels = "WLAN_CTRL";
+ bt {
+ compatible = "qcom,wcnss-bt";
+ };
- interrupts = <0 145 0>, <0 146 0>;
- interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq";
+ wifi {
+ compatible = "qcom,wcnss-wlan";
- qcom,wcnss_mmio = <0xfb000000 0x21b000>;
+ interrupts = <0 145 0>, <0 146 0>;
+ interrupt-names = "tx", "rx";
- // qcom,tx-enable-gpios = <&apps_smsm 10 0>;
- // qcom,tx-rings-empty-gpios = <&apps_smsm 9 0>;
+ qcom,state = <&apps_smsm 10>, <&apps_smsm 9>;
+ qcom,state-names = "tx-enable", "tx-rings-empty";
+ };
};
+ };
+ };
- wcnss_ctrl {
- compatible = "qcom,wcnss-ctrl";
- qcom,smd-channels = "WCNSS_CTRL";
+ hexagon-smp2p {
+ compatible = "qcom,smp2p";
+ qcom,smem = <435>, <428>;
- qcom,wcnss_mmio = <0xfb21b000 0x3000>;
- };
+ interrupts = <0 27 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,ipc = <&apcs 8 14>;
+
+ qcom,local-pid = <0>;
+ qcom,remote-pid = <1>;
+
+ hexagon_smp2p_out: master-kernel {
+ qcom,entry-name = "master-kernel";
+
+ #qcom,state-cells = <1>;
+ };
+
+ hexagon_smp2p_in: slave-kernel {
+ qcom,entry-name = "slave-kernel";
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+
+ wcnss-smp2p {
+ compatible = "qcom,smp2p";
+ qcom,smem = <451>, <431>;
+
+ interrupts = <0 143 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,ipc = <&apcs 8 18>;
+
+ qcom,local-pid = <0>;
+ qcom,remote-pid = <4>;
+
+ wcnss_smp2p_out: master-kernel {
+ qcom,entry-name = "master-kernel";
+
+ #qcom,state-cells = <1>;
+ };
+
+ wcnss_smp2p_in: slave-kernel {
+ qcom,entry-name = "slave-kernel";
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
};
};
+ smsm {
+ compatible = "qcom,smsm";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ipc-1 = <&apcs 0 13>;
+ qcom,ipc-6 = <&apcs 0 19>;
+
+ apps_smsm: apps@0 {
+ reg = <0>;
+
+ #qcom,state-cells = <1>;
+ };
+
+ hexagon_smsm: hexagon@1 {
+ reg = <1>;
+ interrupts = <0 26 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ wcnss_smsm: wcnss@6 {
+ reg = <6>;
+ interrupts = <0 144 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
};
#include "msm8916-pins.dtsi"
CONFIG_PINCTRL_MSM8916=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_GPIO_PL061=y
-CONFIG_GPIO_QCOM_SMSM=y
-CONFIG_GPIO_QCOM_SMP2P=y
CONFIG_GPIO_XGENE=y
CONFIG_POWER_RESET_MSM=y
CONFIG_POWER_RESET_XGENE=y
CONFIG_QCOM_IOMMU_V1=y
CONFIG_QCOM_Q6V5_PIL=y
CONFIG_QCOM_TZ_PIL=y
+CONFIG_QCOM_WCNSS_PIL=y
CONFIG_QCOM_GSBI=y
CONFIG_QCOM_SMEM=y
CONFIG_QCOM_SMD=y
CONFIG_QCOM_SMD_RPM=y
+CONFIG_QCOM_SMP2P=y
+CONFIG_QCOM_SMSM=y
+CONFIG_QCOM_WCNSS_CTRL=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_QTI_LNX_GPS_PROXY=y
CONFIG_BUS_TOPOLOGY_ADHOC=y
config BT_QCOMSMD
tristate "Qualcomm SMD based HCI support"
depends on QCOM_SMD
+ select BT_QCA
help
Qualcomm SMD based HCI driver.
This driver is used to bridge HCI data onto the shared memory
/*
+ * Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
+#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci.h>
-
-#define EDL_NVM_ACCESS_SET_REQ_CMD 0x01
-#define EDL_NVM_ACCESS_OPCODE 0xfc0b
+#include "btqca.h"
struct btqcomsmd {
+ struct hci_dev *hdev;
+
struct qcom_smd_channel *acl_channel;
struct qcom_smd_channel *cmd_channel;
};
-static int btqcomsmd_recv(struct hci_dev *hdev,
- unsigned type,
- const void *data,
+static int btqcomsmd_recv(struct hci_dev *hdev, unsigned type, const void *data,
size_t count)
{
struct sk_buff *skb;
- void *buf;
/* Use GFP_ATOMIC as we're in IRQ context */
skb = bt_skb_alloc(count, GFP_ATOMIC);
- if (!skb)
+ if (!skb) {
+ hdev->stat.err_rx++;
return -ENOMEM;
+ }
bt_cb(skb)->pkt_type = type;
-
- /* Use io accessor as data might be ioremapped */
- buf = skb_put(skb, count);
- memcpy_fromio(buf, data, count);
+ memcpy(skb_put(skb, count), data, count);
return hci_recv_frame(hdev, skb);
}
-static int btqcomsmd_acl_callback(struct qcom_smd_device *qsdev,
+static int btqcomsmd_acl_callback(struct qcom_smd_channel *channel,
const void *data,
size_t count)
{
- struct hci_dev *hdev = dev_get_drvdata(&qsdev->dev);
+ struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
- return btqcomsmd_recv(hdev, HCI_ACLDATA_PKT, data, count);
+ btq->hdev->stat.byte_rx += count;
+ return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
}
-static int btqcomsmd_cmd_callback(struct qcom_smd_device *qsdev,
+static int btqcomsmd_cmd_callback(struct qcom_smd_channel *channel,
const void *data,
size_t count)
{
- struct hci_dev *hdev = dev_get_drvdata(&qsdev->dev);
+ struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
- return btqcomsmd_recv(hdev, HCI_EVENT_PKT, data, count);
+ return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
}
static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
switch (bt_cb(skb)->pkt_type) {
case HCI_ACLDATA_PKT:
- case HCI_SCODATA_PKT:
ret = qcom_smd_send(btq->acl_channel, skb->data, skb->len);
+ hdev->stat.acl_tx++;
+ hdev->stat.byte_tx += skb->len;
break;
case HCI_COMMAND_PKT:
ret = qcom_smd_send(btq->cmd_channel, skb->data, skb->len);
+ hdev->stat.cmd_tx++;
break;
default:
- ret = -ENODEV;
+ ret = -EILSEQ;
break;
}
static int btqcomsmd_open(struct hci_dev *hdev)
{
- set_bit(HCI_RUNNING, &hdev->flags);
return 0;
}
static int btqcomsmd_close(struct hci_dev *hdev)
{
- clear_bit(HCI_RUNNING, &hdev->flags);
- return 0;
-}
-
-static int btqcomsmd_set_bdaddr(struct hci_dev *hdev,
- const bdaddr_t *bdaddr)
-{
- struct sk_buff *skb;
- u8 cmd[9];
- int err;
-
- cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD;
- cmd[1] = 0x02; /* TAG ID */
- cmd[2] = sizeof(bdaddr_t); /* size */
- memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t));
- skb = __hci_cmd_sync_ev(hdev,
- EDL_NVM_ACCESS_OPCODE,
- sizeof(cmd), cmd,
- HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Change address command failed (%d)",
- hdev->name, err);
- return err;
- }
-
- kfree_skb(skb);
-
return 0;
}
-static int btqcomsmd_probe(struct qcom_smd_device *sdev)
+static int btqcomsmd_probe(struct platform_device *pdev)
{
- struct qcom_smd_channel *acl;
struct btqcomsmd *btq;
struct hci_dev *hdev;
+ void *wcnss;
int ret;
- acl = qcom_smd_open_channel(sdev,
- "APPS_RIVA_BT_ACL",
- btqcomsmd_acl_callback);
- if (IS_ERR(acl))
- return PTR_ERR(acl);
-
- btq = devm_kzalloc(&sdev->dev, sizeof(*btq), GFP_KERNEL);
+ btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
if (!btq)
return -ENOMEM;
- btq->acl_channel = acl;
- btq->cmd_channel = sdev->channel;
+ wcnss = dev_get_drvdata(pdev->dev.parent);
+
+ btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
+ btqcomsmd_acl_callback);
+ if (IS_ERR(btq->acl_channel))
+ return PTR_ERR(btq->acl_channel);
+
+ btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
+ btqcomsmd_cmd_callback);
+ if (IS_ERR(btq->cmd_channel))
+ return PTR_ERR(btq->cmd_channel);
+
+ qcom_smd_set_drvdata(btq->acl_channel, btq);
+ qcom_smd_set_drvdata(btq->cmd_channel, btq);
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
+ hci_set_drvdata(hdev, btq);
+ btq->hdev = hdev;
+ SET_HCIDEV_DEV(hdev, &pdev->dev);
+
hdev->bus = HCI_SMD;
hdev->open = btqcomsmd_open;
hdev->close = btqcomsmd_close;
hdev->send = btqcomsmd_send;
- hdev->set_bdaddr = btqcomsmd_set_bdaddr;
+ hdev->set_bdaddr = qca_set_bdaddr_rome;
ret = hci_register_dev(hdev);
if (ret < 0) {
return ret;
}
- hci_set_drvdata(hdev, btq);
- dev_set_drvdata(&sdev->dev, hdev);
+ platform_set_drvdata(pdev, btq);
return 0;
}
-static void btqcomsmd_remove(struct qcom_smd_device *sdev)
+static int btqcomsmd_remove(struct platform_device *pdev)
{
- struct hci_dev *hdev = dev_get_drvdata(&sdev->dev);;
+ struct btqcomsmd *btq = platform_get_drvdata(pdev);
- hci_unregister_dev(hdev);
- hci_free_dev(hdev);
+ hci_unregister_dev(btq->hdev);
+ hci_free_dev(btq->hdev);
+
+ return 0;
}
-static const struct qcom_smd_id btqcomsmd_match[] = {
- { .name = "APPS_RIVA_BT_CMD" },
- {}
+static const struct of_device_id btqcomsmd_of_match[] = {
+ { .compatible = "qcom,wcnss-bt", },
+ { },
};
-static struct qcom_smd_driver btqcomsmd_cmd_driver = {
+static struct platform_driver btqcomsmd_driver = {
.probe = btqcomsmd_probe,
.remove = btqcomsmd_remove,
- .callback = btqcomsmd_cmd_callback,
- .smd_match_table = btqcomsmd_match,
.driver = {
.name = "btqcomsmd",
- .owner = THIS_MODULE,
+ .of_match_table = btqcomsmd_of_match,
},
};
-module_qcom_smd_driver(btqcomsmd_cmd_driver);
+module_platform_driver(btqcomsmd_driver);
+MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
MODULE_LICENSE("GPL v2");
select REGMAP_MMIO
select RESET_CONTROLLER
-config QCOM_CLK_SMD_RPM
- tristate "RPM over SMD based Clock Controller"
- depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
- select QCOM_RPMCC
- help
- Support for the clocks exposed by the Resource Power Manager
- processor on devices like apq8016, apq8084 and msm8974.
-
config QCOM_CLK_RPM
tristate "RPM based Clock Controller"
depends on COMMON_CLK_QCOM && MFD_QCOM_RPM
select QCOM_RPMCC
help
- Support for the clocks exposed by the Resource Power Manager
- processor on devices like apq8064.
+ The RPM (Resource Power Manager) is a dedicated hardware engine for
+ managing the shared SoC resources in order to keep the lowest power
+ profile. It communicates with other hardware subsystems via shared
+ memory and accepts clock requests, aggregates the requests and turns
+ the clocks on/off or scales them on demand.
+ Say Y if you want to support the clocks exposed by the RPM on
+ platforms such as apq8064, msm8660, msm8960 etc.
+
+config QCOM_CLK_SMD_RPM
+ tristate "RPM over SMD based Clock Controller"
+ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
+ select QCOM_RPMCC
+ help
+ The RPM (Resource Power Manager) is a dedicated hardware engine for
+ managing the shared SoC resources in order to keep the lowest power
+ profile. It communicates with other hardware subsystems via shared
+ memory and accepts clock requests, aggregates the requests and turns
+ the clocks on/off or scales them on demand.
+ Say Y if you want to support the clocks exposed by the RPM on
+ platforms such as apq8016, apq8084, msm8974 etc.
config APQ_GCC_8084
tristate "APQ8084 Global Clock Controller"
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o
-clk-qcom-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
-clk-qcom-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
obj-$(CONFIG_KRAITCC) += krait-cc.o
obj-$(CONFIG_QCOM_A53) += clk-a53.o
+obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
+obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
+obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
/*
- * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2016, Linaro Limited
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* GNU General Public License for more details.
*/
-#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
-#include "clk-rpm.h"
#include <dt-bindings/mfd/qcom-rpm.h>
+#include <dt-bindings/clock/qcom,rpmcc.h>
+
+#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63
+#define QCOM_RPM_SCALING_ENABLE_ID 0x2
+
+#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \
+ static struct clk_rpm _platform##_##_active; \
+ static struct clk_rpm _platform##_##_name = { \
+ .rpm_clk_id = (r_id), \
+ .peer = &_platform##_##_active, \
+ .rate = INT_MAX, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpm_ops, \
+ .name = #_name, \
+ .parent_names = (const char *[]){ "pxo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }; \
+ static struct clk_rpm _platform##_##_active = { \
+ .rpm_clk_id = (r_id), \
+ .peer = &_platform##_##_name, \
+ .active_only = true, \
+ .rate = INT_MAX, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpm_ops, \
+ .name = #_active, \
+ .parent_names = (const char *[]){ "pxo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }
+
+#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \
+ static struct clk_rpm _platform##_##_active; \
+ static struct clk_rpm _platform##_##_name = { \
+ .rpm_clk_id = (r_id), \
+ .active_only = true, \
+ .peer = &_platform##_##_active, \
+ .rate = (r), \
+ .branch = true, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpm_branch_ops, \
+ .name = #_name, \
+ .parent_names = (const char *[]){ "pxo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }; \
+ static struct clk_rpm _platform##_##_active = { \
+ .rpm_clk_id = (r_id), \
+ .peer = &_platform##_##_name, \
+ .rate = (r), \
+ .branch = true, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpm_branch_ops, \
+ .name = #_active, \
+ .parent_names = (const char *[]){ "pxo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }
+
+#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \
+ static struct clk_rpm _platform##_##_active; \
+ static struct clk_rpm _platform##_##_name = { \
+ .rpm_clk_id = (r_id), \
+ .peer = &_platform##_##_active, \
+ .rate = (r), \
+ .branch = true, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpm_branch_ops, \
+ .name = #_name, \
+ .parent_names = (const char *[]){ "cxo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }; \
+ static struct clk_rpm _platform##_##_active = { \
+ .rpm_clk_id = (r_id), \
+ .active_only = true, \
+ .peer = &_platform##_##_name, \
+ .rate = (r), \
+ .branch = true, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpm_branch_ops, \
+ .name = #_active, \
+ .parent_names = (const char *[]){ "cxo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }
#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw)
+struct clk_rpm {
+ const int rpm_clk_id;
+ const bool active_only;
+ unsigned long rate;
+ bool enabled;
+ bool branch;
+ struct clk_rpm *peer;
+ struct clk_hw hw;
+ struct qcom_rpm *rpm;
+};
+
+struct rpm_cc {
+ struct qcom_rpm *rpm;
+ struct clk_onecell_data data;
+ struct clk *clks[];
+};
+
+struct rpm_clk_desc {
+ struct clk_rpm **clks;
+ size_t num_clks;
+};
+
static DEFINE_MUTEX(rpm_clk_lock);
+static int clk_rpm_handoff(struct clk_rpm *r)
+{
+ int ret;
+ u32 value = INT_MAX;
+
+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
+ r->rpm_clk_id, &value, 1);
+ if (ret)
+ return ret;
+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
+ r->rpm_clk_id, &value, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate)
{
u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
r->rpm_clk_id, &value, 1);
}
+static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate)
+{
+ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
+
+ return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
+ r->rpm_clk_id, &value, 1);
+}
+
+static void to_active_sleep(struct clk_rpm *r, unsigned long rate,
+ unsigned long *active, unsigned long *sleep)
+{
+ *active = rate;
+
+ /*
+ * Active-only clocks don't care what the rate is during sleep. So,
+ * they vote for zero.
+ */
+ if (r->active_only)
+ *sleep = 0;
+ else
+ *sleep = *active;
+}
+
static int clk_rpm_prepare(struct clk_hw *hw)
{
struct clk_rpm *r = to_clk_rpm(hw);
- unsigned long rate = r->rate;
+ struct clk_rpm *peer = r->peer;
+ unsigned long this_rate = 0, this_sleep_rate = 0;
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
+ unsigned long active_rate, sleep_rate;
int ret = 0;
mutex_lock(&rpm_clk_lock);
- if (!rate)
+ /* Don't send requests to the RPM if the rate has not been set. */
+ if (!r->rate)
goto out;
- if (r->branch)
- rate = !!rate;
+ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
+
+ /* Take peer clock's rate into account only if it's enabled. */
+ if (peer->enabled)
+ to_active_sleep(peer, peer->rate,
+ &peer_rate, &peer_sleep_rate);
- ret = clk_rpm_set_rate_active(r, rate);
+ active_rate = max(this_rate, peer_rate);
+
+ if (r->branch)
+ active_rate = !!active_rate;
+ ret = clk_rpm_set_rate_active(r, active_rate);
if (ret)
goto out;
+ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
+ if (r->branch)
+ sleep_rate = !!sleep_rate;
+
+ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
+ if (ret)
+ /* Undo the active set vote and restore it */
+ ret = clk_rpm_set_rate_active(r, peer_rate);
+
out:
if (!ret)
r->enabled = true;
static void clk_rpm_unprepare(struct clk_hw *hw)
{
struct clk_rpm *r = to_clk_rpm(hw);
+ struct clk_rpm *peer = r->peer;
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
+ unsigned long active_rate, sleep_rate;
int ret;
mutex_lock(&rpm_clk_lock);
if (!r->rate)
goto out;
- ret = clk_rpm_set_rate_active(r, r->rate);
+ /* Take peer clock's rate into account only if it's enabled. */
+ if (peer->enabled)
+ to_active_sleep(peer, peer->rate, &peer_rate,
+ &peer_sleep_rate);
+
+ active_rate = r->branch ? !!peer_rate : peer_rate;
+ ret = clk_rpm_set_rate_active(r, active_rate);
+ if (ret)
+ goto out;
+
+ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
+ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
if (ret)
goto out;
}
static int clk_rpm_set_rate(struct clk_hw *hw,
- unsigned long rate, unsigned long parent_rate)
+ unsigned long rate, unsigned long parent_rate)
{
struct clk_rpm *r = to_clk_rpm(hw);
+ struct clk_rpm *peer = r->peer;
+ unsigned long active_rate, sleep_rate;
+ unsigned long this_rate = 0, this_sleep_rate = 0;
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
int ret = 0;
mutex_lock(&rpm_clk_lock);
- if (r->enabled)
- ret = clk_rpm_set_rate_active(r, rate);
+ if (!r->enabled)
+ goto out;
+
+ to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
- if (!ret)
- r->rate = rate;
+ /* Take peer clock's rate into account only if it's enabled. */
+ if (peer->enabled)
+ to_active_sleep(peer, peer->rate,
+ &peer_rate, &peer_sleep_rate);
+ active_rate = max(this_rate, peer_rate);
+ ret = clk_rpm_set_rate_active(r, active_rate);
+ if (ret)
+ goto out;
+
+ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
+ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
+ if (ret)
+ goto out;
+
+ r->rate = rate;
+
+out:
mutex_unlock(&rpm_clk_lock);
return ret;
}
+
static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
return r->rate;
}
-const struct clk_ops clk_rpm_ops = {
+static const struct clk_ops clk_rpm_ops = {
.prepare = clk_rpm_prepare,
.unprepare = clk_rpm_unprepare,
.set_rate = clk_rpm_set_rate,
.round_rate = clk_rpm_round_rate,
.recalc_rate = clk_rpm_recalc_rate,
};
-EXPORT_SYMBOL_GPL(clk_rpm_ops);
-const struct clk_ops clk_rpm_branch_ops = {
+static const struct clk_ops clk_rpm_branch_ops = {
.prepare = clk_rpm_prepare,
.unprepare = clk_rpm_unprepare,
.round_rate = clk_rpm_round_rate,
.recalc_rate = clk_rpm_recalc_rate,
};
-EXPORT_SYMBOL_GPL(clk_rpm_branch_ops);
-
-struct rpm_cc {
- struct qcom_rpm *rpm;
- struct clk_onecell_data data;
- struct clk *clks[];
-};
-
-struct rpm_clk_desc {
- struct clk_rpm **clks;
- size_t num_clks;
-};
/* apq8064 */
-DEFINE_CLK_RPM_PXO_BRANCH(apq8064, pxo, QCOM_RPM_PXO_CLK, 27000000);
-DEFINE_CLK_RPM_CXO_BRANCH(apq8064, cxo, QCOM_RPM_CXO_CLK, 19200000);
-DEFINE_CLK_RPM(apq8064, afab_clk, QCOM_RPM_APPS_FABRIC_CLK);
-DEFINE_CLK_RPM(apq8064, cfpb_clk, QCOM_RPM_CFPB_CLK);
-DEFINE_CLK_RPM(apq8064, daytona_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
-DEFINE_CLK_RPM(apq8064, ebi1_clk, QCOM_RPM_EBI1_CLK);
-DEFINE_CLK_RPM(apq8064, mmfab_clk, QCOM_RPM_MM_FABRIC_CLK);
-DEFINE_CLK_RPM(apq8064, mmfpb_clk, QCOM_RPM_MMFPB_CLK);
-DEFINE_CLK_RPM(apq8064, sfab_clk, QCOM_RPM_SYS_FABRIC_CLK);
-DEFINE_CLK_RPM(apq8064, sfpb_clk, QCOM_RPM_SFPB_CLK);
-DEFINE_CLK_RPM(apq8064, qdss_clk, QCOM_RPM_QDSS_CLK);
+DEFINE_CLK_RPM_PXO_BRANCH(apq8064, pxo, pxo_a_clk, QCOM_RPM_PXO_CLK, 27000000);
+DEFINE_CLK_RPM_CXO_BRANCH(apq8064, cxo, cxo_a_clk, QCOM_RPM_CXO_CLK, 19200000);
+DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
+DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
+DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
+DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
+DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK);
+DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK);
+DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
+DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
+DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
static struct clk_rpm *apq8064_clks[] = {
- [QCOM_RPM_PXO_CLK] = &apq8064_pxo,
- [QCOM_RPM_CXO_CLK] = &apq8064_cxo,
- [QCOM_RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
- [QCOM_RPM_CFPB_CLK] = &apq8064_cfpb_clk,
- [QCOM_RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk,
- [QCOM_RPM_EBI1_CLK] = &apq8064_ebi1_clk,
- [QCOM_RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk,
- [QCOM_RPM_MMFPB_CLK] = &apq8064_mmfpb_clk,
- [QCOM_RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk,
- [QCOM_RPM_SFPB_CLK] = &apq8064_sfpb_clk,
- [QCOM_RPM_QDSS_CLK] = &apq8064_qdss_clk,
+ [RPM_PXO_CLK] = &apq8064_pxo,
+ [RPM_PXO_A_CLK] = &apq8064_pxo_a_clk,
+ [RPM_CXO_CLK] = &apq8064_cxo,
+ [RPM_CXO_A_CLK] = &apq8064_cxo_a_clk,
+ [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
+ [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
+ [RPM_CFPB_CLK] = &apq8064_cfpb_clk,
+ [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk,
+ [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk,
+ [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk,
+ [RPM_EBI1_CLK] = &apq8064_ebi1_clk,
+ [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk,
+ [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk,
+ [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk,
+ [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk,
+ [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk,
+ [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk,
+ [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk,
+ [RPM_SFPB_CLK] = &apq8064_sfpb_clk,
+ [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk,
+ [RPM_QDSS_CLK] = &apq8064_qdss_clk,
+ [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
};
static const struct rpm_clk_desc rpm_clk_apq8064 = {
struct clk *clk;
struct rpm_cc *rcc;
struct clk_onecell_data *data;
- int ret, i;
- size_t num_clks;
+ int ret;
+ size_t num_clks, i;
struct qcom_rpm *rpm;
- struct clk_rpm **rpm_clks;
+ struct clk_rpm **rpm_clks;
const struct rpm_clk_desc *desc;
rpm = dev_get_drvdata(pdev->dev.parent);
}
rpm_clks[i]->rpm = rpm;
+
+ ret = clk_rpm_handoff(rpm_clks[i]);
+ if (ret)
+ goto err;
+ }
+
+ for (i = 0; i < num_clks; i++) {
+ if (!rpm_clks[i]) {
+ clks[i] = ERR_PTR(-ENOENT);
+ continue;
+ }
+
clk = devm_clk_register(&pdev->dev, &rpm_clks[i]->hw);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
if (ret)
goto err;
- clk_prepare_enable(apq8064_afab_clk.hw.clk);
-
return 0;
-
err:
dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret);
return ret;
+++ /dev/null
-/*
- * Copyright (c) 2015, Linaro Limited
- * Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __QCOM_CLK_RPM_H__
-#define __QCOM_CLK_RPM_H__
-
-#include <linux/clk-provider.h>
-
-struct qcom_rpm;
-
-struct clk_rpm {
- const int rpm_clk_id;
- unsigned long rate;
- bool enabled;
- bool branch;
- struct clk_hw hw;
- struct qcom_rpm *rpm;
-};
-
-extern const struct clk_ops clk_rpm_ops;
-extern const struct clk_ops clk_rpm_branch_ops;
-
-#define DEFINE_CLK_RPM(_platform, _name, r_id) \
- static struct clk_rpm _platform##_##_name = { \
- .rpm_clk_id = (r_id), \
- .rate = INT_MAX, \
- .hw.init = &(struct clk_init_data){ \
- .name = #_name, \
- .parent_names = (const char *[]){ "pxo_board" }, \
- .num_parents = 1, \
- .ops = &clk_rpm_ops, \
- }, \
- }
-
-#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, r_id, r) \
- static struct clk_rpm _platform##_##_name = { \
- .rpm_clk_id = (r_id), \
- .branch = true, \
- .rate = (r), \
- .hw.init = &(struct clk_init_data){ \
- .name = #_name, \
- .parent_names = (const char *[]){ "pxo_board" }, \
- .num_parents = 1, \
- .ops = &clk_rpm_branch_ops, \
- }, \
- }
-
-#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, r_id, r) \
- static struct clk_rpm _platform##_##_name = { \
- .rpm_clk_id = (r_id), \
- .branch = true, \
- .rate = (r), \
- .hw.init = &(struct clk_init_data){ \
- .name = #_name, \
- .parent_names = (const char *[]){ "cxo_board" }, \
- .num_parents = 1, \
- .ops = &clk_rpm_branch_ops, \
- }, \
- }
-#endif
/*
- * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2016, Linaro Limited
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
#include <linux/platform_device.h>
#include <linux/soc/qcom/smd-rpm.h>
-#include "clk-smd-rpm.h"
#include <dt-bindings/clock/qcom,rpmcc.h>
+#include <dt-bindings/mfd/qcom-rpm.h>
+
+#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
+#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
+#define QCOM_RPM_SMD_KEY_RATE 0x007a484b
+#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45
+#define QCOM_RPM_SMD_KEY_STATE 0x54415453
+#define QCOM_RPM_SCALING_ENABLE_ID 0x2
+
+#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \
+ key) \
+ static struct clk_smd_rpm _platform##_##_active; \
+ static struct clk_smd_rpm _platform##_##_name = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .rpm_key = (key), \
+ .peer = &_platform##_##_active, \
+ .rate = INT_MAX, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_smd_rpm_ops, \
+ .name = #_name, \
+ .parent_names = (const char *[]){ "xo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }; \
+ static struct clk_smd_rpm _platform##_##_active = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .active_only = true, \
+ .rpm_key = (key), \
+ .peer = &_platform##_##_name, \
+ .rate = INT_MAX, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_smd_rpm_ops, \
+ .name = #_active, \
+ .parent_names = (const char *[]){ "xo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }
+
+#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \
+ stat_id, r, key) \
+ static struct clk_smd_rpm _platform##_##_active; \
+ static struct clk_smd_rpm _platform##_##_name = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .rpm_key = (key), \
+ .branch = true, \
+ .peer = &_platform##_##_active, \
+ .rate = (r), \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_smd_rpm_branch_ops, \
+ .name = #_name, \
+ .parent_names = (const char *[]){ "xo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }; \
+ static struct clk_smd_rpm _platform##_##_active = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .active_only = true, \
+ .rpm_key = (key), \
+ .branch = true, \
+ .peer = &_platform##_##_name, \
+ .rate = (r), \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_smd_rpm_branch_ops, \
+ .name = #_active, \
+ .parent_names = (const char *[]){ "xo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }
+
+#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \
+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
+ 0, QCOM_RPM_SMD_KEY_RATE)
+
+#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \
+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \
+ r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE)
+
+#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \
+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
+ 0, QCOM_RPM_SMD_KEY_STATE)
+
+#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \
+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
+ QCOM_RPM_KEY_SOFTWARE_ENABLE)
+
+#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \
+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
+ QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY)
#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw)
+struct clk_smd_rpm {
+ const int rpm_res_type;
+ const int rpm_key;
+ const int rpm_clk_id;
+ const int rpm_status_id;
+ const bool active_only;
+ bool enabled;
+ bool branch;
+ struct clk_smd_rpm *peer;
+ struct clk_hw hw;
+ unsigned long rate;
+ struct qcom_smd_rpm *rpm;
+};
+
+struct clk_smd_rpm_req {
+ __le32 key;
+ __le32 nbytes;
+ __le32 value;
+};
+
+struct rpm_cc {
+ struct qcom_rpm *rpm;
+ struct clk_onecell_data data;
+ struct clk *clks[];
+};
+
+struct rpm_smd_clk_desc {
+ struct clk_smd_rpm **clks;
+ size_t num_clks;
+};
+
static DEFINE_MUTEX(rpm_smd_clk_lock);
+static int clk_smd_rpm_handoff(struct clk_smd_rpm *r)
+{
+ int ret;
+ struct clk_smd_rpm_req req = {
+ .key = cpu_to_le32(r->rpm_key),
+ .nbytes = cpu_to_le32(sizeof(u32)),
+ .value = cpu_to_le32(INT_MAX),
+ };
+
+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
+ r->rpm_res_type, r->rpm_clk_id, &req,
+ sizeof(req));
+ if (ret)
+ return ret;
+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
+ r->rpm_res_type, r->rpm_clk_id, &req,
+ sizeof(req));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r,
unsigned long rate)
{
return 0;
}
-const struct clk_ops clk_smd_rpm_ops = {
+static const struct clk_ops clk_smd_rpm_ops = {
.prepare = clk_smd_rpm_prepare,
.unprepare = clk_smd_rpm_unprepare,
.set_rate = clk_smd_rpm_set_rate,
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
};
-EXPORT_SYMBOL_GPL(clk_smd_rpm_ops);
-const struct clk_ops clk_smd_rpm_branch_ops = {
+static const struct clk_ops clk_smd_rpm_branch_ops = {
.prepare = clk_smd_rpm_prepare,
.unprepare = clk_smd_rpm_unprepare,
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
};
-EXPORT_SYMBOL_GPL(clk_smd_rpm_branch_ops);
-
-struct rpm_cc {
- struct qcom_rpm *rpm;
- struct clk_onecell_data data;
- struct clk *clks[];
-};
-
-struct rpm_smd_clk_desc {
- struct clk_smd_rpm **clks;
- size_t num_clks;
-};
/* msm8916 */
DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
-DEFINE_CLK_SMD_RPM_BRANCH(msm8916, xo, xo_a, QCOM_SMD_RPM_MISC_CLK, 0, 19200000);
DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1);
DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5);
static struct clk_smd_rpm *msm8916_clks[] = {
- [RPM_XO_CLK_SRC] = &msm8916_xo,
- [RPM_XO_A_CLK_SRC] = &msm8916_xo_a,
- [RPM_PCNOC_CLK] = &msm8916_pcnoc_clk,
- [RPM_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk,
- [RPM_SNOC_CLK] = &msm8916_snoc_clk,
- [RPM_SNOC_A_CLK] = &msm8916_snoc_a_clk,
- [RPM_BIMC_CLK] = &msm8916_bimc_clk,
- [RPM_BIMC_A_CLK] = &msm8916_bimc_a_clk,
- [RPM_QDSS_CLK] = &msm8916_qdss_clk,
- [RPM_QDSS_A_CLK] = &msm8916_qdss_a_clk,
- [RPM_BB_CLK1] = &msm8916_bb_clk1,
- [RPM_BB_CLK1_A] = &msm8916_bb_clk1_a,
- [RPM_BB_CLK2] = &msm8916_bb_clk2,
- [RPM_BB_CLK2_A] = &msm8916_bb_clk2_a,
- [RPM_RF_CLK1] = &msm8916_rf_clk1,
- [RPM_RF_CLK1_A] = &msm8916_rf_clk1_a,
- [RPM_RF_CLK2] = &msm8916_rf_clk2,
- [RPM_RF_CLK2_A] = &msm8916_rf_clk2_a,
- [RPM_BB_CLK1_PIN] = &msm8916_bb_clk1_pin,
- [RPM_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin,
- [RPM_BB_CLK2_PIN] = &msm8916_bb_clk2_pin,
- [RPM_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin,
- [RPM_RF_CLK1_PIN] = &msm8916_rf_clk1_pin,
- [RPM_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin,
- [RPM_RF_CLK2_PIN] = &msm8916_rf_clk2_pin,
- [RPM_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin,
+ [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk,
+ [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk,
+ [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk,
+ [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk,
+ [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk,
+ [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk,
+ [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk,
+ [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk,
+ [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1,
+ [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a,
+ [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2,
+ [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a,
+ [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1,
+ [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a,
+ [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2,
+ [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a,
+ [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin,
+ [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin,
+ [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin,
+ [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin,
+ [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin,
+ [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin,
+ [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin,
+ [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin,
};
static const struct rpm_smd_clk_desc rpm_clk_msm8916 = {
.num_clks = ARRAY_SIZE(msm8916_clks),
};
-/* msm8974 */
-DEFINE_CLK_SMD_RPM(msm8974, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
-DEFINE_CLK_SMD_RPM(msm8974, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
-DEFINE_CLK_SMD_RPM(msm8974, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2);
-DEFINE_CLK_SMD_RPM(msm8974, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, QCOM_SMD_RPM_BUS_CLK, 3);
-DEFINE_CLK_SMD_RPM(msm8974, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
-DEFINE_CLK_SMD_RPM(msm8974, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2);
-DEFINE_CLK_SMD_RPM(msm8974, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1);
-DEFINE_CLK_SMD_RPM_BRANCH(msm8974, cxo_clk_src, cxo_a_clk_src, QCOM_SMD_RPM_MISC_CLK, 0, 19200000);
-DEFINE_CLK_SMD_RPM_QDSS(msm8974, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d0, cxo_d0_a, 1);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d1, cxo_d1_a, 2);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a0, cxo_a0_a, 4);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a1, cxo_a1_a, 5);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a2, cxo_a2_a, 6);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk1, div_a_clk1, 11);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk2, div_a_clk2, 12);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, diff_clk, diff_a_clk, 7);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d0_pin, cxo_d0_a_pin, 1);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d1_pin, cxo_d1_a_pin, 2);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a0_pin, cxo_a0_a_pin, 4);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a1_pin, cxo_a1_a_pin, 5);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a2_pin, cxo_a2_a_pin, 6);
-
-static struct clk_smd_rpm *msm8974_clks[] = {
- [RPM_CXO_CLK_SRC] = &msm8974_cxo_clk_src,
- [RPM_CXO_A_CLK_SRC] = &msm8974_cxo_a_clk_src,
- [RPM_PNOC_CLK] = &msm8974_pnoc_clk,
- [RPM_PNOC_A_CLK] = &msm8974_pnoc_a_clk,
- [RPM_SNOC_CLK] = &msm8974_snoc_clk,
- [RPM_SNOC_A_CLK] = &msm8974_snoc_a_clk,
- [RPM_BIMC_CLK] = &msm8974_bimc_clk,
- [RPM_BIMC_A_CLK] = &msm8974_bimc_a_clk,
- [RPM_QDSS_CLK] = &msm8974_qdss_clk,
- [RPM_QDSS_A_CLK] = &msm8974_qdss_a_clk,
- [RPM_CNOC_CLK] = &msm8974_cnoc_clk,
- [RPM_CNOC_A_CLK] = &msm8974_cnoc_a_clk,
- [RPM_MMSSNOC_AHB_CLK] = &msm8974_mmssnoc_ahb_clk,
- [RPM_MMSSNOC_AHB_A_CLK] = &msm8974_mmssnoc_ahb_a_clk,
- [RPM_OCMEMGX_CLK] = &msm8974_ocmemgx_clk,
- [RPM_OCMEMGX_A_CLK] = &msm8974_ocmemgx_a_clk,
- [RPM_GFX3D_CLK_SRC] = &msm8974_gfx3d_clk_src,
- [RPM_GFX3D_A_CLK_SRC] = &msm8974_gfx3d_a_clk_src,
- [RPM_CXO_D0] = &msm8974_cxo_d0,
- [RPM_CXO_D0_A] = &msm8974_cxo_d0_a,
- [RPM_CXO_D1] = &msm8974_cxo_d1,
- [RPM_CXO_D1_A] = &msm8974_cxo_d1_a,
- [RPM_CXO_A0] = &msm8974_cxo_a0,
- [RPM_CXO_A0_A] = &msm8974_cxo_a0_a,
- [RPM_CXO_A1] = &msm8974_cxo_a1,
- [RPM_CXO_A1_A] = &msm8974_cxo_a1_a,
- [RPM_CXO_A2] = &msm8974_cxo_a2,
- [RPM_CXO_A2_A] = &msm8974_cxo_a2_a,
- [RPM_DIV_CLK1] = &msm8974_div_clk1,
- [RPM_DIV_A_CLK1] = &msm8974_div_a_clk1,
- [RPM_DIV_CLK2] = &msm8974_div_clk2,
- [RPM_DIV_A_CLK2] = &msm8974_div_a_clk2,
- [RPM_DIFF_CLK] = &msm8974_diff_clk,
- [RPM_DIFF_A_CLK] = &msm8974_diff_a_clk,
- [RPM_CXO_D0_PIN] = &msm8974_cxo_d0_pin,
- [RPM_CXO_D0_A_PIN] = &msm8974_cxo_d0_a_pin,
- [RPM_CXO_D1_PIN] = &msm8974_cxo_d1_pin,
- [RPM_CXO_D1_A_PIN] = &msm8974_cxo_d1_a_pin,
- [RPM_CXO_A0_PIN] = &msm8974_cxo_a0_pin,
- [RPM_CXO_A0_A_PIN] = &msm8974_cxo_a0_a_pin,
- [RPM_CXO_A1_PIN] = &msm8974_cxo_a1_pin,
- [RPM_CXO_A1_A_PIN] = &msm8974_cxo_a1_a_pin,
- [RPM_CXO_A2_PIN] = &msm8974_cxo_a2_pin,
- [RPM_CXO_A2_A_PIN] = &msm8974_cxo_a2_a_pin,
-};
-
-static const struct rpm_smd_clk_desc rpm_clk_msm8974 = {
- .clks = msm8974_clks,
- .num_clks = ARRAY_SIZE(msm8974_clks),
-};
-
-/* apq8084 */
-DEFINE_CLK_SMD_RPM(apq8084, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
-DEFINE_CLK_SMD_RPM(apq8084, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
-DEFINE_CLK_SMD_RPM(apq8084, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2);
-DEFINE_CLK_SMD_RPM(apq8084, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, QCOM_SMD_RPM_BUS_CLK, 3);
-DEFINE_CLK_SMD_RPM(apq8084, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
-DEFINE_CLK_SMD_RPM(apq8084, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2);
-DEFINE_CLK_SMD_RPM(apq8084, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1);
-DEFINE_CLK_SMD_RPM_BRANCH(apq8084, xo_clk_src, xo_a_clk_src, QCOM_SMD_RPM_MISC_CLK, 0, 19200000);
-DEFINE_CLK_SMD_RPM_QDSS(apq8084, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
-
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, bb_clk1, bb_clk1_a, 1);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, bb_clk2, bb_clk2_a, 2);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, rf_clk1, rf_clk1_a, 4);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, rf_clk2, rf_clk2_a, 5);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, rf_clk3, rf_clk3_a, 6);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, diff_clk1, diff_clk1_a, 7);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, div_clk1, div_clk1_a, 11);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, div_clk2, div_clk2_a, 12);
-DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, div_clk3, div_clk3_a, 13);
-
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, bb_clk1_pin, bb_clk1_a_pin, 1);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, bb_clk2_pin, bb_clk2_a_pin, 2);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, rf_clk1_pin, rf_clk1_a_pin, 4);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, rf_clk2_pin, rf_clk2_a_pin, 5);
-DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, rf_clk3_pin, rf_clk3_a_pin, 6);
-
-static struct clk_smd_rpm *apq8084_clks[] = {
- [RPM_XO_CLK_SRC] = &apq8084_xo_clk_src,
- [RPM_XO_A_CLK_SRC] = &apq8084_xo_a_clk_src,
- [RPM_PNOC_CLK] = &apq8084_pnoc_clk,
- [RPM_PNOC_A_CLK] = &apq8084_pnoc_a_clk,
- [RPM_SNOC_CLK] = &apq8084_snoc_clk,
- [RPM_SNOC_A_CLK] = &apq8084_snoc_a_clk,
- [RPM_BIMC_CLK] = &apq8084_bimc_clk,
- [RPM_BIMC_A_CLK] = &apq8084_bimc_a_clk,
- [RPM_QDSS_CLK] = &apq8084_qdss_clk,
- [RPM_QDSS_A_CLK] = &apq8084_qdss_a_clk,
- [RPM_CNOC_CLK] = &apq8084_cnoc_clk,
- [RPM_CNOC_A_CLK] = &apq8084_cnoc_a_clk,
- [RPM_MMSSNOC_AHB_CLK] = &apq8084_mmssnoc_ahb_clk,
- [RPM_MMSSNOC_AHB_A_CLK] = &apq8084_mmssnoc_ahb_a_clk,
- [RPM_OCMEMGX_CLK] = &apq8084_ocmemgx_clk,
- [RPM_OCMEMGX_A_CLK] = &apq8084_ocmemgx_a_clk,
- [RPM_GFX3D_CLK_SRC] = &apq8084_gfx3d_clk_src,
- [RPM_GFX3D_A_CLK_SRC] = &apq8084_gfx3d_a_clk_src,
- [RPM_BB_CLK1] = &apq8084_bb_clk1,
- [RPM_BB_CLK1_A] = &apq8084_bb_clk1_a,
- [RPM_BB_CLK2] = &apq8084_bb_clk2,
- [RPM_BB_CLK2_A] = &apq8084_bb_clk2_a,
- [RPM_RF_CLK1] = &apq8084_rf_clk1,
- [RPM_RF_CLK1_A] = &apq8084_rf_clk1_a,
- [RPM_RF_CLK2] = &apq8084_rf_clk2,
- [RPM_RF_CLK2_A] = &apq8084_rf_clk2_a,
- [RPM_RF_CLK3] = &apq8084_rf_clk3,
- [RPM_RF_CLK3_A] = &apq8084_rf_clk3_a,
- [RPM_DIFF_CLK1] = &apq8084_diff_clk1,
- [RPM_DIFF_CLK1_A] = &apq8084_diff_clk1_a,
- [RPM_DIV_CLK1] = &apq8084_div_clk1,
- [RPM_DIV_CLK1_A] = &apq8084_div_clk1_a,
- [RPM_DIV_CLK2] = &apq8084_div_clk2,
- [RPM_DIV_CLK2_A] = &apq8084_div_clk2_a,
- [RPM_DIV_CLK3] = &apq8084_div_clk3,
- [RPM_DIV_CLK3_A] = &apq8084_div_clk3_a,
- [RPM_BB_CLK1_PIN] = &apq8084_bb_clk1_pin,
- [RPM_BB_CLK1_A_PIN] = &apq8084_bb_clk1_a_pin,
- [RPM_BB_CLK2_PIN] = &apq8084_bb_clk2_pin,
- [RPM_BB_CLK2_A_PIN] = &apq8084_bb_clk2_a_pin,
- [RPM_RF_CLK1_PIN] = &apq8084_rf_clk1_pin,
- [RPM_RF_CLK1_A_PIN] = &apq8084_rf_clk1_a_pin,
- [RPM_RF_CLK2_PIN] = &apq8084_rf_clk2_pin,
- [RPM_RF_CLK2_A_PIN] = &apq8084_rf_clk2_a_pin,
- [RPM_RF_CLK3_PIN] = &apq8084_rf_clk3_pin,
- [RPM_RF_CLK3_A_PIN] = &apq8084_rf_clk3_a_pin,
-};
-
-static const struct rpm_smd_clk_desc rpm_clk_apq8084 = {
- .clks = apq8084_clks,
- .num_clks = ARRAY_SIZE(apq8084_clks),
-};
-
static const struct of_device_id rpm_smd_clk_match_table[] = {
{ .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916},
- { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974},
- { .compatible = "qcom,rpmcc-apq8084", .data = &rpm_clk_apq8084},
{ }
};
MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
struct clk *clk;
struct rpm_cc *rcc;
struct clk_onecell_data *data;
- int ret, i;
- size_t num_clks;
+ int ret;
+ size_t num_clks, i;
struct qcom_smd_rpm *rpm;
struct clk_smd_rpm **rpm_smd_clks;
const struct rpm_smd_clk_desc *desc;
}
rpm_smd_clks[i]->rpm = rpm;
+
+ ret = clk_smd_rpm_handoff(rpm_smd_clks[i]);
+ if (ret)
+ goto err;
+ }
+
+ ret = clk_smd_rpm_enable_scaling(rpm);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < num_clks; i++) {
+ if (!rpm_smd_clks[i]) {
+ clks[i] = ERR_PTR(-ENOENT);
+ continue;
+ }
+
clk = devm_clk_register(&pdev->dev, &rpm_smd_clks[i]->hw);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
if (ret)
goto err;
- ret = clk_smd_rpm_enable_scaling(rpm);
- if (ret) {
- of_clk_del_provider(pdev->dev.of_node);
- goto err;
- }
-
return 0;
err:
dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret);
+++ /dev/null
-/*
- * Copyright (c) 2015, Linaro Limited
- * Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __QCOM_CLK_SMD_RPM_H__
-#define __QCOM_CLK_SMD_RPM_H__
-
-#include <linux/clk-provider.h>
-
-#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
-#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
-#define QCOM_RPM_SMD_KEY_RATE 0x007a484b
-#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45
-#define QCOM_RPM_SMD_KEY_STATE 0x54415453
-#define QCOM_RPM_SCALING_ENABLE_ID 0x2
-
-struct qcom_smd_rpm;
-
-struct clk_smd_rpm {
- const int rpm_res_type;
- const int rpm_key;
- const int rpm_clk_id;
- const int rpm_status_id;
- const bool active_only;
- bool enabled;
- bool branch;
- struct clk_smd_rpm *peer;
- struct clk_hw hw;
- unsigned long rate;
- struct qcom_smd_rpm *rpm;
-};
-
-struct clk_smd_rpm_req {
- __le32 key;
- __le32 nbytes;
- __le32 value;
-};
-
-extern const struct clk_ops clk_smd_rpm_ops;
-extern const struct clk_ops clk_smd_rpm_branch_ops;
-
-#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \
- key) \
- static struct clk_smd_rpm _platform##_##_active; \
- static struct clk_smd_rpm _platform##_##_name = { \
- .rpm_res_type = (type), \
- .rpm_clk_id = (r_id), \
- .rpm_status_id = (stat_id), \
- .rpm_key = (key), \
- .peer = &_platform##_##_active, \
- .rate = INT_MAX, \
- .hw.init = &(struct clk_init_data){ \
- .ops = &clk_smd_rpm_ops, \
- .name = #_name, \
- .parent_names = (const char *[]){ "xo_board" }, \
- .num_parents = 1, \
- }, \
- }; \
- static struct clk_smd_rpm _platform##_##_active = { \
- .rpm_res_type = (type), \
- .rpm_clk_id = (r_id), \
- .rpm_status_id = (stat_id), \
- .rpm_key = (key), \
- .peer = &_platform##_##_name, \
- .active_only = true, \
- .rate = INT_MAX, \
- .hw.init = &(struct clk_init_data){ \
- .ops = &clk_smd_rpm_ops, \
- .name = #_active, \
- .parent_names = (const char *[]){ "xo_board" }, \
- .num_parents = 1, \
- }, \
- }
-
-#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \
- stat_id, r, key) \
- static struct clk_smd_rpm _platform##_##_active; \
- static struct clk_smd_rpm _platform##_##_name = { \
- .rpm_res_type = (type), \
- .rpm_clk_id = (r_id), \
- .rpm_status_id = (stat_id), \
- .rpm_key = (key), \
- .peer = &_platform##_##_active, \
- .branch = true, \
- .rate = (r), \
- .hw.init = &(struct clk_init_data){ \
- .ops = &clk_smd_rpm_branch_ops, \
- .name = #_name, \
- .parent_names = (const char *[]){ "xo_board" }, \
- .num_parents = 1, \
- }, \
- }; \
- static struct clk_smd_rpm _platform##_##_active = { \
- .rpm_res_type = (type), \
- .rpm_clk_id = (r_id), \
- .rpm_status_id = (stat_id), \
- .rpm_key = (key), \
- .peer = &_platform##_##_name, \
- .active_only = true, \
- .branch = true, \
- .rate = (r), \
- .hw.init = &(struct clk_init_data){ \
- .ops = &clk_smd_rpm_branch_ops, \
- .name = #_active, \
- .parent_names = (const char *[]){ "xo_board" }, \
- .num_parents = 1, \
- }, \
- }
-
-#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \
- __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
- 0, QCOM_RPM_SMD_KEY_RATE)
-
-#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \
- __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \
- r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE)
-
-#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \
- __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
- 0, QCOM_RPM_SMD_KEY_STATE)
-
-#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \
- __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
- QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
- QCOM_RPM_KEY_SOFTWARE_ENABLE)
-
-#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \
- __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
- QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
- QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY)
-
-#endif
req, req_cnt * sizeof(*req), resp, sizeof(*resp));
}
-int __qcom_scm_restart_proc(u32 proc_id, int restart, u32 *resp)
+int __qcom_scm_pas_mss_reset(bool reset)
{
+ __le32 val = cpu_to_le32(reset);
+ __le32 resp;
- return qcom_scm_call(QCOM_SCM_SVC_PIL, proc_id,
- &restart, sizeof(restart),
+ return qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET,
+ &val, sizeof(val),
&resp, sizeof(resp));
}
return ret ? false : !!ret_val;
}
-int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size)
+int __qcom_scm_pas_init_image(u32 peripheral, dma_addr_t metadata_phys)
{
- dma_addr_t mdata_phys;
- void *mdata_buf;
- u32 scm_ret;
+ __le32 scm_ret;
int ret;
- struct pas_init_image_req {
- u32 proc;
- u32 image_addr;
+ struct {
+ __le32 proc;
+ __le32 image_addr;
} request;
- /*
- * During the scm call memory protection will be enabled for the meta
- * data blob, so make sure it's physically contiguous, 4K aligned and
- * non-cachable to avoid XPU violations.
- */
- mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL);
- if (!mdata_buf) {
- pr_err("Allocation of metadata buffer failed.\n");
- return -ENOMEM;
- }
- memcpy(mdata_buf, metadata, size);
-
- request.proc = peripheral;
- request.image_addr = mdata_phys;
+ request.proc = cpu_to_le32(peripheral);
+ request.image_addr = cpu_to_le32(metadata_phys);
ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
&request, sizeof(request),
&scm_ret, sizeof(scm_ret));
- dma_free_coherent(dev, size, mdata_buf, mdata_phys);
-
- return ret ? : scm_ret;
+ return ret ? : le32_to_cpu(scm_ret);
}
int __qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
return ret;
}
-int __qcom_scm_restart_proc(u32 proc_id, int restart, u32 *resp)
+int __qcom_scm_pas_mss_reset(bool reset)
{
- int ret;
struct qcom_scm_desc desc = {0};
- desc.args[0] = restart;
+ desc.args[0] = reset;
desc.args[1] = 0;
desc.arginfo = QCOM_SCM_ARGS(2);
- ret = qcom_scm_call(QCOM_SCM_SVC_PIL, proc_id,
- &desc);
- *resp = desc.ret[0];
-
- return ret;
+ return qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc);
}
bool __qcom_scm_pas_supported(u32 peripheral)
return ret ? false : !!desc.ret[0];
}
-int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size)
+int __qcom_scm_pas_init_image(u32 peripheral, dma_addr_t metadata_phys)
{
int ret;
struct qcom_scm_desc desc = {0};
u32 scm_ret;
- dma_addr_t mdata_phys;
- void *mdata_buf;
-
-dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
-
- /*
- * During the scm call memory protection will be enabled for the meta
- * data blob, so make sure it's physically contiguous, 4K aligned and
- * non-cachable to avoid XPU violations.
- */
- mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL);
- if (!mdata_buf) {
- pr_err("Allocation of metadata buffer failed.\n");
- return -ENOMEM;
- }
- memcpy(mdata_buf, metadata, size);
desc.args[0] = peripheral;
- desc.args[1] = mdata_phys;
+ desc.args[1] = metadata_phys;
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW);
ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
&desc);
scm_ret = desc.ret[0];
- dma_free_coherent(dev, size, mdata_buf, mdata_phys);
return ret ? : scm_ret;
}
#include <linux/module.h>
#include <linux/cpumask.h>
#include <linux/export.h>
+#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/qcom_scm.h>
#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/reset-controller.h>
#include "qcom_scm.h"
struct qcom_scm {
+ struct device *dev;
struct clk *core_clk;
struct clk *iface_clk;
struct clk *bus_clk;
+
+ struct reset_controller_dev reset;
};
static struct qcom_scm *__scm;
{
int ret;
- if(__scm->core_clk) {
- ret = clk_prepare_enable(__scm->core_clk);
- if (ret)
- goto bail;
- }
-
- if(__scm->iface_clk) {
- ret = clk_prepare_enable(__scm->iface_clk);
- if (ret)
- goto disable_core;
- }
-
- if(__scm->bus_clk) {
- ret = clk_prepare_enable(__scm->bus_clk);
- if (ret)
- goto disable_iface;
- }
+ ret = clk_prepare_enable(__scm->core_clk);
+ if (ret)
+ goto bail;
+ ret = clk_prepare_enable(__scm->iface_clk);
+ if (ret)
+ goto disable_core;
+ ret = clk_prepare_enable(__scm->bus_clk);
+ if (ret)
+ goto disable_iface;
return 0;
disable_iface:
- if(__scm->iface_clk)
- clk_disable_unprepare(__scm->iface_clk);
+ clk_disable_unprepare(__scm->iface_clk);
disable_core:
- if(__scm->core_clk)
- clk_disable_unprepare(__scm->core_clk);
+ clk_disable_unprepare(__scm->core_clk);
bail:
return ret;
}
static void qcom_scm_clk_disable(void)
{
- if(__scm->core_clk)
- clk_disable_unprepare(__scm->core_clk);
- if(__scm->iface_clk)
- clk_disable_unprepare(__scm->iface_clk);
- if(__scm->bus_clk)
- clk_disable_unprepare(__scm->bus_clk);
+ clk_disable_unprepare(__scm->core_clk);
+ clk_disable_unprepare(__scm->iface_clk);
+ clk_disable_unprepare(__scm->bus_clk);
}
/**
}
EXPORT_SYMBOL(qcom_scm_hdcp_req);
-int qcom_scm_restart_proc(u32 pid, int restart, u32 *resp)
-{
- return __qcom_scm_restart_proc(pid, restart, resp);
-}
-EXPORT_SYMBOL(qcom_scm_restart_proc);
/**
* qcom_scm_pas_supported() - Check if the peripheral authentication service is
* available for the given peripherial
*
* Returns 0 on success.
*/
-int qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size)
+int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
{
- return __qcom_scm_pas_init_image(dev, peripheral, metadata, size);
+ dma_addr_t mdata_phys;
+ void *mdata_buf;
+ int ret;
+
+ /*
+ * During the scm call memory protection will be enabled for the meta
+ * data blob, so make sure it's physically contiguous, 4K aligned and
+ * non-cachable to avoid XPU violations.
+ */
+ mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, GFP_KERNEL);
+ if (!mdata_buf) {
+ dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
+ return -ENOMEM;
+ }
+ memcpy(mdata_buf, metadata, size);
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ goto free_metadata;
+
+ ret = __qcom_scm_pas_init_image(peripheral, mdata_phys);
+
+ qcom_scm_clk_disable();
+
+free_metadata:
+ dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
+
+ return ret;
}
EXPORT_SYMBOL(qcom_scm_pas_init_image);
*/
int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
{
- return __qcom_scm_pas_mem_setup(peripheral, addr, size);
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = __qcom_scm_pas_mem_setup(peripheral, addr, size);
+ qcom_scm_clk_disable();
+
+ return ret;
}
EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
*/
int qcom_scm_pas_auth_and_reset(u32 peripheral)
{
- return __qcom_scm_pas_auth_and_reset(peripheral);
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = __qcom_scm_pas_auth_and_reset(peripheral);
+ qcom_scm_clk_disable();
+
+ return ret;
}
EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
*/
int qcom_scm_pas_shutdown(u32 peripheral)
{
- return __qcom_scm_pas_shutdown(peripheral);
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = __qcom_scm_pas_shutdown(peripheral);
+ qcom_scm_clk_disable();
+
+ return ret;
}
EXPORT_SYMBOL(qcom_scm_pas_shutdown);
return __qcom_scm_init();
}
+static int qcom_scm_reset_assert(struct reset_controller_dev *rcdev, unsigned long idx)
+{
+ return __qcom_scm_pas_mss_reset(1);
+}
+
+static int qcom_scm_reset_deassert(struct reset_controller_dev *rcdev, unsigned long idx)
+{
+ return __qcom_scm_pas_mss_reset(0);
+}
+
+static struct reset_control_ops scm_reset_ops = {
+ .assert = qcom_scm_reset_assert,
+ .deassert = qcom_scm_reset_deassert,
+};
+
static int qcom_scm_probe(struct platform_device *pdev)
{
struct qcom_scm *scm;
if (!scm)
return -ENOMEM;
+ scm->dev = &pdev->dev;
+
scm->core_clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(scm->core_clk)) {
- if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "failed to acquire core clk\n");
+ if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_err(&pdev->dev, "failed to acquire core clk\n");
scm->core_clk = NULL;
}
scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(scm->iface_clk)) {
- if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "failed to acquire iface clk\n");
+ if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_err(&pdev->dev, "failed to acquire iface clk\n");
scm->iface_clk = NULL;
}
scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
if (IS_ERR(scm->bus_clk)) {
- if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "failed to acquire bus clk\n");
+ if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(&pdev->dev, "failed to acquire bus clk\n");
scm->bus_clk = NULL;
}
- if (scm->core_clk) {
+ scm->reset.ops = &scm_reset_ops;
+ scm->reset.nr_resets = 1;
+ scm->reset.of_node = pdev->dev.of_node;
+ reset_controller_register(&scm->reset);
+
/* vote for max clk rate for highest performance */
- rate = clk_round_rate(scm->core_clk, INT_MAX);
- ret = clk_set_rate(scm->core_clk, rate);
- if (ret)
- return ret;
- }
+ rate = clk_round_rate(scm->core_clk, INT_MAX);
+ ret = clk_set_rate(scm->core_clk, rate);
+ if (ret)
+ return ret;
__scm = scm;
#define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5
#define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6
#define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7
+#define QCOM_SCM_PAS_MSS_RESET 0xa
extern bool __qcom_scm_pas_supported(u32 peripheral);
-extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size);
+extern int __qcom_scm_pas_init_image(u32 peripheral, dma_addr_t metadata_phys);
extern int __qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size);
extern int __qcom_scm_pas_auth_and_reset(u32 peripheral);
extern int __qcom_scm_pas_shutdown(u32 peripheral);
+extern int __qcom_scm_pas_mss_reset(bool reset);
/* common error codes */
#define QCOM_SCM_ENOMEM -5
extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id);
extern int __qcom_scm_get_feat_version(u32 feat);
extern int __qcom_scm_restore_sec_cfg(u32 device_id, u32 spare);
-extern int __qcom_scm_restart_proc(u32 proc_id, int restart, u32 *resp);
extern int __qcom_scm_set_video_state(u32 state, u32 spare);
extern int __qcom_scm_mem_protect_video_var(u32 start, u32 size,
help
Say yes here to support the PXA GPIO device
-config GPIO_QCOM_SMSM
- bool "Qualcomm Shared Memory State Machine"
- depends on QCOM_SMEM
- help
- Say yes here to support the Qualcomm Shared Memory State Machine.
- The state machine is represented by bits in shared memory and is
- exposed to the system as GPIOs.
-
-config GPIO_QCOM_SMP2P
- bool "Qualcomm Shared Memory Point to Point support"
- depends on QCOM_SMEM
- help
- Say yes here to support the Qualcomm Shared Memory Point to Point
- protocol, exposed to the system as GPIOs.
-
config GPIO_RCAR
tristate "Renesas R-Car GPIO"
depends on ARCH_SHMOBILE || COMPILE_TEST
obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
-obj-$(CONFIG_GPIO_QCOM_SMSM) += gpio-qcom-smsm.o
-obj-$(CONFIG_GPIO_QCOM_SMP2P) += gpio-qcom-smp2p.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
+++ /dev/null
-/*
- * Copyright (c) 2015, Sony Mobile Communications AB.
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/memblock.h>
-#include <linux/slab.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/regmap.h>
-#include <linux/list.h>
-#include <linux/gpio.h>
-#include <linux/mfd/syscon.h>
-
-#include <linux/delay.h>
-
-#include <linux/soc/qcom/smem.h>
-
-/*
- * The Shared Memory Point to Point (SMP2P) protocol facilitates communication
- * of a single 32-bit value between two processors. Each value has a single
- * writer (the local side) and a single reader (the remote side). Values are
- * uniquely identified in the system by the directed edge (local processor ID
- * to remote processor ID) and a string identifier.
- *
- * Each processor is responsible for creating the outgoing SMEM items and each
- * item is writable by the local processor and readable by the remote
- * processor. By using two separate SMEM items that are single-reader and
- * single-writer, SMP2P does not require any remote locking mechanisms.
- *
- * The driver uses the Linux GPIO and interrupt framework to expose a virtual
- * GPIO for each outbound entry and a virtual interrupt controller for each
- * inbound entry.
- */
-
-#define SMP2P_MAX_ENTRY 16
-#define SMP2P_MAX_ENTRY_NAME 16
-
-#define SMP2P_FEATURE_SSR_ACK 0x1
-
-#define SMP2P_MAGIC 0x504d5324
-
-/**
- * struct smp2p_smem_item - in memory communication structure
- * @magic: magic number
- * @version: version - must be 1
- * @features: features flag - currently unused
- * @local_pid: processor id of sending end
- * @remote_pid: processor id of receiving end
- * @total_entries: number of entries - always SMP2P_MAX_ENTRY
- * @valid_entries: number of allocated entries
- * @flags:
- * @entries: individual communication entries
- * @name: name of the entry
- * @value: content of the entry
- */
-struct smp2p_smem_item {
- u32 magic;
- u8 version;
- unsigned features:24;
- u16 local_pid;
- u16 remote_pid;
- u16 total_entries;
- u16 valid_entries;
- u32 flags;
-
- struct {
- u8 name[SMP2P_MAX_ENTRY_NAME];
- u32 value;
- } entries[SMP2P_MAX_ENTRY];
-} __packed;
-
-/**
- * struct smp2p_entry - driver context matching one entry
- * @node: list entry to keep track of allocated entries
- * @smp2p: reference to the device driver context
- * @name: name of the entry, to match against smp2p_smem_item
- * @value: pointer to smp2p_smem_item entry value
- * @last_value: last handled value
- * @domain: irq_domain for inbound entries
- * @irq_enabled:bitmap to track enabled irq bits
- * @irq_rising: bitmap to mark irq bits for rising detection
- * @irq_falling:bitmap to mark irq bits for falling detection
- * @chip: gpio_chip for outbound entries
- */
-struct smp2p_entry {
- struct list_head node;
- struct qcom_smp2p *smp2p;
-
- const char *name;
- u32 *value;
- u32 last_value;
-
- struct irq_domain *domain;
- DECLARE_BITMAP(irq_enabled, 32);
- DECLARE_BITMAP(irq_rising, 32);
- DECLARE_BITMAP(irq_falling, 32);
-
- struct gpio_chip chip;
-};
-
-#define SMP2P_INBOUND 0
-#define SMP2P_OUTBOUND 1
-
-/**
- * struct qcom_smp2p - device driver context
- * @dev: device driver handle
- * @in: pointer to the inbound smem item
- * @smem_items: ids of the two smem items
- * @valid_entries: already scanned inbound entries
- * @local_pid: processor id of the inbound edge
- * @remote_pid: processor id of the outbound edge
- * @ipc_regmap: regmap for the outbound ipc
- * @ipc_offset: offset within the regmap
- * @ipc_bit: bit in regmap@offset to kick to signal remote processor
- * @inbound: list of inbound entries
- * @outbound: list of outbound entries
- */
-struct qcom_smp2p {
- struct device *dev;
-
- struct smp2p_smem_item *in;
- struct smp2p_smem_item *out;
-
- unsigned smem_items[SMP2P_OUTBOUND + 1];
-
- unsigned valid_entries;
-
- unsigned local_pid;
- unsigned remote_pid;
-
- struct regmap *ipc_regmap;
- int ipc_offset;
- int ipc_bit;
-
- struct list_head inbound;
- struct list_head outbound;
-};
-
-static void qcom_smp2p_kick(struct qcom_smp2p *smp2p)
-{
- /* Make sure any updated data is written before the kick */
- wmb();
- regmap_write(smp2p->ipc_regmap, smp2p->ipc_offset, BIT(smp2p->ipc_bit));
-}
-
-static irqreturn_t qcom_smp2p_intr(int irq, void *data)
-{
- struct smp2p_smem_item *in;
- struct smp2p_entry *entry;
- struct qcom_smp2p *smp2p = data;
- unsigned smem_id = smp2p->smem_items[SMP2P_INBOUND];
- unsigned pid = smp2p->remote_pid;
- size_t size;
- int irq_pin;
- u32 status;
- u32 val;
- int i;
- u8 tmp_name[SMP2P_MAX_ENTRY_NAME];
-
- in = smp2p->in;
-
- /* Acquire smem item, if not already found */
- if (!in) {
- in = qcom_smem_get(pid, smem_id, &size);
- if (IS_ERR(in)) {
- dev_err(smp2p->dev,
- "Unable to acquire remote smp2p item\n");
- return IRQ_HANDLED;
- }
-
- smp2p->in = in;
- }
-
- /* Match newly created entries */
- for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
- list_for_each_entry(entry, &smp2p->inbound, node) {
- memcpy_fromio(tmp_name, in->entries[i].name,
- SMP2P_MAX_ENTRY_NAME);
- if (!strcmp(tmp_name, entry->name)) {
- entry->value = &in->entries[i].value;
- break;
- }
- }
- }
- smp2p->valid_entries = i;
-
- /* Fire interrupts based on any value changes */
- list_for_each_entry(entry, &smp2p->inbound, node) {
- /* Ignore entries not yet allocated by the remote side */
- if (!entry->value)
- continue;
-
- val = *entry->value;
-
- status = val ^ entry->last_value;
- entry->last_value = val;
-
- /* No changes of this entry? */
- if (!status)
- continue;
-
- for_each_set_bit(i, entry->irq_enabled, 32) {
- if (!(status & BIT(i)))
- continue;
-
- if (val & BIT(i)) {
- if (test_bit(i, entry->irq_rising)) {
- irq_pin = irq_find_mapping(entry->domain, i);
- handle_nested_irq(irq_pin);
- }
- } else {
- if (test_bit(i, entry->irq_falling)) {
- irq_pin = irq_find_mapping(entry->domain, i);
- handle_nested_irq(irq_pin);
- }
- }
-
- }
- }
-
- return IRQ_HANDLED;
-}
-
-static void smp2p_mask_irq(struct irq_data *irqd)
-{
- struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
- irq_hw_number_t irq = irqd_to_hwirq(irqd);
-
- clear_bit(irq, entry->irq_enabled);
-}
-
-static void smp2p_unmask_irq(struct irq_data *irqd)
-{
- struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
- irq_hw_number_t irq = irqd_to_hwirq(irqd);
-
- set_bit(irq, entry->irq_enabled);
-}
-
-static int smp2p_set_irq_type(struct irq_data *irqd, unsigned int type)
-{
- struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
- irq_hw_number_t irq = irqd_to_hwirq(irqd);
-
- if (!(type & IRQ_TYPE_EDGE_BOTH))
- return -EINVAL;
-
- if (type & IRQ_TYPE_EDGE_RISING)
- set_bit(irq, entry->irq_rising);
- else
- clear_bit(irq, entry->irq_rising);
-
- if (type & IRQ_TYPE_EDGE_FALLING)
- set_bit(irq, entry->irq_falling);
- else
- clear_bit(irq, entry->irq_falling);
-
- return 0;
-}
-
-static struct irq_chip smp2p_irq_chip = {
- .name = "smp2p",
- .irq_mask = smp2p_mask_irq,
- .irq_unmask = smp2p_unmask_irq,
- .irq_set_type = smp2p_set_irq_type,
-};
-
-static int smp2p_irq_map(struct irq_domain *d,
- unsigned int irq,
- irq_hw_number_t hw)
-{
- struct smp2p_entry *entry = d->host_data;
-
- irq_set_chip_and_handler(irq, &smp2p_irq_chip, handle_level_irq);
- irq_set_chip_data(irq, entry);
- irq_set_nested_thread(irq, 1);
-
- irq_set_noprobe(irq);
-
- return 0;
-}
-
-static const struct irq_domain_ops smp2p_irq_ops = {
- .map = smp2p_irq_map,
- .xlate = irq_domain_xlate_twocell,
-};
-
-static int smp2p_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset,
- int value)
-{
- struct smp2p_entry *entry = container_of(chip, struct smp2p_entry, chip);
-
- if (value)
- *entry->value |= BIT(offset);
- else
- *entry->value &= ~BIT(offset);
-
- qcom_smp2p_kick(entry->smp2p);
-
- return 0;
-}
-
-static void smp2p_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- smp2p_gpio_direction_output(chip, offset, value);
-}
-
-static int qcom_smp2p_inbound_entry(struct qcom_smp2p *smp2p,
- struct smp2p_entry *entry,
- struct device_node *node)
-{
- entry->domain = irq_domain_add_linear(node, 32, &smp2p_irq_ops, entry);
- if (!entry->domain) {
- dev_err(smp2p->dev, "failed to add irq_domain\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
- struct smp2p_entry *entry,
- struct device_node *node)
-{
- struct smp2p_smem_item *out = smp2p->out;
- struct gpio_chip *chip;
- int ret;
-
- /* Allocate an entry from the smem item */
- memcpy_toio(out->entries[out->valid_entries].name, entry->name, SMP2P_MAX_ENTRY_NAME);
- out->valid_entries++;
-
- /* Make the logical entry reference the physical value */
- entry->value = &out->entries[out->valid_entries].value;
-
- chip = &entry->chip;
- chip->base = -1;
- chip->ngpio = 32;
- chip->label = entry->name;
- chip->dev = smp2p->dev;
- chip->owner = THIS_MODULE;
- chip->of_node = node;
-
- chip->set = smp2p_gpio_set;
- chip->direction_output = smp2p_gpio_direction_output;
-
- ret = gpiochip_add(chip);
- if (ret)
- dev_err(smp2p->dev, "failed register gpiochip\n");
-
- return 0;
-}
-
-static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p)
-{
- struct smp2p_smem_item *out;
- unsigned smem_id = smp2p->smem_items[SMP2P_OUTBOUND];
- unsigned pid = smp2p->remote_pid;
- int ret;
-
- ret = qcom_smem_alloc(pid, smem_id, sizeof(*out));
- if (ret < 0 && ret != -EEXIST) {
- if (ret != -EPROBE_DEFER)
- dev_err(smp2p->dev,
- "unable to allocate local smp2p item\n");
- return ret;
- }
-
- out = qcom_smem_get(pid, smem_id, NULL);
- if (IS_ERR(out)) {
- dev_err(smp2p->dev, "Unable to acquire local smp2p item\n");
- return PTR_ERR(out);
- }
-
- memset_io(out, 0, sizeof(*out));
- out->magic = SMP2P_MAGIC;
- out->local_pid = smp2p->local_pid;
- out->remote_pid = smp2p->remote_pid;
- out->total_entries = SMP2P_MAX_ENTRY;
- out->valid_entries = 0;
-
- /*
- * Make sure the rest of the header is written before we validate the
- * item by writing a valid version number.
- */
- wmb();
- out->version = 1;
-
- qcom_smp2p_kick(smp2p);
-
- smp2p->out = out;
-
- return 0;
-}
-
-static int smp2p_parse_ipc(struct qcom_smp2p *smp2p)
-{
- struct device_node *syscon;
- struct device *dev = smp2p->dev;
- const char *key;
- int ret;
-
- syscon = of_parse_phandle(dev->of_node, "qcom,ipc", 0);
- if (!syscon) {
- dev_err(dev, "no qcom,ipc node\n");
- return -ENODEV;
- }
-
- smp2p->ipc_regmap = syscon_node_to_regmap(syscon);
- if (IS_ERR(smp2p->ipc_regmap))
- return PTR_ERR(smp2p->ipc_regmap);
-
- key = "qcom,ipc";
- ret = of_property_read_u32_index(dev->of_node, key, 1, &smp2p->ipc_offset);
- if (ret < 0) {
- dev_err(dev, "no offset in %s\n", key);
- return -EINVAL;
- }
-
- ret = of_property_read_u32_index(dev->of_node, key, 2, &smp2p->ipc_bit);
- if (ret < 0) {
- dev_err(dev, "no bit in %s\n", key);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int qcom_smp2p_probe(struct platform_device *pdev)
-{
- struct smp2p_entry *entry;
- struct device_node *node;
- struct qcom_smp2p *smp2p;
- const char *key;
- int irq;
- int ret;
-
- smp2p = devm_kzalloc(&pdev->dev, sizeof(*smp2p), GFP_KERNEL);
- if (!smp2p)
- return -ENOMEM;
-
- smp2p->dev = &pdev->dev;
- INIT_LIST_HEAD(&smp2p->inbound);
- INIT_LIST_HEAD(&smp2p->outbound);
-
- platform_set_drvdata(pdev, smp2p);
-
- ret = smp2p_parse_ipc(smp2p);
- if (ret)
- return ret;
-
- key = "qcom,smem";
- ret = of_property_read_u32_array(pdev->dev.of_node, key,
- smp2p->smem_items, 2);
- if (ret)
- return ret;
-
- key = "qcom,local-pid";
- ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->local_pid);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to read %s\n", key);
- return -EINVAL;
- }
-
- key = "qcom,remote-pid";
- ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->remote_pid);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to read %s\n", key);
- return -EINVAL;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "unable to acquire smp2p interrupt\n");
- return irq;
- }
-
- ret = qcom_smp2p_alloc_outbound_item(smp2p);
- if (ret < 0)
- return ret;
-
- for_each_available_child_of_node(pdev->dev.of_node, node) {
- entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL);
- if (!entry) {
- ret = -ENOMEM;
- goto unwind_interfaces;
- }
-
- entry->smp2p = smp2p;
-
- ret = of_property_read_string(node, "qcom,entry-name", &entry->name);
- if (ret < 0)
- goto unwind_interfaces;
-
- if (of_property_read_bool(node, "qcom,inbound")) {
- ret = qcom_smp2p_inbound_entry(smp2p, entry, node);
- if (ret < 0)
- goto unwind_interfaces;
-
- list_add(&entry->node, &smp2p->inbound);
- } else if (of_property_read_bool(node, "qcom,outbound")) {
- ret = qcom_smp2p_outbound_entry(smp2p, entry, node);
- if (ret < 0)
- goto unwind_interfaces;
-
- list_add(&entry->node, &smp2p->outbound);
- } else {
- dev_err(&pdev->dev, "neither inbound nor outbound\n");
- ret = -EINVAL;
- goto unwind_interfaces;
- }
- }
-
- /* Kick the outgoing edge after allocating entries */
- qcom_smp2p_kick(smp2p);
-
- ret = devm_request_threaded_irq(&pdev->dev, irq,
- NULL, qcom_smp2p_intr,
- IRQF_ONESHOT,
- "smp2p", (void *)smp2p);
- if (ret) {
- dev_err(&pdev->dev, "failed to request interrupt\n");
- goto unwind_interfaces;
- }
-
-
- return 0;
-
-unwind_interfaces:
- list_for_each_entry(entry, &smp2p->inbound, node)
- irq_domain_remove(entry->domain);
-
- list_for_each_entry(entry, &smp2p->outbound, node)
- gpiochip_remove(&entry->chip);
-
- smp2p->out->valid_entries = 0;
-
- return ret;
-}
-
-static int qcom_smp2p_remove(struct platform_device *pdev)
-{
- struct qcom_smp2p *smp2p = platform_get_drvdata(pdev);
- struct smp2p_entry *entry;
-
- list_for_each_entry(entry, &smp2p->inbound, node)
- irq_domain_remove(entry->domain);
-
- list_for_each_entry(entry, &smp2p->outbound, node)
- gpiochip_remove(&entry->chip);
-
- smp2p->out->valid_entries = 0;
-
- return 0;
-}
-
-static const struct of_device_id qcom_smp2p_of_match[] = {
- { .compatible = "qcom,smp2p" },
- {}
-};
-MODULE_DEVICE_TABLE(of, qcom_smp2p_of_match);
-
-static struct platform_driver qcom_smp2p_driver = {
- .probe = qcom_smp2p_probe,
- .remove = qcom_smp2p_remove,
- .driver = {
- .name = "qcom_smp2p",
- .of_match_table = qcom_smp2p_of_match,
- },
-};
-module_platform_driver(qcom_smp2p_driver);
-
-MODULE_DESCRIPTION("Qualcomm Shared Memory Point to Point driver");
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Copyright (c) 2014, Sony Mobile Communications AB.
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/memblock.h>
-#include <linux/slab.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/hwspinlock.h>
-#include <linux/regmap.h>
-#include <linux/gpio.h>
-#include <linux/mfd/syscon.h>
-
-#include <linux/delay.h>
-
-#include <linux/soc/qcom/smem.h>
-
-#define SMSM_APPS_STATE 0
-#define SMEM_SMSM_SHARED_STATE 85
-
-#define SMSM_MAX_STATES 8
-
-struct qcom_smsm_state {
- unsigned state_id;
- struct gpio_chip chip;
-
- int irq;
-
- struct regmap *ipc_regmap;
- int ipc_bit;
- int ipc_offset;
-};
-
-struct qcom_smsm {
- struct device *dev;
-
- u32 *shared_state;
- size_t shared_state_size;
-
- struct qcom_smsm_state states[SMSM_MAX_STATES];
-};
-
-#if 0
-int qcom_smsm_change_state(struct qcom_smsm *smsm, u32 clear_mask, u32 set_mask)
-{
- u32 state;
-
- dev_dbg(smsm->dev, "SMSM_APPS_STATE clear 0x%x set 0x%x\n", clear_mask, set_mask);
- print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true);
-
- state = readl(&smsm->shared_state[SMSM_APPS_STATE]);
- state &= ~clear_mask;
- state |= set_mask;
- writel(state, &smsm->shared_state[SMSM_APPS_STATE]);
-
- print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true);
-
- // qcom_smem_signal(-1, smsm->smem, smsm->signal_offset, smsm->signal_bit);
-
- return 0;
-}
-EXPORT_SYMBOL(qcom_smsm_change_state);
-#endif
-
-static struct qcom_smsm_state *to_smsm_state(struct gpio_chip *chip)
-{
- return container_of(chip, struct qcom_smsm_state, chip);
-}
-
-static struct qcom_smsm *to_smsm(struct qcom_smsm_state *state)
-{
- return container_of(state, struct qcom_smsm, states[state->state_id]);
-}
-
-static int smsm_gpio_direction_input(struct gpio_chip *chip,
- unsigned offset)
-{
- struct qcom_smsm_state *state = to_smsm_state(chip);
-
- if (state->state_id == SMSM_APPS_STATE)
- return -EINVAL;
- return 0;
-}
-
-static int smsm_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset,
- int value)
-{
- struct qcom_smsm_state *ipc_state;
- struct qcom_smsm_state *state = to_smsm_state(chip);
- struct qcom_smsm *smsm = to_smsm(state);
- unsigned word;
- unsigned bit;
- u32 val;
- int i;
-
- /* Only SMSM_APPS_STATE supports writing */
- if (state->state_id != SMSM_APPS_STATE)
- return -EINVAL;
-
- offset += state->state_id * 32;
-
- word = ALIGN(offset / 32, 4);
- bit = offset % 32;
-
- val = readl(smsm->shared_state + word);
- if (value)
- val |= BIT(bit);
- else
- val &= ~BIT(bit);
- writel(val, smsm->shared_state + word);
-
- /* XXX: send out interrupts */
- for (i = 0; i < SMSM_MAX_STATES; i++) {
- ipc_state = &smsm->states[i];
- if (!ipc_state->ipc_regmap)
- continue;
-
- regmap_write(ipc_state->ipc_regmap, ipc_state->ipc_offset, BIT(ipc_state->ipc_bit));
- }
-
- dev_err(smsm->dev, "set %d %d\n", offset, value);
-
- return 0;
-}
-
-static int smsm_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct qcom_smsm_state *state = to_smsm_state(chip);
- struct qcom_smsm *smsm = to_smsm(state);
- unsigned word;
- unsigned bit;
- u32 val;
-
- offset += state->state_id * 32;
-
- word = ALIGN(offset / 32, 4);
- bit = offset % 32;
-
- val = readl(smsm->shared_state + word);
-
- return !!(val & BIT(bit));
-}
-
-static void smsm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- smsm_gpio_direction_output(chip, offset, value);
-}
-
-static int smsm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- return -EINVAL;
-}
-
-#ifdef CONFIG_DEBUG_FS
-#include <linux/seq_file.h>
-
-static void smsm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
-{
- struct qcom_smsm_state *state = to_smsm_state(chip);
- struct qcom_smsm *smsm = to_smsm(state);
- unsigned i;
- u32 val;
-
- val = readl(smsm->shared_state + state->state_id * 4);
-
- for (i = 0; i < 32; i++) {
- if (val & BIT(i))
- seq_puts(s, "1");
- else
- seq_puts(s, "0");
-
- if (i == 7 || i == 15 || i == 23)
- seq_puts(s, " ");
- }
- seq_puts(s, "\n");
-}
-
-#else
-#define smsm_gpio_dbg_show NULL
-#endif
-
-static struct gpio_chip smsm_gpio_template = {
- .direction_input = smsm_gpio_direction_input,
- .direction_output = smsm_gpio_direction_output,
- .get = smsm_gpio_get,
- .set = smsm_gpio_set,
- .to_irq = smsm_gpio_to_irq,
- .dbg_show = smsm_gpio_dbg_show,
- .owner = THIS_MODULE,
-};
-
-static int qcom_smsm_probe(struct platform_device *pdev)
-{
- struct qcom_smsm_state *state;
- struct device_node *syscon_np;
- struct device_node *node;
- struct qcom_smsm *smsm;
- char *key;
- u32 sid;
- int ret;
-
- smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL);
- if (!smsm)
- return -ENOMEM;
- smsm->dev = &pdev->dev;
-
- ret = qcom_smem_alloc(-1, SMEM_SMSM_SHARED_STATE, 8 * sizeof(uint32_t));
- if (ret < 0 && ret != -EEXIST) {
- dev_err(&pdev->dev, "unable to allocate shared state entry\n");
- return ret;
- }
-
- smsm->shared_state = qcom_smem_get(-1, SMEM_SMSM_SHARED_STATE,
- &smsm->shared_state_size);
- if (IS_ERR(smsm->shared_state)) {
- dev_err(&pdev->dev, "Unable to acquire shared state entry\n");
- return PTR_ERR(smsm->shared_state);
- }
-
- dev_err(smsm->dev, "SMEM_SMSM_SHARED_STATE: %d, %zu\n", ret, smsm->shared_state_size);
- print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true);
-
- for_each_child_of_node(pdev->dev.of_node, node) {
- key = "reg";
- ret = of_property_read_u32(node, key, &sid);
- if (ret || sid >= SMSM_MAX_STATES) {
- dev_err(&pdev->dev, "smsm state missing %s\n", key);
- return -EINVAL;
- }
- state = &smsm->states[sid];
- state->state_id = sid;
-
- state->chip = smsm_gpio_template;
- state->chip.base = -1;
- state->chip.dev = &pdev->dev;
- state->chip.of_node = node;
- state->chip.label = node->name;
- state->chip.ngpio = 8 * sizeof(u32);
- ret = gpiochip_add(&state->chip);
- if (ret) {
- dev_err(&pdev->dev, "failed register gpiochip\n");
- // goto wooha;
- }
-
- /* The remaining properties are only for non-apps state */
- if (sid == SMSM_APPS_STATE)
- continue;
-
- state->irq = irq_of_parse_and_map(node, 0);
- if (state->irq < 0 && state->irq != -EINVAL) {
- dev_err(&pdev->dev, "failed to parse smsm interrupt\n");
- return -EINVAL;
- }
-
- syscon_np = of_parse_phandle(node, "qcom,ipc", 0);
- if (!syscon_np) {
- dev_err(&pdev->dev, "no qcom,ipc node\n");
- return -ENODEV;
- }
-
- state->ipc_regmap = syscon_node_to_regmap(syscon_np);
- if (IS_ERR(state->ipc_regmap))
- return PTR_ERR(state->ipc_regmap);
-
- key = "qcom,ipc";
- ret = of_property_read_u32_index(node, key, 1, &state->ipc_offset);
- if (ret < 0) {
- dev_err(&pdev->dev, "no offset in %s\n", key);
- return -EINVAL;
- }
-
- ret = of_property_read_u32_index(node, key, 2, &state->ipc_bit);
- if (ret < 0) {
- dev_err(&pdev->dev, "no bit in %s\n", key);
- return -EINVAL;
- }
-
- }
-
- return 0;
-}
-
-static const struct of_device_id qcom_smsm_of_match[] = {
- { .compatible = "qcom,smsm" },
- {}
-};
-MODULE_DEVICE_TABLE(of, qcom_smsm_of_match);
-
-static struct platform_driver qcom_smsm_driver = {
- .probe = qcom_smsm_probe,
- .driver = {
- .name = "qcom_smsm",
- .owner = THIS_MODULE,
- .of_match_table = qcom_smsm_of_match,
- },
-};
-
-static int __init qcom_smsm_init(void)
-{
- return platform_driver_register(&qcom_smsm_driver);
-}
-arch_initcall(qcom_smsm_init);
-
-static void __exit qcom_smsm_exit(void)
-{
- platform_driver_unregister(&qcom_smsm_driver);
-}
-module_exit(qcom_smsm_exit)
-
-MODULE_DESCRIPTION("Qualcomm Shared Memory Signaling Mechanism");
-MODULE_LICENSE("GPLv2");
config WCN36XX
tristate "Qualcomm Atheros WCN3660/3680 support"
- depends on MAC80211 && HAS_DMA
+ depends on MAC80211 && HAS_DMA && QCOM_SMD
---help---
This module adds support for wireless adapters based on
Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets.
-obj-$(CONFIG_WCN36XX) := wcn36xx.o wcn36xx-platform.o
+obj-$(CONFIG_WCN36XX) := wcn36xx.o
wcn36xx-y += main.o \
dxe.o \
txrx.o \
smd.o \
pmc.o \
debug.o
-
-wcn36xx-platform-y += wcn36xx-msm.o\
- wcnss_core.o
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/interrupt.h>
+#include <linux/soc/qcom/smem_state.h>
#include "wcn36xx.h"
#include "txrx.h"
return ch->head_blk_ctl->bd_cpu_addr;
}
+static void wcn36xx_ccu_write_register(struct wcn36xx *wcn, int addr, int data)
+{
+ wcn36xx_dbg(WCN36XX_DBG_DXE,
+ "wcn36xx_ccu_write_register: addr=%x, data=%x\n",
+ addr, data);
+
+ writel(data, wcn->ccu_base + addr);
+}
+
static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
{
wcn36xx_dbg(WCN36XX_DBG_DXE,
"wcn36xx_dxe_write_register: addr=%x, data=%x\n",
addr, data);
- writel(data, wcn->mmio + addr);
+ writel(data, wcn->dxe_base + addr);
}
-#define wcn36xx_dxe_write_register_x(wcn, reg, reg_data) \
-do { \
- if (wcn->chip_version != WCN36XX_CHIP_3660) \
- wcn36xx_dxe_write_register(wcn, reg ## _3680, reg_data); \
- else \
- wcn36xx_dxe_write_register(wcn, reg ## _3660, reg_data); \
-} while (0) \
-
static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
{
- *data = readl(wcn->mmio + addr);
+ *data = readl(wcn->dxe_base + addr);
wcn36xx_dbg(WCN36XX_DBG_DXE,
"wcn36xx_dxe_read_register: addr=%x, data=%x\n",
goto out_err;
/* Initialize SMSM state Clear TX Enable RING EMPTY STATE */
- ret = wcn->ctrl_ops->smsm_change_state(
- WCN36XX_SMSM_WLAN_TX_ENABLE,
- WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY);
+ ret = qcom_smem_state_update_bits(wcn->tx_enable_state,
+ WCN36XX_SMSM_WLAN_TX_ENABLE |
+ WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY,
+ WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY);
+ if (ret)
+ goto out_err;
return 0;
* notify chip about new frame through SMSM bus.
*/
if (is_low && vif_priv->pw_state == WCN36XX_BMPS) {
- wcn->ctrl_ops->smsm_change_state(
- 0,
- WCN36XX_SMSM_WLAN_TX_ENABLE);
+ qcom_smem_state_update_bits(wcn->tx_rings_empty_state,
+ WCN36XX_SMSM_WLAN_TX_ENABLE,
+ WCN36XX_SMSM_WLAN_TX_ENABLE);
} else {
/* indicate End Of Packet and generate interrupt on descriptor
* done.
reg_data = WCN36XX_DXE_REG_RESET;
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);
- /* Setting interrupt path */
- reg_data = WCN36XX_DXE_CCU_INT;
- wcn36xx_dxe_write_register_x(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
+ /* Select channels for rx avail and xfer done interrupts... */
+ reg_data = (WCN36XX_DXE_INT_CH3_MASK | WCN36XX_DXE_INT_CH1_MASK) << 16 |
+ WCN36XX_DXE_INT_CH0_MASK | WCN36XX_DXE_INT_CH4_MASK;
+ if (wcn->is_pronto)
+ wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_PRONTO, reg_data);
+ else
+ wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_RIVA, reg_data);
/***************************************/
/* Init descriptors for TX LOW channel */
*/
/* DXE registers */
-#define WCN36XX_DXE_MEM_REG 0x202000
+#define WCN36XX_DXE_MEM_REG 0
-#define WCN36XX_DXE_CCU_INT 0xA0011
-#define WCN36XX_DXE_REG_CCU_INT_3660 0x200b10
-#define WCN36XX_DXE_REG_CCU_INT_3680 0x2050dc
+#define WCN36XX_CCU_DXE_INT_SELECT_RIVA 0x310
+#define WCN36XX_CCU_DXE_INT_SELECT_PRONTO 0x10dc
/* TODO This must calculated properly but not hardcoded */
#define WCN36XX_DXE_CTRL_TX_L 0x328a44
#define WCN36XX_HAL_STA_INVALID_IDX 0xFF
#define WCN36XX_HAL_BSS_INVALID_IDX 0xFF
-/* Default Beacon template size. */
+/* Default Beacon template size */
#define BEACON_TEMPLATE_SIZE 0x180
/* Minimum PVM size that the FW expects. See comment in smd.c for details. */
WCN36XX_HAL_AVOID_FREQ_RANGE_IND = 233,
- WCN36XX_HAL_PRINT_REG_INFO_IND = 259,
-
WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE
};
struct wcn36xx_hal_send_beacon_req_msg {
struct wcn36xx_hal_msg_header header;
- /* length of the template + sizeof(beacon_length) */
- u32 template_length;
+ /* length of the template + 6. Only qcom knows why */
+ u32 beacon_length6;
- /* Beacon data. */
+ /* length of the template. */
u32 beacon_length;
+
+ /* Beacon data. */
u8 beacon[BEACON_TEMPLATE_SIZE - sizeof(u32)];
u8 bssid[ETH_ALEN];
/* Update scan params - sent from host to PNO to be used during PNO
* scanningx */
-struct update_scan_params_req_ex {
+struct wcn36xx_hal_update_scan_params_req_ex {
struct wcn36xx_hal_msg_header header;
/* Cb State */
enum phy_chan_bond_state state;
-};
+} __packed;
/* Update scan params - sent from host to PNO to be used during PNO
* scanningx */
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/soc/qcom/smd.h>
+#include <linux/soc/qcom/smem_state.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
#include "wcn36xx.h"
unsigned int wcn36xx_dbg_mask;
MODULE_PARM_DESC(debug_mask, "Debugging mask");
#define CHAN2G(_freq, _idx) { \
- .band = IEEE80211_BAND_2GHZ, \
+ .band = NL80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 25, \
}
#define CHAN5G(_freq, _idx) { \
- .band = IEEE80211_BAND_5GHZ, \
+ .band = NL80211_BAND_5GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 25, \
wcn36xx_feat_caps_info(wcn);
}
- wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_ENABLE_MC_ADDR_LIST, 1);
-
/* DMA channel initialization */
ret = wcn36xx_dxe_init(wcn);
if (ret) {
return 0;
}
-#define WCN36XX_SUPPORTED_FILTERS (FIF_ALLMULTI)
-
static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
unsigned int changed,
unsigned int *total, u64 multicast)
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
- *total &= WCN36XX_SUPPORTED_FILTERS;
+ *total &= FIF_ALLMULTI;
fp = (void *)(unsigned long)multicast;
list_for_each_entry(tmp, &wcn->vif_list, list) {
return ret;
}
-static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- const u8 *mac_addr)
+static void wcn36xx_hw_scan_worker(struct work_struct *work)
{
- struct wcn36xx *wcn = hw->priv;
+ struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work);
+ struct cfg80211_scan_request *req = wcn->scan_req;
+ u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX];
+ int i;
+
+ wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels);
+
+ for (i = 0; i < req->n_channels; i++)
+ channels[i] = req->channels[i]->hw_value;
+
+ wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels);
wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN);
- wcn36xx_smd_start_scan(wcn);
+ for (i = 0; i < req->n_channels; i++) {
+ wcn->scan_freq = req->channels[i]->center_freq;
+ wcn->scan_band = req->channels[i]->band;
+
+ wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value);
+ msleep(30);
+ wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value);
+
+ wcn->scan_freq = 0;
+ }
+ wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN);
+
+ ieee80211_scan_completed(wcn->hw, false);
+
+ mutex_lock(&wcn->scan_lock);
+ wcn->scan_req = NULL;
+ mutex_unlock(&wcn->scan_lock);
}
-static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
{
struct wcn36xx *wcn = hw->priv;
- wcn36xx_smd_end_scan(wcn);
- wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN);
+ mutex_lock(&wcn->scan_lock);
+ if (wcn->scan_req) {
+ mutex_unlock(&wcn->scan_lock);
+ return -EBUSY;
+ }
+ wcn->scan_req = &hw_req->req;
+ mutex_unlock(&wcn->scan_lock);
+
+ schedule_work(&wcn->scan_work);
+
+ return 0;
}
static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
- enum ieee80211_band band)
+ enum nl80211_band band)
{
int i, size;
u16 *rates_table;
size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
rates_table = sta_priv->supported_rates.dsss_rates;
- if (band == IEEE80211_BAND_2GHZ) {
+ if (band == NL80211_BAND_2GHZ) {
for (i = 0; i < size; i++) {
if (rates & 0x01) {
rates_table[i] = wcn_2ghz_rates[i].hw_value;
.configure_filter = wcn36xx_configure_filter,
.tx = wcn36xx_tx,
.set_key = wcn36xx_set_key,
- .sw_scan_start = wcn36xx_sw_scan_start,
- .sw_scan_complete = wcn36xx_sw_scan_complete,
+ .hw_scan = wcn36xx_hw_scan,
.bss_info_changed = wcn36xx_bss_info_changed,
.set_rts_threshold = wcn36xx_set_rts_threshold,
.sta_add = wcn36xx_sta_add,
ieee80211_hw_set(wcn->hw, SUPPORTS_PS);
ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
-
- /* 3620 powersaving currently unstable */
- if (wcn->chip_version == WCN36XX_CHIP_3620)
- __clear_bit(IEEE80211_HW_SUPPORTS_PS, wcn->hw->flags);
+ ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS);
wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT);
- wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz;
- wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz;
+ wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz;
+ wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz;
+
+ wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS;
+ wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN;
wcn->hw->wiphy->cipher_suites = cipher_suites;
wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
struct platform_device *pdev)
{
+ struct device_node *mmio_node;
struct resource *res;
+ int index;
+ int ret;
+
/* Set TX IRQ */
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "wcnss_wlantx_irq");
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx");
if (!res) {
wcn36xx_err("failed to get tx_irq\n");
return -ENOENT;
wcn->tx_irq = res->start;
/* Set RX IRQ */
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "wcnss_wlanrx_irq");
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx");
if (!res) {
wcn36xx_err("failed to get rx_irq\n");
return -ENOENT;
}
wcn->rx_irq = res->start;
- /* Map the memory */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "wcnss_mmio");
- if (!res) {
- wcn36xx_err("failed to get mmio\n");
+ /* Acquire SMSM tx enable handle */
+ wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev,
+ "tx-enable", &wcn->tx_enable_state_bit);
+ if (IS_ERR(wcn->tx_enable_state)) {
+ wcn36xx_err("failed to get tx-enable state\n");
return -ENOENT;
}
- wcn->mmio = ioremap(res->start, resource_size(res));
- if (!wcn->mmio) {
- wcn36xx_err("failed to map io memory\n");
- return -ENOMEM;
+
+ /* Acquire SMSM tx rings empty handle */
+ wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev,
+ "tx-rings-empty", &wcn->tx_rings_empty_state_bit);
+ if (IS_ERR(wcn->tx_rings_empty_state)) {
+ wcn36xx_err("failed to get tx-rings-empty state\n");
+ return -ENOENT;
+ }
+
+ mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0);
+ if (!mmio_node) {
+ wcn36xx_err("failed to acquire qcom,mmio reference\n");
+ return -EINVAL;
+ }
+
+ wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto");
+
+ /* Map the CCU memory */
+ index = of_property_match_string(mmio_node, "reg-names", "ccu");
+ wcn->ccu_base = of_iomap(mmio_node, index);
+ if (!wcn->ccu_base) {
+ wcn36xx_err("failed to map ccu memory\n");
+ ret = -ENOMEM;
+ goto put_mmio_node;
+ }
+
+ /* Map the DXE memory */
+ index = of_property_match_string(mmio_node, "reg-names", "dxe");
+ wcn->dxe_base = of_iomap(mmio_node, index);
+ if (!wcn->dxe_base) {
+ wcn36xx_err("failed to map dxe memory\n");
+ ret = -ENOMEM;
+ goto unmap_ccu;
}
+
+ of_node_put(mmio_node);
return 0;
+
+unmap_ccu:
+ iounmap(wcn->ccu_base);
+put_mmio_node:
+ of_node_put(mmio_node);
+ return ret;
}
static int wcn36xx_probe(struct platform_device *pdev)
{
struct ieee80211_hw *hw;
struct wcn36xx *wcn;
+ void *wcnss;
int ret;
- u8 addr[ETH_ALEN];
+ const u8 *addr;
wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
+ wcnss = dev_get_drvdata(pdev->dev.parent);
+
hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
if (!hw) {
wcn36xx_err("failed to alloc hw\n");
wcn = hw->priv;
wcn->hw = hw;
wcn->dev = &pdev->dev;
- wcn->dev->dma_mask = kzalloc(sizeof(*wcn->dev->dma_mask), GFP_KERNEL);
- if (!wcn->dev->dma_mask) {
- ret = -ENOMEM;
- goto dma_mask_err;
- }
- dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32));
- wcn->wcn36xx_data = pdev->dev.platform_data;
- wcn->ctrl_ops = &wcn->wcn36xx_data->ctrl_ops;
- wcn->wcn36xx_data->wcn = wcn;
- if (!wcn->ctrl_ops->get_chip_type) {
- dev_err(&pdev->dev, "Missing ops->get_chip_type\n");
- ret = -EINVAL;
+ mutex_init(&wcn->hal_mutex);
+ mutex_init(&wcn->scan_lock);
+
+ INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker);
+
+ wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process);
+ if (IS_ERR(wcn->smd_channel)) {
+ wcn36xx_err("failed to open WLAN_CTRL channel\n");
+ ret = PTR_ERR(wcn->smd_channel);
goto out_wq;
}
- wcn->chip_version = wcn->ctrl_ops->get_chip_type(wcn);
- mutex_init(&wcn->hal_mutex);
+ qcom_smd_set_drvdata(wcn->smd_channel, hw);
- if (!wcn->ctrl_ops->get_hw_mac(wcn, addr)) {
+ addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret);
+ if (addr && ret != ETH_ALEN) {
+ wcn36xx_err("invalid local-mac-address\n");
+ ret = -EINVAL;
+ goto out_wq;
+ } else if (addr) {
wcn36xx_info("mac address: %pM\n", addr);
SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
}
return 0;
out_unmap:
- iounmap(wcn->mmio);
+ iounmap(wcn->ccu_base);
+ iounmap(wcn->dxe_base);
out_wq:
- kfree(wcn->dev->dma_mask);
-dma_mask_err:
ieee80211_free_hw(hw);
out_err:
return ret;
}
+
static int wcn36xx_remove(struct platform_device *pdev)
{
struct ieee80211_hw *hw = platform_get_drvdata(pdev);
mutex_destroy(&wcn->hal_mutex);
ieee80211_unregister_hw(hw);
- iounmap(wcn->mmio);
+
+ qcom_smem_state_put(wcn->tx_enable_state);
+ qcom_smem_state_put(wcn->tx_rings_empty_state);
+
+ iounmap(wcn->dxe_base);
+ iounmap(wcn->ccu_base);
ieee80211_free_hw(hw);
return 0;
}
-static const struct platform_device_id wcn36xx_platform_id_table[] = {
- {
- .name = "wcn36xx",
- .driver_data = 0
- },
+
+static const struct of_device_id wcn36xx_of_match[] = {
+ { .compatible = "qcom,wcnss-wlan" },
{}
};
-MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table);
+MODULE_DEVICE_TABLE(of, wcn36xx_of_match);
static struct platform_driver wcn36xx_driver = {
.probe = wcn36xx_probe,
.remove = wcn36xx_remove,
.driver = {
.name = "wcn36xx",
+ .of_match_table = wcn36xx_of_match,
},
- .id_table = wcn36xx_platform_id_table,
};
-static int __init wcn36xx_init(void)
-{
- platform_driver_register(&wcn36xx_driver);
- return 0;
-}
-module_init(wcn36xx_init);
-
-static void __exit wcn36xx_exit(void)
-{
- platform_driver_unregister(&wcn36xx_driver);
-}
-module_exit(wcn36xx_exit);
+module_platform_driver(wcn36xx_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include <linux/bitops.h>
+#include <linux/soc/qcom/smd.h>
#include "smd.h"
struct wcn36xx_cfg_val {
struct ieee80211_sta *sta,
struct wcn36xx_hal_config_bss_params *bss_params)
{
- if (IEEE80211_BAND_5GHZ == WCN36XX_BAND(wcn))
+ if (NL80211_BAND_5GHZ == WCN36XX_BAND(wcn))
bss_params->nw_type = WCN36XX_HAL_11A_NW_TYPE;
else if (sta && sta->ht_cap.ht_supported)
bss_params->nw_type = WCN36XX_HAL_11N_NW_TYPE;
- else if (sta && (sta->supp_rates[IEEE80211_BAND_2GHZ] & 0x7f))
+ else if (sta && (sta->supp_rates[NL80211_BAND_2GHZ] & 0x7f))
bss_params->nw_type = WCN36XX_HAL_11G_NW_TYPE;
else
bss_params->nw_type = WCN36XX_HAL_11B_NW_TYPE;
init_completion(&wcn->hal_rsp_compl);
start = jiffies;
- ret = wcn->ctrl_ops->tx(wcn, wcn->hal_buf, len);
+ ret = qcom_smd_send(wcn->smd_channel, wcn->hal_buf, len);
if (ret) {
wcn36xx_err("HAL TX failed\n");
goto out;
return ret;
}
-int wcn36xx_smd_start_scan(struct wcn36xx *wcn)
+int wcn36xx_smd_start_scan(struct wcn36xx *wcn, u8 scan_channel)
{
struct wcn36xx_hal_start_scan_req_msg msg_body;
int ret = 0;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_REQ);
- msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn);
+ msg_body.scan_channel = scan_channel;
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
return ret;
}
-int wcn36xx_smd_end_scan(struct wcn36xx *wcn)
+int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel)
{
struct wcn36xx_hal_end_scan_req_msg msg_body;
int ret = 0;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_END_SCAN_REQ);
- msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn);
+ msg_body.scan_channel = scan_channel;
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
return 0;
}
-int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn)
+int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn,
+ u8 *channels, size_t channel_count)
{
- struct wcn36xx_hal_update_scan_params_req msg_body;
+ struct wcn36xx_hal_update_scan_params_req_ex msg_body;
int ret = 0;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ);
- msg_body.dot11d_enabled = 0;
- msg_body.dot11d_resolved = 0;
- msg_body.channel_count = 26;
+ msg_body.dot11d_enabled = false;
+ msg_body.dot11d_resolved = true;
+
+ msg_body.channel_count = channel_count;
+ memcpy(msg_body.channels, channels, channel_count);
msg_body.active_min_ch_time = 60;
msg_body.active_max_ch_time = 120;
msg_body.passive_min_ch_time = 60;
msg_body.passive_max_ch_time = 110;
- msg_body.state = 0;
+ msg_body.state = PHY_SINGLE_CHANNEL_CENTERED;
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
{
struct wcn36xx_hal_send_beacon_req_msg msg_body;
int ret = 0, pad, pvm_len;
- u32 beacon_length;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_SEND_BEACON_REQ);
if (vif->type == NL80211_IFTYPE_MESH_POINT)
pad = 0;
- beacon_length = skb_beacon->len + pad;
- msg_body.template_length = beacon_length + sizeof(beacon_length);
+ msg_body.beacon_length = skb_beacon->len + pad;
+ /* TODO need to find out why + 6 is needed */
+ msg_body.beacon_length6 = msg_body.beacon_length + 6;
- if (msg_body.template_length > BEACON_TEMPLATE_SIZE) {
+ if (msg_body.beacon_length > BEACON_TEMPLATE_SIZE) {
wcn36xx_err("Beacon is to big: beacon size=%d\n",
- msg_body.template_length);
+ msg_body.beacon_length);
ret = -ENOMEM;
goto out;
}
- msg_body.beacon_length = beacon_length;
memcpy(msg_body.beacon, skb_beacon->data, skb_beacon->len);
memcpy(msg_body.bssid, vif->addr, ETH_ALEN);
return ret;
}
-static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
+int wcn36xx_smd_rsp_process(struct qcom_smd_channel *channel,
+ const void *buf, size_t len)
{
- struct wcn36xx_hal_msg_header *msg_header = buf;
+ struct wcn36xx_hal_msg_header msg_header;
+ struct ieee80211_hw *hw = qcom_smd_get_drvdata(channel);
+ struct wcn36xx *wcn = hw->priv;
struct wcn36xx_hal_ind_msg *msg_ind;
wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD <<< ", buf, len);
- switch (msg_header->msg_type) {
+ memcpy_fromio(&msg_header, buf, sizeof(struct wcn36xx_hal_msg_header));
+
+ switch (msg_header.msg_type) {
case WCN36XX_HAL_START_RSP:
case WCN36XX_HAL_CONFIG_STA_RSP:
case WCN36XX_HAL_CONFIG_BSS_RSP:
case WCN36XX_HAL_CH_SWITCH_RSP:
case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP:
case WCN36XX_HAL_8023_MULTICAST_LIST_RSP:
- memcpy(wcn->hal_buf, buf, len);
+ memcpy_fromio(wcn->hal_buf, buf, len);
wcn->hal_rsp_len = len;
complete(&wcn->hal_rsp_compl);
break;
- case WCN36XX_HAL_DEL_BA_IND:
- case WCN36XX_HAL_PRINT_REG_INFO_IND:
case WCN36XX_HAL_COEX_IND:
case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
case WCN36XX_HAL_OTA_TX_COMPL_IND:
case WCN36XX_HAL_MISSED_BEACON_IND:
case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
- msg_ind = kmalloc(sizeof(*msg_ind), GFP_KERNEL);
- if (!msg_ind)
- goto nomem;
- msg_ind->msg_len = len;
- msg_ind->msg = kmemdup(buf, len, GFP_KERNEL);
- if (!msg_ind->msg) {
- kfree(msg_ind);
-nomem:
+ msg_ind = kmalloc(sizeof(*msg_ind) + len, GFP_ATOMIC);
+ if (!msg_ind) {
/*
* FIXME: Do something smarter then just
* printing an error.
*/
wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n",
- msg_header->msg_type);
+ msg_header.msg_type);
break;
}
- mutex_lock(&wcn->hal_ind_mutex);
+
+ msg_ind->msg_len = len;
+ memcpy_fromio(msg_ind->msg, buf, len);
+
+ spin_lock(&wcn->hal_ind_lock);
list_add_tail(&msg_ind->list, &wcn->hal_ind_queue);
queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work);
- mutex_unlock(&wcn->hal_ind_mutex);
+ spin_unlock(&wcn->hal_ind_lock);
wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n");
break;
default:
wcn36xx_err("SMD_EVENT (%d) not supported\n",
- msg_header->msg_type);
+ msg_header.msg_type);
}
+
+ return 0;
}
static void wcn36xx_ind_smd_work(struct work_struct *work)
{
container_of(work, struct wcn36xx, hal_ind_work);
struct wcn36xx_hal_msg_header *msg_header;
struct wcn36xx_hal_ind_msg *hal_ind_msg;
+ unsigned long flags;
- mutex_lock(&wcn->hal_ind_mutex);
+ spin_lock_irqsave(&wcn->hal_ind_lock, flags);
hal_ind_msg = list_first_entry(&wcn->hal_ind_queue,
struct wcn36xx_hal_ind_msg,
msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
switch (msg_header->msg_type) {
- case WCN36XX_HAL_DEL_BA_IND:
- case WCN36XX_HAL_PRINT_REG_INFO_IND:
case WCN36XX_HAL_COEX_IND:
case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
break;
msg_header->msg_type);
}
list_del(wcn->hal_ind_queue.next);
- kfree(hal_ind_msg->msg);
+ spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
kfree(hal_ind_msg);
- mutex_unlock(&wcn->hal_ind_mutex);
}
int wcn36xx_smd_open(struct wcn36xx *wcn)
{
}
INIT_WORK(&wcn->hal_ind_work, wcn36xx_ind_smd_work);
INIT_LIST_HEAD(&wcn->hal_ind_queue);
- mutex_init(&wcn->hal_ind_mutex);
-
- ret = wcn->ctrl_ops->open(wcn, wcn36xx_smd_rsp_process);
- if (ret) {
- wcn36xx_err("failed to open control channel\n");
- goto free_wq;
- }
+ spin_lock_init(&wcn->hal_ind_lock);
- return ret;
+ return 0;
-free_wq:
- destroy_workqueue(wcn->hal_ind_wq);
out:
return ret;
}
void wcn36xx_smd_close(struct wcn36xx *wcn)
{
- wcn->ctrl_ops->close(wcn);
destroy_workqueue(wcn->hal_ind_wq);
- mutex_destroy(&wcn->hal_ind_mutex);
}
struct wcn36xx_hal_ind_msg {
struct list_head list;
- u8 *msg;
size_t msg_len;
+ u8 msg[];
};
struct wcn36xx;
+struct qcom_smd_channel;
int wcn36xx_smd_open(struct wcn36xx *wcn);
void wcn36xx_smd_close(struct wcn36xx *wcn);
int wcn36xx_smd_start(struct wcn36xx *wcn);
int wcn36xx_smd_stop(struct wcn36xx *wcn);
int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode);
-int wcn36xx_smd_start_scan(struct wcn36xx *wcn);
-int wcn36xx_smd_end_scan(struct wcn36xx *wcn);
+int wcn36xx_smd_start_scan(struct wcn36xx *wcn, u8 scan_channel);
+int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel);
int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
enum wcn36xx_hal_sys_mode mode);
-int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn);
+int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, u8 *channels, size_t channel_count);
int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif);
int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr);
int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index);
int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index);
int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value);
+
+int wcn36xx_smd_rsp_process(struct qcom_smd_channel *channel,
+ const void *buf, size_t len);
+
int wcn36xx_smd_set_mc_list(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp);
skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len);
skb_pull(skb, bd->pdu.mpdu_header_off);
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = __le16_to_cpu(hdr->frame_control);
+ sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
+
+ /* When scanning associate beacons to this */
+ if (ieee80211_is_beacon(hdr->frame_control) && wcn->scan_freq) {
+ status.freq = wcn->scan_freq;
+ status.band = wcn->scan_band;
+ } else {
+ status.freq = WCN36XX_CENTER_FREQ(wcn);
+ status.band = WCN36XX_BAND(wcn);
+ }
+
status.mactime = 10;
- status.freq = WCN36XX_CENTER_FREQ(wcn);
- status.band = WCN36XX_BAND(wcn);
status.signal = -get_rssi0(bd);
status.antenna = 1;
status.rate_idx = 1;
RX_FLAG_MMIC_STRIPPED |
RX_FLAG_DECRYPTED;
- wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag);
+ wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%llx\n", status.flag);
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
- hdr = (struct ieee80211_hdr *) skb->data;
- fc = __le16_to_cpu(hdr->frame_control);
- sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
-
if (ieee80211_is_beacon(hdr->frame_control)) {
wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n",
skb, skb->len, fc, sn);
/* default rate for unicast */
if (ieee80211_is_mgmt(hdr->frame_control))
- bd->bd_rate = (WCN36XX_BAND(wcn) == IEEE80211_BAND_5GHZ) ?
+ bd->bd_rate = (WCN36XX_BAND(wcn) == NL80211_BAND_5GHZ) ?
WCN36XX_BD_RATE_CTRL :
WCN36XX_BD_RATE_MGMT;
else if (ieee80211_is_ctl(hdr->frame_control))
+++ /dev/null
-/*
- * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
- * Copyright (c) 2013 Qualcomm Atheros, Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/completion.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/clk.h>
-#include <linux/remoteproc.h>
-#include <linux/soc/qcom/smd.h>
-#include "wcn36xx.h"
-#include "wcnss_core.h"
-
-#define MAC_ADDR_0 "wlan/macaddr0"
-
-struct smd_packet_item {
- struct list_head list;
- void *buf;
- size_t count;
-};
-
-static int wcn36xx_msm_smsm_change_state(u32 clear_mask, u32 set_mask)
-{
- return 0;
-}
-
-static int wcn36xx_msm_get_hw_mac(struct wcn36xx *wcn, u8 *addr)
-{
- const struct firmware *addr_file = NULL;
- int status;
- u8 tmp[18];
- static const u8 qcom_oui[3] = {0x00, 0x0A, 0xF5};
- static const char *files = {MAC_ADDR_0};
- struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
-
- status = request_firmware(&addr_file, files, &pdata->core->dev);
-
- if (status < 0) {
- /* Assign a random mac with Qualcomm oui */
- dev_err(&pdata->core->dev, "Failed (%d) to read macaddress"
- "file %s, using a random address instead", status, files);
- memcpy(addr, qcom_oui, 3);
- get_random_bytes(addr + 3, 3);
- } else {
- memset(tmp, 0, sizeof(tmp));
- memcpy(tmp, addr_file->data, sizeof(tmp) - 1);
- sscanf(tmp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
- &addr[0],
- &addr[1],
- &addr[2],
- &addr[3],
- &addr[4],
- &addr[5]);
-
- release_firmware(addr_file);
- }
-
- return 0;
-}
-
-static int wcn36xx_msm_smd_send_and_wait(struct wcn36xx *wcn, char *buf, size_t len)
-{
- int ret = 0;
- struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
-
- mutex_lock(&pdata->wlan_ctrl_lock);
- ret = qcom_smd_send(pdata->wlan_ctrl_channel, buf, len);
- if (ret) {
- dev_err(wcn->dev, "wlan ctrl channel tx failed\n");
- }
- mutex_unlock(&pdata->wlan_ctrl_lock);
-
- return ret;
-}
-
-static int wcn36xx_msm_smd_open(struct wcn36xx *wcn, void *rsp_cb)
-{
- struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
-
- pdata->cb = rsp_cb;
- return 0;
-}
-
-static void wcn36xx_msm_smd_close(struct wcn36xx *wcn)
-{
- return;
-}
-
-static int wcn36xx_msm_get_chip_type(struct wcn36xx *wcn)
-{
- struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
- return pdata->chip_type;
-}
-
-static struct wcn36xx_platform_data wcn36xx_data = {
- .ctrl_ops = {
- .open = wcn36xx_msm_smd_open,
- .close = wcn36xx_msm_smd_close,
- .tx = wcn36xx_msm_smd_send_and_wait,
- .get_hw_mac = wcn36xx_msm_get_hw_mac,
- .smsm_change_state = wcn36xx_msm_smsm_change_state,
- .get_chip_type = wcn36xx_msm_get_chip_type,
- },
-};
-
-static void wlan_ctrl_smd_process(struct work_struct *worker)
-{
- unsigned long flags;
- struct wcn36xx_platform_data *pdata =
- container_of(worker,
- struct wcn36xx_platform_data, packet_process_work);
-
- spin_lock_irqsave(&pdata->packet_lock, flags);
- while (!list_empty(&pdata->packet_list)) {
- struct smd_packet_item *packet;
-
- packet = list_first_entry(&pdata->packet_list,
- struct smd_packet_item, list);
- list_del(&packet->list);
- spin_unlock_irqrestore(&pdata->packet_lock, flags);
- pdata->cb(pdata->wcn, packet->buf, packet->count);
- kfree(packet->buf);
- spin_lock_irqsave(&pdata->packet_lock, flags);
- }
- spin_unlock_irqrestore(&pdata->packet_lock, flags);
-}
-
-static int qcom_smd_wlan_ctrl_probe(struct qcom_smd_device *sdev)
-{
- pr_info("%s: enter\n", __func__);
- mutex_init(&wcn36xx_data.wlan_ctrl_lock);
- init_completion(&wcn36xx_data.wlan_ctrl_ack);
-
- wcn36xx_data.sdev = sdev;
- spin_lock_init(&wcn36xx_data.packet_lock);
- INIT_LIST_HEAD(&wcn36xx_data.packet_list);
- INIT_WORK(&wcn36xx_data.packet_process_work, wlan_ctrl_smd_process);
-
- dev_set_drvdata(&sdev->dev, &wcn36xx_data);
- wcn36xx_data.wlan_ctrl_channel = sdev->channel;
-
- of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev);
-
- return 0;
-}
-
-static void qcom_smd_wlan_ctrl_remove(struct qcom_smd_device *sdev)
-{
- of_platform_depopulate(&sdev->dev);
-}
-
-static int qcom_smd_wlan_ctrl_callback(struct qcom_smd_device *qsdev,
- const void *data,
- size_t count)
-{
- unsigned long flags;
- struct smd_packet_item *packet = NULL;
- struct wcn36xx_platform_data *pdata = dev_get_drvdata(&qsdev->dev);
- void *buf = kzalloc(count + sizeof(struct smd_packet_item),
- GFP_ATOMIC);
- if (!buf) {
- dev_err(&pdata->core->dev, "can't allocate buffer\n");
- return -ENOMEM;
- }
-
- memcpy_fromio(buf, data, count);
- packet = buf + count;
- packet->buf = buf;
- packet->count = count;
-
- spin_lock_irqsave(&pdata->packet_lock, flags);
- list_add_tail(&packet->list, &pdata->packet_list);
- spin_unlock_irqrestore(&pdata->packet_lock, flags);
- schedule_work(&pdata->packet_process_work);
-
- /* buf will be freed in workqueue */
-
- return 0;
-}
-
-static const struct qcom_smd_id qcom_smd_wlan_ctrl_match[] = {
- { .name = "WLAN_CTRL" },
- {}
-};
-
-static struct qcom_smd_driver qcom_smd_wlan_ctrl_driver = {
- .probe = qcom_smd_wlan_ctrl_probe,
- .remove = qcom_smd_wlan_ctrl_remove,
- .callback = qcom_smd_wlan_ctrl_callback,
- .smd_match_table = qcom_smd_wlan_ctrl_match,
- .driver = {
- .name = "qcom_smd_wlan_ctrl",
- .owner = THIS_MODULE,
- },
-};
-
-static const struct of_device_id wcn36xx_msm_match_table[] = {
- { .compatible = "qcom,wcn3660", .data = (void *)WCN36XX_CHIP_3660 },
- { .compatible = "qcom,wcn3680", .data = (void *)WCN36XX_CHIP_3680 },
- { .compatible = "qcom,wcn3620", .data = (void *)WCN36XX_CHIP_3620 },
- { }
-};
-MODULE_DEVICE_TABLE(of, wcn36xx_msm_match_table);
-
-static int wcn36xx_msm_probe(struct platform_device *pdev)
-{
- int ret;
- const struct of_device_id *of_id;
- struct resource *r;
- struct resource res[3];
- static const char const *rnames[] = {
- "wcnss_mmio", "wcnss_wlantx_irq", "wcnss_wlanrx_irq" };
- static const int rtype[] = {
- IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_IRQ };
- struct device_node *dn;
- int n;
-
- wcnss_core_prepare(pdev);
-
- dn = of_parse_phandle(pdev->dev.of_node, "rproc", 0);
- if (!dn) {
- dev_err(&pdev->dev, "No rproc specified in DT\n");
- } else {
- struct rproc *rproc= rproc_get_by_phandle(dn->phandle);
- if (rproc)
- rproc_boot(rproc);
- else {
- dev_err(&pdev->dev, "No rproc registered\n");
- }
- }
-
- qcom_smd_driver_register(&qcom_smd_wlan_ctrl_driver);
- wcnss_core_init();
-
- of_id = of_match_node(wcn36xx_msm_match_table, pdev->dev.of_node);
- if (!of_id)
- return -EINVAL;
-
- wcn36xx_data.chip_type = (enum wcn36xx_chip_type)of_id->data;
-
- wcn36xx_data.core = platform_device_alloc("wcn36xx", -1);
-
- for (n = 0; n < ARRAY_SIZE(rnames); n++) {
- r = platform_get_resource_byname(pdev, rtype[n], rnames[n]);
- if (!r) {
- dev_err(&wcn36xx_data.core->dev,
- "Missing resource %s'\n", rnames[n]);
- ret = -ENOMEM;
- return ret;
- }
- res[n] = *r;
- }
-
- platform_device_add_resources(wcn36xx_data.core, res, n);
- wcn36xx_data.core->dev.platform_data = &wcn36xx_data;
-
- platform_device_add(wcn36xx_data.core);
-
- dev_info(&pdev->dev, "%s initialized\n", __func__);
-
- return 0;
-}
-
-static int wcn36xx_msm_remove(struct platform_device *pdev)
-{
- platform_device_del(wcn36xx_data.core);
- platform_device_put(wcn36xx_data.core);
- return 0;
-}
-
-static struct platform_driver wcn36xx_msm_driver = {
- .probe = wcn36xx_msm_probe,
- .remove = wcn36xx_msm_remove,
- .driver = {
- .name = "wcn36xx-msm",
- .owner = THIS_MODULE,
- .of_match_table = wcn36xx_msm_match_table,
- },
-};
-
-static int __init wcn36xx_msm_init(void)
-{
- return platform_driver_register(&wcn36xx_msm_driver);
-}
-module_init(wcn36xx_msm_init);
-
-static void __exit wcn36xx_msm_exit(void)
-{
- platform_driver_unregister(&wcn36xx_msm_driver);
-}
-module_exit(wcn36xx_msm_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
-MODULE_FIRMWARE(MAC_ADDR_0);
-
/* How many frames until we start a-mpdu TX session */
#define WCN36XX_AMPDU_START_THRESH 20
+#define WCN36XX_MAX_SCAN_SSIDS 9
+#define WCN36XX_MAX_SCAN_IE_LEN 500
+
extern unsigned int wcn36xx_dbg_mask;
enum wcn36xx_debug_mask {
u8 table;
};
-enum wcn36xx_chip_type {
- WCN36XX_CHIP_UNKNOWN,
- WCN36XX_CHIP_3660,
- WCN36XX_CHIP_3680,
- WCN36XX_CHIP_3620,
-};
-
-/* Interface for platform control path
- *
- * @open: hook must be called when wcn36xx wants to open control channel.
- * @tx: sends a buffer.
- */
-struct wcn36xx_platform_ctrl_ops {
- int (*open)(struct wcn36xx *wcn, void *rsp_cb);
- void (*close)(struct wcn36xx *wcn);
- int (*tx)(struct wcn36xx *wcn, char *buf, size_t len);
- int (*get_hw_mac)(struct wcn36xx *wcn, u8 *addr);
- int (*get_chip_type)(struct wcn36xx *wcn);
- int (*smsm_change_state)(u32 clear_mask, u32 set_mask);
-};
-
-struct wcn36xx_platform_data {
- enum wcn36xx_chip_type chip_type;
-
- struct platform_device *core;
-
- struct qcom_smd_device *sdev;
- struct qcom_smd_channel *wlan_ctrl_channel;
- struct completion wlan_ctrl_ack;
- struct mutex wlan_ctrl_lock;
-
- struct pinctrl *pinctrl;
-
- struct wcn36xx *wcn;
-
- void (*cb)(struct wcn36xx *wcn, void *buf, size_t len);
- struct wcn36xx_platform_ctrl_ops ctrl_ops;
-
- struct work_struct packet_process_work;
- spinlock_t packet_lock;
- struct list_head packet_list;
-};
-
/**
* struct wcn36xx_vif - holds VIF related fields
*
u8 fw_minor;
u8 fw_major;
u32 fw_feat_caps[WCN36XX_HAL_CAPS_SIZE];
- enum wcn36xx_chip_type chip_version;
+ bool is_pronto;
/* extra byte for the NULL termination */
u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
/* IRQs */
int tx_irq;
int rx_irq;
- void __iomem *mmio;
+ void __iomem *ccu_base;
+ void __iomem *dxe_base;
+
+ struct qcom_smd_channel *smd_channel;
+
+ struct qcom_smem_state *tx_enable_state;
+ unsigned tx_enable_state_bit;
+ struct qcom_smem_state *tx_rings_empty_state;
+ unsigned tx_rings_empty_state_bit;
- struct wcn36xx_platform_data *wcn36xx_data;
- struct wcn36xx_platform_ctrl_ops *ctrl_ops;
/*
* smd_buf must be protected with smd_mutex to garantee
* that all messages are sent one after another
struct completion hal_rsp_compl;
struct workqueue_struct *hal_ind_wq;
struct work_struct hal_ind_work;
- struct mutex hal_ind_mutex;
+ spinlock_t hal_ind_lock;
struct list_head hal_ind_queue;
+ struct work_struct scan_work;
+ struct cfg80211_scan_request *scan_req;
+ int scan_freq;
+ int scan_band;
+ struct mutex scan_lock;
+
/* DXE channels */
struct wcn36xx_dxe_ch dxe_tx_l_ch; /* TX low */
struct wcn36xx_dxe_ch dxe_tx_h_ch; /* TX high */
+++ /dev/null
-#include <linux/completion.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/clk.h>
-#include <linux/soc/qcom/smd.h>
-#include "wcn36xx.h"
-#include "wcnss_core.h"
-
-#define WCNSS_CTRL_TIMEOUT (msecs_to_jiffies(500))
-
-static int wcnss_core_config(struct platform_device *pdev, void __iomem *base)
-{
- int ret = 0;
- u32 value, iris_read_v = INVALID_IRIS_REG;
- int clk_48m = 0;
-
- value = readl_relaxed(base + SPARE_OFFSET);
- value |= WCNSS_FW_DOWNLOAD_ENABLE;
- writel_relaxed(value, base + SPARE_OFFSET);
-
- writel_relaxed(0, base + PMU_OFFSET);
- value = readl_relaxed(base + PMU_OFFSET);
- value |= WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP |
- WCNSS_PMU_CFG_IRIS_XO_EN;
- writel_relaxed(value, base + PMU_OFFSET);
-
- iris_read_v = readl_relaxed(base + IRIS_REG_OFFSET);
- pr_info("iris_read_v: 0x%x\n", iris_read_v);
-
- iris_read_v &= 0xffff;
- iris_read_v |= 0x04;
- writel_relaxed(iris_read_v, base + IRIS_REG_OFFSET);
-
- value = readl_relaxed(base + PMU_OFFSET);
- value |= WCNSS_PMU_CFG_IRIS_XO_READ;
- writel_relaxed(value, base + PMU_OFFSET);
-
- while (readl_relaxed(base + PMU_OFFSET) &
- WCNSS_PMU_CFG_IRIS_XO_READ_STS)
- cpu_relax();
-
- iris_read_v = readl_relaxed(base + 0x1134);
- pr_info("wcnss: IRIS Reg: 0x%08x\n", iris_read_v);
- clk_48m = (iris_read_v >> 30) ? 0 : 1;
- value &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
-
- /* XO_MODE[b2:b1]. Clear implies 19.2MHz */
- value &= ~WCNSS_PMU_CFG_IRIS_XO_MODE;
-
- if (clk_48m)
- value |= WCNSS_PMU_CFG_IRIS_XO_MODE_48;
-
- writel_relaxed(value, base + PMU_OFFSET);
-
- /* Reset IRIS */
- value |= WCNSS_PMU_CFG_IRIS_RESET;
- writel_relaxed(value, base + PMU_OFFSET);
-
- while (readl_relaxed(base + PMU_OFFSET) &
- WCNSS_PMU_CFG_IRIS_RESET_STS)
- cpu_relax();
-
- /* reset IRIS reset bit */
- value &= ~WCNSS_PMU_CFG_IRIS_RESET;
- writel_relaxed(value, base + PMU_OFFSET);
-
- /* start IRIS XO configuration */
- value |= WCNSS_PMU_CFG_IRIS_XO_CFG;
- writel_relaxed(value, base + PMU_OFFSET);
-
- /* Wait for XO configuration to finish */
- while (readl_relaxed(base + PMU_OFFSET) &
- WCNSS_PMU_CFG_IRIS_XO_CFG_STS)
- cpu_relax();
-
- /* Stop IRIS XO configuration */
- value &= ~(WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP |
- WCNSS_PMU_CFG_IRIS_XO_CFG);
- writel_relaxed(value, base + PMU_OFFSET);
-
- msleep(200);
-
- return ret;
-}
-
-int wcnss_core_prepare(struct platform_device *pdev)
-{
- int ret = 0;
- struct resource *res;
- void __iomem *wcnss_base;
-
- res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "pronto_phy_base");
- if (!res) {
- ret = -EIO;
- dev_err(&pdev->dev, "resource pronto_phy_base failed\n");
- return ret;
- }
-
- wcnss_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(wcnss_base)) {
- dev_err(&pdev->dev, "pronto_phy_base map failed\n");
- return PTR_ERR(wcnss_base);
- }
-
- ret = wcnss_core_config(pdev, wcnss_base);
- return ret;
-}
-
-struct wcnss_platform_data {
- struct qcom_smd_channel *channel;
- struct completion ack;
- struct mutex lock;
-
- struct work_struct rx_work;
- struct work_struct download_work;
-
- struct qcom_smd_device *sdev;
-};
-
-static struct completion fw_ready_compl;
-#define NV_FILE_NAME "wlan/prima/WCNSS_qcom_wlan_nv.bin"
-static void wcn36xx_nv_download_work(struct work_struct *worker)
-{
- int ret = 0, i;
- const struct firmware *nv = NULL;
- struct wcnss_platform_data *wcnss_data =
- container_of(worker, struct wcnss_platform_data, download_work);
- struct device *dev = &wcnss_data->sdev->dev;
-
- struct nvbin_dnld_req_msg *msg;
- const void *nv_blob_start;
- char *pkt = NULL;
- int nv_blob_size = 0, fragments;
-
- ret = request_firmware(&nv, NV_FILE_NAME, dev);
- if (ret || !nv || !nv->data || !nv->size) {
- dev_err(dev, "request firmware for %s (ret = %d)\n",
- NV_FILE_NAME, ret);
- return;
- }
-
- nv_blob_start = nv->data + 4;
- nv_blob_size = nv->size -4;
-
- fragments = (nv_blob_size + NV_FRAGMENT_SIZE - 1)/NV_FRAGMENT_SIZE;
-
- pkt = kzalloc(sizeof(struct nvbin_dnld_req_msg) + NV_FRAGMENT_SIZE,
- GFP_KERNEL);
- if (!pkt) {
- dev_err(dev, "allocation packet for nv download failed\n");
- release_firmware(nv);
- }
-
- msg = (struct nvbin_dnld_req_msg *)pkt;
- msg->hdr.msg_type = WCNSS_NV_DOWNLOAD_REQ;
- msg->dnld_req_params.msg_flags = 0;
-
- i = 0;
- do {
- int pkt_len = 0;
-
- msg->dnld_req_params.frag_number = i;
- if (nv_blob_size > NV_FRAGMENT_SIZE) {
- msg->dnld_req_params.msg_flags &=
- ~LAST_FRAGMENT;
- pkt_len = NV_FRAGMENT_SIZE;
- } else {
- pkt_len = nv_blob_size;
- msg->dnld_req_params.msg_flags |=
- LAST_FRAGMENT | CAN_RECEIVE_CALDATA;
- }
-
- msg->dnld_req_params.nvbin_buffer_size = pkt_len;
- msg->hdr.msg_len =
- sizeof(struct nvbin_dnld_req_msg) + pkt_len;
-
- memcpy(pkt + sizeof(struct nvbin_dnld_req_msg),
- nv_blob_start + i * NV_FRAGMENT_SIZE, pkt_len);
-
- ret = qcom_smd_send(wcnss_data->channel, pkt, msg->hdr.msg_len);
- if (ret) {
- dev_err(dev, "nv download failed\n");
- goto out;
- }
-
- i++;
- nv_blob_size -= NV_FRAGMENT_SIZE;
- msleep(100);
- } while (nv_blob_size > 0);
-
-out:
- kfree(pkt);
- release_firmware(nv);
- return;
-}
-
-static int qcom_smd_wcnss_ctrl_callback(struct qcom_smd_device *qsdev,
- const void *data,
- size_t count)
-{
- struct wcnss_platform_data *wcnss_data = dev_get_drvdata(&qsdev->dev);
- struct smd_msg_hdr phdr;
- const unsigned char *tmp = data;
-
- memcpy_fromio(&phdr, data, sizeof(struct smd_msg_hdr));
-
- switch (phdr.msg_type) {
- /* CBC COMPLETE means firmware ready for go */
- case WCNSS_CBC_COMPLETE_IND:
- complete(&fw_ready_compl);
- pr_info("wcnss: received WCNSS_CBC_COMPLETE_IND from FW\n");
- break;
-
- case WCNSS_NV_DOWNLOAD_RSP:
- pr_info("fw_status: %d\n", tmp[sizeof(struct smd_msg_hdr)]);
- break;
- }
-
- complete(&wcnss_data->ack);
- return 0;
-}
-
-static int qcom_smd_wcnss_ctrl_probe(struct qcom_smd_device *sdev)
-{
- struct wcnss_platform_data *wcnss_data;
-
- wcnss_data = devm_kzalloc(&sdev->dev, sizeof(*wcnss_data), GFP_KERNEL);
- if (!wcnss_data)
- return -ENOMEM;
-
- mutex_init(&wcnss_data->lock);
- init_completion(&wcnss_data->ack);
-
- wcnss_data->sdev = sdev;
-
- dev_set_drvdata(&sdev->dev, wcnss_data);
- wcnss_data->channel = sdev->channel;
-
- INIT_WORK(&wcnss_data->download_work, wcn36xx_nv_download_work);
-
- of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev);
-
- /* We are ready for download here */
- schedule_work(&wcnss_data->download_work);
- return 0;
-}
-
-static void qcom_smd_wcnss_ctrl_remove(struct qcom_smd_device *sdev)
-{
- of_platform_depopulate(&sdev->dev);
-}
-
-static const struct qcom_smd_id qcom_smd_wcnss_ctrl_match[] = {
- { .name = "WCNSS_CTRL" },
- {}
-};
-
-static struct qcom_smd_driver qcom_smd_wcnss_ctrl_driver = {
- .probe = qcom_smd_wcnss_ctrl_probe,
- .remove = qcom_smd_wcnss_ctrl_remove,
- .callback = qcom_smd_wcnss_ctrl_callback,
- .smd_match_table = qcom_smd_wcnss_ctrl_match,
- .driver = {
- .name = "qcom_smd_wcnss_ctrl",
- .owner = THIS_MODULE,
- },
-};
-
-void wcnss_core_init(void)
-{
- int ret = 0;
-
- init_completion(&fw_ready_compl);
- qcom_smd_driver_register(&qcom_smd_wcnss_ctrl_driver);
-
- ret = wait_for_completion_interruptible_timeout(
- &fw_ready_compl, msecs_to_jiffies(FW_READY_TIMEOUT));
- if (ret <= 0) {
- pr_err("timeout waiting for wcnss firmware ready indicator\n");
- return;
- }
-
- return;
-}
-
-void wcnss_core_deinit(void)
-{
- qcom_smd_driver_unregister(&qcom_smd_wcnss_ctrl_driver);
-}
+++ /dev/null
-#ifndef _WCNSS_CORE_H_
-#define _WCNSS_CORE_H_
-
-#define PMU_OFFSET 0x1004
-#define SPARE_OFFSET 0x1088
-#define IRIS_REG_OFFSET 0x1134
-
-#define INVALID_IRIS_REG 0xbaadbaad
-
-#define WCNSS_PMU_CFG_IRIS_XO_CFG BIT(3)
-#define WCNSS_PMU_CFG_IRIS_XO_EN BIT(4)
-#define WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP BIT(5)
-#define WCNSS_PMU_CFG_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */
-
-#define WCNSS_PMU_CFG_IRIS_RESET BIT(7)
-#define WCNSS_PMU_CFG_IRIS_RESET_STS BIT(8) /* 1: in progress, 0: done */
-#define WCNSS_PMU_CFG_IRIS_XO_READ BIT(9)
-#define WCNSS_PMU_CFG_IRIS_XO_READ_STS BIT(10)
-#define WCNSS_FW_DOWNLOAD_ENABLE BIT(25)
-
-#define WCNSS_PMU_CFG_IRIS_XO_MODE 0x6
-#define WCNSS_PMU_CFG_IRIS_XO_MODE_48 (3 << 1)
-
-#define NV_DOWNLOAD_TIMEOUT 500
-#define NV_FRAGMENT_SIZE 3072
-#define MAX_CALIBRATED_DATA_SIZE (64*1024)
-#define LAST_FRAGMENT (1 << 0)
-#define MESSAGE_TO_FOLLOW (1 << 1)
-#define CAN_RECEIVE_CALDATA (1 << 15)
-#define WCNSS_RESP_SUCCESS 1
-#define WCNSS_RESP_FAIL 0
-
-
-#define WCNSS_NV_DOWNLOAD_REQ 0x01000002
-#define WCNSS_NV_DOWNLOAD_RSP 0x01000003
-#define WCNSS_CBC_COMPLETE_IND 0x0100000C
-
-/*time out 10s for the firmware status ready indicator */
-#define FW_READY_TIMEOUT (10000)
-
-
-struct smd_msg_hdr {
- unsigned int msg_type;
- unsigned int msg_len;
-};
-
-struct nvbin_dnld_req_params {
- /* Fragment sequence number of the NV bin Image. NV Bin Image
- * might not fit into one message due to size limitation of
- * the SMD channel FIFO so entire NV blob is chopped into
- * multiple fragments starting with seqeunce number 0. The
- * last fragment is indicated by marking is_last_fragment field
- * to 1. At receiving side, NV blobs would be concatenated
- * together without any padding bytes in between.
- */
- unsigned short frag_number;
-
- /* bit 0: When set to 1 it indicates that no more fragments will
- * be sent.
- * bit 1: When set, a new message will be followed by this message
- * bit 2- bit 14: Reserved
- * bit 15: when set, it indicates that the sender is capable of
- * receiving Calibrated data.
- */
- unsigned short msg_flags;
-
- /* NV Image size (number of bytes) */
- unsigned int nvbin_buffer_size;
-
- /* Following the 'nvbin_buffer_size', there should be
- * nvbin_buffer_size bytes of NV bin Image i.e.
- * uint8[nvbin_buffer_size].
- */
-};
-
-struct nvbin_dnld_req_msg {
- /* Note: The length specified in nvbin_dnld_req_msg messages
- * should be hdr.msg_len = sizeof(nvbin_dnld_req_msg) +
- * nvbin_buffer_size.
- */
- struct smd_msg_hdr hdr;
- struct nvbin_dnld_req_params dnld_req_params;
-};
-
-struct wcnss_version {
- struct smd_msg_hdr hdr;
- unsigned char major;
- unsigned char minor;
- unsigned char version;
- unsigned char revision;
-};
-
-
-int wcnss_core_prepare(struct platform_device *pdev);
-void wcnss_core_init(void);
-void wcnss_core_deinit(void);
-
-#endif
-
tristate "Qualcomm Hexagon V5 Peripherial Image Loader"
depends on OF && ARCH_QCOM
select REMOTEPROC
+ select QCOM_MDT_LOADER
help
Say y here to support the Qualcomm Peripherial Image Loader for the
Hexagon V5 based remote processors.
Say y here to support the TrustZone based Qualcomm Peripherial Image
Loader.
+config QCOM_MDT_LOADER
+ tristate
+
+config QCOM_WCNSS_PIL
+ tristate "Qualcomm WCNSS Peripheral Image Loader"
+ depends on OF && ARCH_QCOM
+ select REMOTEPROC
+ select QCOM_MDT_LOADER
+ select QCOM_SCM
+ help
+ Peripherial Image Loader for the WCNSS block.
+
endmenu
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
obj-$(CONFIG_QCOM_TZ_PIL) += qcom_tz_pil.o
+obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o
+obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss.o qcom_wcnss_iris.o
+obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
--- /dev/null
+/*
+ * Qualcomm Peripheral Image Loader
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Copyright (C) 2015 Sony Mobile Communications Inc
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as 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/elf.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/qcom_scm.h>
+#include <linux/remoteproc.h>
+#include <linux/slab.h>
+
+#include "remoteproc_internal.h"
+#include "qcom_mdt_loader.h"
+
+/**
+ * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc
+ * @rproc: remoteproc handle
+ * @fw: firmware header
+ * @tablesz: outgoing size of the table
+ *
+ * Returns a dummy table.
+ */
+struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
+ const struct firmware *fw,
+ int *tablesz)
+{
+ static struct resource_table table = { .ver = 1, };
+
+ *tablesz = sizeof(table);
+ return &table;
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table);
+
+int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate)
+{
+ const struct elf32_phdr *phdrs;
+ const struct elf32_phdr *phdr;
+ const struct elf32_hdr *ehdr;
+ phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
+ phys_addr_t max_addr = 0;
+ bool relocate = false;
+ int i;
+
+ ehdr = (struct elf32_hdr *)fw->data;
+ phdrs = (struct elf32_phdr *)(ehdr + 1);
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &phdrs[i];
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
+ continue;
+
+ if (!phdr->p_memsz)
+ continue;
+
+ if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
+ relocate = true;
+
+ if (phdr->p_paddr < min_addr)
+ min_addr = phdr->p_paddr;
+
+ if (phdr->p_paddr + phdr->p_memsz > max_addr)
+ max_addr = round_up(phdr->p_paddr + phdr->p_memsz, SZ_4K);
+ }
+
+ if (fw_addr)
+ *fw_addr = min_addr;
+ if (fw_size)
+ *fw_size = max_addr - min_addr;
+ if (fw_relocate)
+ *fw_relocate = relocate;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_parse);
+
+/**
+ * qcom_mdt_load() - load the firmware which header is defined in fw
+ * @rproc: rproc handle
+ * @pas_id: PAS identifier to load this firmware into
+ * @fw: frimware object for the header
+ *
+ * Returns 0 on success, negative errno otherwise.
+ */
+int qcom_mdt_load(struct rproc *rproc,
+ const struct firmware *fw,
+ const char *firmware)
+{
+ const struct elf32_phdr *phdrs;
+ const struct elf32_phdr *phdr;
+ const struct elf32_hdr *ehdr;
+ unsigned fw_name_len;
+ char *fw_name;
+ void *ptr;
+ int ret;
+ int i;
+
+ ehdr = (struct elf32_hdr *)fw->data;
+ phdrs = (struct elf32_phdr *)(ehdr + 1);
+
+ fw_name_len = strlen(firmware);
+ if (fw_name_len <= 4)
+ return -EINVAL;
+
+ fw_name = kstrdup(firmware, GFP_KERNEL);
+ if (!fw_name)
+ return -ENOMEM;
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &phdrs[i];
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
+ continue;
+
+ if (!phdr->p_memsz)
+ continue;
+
+ ptr = rproc_da_to_va(rproc, phdr->p_paddr, phdr->p_memsz);
+ if (!ptr) {
+ dev_err(&rproc->dev, "segment outside memory range\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (phdr->p_filesz) {
+ sprintf(fw_name + fw_name_len - 3, "b%02d", i);
+ ret = request_firmware(&fw, fw_name, &rproc->dev);
+ if (ret) {
+ dev_err(&rproc->dev, "failed to load %s\n", fw_name);
+ break;
+ }
+
+ memcpy(ptr, fw->data, fw->size);
+
+ release_firmware(fw);
+ }
+
+ if (phdr->p_memsz > phdr->p_filesz)
+ memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
+ }
+
+ kfree(fw_name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_load);
--- /dev/null
+#ifndef __QCOM_MDT_LOADER_H__
+#define __QCOM_MDT_LOADER_H__
+
+#define QCOM_MDT_TYPE_MASK (7 << 24)
+#define QCOM_MDT_TYPE_HASH (2 << 24)
+#define QCOM_MDT_RELOCATABLE BIT(27)
+
+struct resource_table * qcom_mdt_find_rsc_table(struct rproc *rproc, const struct firmware *fw, int *tablesz);
+int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name);
+
+int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate);
+
+#endif
/*
* Qualcomm Peripheral Image Loader
*
+ * Copyright (C) 2016 Linaro Ltd.
* Copyright (C) 2014 Sony Mobile Communications AB
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* GNU General Public License for more details.
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/firmware.h>
-#include <linux/remoteproc.h>
+#include <linux/io.h>
#include <linux/interrupt.h>
-#include <linux/memblock.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
#include <linux/of_address.h>
-#include <linux/elf.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
-#include <linux/soc/qcom/smem.h>
+#include <linux/remoteproc.h>
#include <linux/reset.h>
-#include <linux/qcom_scm.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/smem_state.h>
#include "remoteproc_internal.h"
+#include "qcom_mdt_loader.h"
#include <linux/qcom_scm.h>
-#define SCM_SVC_PIL 0x2
+#define MBA_FIRMWARE_NAME "mba.mbn"
+#define MPSS_FIRMWARE_NAME "modem.mdt"
-struct mdt_hdr {
- struct elf32_hdr hdr;
- struct elf32_phdr phdr[];
-};
+#define MPSS_CRASH_REASON_SMEM 421
+
+/* RMB Status Register Values */
+#define RMB_PBL_SUCCESS 0x1
+
+#define RMB_MBA_XPU_UNLOCKED 0x1
+#define RMB_MBA_XPU_UNLOCKED_SCRIBBLED 0x2
+#define RMB_MBA_META_DATA_AUTH_SUCCESS 0x3
+#define RMB_MBA_AUTH_COMPLETE 0x4
+
+/* PBL/MBA interface registers */
+#define RMB_MBA_IMAGE_REG 0x00
+#define RMB_PBL_STATUS_REG 0x04
+#define RMB_MBA_COMMAND_REG 0x08
+#define RMB_MBA_STATUS_REG 0x0C
+#define RMB_PMI_META_DATA_REG 0x10
+#define RMB_PMI_CODE_START_REG 0x14
+#define RMB_PMI_CODE_LENGTH_REG 0x18
+
+#define RMB_CMD_META_DATA_READY 0x1
+#define RMB_CMD_LOAD_READY 0x2
+
+/* QDSP6SS Register Offsets */
+#define QDSP6SS_RESET_REG 0x014
+#define QDSP6SS_GFMUX_CTL_REG 0x020
+#define QDSP6SS_PWR_CTL_REG 0x030
+
+/* AXI Halt Register Offsets */
+#define AXI_HALTREQ_REG 0x0
+#define AXI_HALTACK_REG 0x4
+#define AXI_IDLE_REG 0x8
+
+#define HALT_ACK_TIMEOUT_MS 100
-struct qproc {
+/* QDSP6SS_RESET */
+#define Q6SS_STOP_CORE BIT(0)
+#define Q6SS_CORE_ARES BIT(1)
+#define Q6SS_BUS_ARES_ENABLE BIT(2)
+
+/* QDSP6SS_GFMUX_CTL */
+#define Q6SS_CLK_ENABLE BIT(1)
+
+/* QDSP6SS_PWR_CTL */
+#define Q6SS_L2DATA_SLP_NRET_N_0 BIT(0)
+#define Q6SS_L2DATA_SLP_NRET_N_1 BIT(1)
+#define Q6SS_L2DATA_SLP_NRET_N_2 BIT(2)
+#define Q6SS_L2TAG_SLP_NRET_N BIT(16)
+#define Q6SS_ETB_SLP_NRET_N BIT(17)
+#define Q6SS_L2DATA_STBY_N BIT(18)
+#define Q6SS_SLP_RET_N BIT(19)
+#define Q6SS_CLAMP_IO BIT(20)
+#define QDSS_BHS_ON BIT(21)
+#define QDSS_LDO_BYP BIT(22)
+
+struct q6v5 {
struct device *dev;
struct rproc *rproc;
void __iomem *reg_base;
- void __iomem *halt_base;
- void __iomem *halt_q6;
- void __iomem *halt_modem;
- void __iomem *halt_nc;
void __iomem *rmb_base;
- void __iomem *restart_sec_base;
- struct reset_control *mss_restart;
+ struct regmap *halt_map;
+ u32 halt_q6;
+ u32 halt_modem;
+ u32 halt_nc;
- int wdog_irq;
- int fatal_irq;
- int ready_irq;
- int handover_irq;
- int stop_ack_irq;
+ struct reset_control *mss_restart;
- struct gpio_desc *stop_gpio;
+ struct qcom_smem_state *state;
+ unsigned stop_bit;
- struct regulator *vdd;
- struct regulator *cx;
- struct regulator *mx;
- struct regulator *pll;
+ struct regulator_bulk_data supply[4];
struct clk *ahb_clk;
struct clk *axi_clk;
struct clk *rom_clk;
struct completion start_done;
+ struct completion stop_done;
+ bool running;
- void *mba_va;
- dma_addr_t mba_da;
- size_t mba_size;
- struct dma_attrs mba_attrs;
+ phys_addr_t mba_phys;
+ void *mba_region;
+ size_t mba_size;
- phys_addr_t reloc_phys;
- size_t reloc_size;
+ phys_addr_t mpss_phys;
+ phys_addr_t mpss_reloc;
+ void *mpss_region;
+ size_t mpss_size;
};
-#define VDD_MSS_UV 1000000
-#define VDD_MSS_UV_MAX 1150000
-#define VDD_MSS_UA 100000
-
-/* Q6 Register Offsets */
-#define QDSP6SS_RST_EVB 0x010
-
-/* AXI Halting Registers */
-#define MSS_Q6_HALT_BASE 0x180
-#define MSS_MODEM_HALT_BASE 0x200
-#define MSS_NC_HALT_BASE 0x280
-
-/* RMB Status Register Values */
-#define STATUS_PBL_SUCCESS 0x1
-#define STATUS_XPU_UNLOCKED 0x1
-#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2
-
-/* PBL/MBA interface registers */
-#define RMB_MBA_IMAGE 0x00
-#define RMB_PBL_STATUS 0x04
-#define RMB_MBA_COMMAND 0x08
-#define RMB_MBA_STATUS 0x0C
-#define RMB_PMI_META_DATA 0x10
-#define RMB_PMI_CODE_START 0x14
-#define RMB_PMI_CODE_LENGTH 0x18
-
-#define POLL_INTERVAL_US 50
-
-#define CMD_META_DATA_READY 0x1
-#define CMD_LOAD_READY 0x2
+enum {
+ Q6V5_SUPPLY_CX,
+ Q6V5_SUPPLY_MX,
+ Q6V5_SUPPLY_MSS,
+ Q6V5_SUPPLY_PLL,
+};
-#define STATUS_META_DATA_AUTH_SUCCESS 0x3
-#define STATUS_AUTH_COMPLETE 0x4
+static int q6v5_regulator_init(struct q6v5 *qproc)
+{
+ int ret;
-/* External BHS */
-#define EXTERNAL_BHS_ON BIT(0)
-#define EXTERNAL_BHS_STATUS BIT(4)
-#define BHS_TIMEOUT_US 50
+ qproc->supply[Q6V5_SUPPLY_CX].supply = "cx";
+ qproc->supply[Q6V5_SUPPLY_MX].supply = "mx";
+ qproc->supply[Q6V5_SUPPLY_MSS].supply = "mss";
+ qproc->supply[Q6V5_SUPPLY_PLL].supply = "pll";
-#define MSS_RESTART_ID 0xA
+ ret = devm_regulator_bulk_get(qproc->dev,
+ ARRAY_SIZE(qproc->supply), qproc->supply);
+ if (ret < 0) {
+ dev_err(qproc->dev, "failed to get supplies\n");
+ return ret;
+ }
-/* QDSP6SS Register Offsets */
-#define QDSP6SS_RESET 0x014
-#define QDSP6SS_GFMUX_CTL 0x020
-#define QDSP6SS_PWR_CTL 0x030
-#define QDSP6SS_STRAP_ACC 0x110
+ regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 100000);
+ regulator_set_load(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 100000);
+ regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 10000);
-/* AXI Halt Register Offsets */
-#define AXI_HALTREQ 0x0
-#define AXI_HALTACK 0x4
-#define AXI_IDLE 0x8
+ return 0;
+}
-#define HALT_ACK_TIMEOUT_US 100000
+static int q6v5_regulator_enable(struct q6v5 *qproc)
+{
+ int ret;
-/* QDSP6SS_RESET */
-#define Q6SS_STOP_CORE BIT(0)
-#define Q6SS_CORE_ARES BIT(1)
-#define Q6SS_BUS_ARES_ENA BIT(2)
+ /* TODO: Q6V5_SUPPLY_CX is supposed to be set to super-turbo here */
+ ret = regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MX].consumer,
+ 1050000, INT_MAX);
+ if (ret)
+ return ret;
-/* QDSP6SS_GFMUX_CTL */
-#define Q6SS_CLK_ENA BIT(1)
-#define Q6SS_CLK_SRC_SEL_C BIT(3)
-#define Q6SS_CLK_SRC_SEL_FIELD 0xC
-#define Q6SS_CLK_SRC_SWITCH_CLK_OVR BIT(8)
+ regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MSS].consumer,
+ 1000000, 1150000);
-/* QDSP6SS_PWR_CTL */
-#define Q6SS_L2DATA_SLP_NRET_N_0 BIT(0)
-#define Q6SS_L2DATA_SLP_NRET_N_1 BIT(1)
-#define Q6SS_L2DATA_SLP_NRET_N_2 BIT(2)
-#define Q6SS_L2TAG_SLP_NRET_N BIT(16)
-#define Q6SS_ETB_SLP_NRET_N BIT(17)
-#define Q6SS_L2DATA_STBY_N BIT(18)
-#define Q6SS_SLP_RET_N BIT(19)
-#define Q6SS_CLAMP_IO BIT(20)
-#define QDSS_BHS_ON BIT(21)
-#define QDSS_LDO_BYP BIT(22)
-
-/* QDSP6v55 parameters */
-#define QDSP6v55_LDO_ON BIT(26)
-#define QDSP6v55_LDO_BYP BIT(25)
-#define QDSP6v55_BHS_ON BIT(24)
-#define QDSP6v55_CLAMP_WL BIT(21)
-#define L1IU_SLP_NRET_N BIT(15)
-#define L1DU_SLP_NRET_N BIT(14)
-#define L2PLRU_SLP_NRET_N BIT(13)
-
-#define HALT_CHECK_MAX_LOOPS (200)
-#define QDSP6SS_XO_CBCR (0x0038)
-
-#define QDSP6SS_ACC_OVERRIDE_VAL 0x20
-
-#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
-static int segment_is_loadable(const struct elf32_phdr *p)
-{
- return (p->p_type == PT_LOAD) &&
- !segment_is_hash(p->p_flags) &&
- p->p_memsz;
+ return regulator_bulk_enable(ARRAY_SIZE(qproc->supply), qproc->supply);
}
-static bool segment_is_relocatable(const struct elf32_phdr *p)
+static void q6v5_regulator_disable(struct q6v5 *qproc)
{
- return !!(p->p_flags & BIT(27));
+ regulator_bulk_disable(ARRAY_SIZE(qproc->supply), qproc->supply);
+ regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_CX].consumer, 0, INT_MAX);
+ regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MX].consumer, 0, INT_MAX);
+ regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 0, 1150000);
}
-static int qproc_sanity_check(struct rproc *rproc,
- const struct firmware *fw)
+static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
{
- if (!fw) {
- dev_err(&rproc->dev, "failed to load %s\n", rproc->name);
- return -EINVAL;
- }
+ struct q6v5 *qproc = rproc->priv;
- /* XXX: ??? */
+ memcpy(qproc->mba_region, fw->data, fw->size);
return 0;
}
-static struct resource_table * qproc_find_rsc_table(struct rproc *rproc,
- const struct firmware *fw,
- int *tablesz)
+static const struct rproc_fw_ops q6v5_fw_ops = {
+ .find_rsc_table = qcom_mdt_find_rsc_table,
+ .load = q6v5_load,
+};
+
+static int q6v5_rmb_pbl_wait(struct q6v5 *qproc, int ms)
{
- static struct resource_table table = { .ver = 1, };
+ unsigned long timeout;
+ s32 val;
- *tablesz = sizeof(table);
- return &table;
+ timeout = jiffies + msecs_to_jiffies(ms);
+ for (;;) {
+ val = readl(qproc->rmb_base + RMB_PBL_STATUS_REG);
+ if (val)
+ break;
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ msleep(1);
+ }
+
+ return val;
}
-static int qproc_load(struct rproc *rproc, const struct firmware *fw)
+static int q6v5_rmb_mba_wait(struct q6v5 *qproc, u32 status, int ms)
{
- struct qproc *qproc = rproc->priv;
- DEFINE_DMA_ATTRS(attrs);
- dma_addr_t phys;
- dma_addr_t end;
- void *ptr;
- dma_set_mask(qproc->dev, DMA_BIT_MASK(32));
- dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &attrs);
+ unsigned long timeout;
+ s32 val;
- ptr = dma_alloc_attrs(qproc->dev, fw->size, &phys, GFP_KERNEL, &attrs);
- if (!ptr) {
- dev_err(qproc->dev, "failed to allocate mba metadata buffer\n");
- return -ENOMEM;
- }
+ timeout = jiffies + msecs_to_jiffies(ms);
+ for (;;) {
+ val = readl(qproc->rmb_base + RMB_MBA_STATUS_REG);
+ if (val < 0)
+ break;
- end = phys + fw->size;
- dev_info(qproc->dev, "loading MBA from %pa to %pa\n", &phys, &end);
+ if (!status && val)
+ break;
+ else if (status && val == status)
+ break;
- memcpy(ptr, fw->data, fw->size);
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
- qproc->mba_va = ptr;
- qproc->mba_da = phys;
- qproc->mba_size = fw->size;
- qproc->mba_attrs = attrs;
+ msleep(1);
+ }
- return 0;
+ return val;
}
-static const struct rproc_fw_ops qproc_fw_ops = {
- .find_rsc_table = qproc_find_rsc_table,
- .load = qproc_load,
- .sanity_check = qproc_sanity_check,
-};
-
-static void q6v5proc_reset(struct qproc *qproc)
+static void q6v5proc_reset(struct q6v5 *qproc)
{
u32 val;
/* Assert resets, stop core */
- val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET);
- val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA | Q6SS_STOP_CORE);
- writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET);
+ val = readl(qproc->reg_base + QDSP6SS_RESET_REG);
+ val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE);
+ writel(val, qproc->reg_base + QDSP6SS_RESET_REG);
/* Enable power block headswitch, and wait for it to stabilize */
- val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL);
+ val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val |= QDSS_BHS_ON | QDSS_LDO_BYP;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
mb();
udelay(1);
* Turn on memories. L2 banks should be done individually
* to minimize inrush current.
*/
- val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL);
+ val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N |
Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val |= Q6SS_L2DATA_SLP_NRET_N_2;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val |= Q6SS_L2DATA_SLP_NRET_N_1;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val |= Q6SS_L2DATA_SLP_NRET_N_0;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
/* Remove IO clamp */
val &= ~Q6SS_CLAMP_IO;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
/* Bring core out of reset */
- val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET);
+ val = readl(qproc->reg_base + QDSP6SS_RESET_REG);
val &= ~Q6SS_CORE_ARES;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET);
+ writel(val, qproc->reg_base + QDSP6SS_RESET_REG);
/* Turn on core clock */
- val = readl_relaxed(qproc->reg_base + QDSP6SS_GFMUX_CTL);
- val |= Q6SS_CLK_ENA;
-
-#if 0
- /* Need a different clock source for v5.2.0 */
- if (qproc->qdsp6v5_2_0) {
- val &= ~Q6SS_CLK_SRC_SEL_FIELD;
- val |= Q6SS_CLK_SRC_SEL_C;
- }
-
-#endif
- /* force clock on during source switch */
-// if (qproc->qdsp6v56)
- val |= Q6SS_CLK_SRC_SWITCH_CLK_OVR;
-
- writel_relaxed(val, qproc->reg_base + QDSP6SS_GFMUX_CTL);
+ val = readl(qproc->reg_base + QDSP6SS_GFMUX_CTL_REG);
+ val |= Q6SS_CLK_ENABLE;
+ writel(val, qproc->reg_base + QDSP6SS_GFMUX_CTL_REG);
/* Start core execution */
- val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET);
+ val = readl(qproc->reg_base + QDSP6SS_RESET_REG);
val &= ~Q6SS_STOP_CORE;
- writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET);
+ writel(val, qproc->reg_base + QDSP6SS_RESET_REG);
}
-static void q6v5proc_halt_axi_port(struct qproc *qproc, void __iomem *halt)
+static void q6v5proc_halt_axi_port(struct q6v5 *qproc,
+ struct regmap *halt_map,
+ u32 offset)
{
unsigned long timeout;
+ unsigned int val;
+ int ret;
- if (readl_relaxed(halt + AXI_IDLE))
+ /* Check if we're already idle */
+ ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
+ if (!ret && val)
return;
- /* Assert halt request */
- writel_relaxed(1, halt + AXI_HALTREQ);
+ /* Assert halt request */
+ regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
- /* Wait for halt */
- timeout = jiffies + 10 * HZ;
+ /* Wait for halt */
+ timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
for (;;) {
- if (readl(halt + AXI_HALTACK) || time_after(jiffies, timeout))
+ ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
+ if (ret || val || time_after(jiffies, timeout))
break;
msleep(1);
}
- if (!readl_relaxed(halt + AXI_IDLE))
- dev_err(qproc->dev, "port %pa failed halt\n", &halt);
+ ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
+ if (ret || !val)
+ dev_err(qproc->dev, "port failed halt\n");
- /* Clear halt request (port will remain halted until reset) */
- writel_relaxed(0, halt + AXI_HALTREQ);
+ /* Clear halt request (port will remain halted until reset) */
+ regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
}
-static int qproc_mba_load_mdt(struct qproc *qproc, const struct firmware *fw)
+static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw)
{
DEFINE_DMA_ATTRS(attrs);
- unsigned long timeout;
dma_addr_t phys;
- dma_addr_t end;
void *ptr;
int ret;
- s32 val;
- dma_set_mask(qproc->dev, DMA_BIT_MASK(32));
dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &attrs);
-
ptr = dma_alloc_attrs(qproc->dev, fw->size, &phys, GFP_KERNEL, &attrs);
if (!ptr) {
- dev_err(qproc->dev, "failed to allocate mba metadata buffer\n");
+ dev_err(qproc->dev, "failed to allocate mdt buffer\n");
return -ENOMEM;
}
- end = phys + fw->size;
- dev_info(qproc->dev, "loading mdt header from %pa to %pa\n", &phys, &end);
-
memcpy(ptr, fw->data, fw->size);
- writel_relaxed(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH);
+ writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG);
+ writel(RMB_CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
- writel_relaxed(phys, qproc->rmb_base + RMB_PMI_META_DATA);
- writel(CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND);
+ ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_META_DATA_AUTH_SUCCESS, 1000);
+ if (ret == -ETIMEDOUT)
+ dev_err(qproc->dev, "MBA header authentication timed out\n");
+ else if (ret < 0)
+ dev_err(qproc->dev, "MBA returned error %d for MDT header\n", ret);
- timeout = jiffies + HZ;
- for (;;) {
- msleep(1);
-
- val = readl(qproc->rmb_base + RMB_MBA_STATUS);
- if (val == STATUS_META_DATA_AUTH_SUCCESS || val < 0)
- break;
-
- if (time_after(jiffies, timeout))
- break;
- }
- if (val == 0) {
- dev_err(qproc->dev, "MBA authentication of headers timed out\n");
- ret = -ETIMEDOUT;
- goto out;
- } else if (val < 0) {
- dev_err(qproc->dev, "MBA returned error %d for headers\n", val);
- ret = -EINVAL;
- goto out;
- }
-
- dev_err(qproc->dev, "mdt authenticated\n");
-
- ret = 0;
-out:
dma_free_attrs(qproc->dev, fw->size, ptr, phys, &attrs);
- return ret;
+ return ret < 0 ? ret : 0;
}
-
-static int
-old_qproc_load_segments(struct qproc *qproc, const struct firmware *fw)
+static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw)
{
- struct device *dev = qproc->dev;
+ const struct elf32_phdr *phdrs;
+ const struct elf32_phdr *phdr;
struct elf32_hdr *ehdr;
- struct elf32_phdr *phdr;
- int i, ret = 0;
- const u8 *elf_data = fw->data;
- const struct firmware *seg_fw;
- char fw_name[20];
-
- ehdr = (struct elf32_hdr *)elf_data;
- phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
-
-
-
- /* go through the available ELF segments */
- for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
- u32 da = phdr->p_paddr;
- u32 memsz = phdr->p_memsz;
- u32 filesz = phdr->p_filesz;
- void *ptr;
-
- if (!segment_is_loadable(phdr))
- continue;
- /*
- if (phdr->p_type != PT_LOAD)
- continue;
-
- if (segment_is_hash(phdr->p_flags))
- continue;
-
- if (filesz == 0)
- continue;
-*/
- //dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
- pr_emerg("phdr(%d): type %d da 0x%x memsz 0x%x filesz 0x%x\n",
- i, phdr->p_type, da, memsz, filesz);
-
- if (filesz > memsz) {
- dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
- filesz, memsz);
- ret = -EINVAL;
- break;
- }
-
- ptr = ioremap(da, memsz);
- if (!ptr) {
- dev_err(qproc->dev, "failed to allocate mba metadata buffer\n");
- ret = -ENOMEM;
- break;
- }
-
- if (filesz) {
- snprintf(fw_name, sizeof(fw_name), "modem.b%02d", i);
- ret = request_firmware(&seg_fw, fw_name, qproc->dev);
- if (ret) {
- iounmap(ptr);
- break;
- }
-
- memcpy(ptr, seg_fw->data, filesz);
-
- release_firmware(seg_fw);
- }
-
- if (memsz > filesz)
- memset(ptr + filesz, 0, memsz - filesz);
-
-
-
- wmb();
- iounmap(ptr);
- }
-
- return ret;
-}
-
-static int qproc_verify_segment(struct qproc *qproc, phys_addr_t paddr, size_t size)
-{
- s32 status;
- u32 img_length;
-
- img_length = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH);
- dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %x\n", img_length);
-
- msleep(1);
- img_length = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH);
- dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %x\n", img_length);
-
-
- if (img_length == 0) {
- writel_relaxed(paddr, qproc->rmb_base + RMB_PMI_CODE_START);
- writel(CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND);
- }
- img_length += size;
- writel(img_length, qproc->rmb_base + RMB_PMI_CODE_LENGTH);
-
- img_length = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH);
- dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %x\n", img_length);
-
- status = readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS);
-
-
- if (status < 0) {
- dev_err(qproc->dev, "MBA returned error %d\n", status);
- }
-
- printk("DEBUG:pil: status... %08x\n", readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS));
-
- return 0;
-}
-
-static int qproc_load_segment(struct qproc *rproc, const char *fw_name,
- const struct elf32_phdr *phdr, phys_addr_t paddr)
-{
- const struct firmware *fw;
- void *ptr;
- int ret = 0;
-
- ptr = ioremap_nocache(paddr, phdr->p_memsz);
- if (!ptr) {
- dev_err(rproc->dev, "failed to ioremap segment area (%pa+0x%x)\n", &paddr, phdr->p_memsz);
- return -EBUSY;
- }
-
- if (phdr->p_filesz) {
- ret = request_firmware(&fw, fw_name, rproc->dev);
- if (ret) {
- dev_err(rproc->dev, "failed to load %s\n", fw_name);
- goto out;
- }
-
- memcpy_toio(ptr, fw->data, fw->size);
+ phys_addr_t boot_addr;
+ phys_addr_t fw_addr;
+ bool relocate;
+ size_t size;
+ u32 val;
+ int ret;
+ int i;
- release_firmware(fw);
+ ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate);
+ if (ret) {
+ dev_err(qproc->dev, "failed to parse mdt header\n");
+ return ret;
}
- if (phdr->p_memsz > phdr->p_filesz)
- memset_io(ptr + phdr->p_filesz, 0,
- phdr->p_memsz - phdr->p_filesz);
-
- printk("DEBUG:pil: verifing %s size: %x\n",fw_name, phdr->p_memsz);
- qproc_verify_segment(rproc, paddr, phdr->p_memsz);
-
-out:
- iounmap(ptr);
- return ret;
-}
-
-static int
-qproc_load_segments(struct qproc *qproc, const struct firmware *fw)
-{
- struct device *dev = qproc->dev;
- struct elf32_hdr *ehdr;
- struct elf32_phdr *phdr;
- int i, ret = 0;
- const u8 *elf_data = fw->data;
- const struct firmware *seg_fw;
- char fw_name[20];
-
- const struct mdt_hdr *mdt;
- phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
- phys_addr_t max_addr = 0;
- size_t align = 0;
- bool relocatable = false;
- phys_addr_t paddr;
-
-
- ehdr = (struct elf32_hdr *)elf_data;
- phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
-
-
- mdt = (struct mdt_hdr *)fw->data;
- ehdr = &mdt->hdr;
-
- for (i = 0; i < ehdr->e_phnum; i++) {
- phdr = &mdt->phdr[i];
-
- if (!segment_is_loadable(phdr))
- continue;
-
- if (phdr->p_paddr < min_addr) {
- min_addr = phdr->p_paddr;
-
- if (segment_is_relocatable(phdr)) {
- align = phdr->p_align;
- relocatable = true;
- }
- }
-
- if (phdr->p_paddr + phdr->p_memsz > max_addr)
- max_addr = round_up(phdr->p_paddr + phdr->p_memsz, SZ_4K);
- }
+ if (relocate)
+ boot_addr = qproc->mpss_phys;
+ else
+ boot_addr = fw_addr;
- ehdr = (struct elf32_hdr *)elf_data;
- phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
- /* go through the available ELF segments */
+ ehdr = (struct elf32_hdr *)fw->data;
+ phdrs = (struct elf32_phdr *)(ehdr + 1);
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
- u32 da = phdr->p_paddr;
- u32 paddr = phdr->p_paddr;
- u32 memsz = phdr->p_memsz;
- u32 filesz = phdr->p_filesz;
- void *ptr;
+ phdr = &phdrs[i];
- if (!segment_is_loadable(phdr))
- continue;
- /*
if (phdr->p_type != PT_LOAD)
continue;
- if (segment_is_hash(phdr->p_flags))
+ if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
continue;
- if (filesz == 0)
+ if (!phdr->p_memsz)
continue;
-*/
- //dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
- pr_emerg("phdr(%d): type %d paddr 0x%x memsz 0x%x filesz 0x%x\n",
- i, phdr->p_type, paddr, memsz, filesz);
-
- if (filesz > memsz) {
- dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
- filesz, memsz);
- ret = -EINVAL;
- break;
- }
- paddr = relocatable ?
- (phdr->p_paddr - min_addr + qproc->reloc_phys) :
- phdr->p_paddr;
-
- pr_emerg("Relocated-phdr(%d): type %d paddr 0x%x memsz 0x%x filesz 0x%x\n",
- i, phdr->p_type, paddr, memsz, filesz);
-// if (filesz) {
- snprintf(fw_name, sizeof(fw_name), "modem.b%02d", i);
- ret = qproc_load_segment(qproc, fw_name, phdr, paddr);
-// }
-#if 0
-
- ptr = ioremap(da, memsz);
- if (!ptr) {
- dev_err(qproc->dev, "failed to allocate mba metadata buffer\n");
- ret = -ENOMEM;
- break;
+ size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
+ if (!size) {
+ writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG);
+ writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
}
- if (filesz) {
- snprintf(fw_name, sizeof(fw_name), "modem.b%02d", i);
- ret = request_firmware(&seg_fw, fw_name, qproc->dev);
- if (ret) {
- iounmap(ptr);
- break;
- }
-
- memcpy(ptr, seg_fw->data, filesz);
-
- release_firmware(seg_fw);
- }
-
- if (memsz > filesz)
- memset(ptr + filesz, 0, memsz - filesz);
-
-
-
- wmb();
- iounmap(ptr);
-#endif
+ size += phdr->p_memsz;
+ writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
}
- return ret;
+ val = readl(qproc->rmb_base + RMB_MBA_STATUS_REG);
+ return val < 0 ? val : 0;
}
-static int qproc_verify_segments(struct qproc *qproc, const struct firmware *fw)
+static int q6v5_mpss_load(struct q6v5 *qproc)
{
- struct elf32_hdr *ehdr;
- struct elf32_phdr *phdr;
- const u8 *elf_data = fw->data;
- unsigned long timeout;
- phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
- u32 size = 0;
- s32 val;
+ const struct firmware *fw;
+ phys_addr_t fw_addr;
+ bool relocate;
int ret;
- int i;
- u32 v;
-
- ehdr = (struct elf32_hdr *)elf_data;
- phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
-
- v = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH);
- dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %pa\n", &v);
-
- msleep(1);
-
-
-#if 1
- for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
- phys_addr_t da = phdr->p_paddr;
- u32 memsz = phdr->p_memsz;
-
-
- if (!segment_is_loadable(phdr))
- continue;
- /*
- if (phdr->p_type != PT_LOAD)
- continue;
-*/
- dev_err(qproc->dev, "0x%x %d %d\n", phdr->p_paddr, segment_is_hash(phdr->p_flags), !!(phdr->p_flags & BIT(27)));
-
- /*
- if (segment_is_hash(phdr->p_flags))
- continue;
-
- if (memsz == 0)
- continue;
- */
- if (da < min_addr)
- min_addr = da;
-
- size += memsz;
- }
-
- dev_err(qproc->dev, "verify: %pa:%pa\n", &min_addr, &size);
- v = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH);
- dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %pa\n", &v);
-#if 0
-if (v == 0) {
- writel_relaxed(min_addr, qproc->rmb_base + RMB_PMI_CODE_START);
- writel(CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND);
-}
- writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH);
-#endif
-#endif
-
- v = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH);
- dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %pa\n", &v);
-
- printk("DEBUG:pil: status... %08x\n", readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS));
- timeout = jiffies + 10 * HZ;
- for (;;) {
- msleep(1);
-
- val = readl(qproc->rmb_base + RMB_MBA_STATUS);
- if (val == STATUS_AUTH_COMPLETE || val < 0)
- break;
-
- if (time_after(jiffies, timeout))
- break;
- }
- if (val == 0) {
- dev_err(qproc->dev, "MBA authentication of headers timed out\n");
- ret = -ETIMEDOUT;
- goto out;
- } else if (val < 0) {
- dev_err(qproc->dev, "MBA returned error %d for segments\n", val);
- ret = -EINVAL;
- goto out;
+ ret = request_firmware(&fw, MPSS_FIRMWARE_NAME, qproc->dev);
+ if (ret < 0) {
+ dev_err(qproc->dev, "unable to load " MPSS_FIRMWARE_NAME "\n");
+ return ret;
}
- ret = 0;
-out:
- return ret;
-
-
-}
-
-static int qproc_msa_mba_auth(struct qproc *qproc)
-{
- unsigned long timeout;
- s32 val;
- int ret;
- int i;
- u32 v;
-
- timeout = jiffies + 10 * HZ;
- for (;;) {
- msleep(1);
-
- val = readl(qproc->rmb_base + RMB_MBA_STATUS);
- if (val == STATUS_AUTH_COMPLETE || val < 0)
- break;
-
- if (time_after(jiffies, timeout))
- break;
- }
- if (val == 0) {
- dev_err(qproc->dev, "MBA authentication of headers timed out\n");
- ret = -ETIMEDOUT;
-// goto out;
- } else if (val < 0) {
- dev_err(qproc->dev, "MBA returned error %d for segments\n", val);
- ret = -EINVAL;
-// goto out;
+ ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate);
+ if (ret) {
+ dev_err(qproc->dev, "failed to parse mdt header\n");
+ goto release_firmware;
}
- printk("DEBUG:pil: status auth... %08x\n", readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS));
-
- return 0;
-}
-static int qproc_load_modem(struct qproc *qproc)
-{
- const struct firmware *fw;
- int ret;
+ if (relocate)
+ qproc->mpss_reloc = fw_addr;
- ret = request_firmware(&fw, "modem.mdt", qproc->dev);
- if (ret < 0) {
- dev_err(qproc->dev, "unable to load modem.mdt\n");
- return ret;
- }
+ /* Initialize the RMB validator */
+ writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
- dev_err(qproc->dev, "Loading mba\n");
- ret = qproc_mba_load_mdt(qproc, fw);
+ ret = q6v5_mpss_init_image(qproc, fw);
if (ret)
- goto out;
+ goto release_firmware;
- dev_err(qproc->dev, "Loading segments\n");
- ret = qproc_load_segments(qproc, fw);
+ ret = qcom_mdt_load(qproc->rproc, fw, MPSS_FIRMWARE_NAME);
if (ret)
- goto out;
+ goto release_firmware;
- ret = qproc_msa_mba_auth(qproc);
-// dev_err(qproc->dev, "Verifying segments\n");
-// ret = qproc_verify_segments(qproc, fw);
-// if (ret)
-// goto out;
- return 0;
-out:
- release_firmware(fw);
+ ret = q6v5_mpss_validate(qproc, fw);
+ if (ret)
+ goto release_firmware;
- return ret;
-}
+ ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_AUTH_COMPLETE, 10000);
+ if (ret == -ETIMEDOUT)
+ dev_err(qproc->dev, "MBA authentication timed out\n");
+ else if (ret < 0)
+ dev_err(qproc->dev, "MBA returned error %d\n", ret);
-static int pil_mss_restart_reg(struct qproc *qproc, int mss_restart)
-{
- int ret = 0;
- unsigned int resp;
- ret = qcom_scm_restart_proc(MSS_RESTART_ID, mss_restart, &resp);
- if (ret || resp)
- pr_err("Secure MSS restart failed\n");
+release_firmware:
+ release_firmware(fw);
- return ret;
+ return ret < 0 ? ret : 0;
}
-static int qproc_start(struct rproc *rproc)
+static int q6v5_start(struct rproc *rproc)
{
- struct qproc *qproc = (struct qproc *)rproc->priv;
- unsigned long timeout;
+ struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
int ret;
- u32 val;
-
- printk("DEBUG::: pill................... %s \n", __func__);
-// ret = regulator_enable(qproc->vdd);
-// if (ret) {
-// dev_err(qproc->dev, "failed to enable mss vdd\n");
-// return ret;
-// }
- ret = regulator_enable(qproc->pll);
+ ret = q6v5_regulator_enable(qproc);
if (ret) {
- dev_err(qproc->dev, "failed to enable mss vdd pll\n");
+ dev_err(qproc->dev, "failed to enable supplies\n");
return ret;
}
-
- ret = pil_mss_restart_reg(qproc, 0);
- //FIXME
- //ret = reset_control_deassert(qproc->mss_restart);
+ ret = reset_control_deassert(qproc->mss_restart);
if (ret) {
dev_err(qproc->dev, "failed to deassert mss restart\n");
goto disable_vdd;
if (ret)
goto disable_axi_clk;
- writel_relaxed(qproc->mba_da, qproc->rmb_base + RMB_MBA_IMAGE);
-
- /* Ensure order of data/entry point and the following reset release */
- wmb();
+ writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG);
q6v5proc_reset(qproc);
- timeout = jiffies + HZ;
- for (;;) {
- msleep(1);
-
- val = readl(qproc->rmb_base + RMB_PBL_STATUS);
- if (val || time_after(jiffies, timeout))
- break;
- }
- if (val == 0) {
+ ret = q6v5_rmb_pbl_wait(qproc, 1000);
+ if (ret == -ETIMEDOUT) {
dev_err(qproc->dev, "PBL boot timed out\n");
- ret = -ETIMEDOUT;
goto halt_axi_ports;
- } else if (val != STATUS_PBL_SUCCESS) {
- dev_err(qproc->dev, "PBL returned unexpected status %d\n", val);
+ } else if (ret != RMB_PBL_SUCCESS) {
+ dev_err(qproc->dev, "PBL returned unexpected status %d\n", ret);
ret = -EINVAL;
goto halt_axi_ports;
}
- timeout = jiffies + HZ;
- for (;;) {
- msleep(1);
-
- val = readl(qproc->rmb_base + RMB_MBA_STATUS);
- if (val || time_after(jiffies, timeout))
- break;
- }
- if (val == 0) {
+ ret = q6v5_rmb_mba_wait(qproc, 0, 5000);
+ if (ret == -ETIMEDOUT) {
dev_err(qproc->dev, "MBA boot timed out\n");
- ret = -ETIMEDOUT;
goto halt_axi_ports;
- } else if (val != STATUS_XPU_UNLOCKED && val != STATUS_XPU_UNLOCKED_SCRIBBLED) {
- dev_err(qproc->dev, "MBA returned unexpected status %d\n", val);
+ } else if (ret != RMB_MBA_XPU_UNLOCKED && ret != RMB_MBA_XPU_UNLOCKED_SCRIBBLED) {
+ dev_err(qproc->dev, "MBA returned unexpected status %d\n", ret);
ret = -EINVAL;
goto halt_axi_ports;
}
- dev_info(qproc->dev, "MBA boot done\n");
+ dev_info(qproc->dev, "MBA booted, loading mpss\n");
- ret = qproc_load_modem(qproc);
+ ret = q6v5_mpss_load(qproc);
if (ret)
goto halt_axi_ports;
+ ret = wait_for_completion_timeout(&qproc->start_done,
+ msecs_to_jiffies(5000));
+ if (ret == 0) {
+ dev_err(qproc->dev, "start timed out\n");
+ ret = -ETIMEDOUT;
+ goto halt_axi_ports;
+ }
+
+ qproc->running = true;
+
+ /* All done, release the handover resources */
+
return 0;
halt_axi_ports:
- q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_Q6_HALT_BASE);
- q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_MODEM_HALT_BASE);
- q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_NC_HALT_BASE);
+ q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
+ q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
+ q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
disable_axi_clk:
clk_disable_unprepare(qproc->axi_clk);
disable_ahb_clk:
assert_reset:
reset_control_assert(qproc->mss_restart);
disable_vdd:
-// regulator_disable(qproc->vdd);
+ q6v5_regulator_disable(qproc);
- dma_free_attrs(qproc->dev, qproc->mba_size, qproc->mba_va, qproc->mba_da, &qproc->mba_attrs);
return ret;
}
-static int qproc_stop(struct rproc *rproc)
+static int q6v5_stop(struct rproc *rproc)
{
- struct qproc *qproc = (struct qproc *)rproc->priv;
+ struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
+ int ret;
+
+ qproc->running = false;
+
+ qcom_smem_state_update_bits(qproc->state,
+ BIT(qproc->stop_bit), BIT(qproc->stop_bit));
+
+ ret = wait_for_completion_timeout(&qproc->stop_done,
+ msecs_to_jiffies(5000));
+ if (ret == 0)
+ dev_err(qproc->dev, "timed out on wait\n");
- q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_Q6_HALT_BASE);
- q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_MODEM_HALT_BASE);
- q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_NC_HALT_BASE);
+ qcom_smem_state_update_bits(qproc->state, BIT(qproc->stop_bit), 0);
+
+ q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
+ q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
+ q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
reset_control_assert(qproc->mss_restart);
clk_disable_unprepare(qproc->axi_clk);
clk_disable_unprepare(qproc->ahb_clk);
-// regulator_disable(qproc->vdd);
-
- dma_free_attrs(qproc->dev, qproc->mba_size, qproc->mba_va, qproc->mba_da, &qproc->mba_attrs);
+ q6v5_regulator_disable(qproc);
return 0;
}
-static const struct rproc_ops qproc_ops = {
- .start = qproc_start,
- .stop = qproc_stop,
-};
-
-static irqreturn_t qproc_wdog_interrupt(int irq, void *dev)
+static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len)
{
- struct qproc *qproc = dev;
+ struct q6v5 *qproc = rproc->priv;
+ int offset;
- dev_err(qproc->dev, " WATCHDOG\n");
+ offset = da - qproc->mpss_reloc;
+ if (offset < 0 || offset + len > qproc->mpss_size)
+ return NULL;
- rproc_report_crash(qproc->rproc, RPROC_WATCHDOG);
- return IRQ_HANDLED;
+ return qproc->mpss_region + offset;
}
-static irqreturn_t qproc_fatal_interrupt(int irq, void *dev)
-{
- struct qproc *qproc = dev;
-
- dev_err(qproc->dev, " FATAL\n");
+static const struct rproc_ops q6v5_ops = {
+ .start = q6v5_start,
+ .stop = q6v5_stop,
+ .da_to_va = q6v5_da_to_va,
+};
- rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR);
+static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev)
+{
+ struct q6v5 *qproc = dev;
+ size_t len;
+ char *msg;
- return IRQ_HANDLED;
-}
+ /* Sometimes the stop triggers a watchdog rather than a stop-ack */
+ if (!qproc->running) {
+ complete(&qproc->stop_done);
+ return IRQ_HANDLED;
+ }
-static irqreturn_t qproc_ready_interrupt(int irq, void *dev)
-{
- struct qproc *qproc = dev;
+ msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len);
+ if (!IS_ERR(msg) && len > 0 && msg[0])
+ dev_err(qproc->dev, "watchdog received: %s\n", msg);
+ else
+ dev_err(qproc->dev, "watchdog without message\n");
- dev_err(qproc->dev, " READY\n");
+ rproc_report_crash(qproc->rproc, RPROC_WATCHDOG);
- complete(&qproc->start_done);
+ if (!IS_ERR(msg))
+ msg[0] = '\0';
return IRQ_HANDLED;
}
-static irqreturn_t qproc_handover_interrupt(int irq, void *dev)
+static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev)
{
- struct qproc *qproc = dev;
+ struct q6v5 *qproc = dev;
+ size_t len;
+ char *msg;
- dev_err(qproc->dev, " HANDOVER\n");
-
- return IRQ_HANDLED;
-}
+ msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len);
+ if (!IS_ERR(msg) && len > 0 && msg[0])
+ dev_err(qproc->dev, "fatal error received: %s\n", msg);
+ else
+ dev_err(qproc->dev, "fatal error without message\n");
-static irqreturn_t qproc_stop_ack_interrupt(int irq, void *dev)
-{
- struct qproc *qproc = dev;
+ rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR);
- dev_err(qproc->dev, " STOP-ACK\n");
+ if (!IS_ERR(msg))
+ msg[0] = '\0';
return IRQ_HANDLED;
}
-static ssize_t qproc_boot_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
+static irqreturn_t q6v5_handover_interrupt(int irq, void *dev)
{
- struct qproc *qproc = dev_get_drvdata(dev);
- int ret;
+ struct q6v5 *qproc = dev;
- ret = rproc_boot(qproc->rproc);
- return ret ? : size;
+ complete(&qproc->start_done);
+ return IRQ_HANDLED;
}
-static ssize_t qproc_shutdown_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
+static irqreturn_t q6v5_stop_ack_interrupt(int irq, void *dev)
{
- struct qproc *qproc = dev_get_drvdata(dev);
+ struct q6v5 *qproc = dev;
- rproc_shutdown(qproc->rproc);
- return size;
+ complete(&qproc->stop_done);
+ return IRQ_HANDLED;
}
-static const struct device_attribute qproc_attrs[] = {
- __ATTR(boot, S_IWUSR, 0, qproc_boot_store),
- __ATTR(shutdown, S_IWUSR, 0, qproc_shutdown_store),
-};
-
-static int qproc_init_mem(struct qproc *qproc, struct platform_device *pdev)
+static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
{
+ struct device_node *halt_np;
struct resource *res;
+ int ret;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base");
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
qproc->reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->reg_base))
+ if (IS_ERR(qproc->reg_base)) {
+ dev_err(qproc->dev, "failed to get qdsp6_base\n");
return PTR_ERR(qproc->reg_base);
+ }
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base");
- qproc->halt_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->halt_base)) {
-#if 0
- //return PTR_ERR(qproc->halt_base);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_q6");
- qproc->halt_q6 = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->halt_q6))
- return PTR_ERR(qproc->halt_q6);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb");
+ qproc->rmb_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(qproc->rmb_base)) {
+ dev_err(qproc->dev, "failed to get rmb_base\n");
+ return PTR_ERR(qproc->rmb_base);
+ }
+ halt_np = of_parse_phandle(pdev->dev.of_node, "qcom,halt-regs", 0);
+ if (!halt_np) {
+ dev_err(&pdev->dev, "no qcom,halt-regs node\n");
+ return -ENODEV;
+ }
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_modem");
- qproc->halt_modem = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->halt_modem))
- return PTR_ERR(qproc->halt_modem);
+ qproc->halt_map = syscon_node_to_regmap(halt_np);
+ if (IS_ERR(qproc->halt_map))
+ return PTR_ERR(qproc->halt_map);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_nc");
- qproc->halt_nc = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->halt_nc))
- return PTR_ERR(qproc->halt_nc);
-#endif
+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,halt-regs",
+ 1, &qproc->halt_q6);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no q6 halt offset\n");
+ return -EINVAL;
+ }
+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,halt-regs",
+ 2, &qproc->halt_modem);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no modem halt offset\n");
+ return -EINVAL;
}
-//Only required if self auth is set???
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_base");
- qproc->rmb_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->rmb_base))
- return PTR_ERR(qproc->rmb_base);
-// res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg_sec");
-// qproc->restart_sec_base = devm_ioremap_resource(&pdev->dev, res);
-// if (IS_ERR(qproc->restart_sec_base))
-// return PTR_ERR(qproc->restart_sec_base);
+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,halt-regs",
+ 3, &qproc->halt_nc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no nc halt offset\n");
+ return -EINVAL;
+ }
return 0;
}
-static int qproc_init_clocks(struct qproc *qproc)
+static int q6v5_init_clocks(struct q6v5 *qproc)
{
qproc->ahb_clk = devm_clk_get(qproc->dev, "iface");
- if (IS_ERR(qproc->ahb_clk))
+ if (IS_ERR(qproc->ahb_clk)) {
+ dev_err(qproc->dev, "failed to get iface clock\n");
return PTR_ERR(qproc->ahb_clk);
+ }
qproc->axi_clk = devm_clk_get(qproc->dev, "bus");
- if (IS_ERR(qproc->axi_clk))
+ if (IS_ERR(qproc->axi_clk)) {
+ dev_err(qproc->dev, "failed to get bus clock\n");
return PTR_ERR(qproc->axi_clk);
+ }
qproc->rom_clk = devm_clk_get(qproc->dev, "mem");
- if (IS_ERR(qproc->rom_clk))
+ if (IS_ERR(qproc->rom_clk)) {
+ dev_err(qproc->dev, "failed to get mem clock\n");
return PTR_ERR(qproc->rom_clk);
+ }
return 0;
}
-static int qproc_init_regulators(struct qproc *qproc)
-{
- int ret;
- u32 uV;
-
- printk("DEBUG:pil: starting vdd\n");
-// qproc->vdd = devm_regulator_get_optional(qproc->dev, "qcom,vdd");
-// if (IS_ERR(qproc->vdd))
-// return PTR_ERR(qproc->vdd);
-
-// regulator_set_voltage(qproc->vdd, VDD_MSS_UV, VDD_MSS_UV_MAX);
-// regulator_set_load(qproc->vdd, VDD_MSS_UA);
-
- printk("DEBUG:pil: done vdd\n");
-// qproc->cx = devm_regulator_get(qproc->dev, "qcom,cx");
-// if (IS_ERR(qproc->cx))
-// return PTR_ERR(qproc->cx);
-
- qproc->mx = devm_regulator_get(qproc->dev, "qcom,mx");
- if (IS_ERR(qproc->mx))
- return PTR_ERR(qproc->mx);
-
- ret = of_property_read_u32(qproc->dev->of_node, "qcom,mx-uV", &uV);
- if (!ret)
- regulator_set_voltage(qproc->mx, uV, uV);
-
- qproc->pll = devm_regulator_get(qproc->dev, "qcom,pll");
- if (IS_ERR(qproc->pll))
- return PTR_ERR(qproc->pll);
-
- ret = of_property_read_u32(qproc->dev->of_node, "qcom,pll-uV", &uV);
- if (!ret)
- regulator_set_voltage(qproc->pll, uV, uV);
-
- return 0;
-}
-
-static int qproc_init_reset(struct qproc *qproc)
+static int q6v5_init_reset(struct q6v5 *qproc)
{
- //FIXME
qproc->mss_restart = devm_reset_control_get(qproc->dev, NULL);
if (IS_ERR(qproc->mss_restart)) {
dev_err(qproc->dev, "failed to acquire mss restart\n");
return 0;
}
-static int qproc_request_irq(struct qproc *qproc,
+static int q6v5_request_irq(struct q6v5 *qproc,
struct platform_device *pdev,
const char *name,
irq_handler_t thread_fn)
ret = devm_request_threaded_irq(&pdev->dev, ret,
NULL, thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "qproc", qproc);
+ "q6v5", qproc);
if (ret)
dev_err(&pdev->dev, "request %s IRQ failed\n", name);
return ret;
}
-static int qproc_probe(struct platform_device *pdev)
+static int q6v5_alloc_memory_region(struct q6v5 *qproc)
+{
+ struct device_node *child;
+ struct device_node *node;
+ struct resource r;
+ int ret;
+
+ child = of_get_child_by_name(qproc->dev->of_node, "mba");
+ node = of_parse_phandle(child, "memory-region", 0);
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret) {
+ dev_err(qproc->dev, "unable to resolve mba region\n");
+ return ret;
+ }
+
+ qproc->mba_phys = r.start;
+ qproc->mba_size = resource_size(&r);
+ qproc->mba_region = devm_ioremap_wc(qproc->dev, qproc->mba_phys, qproc->mba_size);
+ if (!qproc->mba_region) {
+ dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n",
+ &r.start, qproc->mba_size);
+ return -EBUSY;
+ }
+
+ child = of_get_child_by_name(qproc->dev->of_node, "mpss");
+ node = of_parse_phandle(child, "memory-region", 0);
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret) {
+ dev_err(qproc->dev, "unable to resolve mpss region\n");
+ return ret;
+ }
+
+ qproc->mpss_phys = qproc->mpss_reloc = r.start;
+ qproc->mpss_size = resource_size(&r);
+ qproc->mpss_region = devm_ioremap_wc(qproc->dev, qproc->mpss_phys, qproc->mpss_size);
+ if (!qproc->mpss_region) {
+ dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n",
+ &r.start, qproc->mpss_size);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int q6v5_probe(struct platform_device *pdev)
{
- struct qproc *qproc;
+ struct q6v5 *qproc;
struct rproc *rproc;
int ret;
- int i;
- struct device_node *np;
- struct resource r;
- rproc = rproc_alloc(&pdev->dev, pdev->name, &qproc_ops,
- "mba.mbn", sizeof(*qproc));
- if (!rproc)
+ rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops,
+ MBA_FIRMWARE_NAME, sizeof(*qproc));
+ if (!rproc) {
+ dev_err(&pdev->dev, "failed to allocate rproc\n");
return -ENOMEM;
+ }
- rproc->fw_ops = &qproc_fw_ops;
+ rproc->fw_ops = &q6v5_fw_ops;
- qproc = (struct qproc *)rproc->priv;
+ qproc = (struct q6v5 *)rproc->priv;
qproc->dev = &pdev->dev;
qproc->rproc = rproc;
platform_set_drvdata(pdev, qproc);
init_completion(&qproc->start_done);
+ init_completion(&qproc->stop_done);
- ret = qproc_init_mem(qproc, pdev);
+ ret = q6v5_init_mem(qproc, pdev);
if (ret)
goto free_rproc;
- ret = qproc_init_clocks(qproc);
+ ret = q6v5_alloc_memory_region(qproc);
if (ret)
goto free_rproc;
- ret = qproc_init_regulators(qproc);
+ ret = q6v5_init_clocks(qproc);
if (ret)
goto free_rproc;
- //FIXME need to convert this to a proper reset...
-// ret = qproc_init_reset(qproc);
-// if (ret)
-// goto free_rproc;
+ ret = q6v5_regulator_init(qproc);
+ if (ret)
+ goto free_rproc;
- ret = qproc_request_irq(qproc, pdev, "wdog", qproc_wdog_interrupt);
- if (ret < 0)
+ ret = q6v5_init_reset(qproc);
+ if (ret)
goto free_rproc;
- qproc->wdog_irq = ret;
- ret = qproc_request_irq(qproc, pdev, "fatal", qproc_fatal_interrupt);
+ ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt);
if (ret < 0)
goto free_rproc;
- qproc->fatal_irq = ret;
- ret = qproc_request_irq(qproc, pdev, "ready", qproc_ready_interrupt);
+ ret = q6v5_request_irq(qproc, pdev, "fatal", q6v5_fatal_interrupt);
if (ret < 0)
goto free_rproc;
- qproc->ready_irq = ret;
- ret = qproc_request_irq(qproc, pdev, "handover", qproc_handover_interrupt);
+ ret = q6v5_request_irq(qproc, pdev, "handover", q6v5_handover_interrupt);
if (ret < 0)
goto free_rproc;
- qproc->handover_irq = ret;
- ret = qproc_request_irq(qproc, pdev, "stop-ack", qproc_stop_ack_interrupt);
+ ret = q6v5_request_irq(qproc, pdev, "stop-ack", q6v5_stop_ack_interrupt);
if (ret < 0)
goto free_rproc;
- qproc->stop_ack_irq = ret;
-
- qproc->stop_gpio = devm_gpiod_get(&pdev->dev, "qcom,stop", GPIOD_OUT_LOW);
- if (IS_ERR(qproc->stop_gpio)) {
- dev_err(&pdev->dev, "failed to acquire stop gpio\n");
- return PTR_ERR(qproc->stop_gpio);
- }
-
- for (i = 0; i < ARRAY_SIZE(qproc_attrs); i++) {
- ret = device_create_file(&pdev->dev, &qproc_attrs[i]);
- if (ret) {
- dev_err(&pdev->dev, "unable to create sysfs file\n");
- goto remove_device_files;
- }
- }
-
- np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
- if (!np) {
- dev_err(&pdev->dev, "No memory region specified\n");
- } else {
-
- ret = of_address_to_resource(np, 0, &r);
- of_node_put(np);
- if (ret)
- return ret;
-
- qproc->reloc_phys = r.start;
- qproc->reloc_size = resource_size(&r);
-
- dev_info(&pdev->dev, "Found relocation area %lu@%pad\n",
- qproc->reloc_size, &qproc->reloc_phys);
- }
+ qproc->state = qcom_smem_state_get(&pdev->dev, "stop", &qproc->stop_bit);
+ if (IS_ERR(qproc->state))
+ goto free_rproc;
ret = rproc_add(rproc);
if (ret)
- goto remove_device_files;
+ goto free_rproc;
return 0;
-remove_device_files:
- for (i--; i >= 0; i--)
- device_remove_file(&pdev->dev, &qproc_attrs[i]);
-
free_rproc:
rproc_put(rproc);
return ret;
}
-static int qproc_remove(struct platform_device *pdev)
+static int q6v5_remove(struct platform_device *pdev)
{
- struct qproc *qproc = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(qproc_attrs); i++)
- device_remove_file(&pdev->dev, &qproc_attrs[i]);
+ struct q6v5 *qproc = platform_get_drvdata(pdev);
+ rproc_del(qproc->rproc);
rproc_put(qproc->rproc);
return 0;
}
-static const struct of_device_id qproc_of_match[] = {
+static const struct of_device_id q6v5_of_match[] = {
{ .compatible = "qcom,q6v5-pil", },
{ },
};
-static struct platform_driver qproc_driver = {
- .probe = qproc_probe,
- .remove = qproc_remove,
+static struct platform_driver q6v5_driver = {
+ .probe = q6v5_probe,
+ .remove = q6v5_remove,
.driver = {
.name = "qcom-q6v5-pil",
- .of_match_table = qproc_of_match,
+ .of_match_table = q6v5_of_match,
},
};
-module_platform_driver(qproc_driver);
+module_platform_driver(q6v5_driver);
max_addr = round_up(phdr->p_paddr + phdr->p_memsz, SZ_4K);
}
- ret = qcom_scm_pas_init_image(qproc->dev,
- qproc->pas_id, fw->data, fw->size);
+ ret = qcom_scm_pas_init_image(qproc->pas_id, fw->data, fw->size);
if (ret) {
dev_err(qproc->dev, "Invalid firmware metadata\n");
return -EINVAL;
--- /dev/null
+/*
+ * Qualcomm Peripheral Image Loader
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Copyright (C) 2014 Sony Mobile Communications AB
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as 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/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom_scm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/remoteproc.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/smem_state.h>
+
+#include "qcom_mdt_loader.h"
+#include "remoteproc_internal.h"
+#include "qcom_wcnss.h"
+
+#define WCNSS_CRASH_REASON_SMEM 422
+#define WCNSS_FIRMWARE_NAME "wcnss.mdt"
+#define WCNSS_PAS_ID 6
+
+#define WCNSS_SPARE_NVBIN_DLND BIT(25)
+
+#define WCNSS_PMU_IRIS_XO_CFG BIT(3)
+#define WCNSS_PMU_IRIS_XO_EN BIT(4)
+#define WCNSS_PMU_GC_BUS_MUX_SEL_TOP BIT(5)
+#define WCNSS_PMU_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */
+
+#define WCNSS_PMU_IRIS_RESET BIT(7)
+#define WCNSS_PMU_IRIS_RESET_STS BIT(8) /* 1: in progress, 0: done */
+#define WCNSS_PMU_IRIS_XO_READ BIT(9)
+#define WCNSS_PMU_IRIS_XO_READ_STS BIT(10)
+
+#define WCNSS_PMU_XO_MODE_MASK GENMASK(2, 1)
+#define WCNSS_PMU_XO_MODE_19p2 0
+#define WCNSS_PMU_XO_MODE_48 3
+
+static const struct rproc_ops wcnss_ops;
+
+struct wcnss_data {
+ size_t pmu_offset;
+ size_t spare_offset;
+
+ const struct wcnss_vreg_info *vregs;
+ size_t num_vregs;
+};
+
+struct qcom_wcnss {
+ struct device *dev;
+ struct rproc *rproc;
+
+ void __iomem *pmu_cfg;
+ void __iomem *spare_out;
+
+ bool use_48mhz_xo;
+
+ int wdog_irq;
+ int fatal_irq;
+ int ready_irq;
+ int handover_irq;
+ int stop_ack_irq;
+
+ struct qcom_smem_state *state;
+ unsigned stop_bit;
+
+ struct mutex iris_lock;
+ struct qcom_iris *iris;
+
+ struct regulator_bulk_data *vregs;
+ size_t num_vregs;
+
+ struct completion start_done;
+ struct completion stop_done;
+
+ phys_addr_t mem_phys;
+ phys_addr_t mem_reloc;
+ void *mem_region;
+ size_t mem_size;
+};
+
+static const struct wcnss_data riva_data = {
+ .pmu_offset = 0x28,
+ .spare_offset = 0xb4,
+
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddmx", 1050000, 1150000, 0 },
+ { "vddcx", 1050000, 1150000, 0 },
+ { "vddpx", 1800000, 1800000, 0 },
+ },
+ .num_vregs = 3,
+};
+
+static const struct wcnss_data pronto_v1_data = {
+ .pmu_offset = 0x1004,
+ .spare_offset = 0x1088,
+
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddmx", 950000, 1150000, 0 },
+ { "vddcx", .super_turbo = true},
+ { "vddpx", 1800000, 1800000, 0 },
+ },
+ .num_vregs = 3,
+};
+
+static const struct wcnss_data pronto_v2_data = {
+ .pmu_offset = 0x1004,
+ .spare_offset = 0x1088,
+
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddmx", 1287500, 1287500, 0 },
+ { "vddcx", .super_turbo = true },
+ { "vddpx", 1800000, 1800000, 0 },
+ },
+ .num_vregs = 3,
+};
+
+void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
+ struct qcom_iris *iris,
+ bool use_48mhz_xo)
+{
+ mutex_lock(&wcnss->iris_lock);
+
+ wcnss->iris = iris;
+ wcnss->use_48mhz_xo = use_48mhz_xo;
+
+ mutex_unlock(&wcnss->iris_lock);
+}
+
+static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
+{
+ struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
+ phys_addr_t fw_addr;
+ size_t fw_size;
+ bool relocate;
+ int ret;
+
+ ret = qcom_scm_pas_init_image(WCNSS_PAS_ID, fw->data, fw->size);
+ if (ret) {
+ dev_err(&rproc->dev, "invalid firmware metadata\n");
+ return -EINVAL;
+ }
+
+ ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate);
+ if (ret) {
+ dev_err(&rproc->dev, "failed to parse mdt header\n");
+ return ret;
+ }
+
+ if (relocate) {
+ wcnss->mem_reloc = fw_addr;
+
+ ret = qcom_scm_pas_mem_setup(WCNSS_PAS_ID, wcnss->mem_phys, fw_size);
+ if (ret) {
+ dev_err(&rproc->dev, "unable to setup memory for image\n");
+ return -EINVAL;
+ }
+ }
+
+ return qcom_mdt_load(rproc, fw, rproc->firmware);
+}
+
+static const struct rproc_fw_ops wcnss_fw_ops = {
+ .find_rsc_table = qcom_mdt_find_rsc_table,
+ .load = wcnss_load,
+};
+
+static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss)
+{
+ u32 val;
+
+ /* Indicate NV download capability */
+ val = readl(wcnss->spare_out);
+ val |= WCNSS_SPARE_NVBIN_DLND;
+ writel(val, wcnss->spare_out);
+}
+
+static void wcnss_configure_iris(struct qcom_wcnss *wcnss)
+{
+ u32 val;
+
+ /* Clear PMU cfg register */
+ writel(0, wcnss->pmu_cfg);
+
+ val = WCNSS_PMU_GC_BUS_MUX_SEL_TOP | WCNSS_PMU_IRIS_XO_EN;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Clear XO_MODE */
+ val &= ~WCNSS_PMU_XO_MODE_MASK;
+ if (wcnss->use_48mhz_xo)
+ val |= WCNSS_PMU_XO_MODE_48 << 1;
+ else
+ val |= WCNSS_PMU_XO_MODE_19p2 << 1;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Reset IRIS */
+ val |= WCNSS_PMU_IRIS_RESET;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Wait for PMU.iris_reg_reset_sts */
+ while (readl(wcnss->pmu_cfg) & WCNSS_PMU_IRIS_RESET_STS)
+ cpu_relax();
+
+ /* Clear IRIS reset */
+ val &= ~WCNSS_PMU_IRIS_RESET;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Start IRIS XO configuration */
+ val |= WCNSS_PMU_IRIS_XO_CFG;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Wait for XO configuration to finish */
+ while (readl(wcnss->pmu_cfg) & WCNSS_PMU_IRIS_XO_CFG_STS)
+ cpu_relax();
+
+ /* Stop IRIS XO configuration */
+ val &= ~WCNSS_PMU_GC_BUS_MUX_SEL_TOP;
+ val &= ~WCNSS_PMU_IRIS_XO_CFG;
+ writel(val, wcnss->pmu_cfg);
+
+ /* Add some delay for XO to settle */
+ msleep(20);
+}
+
+static int wcnss_start(struct rproc *rproc)
+{
+ struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
+ int ret;
+
+ mutex_lock(&wcnss->iris_lock);
+ if (!wcnss->iris) {
+ dev_err(wcnss->dev, "no iris registered\n");
+ ret = -EINVAL;
+ goto release_iris_lock;
+ }
+
+ ret = regulator_bulk_enable(wcnss->num_vregs, wcnss->vregs);
+ if (ret)
+ goto release_iris_lock;
+
+ ret = qcom_iris_enable(wcnss->iris);
+ if (ret)
+ goto disable_regulators;
+
+ wcnss_indicate_nv_download(wcnss);
+ wcnss_configure_iris(wcnss);
+
+ ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID);
+ if (ret) {
+ dev_err(wcnss->dev,
+ "failed to authenticate image and release reset\n");
+ goto disable_iris;
+ }
+
+ ret = wait_for_completion_timeout(&wcnss->start_done,
+ msecs_to_jiffies(5000));
+ if (wcnss->ready_irq > 0 && ret == 0) {
+ /* We have a ready_irq, but it didn't fire in time. */
+ dev_err(wcnss->dev, "start timed out\n");
+ qcom_scm_pas_shutdown(WCNSS_PAS_ID);
+ ret = -ETIMEDOUT;
+ goto disable_iris;
+ }
+
+ ret = 0;
+
+disable_iris:
+ qcom_iris_disable(wcnss->iris);
+disable_regulators:
+ regulator_bulk_disable(wcnss->num_vregs, wcnss->vregs);
+release_iris_lock:
+ mutex_unlock(&wcnss->iris_lock);
+
+ return ret;
+}
+
+static int wcnss_stop(struct rproc *rproc)
+{
+ struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
+ int ret;
+
+ if (wcnss->state) {
+ qcom_smem_state_update_bits(wcnss->state,
+ BIT(wcnss->stop_bit),
+ BIT(wcnss->stop_bit));
+
+ ret = wait_for_completion_timeout(&wcnss->stop_done,
+ msecs_to_jiffies(5000));
+ if (ret == 0)
+ dev_err(wcnss->dev, "timed out on wait\n");
+
+ qcom_smem_state_update_bits(wcnss->state,
+ BIT(wcnss->stop_bit),
+ 0);
+ }
+
+ ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID);
+ if (ret)
+ dev_err(wcnss->dev, "failed to shutdown: %d\n", ret);
+
+ return ret;
+}
+
+static void *wcnss_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+ struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
+ int offset;
+
+ offset = da - wcnss->mem_reloc;
+ if (offset < 0 || offset + len > wcnss->mem_size)
+ return NULL;
+
+ return wcnss->mem_region + offset;
+}
+
+static const struct rproc_ops wcnss_ops = {
+ .start = wcnss_start,
+ .stop = wcnss_stop,
+ .da_to_va = wcnss_da_to_va,
+};
+
+static irqreturn_t wcnss_wdog_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+
+ rproc_report_crash(wcnss->rproc, RPROC_WATCHDOG);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_fatal_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+ size_t len;
+ char *msg;
+
+ msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, WCNSS_CRASH_REASON_SMEM, &len);
+ if (!IS_ERR(msg) && len > 0 && msg[0])
+ dev_err(wcnss->dev, "fatal error received: %s\n", msg);
+
+ rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR);
+
+ if (!IS_ERR(msg))
+ msg[0] = '\0';
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_ready_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+
+ complete(&wcnss->start_done);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_handover_interrupt(int irq, void *dev)
+{
+ /*
+ * XXX: At this point we're supposed to release the resources that we
+ * have been holding on behalf of the WCNSS. Unfortunately this
+ * interrupt comes way before the other side seems to be done.
+ *
+ * So we're currently relying on the ready interrupt firing later then
+ * this and we just disable the resources at the end of wcnss_start().
+ */
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev)
+{
+ struct qcom_wcnss *wcnss = dev;
+
+ complete(&wcnss->stop_done);
+ return IRQ_HANDLED;
+}
+
+static int wcnss_init_regulators(struct qcom_wcnss *wcnss,
+ const struct wcnss_vreg_info *info,
+ int num_vregs)
+{
+ struct regulator_bulk_data *bulk;
+ int ret;
+ int i;
+
+ bulk = devm_kcalloc(wcnss->dev,
+ num_vregs, sizeof(struct regulator_bulk_data),
+ GFP_KERNEL);
+ if (!bulk)
+ return -ENOMEM;
+
+ for (i = 0; i < num_vregs; i++)
+ bulk[i].supply = info[i].name;
+
+ ret = devm_regulator_bulk_get(wcnss->dev, num_vregs, bulk);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_vregs; i++) {
+ if (info[i].max_voltage)
+ regulator_set_voltage(bulk[i].consumer,
+ info[i].min_voltage,
+ info[i].max_voltage);
+
+ if (info[i].load_uA)
+ regulator_set_load(bulk[i].consumer, info[i].load_uA);
+ }
+
+ wcnss->vregs = bulk;
+ wcnss->num_vregs = num_vregs;
+
+ return 0;
+}
+
+static int wcnss_request_irq(struct qcom_wcnss *wcnss,
+ struct platform_device *pdev,
+ const char *name,
+ bool optional,
+ irq_handler_t thread_fn)
+{
+ int ret;
+
+ ret = platform_get_irq_byname(pdev, name);
+ if (ret < 0 && optional) {
+ dev_dbg(&pdev->dev, "no %s IRQ defined, ignoring\n", name);
+ return 0;
+ } else if (ret < 0) {
+ dev_err(&pdev->dev, "no %s IRQ defined\n", name);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, ret,
+ NULL, thread_fn,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "wcnss", wcnss);
+ if (ret)
+ dev_err(&pdev->dev, "request %s IRQ failed\n", name);
+ return ret;
+}
+
+static int wcnss_alloc_memory_region(struct qcom_wcnss *wcnss)
+{
+ struct device_node *node;
+ struct resource r;
+ int ret;
+
+ node = of_parse_phandle(wcnss->dev->of_node, "memory-region", 0);
+ if (!node) {
+ dev_err(wcnss->dev, "no memory-region specified\n");
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret)
+ return ret;
+
+ wcnss->mem_phys = wcnss->mem_reloc = r.start;
+ wcnss->mem_size = resource_size(&r);
+ wcnss->mem_region = devm_ioremap_wc(wcnss->dev, wcnss->mem_phys, wcnss->mem_size);
+ if (!wcnss->mem_region) {
+ dev_err(wcnss->dev, "unable to map memory region: %pa+%zx\n",
+ &r.start, wcnss->mem_size);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int wcnss_probe(struct platform_device *pdev)
+{
+ const struct wcnss_data *data;
+ struct qcom_wcnss *wcnss;
+ struct resource *res;
+ struct rproc *rproc;
+ void __iomem *mmio;
+ int ret;
+
+ data = of_device_get_match_data(&pdev->dev);
+
+ if (!qcom_scm_is_available())
+ return -EPROBE_DEFER;
+
+ if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) {
+ dev_err(&pdev->dev, "PAS is not available for WCNSS\n");
+ return -ENXIO;
+ }
+
+ rproc = rproc_alloc(&pdev->dev, pdev->name, &wcnss_ops,
+ WCNSS_FIRMWARE_NAME, sizeof(*wcnss));
+ if (!rproc) {
+ dev_err(&pdev->dev, "unable to allocate remoteproc\n");
+ return -ENOMEM;
+ }
+
+ rproc->fw_ops = &wcnss_fw_ops;
+
+ wcnss = (struct qcom_wcnss *)rproc->priv;
+ wcnss->dev = &pdev->dev;
+ wcnss->rproc = rproc;
+ platform_set_drvdata(pdev, wcnss);
+
+ init_completion(&wcnss->start_done);
+ init_completion(&wcnss->stop_done);
+
+ mutex_init(&wcnss->iris_lock);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu");
+ mmio = devm_ioremap_resource(&pdev->dev, res);
+ if (!mmio) {
+ ret = -ENOMEM;
+ goto free_rproc;
+ };
+
+ ret = wcnss_alloc_memory_region(wcnss);
+ if (ret)
+ goto free_rproc;
+
+ wcnss->pmu_cfg = mmio + data->pmu_offset;
+ wcnss->spare_out = mmio + data->spare_offset;
+
+ ret = wcnss_init_regulators(wcnss, data->vregs, data->num_vregs);
+ if (ret)
+ goto free_rproc;
+
+ ret = wcnss_request_irq(wcnss, pdev, "wdog", false, wcnss_wdog_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->wdog_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "fatal", false, wcnss_fatal_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->fatal_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "ready", true, wcnss_ready_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->ready_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "handover", true, wcnss_handover_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->handover_irq = ret;
+
+ ret = wcnss_request_irq(wcnss, pdev, "stop-ack", true, wcnss_stop_ack_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+ wcnss->stop_ack_irq = ret;
+
+ if (wcnss->stop_ack_irq) {
+ wcnss->state = qcom_smem_state_get(&pdev->dev, "stop",
+ &wcnss->stop_bit);
+ if (IS_ERR(wcnss->state)) {
+ ret = PTR_ERR(wcnss->state);
+ goto free_rproc;
+ }
+ }
+
+ ret = rproc_add(rproc);
+ if (ret)
+ goto free_rproc;
+
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+free_rproc:
+ rproc_put(rproc);
+
+ return ret;
+}
+
+static int wcnss_remove(struct platform_device *pdev)
+{
+ struct qcom_wcnss *wcnss = platform_get_drvdata(pdev);
+
+ of_platform_depopulate(&pdev->dev);
+
+ qcom_smem_state_put(wcnss->state);
+ rproc_del(wcnss->rproc);
+ rproc_put(wcnss->rproc);
+
+ return 0;
+}
+
+static const struct of_device_id wcnss_of_match[] = {
+ { .compatible = "qcom,riva-pil", &riva_data },
+ { .compatible = "qcom,pronto-v1-pil", &pronto_v1_data },
+ { .compatible = "qcom,pronto-v2-pil", &pronto_v2_data },
+ { },
+};
+
+static struct platform_driver wcnss_driver = {
+ .probe = wcnss_probe,
+ .remove = wcnss_remove,
+ .driver = {
+ .name = "qcom-wcnss-pil",
+ .of_match_table = wcnss_of_match,
+ },
+};
+
+module_platform_driver(wcnss_driver);
--- /dev/null
+#ifndef __QCOM_WNCSS_H__
+#define __QCOM_WNCSS_H__
+
+struct qcom_iris;
+struct qcom_wcnss;
+
+struct wcnss_vreg_info {
+ const char * const name;
+ int min_voltage;
+ int max_voltage;
+
+ int load_uA;
+
+ bool super_turbo;
+};
+
+int qcom_iris_enable(struct qcom_iris *iris);
+void qcom_iris_disable(struct qcom_iris *iris);
+
+void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, struct qcom_iris *iris, bool use_48mhz_xo);
+
+#endif
--- /dev/null
+/*
+ * Qualcomm Peripheral Image Loader
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Copyright (C) 2014 Sony Mobile Communications AB
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as 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/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom_scm.h>
+#include <linux/regulator/consumer.h>
+
+#include "qcom_wcnss.h"
+
+struct qcom_iris {
+ struct device *dev;
+
+ struct clk *xo_clk;
+
+ struct regulator_bulk_data *vregs;
+ size_t num_vregs;
+};
+
+struct iris_data {
+ const struct wcnss_vreg_info *vregs;
+ size_t num_vregs;
+
+ bool use_48mhz_xo;
+};
+
+static const struct iris_data wcn3620_data = {
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddxo", 1800000, 1800000, 10000 },
+ { "vddrfa", 1300000, 1300000, 100000 },
+ { "vddpa", 3300000, 3300000, 515000 },
+ { "vdddig", 1800000, 1800000, 10000 },
+ },
+ .num_vregs = 4,
+ .use_48mhz_xo = false,
+};
+
+static const struct iris_data wcn3660_data = {
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddxo", 1800000, 1800000, 10000 },
+ { "vddrfa", 1300000, 1300000, 100000 },
+ { "vddpa", 2900000, 3000000, 515000 },
+ { "vdddig", 1200000, 1225000, 10000 },
+ },
+ .num_vregs = 4,
+ .use_48mhz_xo = true,
+};
+
+static const struct iris_data wcn3680_data = {
+ .vregs = (struct wcnss_vreg_info[]) {
+ { "vddxo", 1800000, 1800000, 10000 },
+ { "vddrfa", 1300000, 1300000, 100000 },
+ { "vddpa", 3300000, 3300000, 515000 },
+ { "vdddig", 1800000, 1800000, 10000 },
+ },
+ .num_vregs = 4,
+ .use_48mhz_xo = true,
+};
+
+int qcom_iris_enable(struct qcom_iris *iris)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(iris->num_vregs, iris->vregs);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(iris->xo_clk);
+ if (ret) {
+ dev_err(iris->dev, "failed to enable xo clk\n");
+ goto disable_regulators;
+ }
+
+ return 0;
+
+disable_regulators:
+ regulator_bulk_disable(iris->num_vregs, iris->vregs);
+
+ return ret;
+}
+
+void qcom_iris_disable(struct qcom_iris *iris)
+{
+ clk_disable_unprepare(iris->xo_clk);
+ regulator_bulk_disable(iris->num_vregs, iris->vregs);
+}
+
+static int qcom_iris_probe(struct platform_device *pdev)
+{
+ const struct iris_data *data;
+ struct qcom_wcnss *wcnss;
+ struct qcom_iris *iris;
+ int ret;
+ int i;
+
+ iris = devm_kzalloc(&pdev->dev, sizeof(struct qcom_iris), GFP_KERNEL);
+ if (!iris)
+ return -ENOMEM;
+
+ data = of_device_get_match_data(&pdev->dev);
+ wcnss = dev_get_drvdata(pdev->dev.parent);
+
+ iris->xo_clk = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(iris->xo_clk)) {
+ if (PTR_ERR(iris->xo_clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to acquire xo clk\n");
+ return PTR_ERR(iris->xo_clk);
+ }
+
+ iris->num_vregs = data->num_vregs;
+ iris->vregs = devm_kcalloc(&pdev->dev,
+ iris->num_vregs,
+ sizeof(struct regulator_bulk_data),
+ GFP_KERNEL);
+ if (!iris->vregs)
+ return -ENOMEM;
+
+ for (i = 0; i < iris->num_vregs; i++)
+ iris->vregs[i].supply = data->vregs[i].name;
+
+ ret = devm_regulator_bulk_get(&pdev->dev, iris->num_vregs, iris->vregs);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get regulators\n");
+ return ret;
+ }
+
+ for (i = 0; i < iris->num_vregs; i++) {
+ if (data->vregs[i].max_voltage)
+ regulator_set_voltage(iris->vregs[i].consumer,
+ data->vregs[i].min_voltage,
+ data->vregs[i].max_voltage);
+
+ if (data->vregs[i].load_uA)
+ regulator_set_load(iris->vregs[i].consumer,
+ data->vregs[i].load_uA);
+ }
+
+ qcom_wcnss_assign_iris(wcnss, iris, data->use_48mhz_xo);
+
+ return 0;
+}
+
+static int qcom_iris_remove(struct platform_device *pdev)
+{
+ struct qcom_wcnss *wcnss = dev_get_drvdata(pdev->dev.parent);
+
+ qcom_wcnss_assign_iris(wcnss, NULL, false);
+
+ return 0;
+}
+
+static const struct of_device_id iris_of_match[] = {
+ { .compatible = "qcom,wcn3620", .data = &wcn3620_data },
+ { .compatible = "qcom,wcn3660", .data = &wcn3660_data },
+ { .compatible = "qcom,wcn3680", .data = &wcn3680_data },
+ {}
+};
+
+static struct platform_driver wcnss_driver = {
+ .probe = qcom_iris_probe,
+ .remove = qcom_iris_remove,
+ .driver = {
+ .name = "qcom-iris",
+ .of_match_table = iris_of_match,
+ },
+};
+
+module_platform_driver(wcnss_driver);
return simple_read_from_buffer(userbuf, count, ppos, buf, i);
}
+static ssize_t rproc_state_write(struct file *filp, const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct rproc *rproc = filp->private_data;
+ char buf[10];
+ int ret;
+
+ if (count > sizeof(buf) || count <= 0)
+ return -EINVAL;
+
+ ret = copy_from_user(buf, userbuf, count);
+ if (ret)
+ return -EFAULT;
+
+ if (buf[count - 1] == '\n')
+ buf[count - 1] = '\0';
+
+ if (!strncmp(buf, "start", count)) {
+ ret = rproc_boot(rproc);
+ if (ret) {
+ dev_err(&rproc->dev, "Boot failed: %d\n", ret);
+ return ret;
+ }
+ } else if (!strncmp(buf, "stop", count)) {
+ rproc_shutdown(rproc);
+ } else {
+ dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
static const struct file_operations rproc_state_ops = {
.read = rproc_state_read,
+ .write = rproc_state_write,
.open = simple_open,
.llseek = generic_file_llseek,
};
directionality of connections by explicitly listing device connections
thus avoiding illegal routes.
-config QCOM_IPCRTR_STUB
- tristate "Qualcomm IPCRTR stub driver"
- depends on QCOM_SMD
- help
- Stub driver to bring the IPCRTR channel up.
obj-$(CONFIG_MSM_BUS_SCALING) += msm_bus/
obj-$(CONFIG_BUS_TOPOLOGY_ADHOC) += msm_bus/
-obj-$(CONFIG_QCOM_IPCRTR_STUB) += ipcrtr_stub.o
-
obj-$(CONFIG_QTI_LNX_GPS_PROXY) += gps_proxy.o
+++ /dev/null
-/*
- * Copyright (c) 2014, Sony Mobile Communications AB.
- *
- * This 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/platform_device.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/firmware.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-
-#include <linux/soc/qcom/smd.h>
-
-static int ipcrtr_stub_callback(struct qcom_smd_device *qsdev,
- const void *data,
- size_t count)
-{
- print_hex_dump(KERN_DEBUG, "IPCRTR <<<: ", DUMP_PREFIX_OFFSET, 16, 1, data, count, true);
-
- return 0;
-}
-
-static int ipcrtr_stub_probe(struct qcom_smd_device *sdev)
-{
- dev_err(&sdev->dev, "ipcrtr initialized\n");
-
- return 0;
-}
-
-static const struct of_device_id ipcrtr_stub_of_match[] = {
- { .compatible = "qcom,ipcrtr" },
- {}
-};
-MODULE_DEVICE_TABLE(of, ipcrtr_stub_of_match);
-
-static struct qcom_smd_driver ipcrtr_stub_driver = {
- .probe = ipcrtr_stub_probe,
- .callback = ipcrtr_stub_callback,
- .driver = {
- .name = "ipcrtr_stub",
- .owner = THIS_MODULE,
- .of_match_table = ipcrtr_stub_of_match,
- },
-};
-
-module_qcom_smd_driver(ipcrtr_stub_driver);
-
-MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
-MODULE_DESCRIPTION("Qualcomm IPCRTR stub driver");
-MODULE_LICENSE("GPLv2");
-
*/
struct qcom_smd_rpm {
struct qcom_smd_channel *rpm_channel;
+ struct device *dev;
struct completion ack;
struct mutex lock;
}
EXPORT_SYMBOL(qcom_rpm_smd_write);
-static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev,
+static int qcom_smd_rpm_callback(struct qcom_smd_channel *channel,
const void *data,
size_t count)
{
const struct qcom_rpm_header *hdr = data;
size_t hdr_length = le32_to_cpu(hdr->length);
const struct qcom_rpm_message *msg;
- struct qcom_smd_rpm *rpm = dev_get_drvdata(&qsdev->dev);
+ struct qcom_smd_rpm *rpm = qcom_smd_get_drvdata(channel);
const u8 *buf = data + sizeof(struct qcom_rpm_header);
const u8 *end = buf + hdr_length;
char msgbuf[32];
if (le32_to_cpu(hdr->service_type) != RPM_SERVICE_TYPE_REQUEST ||
hdr_length < sizeof(struct qcom_rpm_message)) {
- dev_err(&qsdev->dev, "invalid request\n");
+ dev_err(rpm->dev, "invalid request\n");
return 0;
}
mutex_init(&rpm->lock);
init_completion(&rpm->ack);
+ rpm->dev = &sdev->dev;
rpm->rpm_channel = sdev->channel;
+ qcom_smd_set_drvdata(sdev->channel, rpm);
dev_set_drvdata(&sdev->dev, rpm);
int ipc_bit;
struct list_head channels;
- rwlock_t channels_lock;
+ spinlock_t channels_lock;
DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE);
struct work_struct state_work;
};
+/*
+ * SMD channel states.
+ */
+enum smd_channel_state {
+ SMD_CHANNEL_CLOSED,
+ SMD_CHANNEL_OPENING,
+ SMD_CHANNEL_OPENED,
+ SMD_CHANNEL_FLUSHING,
+ SMD_CHANNEL_CLOSING,
+ SMD_CHANNEL_RESET,
+ SMD_CHANNEL_RESET_OPENING
+};
+
+/**
+ * struct qcom_smd_channel - smd channel struct
+ * @edge: qcom_smd_edge this channel is living on
+ * @qsdev: reference to a associated smd client device
+ * @name: name of the channel
+ * @state: local state of the channel
+ * @remote_state: remote state of the channel
+ * @info: byte aligned outgoing/incoming channel info
+ * @info_word: word aligned outgoing/incoming channel info
+ * @tx_lock: lock to make writes to the channel mutually exclusive
+ * @fblockread_event: wakeup event tied to tx fBLOCKREADINTR
+ * @tx_fifo: pointer to the outgoing ring buffer
+ * @rx_fifo: pointer to the incoming ring buffer
+ * @fifo_size: size of each ring buffer
+ * @bounce_buffer: bounce buffer for reading wrapped packets
+ * @cb: callback function registered for this channel
+ * @recv_lock: guard for rx info modifications and cb pointer
+ * @pkt_size: size of the currently handled packet
+ * @list: lite entry for @channels in qcom_smd_edge
+ */
+struct qcom_smd_channel {
+ struct qcom_smd_edge *edge;
+
+ struct qcom_smd_device *qsdev;
+
+ char *name;
+ enum smd_channel_state state;
+ enum smd_channel_state remote_state;
+
+ struct smd_channel_info_pair *info;
+ struct smd_channel_info_word_pair *info_word;
+
+ struct mutex tx_lock;
+ wait_queue_head_t fblockread_event;
+
+ void *tx_fifo;
+ void *rx_fifo;
+ int fifo_size;
+
+ void *bounce_buffer;
+ qcom_smd_cb_t cb;
+
+ spinlock_t recv_lock;
+
+ int pkt_size;
+
+ void *drvdata;
+
+ struct list_head list;
+ struct list_head dev_list;
+};
/**
* struct qcom_smd - smd struct
*/
static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel)
{
- struct qcom_smd_device *qsdev = channel->qsdev;
unsigned tail;
size_t len;
void *ptr;
len = channel->pkt_size;
}
- ret = channel->cb(qsdev, ptr, len);
+ ret = channel->cb(channel, ptr, len);
if (ret < 0)
return ret;
struct qcom_smd_edge *edge = data;
struct qcom_smd_channel *channel;
unsigned available;
- bool kick_worker = false;
+ bool kick_scanner = false;
+ bool kick_state = false;
/*
* Handle state changes or data on each of the channels on this edge
*/
- read_lock(&edge->channels_lock);
+ spin_lock(&edge->channels_lock);
list_for_each_entry(channel, &edge->channels, list) {
spin_lock(&channel->recv_lock);
- kick_worker |= qcom_smd_channel_intr(channel);
+ kick_state |= qcom_smd_channel_intr(channel);
spin_unlock(&channel->recv_lock);
}
- read_unlock(&edge->channels_lock);
+ spin_unlock(&edge->channels_lock);
/*
* Creating a new channel requires allocating an smem entry, so we only
available = qcom_smem_get_free_space(edge->remote_pid);
if (available != edge->smem_available) {
edge->smem_available = available;
- kick_worker = true;
+ kick_scanner = true;
}
- if (kick_worker)
+ if (kick_scanner)
schedule_work(&edge->scan_work);
+ if (kick_state)
+ schedule_work(&edge->state_work);
return IRQ_HANDLED;
}
{
__le32 hdr[5] = { cpu_to_le32(len), };
int tlen = sizeof(hdr) + len;
- int ret, length;
+ int ret;
/* Word aligned channels only accept word size aligned data */
if (channel->info_word && len % 4)
if (tlen >= channel->fifo_size)
return -EINVAL;
- length = qcom_smd_get_tx_avail(channel);
-
ret = mutex_lock_interruptible(&channel->tx_lock);
if (ret)
return ret;
- length = qcom_smd_get_tx_avail(channel);
while (qcom_smd_get_tx_avail(channel) < tlen) {
if (channel->state != SMD_CHANNEL_OPENED) {
ret = -EPIPE;
SET_TX_CHANNEL_FLAG(channel, fTAIL, 0);
- length = qcom_smd_get_tx_avail(channel);
qcom_smd_write_fifo(channel, hdr, sizeof(hdr));
qcom_smd_write_fifo(channel, data, len);
}
EXPORT_SYMBOL(qcom_smd_driver_register);
+void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)
+{
+ return channel->drvdata;
+}
+EXPORT_SYMBOL(qcom_smd_get_drvdata);
+
+void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)
+{
+ channel->drvdata = data;
+}
+EXPORT_SYMBOL(qcom_smd_set_drvdata);
+
/**
* qcom_smd_driver_unregister - unregister a smd driver
* @qsdrv: qcom_smd_driver struct
{
struct qcom_smd_channel *channel;
struct qcom_smd_channel *ret = NULL;
+ unsigned long flags;
unsigned state;
- read_lock(&edge->channels_lock);
+ spin_lock_irqsave(&edge->channels_lock, flags);
list_for_each_entry(channel, &edge->channels, list) {
if (strcmp(channel->name, name))
continue;
ret = channel;
break;
}
- read_unlock(&edge->channels_lock);
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
return ret;
}
* Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't
* ready.
*/
-struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev,
+struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent,
const char *name,
qcom_smd_cb_t cb)
{
struct qcom_smd_channel *channel;
- struct qcom_smd_edge *edge = sdev->channel->edge;
+ struct qcom_smd_device *sdev = parent->qsdev;
+ struct qcom_smd_edge *edge = parent->edge;
int ret;
/* Wait up to HZ for the channel to appear */
/* The channel consist of a rx and tx fifo of equal size */
fifo_size /= 2;
- dev_err(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
+ dev_dbg(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
name, info_size, fifo_size);
channel->tx_fifo = fifo_base;
if (IS_ERR(channel))
continue;
- write_lock_irqsave(&edge->channels_lock, flags);
+ spin_lock_irqsave(&edge->channels_lock, flags);
list_add(&channel->list, &edge->channels);
- write_unlock_irqrestore(&edge->channels_lock, flags);
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name);
set_bit(i, edge->allocated[tbl]);
struct qcom_smd_edge,
state_work);
unsigned remote_state;
+ unsigned long flags;
/*
* Register a device for any closed channel where the remote processor
* is showing interest in opening the channel.
*/
- read_lock(&edge->channels_lock);
+ spin_lock_irqsave(&edge->channels_lock, flags);
list_for_each_entry(channel, &edge->channels, list) {
if (channel->state != SMD_CHANNEL_CLOSED)
continue;
remote_state != SMD_CHANNEL_OPENED)
continue;
- read_unlock(&edge->channels_lock);
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
qcom_smd_create_device(channel);
- read_lock(&edge->channels_lock);
+ spin_lock_irqsave(&edge->channels_lock, flags);
}
/*
remote_state == SMD_CHANNEL_OPENED)
continue;
- read_unlock(&edge->channels_lock);
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
qcom_smd_destroy_device(channel);
- read_lock(&edge->channels_lock);
+ spin_lock_irqsave(&edge->channels_lock, flags);
}
- read_unlock(&edge->channels_lock);
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
}
/*
int ret;
INIT_LIST_HEAD(&edge->channels);
- rwlock_init(&edge->channels_lock);
+ spin_lock_init(&edge->channels_lock);
INIT_WORK(&edge->scan_work, qcom_channel_scan_worker);
INIT_WORK(&edge->state_work, qcom_channel_state_worker);
smem->regions[i].aux_base = (u32)r.start;
smem->regions[i].size = resource_size(&r);
- smem->regions[i].virt_base = devm_ioremap_nocache(dev, r.start,
- resource_size(&r));
+ smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r));
if (!smem->regions[i].virt_base)
return -ENOMEM;
/*
+ * Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
+#define WCNSS_CBC_TIMEOUT (10 * HZ)
+
+#define WCNSS_ACK_DONE_BOOTING 1
+#define WCNSS_ACK_COLD_BOOTING 2
#define NV_FRAGMENT_SIZE 3072
#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
* @dev: device handle
* @channel: SMD channel handle
* @ack: completion for outstanding requests
+ * @cbc: completion for cbc complete indication
* @ack_status: status of the outstanding request
- * @download_nv_work: worker for uploading nv binary
+ * @probe_work: worker for uploading nv binary
*/
struct wcnss_ctrl {
struct device *dev;
struct qcom_smd_channel *channel;
struct completion ack;
+ struct completion cbc;
int ack_status;
- struct work_struct download_nv_work;
+ struct work_struct probe_work;
};
/* message types */
WCNSS_UPLOAD_CAL_RESP,
WCNSS_DOWNLOAD_CAL_REQ,
WCNSS_DOWNLOAD_CAL_RESP,
+ WCNSS_VBAT_LEVEL_IND,
+ WCNSS_BUILD_VERSION_REQ,
+ WCNSS_BUILD_VERSION_RESP,
+ WCNSS_PM_CONFIG_REQ,
+ WCNSS_CBC_COMPLETE_IND,
};
/**
/**
* wcnss_ctrl_smd_callback() - handler from SMD responses
- * @qsdev: smd device handle
+ * @channel: smd channel handle
* @data: pointer to the incoming data packet
* @count: size of the incoming data packet
*
* Handles any incoming packets from the remote WCNSS_CTRL service.
*/
-static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev,
+static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
const void *data,
size_t count)
{
- struct wcnss_ctrl *wcnss = dev_get_drvdata(&qsdev->dev);
+ struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(channel);
const struct wcnss_download_nv_resp *nvresp;
const struct wcnss_version_resp *version;
const struct wcnss_msg_hdr *hdr = data;
version->major, version->minor,
version->version, version->revision);
- schedule_work(&wcnss->download_nv_work);
+ complete(&wcnss->ack);
break;
case WCNSS_DOWNLOAD_NV_RESP:
if (count != sizeof(*nvresp)) {
wcnss->ack_status = nvresp->status;
complete(&wcnss->ack);
break;
+ case WCNSS_CBC_COMPLETE_IND:
+ dev_dbg(wcnss->dev, "cold boot complete\n");
+ complete(&wcnss->cbc);
+ break;
default:
dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
break;
static int wcnss_request_version(struct wcnss_ctrl *wcnss)
{
struct wcnss_msg_hdr msg;
+ int ret;
msg.type = WCNSS_VERSION_REQ;
msg.len = sizeof(msg);
+ ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
+ if (!ret) {
+ dev_err(wcnss->dev, "timeout waiting for version response\n");
+ return -ETIMEDOUT;
+ }
- return qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
+ return 0;
}
/**
* wcnss_download_nv() - send nv binary to WCNSS
- * @work: work struct to acquire wcnss context
+ * @wcnss: wcnss_ctrl state handle
+ * @expect_cbc: indicator to caller that an cbc event is expected
+ *
+ * Returns 0 on success. Negative errno on failure.
*/
-static void wcnss_download_nv(struct work_struct *work)
+static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
{
- struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work);
struct wcnss_download_nv_req *req;
const struct firmware *fw;
const void *data;
req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
if (!req)
- return;
+ return -ENOMEM;
ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
- if (ret) {
+ if (ret < 0) {
dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
NVBIN_FILE, ret);
goto free_req;
memcpy(req->fragment, data, req->frag_size);
ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
- if (ret) {
+ if (ret < 0) {
dev_err(wcnss->dev, "failed to send smd packet\n");
goto release_fw;
}
} while (left > 0);
ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
- if (!ret)
+ if (!ret) {
dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
- else if (wcnss->ack_status != 1)
- dev_err(wcnss->dev, "nv upload response failed err: %d\n",
- wcnss->ack_status);
+ ret = -ETIMEDOUT;
+ } else {
+ *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;
+ ret = 0;
+ }
release_fw:
release_firmware(fw);
free_req:
kfree(req);
+
+ return ret;
+}
+
+int wcnss_ctrl_done_loading_nv;
+EXPORT_SYMBOL(wcnss_ctrl_done_loading_nv);
+
+/**
+ * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS
+ * @wcnss: wcnss handle, retrieved from drvdata
+ * @name: SMD channel name
+ * @cb: callback to handle incoming data on the channel
+ */
+struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb)
+{
+ struct wcnss_ctrl *_wcnss = wcnss;
+
+ return qcom_smd_open_channel(_wcnss->channel, name, cb);
+}
+EXPORT_SYMBOL(qcom_wcnss_open_channel);
+
+static void wcnss_async_probe(struct work_struct *work)
+{
+ struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
+ bool expect_cbc;
+ int ret;
+
+ ret = wcnss_request_version(wcnss);
+ if (ret < 0)
+ return;
+
+ ret = wcnss_download_nv(wcnss, &expect_cbc);
+ if (ret < 0)
+ return;
+
+ /* Wait for pending cold boot completion if indicated by the nv downloader */
+ if (expect_cbc) {
+ ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
+ if (!ret)
+ dev_err(wcnss->dev, "expected cold boot completion\n");
+ }
+
+ wcnss_ctrl_done_loading_nv = 1;
+
+ of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
}
static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
wcnss->channel = sdev->channel;
init_completion(&wcnss->ack);
- INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv);
+ init_completion(&wcnss->cbc);
+ INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
+ qcom_smd_set_drvdata(sdev->channel, wcnss);
dev_set_drvdata(&sdev->dev, wcnss);
- return wcnss_request_version(wcnss);
+ qcom_smd_set_drvdata(sdev->channel, wcnss);
+
+ schedule_work(&wcnss->probe_work);
+
+ return 0;
+}
+
+static void wcnss_ctrl_remove(struct qcom_smd_device *sdev)
+{
+ struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel);
+
+ cancel_work_sync(&wcnss->probe_work);
+ of_platform_depopulate(&sdev->dev);
}
-static const struct qcom_smd_id wcnss_ctrl_smd_match[] = {
- { .name = "WCNSS_CTRL" },
+static const struct of_device_id wcnss_ctrl_of_match[] = {
+ { .compatible = "qcom,wcnss", },
{}
};
static struct qcom_smd_driver wcnss_ctrl_driver = {
.probe = wcnss_ctrl_probe,
+ .remove = wcnss_ctrl_remove,
.callback = wcnss_ctrl_smd_callback,
- .smd_match_table = wcnss_ctrl_smd_match,
.driver = {
.name = "qcom_wcnss_ctrl",
.owner = THIS_MODULE,
+ .of_match_table = wcnss_ctrl_of_match,
},
};
#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H
#define _DT_BINDINGS_CLK_MSM_RPMCC_H
-/* msm8916 */
-#define RPM_XO_CLK_SRC 0
-#define RPM_XO_A_CLK_SRC 1
-#define RPM_PCNOC_CLK 2
-#define RPM_PCNOC_A_CLK 3
-#define RPM_SNOC_CLK 4
-#define RPM_SNOC_A_CLK 5
-#define RPM_BIMC_CLK 6
-#define RPM_BIMC_A_CLK 7
-#define RPM_QDSS_CLK 8
-#define RPM_QDSS_A_CLK 9
-#define RPM_BB_CLK1 10
-#define RPM_BB_CLK1_A 11
-#define RPM_BB_CLK2 12
-#define RPM_BB_CLK2_A 13
-#define RPM_RF_CLK1 14
-#define RPM_RF_CLK1_A 15
-#define RPM_RF_CLK2 16
-#define RPM_RF_CLK2_A 17
-#define RPM_BB_CLK1_PIN 18
-#define RPM_BB_CLK1_A_PIN 19
-#define RPM_BB_CLK2_PIN 20
-#define RPM_BB_CLK2_A_PIN 21
-#define RPM_RF_CLK1_PIN 22
-#define RPM_RF_CLK1_A_PIN 23
-#define RPM_RF_CLK2_PIN 24
-#define RPM_RF_CLK2_A_PIN 25
-
-/* msm8974 */
-#define RPM_CXO_CLK_SRC 0
-#define RPM_CXO_A_CLK_SRC 1
-#define RPM_PNOC_CLK 2
-#define RPM_PNOC_A_CLK 3
-#define RPM_SNOC_CLK 4
-#define RPM_SNOC_A_CLK 5
-#define RPM_BIMC_CLK 6
-#define RPM_BIMC_A_CLK 7
+/* apq8064 */
+#define RPM_PXO_CLK 0
+#define RPM_PXO_A_CLK 1
+#define RPM_CXO_CLK 2
+#define RPM_CXO_A_CLK 3
+#define RPM_APPS_FABRIC_CLK 4
+#define RPM_APPS_FABRIC_A_CLK 5
+#define RPM_CFPB_CLK 6
+#define RPM_CFPB_A_CLK 7
#define RPM_QDSS_CLK 8
#define RPM_QDSS_A_CLK 9
-#define RPM_CXO_D0 10
-#define RPM_CXO_D0_A 11
-#define RPM_CXO_D1 12
-#define RPM_CXO_D1_A 13
-#define RPM_CXO_A0 14
-#define RPM_CXO_A0_A 15
-#define RPM_CXO_A1 16
-#define RPM_CXO_A1_A 17
-#define RPM_CXO_A2 18
-#define RPM_CXO_A2_A 19
-#define RPM_CXO_D0_PIN 20
-#define RPM_CXO_D0_A_PIN 21
-#define RPM_CXO_A2_PIN 22
-#define RPM_CXO_A2_A_PIN 23
-#define RPM_CXO_A1_PIN 24
-#define RPM_CXO_A1_A_PIN 25
-#define RPM_DIFF_CLK 26
-#define RPM_DIFF_A_CLK 27
-#define RPM_CNOC_CLK 28
-#define RPM_CNOC_A_CLK 29
-#define RPM_MMSSNOC_AHB_CLK 30
-#define RPM_MMSSNOC_AHB_A_CLK 31
-#define RPM_OCMEMGX_CLK 32
-#define RPM_OCMEMGX_A_CLK 33
-#define RPM_GFX3D_CLK_SRC 34
-#define RPM_GFX3D_A_CLK_SRC 35
-#define RPM_DIV_CLK1 36
-#define RPM_DIV_A_CLK1 37
-#define RPM_DIV_CLK2 38
-#define RPM_DIV_A_CLK2 39
-#define RPM_CXO_D1_PIN 40
-#define RPM_CXO_D1_A_PIN 41
-#define RPM_CXO_A0_PIN 42
-#define RPM_CXO_A0_A_PIN 43
+#define RPM_DAYTONA_FABRIC_CLK 10
+#define RPM_DAYTONA_FABRIC_A_CLK 11
+#define RPM_EBI1_CLK 12
+#define RPM_EBI1_A_CLK 13
+#define RPM_MM_FABRIC_CLK 14
+#define RPM_MM_FABRIC_A_CLK 15
+#define RPM_MMFPB_CLK 16
+#define RPM_MMFPB_A_CLK 17
+#define RPM_SYS_FABRIC_CLK 18
+#define RPM_SYS_FABRIC_A_CLK 19
+#define RPM_SFPB_CLK 20
+#define RPM_SFPB_A_CLK 21
-/* apq8084 */
-#define RPM_XO_CLK_SRC 0
-#define RPM_XO_A_CLK_SRC 1
-#define RPM_PNOC_CLK 2
-#define RPM_PNOC_A_CLK 3
-#define RPM_SNOC_CLK 4
-#define RPM_SNOC_A_CLK 5
-#define RPM_BIMC_CLK 6
-#define RPM_BIMC_A_CLK 7
-#define RPM_QDSS_CLK 8
-#define RPM_QDSS_A_CLK 9
-#define RPM_BB_CLK1 10
-#define RPM_BB_CLK1_A 11
-#define RPM_BB_CLK2 12
-#define RPM_BB_CLK2_A 13
-#define RPM_RF_CLK1 14
-#define RPM_RF_CLK1_A 15
-#define RPM_RF_CLK2 16
-#define RPM_RF_CLK2_A 17
-#define RPM_BB_CLK1_PIN 18
-#define RPM_BB_CLK1_A_PIN 19
-#define RPM_BB_CLK2_PIN 20
-#define RPM_BB_CLK2_A_PIN 21
-#define RPM_RF_CLK1_PIN 22
-#define RPM_RF_CLK1_A_PIN 23
-#define RPM_RF_CLK2_PIN 24
-#define RPM_RF_CLK2_A_PIN 25
-#define RPM_DIFF_CLK1 26
-#define RPM_DIFF_CLK1_A 27
-#define RPM_CNOC_CLK 28
-#define RPM_CNOC_A_CLK 29
-#define RPM_MMSSNOC_AHB_CLK 30
-#define RPM_MMSSNOC_AHB_A_CLK 31
-#define RPM_OCMEMGX_CLK 32
-#define RPM_OCMEMGX_A_CLK 33
-#define RPM_GFX3D_CLK_SRC 34
-#define RPM_GFX3D_A_CLK_SRC 35
-#define RPM_DIV_CLK1 36
-#define RPM_DIV_CLK1_A 37
-#define RPM_DIV_CLK2 38
-#define RPM_DIV_CLK2_A 39
-#define RPM_DIV_CLK3 40
-#define RPM_DIV_CLK3_A 41
-#define RPM_RF_CLK3_PIN 44
-#define RPM_RF_CLK3_A_PIN 45
-#define RPM_RF_CLK3 46
-#define RPM_RF_CLK3_A 47
+/* msm8916 */
+#define RPM_SMD_XO_CLK_SRC 0
+#define RPM_SMD_XO_A_CLK_SRC 1
+#define RPM_SMD_PCNOC_CLK 2
+#define RPM_SMD_PCNOC_A_CLK 3
+#define RPM_SMD_SNOC_CLK 4
+#define RPM_SMD_SNOC_A_CLK 5
+#define RPM_SMD_BIMC_CLK 6
+#define RPM_SMD_BIMC_A_CLK 7
+#define RPM_SMD_QDSS_CLK 8
+#define RPM_SMD_QDSS_A_CLK 9
+#define RPM_SMD_BB_CLK1 10
+#define RPM_SMD_BB_CLK1_A 11
+#define RPM_SMD_BB_CLK2 12
+#define RPM_SMD_BB_CLK2_A 13
+#define RPM_SMD_RF_CLK1 14
+#define RPM_SMD_RF_CLK1_A 15
+#define RPM_SMD_RF_CLK2 16
+#define RPM_SMD_RF_CLK2_A 17
+#define RPM_SMD_BB_CLK1_PIN 18
+#define RPM_SMD_BB_CLK1_A_PIN 19
+#define RPM_SMD_BB_CLK2_PIN 20
+#define RPM_SMD_BB_CLK2_A_PIN 21
+#define RPM_SMD_RF_CLK1_PIN 22
+#define RPM_SMD_RF_CLK1_A_PIN 23
+#define RPM_SMD_RF_CLK2_PIN 24
+#define RPM_SMD_RF_CLK2_A_PIN 25
#endif
u32 *resp);
extern bool qcom_scm_pas_supported(u32 peripheral);
-extern int qcom_scm_restart_proc(u32 pid, int restart, u32 *resp);
-extern int qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size);
+extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size);
extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size);
extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
extern int qcom_scm_pas_shutdown(u32 peripheral);
#include <linux/mod_devicetable.h>
struct qcom_smd;
+struct qcom_smd_channel;
struct qcom_smd_lookup;
-struct qcom_smd_device;
-
-typedef int (*qcom_smd_cb_t)(struct qcom_smd_device *, const void *, size_t);
-
-/*
- * SMD channel states.
- */
-enum smd_channel_state {
- SMD_CHANNEL_CLOSED,
- SMD_CHANNEL_OPENING,
- SMD_CHANNEL_OPENED,
- SMD_CHANNEL_FLUSHING,
- SMD_CHANNEL_CLOSING,
- SMD_CHANNEL_RESET,
- SMD_CHANNEL_RESET_OPENING
-};
-
-/**
- * struct qcom_smd_channel - smd channel struct
- * @edge: qcom_smd_edge this channel is living on
- * @qsdev: reference to a associated smd client device
- * @name: name of the channel
- * @state: local state of the channel
- * @remote_state: remote state of the channel
- * @info: byte aligned outgoing/incoming channel info
- * @info_word: word aligned outgoing/incoming channel info
- * @tx_lock: lock to make writes to the channel mutually exclusive
- * @fblockread_event: wakeup event tied to tx fBLOCKREADINTR
- * @tx_fifo: pointer to the outgoing ring buffer
- * @rx_fifo: pointer to the incoming ring buffer
- * @fifo_size: size of each ring buffer
- * @bounce_buffer: bounce buffer for reading wrapped packets
- * @cb: callback function registered for this channel
- * @recv_lock: guard for rx info modifications and cb pointer
- * @pkt_size: size of the currently handled packet
- * @list: lite entry for @channels in qcom_smd_edge
- */
-
-struct qcom_smd_channel {
- struct qcom_smd_edge *edge;
-
- struct qcom_smd_device *qsdev;
-
- char *name;
- enum smd_channel_state state;
- enum smd_channel_state remote_state;
-
- struct smd_channel_info_pair *info;
- struct smd_channel_info_word_pair *info_word;
-
- struct mutex tx_lock;
- wait_queue_head_t fblockread_event;
-
- void *tx_fifo;
- void *rx_fifo;
- int fifo_size;
-
- void *bounce_buffer;
- qcom_smd_cb_t cb;
-
- spinlock_t recv_lock;
-
- int pkt_size;
-
- struct list_head list;
- struct list_head dev_list;
-};
/**
* struct qcom_smd_id - struct used for matching a smd device
struct qcom_smd_channel *channel;
};
+typedef int (*qcom_smd_cb_t)(struct qcom_smd_channel *, const void *, size_t);
+
/**
* struct qcom_smd_driver - smd driver struct
* @driver: underlying device driver
qcom_smd_cb_t callback;
};
+#if IS_ENABLED(CONFIG_QCOM_SMD)
+
int qcom_smd_driver_register(struct qcom_smd_driver *drv);
void qcom_smd_driver_unregister(struct qcom_smd_driver *drv);
+struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *channel,
+ const char *name,
+ qcom_smd_cb_t cb);
+void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel);
+void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data);
+int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
+
+
+#else
+
+static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv)
+{
+ return -ENXIO;
+}
+
+static inline void qcom_smd_driver_unregister(struct qcom_smd_driver *drv)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+}
+
+static inline struct qcom_smd_channel *
+qcom_smd_open_channel(struct qcom_smd_channel *channel,
+ const char *name,
+ qcom_smd_cb_t cb)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+ return NULL;
+}
+
+void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+ return NULL;
+}
+
+void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+}
+
+static inline int qcom_smd_send(struct qcom_smd_channel *channel,
+ const void *data, int len)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+ return -ENXIO;
+}
+
+#endif
+
#define module_qcom_smd_driver(__smd_driver) \
module_driver(__smd_driver, qcom_smd_driver_register, \
qcom_smd_driver_unregister)
-int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
-
-struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev,
- const char *name,
- qcom_smd_cb_t cb);
#endif
--- /dev/null
+#ifndef __WCNSS_CTRL_H__
+#define __WCNSS_CTRL_H__
+
+#include <linux/soc/qcom/smd.h>
+
+struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb);
+
+#endif
* @hw: the &struct ieee80211_hw to set the MAC address for
* @addr: the address to set
*/
-static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr)
+static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, const u8 *addr)
{
memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN);
}
struct qrtr_smd_dev {
struct qrtr_endpoint ep;
struct qcom_smd_channel *channel;
+ struct device *dev;
};
/* from smd to qrtr */
-static int qcom_smd_qrtr_callback(struct qcom_smd_device *sdev,
+static int qcom_smd_qrtr_callback(struct qcom_smd_channel *channel,
const void *data, size_t len)
{
- struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev);
+ struct qrtr_smd_dev *qdev = qcom_smd_get_drvdata(channel);
int rc;
if (!qdev)
rc = qrtr_endpoint_post(&qdev->ep, data, len);
if (rc == -EINVAL) {
- dev_err(&sdev->dev, "invalid ipcrouter packet\n");
+ dev_err(qdev->dev, "invalid ipcrouter packet\n");
/* return 0 to let smd drop the packet */
rc = 0;
}
return -ENOMEM;
qdev->channel = sdev->channel;
+ qdev->dev = &sdev->dev;
qdev->ep.xmit = qcom_smd_qrtr_send;
rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO);
if (rc)
return rc;
+ qcom_smd_set_drvdata(sdev->channel, qdev);
dev_set_drvdata(&sdev->dev, qdev);
dev_dbg(&sdev->dev, "Qualcomm SMD QRTR driver probed\n");
return NULL;
}
pr_info("apr_tal:Wakeup done\n");
- apr_svc_ch[dl][dest][svc].dest_state = 0;
- }
-
- rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait,
- (apr_svc_ch[dl][dest][svc].ch->state == SMD_CHANNEL_OPENED), 5 * HZ);
- if (rc == 0) {
- pr_err("apr_tal:TIMEOUT for OPEN event\n");
- apr_tal_close(&apr_svc_ch[dl][dest][svc]);
- return NULL;
- }
- if (!apr_svc_ch[dl][dest][svc].dest_state) {
- apr_svc_ch[dl][dest][svc].dest_state = 1;
- pr_info("apr_tal:Waiting for apr svc init\n");
- msleep(200);
- pr_info("apr_tal:apr svc init done\n");
}
apr_svc_ch[dl][dest][svc].func = func;
apr_svc_ch[dl][dest][svc].priv = priv;
+ pr_info("apr_tal:apr svc init done\n");
return &apr_svc_ch[dl][dest][svc];
}
}
-static int qcom_smd_q6_callback(struct qcom_smd_device *sdev,
+static int qcom_smd_q6_callback(struct qcom_smd_channel *channel,
const void *data,
size_t count)
{
- struct apr_svc_ch_dev *apr_ch = dev_get_drvdata(&sdev->dev);
+ struct apr_svc_ch_dev *apr_ch = qcom_smd_get_drvdata(channel);
- memcpy_fromio(apr_ch->data, data, count);
+ memcpy(apr_ch->data, data, count);
if (apr_ch->func)
apr_ch->func(apr_ch->data, count, apr_ch->priv);
static int qcom_smd_q6_probe(struct qcom_smd_device *sdev)
{
- int dest = APR_DEST_QDSP6;
- int clnt = APR_CLIENT_AUDIO;
-
- apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO].ch = sdev->channel;
+ struct apr_svc_ch_dev *apr = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO];
pr_info("apr_tal:Q6 Is Up\n");
- apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1;
- wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest);
- dev_set_drvdata(&sdev->dev, &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO]);
+ qcom_smd_set_drvdata(sdev->channel, apr);
+
+ apr->ch = sdev->channel;
+ apr->dest_state = 1;
+ wake_up(&apr->dest);
return 0;
}
static void qcom_smd_q6_remove(struct qcom_smd_device *sdev)
{
+ struct apr_svc_ch_dev *apr = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO];
+
+ apr->ch = NULL;
+ apr->dest_state = 0;
}