Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bbcadc95 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "icnss: Add snapshot of icnss driver"

parents 9cc62302 b4d21cce
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
* Qualcomm Technologies Inc Q6 Integrated connectivity Platform Driver

This platform driver adds support for the Integrated WLAN that runs
on Q6 based platforms. WLAN FW on these architecture runs on Q6. This
platform driver communicates with WLAN FW over QMI, WLAN on/off messages
to FW are communicated thru this interface. This driver also listens to
WLAN PD restart notifications.

Required properties:
  - compatible: "qcom,icnss"
  - reg: Memory regions defined as starting address and size
  - reg-names: Names of the memory regions defined in reg entry
  - interrupts: Copy engine interrupt table
  - qcom,wlan-msa-memory: MSA memory size
  - clocks: List of clock phandles
  - clock-names: List of clock names corresponding to the "clocks" property
  - iommus: SMMUs and corresponding Stream IDs needed by WLAN
  - qcom,wlan-smmu-iova-address: I/O virtual address range as <start length>
    format to be used for allocations associated between WLAN and SMMU

Optional properties:
  - <supply-name>-supply: phandle to the regulator device tree node
			   optional "supply-name" is "vdd-0.8-cx-mx".
  - qcom,<supply>-config: Specifies voltage levels for supply. Should be
			   specified in pairs (min, max), units uV.  There can
			   be optional load in uA and Regulator settle delay in
			   uS.
  - qcom,icnss-vadc: VADC handle for vph_pwr read APIs.
  - qcom,icnss-adc_tm: VADC handle for vph_pwr notification APIs.
  - io-channels: IIO channel to monitor for vph_pwr power.
  - io-channel-names: IIO channel name as per the client name.
  - qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass
  - qcom,wlan-msa-fixed-region: phandle, specifier pairs to children of /reserved-memory
  - qcom,hyp_disabled: Boolean context flag to disable hyperviser

WLAN SMP2P sub nodes

  - qcom,smp2p_map_wlan_1_in - represents the in smp2p to
				  wlan driver from modem.

Example:

    qcom,icnss@0a000000 {
        compatible = "qcom,icnss";
        reg = <0x0a000000 0x1000000>;
        reg-names = "membase";
        clocks = <&clock_gcc clk_aggre2_noc_clk>;
        clock-names = "smmu_aggre2_noc_clk";
        iommus = <&anoc2_smmu 0x1900>,
                 <&anoc2_smmu 0x1901>;
        qcom,wlan-smmu-iova-address = <0 0x10000000>;
        interrupts =
		   <0 130 0 /* CE0 */ >,
		   <0 131 0 /* CE1 */ >,
		   <0 132 0 /* CE2 */ >,
		   <0 133 0 /* CE3 */ >,
		   <0 134 0 /* CE4 */ >,
		   <0 135 0 /* CE5 */ >,
		   <0 136 0 /* CE6 */ >,
		   <0 137 0 /* CE7 */ >,
		   <0 138 0 /* CE8 */ >,
		   <0 139 0 /* CE9 */ >,
		   <0 140 0 /* CE10 */ >,
		   <0 141 0 /* CE11 */ >;
        qcom,wlan-msa-memory = <0x200000>;
	qcom,wlan-msa-fixed-region = <&wlan_msa_mem>;
	qcom,smmu-s1-bypass;
	vdd-0.8-cx-mx-supply = <&pm8998_l5>;
	qcom,vdd-0.8-cx-mx-config = <800000 800000 2400 1000>;
	qcom,hyp_disabled;
	qcom,smp2p_map_wlan_1_in {
		interrupts-extended = <&smp2p_wlan_1_in 0 0>,
			<&smp2p_wlan_1_in 1 0>;
		interrupt-names = "qcom,smp2p-force-fatal-error",
			"qcom,smp2p-early-crash-ind";
	};
    };
+28 −0
Original line number Diff line number Diff line
@@ -768,3 +768,31 @@ config QCOM_HYP_CORE_CTL
	  CPUs can be assigned to the other guest OS by the hypervisor.
	  An offline CPU is considered as a reserved CPU since this OS can't use
	  it.

config ICNSS
	tristate "Platform driver for Q6 integrated connectivity"
	select CNSS_UTILS
	help
	  This module adds support for Q6 integrated WLAN connectivity
	  subsystem. This module is responsible for communicating WLAN on/off
	  control messages to FW over QMI channel. It is also responsible for
	  handling WLAN PD restart notifications.

