diff --git a/arch/arm/configs/vendor/sdxlemur.config b/arch/arm/configs/vendor/sdxlemur.config index 5974ea0c7c99..97f39227174d 100644 --- a/arch/arm/configs/vendor/sdxlemur.config +++ b/arch/arm/configs/vendor/sdxlemur.config @@ -171,6 +171,8 @@ CONFIG_NET_SCH_PRIO=y CONFIG_NET_SCH_TBF=y CONFIG_QRTR=y CONFIG_QRTR_MHI=y +CONFIG_QRTR_MHI_DEV=y +CONFIG_QRTR_NODE_ID=2 CONFIG_QRTR_SMD=y CONFIG_NETDEVICES=y CONFIG_MAILBOX=y @@ -319,9 +321,7 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_SPS_DMA=y CONFIG_I2C_MSM_V2=y CONFIG_SPI_QUP=y -CONFIG_BLK_CGROUP=y CONFIG_IOSCHED_BFQ=y -CONFIG_BFQ_GROUP_IOSCHED=y CONFIG_MMC=y CONFIG_SDC_QTI=y CONFIG_MMC_SDHCI=y @@ -368,3 +368,6 @@ CONFIG_PCIEASPM_POWER_SUPERSAVE=y CONFIG_PAGE_POOL=y CONFIG_PHYLINK=m CONFIG_QPS615=m +# CONFIG_BLK_CGROUP is not set +# CONFIG_BLK_DEBUG_FS is not set +# CONFIG_VT is not set diff --git a/drivers/cpufreq/qcom-cpufreq-hw-debug.c b/drivers/cpufreq/qcom-cpufreq-hw-debug.c index 7aa1266cbd4f..d7a1bad08cdf 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw-debug.c +++ b/drivers/cpufreq/qcom-cpufreq-hw-debug.c @@ -5,7 +5,6 @@ #define pr_fmt(fmt) "cpufreq_hw_debug: %s: " fmt, __func__ -#include #include #include #include @@ -19,6 +18,7 @@ enum debug_hw_regs_data { REG_PSTATE_STATUS, REG_EPSS_DEBUG_STATUS, REG_EPSS_DEBUG_SRB, + REG_EPSS_DEBUG_LUT, REG_ARRAY_SIZE, }; @@ -26,7 +26,6 @@ enum debug_hw_regs_data { struct cpufreq_hwregs { void __iomem *base[REG_ARRAY_SIZE]; int domain_cnt; - struct dentry *debugfs_base; }; struct cpufreq_register_data { @@ -36,6 +35,8 @@ struct cpufreq_register_data { static struct cpufreq_hwregs *hw_regs; static const u16 *offsets; +static struct kobj_attribute cpufreq_hwregs_attr; +static struct kobject *cpufreqhw_kobj; static const u16 cpufreq_qcom_std_data[] = { [REG_PERF_STATE] = 0x920, @@ -49,12 +50,14 @@ static const u16 cpufreq_qcom_std_epss_data[] = { [REG_PSTATE_STATUS] = 0x020, [REG_EPSS_DEBUG_STATUS] = 0x01c, [REG_EPSS_DEBUG_SRB] = 0x0bc, + [REG_EPSS_DEBUG_LUT] = 0x100, }; -static int print_cpufreq_hw_debug_regs(struct seq_file *s, void *unused) +static ssize_t cpufreq_hwregs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { int i, j, size = ARRAY_SIZE(cpufreq_qcom_std_data); - u32 regval; + u32 regval, count = 0; static struct cpufreq_register_data data[] = { {"PERF_STATE_DESIRED", REG_PERF_STATE}, @@ -62,35 +65,26 @@ static int print_cpufreq_hw_debug_regs(struct seq_file *s, void *unused) {"PSTATE_STATUS", REG_PSTATE_STATUS}, {"EPSS_DEBUG_STATUS", REG_EPSS_DEBUG_STATUS}, {"EPSS_DEBUG_SRB", REG_EPSS_DEBUG_SRB}, + {"EPSS_DEBUG_LUT", REG_EPSS_DEBUG_LUT}, }; if (offsets == cpufreq_qcom_std_epss_data) size = ARRAY_SIZE(cpufreq_qcom_std_epss_data); for (i = 0; i < hw_regs->domain_cnt; i++) { - seq_printf(s, "FREQUENCY DOMAIN %d\n", i); + count += scnprintf(buf + count, PAGE_SIZE, + "FREQUENCY DOMAIN %d\n", i); for (j = 0; j < size; j++) { regval = readl_relaxed(hw_regs->base[i] + offsets[data[j].offset]); - seq_printf(s, "%25s: 0x%.8x\n", data[j].name, regval); + count += scnprintf(buf + count, PAGE_SIZE, + "%25s: 0x%.8x\n", data[j].name, regval); } } - return 0; + return count; } -static int print_cpufreq_hw_reg_open(struct inode *inode, struct file *file) -{ - return single_open(file, print_cpufreq_hw_debug_regs, NULL); -} - -static const struct file_operations cpufreq_debug_register_fops = { - .open = print_cpufreq_hw_reg_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - static int cpufreq_panic_callback(struct notifier_block *nfb, unsigned long event, void *unused) { @@ -103,6 +97,7 @@ static int cpufreq_panic_callback(struct notifier_block *nfb, {"PSTATE_STATUS", REG_PSTATE_STATUS}, {"EPSS_DEBUG_STATUS", REG_EPSS_DEBUG_STATUS}, {"EPSS_DEBUG_SRB", REG_EPSS_DEBUG_SRB}, + {"EPSS_DEBUG_LUT", REG_EPSS_DEBUG_LUT}, }; if (offsets == cpufreq_qcom_std_epss_data) @@ -181,22 +176,23 @@ static int enable_cpufreq_hw_debug(struct platform_device *pdev) return ret; } - hw_regs->debugfs_base = debugfs_create_dir("qcom-cpufreq-hw", NULL); - if (!hw_regs->debugfs_base) { - dev_err(&pdev->dev, "Failed to create debugfs entry\n"); - return -ENODEV; + cpufreqhw_kobj = kobject_create_and_add("qcom-cpufreq-hw", + kernel_kobj); + if (!cpufreqhw_kobj) + return -ENOMEM; + + sysfs_attr_init(&cpufreq_hwregs_attr.attr); + cpufreq_hwregs_attr.attr.name = "print_cpufreq_debug_regs"; + cpufreq_hwregs_attr.show = cpufreq_hwregs_show; + cpufreq_hwregs_attr.attr.mode = 0444; + + ret = sysfs_create_file(cpufreqhw_kobj, &cpufreq_hwregs_attr.attr); + if (ret) { + dev_err(&pdev->dev, "Failed to create sysfs entry\n"); + kobject_put(cpufreqhw_kobj); } - if (!debugfs_create_file("print_cpufreq_debug_regs", 0444, - hw_regs->debugfs_base, NULL, &cpufreq_debug_register_fops)) - goto debugfs_fail; - - return 0; - -debugfs_fail: - dev_err(&pdev->dev, "Failed to create debugfs entry so cleaning up\n"); - debugfs_remove_recursive(hw_regs->debugfs_base); - return -ENODEV; + return ret; } static int qcom_cpufreq_hw_debug_probe(struct platform_device *pdev) @@ -206,7 +202,8 @@ static int qcom_cpufreq_hw_debug_probe(struct platform_device *pdev) static int qcom_cpufreq_hw_debug_remove(struct platform_device *pdev) { - debugfs_remove_recursive(hw_regs->debugfs_base); + sysfs_remove_file(kernel_kobj, &cpufreq_hwregs_attr.attr); + kobject_put(cpufreqhw_kobj); return 0; } diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index fac02e36999b..2388ccd48b70 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -869,11 +869,25 @@ static void kgsl_destroy_process_private(struct kref *kref) struct kgsl_process_private *private = container_of(kref, struct kgsl_process_private, refcount); + mutex_lock(&kgsl_driver.process_mutex); + debugfs_remove_recursive(private->debug_root); + kgsl_process_uninit_sysfs(private); + + /* When using global pagetables, do not detach global pagetable */ + if (private->pagetable->name != KGSL_MMU_GLOBAL_PT) + kgsl_mmu_detach_pagetable(private->pagetable); + + /* Remove the process struct from the master list */ + write_lock(&kgsl_driver.proclist_lock); + list_del(&private->list); + write_unlock(&kgsl_driver.proclist_lock); + mutex_unlock(&kgsl_driver.process_mutex); + put_pid(private->pid); idr_destroy(&private->mem_idr); idr_destroy(&private->syncsource_idr); - /* When using global pagetables, do not detach global pagetable */ + /* When using global pagetables, do not put global pagetable */ if (private->pagetable->name != KGSL_MMU_GLOBAL_PT) kgsl_mmu_putpagetable(private->pagetable); @@ -962,8 +976,15 @@ static struct kgsl_process_private *kgsl_process_private_new( kfree(private); private = ERR_PTR(err); + return private; } + kgsl_process_init_sysfs(device, private); + kgsl_process_init_debugfs(private); + write_lock(&kgsl_driver.proclist_lock); + list_add(&private->list, &kgsl_driver.process_list); + write_unlock(&kgsl_driver.proclist_lock); + return private; } @@ -1008,8 +1029,8 @@ static void kgsl_process_private_close(struct kgsl_device_private *dev_priv, } /* - * If this is the last file on the process take down the debug - * directories and garbage collect any outstanding resources + * If this is the last file on the process garbage collect + * any outstanding resources */ process_release_memory(private); @@ -1017,18 +1038,6 @@ static void kgsl_process_private_close(struct kgsl_device_private *dev_priv, /* Release all syncsource objects from process private */ kgsl_syncsource_process_release_syncsources(private); - debugfs_remove_recursive(private->debug_root); - kgsl_process_uninit_sysfs(private); - - /* When using global pagetables, do not detach global pagetable */ - if (private->pagetable->name != KGSL_MMU_GLOBAL_PT) - kgsl_mmu_detach_pagetable(private->pagetable); - - /* Remove the process struct from the master list */ - write_lock(&kgsl_driver.proclist_lock); - list_del(&private->list); - write_unlock(&kgsl_driver.proclist_lock); - mutex_unlock(&kgsl_driver.process_mutex); kgsl_process_private_put(private); @@ -1046,19 +1055,7 @@ static struct kgsl_process_private *kgsl_process_private_open( if (IS_ERR(private)) goto done; - /* - * If this is a new process create the debug directories and add it to - * the process list - */ - - if (private->fd_count++ == 0) { - kgsl_process_init_sysfs(device, private); - kgsl_process_init_debugfs(private); - - write_lock(&kgsl_driver.proclist_lock); - list_add(&private->list, &kgsl_driver.process_list); - write_unlock(&kgsl_driver.proclist_lock); - } + private->fd_count++; done: mutex_unlock(&kgsl_driver.process_mutex); diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 8bb7bd4aadc7..599c5b61e9f8 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -202,10 +202,10 @@ static ssize_t mem_entry_sysfs_show(struct kobject *kobj, ssize_t ret; /* - * kgsl_process_init_sysfs takes a refcount to the process_private, - * which is put when the kobj is released. This implies that priv will - * not be freed until this function completes, and no further locking - * is needed. + * sysfs_remove_file waits for reads to complete before the node is + * deleted and process private is freed only once kobj is released. + * This implies that priv will not be freed until this function + * completes, and no further locking is needed. */ priv = kobj ? container_of(kobj, struct kgsl_process_private, kobj) : NULL; @@ -228,10 +228,10 @@ static ssize_t memtype_sysfs_show(struct kobject *kobj, int id = 0; /* - * kgsl_process_init_sysfs takes a refcount to the process_private, - * which is put when the kobj is released. This implies that priv will - * not be freed until this function completes, and no further locking - * is needed. + * sysfs_remove_file waits for reads to complete before the node is + * deleted and process private is freed only once kobj is released. + * This implies that priv will not be freed until this function + * completes, and no further locking is needed. */ priv = container_of(kobj, struct kgsl_process_private, kobj_memtype); memtype = container_of(attr, struct kgsl_memtype, attr); @@ -261,13 +261,9 @@ static ssize_t memtype_sysfs_show(struct kobject *kobj, return scnprintf(buf, PAGE_SIZE, "%llu\n", size); } +/* Dummy release function - we have nothing to do here */ static void mem_entry_release(struct kobject *kobj) { - struct kgsl_process_private *priv; - - priv = container_of(kobj, struct kgsl_process_private, kobj); - /* Put the refcount we got in kgsl_process_init_sysfs */ - kgsl_process_private_put(priv); } static const struct sysfs_ops mem_entry_sysfs_ops = { @@ -329,9 +325,6 @@ void kgsl_process_init_sysfs(struct kgsl_device *device, { int i; - /* Keep private valid until the sysfs enries are removed. */ - kgsl_process_private_get(private); - if (kobject_init_and_add(&private->kobj, &ktype_mem_entry, kgsl_driver.prockobj, "%d", pid_nr(private->pid))) { dev_err(device->dev, "Unable to add sysfs for process %d\n", diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index d3aa3de2bc7c..f75fa57fa30f 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -687,13 +687,18 @@ static void kgsl_device_snapshot_atomic(struct kgsl_device *device) } device->snapshot_memory_atomic.size = device->snapshot_memory.size; - device->snapshot_memory_atomic.ptr = devm_kzalloc(&device->pdev->dev, - device->snapshot_memory_atomic.size, GFP_ATOMIC); + if (!device->snapshot_faultcount) { + /* Use non-atomic snapshot memory if it is unused */ + device->snapshot_memory_atomic.ptr = device->snapshot_memory.ptr; + } else { + device->snapshot_memory_atomic.ptr = devm_kzalloc(&device->pdev->dev, + device->snapshot_memory_atomic.size, GFP_ATOMIC); - if (!device->snapshot_memory_atomic.ptr) { - dev_err(device->dev, - "KGSL failed to allocate memory for atomic snapshot\n"); - return; + if (!device->snapshot_memory_atomic.ptr) { + dev_err(device->dev, + "KGSL failed to allocate memory for atomic snapshot\n"); + return; + } } /* Allocate memory for the snapshot instance */ diff --git a/drivers/interconnect/qcom/holi.c b/drivers/interconnect/qcom/holi.c index d0edbfd1e1e9..a6a92e8719c8 100644 --- a/drivers/interconnect/qcom/holi.c +++ b/drivers/interconnect/qcom/holi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. * */ @@ -1718,6 +1718,13 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + ret = clk_bulk_prepare_enable(qp->num_qos_clks, qp->qos_clks); + if (ret) { + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + dev_err(&pdev->dev, "failed to enable QoS clocks\n"); + return ret; + } + for (i = 0; i < num_nodes; i++) { size_t j; @@ -1732,6 +1739,11 @@ static int qnoc_probe(struct platform_device *pdev) goto err; } + if (qnodes[i]->qosbox) { + qnodes[i]->noc_ops->set_qos(qnodes[i]); + qnodes[i]->qosbox->initialized = true; + } + node->name = qnodes[i]->name; node->data = qnodes[i]; icc_node_add(node, provider); @@ -1743,6 +1755,8 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; + clk_bulk_disable_unprepare(qp->num_qos_clks, qp->qos_clks); + platform_set_drvdata(pdev, qp); dev_info(dev, "Registered HOLI ICC\n"); @@ -1758,6 +1772,7 @@ err: icc_node_destroy(node->id); } + clk_bulk_disable_unprepare(qp->num_qos_clks, qp->qos_clks); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); icc_provider_del(provider); return ret; diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 4e4ac6b2dfff..ef73e2c2cf85 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -99,7 +99,7 @@ obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o obj-$(CONFIG_QCOM_MPM) += mpm.o -mpm-objs += qcom-mpm.o qcom-mpm-holi.o qcom-mpm-sdxnightjar.o qcom-mpm-monaco.o qcom-mpm-qcs405.o +mpm-objs += qcom-mpm.o qcom-mpm-holi.o qcom-mpm-sdxnightjar.o qcom-mpm-monaco.o qcom-mpm-qcs405.o qcom-mpm-blair.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_MADERA_IRQ) += irq-madera.o diff --git a/drivers/irqchip/qcom-mpm-blair.c b/drivers/irqchip/qcom-mpm-blair.c new file mode 100644 index 000000000000..b4da13878435 --- /dev/null +++ b/drivers/irqchip/qcom-mpm-blair.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include +const struct mpm_pin mpm_blair_gic_chip_data[] = { + {5, 296}, /* lpass_irq_out_sdc */ + {12, 422}, /* qmp_usb3_lfps_rxterm_irq_cx */ + {86, 183}, /* mpm_wake,spmi_m */ + {89, 314}, /* tsens0_tsens_0C_int */ + {90, 315}, /* tsens1_tsens_0C_int */ + {93, 164}, /* eud_p0_dmse_int_mx */ + {94, 165}, /* eud_p0_dpse_int_mx */ + {-1}, +}; diff --git a/drivers/irqchip/qcom-mpm.c b/drivers/irqchip/qcom-mpm.c index acdddcbfac11..bebd0a8898fe 100644 --- a/drivers/irqchip/qcom-mpm.c +++ b/drivers/irqchip/qcom-mpm.c @@ -572,6 +572,10 @@ static const struct of_device_id mpm_gic_chip_data_table[] = { .compatible = "qcom,mpm-gic-qcs405", .data = mpm_qcs405_gic_chip_data, }, + { + .compatible = "qcom,mpm-gic-blair", + .data = mpm_blair_gic_chip_data, + }, {} }; MODULE_DEVICE_TABLE(of, mpm_gic_chip_data_table); diff --git a/drivers/media/radio/rtc6226/radio-rtc6226-common.c b/drivers/media/radio/rtc6226/radio-rtc6226-common.c index e1c9de541f57..ea08d264ac7f 100644 --- a/drivers/media/radio/rtc6226/radio-rtc6226-common.c +++ b/drivers/media/radio/rtc6226/radio-rtc6226-common.c @@ -312,6 +312,7 @@ void rtc6226_scan(struct work_struct *work) struct kfifo *data_b; int len = 0; u32 next_freq_khz; + u8 factor; int retval = 0; int i, rssi; @@ -427,7 +428,26 @@ void rtc6226_scan(struct work_struct *work) TUNE_STEP_SIZE) == next_freq_khz) { FMDERR("%s Seek one more time if lower freq is valid\n", __func__); - retval = rtc6226_set_seek(radio, SRCH_UP, WRAP_ENABLE); + // Tuned to band low limit + chan spacing then seek down with + // bandlimit config + if (radio->space == 0) + factor = 20; + else if (radio->space == 1) + factor = 10; + else + factor = 5; + retval = rtc6226_set_freq(radio, + (radio->recv_conf.band_low_limit + factor) * TUNE_STEP_SIZE); + if (retval < 0) + goto seek_tune_fail; + /* wait for tune to complete. */ + if (!wait_for_completion_timeout(&radio->completion, + msecs_to_jiffies(TUNE_TIMEOUT_MSEC))) { + FMDERR("In %s, didn't receive STC for tune\n", __func__); + rtc6226_q_event(radio, RTC6226_EVT_ERROR); + return; + } + retval = rtc6226_set_seek(radio, SRCH_DOWN, WRAP_DISABLE); if (retval < 0) { FMDERR("%s seek fail %d\n", __func__, retval); goto seek_tune_fail; @@ -454,10 +474,12 @@ void rtc6226_scan(struct work_struct *work) rssi = radio->registers[RSSI] & RSSI_RSSI; FMDBG("%s freq %d, rssi %d rssi threshold %d\n", __func__, next_freq_khz, rssi, radio->rssi_th); - if ((radio->recv_conf.band_low_limit * - TUNE_STEP_SIZE) == - next_freq_khz && - rssi >= radio->rssi_th) { + // Reach to band low limit, + // if SF is 0, means frequency at low limit is a commercial + // station Or a invalid channel + if (((radio->registers[STATUS] & STATUS_SF) == 0) || + (radio->recv_conf.band_high_limit * + TUNE_STEP_SIZE) == next_freq_khz) { FMDERR("lower band freq is valid\n"); rtc6226_q_event(radio, RTC6226_EVT_TUNE_SUCC); diff --git a/drivers/media/radio/rtc6226/radio-rtc6226.h b/drivers/media/radio/rtc6226/radio-rtc6226.h index 5e6bba18ea6d..8f412cd96623 100644 --- a/drivers/media/radio/rtc6226/radio-rtc6226.h +++ b/drivers/media/radio/rtc6226/radio-rtc6226.h @@ -200,7 +200,7 @@ #define SCAN_PENDING 3 #define START_SCAN 1 #define TUNE_TIMEOUT_MSEC 3000 -#define SEEK_TIMEOUT_MSEC 15000 +#define SEEK_TIMEOUT_MSEC 30000 #define RTC6226_MIN_SRCH_MODE 0x00 #define RTC6226_MAX_SRCH_MODE 0x02 diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 55fb99a8d6de..90c707279f8f 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -3913,8 +3913,7 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) if (!fw_mem[i].va) { cnss_pr_err("Failed to allocate memory for FW, size: 0x%zx, type: %u\n", fw_mem[i].size, fw_mem[i].type); - - return -ENOMEM; + BUG(); } } } diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index 604af34daea5..e58be80b51b2 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "../pci.h" @@ -764,6 +765,7 @@ struct msm_pcie_dev_t { uint32_t gpio_n; uint32_t parf_deemph; uint32_t parf_swing; + uint32_t clkreq_gpio; struct msm_pcie_vreg_info_t *cx_vreg; struct msm_pcie_clk_info_t *rate_change_clk; @@ -6266,6 +6268,12 @@ static int msm_pcie_probe(struct platform_device *pdev) PCIE_DBG(pcie_dev, "RC%d: aux clock frequency: %d.\n", pcie_dev->rc_idx, pcie_dev->aux_clk_freq); + of_property_read_u32(of_node, "qcom,clkreq-gpio", + &pcie_dev->clkreq_gpio); + + PCIE_DBG(pcie_dev, "RC%d: clkreq gpio no:%u\n", + pcie_dev->rc_idx, pcie_dev->clkreq_gpio); + pcie_dev->shadow_en = true; pcie_dev->aer_enable = true; @@ -7725,6 +7733,10 @@ static int msm_pcie_drv_resume(struct msm_pcie_dev_t *pcie_dev) PCIE_DBG(pcie_dev, "PCIe RC%d: LTSSM_STATE: %s\n", pcie_dev->rc_idx, TO_LTSSM_STR(val & 0x3f)); + msm_gpio_mpm_wake_set(pcie_dev->clkreq_gpio, false); + + PCIE_DBG(pcie_dev, "PCIe: RC%d: disable wake up cap for CLKREQ GPIO\n", + pcie_dev->rc_idx); mutex_unlock(&pcie_dev->setup_lock); mutex_unlock(&pcie_dev->recovery_lock); @@ -7747,6 +7759,10 @@ static int msm_pcie_drv_suspend(struct msm_pcie_dev_t *pcie_dev, } mutex_lock(&pcie_dev->recovery_lock); + msm_gpio_mpm_wake_set(pcie_dev->clkreq_gpio, true); + + PCIE_DBG(pcie_dev, "PCIe: RC%d:Enable wake up cap for CLKREQ GPIO\n", + pcie_dev->rc_idx); /* disable global irq - no more linkdown/aer detection */ disable_irq(pcie_dev->irq[MSM_PCIE_INT_GLOBAL_INT].num); diff --git a/drivers/pinctrl/qcom/pinctrl-blair.c b/drivers/pinctrl/qcom/pinctrl-blair.c index 56689c707d48..8d4b6a3f83ff 100644 --- a/drivers/pinctrl/qcom/pinctrl-blair.c +++ b/drivers/pinctrl/qcom/pinctrl-blair.c @@ -118,7 +118,23 @@ .offset = qup_offset, \ } +#define TLMM_NORTH_SPARE_OFFSET 0x1B3000 +#define TLMM_NORTH_SPARE1_OFFSET 0x1B4000 +#define SPARE_REG(sparereg, spare_offset) \ + { \ + .spare_reg = tlmm_##sparereg, \ + .offset = spare_offset, \ + } + +enum blair_tlmm_spare { + tlmm_west_spare, + tlmm_west_spare1, + tlmm_north_spare, + tlmm_north_spare1, + tlmm_south_spare, + tlmm_south_spare1, +}; static const struct pinctrl_pin_desc blair_pins[] = { PINCTRL_PIN(0, "GPIO_0"), @@ -1312,6 +1328,15 @@ static const struct msm_function blair_functions[] = { FUNCTION(USB_PHY), }; +static const struct msm_spare_tlmm blair_spare_regs[] = { + SPARE_REG(west_spare, 0), + SPARE_REG(west_spare1, 0), + SPARE_REG(north_spare, TLMM_NORTH_SPARE_OFFSET), + SPARE_REG(north_spare1, TLMM_NORTH_SPARE1_OFFSET), + SPARE_REG(south_spare, 0), + SPARE_REG(south_spare1, 0), +}; + /* Every pin is maintained as a single group, and missing or non-existing pin * would be maintained as dummy group to synchronize pin group index with * pin descriptor registered with pinctrl core. @@ -1652,6 +1677,8 @@ static const struct msm_pinctrl_soc_data blair_pinctrl = { .ngpios = 157, .wakeirq_map = blair_mpm_map, .nwakeirq_map = ARRAY_SIZE(blair_mpm_map), + .spare_regs = blair_spare_regs, + .nspare_regs = ARRAY_SIZE(blair_spare_regs), }; static int blair_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 85f8548d04ee..4f5442c37985 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -37,6 +37,7 @@ #define MAX_NR_TILES 4 #define PS_HOLD_OFFSET 0x820 #define QUP_MASK GENMASK(5, 0) +#define SPARE_MASK GENMASK(15, 8) /** * struct msm_pinctrl - state for a pinctrl-msm device @@ -1505,6 +1506,45 @@ int msm_qup_read(unsigned int mode) return -ENOENT; } +int msm_spare_write(int spare_reg, u32 val) +{ + u32 offset; + const struct msm_spare_tlmm *regs = msm_pinctrl_data->soc->spare_regs; + int num_regs = msm_pinctrl_data->soc->nspare_regs; + + if (!regs || spare_reg >= num_regs) + return -ENOENT; + + offset = regs[spare_reg].offset; + if (offset != 0) { + writel_relaxed(val & SPARE_MASK, + msm_pinctrl_data->regs[0] + offset); + return 0; + } + + return -ENOENT; +} +EXPORT_SYMBOL(msm_spare_write); + +int msm_spare_read(int spare_reg) +{ + u32 offset, val; + const struct msm_spare_tlmm *regs = msm_pinctrl_data->soc->spare_regs; + int num_regs = msm_pinctrl_data->soc->nspare_regs; + + if (!regs || spare_reg >= num_regs) + return -ENOENT; + + offset = regs[spare_reg].offset; + if (offset != 0) { + val = readl_relaxed(msm_pinctrl_data->regs[0] + offset); + return val & SPARE_MASK; + } + + return -ENOENT; +} +EXPORT_SYMBOL(msm_spare_read); + /* * msm_gpio_mpm_wake_set - API to make interrupt wakeup capable * @dev: Device corrsponding to pinctrl diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 5bcac8855500..687b4a2f3435 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -125,6 +125,16 @@ struct pinctrl_qup { u32 offset; }; +/* + * struct msm_spare_tlmm - TLMM spare registers config + * @spare_reg: spare register number + * @offset: Offset of spare register + */ +struct msm_spare_tlmm { + int spare_reg; + u32 offset; +}; + /** * struct msm_gpio_wakeirq_map - Map of GPIOs and their wakeup pins * @gpio: The GPIOs that are wakeup capable @@ -169,6 +179,8 @@ struct msm_pinctrl_soc_data { unsigned int n_no_wake_gpios; struct pinctrl_qup *qup_regs; unsigned int nqup_regs; + const struct msm_spare_tlmm *spare_regs; + unsigned int nspare_regs; struct msm_dir_conn *dir_conn; }; diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index de4ed7ff5a8a..5bb96011b7f8 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -180,15 +180,6 @@ config R8125_IOSS To compile this driver as a module, choose M here. Module will be called r8125_ioss. If unsure, say N. -config R8125_IPA_OFFLOAD - bool "Enable IPA offload on Realtek R8125 driver" - depends on IPA3 - help - Enables IPA Ethernet Offload path on Realtek R8125 driver. It is - designed to support generic HW processing of UL/DL IP packets for - various use cases independent of radio technology. When this is - disabled Realtek R8125 driver switches to embedded software path. - config AQFWD_IOSS tristate "AQC glue driver for IOSS v2" depends on AQFWD diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index 4f5bfc135cbd..0eeef414892c 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -87,6 +87,10 @@ static int mhi_dev_alloc_evt_buf_evt_req(struct mhi_dev *mhi, static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq); static void mhi_dev_event_msi_cb(void *req); +static void mhi_dev_cmd_event_msi_cb(void *req); + +static int mhi_dev_alloc_cmd_ack_buf_req(struct mhi_dev *mhi); + static struct mhi_dev_uevent_info channel_state_info[MHI_MAX_CHANNELS]; static DECLARE_COMPLETION(read_from_host); @@ -262,7 +266,7 @@ static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq) { struct ep_pcie_msi_config cfg; struct mhi_addr msi_addr; - struct mhi_dev_channel *ch = ereq->context; + struct mhi_dev_channel *ch; uint64_t evnt_ring_idx = mhi->ev_ring_start + ereq->event_ring; struct mhi_dev_ring *ring = &mhi->ring[evnt_ring_idx]; union mhi_dev_ring_ctx *ctx; @@ -283,14 +287,23 @@ static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq) msi_addr.phy_addr = ring->msi_buf_dma_handle; ereq->event_type = SEND_MSI; - ereq->msi_cb = mhi_dev_event_msi_cb; + if (!ereq->is_cmd_cpl) { + ch = ereq->context; + ereq->msi_cb = mhi_dev_event_msi_cb; + ch->msi_cnt++; + mhi_log(MHI_MSG_VERBOSE, + "Sending MSI %d to 0x%llx as data = 0x%x for ch %d msi_count %d, ereq flush_num %d\n", + ctx->ev.msivec, msi_addr.host_pa, + *ring->msi_buf, ch->ch_id, + ch->msi_cnt, ereq->flush_num); + } else { + ereq->msi_cb = mhi_dev_cmd_event_msi_cb; + mhi_log(MHI_MSG_VERBOSE, + "Sending MSI %d to 0x%llx as data = 0x%x for cmd ack, ereq flush_num %d\n", + ctx->ev.msivec, msi_addr.host_pa, *ring->msi_buf, + ereq->flush_num); + } - ch->msi_cnt++; - - mhi_log(MHI_MSG_VERBOSE, - "Sending MSI %d to 0x%llx as data = 0x%x for ch %d msi_count %d, ereq flush_num %d\n", - ctx->ev.msivec, msi_addr.host_pa, *ring->msi_buf, ch->ch_id, - ch->msi_cnt, ereq->flush_num); mhi_ctx->write_to_host(mhi, &msi_addr, ereq, MHI_DEV_DMA_ASYNC); return 0; @@ -314,6 +327,27 @@ static void mhi_dev_event_rd_offset_completion_cb(void *req) sizeof(uint64_t), DMA_TO_DEVICE); } +static void mhi_dev_cmd_event_msi_cb(void *req) +{ + struct mhi_cmd_cmpl_ctx *cmd_ctx; + struct mhi_dev *mhi; + struct event_req *ereq = req; + unsigned long flags; + + mhi_log(MHI_MSG_VERBOSE, "MSI completed for flush req %d\n", + ereq->flush_num); + + /*Cmd completion handling*/ + mhi = ereq->context; + cmd_ctx = mhi->cmd_ctx; + cmd_ctx->cmd_buf_wp += ereq->num_events; + if (cmd_ctx->cmd_buf_wp == NUM_CMD_EVENTS_DEFAULT) + cmd_ctx->cmd_buf_wp = 0; + spin_lock_irqsave(&mhi->lock, flags); + list_add_tail(&ereq->list, &cmd_ctx->cmd_req_buffers); + spin_unlock_irqrestore(&mhi->lock, flags); +} + static void mhi_dev_event_msi_cb(void *req) { struct event_req *ereq = req; @@ -401,7 +435,8 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx) } static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, - struct event_req *ereq, uint32_t evt_len) + struct event_req *ereq, uint32_t evt_len, + enum mhi_dev_tr_compl_evt_type event_type) { int rc = 0; uint64_t evnt_ring_idx = mhi->ev_ring_start + evnt_ring; @@ -430,25 +465,25 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, return rc; } } - ch = ereq->context; - /* Check the limits of the buffer to be flushed */ - if (ereq->tr_events < ch->tr_events || - (ereq->tr_events + ereq->num_events) > - (ch->tr_events + ch->evt_buf_size)) { - pr_err("%s: Invalid completion event buffer!\n", __func__); - mhi_log(MHI_MSG_ERROR, - "Invalid cmpl evt buf - start %pK, end %pK\n", - ereq->tr_events, ereq->tr_events + ereq->num_events); - return -EINVAL; - } + mutex_lock(&ring->event_lock); - mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n", - ereq->num_events, ch->ch_id); + /* add the events */ ereq->client_cb = mhi_dev_event_buf_completion_cb; + ereq->is_cmd_cpl = (event_type == SEND_CMD_CMP) ? true:false; ereq->event_type = SEND_EVENT_BUFFER; + if (!ereq->is_cmd_cpl) { + ch = ereq->context; + mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n", + ereq->num_events, ch->ch_id); + } else { + mhi_log(MHI_MSG_VERBOSE, + "Flushing %d cmpl events of cmd ring\n", + ereq->num_events); + } + rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len); if (rc) { pr_err("%s(): error in adding element rc %d\n", __func__, rc); @@ -506,6 +541,55 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, return rc; } +static int mhi_dev_flush_cmd_completion_events(struct mhi_dev *mhi, + union mhi_dev_ring_element_type *el) +{ + struct mhi_cmd_cmpl_ctx *cmd_ctx = mhi->cmd_ctx; + unsigned long flags; + struct event_req *flush_ereq; + union mhi_dev_ring_element_type *compl_ev; + int rc = 0; + + /*cmd completions are sent on event ring 0 always*/ + if (!mhi->cmd_ctx) { + if (mhi_dev_alloc_cmd_ack_buf_req(mhi)) { + mhi_log(MHI_MSG_ERROR, "Alloc cmd ack buff failed"); + return -ENOMEM; + } + } + cmd_ctx = mhi->cmd_ctx; + if (list_empty(&cmd_ctx->cmd_req_buffers)) { + mhi_log(MHI_MSG_ERROR, "cmd req buff list is empty"); + return -ENOMEM; + } + + spin_lock_irqsave(&mhi->lock, flags); + flush_ereq = container_of(cmd_ctx->cmd_req_buffers.next, + struct event_req, list); + list_del_init(&flush_ereq->list); + flush_ereq->context = mhi; + spin_unlock_irqrestore(&mhi->lock, flags); + + compl_ev = cmd_ctx->cmd_events + cmd_ctx->cmd_buf_rp; + memcpy(compl_ev, el, sizeof(union mhi_dev_ring_element_type)); + cmd_ctx->cmd_buf_rp++; + if (cmd_ctx->cmd_buf_rp == NUM_CMD_EVENTS_DEFAULT) + cmd_ctx->cmd_buf_rp = 0; + flush_ereq->tr_events = compl_ev; + rc = mhi_dev_send_multiple_tr_events(mhi, + 0, + flush_ereq, + (1 * + sizeof(union mhi_dev_ring_element_type)), + SEND_CMD_CMP); + if (rc) { + mhi_log(MHI_MSG_ERROR, "failed to send compl evts\n"); + return rc; + } + + return rc; +} + static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, struct mhi_dev_channel *ch) { @@ -539,11 +623,25 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, flush_ereq->flush_num = ch->flush_req_cnt; mhi_log(MHI_MSG_DBG, "Flush num %d called for ch %d\n", ch->flush_req_cnt, ch->ch_id); + + /* Check the limits of the buffer to be flushed */ + if (flush_ereq->tr_events < ch->tr_events || + (flush_ereq->tr_events + flush_ereq->num_events) > + (ch->tr_events + ch->evt_buf_size)) { + pr_err("%s: Invalid completion event buffer!\n", + __func__); + mhi_log(MHI_MSG_ERROR, + "Invalid cmpl evt buf - start %pK, end %pK\n", + flush_ereq->tr_events, + flush_ereq->tr_events + flush_ereq->num_events); + return -EINVAL; + } rc = mhi_dev_send_multiple_tr_events(mhi, mhi->ch_ctx_cache[ch->ch_id].err_indx, flush_ereq, (flush_ereq->num_events * - sizeof(union mhi_dev_ring_element_type))); + sizeof(union mhi_dev_ring_element_type)), + SEND_EVENT_BUFFER); if (rc) { mhi_log(MHI_MSG_ERROR, "failed to send compl evts\n"); break; @@ -1608,7 +1706,7 @@ int mhi_dev_send_state_change_event(struct mhi_dev *mhi, event.evt_state_change.type = MHI_DEV_RING_EL_MHI_STATE_CHG; event.evt_state_change.mhistate = state; - return mhi_dev_send_event(mhi, 0, &event); + return mhi_dev_flush_cmd_completion_events(mhi, &event); } EXPORT_SYMBOL(mhi_dev_send_state_change_event); @@ -1619,7 +1717,7 @@ int mhi_dev_send_ee_event(struct mhi_dev *mhi, enum mhi_dev_execenv exec_env) event.evt_ee_state.type = MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY; event.evt_ee_state.execenv = exec_env; - return mhi_dev_send_event(mhi, 0, &event); + return mhi_dev_flush_cmd_completion_events(mhi, &event); } EXPORT_SYMBOL(mhi_dev_send_ee_event); @@ -1669,7 +1767,7 @@ static int mhi_dev_send_cmd_comp_event(struct mhi_dev *mhi, (size_t) event.evt_cmd_comp.ptr); event.evt_cmd_comp.type = MHI_DEV_RING_EL_CMD_COMPLETION_EVT; event.evt_cmd_comp.code = code; - return mhi_dev_send_event(mhi, 0, &event); + return mhi_dev_flush_cmd_completion_events(mhi, &event); } static int mhi_dev_process_stop_cmd(struct mhi_dev_ring *ring, uint32_t ch_id, @@ -1852,7 +1950,7 @@ send_start_completion_event: event.evt_cmd_comp.code = MHI_CMD_COMPL_CODE_UNDEFINED; - rc = mhi_dev_send_event(mhi, 0, &event); + rc = mhi_dev_flush_cmd_completion_events(mhi, &event); if (rc) { pr_err("stop event send failed\n"); return; @@ -1912,7 +2010,7 @@ send_start_completion_event: event.evt_cmd_comp.code = MHI_CMD_COMPL_CODE_UNDEFINED; - rc = mhi_dev_send_event(mhi, 0, &event); + rc = mhi_dev_flush_cmd_completion_events(mhi, &event); if (rc) { pr_err("stop event send failed\n"); return; @@ -2723,6 +2821,61 @@ static uint32_t mhi_dev_get_evt_ring_size(struct mhi_dev *mhi, uint32_t ch_id) mhi->ch_ctx_cache[ch_id].err_indx].ring_size; } +static int mhi_dev_alloc_cmd_ack_buf_req(struct mhi_dev *mhi) +{ + int rc = 0; + uint32_t i; + struct mhi_cmd_cmpl_ctx *cmd_ctx; + union mhi_dev_ring_element_type *cmd_events; + + mhi->cmd_ctx = kmalloc(sizeof(struct mhi_cmd_cmpl_ctx), + GFP_KERNEL); + if (!mhi->cmd_ctx) + return -ENOMEM; + + cmd_ctx = mhi->cmd_ctx; + /* Allocate event requests */ + cmd_ctx->ereqs = kcalloc(NUM_CMD_EVENTS_DEFAULT, + sizeof(*cmd_ctx->ereqs), + GFP_KERNEL); + if (!cmd_ctx->ereqs) { + goto free_ereqs; + return -ENOMEM; + } + + /* Allocate buffers to queue transfer completion events */ + cmd_ctx->cmd_events = kcalloc(NUM_CMD_EVENTS_DEFAULT, + sizeof(*cmd_events), + GFP_KERNEL); + if (!cmd_ctx->cmd_events) { + rc = -ENOMEM; + goto free_ereqs; + } + + /* Organize event flush requests into a linked list */ + INIT_LIST_HEAD(&cmd_ctx->cmd_req_buffers); + + for (i = 0; i < NUM_CMD_EVENTS_DEFAULT; ++i) { + list_add_tail(&cmd_ctx->ereqs[i].list, + &cmd_ctx->cmd_req_buffers); + } + + /* + * Initialize cmpl event buffer indexes - cmd_buf_rp and + * cmd_buf_wp point to the first and last free index available. + */ + cmd_ctx->cmd_buf_rp = 0; + cmd_ctx->cmd_buf_wp = NUM_CMD_EVENTS_DEFAULT - 1; + + return 0; +free_ereqs: + kfree(mhi->cmd_ctx); + kfree(cmd_ctx->ereqs); + cmd_ctx->ereqs = NULL; + mhi->cmd_ctx = NULL; + return rc; +} + static int mhi_dev_alloc_evt_buf_evt_req(struct mhi_dev *mhi, struct mhi_dev_channel *ch, struct mhi_dev_ring *evt_ring) { diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index 10e9ca3145dc..7fec5e68a001 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -271,6 +271,8 @@ struct mhi_config { /* maximum transfer completion events buffer */ #define NUM_TR_EVENTS_DEFAULT 128 +#define NUM_CMD_EVENTS_DEFAULT 20 + /* Set flush threshold to 80% of event buf size */ #define MHI_CMPL_EVT_FLUSH_THRSHLD(n) ((n * 8) / 10) @@ -354,7 +356,8 @@ enum mhi_dev_ch_operation { enum mhi_dev_tr_compl_evt_type { SEND_EVENT_BUFFER, SEND_EVENT_RD_OFFSET, - SEND_MSI + SEND_MSI, + SEND_CMD_CMP, }; enum mhi_dev_transfer_type { @@ -448,6 +451,18 @@ struct event_req { void (*msi_cb)(void *req); struct list_head list; u32 flush_num; + bool is_cmd_cpl; +}; + +struct mhi_cmd_cmpl_ctx { + /* Indices for completion event buffer */ + uint32_t cmd_buf_rp; + uint32_t cmd_buf_wp; + uint32_t cmd_buf_size; + bool mem_allocated; + struct list_head cmd_req_buffers; + struct event_req *ereqs; + union mhi_dev_ring_element_type *cmd_events; }; struct mhi_dev_channel { @@ -531,6 +546,7 @@ struct mhi_dev { struct mhi_dev_ring *ring; int mhi_irq; struct mhi_dev_channel *ch; + struct mhi_cmd_cmpl_ctx *cmd_ctx; int ctrl_int; int cmd_int; diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.c b/drivers/platform/msm/mhi_dev/mhi_sm.c index 7e3be7654729..655764d8664d 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.c +++ b/drivers/platform/msm/mhi_dev/mhi_sm.c @@ -22,7 +22,10 @@ #define MHI_SM_FUNC_ENTRY() MHI_SM_DBG("ENTRY\n") #define MHI_SM_FUNC_EXIT() MHI_SM_DBG("EXIT\n") -#define PCIE_EP_TIMER_US 500000000 +#define PCIE_EP_TIMER_US 500000000 +#define MHI_IPA_DISABLE_DELAY_MS 10 +#define MHI_IPA_DISABLE_COUNTER 20 + static inline const char *mhi_sm_dev_event_str(enum mhi_dev_event state) { @@ -591,7 +594,7 @@ static int mhi_sm_prepare_suspend(enum mhi_dev_state new_state) { enum mhi_dev_state old_state; struct ep_pcie_inactivity inact_param; - int res = 0, rc; + int res = 0, rc, wait_timeout = 0; MHI_SM_DBG("Switching event:%d\n", new_state); @@ -668,11 +671,24 @@ static int mhi_sm_prepare_suspend(enum mhi_dev_state new_state) (new_state == MHI_DEV_M3_STATE))) { if (mhi_sm_ctx->mhi_dev->use_ipa) { MHI_SM_DBG("Disable IPA with ipa_dma_disable()\n"); - res = ipa_dma_disable(); - if (res) { - MHI_SM_ERR("IPA disable failed\n"); + while (wait_timeout < MHI_IPA_DISABLE_COUNTER) { + /* wait for the disable to finish */ + res = ipa_dma_disable(); + if (!res) + break; + MHI_SM_ERR + ("IPA disable fail cnt:%d\n", + wait_timeout); + msleep(MHI_IPA_DISABLE_DELAY_MS); + wait_timeout++; + } + + if (wait_timeout >= MHI_IPA_DISABLE_COUNTER) { + MHI_SM_ERR + ("Fail to disable IPA for M3\n"); goto exit; } + MHI_SM_ERR("IPA DMA successfully disabled\n"); } } diff --git a/drivers/platform/msm/mhi_dev/mhi_uci.c b/drivers/platform/msm/mhi_dev/mhi_uci.c index 3a69f9359b45..d33ddfe5ea81 100644 --- a/drivers/platform/msm/mhi_dev/mhi_uci.c +++ b/drivers/platform/msm/mhi_dev/mhi_uci.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -95,8 +96,11 @@ static void mhi_uci_generic_client_cb(struct mhi_dev_client_cb_data *cb_data); static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data); static void mhi_uci_at_ctrl_tre_cb(struct mhi_dev_client_cb_reason *reason); -/* UCI channel attributes table */ -static const struct chan_attr uci_chan_attr_table[] = { +/* MHI channel attributes table + * Skip node creation for IPCR channels but still allow uevent broadcast to + * QRTR client by setting the broadcast flag + */ +static const struct chan_attr mhi_chan_attr_table[] = { { MHI_CLIENT_LOOPBACK_OUT, TRB_MAX_DATA_SIZE, @@ -266,7 +270,7 @@ static const struct chan_attr uci_chan_attr_table[] = { NULL, NULL, false, - false, + true, true }, { @@ -424,6 +428,8 @@ static ssize_t mhi_uci_ctrl_client_read(struct file *file, char __user *buf, size_t count, loff_t *offp); static ssize_t mhi_uci_client_write(struct file *file, const char __user *buf, size_t count, loff_t *offp); +static ssize_t mhi_uci_client_write_iter(struct kiocb *iocb, + struct iov_iter *buf); static int mhi_uci_client_open(struct inode *mhi_inode, struct file*); static int mhi_uci_ctrl_open(struct inode *mhi_inode, struct file*); static int mhi_uci_client_release(struct inode *mhi_inode, @@ -1221,8 +1227,8 @@ static int mhi_state_uevent(struct device *dev, struct kobj_uevent_env *env) mhi_parse_state(buf, &nbytes, info); add_uevent_var(env, "MHI_STATE=%s", buf); - for (i = 0; i < ARRAY_SIZE(uci_chan_attr_table); i++) { - chan_attrib = &uci_chan_attr_table[i]; + for (i = 0; i < ARRAY_SIZE(mhi_chan_attr_table); i++) { + chan_attrib = &mhi_chan_attr_table[i]; if (chan_attrib->state_bcast) { uci_log(UCI_DBG_ERROR, "Calling notify for ch %d\n", chan_attrib->chan_id); @@ -1480,14 +1486,73 @@ error_memcpy: } +static ssize_t mhi_uci_client_write_iter(struct kiocb *iocb, + struct iov_iter *buf) +{ + struct uci_client *uci_handle = NULL; + void *data_loc; + unsigned long memcpy_result; + int rc; + struct file *file = iocb->ki_filp; + ssize_t count = iov_iter_count(buf); + + if (!file || !buf || !count || !file->private_data) { + uci_log(UCI_DBG_DBG, "Invalid access to write-iter\n"); + return -EINVAL; + } + + uci_handle = file->private_data; + if (!uci_handle->send || !uci_handle->out_handle) { + uci_log(UCI_DBG_DBG, "Invalid handle or send\n"); + return -EINVAL; + } + if (atomic_read(&uci_ctxt.mhi_disabled)) { + uci_log(UCI_DBG_ERROR, + "Client %d attempted to write while MHI is disabled\n", + uci_handle->out_chan); + return -EIO; + } + + if (!mhi_uci_are_channels_connected(uci_handle)) { + uci_log(UCI_DBG_ERROR, "%s:Channels are not connected\n", + __func__); + return -ENODEV; + } + + if (count > TRB_MAX_DATA_SIZE) { + uci_log(UCI_DBG_ERROR, + "Too big write size: %lu, max supported size is %d\n", + count, TRB_MAX_DATA_SIZE); + return -EFBIG; + } + + data_loc = kmalloc(count, GFP_KERNEL); + if (!data_loc) + return -ENOMEM; + + memcpy_result = copy_from_iter_full(data_loc, count, buf); + if (!memcpy_result) { + rc = -EFAULT; + goto error_memcpy; + } + + rc = mhi_uci_send_packet(uci_handle, data_loc, count); + if (rc == count) + return rc; + +error_memcpy: + kfree(data_loc); + return rc; +} + void mhi_uci_chan_state_notify_all(struct mhi_dev *mhi, enum mhi_ctrl_info ch_state) { unsigned int i; const struct chan_attr *chan_attrib; - for (i = 0; i < ARRAY_SIZE(uci_chan_attr_table); i++) { - chan_attrib = &uci_chan_attr_table[i]; + for (i = 0; i < ARRAY_SIZE(mhi_chan_attr_table); i++) { + chan_attrib = &mhi_chan_attr_table[i]; if (chan_attrib->state_bcast) { uci_log(UCI_DBG_ERROR, "Calling notify for ch %d\n", chan_attrib->chan_id); @@ -1877,6 +1942,7 @@ static const struct file_operations mhi_uci_ctrl_client_fops = { static const struct file_operations mhi_uci_client_fops = { .read = mhi_uci_client_read, .write = mhi_uci_client_write, + .write_iter = mhi_uci_client_write_iter, .open = mhi_uci_client_open, .release = mhi_uci_client_release, .poll = mhi_uci_client_poll, @@ -2038,8 +2104,8 @@ static int uci_init_client_attributes(struct mhi_uci_ctxt_t *uci_ctxt) struct uci_client *client; const struct chan_attr *chan_attrib; - for (i = 0; i < ARRAY_SIZE(uci_chan_attr_table); i += 2) { - chan_attrib = &uci_chan_attr_table[i]; + for (i = 0; i < ARRAY_SIZE(mhi_chan_attr_table); i += 2) { + chan_attrib = &mhi_chan_attr_table[i]; index = CHAN_TO_CLIENT(chan_attrib->chan_id); client = &uci_ctxt->client_handles[index]; client->out_chan_attr = chan_attrib; diff --git a/drivers/soc/qcom/icnss2/main.c b/drivers/soc/qcom/icnss2/main.c index 05c4c0499ae9..49d0d8a74a0f 100644 --- a/drivers/soc/qcom/icnss2/main.c +++ b/drivers/soc/qcom/icnss2/main.c @@ -1656,6 +1656,36 @@ static void icnss_update_state_send_modem_shutdown(struct icnss_priv *priv, } } +static char *icnss_subsys_notify_state_to_str(enum subsys_notif_type code) +{ + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + return "BEFORE_SHUTDOWN"; + case SUBSYS_AFTER_SHUTDOWN: + return "AFTER_SHUTDOWN"; + case SUBSYS_BEFORE_POWERUP: + return "BEFORE_POWERUP"; + case SUBSYS_AFTER_POWERUP: + return "AFTER_POWERUP"; + case SUBSYS_BEFORE_AUTH_AND_RESET: + return "BEFORE_AUTH_AND_RESET"; + case SUBSYS_RAMDUMP_NOTIFICATION: + return "RAMDUMP_NOTIFICATION"; + case SUBSYS_POWERUP_FAILURE: + return "POWERUP_FAILURE"; + case SUBSYS_PROXY_VOTE: + return "PROXY_VOTE"; + case SUBSYS_PROXY_UNVOTE: + return "PROXY_UNVOTE"; + case SUBSYS_SOC_RESET: + return "SOC_RESET"; + case SUBSYS_NOTIF_TYPE_COUNT: + return "NOTIF_TYPE_COUNT"; + default: + return "UNKNOWN"; + } +}; + static int icnss_wpss_notifier_nb(struct notifier_block *nb, unsigned long code, void *data) @@ -1666,7 +1696,8 @@ static int icnss_wpss_notifier_nb(struct notifier_block *nb, wpss_ssr_nb); struct icnss_uevent_fw_down_data fw_down_data = {0}; - icnss_pr_vdbg("WPSS-Notify: event %lu\n", code); + icnss_pr_vdbg("WPSS-Notify: event %s(%lu)\n", + icnss_subsys_notify_state_to_str(code), code); if (code == SUBSYS_AFTER_SHUTDOWN) { icnss_pr_info("Collecting msa0 segment dump\n"); @@ -1723,7 +1754,8 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, modem_ssr_nb); struct icnss_uevent_fw_down_data fw_down_data = {0}; - icnss_pr_vdbg("Modem-Notify: event %lu\n", code); + icnss_pr_vdbg("Modem-Notify: event %s(%lu)\n", + icnss_subsys_notify_state_to_str(code), code); if (code == SUBSYS_AFTER_SHUTDOWN) { icnss_pr_info("Collecting msa0 segment dump\n"); diff --git a/drivers/soc/qcom/icnss2/qmi.c b/drivers/soc/qcom/icnss2/qmi.c index 95b58adcf92e..b523a357ec95 100644 --- a/drivers/soc/qcom/icnss2/qmi.c +++ b/drivers/soc/qcom/icnss2/qmi.c @@ -976,6 +976,22 @@ static int icnss_get_bdf_file_name(struct icnss_priv *priv, return ret; } +static char *icnss_bdf_type_to_str(enum icnss_bdf_type bdf_type) +{ + switch (bdf_type) { + case ICNSS_BDF_BIN: + return "BDF"; + case ICNSS_BDF_ELF: + return "BDF"; + case ICNSS_BDF_REGDB: + return "REGDB"; + case ICNSS_BDF_DUMMY: + return "BDF"; + default: + return "UNKNOWN"; + } +}; + int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type) { struct wlfw_bdf_download_req_msg_v01 *req; @@ -987,8 +1003,8 @@ int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type) unsigned int remaining; int ret = 0; - icnss_pr_dbg("Sending BDF download message, state: 0x%lx, type: %d\n", - priv->state, bdf_type); + icnss_pr_dbg("Sending %s download message, state: 0x%lx, type: %d\n", + icnss_bdf_type_to_str(bdf_type), priv->state, bdf_type); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) @@ -1012,7 +1028,8 @@ int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type) ret = request_firmware(&fw_entry, filename, &priv->pdev->dev); if (ret) { - icnss_pr_err("Failed to load BDF: %s\n", filename); + icnss_pr_err("Failed to load %s: %s ret:%d\n", + icnss_bdf_type_to_str(bdf_type), filename, ret); goto err_req_fw; } @@ -1020,7 +1037,8 @@ int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type) remaining = fw_entry->size; bypass_bdf: - icnss_pr_dbg("Downloading BDF: %s, size: %u\n", filename, remaining); + icnss_pr_dbg("Downloading %s: %s, size: %u\n", + icnss_bdf_type_to_str(bdf_type), filename, remaining); while (remaining) { req->valid = 1; @@ -1046,8 +1064,8 @@ bypass_bdf: ret = qmi_txn_init(&priv->qmi, &txn, wlfw_bdf_download_resp_msg_v01_ei, resp); if (ret < 0) { - icnss_pr_err("Failed to initialize txn for BDF download request, err: %d\n", - ret); + icnss_pr_err("Failed to initialize txn for %s download request, err: %d\n", + icnss_bdf_type_to_str(bdf_type), ret); goto err_send; } @@ -1058,21 +1076,22 @@ bypass_bdf: wlfw_bdf_download_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); - icnss_pr_err("Failed to send respond BDF download request, err: %d\n", - ret); + icnss_pr_err("Failed to send respond %s download request, err: %d\n", + icnss_bdf_type_to_str(bdf_type), ret); goto err_send; } ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (ret < 0) { - icnss_pr_err("Failed to wait for response of BDF download request, err: %d\n", - ret); + icnss_pr_err("Failed to wait for response of %s download request, err: %d\n", + icnss_bdf_type_to_str(bdf_type), ret); goto err_send; } if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { - icnss_pr_err("BDF download request failed, result: %d, err: %d\n", - resp->resp.result, resp->resp.error); + icnss_pr_err("%s download request failed, result: %d, err: %d\n", + icnss_bdf_type_to_str(bdf_type), resp->resp.result, + resp->resp.error); ret = -resp->resp.result; goto err_send; } @@ -1250,8 +1269,8 @@ int icnss_wlfw_qdss_dnld_send_sync(struct icnss_priv *priv) ret = request_firmware(&fw_entry, filename, &priv->pdev->dev); if (ret) { - icnss_pr_err("Failed to load QDSS: %s\n", - filename); + icnss_pr_err("Failed to load QDSS: %s ret:%d\n", + filename, ret); goto err_req_fw; } diff --git a/drivers/spi/spi-msm-geni.c b/drivers/spi/spi-msm-geni.c index 954144323254..0b71df4757e6 100644 --- a/drivers/spi/spi-msm-geni.c +++ b/drivers/spi/spi-msm-geni.c @@ -1350,9 +1350,14 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) struct se_geni_rsc *rsc; int ret = 0; - rsc = &mas->spi_rsc; - ret = pinctrl_select_state(rsc->geni_pinctrl, - rsc->geni_gpio_active); + if (!mas->is_la_vm) { + /* Do this only for non TVM LA usecase */ + /* May not be needed here, but maintain parity */ + rsc = &mas->spi_rsc; + ret = pinctrl_select_state(rsc->geni_pinctrl, + rsc->geni_gpio_active); + } + if (ret) GENI_SE_ERR(mas->ipc, false, NULL, "%s: Error %d pinctrl_select_state\n", __func__, ret); @@ -1400,17 +1405,17 @@ static int spi_geni_unprepare_transfer_hardware(struct spi_master *spi) if (mas->shared_ee || mas->is_le_vm) return 0; - if (mas->is_la_vm) - /* Client on LA VM to controls resources, hence return */ - return 0; - if (mas->gsi_mode) { struct se_geni_rsc *rsc; int ret = 0; - rsc = &mas->spi_rsc; - ret = pinctrl_select_state(rsc->geni_pinctrl, + if (!mas->is_la_vm) { + /* Do this only for non TVM LA usecase */ + rsc = &mas->spi_rsc; + ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep); + } + if (ret) GENI_SE_ERR(mas->ipc, false, NULL, "%s: Error %d pinctrl_select_state\n", __func__, ret); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 77afbed72763..3b439bf49bdf 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -490,11 +490,15 @@ struct dwc3_msm { bool suspend; bool use_pdc_interrupts; enum dwc3_id_state id_state; - bool use_pwr_event_for_wakeup; + unsigned long use_pwr_event_for_wakeup; +#define PWR_EVENT_SS_WAKEUP BIT(0) +#define PWR_EVENT_HS_WAKEUP BIT(1) + unsigned long lpm_flags; #define MDWC3_SS_PHY_SUSPEND BIT(0) #define MDWC3_ASYNC_IRQ_WAKE_CAPABILITY BIT(1) #define MDWC3_POWER_COLLAPSE BIT(2) +#define MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP BIT(3) struct extcon_nb *extcon; int ext_idx; @@ -3047,7 +3051,7 @@ static void configure_usb_wakeup_interrupt(struct dwc3_msm *mdwc, } } -static void enable_usb_pdc_interrupt(struct dwc3_msm *mdwc, bool enable) +static void configure_usb_wakeup_interrupts(struct dwc3_msm *mdwc, bool enable) { if (!enable) goto disable_usb_irq; @@ -3131,7 +3135,7 @@ static void configure_nonpdc_usb_interrupt(struct dwc3_msm *mdwc, } } -static void dwc3_msm_set_ss_pwr_events(struct dwc3_msm *mdwc, bool on) +static void dwc3_msm_set_pwr_events(struct dwc3_msm *mdwc, bool on) { u32 irq_mask, irq_stat; @@ -3142,12 +3146,28 @@ static void dwc3_msm_set_ss_pwr_events(struct dwc3_msm *mdwc, bool on) irq_mask = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_MASK_REG); - if (on) - irq_mask |= (PWR_EVNT_POWERDOWN_OUT_P3_MASK | - PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK); - else - irq_mask &= ~(PWR_EVNT_POWERDOWN_OUT_P3_MASK | - PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK); + if (on) { + /* + * In case of platforms which use mpm interrupts, in case where + * suspend happens with a hs/fs/ls device connected in host mode. + * DP/DM falling edge will be monitored, but gic doesn't have + * capability to detect falling edge. So program power event irq + * to notify exit from lpm in such case. + */ + if (mdwc->use_pwr_event_for_wakeup & PWR_EVENT_HS_WAKEUP) + irq_mask |= PWR_EVNT_LPM_OUT_L2_MASK; + if ((mdwc->use_pwr_event_for_wakeup & PWR_EVENT_SS_WAKEUP) + && !(mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND)) + irq_mask |= (PWR_EVNT_POWERDOWN_OUT_P3_MASK | + PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK); + } else { + if (mdwc->use_pwr_event_for_wakeup & PWR_EVENT_HS_WAKEUP) + irq_mask &= ~PWR_EVNT_LPM_OUT_L2_MASK; + if ((mdwc->use_pwr_event_for_wakeup & PWR_EVENT_SS_WAKEUP) + && !(mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND)) + irq_mask &= ~(PWR_EVNT_POWERDOWN_OUT_P3_MASK | + PWR_EVNT_LPM_OUT_RX_ELECIDLE_IRQ_MASK); + } dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_MASK_REG, irq_mask); } @@ -3280,7 +3300,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool force_power_collapse, ((mdwc->hs_phy->flags & (PHY_HSFS_MODE | PHY_LS_MODE)) && !dwc3_msm_is_superspeed(mdwc))); can_suspend_ssphy = dwc->maximum_speed >= USB_SPEED_SUPER && - (!mdwc->use_pwr_event_for_wakeup || no_active_ss || + (!(mdwc->use_pwr_event_for_wakeup & PWR_EVENT_SS_WAKEUP) || no_active_ss || !enable_wakeup); /* Suspend SS PHY */ if (can_suspend_ssphy) { @@ -3306,11 +3326,21 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool force_power_collapse, usb_phy_set_suspend(mdwc->ss_phy, 1); usb_phy_set_suspend(mdwc->ss_phy1, 1); mdwc->lpm_flags |= MDWC3_SS_PHY_SUSPEND; - } else if (mdwc->use_pwr_event_for_wakeup) { - dwc3_msm_set_ss_pwr_events(mdwc, true); - enable_irq(mdwc->wakeup_irq[PWR_EVNT_IRQ].irq); + } else if (mdwc->use_pwr_event_for_wakeup & PWR_EVENT_SS_WAKEUP) { + mdwc->lpm_flags |= MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP; } + /* + * When operating in HS host mode, check if pwr event IRQ is + * required for wakeup. + */ + if (mdwc->in_host_mode && (mdwc->use_pwr_event_for_wakeup + & PWR_EVENT_HS_WAKEUP)) + mdwc->lpm_flags |= MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP; + + if (mdwc->lpm_flags & MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP) + dwc3_msm_set_pwr_events(mdwc, true); + /* make sure above writes are completed before turning off clocks */ wmb(); @@ -3362,11 +3392,13 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool force_power_collapse, /* * with the core in power collapse, we dont require wakeup * using HS_PHY_IRQ or SS_PHY_IRQ. Hence enable wakeup only in - * case of host bus suspend and device bus suspend. + * case of host bus suspend and device bus suspend. Also in + * case of platforms with mpm interrupts and snps phy, enable + * dpse hsphy irq and dmse hsphy irq as done for pdc interrupts. */ if (!(mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) && enable_wakeup) { - if (mdwc->use_pdc_interrupts) { - enable_usb_pdc_interrupt(mdwc, true); + if (mdwc->use_pdc_interrupts || !mdwc->wakeup_irq[HS_PHY_IRQ].irq) { + configure_usb_wakeup_interrupts(mdwc, true); } else { uirq = &mdwc->wakeup_irq[HS_PHY_IRQ]; configure_nonpdc_usb_interrupt(mdwc, uirq, true); @@ -3376,6 +3408,9 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool force_power_collapse, mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY; } + if (mdwc->lpm_flags & MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP) + enable_irq(mdwc->wakeup_irq[PWR_EVNT_IRQ].irq); + dev_info(mdwc->dev, "DWC3 in low power mode\n"); dbg_event(0xFF, "Ctl Sus", atomic_read(&dwc->in_lpm)); @@ -3468,10 +3503,10 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) * Disable any wakeup events that were enabled if pwr_event_irq * is used as wakeup interrupt. */ - if (mdwc->use_pwr_event_for_wakeup && - !(mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND)) { + if (mdwc->lpm_flags & MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP) { disable_irq_nosync(mdwc->wakeup_irq[PWR_EVNT_IRQ].irq); - dwc3_msm_set_ss_pwr_events(mdwc, false); + dwc3_msm_set_pwr_events(mdwc, false); + mdwc->lpm_flags &= ~MDWC3_USE_PWR_EVENT_IRQ_FOR_WAKEUP; } /* Resume SS PHY */ @@ -3542,8 +3577,8 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) /* Disable wakeup capable for HS_PHY IRQ & SS_PHY_IRQ if enabled */ if (mdwc->lpm_flags & MDWC3_ASYNC_IRQ_WAKE_CAPABILITY) { - if (mdwc->use_pdc_interrupts) { - enable_usb_pdc_interrupt(mdwc, false); + if (mdwc->use_pdc_interrupts || !mdwc->wakeup_irq[HS_PHY_IRQ].irq) { + configure_usb_wakeup_interrupts(mdwc, false); } else { uirq = &mdwc->wakeup_irq[HS_PHY_IRQ]; configure_nonpdc_usb_interrupt(mdwc, uirq, false); @@ -3791,6 +3826,13 @@ static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc) irq_clear |= PWR_EVNT_LPM_OUT_L1_MASK; } + /* Handle exit from L2 events */ + if (irq_stat & PWR_EVNT_LPM_OUT_L2_MASK) { + dev_dbg(mdwc->dev, "%s: handling PWR_EVNT_LPM_OUT_L2_MASK\n", + __func__); + irq_stat &= ~PWR_EVNT_LPM_OUT_L2_MASK; + irq_clear |= PWR_EVNT_LPM_OUT_L2_MASK; + } /* Unhandled events */ if (irq_stat) dev_dbg(mdwc->dev, "%s: unexpected PWR_EVNT, irq_stat=%X\n", @@ -4895,8 +4937,17 @@ static int dwc3_msm_probe(struct platform_device *pdev) * On platforms with SS PHY that do not support ss_phy_irq for wakeup * events, use pwr_event_irq for wakeup events in superspeed mode. */ - mdwc->use_pwr_event_for_wakeup = dwc->maximum_speed >= USB_SPEED_SUPER - && !mdwc->wakeup_irq[SS_PHY_IRQ].irq; + if (dwc->maximum_speed >= USB_SPEED_SUPER + && !mdwc->wakeup_irq[SS_PHY_IRQ].irq) + mdwc->use_pwr_event_for_wakeup |= PWR_EVENT_SS_WAKEUP; + + /* + * On platforms with mpm interrupts and snps phy, when operating in + * HS host mode use power event irq for wakeup events as GIC is not + * capable to detect falling edge of dp/dm hsphy irq. + */ + if (!mdwc->use_pdc_interrupts && !mdwc->wakeup_irq[HS_PHY_IRQ].irq) + mdwc->use_pwr_event_for_wakeup |= PWR_EVENT_HS_WAKEUP; /* * Clocks and regulators will not be turned on until the first time diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2edfa2fa4b42..fdc52c8d940e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1866,8 +1866,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) { struct dwc3_gadget_ep_cmd_params params; struct dwc3 *dwc = dep->dwc; - struct dwc3_request *req; - struct dwc3_request *tmp; int ret; if (!dep->endpoint.desc) { @@ -1926,15 +1924,13 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) if (!dep->gsi) { dwc3_stop_active_transfer(dep, true, true); - list_for_each_entry_safe(req, tmp, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(req); + if (!list_empty(&dep->started_list)) + dep->flags |= DWC3_EP_DELAY_START; if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) { dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; return 0; } - - dwc3_gadget_ep_cleanup_cancelled_requests(dep); } ret = dwc3_send_clear_stall_ep_cmd(dep); diff --git a/include/linux/pinctrl/qcom-pinctrl.h b/include/linux/pinctrl/qcom-pinctrl.h index a3629fb09802..14b20ed634da 100644 --- a/include/linux/pinctrl/qcom-pinctrl.h +++ b/include/linux/pinctrl/qcom-pinctrl.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019,2021 The Linux Foundation. All rights reserved. */ #ifndef __LINUX_PINCTRL_MSM_H__ @@ -15,4 +15,8 @@ int msm_qup_read(u32 mode); /* API to write to mpm_wakeup registers */ int msm_gpio_mpm_wake_set(unsigned int gpio, bool enable); +/* APIS to TLMM Spare registers */ +int msm_spare_write(int spare_reg, u32 val); +int msm_spare_read(int spare_reg); + #endif /* __LINUX_PINCTRL_MSM_H__ */ diff --git a/include/soc/qcom/mpm.h b/include/soc/qcom/mpm.h index 49a82f6246a1..6c9cb12cddc8 100644 --- a/include/soc/qcom/mpm.h +++ b/include/soc/qcom/mpm.h @@ -20,4 +20,5 @@ extern const struct mpm_pin mpm_holi_gic_chip_data[]; extern const struct mpm_pin mpm_sdxnightjar_gic_chip_data[]; extern const struct mpm_pin mpm_monaco_gic_chip_data[]; extern const struct mpm_pin mpm_qcs405_gic_chip_data[]; +extern const struct mpm_pin mpm_blair_gic_chip_data[]; #endif /* __QCOM_MPM_H__ */ diff --git a/init/do_mounts.c b/init/do_mounts.c index 9634ecf3743d..fa364cf4c131 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -386,9 +386,24 @@ static void __init get_fs_names(char *page) static int __init do_mount_root(char *name, char *fs, int flags, void *data) { struct super_block *s; - int err = ksys_mount(name, "/root", fs, flags, data); - if (err) - return err; + char *data_page = NULL; + struct page *p = NULL; + int ret; + + if (data) { + /* do_mount() requires a full page as fifth argument */ + p = alloc_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + + data_page = page_address(p); + /* zero-pad. do_mount() will make sure it's terminated */ + strscpy(data_page, data, PAGE_SIZE); + } + + ret = ksys_mount(name, "/root", fs, flags, data_page); + if (ret) + goto out; ksys_chdir("/root"); s = current->fs->pwd.dentry->d_sb; @@ -398,7 +413,11 @@ static int __init do_mount_root(char *name, char *fs, int flags, void *data) s->s_type->name, sb_rdonly(s) ? " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); - return 0; + +out: + if (p) + put_page(p); + return ret; } void __init mount_block_root(char *name, int flags) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 20e5b74c7723..ba9574694f98 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -2187,6 +2187,7 @@ static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1 static void __exit nf_conntrack_sip_fini(void) { nf_conntrack_helpers_unregister(sip, ports_c * 4); + unregister_net_sysctl_table(sip_sysctl_header); } static int __init nf_conntrack_sip_init(void) diff --git a/net/qrtr/Kconfig b/net/qrtr/Kconfig index 78c078284532..0fde52dbc565 100644 --- a/net/qrtr/Kconfig +++ b/net/qrtr/Kconfig @@ -68,4 +68,13 @@ config QRTR_HAVEN Router communication between two virtual machines. The transport uses dynamically shared memory and haven doorbells. +config QRTR_MHI_DEV + tristate "MHI Device IPC Router channels" + depends on MSM_MHI_DEV || (COMPILE_TEST && MSM_MHI_DEV=n) + help + Say Y here to support MHI base ipcrouter channels for device + endpoint mode. MHI is the transport used for external modem + connections. This driver enables QRTR to run on the modem device + side. + endif # QRTR diff --git a/net/qrtr/Makefile b/net/qrtr/Makefile index dfca913847d4..ecb9b5a29c58 100644 --- a/net/qrtr/Makefile +++ b/net/qrtr/Makefile @@ -7,5 +7,7 @@ obj-$(CONFIG_QRTR_TUN) += qrtr-tun.o qrtr-tun-y := tun.o obj-$(CONFIG_QRTR_MHI) += qrtr-mhi.o qrtr-mhi-y := mhi.o +obj-$(CONFIG_QRTR_MHI_DEV) += qrtr-mhi-dev.o +qrtr-mhi-dev-y := mhi_dev.o obj-$(CONFIG_QRTR_HAVEN) += qrtr-haven.o qrtr-haven-y := haven.o diff --git a/net/qrtr/mhi_dev.c b/net/qrtr/mhi_dev.c new file mode 100644 index 000000000000..3f6091c1656b --- /dev/null +++ b/net/qrtr/mhi_dev.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "qrtr.h" + +#define QRTR_MAX_PKT_SIZE SZ_32K + +/* MHI DEV Enums are defined from Host perspective */ +#define QRTR_MHI_DEV_OUT MHI_CLIENT_IPCR_IN +#define QRTR_MHI_DEV_IN MHI_CLIENT_IPCR_OUT + +/** + * struct qrtr_mhi_dev_ep - qrtr mhi device endpoint + * @ep: endpoint + * @dev: device from platform bus + * @out: channel handle from mhi dev + * @out_tre: complete when channel is ready to send + * @out_lock: hold when resetting completion variable + * @in: channel handle from mhi dev + * @buf_in: buffer to hold incoming data + * @net_id: subnet id used by qrtr core + * @rt: realtime option used by qrtr core + */ +struct qrtr_mhi_dev_ep { + struct qrtr_endpoint ep; + struct device *dev; + struct mhi_dev_client *out; + struct completion out_tre; + struct mutex out_lock; /* for out critical sections */ + struct mhi_dev_client *in; + void *buf_in; + + u32 net_id; + bool rt; +}; + +static struct qrtr_mhi_dev_ep *qrtr_mhi_device_endpoint; + +static int qrtr_mhi_dev_send(struct qrtr_endpoint *ep, struct sk_buff *skb) +{ + struct qrtr_mhi_dev_ep *qep; + struct mhi_req req = { 0 }; + int rc; + + qep = container_of(ep, struct qrtr_mhi_dev_ep, ep); + rc = skb_linearize(skb); + if (rc) { + kfree_skb(skb); + return rc; + } + + req.chan = QRTR_MHI_DEV_OUT; + req.client = qep->out; + req.mode = DMA_SYNC; + req.buf = skb->data; + req.len = skb->len; + + do { + wait_for_completion(&qep->out_tre); + + mutex_lock(&qep->out_lock); + rc = mhi_dev_write_channel(&req); + if (rc == 0) + reinit_completion(&qep->out_tre); + mutex_unlock(&qep->out_lock); + } while (!rc); + + if (rc != skb->len) { + dev_err(qep->dev, "send failed rc:%d len:%d\n", rc, skb->len); + kfree_skb(skb); + return rc; + } + + consume_skb(skb); + return 0; +} + +static void qrtr_mhi_dev_read(struct qrtr_mhi_dev_ep *qep) +{ + struct mhi_req req = { 0 }; + int rc; + int bytes_read; + + req.chan = QRTR_MHI_DEV_IN; + req.client = qep->in; + req.mode = DMA_SYNC; + req.buf = qep->buf_in; + req.len = QRTR_MAX_PKT_SIZE; + + do { + bytes_read = mhi_dev_read_channel(&req); + if (bytes_read < 0) { + dev_err(qep->dev, "failed to read channel %d\n", + bytes_read); + return; + } + if (bytes_read == 0) + return; + + rc = qrtr_endpoint_post(&qep->ep, req.buf, req.transfer_len); + if (rc == -EINVAL) + dev_err(qep->dev, "invalid ipcrouter packet\n"); + } while (bytes_read > 0); +} + +static void qrtr_mhi_dev_event_cb(struct mhi_dev_client_cb_reason *reason) +{ + struct qrtr_mhi_dev_ep *qep; + + qep = qrtr_mhi_device_endpoint; + if (!qep) + return; + + if (reason->reason == MHI_DEV_TRE_AVAILABLE) { + pr_debug("TRE available event for chan %d\n", reason->ch_id); + if (reason->ch_id == QRTR_MHI_DEV_IN) { + qrtr_mhi_dev_read(qep); + } else { + mutex_lock(&qep->out_lock); + complete_all(&qep->out_tre); + mutex_unlock(&qep->out_lock); + } + } +} + +static int qrtr_mhi_dev_open_channels(struct qrtr_mhi_dev_ep *qep) +{ + int rc; + + /* write channel */ + rc = mhi_dev_open_channel(QRTR_MHI_DEV_IN, &qep->in, + qrtr_mhi_dev_event_cb); + if (rc < 0) + return rc; + + /* read channel */ + rc = mhi_dev_open_channel(QRTR_MHI_DEV_OUT, &qep->out, + qrtr_mhi_dev_event_cb); + if (rc < 0) { + mhi_dev_close_channel(qep->in); + return rc; + } + return 0; +} + +static void qrtr_mhi_dev_close_channels(struct qrtr_mhi_dev_ep *qep) +{ + + mhi_dev_close_channel(qep->in); + mhi_dev_close_channel(qep->out); +} + +static void qrtr_mhi_dev_state_cb(struct mhi_dev_client_cb_data *cb_data) +{ + struct qrtr_mhi_dev_ep *qep; + int rc; + + if (!cb_data || !cb_data->user_data) + return; + qep = cb_data->user_data; + + switch (cb_data->ctrl_info) { + case MHI_STATE_CONNECTED: + rc = qrtr_mhi_dev_open_channels(qep); + if (rc) { + dev_err(qep->dev, "open failed %d\n", rc); + return; + } + + rc = qrtr_endpoint_register(&qep->ep, qep->net_id, qep->rt); + if (rc) { + dev_err(qep->dev, "register failed %d\n", rc); + qrtr_mhi_dev_close_channels(qep); + } + break; + case MHI_STATE_DISCONNECTED: + qrtr_endpoint_unregister(&qep->ep); + qrtr_mhi_dev_close_channels(qep); + break; + default: + break; + } +} + +static int qrtr_mhi_dev_probe(struct platform_device *pdev) +{ + struct qrtr_mhi_dev_ep *qep; + struct device_node *node; + int rc; + struct mhi_dev_client_cb_data cb_data; + + qep = devm_kzalloc(&pdev->dev, sizeof(*qep), GFP_KERNEL); + if (!qep) + return -ENOMEM; + qep->dev = &pdev->dev; + + node = pdev->dev.of_node; + rc = of_property_read_u32(node, "qcom,net-id", &qep->net_id); + if (rc < 0) + qep->net_id = QRTR_EP_NET_ID_AUTO; + qep->rt = of_property_read_bool(node, "qcom,low-latency"); + + qep->buf_in = devm_kzalloc(&pdev->dev, QRTR_MAX_PKT_SIZE, GFP_KERNEL); + if (!qep->buf_in) + return -ENOMEM; + + qrtr_mhi_device_endpoint = qep; + + mutex_init(&qep->out_lock); + init_completion(&qep->out_tre); + qep->ep.xmit = qrtr_mhi_dev_send; + /* HOST init TX first followed by RX, so register for endpoint TX + * which makes both channel ready by checking one channel state. + */ + rc = mhi_register_state_cb(qrtr_mhi_dev_state_cb, qep, + QRTR_MHI_DEV_OUT); + if (rc == -EEXIST) { + /** + * MHI stack will return -EEXIST if mhi channel is already + * opend by the host and will not invoke reqistered callback. + * But future state change notification will inform through + * registered callback. + */ + complete_all(&qep->out_tre); + cb_data.user_data = (void *)qep; + cb_data.channel = QRTR_MHI_DEV_OUT; + cb_data.ctrl_info = MHI_STATE_CONNECTED; + qrtr_mhi_dev_state_cb(&cb_data); + } else if (rc) { + return rc; + } + + return 0; +} + +static const struct of_device_id qrtr_mhi_dev_match_table[] = { + { .compatible = "qcom,qrtr-mhi-dev"}, + {}, +}; + +static struct platform_driver qrtr_mhi_dev_driver = { + .probe = qrtr_mhi_dev_probe, + .driver = { + .name = "qrtr_mhi_dev", + .of_match_table = qrtr_mhi_dev_match_table, + }, +}; +module_platform_driver(qrtr_mhi_dev_driver); + +MODULE_DESCRIPTION("QTI IPC-Router MHI device interface driver"); +MODULE_LICENSE("GPL v2"); diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index d138ca1d27bc..845f21c8e1d0 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -227,24 +227,26 @@ static int announce_servers(struct sockaddr_qrtr *sq) struct qrtr_server *srv; struct qrtr_node *node; unsigned long index; + unsigned long node_idx; int ret; - node = node_get(qrtr_ns.local_node); - if (!node) - return 0; - /* Announce the list of servers registered in this node */ - xa_for_each(&node->servers, index, srv) { - ret = service_announce_new(sq, srv); - if (ret < 0) { - if (ret == -ENODEV) - continue; + xa_for_each(&nodes, node_idx, node) { + if (node->id == sq->sq_node) { + pr_info("Avoiding duplicate announce for NODE ID %u\n", node->id); + continue; + } + xa_for_each(&node->servers, index, srv) { + ret = service_announce_new(sq, srv); + if (ret < 0) { + if (ret == -ENODEV) + continue; - pr_err("failed to announce new service %d\n", ret); - return ret; + pr_err("failed to announce new service %d\n", ret); + return ret; + } } } - return 0; } diff --git a/usr/include/Makefile b/usr/include/Makefile index bfc7f8eda7d4..95e0da5500dd 100644 --- a/usr/include/Makefile +++ b/usr/include/Makefile @@ -63,6 +63,20 @@ header-test- += xen/evtchn.h header-test- += xen/gntdev.h header-test- += xen/privcmd.h +header-test- += camera/media/cam_jpeg.h +header-test- += camera/media/cam_icp.h +header-test- += camera/media/cam_isp.h +header-test- += camera/media/cam_cpas.h +header-test- += camera/media/cam_sensor.h +header-test- += camera/media/cam_fd.h +header-test- += camera/media/cam_tfe.h +header-test- += camera/media/cam_custom.h +header-test- += camera/media/cam_ope.h +header-test- += camera/media/cam_sync.h +header-test- += camera/media/cam_req_mgr.h +header-test- += camera/media/cam_lrme.h +header-test- += camera/media/cam_defs.h + header-test- += ais/media/ais_isp.h header-test- += ais/media/cam_lrme.h header-test- += ais/media/cam_cpas.h