Merge 774e901388 on remote branch

Change-Id: Ibc8c93267cfdd3c78c80d62bdb38a2b10607f62f
This commit is contained in:
Linux Build Service Account 2021-07-09 08:49:52 -07:00
commit b82eb1e1fa
35 changed files with 1029 additions and 219 deletions

View file

@ -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

View file

@ -5,7 +5,6 @@
#define pr_fmt(fmt) "cpufreq_hw_debug: %s: " fmt, __func__
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
@ -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;
}

View file

@ -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);

View file

@ -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",

View file

@ -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 */

View file

@ -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;

View file

@ -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

View file

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <soc/qcom/mpm.h>
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},
};

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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();
}
}
}

View file

@ -36,6 +36,7 @@
#include <linux/types.h>
#include <linux/uaccess.h>
#include <soc/qcom/subsystem_notif.h>
#include <linux/pinctrl/qcom-pinctrl.h>
#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);

View file

@ -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)

View file

@ -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

View file

@ -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;
};

View file

@ -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

View file

@ -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)
{

View file

@ -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;

View file

@ -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");
}
}

View file

@ -8,6 +8,7 @@
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/sched.h>
@ -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;

View file

@ -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");

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

266
net/qrtr/mhi_dev.c Normal file
View file

@ -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 <linux/platform_device.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/of.h>
#include <linux/msm_mhi_dev.h>
#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");

View file

@ -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;
}

View file

@ -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