config ICNSS_DEBUG
	bool "ICNSS debug support"
	depends on ICNSS
	help
	  Say 'Y' here to enable ICNSS driver debug support. Debug support
	  primarily consists of logs consisting of information related to
	  hardware register access and enabling BUG_ON for certain cases to aid
	  the debugging.

config ICNSS_QMI
	bool "ICNSS QMI support"
	depends on ICNSS
	help
	  Say 'Y' here to enable ICNSS QMI support. ICNSS driver will use
	  QMI framework to communicate with WLAN FW. It will send coldboot
	  handshake messages to WLAN FW, which includes hardware capabilities
	  and configurations. It also send WLAN on/off control message to FW
	  over QMI channel.
+2 −0
Original line number Diff line number Diff line
@@ -83,3 +83,5 @@ obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o
obj-$(CONFIG_QCOM_HYP_CORE_CTL) += hyp_core_ctl.o
obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o
obj-$(CONFIG_QCOM_CDSP_RM) += cdsprm.o
obj-$(CONFIG_ICNSS) += icnss.o
obj-$(CONFIG_ICNSS_QMI) += icnss_qmi.o wlan_firmware_service_v01.o
+3640 −0

File added.

Preview size limit exceeded, changes collapsed.

+371 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 */

#ifndef __ICNSS_PRIVATE_H__
#define __ICNSS_PRIVATE_H__

#include <linux/adc-tm-clients.h>
#include <linux/iio/consumer.h>

#define icnss_ipc_log_string(_x...) do {				\
	if (icnss_ipc_log_context)					\
		ipc_log_string(icnss_ipc_log_context, _x);		\
	} while (0)

#define icnss_ipc_log_long_string(_x...) do {				\
	if (icnss_ipc_log_long_context)					\
		ipc_log_string(icnss_ipc_log_long_context, _x);		\
	} while (0)

#define icnss_pr_err(_fmt, ...) do {					\
	printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__);		\
	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",			\
			     ##__VA_ARGS__);				\
	} while (0)

#define icnss_pr_warn(_fmt, ...) do {					\
	printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__);		\
	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",			\
			     ##__VA_ARGS__);				\
	} while (0)

#define icnss_pr_info(_fmt, ...) do {					\
	printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__);		\
	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",			\
			     ##__VA_ARGS__);				\
	} while (0)

#if defined(CONFIG_DYNAMIC_DEBUG)
#define icnss_pr_dbg(_fmt, ...) do {					\
	pr_debug(_fmt, ##__VA_ARGS__);					\
	icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__);		\
	} while (0)

#define icnss_pr_vdbg(_fmt, ...) do {					\
	pr_debug(_fmt, ##__VA_ARGS__);					\
	icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__);		\
	} while (0)
#elif defined(DEBUG)
#define icnss_pr_dbg(_fmt, ...) do {					\
	printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__);		\
	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",			\
			     ##__VA_ARGS__);				\
	} while (0)

#define icnss_pr_vdbg(_fmt, ...) do {					\
	printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__);		\
	icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "",		\
				  ##__VA_ARGS__);			\
	} while (0)
#else
#define icnss_pr_dbg(_fmt, ...) do {					\
	no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__);	\
	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",			\
		     ##__VA_ARGS__);					\
	} while (0)

#define icnss_pr_vdbg(_fmt, ...) do {					\
	no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__);	\
	icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "",		\
				  ##__VA_ARGS__);			\
	} while (0)
#endif

#ifdef CONFIG_ICNSS_DEBUG
#define ICNSS_ASSERT(_condition) do {					\
		if (!(_condition)) {					\
			icnss_pr_err("ASSERT at line %d\n", __LINE__);	\
			BUG();						\
		}							\
	} while (0)
#else
#define ICNSS_ASSERT(_condition) do { } while (0)
#endif

