diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index 3dbaef99d89c..947ba3ca140d 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -244,8 +244,12 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova, } spin_lock_irqsave(&mapping->lock, flags); - av8l_fast_unmap_public(mapping->pgtbl_ops, iova, len); + + if (unlikely(!av8l_fast_unmap_public(mapping->pgtbl_ops, iova, len))) + goto fail; + __fast_smmu_free_iova(mapping, iova, len); +fail: spin_unlock_irqrestore(&mapping->lock, flags); trace_unmap(to_msm_iommu_domain(mapping->domain), iova - offset, len, @@ -385,7 +389,8 @@ static void fast_smmu_unmap_sg(struct device *dev, len = ALIGN(sg_dma_address(sg) + sg_dma_len(sg) - (start - offset), FAST_PAGE_SIZE); - av8l_fast_unmap_public(mapping->pgtbl_ops, start, len); + if (unlikely(!av8l_fast_unmap_public(mapping->pgtbl_ops, start, len))) + return; spin_lock_irqsave(&mapping->lock, flags); __fast_smmu_free_iova(mapping, start, len); @@ -653,7 +658,10 @@ static void fast_smmu_free(struct device *dev, size_t size, size = ALIGN(size, FAST_PAGE_SIZE); spin_lock_irqsave(&mapping->lock, flags); - av8l_fast_unmap_public(mapping->pgtbl_ops, dma_handle, size); + + if (unlikely(!av8l_fast_unmap_public(mapping->pgtbl_ops, dma_handle, size))) + goto fail; + __fast_smmu_free_iova(mapping, dma_handle, size); spin_unlock_irqrestore(&mapping->lock, flags); @@ -674,6 +682,11 @@ static void fast_smmu_free(struct device *dev, size_t size, if (page) dma_free_contiguous(dev, page, size); + + return; + +fail: + spin_unlock_irqrestore(&mapping->lock, flags); } static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c index a9159c012106..f07e93b33f05 100644 --- a/drivers/iommu/io-pgtable-fast.c +++ b/drivers/iommu/io-pgtable-fast.c @@ -127,7 +127,14 @@ #define PTE_SH_IDX(pte) (pte & AV8L_FAST_PTE_SH_MASK) -#define iopte_pmd_offset(pmds, base, iova) (pmds + ((iova - base) >> 12)) +#define iopte_pmd_offset(pmds, base, iova) \ +({ \ + typeof(iova) __iova = (iova); \ + typeof(base) __base = (base); \ + typeof(pmds) __pmds = (pmds); \ + (__iova < __base) ? ERR_PTR(-EINVAL) : \ + __pmds + ((__iova - __base) >> AV8L_FAST_PAGE_SHIFT); \ +}) static inline dma_addr_t av8l_dma_addr(void *addr) { @@ -202,6 +209,12 @@ void av8l_fast_clear_stale_ptes(struct io_pgtable_ops *ops, u64 base, struct io_pgtable *iop = iof_pgtable_ops_to_pgtable(ops); av8l_fast_iopte *pmdp = iopte_pmd_offset(data->pmds, data->base, base); + if (IS_ERR(pmdp)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return; + } + for (i = base >> AV8L_FAST_PAGE_SHIFT; i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) { if (!(*pmdp & AV8L_FAST_PTE_VALID)) { @@ -254,6 +267,12 @@ static int av8l_fast_map(struct io_pgtable_ops *ops, unsigned long iova, unsigned long i, nptes = size >> AV8L_FAST_PAGE_SHIFT; av8l_fast_iopte pte; + if (IS_ERR(ptep)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return -EINVAL; + } + pte = av8l_fast_prot_to_pte(data, prot); paddr &= AV8L_FAST_PTE_ADDR_MASK; for (i = 0; i < nptes; i++, paddr += SZ_4K) { @@ -286,6 +305,12 @@ __av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, ptep = iopte_pmd_offset(data->pmds, data->base, iova); nptes = size >> AV8L_FAST_PAGE_SHIFT; + if (IS_ERR(ptep)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return 0; + } + memset(ptep, val, sizeof(*ptep) * nptes); av8l_clean_range(&iop->cfg, ptep, ptep + nptes); if (!allow_stale_tlb) @@ -295,10 +320,10 @@ __av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, } /* caller must take care of tlb cache maintenance */ -void av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, +size_t av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, size_t size) { - __av8l_fast_unmap(ops, iova, size, true); + return __av8l_fast_unmap(ops, iova, size, true); } static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, @@ -383,6 +408,12 @@ static bool av8l_fast_iova_coherent(struct io_pgtable_ops *ops, struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops); av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova); + if (IS_ERR(ptep)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return false; + } + return ((PTE_MAIR_IDX(*ptep) == AV8L_FAST_MAIR_ATTR_IDX_CACHE) && ((PTE_SH_IDX(*ptep) == AV8L_FAST_PTE_SH_OS) || (PTE_SH_IDX(*ptep) == AV8L_FAST_PTE_SH_IS))); diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index c8e23ac53d2f..cef10b457073 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -2179,6 +2179,11 @@ int ep_pcie_core_disable_endpoint(void) if (atomic_read(&dev->host_wake_pending)) { EP_PCIE_DBG(dev, "PCIe V%d: wake pending, init wakeup\n", dev->rev); + /* + * Clear the wake pending otherwise ep_pcie_core_wakeup_host_internal + * will return without WAKE toggle + */ + atomic_set(&dev->host_wake_pending, 0); ep_pcie_core_wakeup_host_internal(EP_PCIE_EVENT_PM_D3_COLD); } diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index d7d9ecad8d73..141ce1157e9c 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -39,7 +39,7 @@ /* Wait time on the device for Host to set BHI_INTVEC */ #define MHI_BHI_INTVEC_MAX_CNT 200 #define MHI_BHI_INTVEC_WAIT_MS 50 -#define MHI_WAKEUP_TIMEOUT_CNT 20 +#define MHI_WAKEUP_TIMEOUT_CNT 25 #define MHI_MASK_CH_EV_LEN 32 #define MHI_RING_CMD_ID 0 #define MHI_RING_PRIMARY_EVT_ID 1 diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.c b/drivers/platform/msm/mhi_dev/mhi_sm.c index 89190bfeadfc..e8d9300c7812 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.c +++ b/drivers/platform/msm/mhi_dev/mhi_sm.c @@ -12,6 +12,7 @@ #include "mhi_hwio.h" #include "mhi_sm.h" #include +#include #define MHI_SM_DBG(fmt, args...) \ mhi_log(MHI_MSG_DBG, fmt, ##args) @@ -28,7 +29,11 @@ #define PCIE_EP_TIMER_US 500000000 #define MHI_IPA_DISABLE_DELAY_MS 10 #define MHI_IPA_DISABLE_COUNTER 20 +/* Maximum wait time for D state transitions to D3hot */ +#define M3_DO_WAKEUP_TIMEOUT_MS 2500 +static void wait_d3_and_wakeup(struct work_struct *work); +static int mhi_dev_sm_get_mhi_pcie_states(uint32_t *mstate, uint32_t *dstate); static inline const char *mhi_sm_dev_event_str(enum mhi_dev_event state) { @@ -235,6 +240,8 @@ struct mhi_sm_dev { struct mutex mhi_state_lock; bool syserr_occurred; struct workqueue_struct *mhi_sm_wq; + struct workqueue_struct *mhi_wake_wq; + struct work_struct mhi_wake_work; atomic_t pending_device_events; atomic_t pending_pcie_events; struct mhi_sm_stats stats; @@ -734,12 +741,12 @@ exit: * mhi_sm_wakeup_host() - wakeup MHI-host *@event: MHI state chenge event * - * Sends wekup event to MHI-host via EP-PCIe, in case MHI is in M3 state. + * Sends wakeup event to MHI-host via EP-PCIe, in case MHI is in M3 state. * * Return: 0:success * negative: failure */ -static int mhi_sm_wakeup_host(enum mhi_dev_event event) +static int mhi_sm_wakeup_host(void) { int res = 0; enum ep_pcie_event pcie_event; @@ -754,7 +761,8 @@ static int mhi_sm_wakeup_host(enum mhi_dev_event event) } else if (mhi_sm_ctx->mhi_state == MHI_DEV_M3_STATE) { /* * Check and send D3_HOT to enable waking up the host - * using inband PME. + * using inband PME if the host is in D3_HOT state, otherwise + * send D3_COLD to wake up the host. */ if (mhi_sm_ctx->d_state == MHI_SM_EP_PCIE_D3_HOT_STATE) pcie_event = EP_PCIE_EVENT_PM_D3_HOT; @@ -906,9 +914,7 @@ static void mhi_sm_dev_event_manager(struct work_struct *work) break; case MHI_DEV_EVENT_HW_ACC_WAKEUP: case MHI_DEV_EVENT_CORE_WAKEUP: - res = mhi_sm_wakeup_host(chg_event->event); - if (res) - MHI_SM_ERR("Failed to wakeup MHI host\n"); + queue_work(mhi_sm_ctx->mhi_wake_wq, &mhi_sm_ctx->mhi_wake_work); break; case MHI_DEV_EVENT_CTRL_TRIG: case MHI_DEV_EVENT_M1_STATE: @@ -1119,9 +1125,19 @@ int mhi_dev_sm_init(struct mhi_dev *mhi_dev) if (!mhi_sm_ctx->mhi_sm_wq) { MHI_SM_ERR("Failed to create singlethread_workqueue: sm_wq\n"); res = -ENOMEM; - goto fail_init_wq; + goto fail_init_sm_wq; } + if (!mhi_sm_ctx->mhi_wake_wq) + mhi_sm_ctx->mhi_wake_wq = alloc_workqueue( + "mhi_wake_wq", WQ_HIGHPRI | WQ_UNBOUND, 1); + if (!mhi_sm_ctx->mhi_wake_wq) { + MHI_SM_ERR("Failed to create singlethread_workqueue: wake_wq\n"); + res = -ENOMEM; + goto fail_init_wake_wq; + } + INIT_WORK(&mhi_sm_ctx->mhi_wake_work, wait_d3_and_wakeup); + mutex_init(&mhi_sm_ctx->mhi_state_lock); mhi_sm_ctx->mhi_dev = mhi_dev; mhi_sm_ctx->mhi_state = MHI_DEV_RESET_STATE; @@ -1134,7 +1150,10 @@ int mhi_dev_sm_init(struct mhi_dev *mhi_dev) MHI_SM_FUNC_EXIT(); return 0; -fail_init_wq: +fail_init_wake_wq: + flush_workqueue(mhi_sm_ctx->mhi_sm_wq); + destroy_workqueue(mhi_sm_ctx->mhi_sm_wq); +fail_init_sm_wq: mhi_sm_ctx = NULL; mhi_sm_debugfs_destroy(); return res; @@ -1162,20 +1181,20 @@ int mhi_dev_sm_exit(struct mhi_dev *mhi_dev) EXPORT_SYMBOL(mhi_dev_sm_exit); /** - * mhi_dev_sm_get_mhi_state() -Get current MHI state. + * mhi_dev_sm_get_mhi_pcie_states() -Get current MHI and Pcie states. * @state: return param * - * Returns the current MHI state of the state machine. + * Returns the current MHI and PCIe states of the state machine. * * Return: 0 success * -EINVAL: invalid param * -EFAULT: state machine isn't initialized */ -int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state) +static int mhi_dev_sm_get_mhi_pcie_states(uint32_t *mstate, uint32_t *dstate) { MHI_SM_FUNC_ENTRY(); - if (!state) { + if (!mstate || !dstate) { MHI_SM_ERR("Fail: Null argument\n"); return -EINVAL; } @@ -1183,15 +1202,60 @@ int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state) MHI_SM_ERR("Fail: MHI SM is not initialized\n"); return -EFAULT; } - *state = mhi_sm_ctx->mhi_state; + mutex_lock(&mhi_sm_ctx->mhi_state_lock); + *mstate = mhi_sm_ctx->mhi_state; + *dstate = mhi_sm_ctx->d_state; + mutex_unlock(&mhi_sm_ctx->mhi_state_lock); MHI_SM_DBG("state machine states are: %s and %s\n", - mhi_sm_mstate_str(*state), - mhi_sm_dstate_str(mhi_sm_ctx->d_state)); + mhi_sm_mstate_str(*mstate), + mhi_sm_dstate_str(*dstate)); MHI_SM_FUNC_EXIT(); return 0; } -EXPORT_SYMBOL(mhi_dev_sm_get_mhi_state); + +static void wait_d3_and_wakeup(struct work_struct *work) +{ + struct mhi_sm_dev *mhi_sm_ctx = container_of(work, struct mhi_sm_dev, mhi_wake_work); + enum mhi_dev_state mstate; + enum mhi_sm_ep_pcie_state dstate; + ktime_t timeout = 0; + + if (mhi_dev_sm_get_mhi_pcie_states(&mstate, &dstate)) { + MHI_SM_ERR("Unable to read states\n"); + return; + } + /* + * Handle host wakeup in M3 + D0 states. + * When a MHI WAKE request is received while device is in D0, + * wait for D3 and wakeup the host using inband PME. + * If the MHI state changes to M0 while waiting for D3, + * exit, since both MHI and the device are in active state + */ + if (dstate == MHI_SM_EP_PCIE_D0_STATE) { + timeout = ktime_add_ms(ktime_get(), M3_DO_WAKEUP_TIMEOUT_MS); + while (1) { + mhi_dev_sm_get_mhi_pcie_states(&mstate, &dstate); + if (mstate == MHI_DEV_M0_STATE) { + MHI_SM_DBG("M0 state received\n"); + return; + } + if (dstate == MHI_SM_EP_PCIE_D3_HOT_STATE || + dstate == MHI_SM_EP_PCIE_D3_COLD_STATE) { + MHI_SM_DBG("D3 state received\n"); + goto send_host_wakeup; + } + if (ktime_after(ktime_get(), timeout)) { + MHI_SM_ERR("Neither received D3 nor M0 in stipulated time\n"); + return; + } + usleep_range(1000, 2000); + } + } +send_host_wakeup: + if (dstate == MHI_SM_EP_PCIE_D3_HOT_STATE || dstate == MHI_SM_EP_PCIE_D3_COLD_STATE) + mhi_sm_wakeup_host(); +} /** * mhi_dev_sm_set_ready() -Set MHI state to ready. diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.h b/drivers/platform/msm/mhi_dev/mhi_sm.h index 80ed0086472f..24e6daf46777 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.h +++ b/drivers/platform/msm/mhi_dev/mhi_sm.h @@ -42,7 +42,6 @@ int mhi_dev_sm_init(struct mhi_dev *dev); int mhi_dev_sm_exit(struct mhi_dev *dev); int mhi_dev_sm_set_ready(void); int mhi_dev_notify_sm_event(enum mhi_dev_event event); -int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state); int mhi_dev_sm_syserr(void); void mhi_dev_sm_pcie_handler(struct ep_pcie_notify *notify); diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h index 245f86fbbe46..95b05a85f61c 100644 --- a/include/linux/io-pgtable-fast.h +++ b/include/linux/io-pgtable-fast.h @@ -44,7 +44,7 @@ struct av8l_fast_io_pgtable { int av8l_fast_map_public(struct io_pgtable_ops *ops, unsigned long iova, phys_addr_t paddr, size_t size, int prot); -void av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, +size_t av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, size_t size); int av8l_fast_map_sg_public(struct io_pgtable_ops *ops, @@ -63,9 +63,10 @@ av8l_fast_map_public(struct io_pgtable_ops *ops, unsigned long iova, { return -EINVAL; } -static inline void av8l_fast_unmap_public(struct io_pgtable_ops *ops, +static inline size_t av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, size_t size) { + return 0; } static inline int av8l_fast_map_sg_public(struct io_pgtable_ops *ops, diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index 8d2bb198379d..5f63c7b96479 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. * - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _UAPI_MSM_IPA_H_ @@ -152,8 +152,9 @@ #define IPA_IOCTL_SET_EXT_ROUTER_MODE 95 #define IPA_IOCTL_ADD_DEL_DSCP_PCP_MAPPING 96 #define IPA_IOCTL_SEND_VLAN_MUXID_MAPPING 97 -#define IPA_IOCTL_SEND_TUNNEL_TEMPLATE_INFO 98 -#define IPA_IOCTL_QUERY_TUNNEL_FEATURE 99 +#define IPA_IOCTL_SEND_TUNNEL_TEMPLATE_INFO 98 +#define IPA_IOCTL_QUERY_TUNNEL_FEATURE 99 +#define IPA_IOCTL_ADD_IPOGRE_MAPPING 100 /** * max size of the header to be inserted */ @@ -976,8 +977,12 @@ enum ipa_eth_pdu_evt { #define IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX }; - -#define IPA_EVENT_MAX_NUM (IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX) +enum ipa_ipogre_event { + IPA_IPOGRE_NOTIFY_EVENT = IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX, + IPA_IPOGRE_EVENT_MAX +#define IPA_IPOGRE_EVENT_MAX IPA_IPOGRE_EVENT_MAX +}; +#define IPA_EVENT_MAX_NUM (IPA_IPOGRE_EVENT_MAX) #define IPA_EVENT_MAX ((int)IPA_EVENT_MAX_NUM) /** @@ -1140,48 +1145,6 @@ static inline const char *exception_type_as_str(enum ipa_exception_type t) "???"; } -/** - * Macro ipa_exception_type_pppoe - * - * This macro is for describing which field is to be looked at for - * exception path consideration. - * - * NOTE 1: The field implies an offset into the packet under - * consideration. This offset will be calculated on behalf of - * the user of this API. - * - * NOTE 2: When exceptions are generated/sent in an ipa_exception - * structure, they will considered to be from the upload - * perspective. And when appropriate, a corresponding, and - * perhaps inverted, downlink exception will be automatically - * created on the callers behalf. As an example: If a - * FIELD_UDP_SRC_PORT is sent, an uplink exception will be - * created for udp source port, and a corresponding - * FIELD_UDP_DST_PORT will be automatically created for the - * downlink. - */ -#define FIELD_IP_PROTOCOL_PPPOE (FIELD_ETHER_TYPE + 1) -#define FIELD_TCP_SRC_PORT_PPPOE (FIELD_IP_PROTOCOL_PPPOE + 1) -#define FIELD_TCP_DST_PORT_PPPOE (FIELD_TCP_SRC_PORT_PPPOE + 1) -#define FIELD_UDP_SRC_PORT_PPPOE (FIELD_TCP_DST_PORT_PPPOE + 1) -#define FIELD_UDP_DST_PORT_PPPOE (FIELD_UDP_SRC_PORT_PPPOE + 1) -#define FIELD_ETHER_TYPE_PPPOE (FIELD_UDP_DST_PORT_PPPOE + 1) -#define FIELD_PPPOE_MAX (FIELD_ETHER_TYPE_PPPOE + 1) - -/* Function to read PPPoE exception in string format */ -static inline const char *pppoe_exception_type_as_str(uint32_t t) -{ - return - (t == FIELD_IP_PROTOCOL_PPPOE) ? "pppoe_ip_protocol" : - (t == FIELD_TCP_SRC_PORT_PPPOE) ? "pppoe_tcp_src_port" : - (t == FIELD_TCP_DST_PORT_PPPOE) ? "pppoe_tcp_dst_port" : - (t == FIELD_UDP_SRC_PORT_PPPOE) ? "pppoe_udp_src_port" : - (t == FIELD_UDP_DST_PORT_PPPOE) ? "pppoe_udp_dst_port" : - (t == FIELD_ETHER_TYPE_PPPOE) ? "pppoe_ether_type" : - (t == FIELD_PPPOE_MAX) ? "pppoe_max" : - "???"; -} - #define IP_TYPE_EXCEPTION(x) \ ((x) == FIELD_IP_PROTOCOL || \ (x) == FIELD_TCP_SRC_PORT || \ @@ -1262,7 +1225,6 @@ struct ipa_field_val_equation_gen { * @payload_length: Payload length. * @ext_attrib_mask: Extended attributes. * @l2tp_udp_next_hdr: next header in L2TP tunneling - * @p_exception : exception to enable for mpls-pppoe * @field_val_equ: for finding a value at a particular offset */ struct ipa_rule_attrib { @@ -1308,7 +1270,7 @@ struct ipa_rule_attrib { __u16 payload_length; __u32 ext_attrib_mask; __u8 l2tp_udp_next_hdr; - __u8 p_exception; + __u8 padding1; struct ipa_field_val_equation_gen fld_val_eq; }; @@ -1594,9 +1556,11 @@ enum ipa_hdr_proc_type { IPA_HDR_PROC_EoGRE_HEADER_REMOVE, IPA_HDR_PROC_WWAN_TO_ETHII_EX, IPA_HDR_PROC_GRE_HEADER_ADD, - IPA_HDR_PROC_GRE_HEADER_REMOVE + IPA_HDR_PROC_GRE_HEADER_REMOVE, + IPA_HDR_PROC_IPOGRE_HEADER_ADD, + IPA_HDR_PROC_IPOGRE_HEADER_REMOVE }; -#define IPA_HDR_PROC_MAX (IPA_HDR_PROC_GRE_HEADER_REMOVE + 1) +#define IPA_HDR_PROC_MAX (IPA_HDR_PROC_IPOGRE_HEADER_REMOVE + 1) /** * struct ipa_rt_rule - attributes of a routing rule @@ -2001,6 +1965,68 @@ struct ipa_ioc_eogre_info { struct IpaDscpVlanPcpMap_t map_info; }; +#define MAX_FLOW_PER_IPOGRE_TUNNEL 10 + +/** + * struct ipa_ipogre_info - + * @ipv4_src: Specifies source v4 address if GRE tunnel is ipv4 + * @ipv4_dst: Specifies destination v4 address if GRE tunnel is ipv4 + * @ipv6_src: Specifies source v6 address if GRE tunnel is ipv6 + * @ipv6_dst: Specifies destination v6 address if GRE tunnel is ipv6 + * @iptype: Specifies GRE tunnel's ip address type + * @tunnel_id: Specifies tunnel id + */ + +struct ipa_ipogre_tunnel_info { + uint32_t ipv4_src; + uint32_t ipv4_dst; + uint32_t ipv6_src[4]; + uint32_t ipv6_dst[4]; + enum ipa_ip_type iptype; + uint8_t tunnel_id; +} __packed; + +/** + * struct ipa_ipogre_info - + * @ipv4_src: Specifies source v4 address if GRE tunnel is ipv4 + * @ipv4_src_subnet: Specifies source v4 address subnet if GRE tunnel is ipv4 + * @ipv4_dst: Specifies destination v4 address if GRE tunnel is ipv4 + * @ipv4_dst_subnet: Specifies destination v4 address subnet if GRE tunnel is ipv4 + * @ipv6_src: Specifies source v6 address if GRE tunnel is ipv6 + * @ipv6_src_subnet: Specifies source v6 address subnet if GRE tunnel is ipv6 + * @ipv6_dst: Specifies destination v6 address if GRE tunnel is ipv6 + * @ipv6_dst_subnet: Specifies destination v6 address subnet if GRE tunnel is ipv6 + * @iptype: Specifies GRE tunnel's ip address type + * @protocol: Specifies protocol of the data traffic + */ + +struct ipa_ipogre_flow_info { + uint32_t ipv4_src; + uint32_t ipv4_src_subnet; + uint32_t ipv4_dst; + uint32_t ipv4_dst_subnet; + uint32_t ipv6_src[4]; + uint32_t ipv6_dst[4]; + uint32_t src_port; + uint32_t dst_port; + enum ipa_ip_type iptype; + uint8_t protocol; + uint8_t ipv6_src_subnet; + uint8_t ipv6_dst_subnet; +} __packed; + +/** + * struct ipa_ipogre_info - + * @ipogre_tunnel_info: Specifies tunnel information + * @ipogre_flow_info: Specifies flows to be offloaded + * @ipa_ipogre_num_flow: Specifies number of flow to be offloaded + */ + +struct ipa_ioc_ipogre_info { + struct ipa_ipogre_tunnel_info ipogre_tunnel_info; + struct ipa_ipogre_flow_info ipogre_flow_info[MAX_FLOW_PER_IPOGRE_TUNNEL]; + uint8_t ipa_ipogre_num_flow; +}; /** * struct ipa_eogre_header_add_procparams - * @eth_hdr_retained: Specifies if Ethernet header is retained or not @@ -2092,6 +2118,45 @@ struct ipa_gre_hdr_proc_ctx_params { struct ipa_gre_header_remove_procparams hdr_remove_param; }; +/** + * struct ipa_ipogre_header_add_procparams - + * @input_ip_version: Specifies if Input header is IPV4(0) or IPV6(1) + * @output_ip_version: Specifies if template header's outer IP is IPV4(0) + * or IPV6(1) + * @Tunnel_Id: Tunnel id associated with the header. + * @Mux_Id: Specifies mux id associated with the template header + */ +struct ipa_ipogre_header_add_procparams { + uint32_t input_ip_version : 1; + uint32_t output_ip_version : 1; + uint32_t tunnel_id : 4; + uint32_t mux_id : 8; + uint32_t reserved :18; +}; + +/** + * struct ipa_ipogre_header_remove_procparams - + * @hdr_len_remove: Specifies how much (in bytes) of the header needs + * to be removed + * @input_ip_version: Specifies if Input header is IPV4(0) or IPV6(1) + * @Tunnel_Id: Tunnel id associated with the header. + */ +struct ipa_ipogre_header_remove_procparams { + uint32_t hdr_len_remove : 8; + uint32_t input_ip_version : 1; + uint32_t tunnel_id : 4; + uint32_t reserved :19; +}; + +/** + * struct ipa_ipogre_hdr_proc_ctx_params - + * @hdr_add_param: parameters for header add + * @hdr_remove_param: parameters for header remove + */ +struct ipa_ipogre_hdr_proc_ctx_params { + struct ipa_ipogre_header_add_procparams hdr_add_param; + struct ipa_ipogre_header_remove_procparams hdr_remove_param; +}; /** * struct ipa_eth_II_to_eth_II_ex_procparams - * @input_ethhdr_negative_offset: Specifies where the ethernet hdr offset is @@ -2154,6 +2219,7 @@ struct ipa_hdr_proc_ctx_add { struct ipa_eth_II_to_eth_II_ex_procparams generic_params; struct ipa_wwan_to_eth_II_ex_procparams generic_params_v2; struct ipa_gre_hdr_proc_ctx_params gre_params; + struct ipa_ipogre_hdr_proc_ctx_params ipogre_params; }; #define IPA_L2TP_HDR_PROC_SUPPORT @@ -4264,6 +4330,10 @@ struct ipa_ioc_dscp_pcp_map_info { IPA_IOCTL_QUERY_TUNNEL_FEATURE, \ uint8_t) +#define IPA_IOC_ADD_IPoGRE_MAPPING _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_ADD_IPOGRE_MAPPING, \ + struct ipa_ioc_ipogre_info) + /* * unique magic number of the Tethering bridge ioctls */