#define icnss_fatal_err(_fmt, ...)					\
	icnss_pr_err("fatal: "_fmt, ##__VA_ARGS__)

enum icnss_debug_quirks {
	HW_ALWAYS_ON,
	HW_DEBUG_ENABLE,
	SKIP_QMI,
	HW_ONLY_TOP_LEVEL_RESET,
	RECOVERY_DISABLE,
	SSR_ONLY,
	PDR_ONLY,
	FW_REJUVENATE_ENABLE,
};

extern uint64_t dynamic_feature_mask;
extern void *icnss_ipc_log_context;
extern void *icnss_ipc_log_long_context;
extern unsigned long quirks;

enum icnss_driver_event_type {
	ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
	ICNSS_DRIVER_EVENT_SERVER_EXIT,
	ICNSS_DRIVER_EVENT_FW_READY_IND,
	ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
	ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
	ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
	ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
	ICNSS_DRIVER_EVENT_MAX,
};

struct icnss_event_server_arrive_data {
	unsigned int node;
	unsigned int port;
};

struct icnss_event_pd_service_down_data {
	bool crashed;
	bool fw_rejuvenate;
};

struct icnss_driver_event {
	struct list_head list;
	enum icnss_driver_event_type type;
	bool sync;
	struct completion complete;
	int ret;
	void *data;
};

enum icnss_driver_state {
	ICNSS_WLFW_CONNECTED,
	ICNSS_POWER_ON,
	ICNSS_FW_READY,
	ICNSS_DRIVER_PROBED,
	ICNSS_FW_TEST_MODE,
	ICNSS_PM_SUSPEND,
	ICNSS_PM_SUSPEND_NOIRQ,
	ICNSS_SSR_REGISTERED,
	ICNSS_PDR_REGISTERED,
	ICNSS_PD_RESTART,
	ICNSS_MSA0_ASSIGNED,
	ICNSS_WLFW_EXISTS,
	ICNSS_SHUTDOWN_DONE,
	ICNSS_HOST_TRIGGERED_PDR,
	ICNSS_FW_DOWN,
	ICNSS_DRIVER_UNLOADING,
	ICNSS_REJUVENATE,
	ICNSS_MODE_ON,
	ICNSS_BLOCK_SHUTDOWN,
};

struct ce_irq_list {
	int irq;
	irqreturn_t (*handler)(int irq, void *priv);
};

struct icnss_vreg_info {
	struct regulator *reg;
	const char *name;
	u32 min_v;
	u32 max_v;
	u32 load_ua;
	unsigned long settle_delay;
	bool required;
};

struct icnss_clk_info {
	struct clk *handle;
	const char *name;
	u32 freq;
	bool required;
};

struct icnss_stats {
	struct {
		uint32_t posted;
		uint32_t processed;
	} events[ICNSS_DRIVER_EVENT_MAX];

	struct {
		uint32_t request;
		uint32_t free;
		uint32_t enable;
		uint32_t disable;
	} ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];

	struct {
		uint32_t pdr_fw_crash;
		uint32_t pdr_host_error;
		uint32_t root_pd_crash;
		uint32_t root_pd_shutdown;
	} recovery;

	uint32_t pm_suspend;
	uint32_t pm_suspend_err;
	uint32_t pm_resume;
	uint32_t pm_resume_err;
	uint32_t pm_suspend_noirq;
	uint32_t pm_suspend_noirq_err;
	uint32_t pm_resume_noirq;
	uint32_t pm_resume_noirq_err;
	uint32_t pm_stay_awake;
	uint32_t pm_relax;

	uint32_t ind_register_req;
	uint32_t ind_register_resp;
	uint32_t ind_register_err;
	uint32_t msa_info_req;
	uint32_t msa_info_resp;
	uint32_t msa_info_err;
	uint32_t msa_ready_req;
	uint32_t msa_ready_resp;
	uint32_t msa_ready_err;
	uint32_t msa_ready_ind;
	uint32_t cap_req;
	uint32_t cap_resp;
	uint32_t cap_err;
	uint32_t pin_connect_result;
	uint32_t cfg_req;
	uint32_t cfg_resp;
	uint32_t cfg_req_err;
	uint32_t mode_req;
	uint32_t mode_resp;
	uint32_t mode_req_err;
	uint32_t ini_req;
	uint32_t ini_resp;
	uint32_t ini_req_err;
	u32 rejuvenate_ind;
	uint32_t rejuvenate_ack_req;
	uint32_t rejuvenate_ack_resp;
	uint32_t rejuvenate_ack_err;
	uint32_t vbatt_req;
	uint32_t vbatt_resp;
	uint32_t vbatt_req_err;
};

#define WLFW_MAX_TIMESTAMP_LEN 32
#define WLFW_MAX_BUILD_ID_LEN 128
#define WLFW_MAX_NUM_MEMORY_REGIONS 2
#define WLFW_FUNCTION_NAME_LEN 129
#define WLFW_MAX_DATA_SIZE 6144
#define WLFW_MAX_STR_LEN 16
#define WLFW_MAX_NUM_CE 12
#define WLFW_MAX_NUM_SVC 24
#define WLFW_MAX_NUM_SHADOW_REG 24

struct service_notifier_context {
	void *handle;
	uint32_t instance_id;
	char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
};

struct wlfw_rf_chip_info {
	uint32_t chip_id;
	uint32_t chip_family;
};

struct wlfw_rf_board_info {
	uint32_t board_id;
};

struct wlfw_fw_version_info {
	uint32_t fw_version;
	char fw_build_timestamp[WLFW_MAX_TIMESTAMP_LEN + 1];
};

enum icnss_msa_perm {
	ICNSS_MSA_PERM_HLOS_ALL = 0,
	ICNSS_MSA_PERM_WLAN_HW_RW = 1,
	ICNSS_MSA_PERM_MAX,
};

#define ICNSS_MAX_VMIDS     4

struct icnss_mem_region_info {
	uint64_t reg_addr;
	uint32_t size;
	uint8_t secure_flag;
	enum icnss_msa_perm perm;
};

struct icnss_msa_perm_list_t {
	int vmids[ICNSS_MAX_VMIDS];
	int perms[ICNSS_MAX_VMIDS];
	int nelems;
};

struct icnss_priv {
	uint32_t magic;
	struct platform_device *pdev;
	struct icnss_driver_ops *ops;
	struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
	struct icnss_vreg_info *vreg_info;
	struct icnss_clk_info *clk_info;
	u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
	phys_addr_t mem_base_pa;
	void __iomem *mem_base_va;
	struct dma_iommu_mapping *smmu_mapping;
	dma_addr_t smmu_iova_start;
	size_t smmu_iova_len;
	dma_addr_t smmu_iova_ipa_start;
	size_t smmu_iova_ipa_len;
	struct qmi_handle qmi;
	struct list_head event_list;
	spinlock_t event_lock;
	struct work_struct event_work;
	struct work_struct fw_recv_msg_work;
	struct workqueue_struct *event_wq;
	phys_addr_t msa_pa;
	uint32_t msa_mem_size;
	void *msa_va;
	unsigned long state;
	struct wlfw_rf_chip_info chip_info;
	uint32_t board_id;
	uint32_t soc_id;
	struct wlfw_fw_version_info fw_version_info;
	char fw_build_id[WLFW_MAX_BUILD_ID_LEN + 1];
	u32 pwr_pin_result;
	u32 phy_io_pin_result;
	u32 rf_pin_result;
	uint32_t nr_mem_region;
	struct icnss_mem_region_info
		mem_region[WLFW_MAX_NUM_MEMORY_REGIONS];
	struct dentry *root_dentry;
	spinlock_t on_off_lock;
	struct icnss_stats stats;
	struct work_struct service_notifier_work;
	struct service_notifier_context *service_notifier;
	struct notifier_block service_notifier_nb;
	int total_domains;
	struct notifier_block get_service_nb;
	void *modem_notify_handler;
	struct notifier_block modem_ssr_nb;
	uint32_t diag_reg_read_addr;
	uint32_t diag_reg_read_mem_type;
	uint32_t diag_reg_read_len;
	uint8_t *diag_reg_read_buf;
	atomic_t pm_count;
	struct ramdump_device *msa0_dump_dev;
	bool bypass_s1_smmu;
	bool force_err_fatal;
	bool allow_recursive_recovery;
	bool early_crash_ind;
	u8 cause_for_rejuvenation;
	u8 requesting_sub_system;
	u16 line_number;
	struct mutex dev_lock;
	bool is_hyp_disabled;
	uint32_t fw_error_fatal_irq;
	uint32_t fw_early_crash_irq;
	struct completion unblock_shutdown;
	struct adc_tm_param vph_monitor_params;
	struct adc_tm_chip *adc_tm_dev;
	struct iio_channel *channel;
	uint64_t vph_pwr;
	bool vbatt_supported;
	char function_name[WLFW_FUNCTION_NAME_LEN + 1];
};

int icnss_call_driver_uevent(struct icnss_priv *priv,
				    enum icnss_uevent uevent, void *data);
int icnss_driver_event_post(enum icnss_driver_event_type type,
				   u32 flags, void *data);
#endif
Loading