Merge "thermal: virtual-sensor: Add trace event for virtual sensor"
diff --git a/Documentation/devicetree/bindings/arm/msm/wil6210.txt b/Documentation/devicetree/bindings/arm/msm/wil6210.txt
index 54bbf25..0c75cf6 100644
--- a/Documentation/devicetree/bindings/arm/msm/wil6210.txt
+++ b/Documentation/devicetree/bindings/arm/msm/wil6210.txt
@@ -32,7 +32,7 @@
- clocks : List of phandle and clock specifier pairs
- clock-names : List of clock input name strings sorted in the same
order as the clocks property.
-- qcom,keep_radio_on_during_sleep: Boolean flag to indicate if to suspend to d3hot
+- qcom,keep-radio-on-during-sleep: Boolean flag to indicate if to suspend to d3hot
instead of turning off the device
Example:
@@ -58,6 +58,6 @@
clocks = <&clock_gcc clk_rf_clk3>,
<&clock_gcc clk_rf_clk3_pin>;
clock-names = "rf_clk3_clk", "rf_clk3_pin_clk";
- qcom,keep_radio_on_during_sleep;
+ qcom,keep-radio-on-during-sleep;
};
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
index a61bab3..62a51cf 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
@@ -104,6 +104,17 @@
Please refer Documentation/devicetree/bindings/arm/msm/msm_bus.txt
for the properties above.
+- vdd-corners
+ Usage: required
+ Value type: <u32>
+ Definition: List of vdd corners to map for ahb level.
+
+- vdd-corner-ahb-mapping
+ Usage: required
+ Value type: <string>
+ Definition: List of ahb level strings corresponds to vdd-corners.
+ Supported strings: suspend, svs, nominal, turbo
+
- client-id-based
Usage: required
Value type: <empty>
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index 07ec82f..441d771 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -176,6 +176,12 @@
Definition: Specifies the maximum charger buck/boost switching frequency in
KHz. It overrides the max frequency defined for the charger.
+- qcom,otg-deglitch-time-ms
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies the deglitch interval for OTG detection.
+ If the value is not present, 50 msec is used as default.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index fc0d3b0..40289a8 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -270,6 +270,8 @@
CONFIG_MSM_SMP2P=y
CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_PWM=y
CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_ANDROID=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 9d12771..d91f5f6 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -263,6 +263,8 @@
CONFIG_MSM_BOOT_STATS=y
CONFIG_TRACER_PKT=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
CONFIG_PWM=y
CONFIG_QCOM_SHOW_RESUME_IRQ=y
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index aaef335..02f1cbb 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -366,6 +366,21 @@
MSM_BUS_SLAVE_CAMERA_CFG 0 640000>,
<MSM_BUS_MASTER_AMPSS_M0
MSM_BUS_SLAVE_CAMERA_CFG 0 640000>;
+ vdd-corners = <RPMH_REGULATOR_LEVEL_OFF
+ RPMH_REGULATOR_LEVEL_RETENTION
+ RPMH_REGULATOR_LEVEL_MIN_SVS
+ RPMH_REGULATOR_LEVEL_LOW_SVS
+ RPMH_REGULATOR_LEVEL_SVS
+ RPMH_REGULATOR_LEVEL_SVS_L1
+ RPMH_REGULATOR_LEVEL_NOM
+ RPMH_REGULATOR_LEVEL_NOM_L1
+ RPMH_REGULATOR_LEVEL_NOM_L2
+ RPMH_REGULATOR_LEVEL_TURBO
+ RPMH_REGULATOR_LEVEL_TURBO_L1>;
+ vdd-corner-ahb-mapping = "suspend", "suspend",
+ "svs", "svs", "svs", "svs",
+ "nominal", "nominal", "nominal",
+ "turbo", "turbo";
client-id-based;
client-names =
"csiphy0", "csiphy1", "csiphy2", "cci0",
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 4943da8..41a6f9f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -480,9 +480,14 @@
#size-cells = <2>;
ranges;
- removed_regions: removed_regions@85800000 {
+ removed_region1: removed_region1@85700000 {
no-map;
- reg = <0 0x85800000 0 0x3700000>;
+ reg = <0 0x85700000 0 0x800000>;
+ };
+
+ removed_region2: removed_region2@85fc0000 {
+ no-map;
+ reg = <0 0x85fc0000 0 0x2f40000>;
};
pil_camera_mem: camera_region@8ab00000 {
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index b92953a..f10047f 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -9,6 +9,8 @@
CONFIG_SCHED_WALT=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 5fd2d84..4cd202c 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -12,6 +12,8 @@
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/drivers/gpu/drm/msm/msm_evtlog.c b/drivers/gpu/drm/msm/msm_evtlog.c
deleted file mode 100644
index dbe9b88..0000000
--- a/drivers/gpu/drm/msm/msm_evtlog.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/* Copyright (c) 2016, 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.
- */
-
-#define pr_fmt(fmt) "msm_evtlog:[%s] " fmt, __func__
-
-#include "msm_evtlog.h"
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <asm-generic/current.h>
-#include <linux/uaccess.h>
-#include <linux/debugfs.h>
-
-#include "sde_trace.h"
-
-#define SIZE_MASK(x) (x - 1)
-
-static int msm_evtlog_debugfs_dump(struct seq_file *s, void *data)
-{
- struct msm_evtlog *log = s->private;
- unsigned long cnt; /* # of samples since clear */
- unsigned long n; /* # of samples to print, also head index */
- unsigned long i;
- struct timespec timespec;
-
- /**
- * Prints in chronological order, oldest -> newest
- * Note due to lock-less design, the first few printed entries
- * may be corrupted by new writer not oldest.
- * This is a tradeoff for speed of sampling
- */
- cnt = atomic_read(&log->cnt);
- if (!cnt)
- return 0;
-
- n = cnt & SIZE_MASK(log->size);
-
- /**
- * If not full, print from first log
- * (which is index 1 since atomic_inc_return is prefix operator)
- */
- i = (cnt < log->size) ? 0 : n;
-
- seq_puts(s, "time_ns, pid, func, line, val1, val2, msg\n");
- do {
- i = (i + 1) & SIZE_MASK(log->size);
- timespec = ktime_to_timespec(log->events[i].ktime);
- seq_printf(s, "[%5lu.%06lu], %d, %s, %d, %llu, %llu, %s\n",
- timespec.tv_sec,
- timespec.tv_nsec / 1000,
- log->events[i].pid,
- log->events[i].func,
- log->events[i].line,
- log->events[i].val1,
- log->events[i].val2,
- log->events[i].msg);
- } while (i != n);
-
- return 0;
-}
-
-static int msm_evtlog_debugfs_open_dump(struct inode *inode, struct file *file)
-{
- return single_open(file, msm_evtlog_debugfs_dump, inode->i_private);
-}
-
-static ssize_t msm_evtlog_debugfs_write(
- struct file *file,
- const char __user *user_buf,
- size_t size,
- loff_t *ppos)
-{
- struct seq_file *s = file->private_data;
- struct msm_evtlog *log = s->private;
- char buf[64];
- int buf_size;
-
- buf_size = min(size, (sizeof(buf) - 1));
- if (strncpy_from_user(buf, user_buf, buf_size) < 0)
- return -EFAULT;
- buf[buf_size] = 0;
-
- if (strcmp(buf, "0") == 0)
- atomic_set(&log->cnt, 0);
-
- return size;
-
-}
-
-static const struct file_operations msm_evtlog_fops = {
- .open = msm_evtlog_debugfs_open_dump,
- .read = seq_read,
- .write = msm_evtlog_debugfs_write,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-int msm_evtlog_init(
- struct msm_evtlog *log,
- int size,
- struct dentry *parent)
-{
- if (!log || size < 1) {
- pr_err("Invalid params\n");
- return -EINVAL;
- }
-
- memset(log, 0, sizeof(*log));
- log->size = roundup_pow_of_two(size);
- log->events = kcalloc(log->size, sizeof(struct msm_evtlog_evt),
- GFP_KERNEL);
-
- if (!log->events) {
- pr_err("Insufficient memory\n");
- return -ENOMEM;
- }
-
- atomic_set(&log->cnt, 0);
-
- log->dentry = debugfs_create_file("evtlog", 0644, parent,
- log, &msm_evtlog_fops);
-
- if (IS_ERR_OR_NULL(log->dentry)) {
- int rc = PTR_ERR(log->dentry);
-
- pr_err("debugfs create file failed, rc=%d\n", rc);
- kfree(log->events);
- return rc;
- }
-
- return 0;
-}
-
-void msm_evtlog_destroy(struct msm_evtlog *log)
-{
- debugfs_remove(log->dentry);
-
- /* Caller needs to make sure that log sampling has stopped */
- kfree(log->events);
-
-}
-
-void msm_evtlog_sample(
- struct msm_evtlog *log,
- const char *func,
- const char *msg,
- uint64_t val1,
- uint64_t val2,
- uint32_t line)
-{
- unsigned long i;
-
- /**
- * Since array sized with pow of 2, roll to 0 when cnt overflows
- * mod the value with the size to get current idx into array
- */
- i = (unsigned long)(atomic_inc_return(&log->cnt)) &
- SIZE_MASK(log->size);
- log->events[i].ktime = ktime_get();
- log->events[i].func = func;
- log->events[i].msg = msg;
- log->events[i].val1 = val1;
- log->events[i].val2 = val2;
- log->events[i].line = line;
- log->events[i].pid = current->pid;
-
- trace_sde_evtlog(func, line, val1, val2);
-}
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index f0aad08..ed25f30 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -80,8 +80,6 @@
unsigned dm_get_md_type(struct mapped_device *md);
struct target_type *dm_get_immutable_target_type(struct mapped_device *md);
-int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t);
-
/*
* To check the return value from dm_table_find_target().
*/
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
index 4f246e1..8d9f4a5 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
@@ -628,9 +628,46 @@
return rc;
}
-static int cam_cpas_util_apply_client_ahb_vote(struct cam_cpas *cpas_core,
+static int cam_cpas_util_get_ahb_level(struct cam_hw_info *cpas_hw,
+ struct device *dev, unsigned long freq, enum cam_vote_level *req_level)
+{
+ struct cam_cpas_private_soc *soc_private =
+ (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
+ struct dev_pm_opp *opp;
+ unsigned int corner;
+ enum cam_vote_level level = CAM_SVS_VOTE;
+ unsigned long corner_freq = freq;
+ int i;
+
+ if (!dev || !req_level) {
+ pr_err("Invalid params %pK, %pK\n", dev, req_level);
+ return -EINVAL;
+ }
+
+ opp = dev_pm_opp_find_freq_ceil(dev, &corner_freq);
+ if (IS_ERR(opp)) {
+ pr_err("Error on OPP freq :%ld, %pK\n", corner_freq, opp);
+ return -EINVAL;
+ }
+
+ corner = dev_pm_opp_get_voltage(opp);
+
+ for (i = 0; i < soc_private->num_vdd_ahb_mapping; i++)
+ if (corner == soc_private->vdd_ahb[i].vdd_corner)
+ level = soc_private->vdd_ahb[i].ahb_level;
+
+ CPAS_CDBG("From OPP table : freq=[%ld][%ld], corner=%d, level=%d\n",
+ freq, corner_freq, corner, level);
+
+ *req_level = level;
+
+ return 0;
+}
+
+static int cam_cpas_util_apply_client_ahb_vote(struct cam_hw_info *cpas_hw,
struct cam_cpas_client *cpas_client, struct cam_ahb_vote *ahb_vote)
{
+ struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
struct cam_cpas_bus_client *ahb_bus_client = &cpas_core->ahb_bus_client;
enum cam_vote_level required_level;
enum cam_vote_level highest_level;
@@ -642,12 +679,14 @@
}
if (ahb_vote->type == CAM_VOTE_DYNAMIC) {
- pr_err("Dynamic AHB vote not supported\n");
- return -EINVAL;
+ rc = cam_cpas_util_get_ahb_level(cpas_hw, cpas_client->data.dev,
+ ahb_vote->vote.freq, &required_level);
+ if (rc)
+ return rc;
+ } else {
+ required_level = ahb_vote->vote.level;
}
- required_level = ahb_vote->vote.level;
-
if (cpas_client->ahb_level == required_level)
return 0;
@@ -708,7 +747,7 @@
ahb_vote->vote.freq,
cpas_core->cpas_client[client_indx]->ahb_level);
- rc = cam_cpas_util_apply_client_ahb_vote(cpas_core,
+ rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw,
cpas_core->cpas_client[client_indx], ahb_vote);
unlock_client:
@@ -780,7 +819,7 @@
CPAS_CDBG("AHB :client[%d] type[%d], level[%d], applied[%d]\n",
client_indx, ahb_vote->type, ahb_vote->vote.level,
cpas_client->ahb_level);
- rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client,
+ rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
ahb_vote);
if (rc)
goto done;
@@ -800,8 +839,8 @@
goto done;
}
- if (cpas_core->internal_ops.power_on_settings) {
- rc = cpas_core->internal_ops.power_on_settings(cpas_hw);
+ if (cpas_core->internal_ops.power_on) {
+ rc = cpas_core->internal_ops.power_on(cpas_hw);
if (rc) {
cam_cpas_soc_disable_resources(
&cpas_hw->soc_info);
@@ -873,6 +912,15 @@
cpas_core->streamon_clients--;
if (cpas_core->streamon_clients == 0) {
+ if (cpas_core->internal_ops.power_off) {
+ rc = cpas_core->internal_ops.power_off(cpas_hw);
+ if (rc) {
+ pr_err("failed in power_off settings rc=%d\n",
+ rc);
+ /* Do not return error, passthrough */
+ }
+ }
+
rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
if (rc) {
pr_err("disable_resorce failed, rc=%d\n", rc);
@@ -883,7 +931,7 @@
ahb_vote.type = CAM_VOTE_ABSOLUTE;
ahb_vote.vote.level = CAM_SUSPEND_VOTE;
- rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client,
+ rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
&ahb_vote);
if (rc)
goto done;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
index c181302..52649ec 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
@@ -45,7 +45,8 @@
* @init_hw_version: Function pointer for hw init based on version
* @handle_irq: Function poniter for irq handling
* @setup_regbase: Function pointer for setup rebase indices
- * @power_on_settings: Function pointer for hw core specific power on settings
+ * @power_on: Function pointer for hw core specific power on settings
+ * @power_off: Function pointer for hw core specific power off settings
*
*/
struct cam_cpas_internal_ops {
@@ -56,7 +57,8 @@
irqreturn_t (*handle_irq)(int irq_num, void *data);
int (*setup_regbase)(struct cam_hw_soc_info *soc_info,
int32_t regbase_index[], int32_t num_reg_map);
- int (*power_on_settings)(struct cam_hw_info *cpas_hw);
+ int (*power_on)(struct cam_hw_info *cpas_hw);
+ int (*power_off)(struct cam_hw_info *cpas_hw);
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h
index d2c3e06..9ee5a43 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h
@@ -29,6 +29,13 @@
#define BITS_MASK_SHIFT(x, mask, shift) (((x) & (mask)) >> shift)
+/* Number of times to retry while polling */
+#define CAM_CPAS_POLL_RETRY_CNT 5
+/* Minimum usecs to sleep while polling */
+#define CAM_CPAS_POLL_MIN_USECS 200
+/* Maximum usecs to sleep while polling */
+#define CAM_CPAS_POLL_MAX_USECS 250
+
/**
* enum cam_cpas_hw_type - Enum for CPAS HW type
*/
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
index 0a8e6bb..0c71ece 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
@@ -22,6 +22,26 @@
#include "cam_cpas_hw.h"
#include "cam_cpas_soc.h"
+static int cam_cpas_get_vote_level_from_string(const char *string,
+ enum cam_vote_level *vote_level)
+{
+ if (!vote_level || !string)
+ return -EINVAL;
+
+ if (strnstr("suspend", string, strlen(string)))
+ *vote_level = CAM_SUSPEND_VOTE;
+ else if (strnstr("svs", string, strlen(string)))
+ *vote_level = CAM_SVS_VOTE;
+ else if (strnstr("nominal", string, strlen(string)))
+ *vote_level = CAM_NOMINAL_VOTE;
+ else if (strnstr("turbo", string, strlen(string)))
+ *vote_level = CAM_TURBO_VOTE;
+ else
+ *vote_level = CAM_SVS_VOTE;
+
+ return 0;
+}
+
int cam_cpas_get_custom_dt_info(struct platform_device *pdev,
struct cam_cpas_private_soc *soc_private)
{
@@ -89,6 +109,42 @@
soc_private->axi_camnoc_based = of_property_read_bool(of_node,
"client-bus-camnoc-based");
+ count = of_property_count_u32_elems(of_node, "vdd-corners");
+ if ((count > 0) && (count <= CAM_REGULATOR_LEVEL_MAX) &&
+ (of_property_count_strings(of_node, "vdd-corner-ahb-mapping") ==
+ count)) {
+ const char *ahb_string;
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_u32_index(of_node, "vdd-corners",
+ i, &soc_private->vdd_ahb[i].vdd_corner);
+ if (rc) {
+ pr_err("vdd-corners failed at index=%d\n", i);
+ return -ENODEV;
+ }
+
+ rc = of_property_read_string_index(of_node,
+ "vdd-corner-ahb-mapping", i, &ahb_string);
+ if (rc) {
+ pr_err("no ahb-mapping at index=%d\n", i);
+ return -ENODEV;
+ }
+
+ rc = cam_cpas_get_vote_level_from_string(ahb_string,
+ &soc_private->vdd_ahb[i].ahb_level);
+ if (rc) {
+ pr_err("invalid ahb-string at index=%d\n", i);
+ return -EINVAL;
+ }
+
+ CPAS_CDBG("Vdd-AHB mapping [%d] : [%d] [%s] [%d]\n", i,
+ soc_private->vdd_ahb[i].vdd_corner,
+ ahb_string, soc_private->vdd_ahb[i].ahb_level);
+ }
+
+ soc_private->num_vdd_ahb_mapping = count;
+ }
+
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
index fdd9386..d3dfbbd 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
@@ -16,6 +16,19 @@
#include "cam_soc_util.h"
#define CAM_CPAS_MAX_CLIENTS 20
+#define CAM_REGULATOR_LEVEL_MAX 16
+
+/**
+ * struct cam_cpas_vdd_ahb_mapping : Voltage to ahb level mapping
+ *
+ * @vdd_corner : Voltage corner value
+ * @ahb_level : AHB vote level corresponds to this vdd_corner
+ *
+ */
+struct cam_cpas_vdd_ahb_mapping {
+ unsigned int vdd_corner;
+ enum cam_vote_level ahb_level;
+};
/**
* struct cam_cpas_private_soc : CPAS private DT info
@@ -27,6 +40,8 @@
* @axi_camnoc_based: Whether AXi access is camnoc based
* @client_axi_port_name: AXI Port name for each client
* @axi_port_list_node : Node representing AXI Ports list
+ * @num_vdd_ahb_mapping : Number of vdd to ahb level mapping supported
+ * @vdd_ahb : AHB level mapping info for the supported vdd levels
*
*/
struct cam_cpas_private_soc {
@@ -37,6 +52,8 @@
bool axi_camnoc_based;
const char *client_axi_port_name[CAM_CPAS_MAX_CLIENTS];
struct device_node *axi_port_list_node;
+ uint32_t num_vdd_ahb_mapping;
+ struct cam_cpas_vdd_ahb_mapping vdd_ahb[CAM_REGULATOR_LEVEL_MAX];
};
int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info,
diff --git a/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c
index fa8ab89..95e26c5 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c
@@ -81,7 +81,8 @@
internal_ops->init_hw_version = NULL;
internal_ops->handle_irq = NULL;
internal_ops->setup_regbase = cam_camsstop_setup_regbase_indices;
- internal_ops->power_on_settings = NULL;
+ internal_ops->power_on = NULL;
+ internal_ops->power_off = NULL;
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
index 415de47..d26c2b6 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
@@ -230,7 +230,7 @@
return IRQ_HANDLED;
}
-static int cam_cpastop_static_settings(struct cam_hw_info *cpas_hw)
+static int cam_cpastop_poweron(struct cam_hw_info *cpas_hw)
{
int i;
@@ -256,6 +256,38 @@
return 0;
}
+static int cam_cpastop_poweroff(struct cam_hw_info *cpas_hw)
+{
+ struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+ struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
+ int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
+ int rc = 0;
+ struct cam_cpas_hw_errata_wa_list *errata_wa_list =
+ camnoc_info->errata_wa_list;
+
+ if (!errata_wa_list)
+ return 0;
+
+ if (errata_wa_list->camnoc_flush_slave_pending_trans.enable) {
+ struct cam_cpas_hw_errata_wa *errata_wa =
+ &errata_wa_list->camnoc_flush_slave_pending_trans;
+
+ rc = cam_io_poll_value_wmask(
+ soc_info->reg_map[camnoc_index].mem_base +
+ errata_wa->data.reg_info.offset,
+ errata_wa->data.reg_info.value,
+ errata_wa->data.reg_info.mask,
+ CAM_CPAS_POLL_RETRY_CNT,
+ CAM_CPAS_POLL_MIN_USECS, CAM_CPAS_POLL_MAX_USECS);
+ if (rc) {
+ pr_err("camnoc flush slave pending trans failed\n");
+ /* Do not return error, passthrough */
+ }
+ }
+
+ return rc;
+}
+
static int cam_cpastop_init_hw_version(struct cam_hw_info *cpas_hw,
struct cam_cpas_hw_caps *hw_caps)
{
@@ -295,7 +327,8 @@
internal_ops->init_hw_version = cam_cpastop_init_hw_version;
internal_ops->handle_irq = cam_cpastop_handle_irq;
internal_ops->setup_regbase = cam_cpastop_setup_regbase_indices;
- internal_ops->power_on_settings = cam_cpastop_static_settings;
+ internal_ops->power_on = cam_cpastop_poweron;
+ internal_ops->power_off = cam_cpastop_poweroff;
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
index 99aae3f..c61204a 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
@@ -147,6 +147,31 @@
};
/**
+ * struct cam_cpas_hw_errata_wa : Struct for HW errata workaround info
+ *
+ * @enable: Whether to enable this errata workround
+ * @data: HW Errata workaround data
+ *
+ */
+struct cam_cpas_hw_errata_wa {
+ bool enable;
+ union {
+ struct cam_cpas_reg reg_info;
+ } data;
+};
+
+/**
+ * struct cam_cpas_hw_errata_wa_list : List of HW Errata workaround info
+ *
+ * @camnoc_flush_slave_pending_trans: Errata workaround info for flushing
+ * camnoc slave pending transactions before turning off CPAS_TOP gdsc
+ *
+ */
+struct cam_cpas_hw_errata_wa_list {
+ struct cam_cpas_hw_errata_wa camnoc_flush_slave_pending_trans;
+};
+
+/**
* struct cam_camnoc_info : Overall CAMNOC settings info
*
* @specific: Pointer to CAMNOC SPECIFICTONTTPTR settings
@@ -156,6 +181,7 @@
* @irq_err_size: Array size of IRQ Error settings
* @error_logger: Pointer to CAMNOC IRQ Error logger read registers
* @error_logger_size: Array size of IRQ Error logger
+ * @errata_wa_list: HW Errata workaround info
*
*/
struct cam_camnoc_info {
@@ -166,6 +192,7 @@
int irq_err_size;
uint32_t *error_logger;
int error_logger_size;
+ struct cam_cpas_hw_errata_wa_list *errata_wa_list;
};
#endif /* _CAM_CPASTOP_HW_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
index 20ed1b6..b30cd05 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
@@ -513,6 +513,18 @@
0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
};
+static struct cam_cpas_hw_errata_wa_list cam170_cpas100_errata_wa_list = {
+ .camnoc_flush_slave_pending_trans = {
+ .enable = true,
+ .data.reg_info = {
+ .access_type = CAM_REG_TYPE_READ,
+ .offset = 0x2100, /* SidebandManager_SenseIn0_Low */
+ .mask = 0xE0000, /* Bits 17, 18, 19 */
+ .value = 0, /* expected to be 0 */
+ },
+ },
+};
+
struct cam_camnoc_info cam170_cpas100_camnoc_info = {
.specific = &cam_cpas100_camnoc_specific[0],
.specific_size = sizeof(cam_cpas100_camnoc_specific) /
@@ -524,6 +536,7 @@
.error_logger = &slave_error_logger[0],
.error_logger_size = sizeof(slave_error_logger) /
sizeof(slave_error_logger[0]),
+ .errata_wa_list = &cam170_cpas100_errata_wa_list,
};
#endif /* _CPASTOP100_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
index bdae1d1..6292a9f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
@@ -5,4 +5,4 @@
ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
-obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io.o cam_sensor_cci_i2c.o
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io.o cam_sensor_cci_i2c.o cam_sensor_qup_i2c.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
index 1261c4b..06e8104 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
@@ -75,4 +75,64 @@
enum camera_sensor_i2c_type addr_type,
uint32_t delay_ms);
-#endif /* _CAM_SENSOR_I2C_H_ */
+
+/**
+ * cam_qup_i2c_read : QUP based i2c read
+ * @client : QUP I2C client structure
+ * @data : I2C data
+ * @addr_type : I2c address type
+ * @data_type : I2C data type
+ *
+ * This API handles QUP I2C read
+ */
+
+int32_t cam_qup_i2c_read(struct i2c_client *client,
+ uint32_t addr, uint32_t *data,
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type);
+
+/**
+ * cam_qup_i2c_read_seq : QUP based I2C sequential read
+ * @client : QUP I2C client structure
+ * @data : I2C data
+ * @addr_type : I2c address type
+ * @num_bytes : number of bytes to read
+ * This API handles QUP I2C Sequential read
+ */
+
+int32_t cam_qup_i2c_read_seq(struct i2c_client *client,
+ uint32_t addr, uint8_t *data,
+ enum camera_sensor_i2c_type addr_type,
+ uint32_t num_byte);
+
+/**
+ * cam_qup_i2c_poll : QUP based I2C poll operation
+ * @client : QUP I2C client structure
+ * @addr : I2C address
+ * @data : I2C data
+ * @data_mask : I2C data mask
+ * @data_type : I2C data type
+ * @addr_type : I2C addr type
+ * @delay_ms : Delay in milli seconds
+ *
+ * This API implements QUP based I2C poll
+ */
+
+int32_t cam_qup_i2c_poll(struct i2c_client *client,
+ uint32_t addr, uint16_t data, uint16_t data_mask,
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type,
+ uint32_t delay_ms);
+
+/**
+ * cam_qup_i2c_write_table : QUP based I2C write random
+ * @client : QUP I2C client structure
+ * @write_setting : I2C register settings
+ *
+ * This API handles QUP I2C random write
+ */
+
+int32_t cam_qup_i2c_write_table(
+ struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_setting *write_setting);
+#endif /*_CAM_SENSOR_I2C_H*/
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
index 13e115a..3e1b331 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -29,6 +29,10 @@
if (io_master_info->master_type == CCI_MASTER) {
return cam_cci_i2c_poll(io_master_info->cci_client,
addr, data, mask, data_type, addr_type, delay_ms);
+ } else if (io_master_info->master_type == I2C_MASTER) {
+ return cam_qup_i2c_poll(io_master_info->client,
+ addr, data, data_mask, addr_type, data_type,
+ delay_ms);
} else {
pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
__LINE__, io_master_info->master_type);
@@ -49,6 +53,9 @@
if (io_master_info->master_type == CCI_MASTER) {
return cam_cci_i2c_read(io_master_info->cci_client,
addr, data, addr_type, data_type);
+ } else if (io_master_info->master_type == I2C_MASTER) {
+ return cam_qup_i2c_read(io_master_info->client,
+ addr, data, addr_type, data_type);
} else {
pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
__LINE__, io_master_info->master_type);
@@ -68,6 +75,9 @@
if (io_master_info->master_type == CCI_MASTER) {
return cam_cci_i2c_write_table(io_master_info,
write_setting);
+ } else if (io_master_info->master_type == I2C_MASTER) {
+ return cam_qup_i2c_write_table(io_master_info,
+ write_setting);
} else {
pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
__LINE__, io_master_info->master_type);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
index 27bbe6e..f721afd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
@@ -38,7 +38,7 @@
* @io_master_info: I2C/SPI master information
* @addr: I2C address
* @data: I2C data
- * @addr_type: I2C addr type
+ * @addr_type: I2C addr_type
* @data_type: I2C data type
*
* This API abstracts read functionality based on master type
@@ -50,6 +50,21 @@
/**
* @io_master_info: I2C/SPI master information
+ * @addr: I2C address
+ * @data: I2C data
+ * @addr_type: I2C addr type
+ * @num_bytes: number of bytes
+ *
+ * This API abstracts sequential read functionality based on master type
+ */
+int32_t camera_io_dev_read_seq(struct camera_io_master *io_master_info,
+ uint32_t addr, uint8_t *data,
+ enum camera_sensor_i2c_type addr_type,
+ uint32_t num_bytes);
+
+
+/**
+ * @io_master_info: I2C/SPI master information
*
* This API initializes the I2C/SPI master based on master type
*/
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c
new file mode 100644
index 0000000..b25b1855
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2017, 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 "cam_sensor_cmn_header.h"
+#include "cam_sensor_i2c.h"
+#include "cam_sensor_io.h"
+
+#define I2C_REG_DATA_MAX (8*1024)
+#define I2C_REG_MAX_BUF_SIZE 8
+
+static int32_t cam_qup_i2c_rxdata(
+ struct i2c_client *dev_client, unsigned char *rxdata,
+ enum camera_sensor_i2c_type addr_type,
+ int data_length)
+{
+ int32_t rc = 0;
+ uint16_t saddr = dev_client->addr >> 1;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = addr_type,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = data_length,
+ .buf = rxdata,
+ },
+ };
+ rc = i2c_transfer(dev_client->adapter, msgs, 2);
+ if (rc < 0)
+ pr_err("%s:failed 0x%x\n", __func__, saddr);
+ return rc;
+}
+
+
+static int32_t cam_qup_i2c_txdata(
+ struct camera_io_master *dev_client, unsigned char *txdata,
+ int length)
+{
+ int32_t rc = 0;
+ uint16_t saddr = dev_client->client->addr >> 1;
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+ rc = i2c_transfer(dev_client->client->adapter, msg, 1);
+ if (rc < 0)
+ pr_err("%s: failed 0x%x\n", __func__, saddr);
+ return rc;
+}
+
+int32_t cam_qup_i2c_read(struct i2c_client *client,
+ uint32_t addr, uint32_t *data,
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type)
+{
+ int32_t rc = -EINVAL;
+ unsigned char *buf = NULL;
+
+ if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+ || data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+ pr_err("ERR: %s Failed with addr/data_type verfication\n",
+ __func__);
+ return rc;
+ }
+
+ buf = kzalloc(addr_type + data_type, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+ buf[0] = addr;
+ } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+ buf[0] = addr >> 8;
+ buf[1] = addr;
+ } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+ buf[0] = addr >> 16;
+ buf[1] = addr >> 8;
+ buf[2] = addr;
+ } else {
+ buf[0] = addr >> 24;
+ buf[1] = addr >> 16;
+ buf[2] = addr >> 8;
+ buf[3] = addr;
+ }
+
+ rc = cam_qup_i2c_rxdata(client, buf, addr_type, data_type);
+ if (rc < 0) {
+ pr_err("%s fail\n", __func__);
+ goto read_fail;
+ }
+
+ if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE)
+ *data = buf[0];
+ else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD)
+ *data = buf[0] << 8 | buf[1];
+ else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B)
+ *data = buf[0] << 16 | buf[1] << 8 | buf[2];
+ else
+ *data = buf[0] << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3];
+
+ CDBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
+read_fail:
+ kfree(buf);
+ buf = NULL;
+ return rc;
+}
+
+int32_t cam_qup_i2c_read_seq(struct i2c_client *client,
+ uint32_t addr, uint8_t *data,
+ enum camera_sensor_i2c_type addr_type,
+ uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char *buf = NULL;
+ int i;
+
+ if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+ pr_err("ERR: %s Failed with addr_type verification\n",
+ __func__);
+ return rc;
+ }
+
+ if ((num_byte == 0) || (num_byte > I2C_REG_DATA_MAX)) {
+ pr_err("%s: Error num_byte:0x%x max supported:0x%x\n",
+ __func__, num_byte, I2C_REG_DATA_MAX);
+ return rc;
+ }
+
+ buf = kzalloc(addr_type + num_byte, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+ buf[0] = addr;
+ } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+ buf[0] = addr >> 16;
+ buf[1] = addr >> 8;
+ buf[2] = addr;
+ } else {
+ buf[0] = addr >> 24;
+ buf[1] = addr >> 16;
+ buf[2] = addr >> 8;
+ buf[3] = addr;
+ }
+
+ rc = cam_qup_i2c_rxdata(client, buf, addr_type, num_byte);
+ if (rc < 0) {
+ pr_err("%s fail\n", __func__);
+ goto read_seq_fail;
+ }
+
+ for (i = 0; i < num_byte; i++)
+ data[i] = buf[i];
+
+read_seq_fail:
+ kfree(buf);
+ buf = NULL;
+ return rc;
+}
+
+static int32_t cam_qup_i2c_compare(struct i2c_client *client,
+ uint32_t addr, uint32_t data, uint16_t data_mask,
+ enum camera_sensor_i2c_type data_type,
+ enum camera_sensor_i2c_type addr_type)
+{
+ int32_t rc;
+ uint32_t reg_data = 0;
+
+ rc = cam_qup_i2c_read(client, addr, ®_data,
+ addr_type, data_type);
+ if (rc < 0)
+ return rc;
+
+ reg_data = reg_data & 0xFFFF;
+ if (data != (reg_data & ~data_mask))
+ return I2C_COMPARE_MISMATCH;
+
+ return I2C_COMPARE_MATCH;
+}
+
+int32_t cam_qup_i2c_poll(struct i2c_client *client,
+ uint32_t addr, uint16_t data, uint16_t data_mask,
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type,
+ uint32_t delay_ms)
+{
+ int32_t rc = 0;
+ int i = 0;
+
+ if ((delay_ms > MAX_POLL_DELAY_MS) || (delay_ms == 0)) {
+ pr_err("%s:%d invalid delay = %d max_delay = %d\n",
+ __func__, __LINE__, delay_ms, MAX_POLL_DELAY_MS);
+ return -EINVAL;
+ }
+
+ if ((addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+ || data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || data_type >= CAMERA_SENSOR_I2C_TYPE_MAX))
+ return -EINVAL;
+
+ for (i = 0; i < delay_ms; i++) {
+ rc = cam_qup_i2c_compare(client,
+ addr, data, data_mask, data_type, addr_type);
+ if (rc == I2C_COMPARE_MATCH)
+ return rc;
+
+ usleep_range(1000, 1010);
+ }
+ /* If rc is MISMATCH then read is successful but poll is failure */
+ if (rc == I2C_COMPARE_MISMATCH)
+ pr_err("%s:%d poll failed rc=%d(non-fatal)\n",
+ __func__, __LINE__, rc);
+ if (rc < 0)
+ pr_err("%s:%d poll failed rc=%d\n", __func__, __LINE__, rc);
+
+ return rc;
+}
+
+static int32_t cam_qup_i2c_write(struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_array *reg_setting,
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type)
+{
+ int32_t rc = 0;
+ unsigned char buf[I2C_REG_MAX_BUF_SIZE];
+ uint8_t len = 0;
+
+ CDBG("%s reg addr = 0x%x data type: %d\n",
+ __func__, reg_setting->reg_addr, data_type);
+ if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+ buf[0] = reg_setting->reg_addr;
+ CDBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ len = 1;
+ } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+ buf[0] = reg_setting->reg_addr >> 8;
+ buf[1] = reg_setting->reg_addr;
+ CDBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ CDBG("%s byte %d: 0x%x\n", __func__,
+ len+1, buf[len+1]);
+ len = 2;
+ } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+ buf[0] = reg_setting->reg_addr >> 16;
+ buf[1] = reg_setting->reg_addr >> 8;
+ buf[2] = reg_setting->reg_addr;
+ len = 3;
+ } else if (addr_type == CAMERA_SENSOR_I2C_TYPE_DWORD) {
+ buf[0] = reg_setting->reg_addr >> 24;
+ buf[1] = reg_setting->reg_addr >> 16;
+ buf[2] = reg_setting->reg_addr >> 8;
+ buf[3] = reg_setting->reg_addr;
+ len = 4;
+ } else {
+ pr_err("%s: Invalid I2C addr type\n", __func__);
+ return -EINVAL;
+ }
+
+ CDBG("Data: 0x%x\n", reg_setting->reg_data);
+ if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+ buf[len] = reg_setting->reg_data;
+ CDBG("Byte %d: 0x%x\n", len, buf[len]);
+ len += 1;
+ } else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+ buf[len] = reg_setting->reg_data >> 8;
+ buf[len+1] = reg_setting->reg_data;
+ CDBG("Byte %d: 0x%x\n", len, buf[len]);
+ CDBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+ len += 2;
+ } else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+ buf[len] = reg_setting->reg_data >> 16;
+ buf[len + 1] = reg_setting->reg_data >> 8;
+ buf[len + 2] = reg_setting->reg_data;
+ CDBG("Byte %d: 0x%x\n", len, buf[len]);
+ CDBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+ CDBG("Byte %d: 0x%x\n", len+2, buf[len+2]);
+ len += 3;
+ } else if (data_type == CAMERA_SENSOR_I2C_TYPE_DWORD) {
+ buf[len] = reg_setting->reg_data >> 24;
+ buf[len + 1] = reg_setting->reg_data >> 16;
+ buf[len + 2] = reg_setting->reg_data >> 8;
+ buf[len + 3] = reg_setting->reg_data;
+ CDBG("Byte %d: 0x%x\n", len, buf[len]);
+ CDBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+ CDBG("Byte %d: 0x%x\n", len+2, buf[len+2]);
+ CDBG("Byte %d: 0x%x\n", len+3, buf[len+3]);
+ len += 4;
+ } else {
+ pr_err("%s: Invalid Data Type\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = cam_qup_i2c_txdata(client, buf, len);
+ if (rc < 0)
+ pr_err("%s fail\n", __func__);
+ return rc;
+}
+
+int32_t cam_qup_i2c_write_table(struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EINVAL;
+ struct cam_sensor_i2c_reg_array *reg_setting;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+ || (write_setting->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+ || write_setting->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX)))
+ return rc;
+
+ reg_setting = write_setting->reg_setting;
+
+ for (i = 0; i < write_setting->size; i++) {
+ CDBG("%s addr 0x%x data 0x%x\n", __func__,
+ reg_setting->reg_addr, reg_setting->reg_data);
+
+ rc = cam_qup_i2c_write(client, reg_setting,
+ write_setting->addr_type, write_setting->data_type);
+ if (rc < 0)
+ break;
+ reg_setting++;
+ }
+
+ if (write_setting->delay > 20)
+ msleep(write_setting->delay);
+ else if (write_setting->delay)
+ usleep_range(write_setting->delay * 1000, (write_setting->delay
+ * 1000) + 1000);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index a52fe05..05af186 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -371,7 +371,7 @@
struct msm_vidc_inst *temp;
struct msm_vidc_core *core;
unsigned long max_freq, freq_left, ops_left, load, cycles, freq = 0;
- unsigned long mbs_per_frame;
+ unsigned long mbs_per_second;
if (!inst || !inst->core) {
dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
@@ -394,14 +394,21 @@
list_for_each_entry(temp, &core->instances, list) {
- mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst);
+ if (!temp ||
+ temp->state < MSM_VIDC_START_DONE ||
+ temp->state >= MSM_VIDC_RELEASE_RESOURCES_DONE)
+ continue;
+
+ mbs_per_second = msm_comm_get_inst_load(temp,
+ LOAD_CALC_NO_QUIRKS);
+
cycles = temp->clk_data.entry->vpp_cycles;
- if (inst->session_type == MSM_VIDC_ENCODER)
+ if (temp->session_type == MSM_VIDC_ENCODER)
cycles = temp->flags & VIDC_LOW_POWER ?
- inst->clk_data.entry->low_power_cycles :
+ temp->clk_data.entry->low_power_cycles :
cycles;
- load = cycles * mbs_per_frame;
+ load = cycles * mbs_per_second;
ops_left = load ? (freq_left / load) : 0;
/* Convert remaining operating rate to Q16 format */
@@ -418,7 +425,7 @@
ctrl->name, ctrl->default_value, ctrl->val);
v4l2_ctrl_modify_range(ctrl, ctrl->minimum,
ctrl->val + ops_left, ctrl->step,
- ctrl->minimum);
+ ctrl->default_value);
dprintk(VIDC_DBG,
"%s: Updated Range = %lld --> %lld\n",
ctrl->name, ctrl->minimum, ctrl->maximum);
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 5610c4f..9afd6f2 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -15,6 +15,7 @@
*/
#include <linux/etherdevice.h>
+#include <net/netlink.h>
#include "wil6210.h"
#include "wmi.h"
#include "ftm.h"
@@ -55,6 +56,62 @@
#define QCA_NL80211_VENDOR_ID 0x001374
+#define WIL_MAX_RF_SECTORS (128)
+#define WIL_CID_ALL (0xff)
+
+enum qca_wlan_vendor_attr_rf_sector {
+ QCA_ATTR_MAC_ADDR = 6,
+ QCA_ATTR_PAD = 13,
+ QCA_ATTR_TSF = 29,
+ QCA_ATTR_DMG_RF_SECTOR_INDEX = 30,
+ QCA_ATTR_DMG_RF_SECTOR_TYPE = 31,
+ QCA_ATTR_DMG_RF_MODULE_MASK = 32,
+ QCA_ATTR_DMG_RF_SECTOR_CFG = 33,
+ QCA_ATTR_DMG_RF_SECTOR_MAX,
+};
+
+enum qca_wlan_vendor_attr_dmg_rf_sector_type {
+ QCA_ATTR_DMG_RF_SECTOR_TYPE_RX,
+ QCA_ATTR_DMG_RF_SECTOR_TYPE_TX,
+ QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX
+};
+
+enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
+ QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
+
+ /* keep last */
+ QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
+ QCA_ATTR_DMG_RF_SECTOR_CFG_MAX =
+ QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
+};
+
+static const struct
+nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = {
+ [QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN },
+ [QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 },
+ [QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 },
+ [QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED },
+};
+
+static const struct
+nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = {
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 },
+ [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 },
+};
+
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128,
QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129,
@@ -65,8 +122,25 @@
QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134,
QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135,
QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
};
+static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+static int wil_rf_sector_get_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+static int wil_rf_sector_set_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len);
+
/* vendor specific commands */
static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
{
@@ -111,6 +185,36 @@
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wil_aoa_abort_measurement
},
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_get_cfg
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_set_cfg
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd =
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_get_selected
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd =
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_rf_sector_set_selected
+ },
};
/* vendor specific events */
@@ -1837,3 +1941,451 @@
kfree(p2p_wdev);
}
}
+
+static int wil_rf_sector_status_to_rc(u8 status)
+{
+ switch (status) {
+ case WMI_RF_SECTOR_STATUS_SUCCESS:
+ return 0;
+ case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR:
+ return -EINVAL;
+ case WMI_RF_SECTOR_STATUS_BUSY_ERROR:
+ return -EAGAIN;
+ case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ u16 sector_index;
+ u8 sector_type;
+ u32 rf_modules_vec;
+ struct wmi_get_rf_sector_params_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_get_rf_sector_params_done_event evt;
+ } __packed reply;
+ struct sk_buff *msg;
+ struct nlattr *nl_cfgs, *nl_cfg;
+ u32 i;
+ struct wmi_rf_sector_info *si;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
+ !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+
+ sector_index = nla_get_u16(
+ tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
+ if (sector_index >= WIL_MAX_RF_SECTORS) {
+ wil_err(wil, "Invalid sector index %d\n", sector_index);
+ return -EINVAL;
+ }
+
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ rf_modules_vec = nla_get_u32(
+ tb[QCA_ATTR_DMG_RF_MODULE_MASK]);
+ if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) {
+ wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec);
+ return -EINVAL;
+ }
+
+ cmd.sector_idx = cpu_to_le16(sector_index);
+ cmd.sector_type = sector_type;
+ cmd.rf_modules_vec = rf_modules_vec & 0xFF;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
+ WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ if (reply.evt.status) {
+ wil_err(wil, "get rf sector cfg failed with status %d\n",
+ reply.evt.status);
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+ }
+
+ msg = cfg80211_vendor_cmd_alloc_reply_skb(
+ wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
+ le64_to_cpu(reply.evt.tsf),
+ QCA_ATTR_PAD))
+ goto nla_put_failure;
+
+ nl_cfgs = nla_nest_start(msg, QCA_ATTR_DMG_RF_SECTOR_CFG);
+ if (!nl_cfgs)
+ goto nla_put_failure;
+ for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) {
+ if (!(rf_modules_vec & BIT(i)))
+ continue;
+ nl_cfg = nla_nest_start(msg, i);
+ if (!nl_cfg)
+ goto nla_put_failure;
+ si = &reply.evt.sectors_info[i];
+ if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
+ i) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
+ le32_to_cpu(si->etype0)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
+ le32_to_cpu(si->etype1)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
+ le32_to_cpu(si->etype2)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
+ le32_to_cpu(si->psh_hi)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
+ le32_to_cpu(si->psh_lo)) ||
+ nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
+ le32_to_cpu(si->dtype_swch_off)))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_cfg);
+ }
+
+ nla_nest_end(msg, nl_cfgs);
+ rc = cfg80211_vendor_cmd_reply(msg);
+ return rc;
+nla_put_failure:
+ kfree_skb(msg);
+ return -ENOBUFS;
+}
+
+static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc, tmp;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
+ u16 sector_index, rf_module_index;
+ u8 sector_type;
+ u32 rf_modules_vec = 0;
+ struct wmi_set_rf_sector_params_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_set_rf_sector_params_done_event evt;
+ } __packed reply;
+ struct nlattr *nl_cfg;
+ struct wmi_rf_sector_info *si;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+
+ sector_index = nla_get_u16(
+ tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
+ if (sector_index >= WIL_MAX_RF_SECTORS) {
+ wil_err(wil, "Invalid sector index %d\n", sector_index);
+ return -EINVAL;
+ }
+
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.sector_idx = cpu_to_le16(sector_index);
+ cmd.sector_type = sector_type;
+ nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG],
+ tmp) {
+ rc = nla_parse_nested(tb2, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX,
+ nl_cfg, wil_rf_sector_cfg_policy);
+ if (rc) {
+ wil_err(wil, "invalid sector cfg\n");
+ return -EINVAL;
+ }
+
+ if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] ||
+ !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) {
+ wil_err(wil, "missing cfg params\n");
+ return -EINVAL;
+ }
+
+ rf_module_index = nla_get_u8(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]);
+ if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) {
+ wil_err(wil, "invalid RF module index %d\n",
+ rf_module_index);
+ return -EINVAL;
+ }
+ rf_modules_vec |= BIT(rf_module_index);
+ si = &cmd.sectors_info[rf_module_index];
+ si->etype0 = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0]));
+ si->etype1 = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1]));
+ si->etype2 = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2]));
+ si->psh_hi = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI]));
+ si->psh_lo = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO]));
+ si->dtype_swch_off = cpu_to_le32(nla_get_u32(
+ tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]));
+ }
+
+ cmd.rf_modules_vec = rf_modules_vec & 0xFF;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
+ WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+}
+
+static int wil_rf_sector_get_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ u8 sector_type, mac_addr[ETH_ALEN];
+ int cid = 0;
+ struct wmi_get_selected_rf_sector_index_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_get_selected_rf_sector_index_done_event evt;
+ } __packed reply;
+ struct sk_buff *msg;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ if (tb[QCA_ATTR_MAC_ADDR]) {
+ ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
+ cid = wil_find_cid(wil, mac_addr);
+ if (cid < 0) {
+ wil_err(wil, "invalid MAC address %pM\n", mac_addr);
+ return -ENOENT;
+ }
+ } else {
+ if (test_bit(wil_status_fwconnected, wil->status)) {
+ wil_err(wil, "must specify MAC address when connected\n");
+ return -EINVAL;
+ }
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cid = (u8)cid;
+ cmd.sector_type = sector_type;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID,
+ &cmd, sizeof(cmd),
+ WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ if (reply.evt.status) {
+ wil_err(wil, "get rf selected sector cfg failed with status %d\n",
+ reply.evt.status);
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+ }
+
+ msg = cfg80211_vendor_cmd_alloc_reply_skb(
+ wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
+ le64_to_cpu(reply.evt.tsf),
+ QCA_ATTR_PAD) ||
+ nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX,
+ le16_to_cpu(reply.evt.sector_idx)))
+ goto nla_put_failure;
+
+ rc = cfg80211_vendor_cmd_reply(msg);
+ return rc;
+nla_put_failure:
+ kfree_skb(msg);
+ return -ENOBUFS;
+}
+
+static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
+ u16 sector_index,
+ u8 sector_type, u8 cid)
+{
+ struct wmi_set_selected_rf_sector_index_cmd cmd;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_set_selected_rf_sector_index_done_event evt;
+ } __packed reply;
+ int rc;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.sector_idx = cpu_to_le16(sector_index);
+ cmd.sector_type = sector_type;
+ cmd.cid = (u8)cid;
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID,
+ &cmd, sizeof(cmd),
+ WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
+ &reply, sizeof(reply),
+ 500);
+ if (rc)
+ return rc;
+ return wil_rf_sector_status_to_rc(reply.evt.status);
+}
+
+static int wil_rf_sector_set_selected(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
+ int rc;
+ struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
+ u16 sector_index;
+ u8 sector_type, mac_addr[ETH_ALEN], i;
+ int cid = 0;
+
+ if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len,
+ wil_rf_sector_policy);
+ if (rc) {
+ wil_err(wil, "Invalid rf sector ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
+ !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
+ wil_err(wil, "Invalid rf sector spec\n");
+ return -EINVAL;
+ }
+
+ sector_index = nla_get_u16(
+ tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
+ if (sector_index >= WIL_MAX_RF_SECTORS &&
+ sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
+ wil_err(wil, "Invalid sector index %d\n", sector_index);
+ return -EINVAL;
+ }
+
+ sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
+ if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
+ wil_err(wil, "Invalid sector type %d\n", sector_type);
+ return -EINVAL;
+ }
+
+ if (tb[QCA_ATTR_MAC_ADDR]) {
+ ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
+ if (!is_broadcast_ether_addr(mac_addr)) {
+ cid = wil_find_cid(wil, mac_addr);
+ if (cid < 0) {
+ wil_err(wil, "invalid MAC address %pM\n",
+ mac_addr);
+ return -ENOENT;
+ }
+ } else {
+ if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
+ wil_err(wil, "broadcast MAC valid only with unlocking\n");
+ return -EINVAL;
+ }
+ cid = -1;
+ }
+ } else {
+ if (test_bit(wil_status_fwconnected, wil->status)) {
+ wil_err(wil, "must specify MAC address when connected\n");
+ return -EINVAL;
+ }
+ /* otherwise, using cid=0 for unassociated station */
+ }
+
+ if (cid >= 0) {
+ rc = wil_rf_sector_wmi_set_selected(wil, sector_index,
+ sector_type, cid);
+ } else {
+ /* unlock all cids */
+ rc = wil_rf_sector_wmi_set_selected(
+ wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type,
+ WIL_CID_ALL);
+ if (rc == -EINVAL) {
+ for (i = 0; i < WIL6210_MAX_CID; i++) {
+ rc = wil_rf_sector_wmi_set_selected(
+ wil, WMI_INVALID_RF_SECTOR_INDEX,
+ sector_type, i);
+ /* the FW will silently ignore and return
+ * success for unused cid, so abort the loop
+ * on any other error
+ */
+ if (rc) {
+ wil_err(wil, "unlock cid %d failed with status %d\n",
+ i, rc);
+ break;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/net/wireless/ath/wil6210/ioctl.c b/drivers/net/wireless/ath/wil6210/ioctl.c
index bbdd232..f8d2c20 100644
--- a/drivers/net/wireless/ath/wil6210/ioctl.c
+++ b/drivers/net/wireless/ath/wil6210/ioctl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -54,7 +54,7 @@
}
off = a - wil->csr;
- if (size >= WIL6210_MEM_SIZE - off) {
+ if (size >= wil->bar_size - off) {
wil_err(wil, "Requested block does not fit into memory: "
"off = 0x%08x size = 0x%08x\n", off, size);
return NULL;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 03246a9..cf3fadc 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -198,16 +198,18 @@
.ramdump = wil_platform_rop_ramdump,
.fw_recovery = wil_platform_rop_fw_recovery,
};
+ u32 bar_size = pci_resource_len(pdev, 0);
/* check HW */
dev_info(&pdev->dev, WIL_NAME
- " device found [%04x:%04x] (rev %x)\n",
- (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
+ " device found [%04x:%04x] (rev %x) bar size 0x%x\n",
+ (int)pdev->vendor, (int)pdev->device, (int)pdev->revision,
+ bar_size);
- if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
- dev_err(&pdev->dev, "Not " WIL_NAME "? "
- "BAR0 size is %lu while expecting %lu\n",
- (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
+ if ((bar_size < WIL6210_MIN_MEM_SIZE) ||
+ (bar_size > WIL6210_MAX_MEM_SIZE)) {
+ dev_err(&pdev->dev, "Unexpected BAR0 size 0x%x\n",
+ bar_size);
return -ENODEV;
}
@@ -220,6 +222,7 @@
wil->pdev = pdev;
pci_set_drvdata(pdev, wil);
+ wil->bar_size = bar_size;
/* rollback to if_free */
wil->platform_handle =
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index 015dc3c..ce1f384 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -80,6 +80,8 @@
wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
wil_unmask_irq(wil);
+ wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend);
+
/* Send WMI resume request to the device */
rc = wmi_resume(wil);
if (rc) {
@@ -96,7 +98,9 @@
}
}
- wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend);
+ /* Wake all queues */
+ if (test_bit(wil_status_fwconnected, wil->status))
+ wil_update_net_queues_bh(wil, NULL, false);
out:
if (rc)
@@ -113,6 +117,7 @@
/* Prevent handling of new tx and wmi commands */
set_bit(wil_status_suspending, wil->status);
+ wil_update_net_queues_bh(wil, NULL, true);
if (!wil_is_tx_idle(wil)) {
wil_dbg_pm(wil, "Pending TX data, reject suspend\n");
@@ -175,42 +180,43 @@
/* Disable device reset on PERST */
wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
- /* Save the current bus request to return to the same in resume */
- wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps;
- wil6210_bus_request(wil, 0);
-
if (wil->platform_ops.suspend) {
rc = wil->platform_ops.suspend(wil->platform_handle, true);
if (rc) {
wil_err(wil, "platform device failed to suspend (%d)\n",
rc);
wil->suspend_stats.failed_suspends++;
- clear_bit(wil_status_suspending, wil->status);
- rc = wil_resume_keep_radio_on(wil);
- /* if resume succeeded, reject the suspend */
- if (!rc)
- rc = -EBUSY;
- goto out;
+ wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
+ wil_unmask_irq(wil);
+ goto resume_after_fail;
}
}
+ /* Save the current bus request to return to the same in resume */
+ wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps;
+ wil6210_bus_request(wil, 0);
+
set_bit(wil_status_suspended, wil->status);
clear_bit(wil_status_suspending, wil->status);
return rc;
resume_after_fail:
+ set_bit(wil_status_resuming, wil->status);
clear_bit(wil_status_suspending, wil->status);
rc = wmi_resume(wil);
/* if resume succeeded, reject the suspend */
- if (!rc)
+ if (!rc) {
rc = -EBUSY;
-
-out:
+ if (test_bit(wil_status_fwconnected, wil->status))
+ wil_update_net_queues_bh(wil, NULL, false);
+ }
return rc;
reject_suspend:
clear_bit(wil_status_suspending, wil->status);
+ if (test_bit(wil_status_fwconnected, wil->status))
+ wil_update_net_queues_bh(wil, NULL, false);
return -EBUSY;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 439fe30..8f1e79b4 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -1930,6 +1930,11 @@
return;
}
+ /* Do not wake the queues in suspend flow */
+ if (test_bit(wil_status_suspending, wil->status) ||
+ test_bit(wil_status_suspended, wil->status))
+ return;
+
/* check wake */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
struct vring *cur_vring = &wil->vring_tx[i];
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 38f61e3..eca5685 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -59,7 +59,8 @@
return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1);
}
-#define WIL6210_MEM_SIZE (2*1024*1024UL)
+#define WIL6210_MIN_MEM_SIZE (2 * 1024 * 1024UL)
+#define WIL6210_MAX_MEM_SIZE (4 * 1024 * 1024UL)
#define WIL_TX_Q_LEN_DEFAULT (4000)
#define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
@@ -617,6 +618,7 @@
struct wil6210_priv {
struct pci_dev *pdev;
+ u32 bar_size;
struct wireless_dev *wdev;
void __iomem *csr;
DECLARE_BITMAP(status, wil_status_last);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index ba2b207..ae0952f 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -160,7 +160,7 @@
return NULL;
off = HOSTADDR(ptr);
- if (off > WIL6210_MEM_SIZE - 4)
+ if (off > wil->bar_size - 4)
return NULL;
return wil->csr + off;
@@ -180,7 +180,7 @@
return NULL;
off = HOSTADDR(ptr);
- if (off > WIL6210_MEM_SIZE - 4)
+ if (off > wil->bar_size - 4)
return NULL;
return wil->csr + off;
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index b0b51d6..e76ff14 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -747,9 +747,6 @@
int rc;
int force_pt_coherent = 1;
int smmu_bypass = !ctx->smmu_s1_en;
- dma_addr_t iova_base = 0;
- dma_addr_t iova_end = ctx->smmu_base + ctx->smmu_size - 1;
- struct iommu_domain_geometry geometry;
if (!ctx->use_smmu)
return 0;
@@ -807,17 +804,6 @@
rc);
goto release_mapping;
}
- memset(&geometry, 0, sizeof(geometry));
- geometry.aperture_start = iova_base;
- geometry.aperture_end = iova_end;
- rc = iommu_domain_set_attr(ctx->mapping->domain,
- DOMAIN_ATTR_GEOMETRY,
- &geometry);
- if (rc) {
- dev_err(ctx->dev, "Set geometry attribute to SMMU failed (%d)\n",
- rc);
- goto release_mapping;
- }
}
}
@@ -1097,7 +1083,7 @@
}
ctx->use_smmu = of_property_read_bool(of_node, "qcom,smmu-support");
ctx->keep_radio_on_during_sleep = of_property_read_bool(of_node,
- "qcom,keep_radio_on_during_sleep");
+ "qcom,keep-radio-on-during-sleep");
ctx->bus_scale = msm_bus_cl_get_pdata(pdev);
ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en");
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 4ecf9a5..bf5f952 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -39,6 +39,8 @@
#define PL_VOTER "PL_VOTER"
#define RESTRICT_CHG_VOTER "RESTRICT_CHG_VOTER"
#define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER"
+#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
+#define USBIN_I_VOTER "USBIN_I_VOTER"
struct pl_data {
int pl_mode;
@@ -53,7 +55,8 @@
struct votable *pl_awake_votable;
struct votable *hvdcp_hw_inov_dis_votable;
struct votable *usb_icl_votable;
- struct work_struct status_change_work;
+ struct votable *pl_enable_votable_indirect;
+ struct delayed_work status_change_work;
struct work_struct pl_disable_forever_work;
struct delayed_work pl_taper_work;
struct power_supply *main_psy;
@@ -491,6 +494,7 @@
}
#define ICL_STEP_UA 25000
+#define PL_DELAY_MS 3000
static int usb_icl_vote_callback(struct votable *votable, void *data,
int icl_ua, const char *client)
{
@@ -512,6 +516,21 @@
*/
vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, true, 0);
+ /*
+ * if (ICL < 1400)
+ * disable parallel charger using USBIN_I_VOTER
+ * else
+ * instead of re-enabling here rely on status_changed_work
+ * (triggered via AICL completed or scheduled from here to
+ * unvote USBIN_I_VOTER) the status_changed_work enables
+ * USBIN_I_VOTER based on settled current.
+ */
+ if (icl_ua <= 1400000)
+ vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
+ else
+ schedule_delayed_work(&chip->status_change_work,
+ msecs_to_jiffies(PL_DELAY_MS));
+
/* rerun AICL */
/* get the settled current */
rc = power_supply_get_property(chip->main_psy,
@@ -643,6 +662,16 @@
return 0;
}
+static int pl_enable_indirect_vote_callback(struct votable *votable,
+ void *data, int pl_enable, const char *client)
+{
+ struct pl_data *chip = data;
+
+ vote(chip->pl_disable_votable, PL_INDIRECT_VOTER, !pl_enable, 0);
+
+ return 0;
+}
+
static int pl_awake_vote_callback(struct votable *votable,
void *data, int awake, const char *client)
{
@@ -775,6 +804,42 @@
union power_supply_propval pval = {0, };
int new_total_settled_ua;
int rc;
+ int main_settled_ua;
+ int main_limited;
+ int total_current_ua;
+
+ total_current_ua = get_effective_result_locked(chip->usb_icl_votable);
+
+ /*
+ * call aicl split only when USBIN_USBIN and enabled
+ * and if aicl changed
+ */
+ rc = power_supply_get_property(chip->main_psy,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get aicl settled value rc=%d\n", rc);
+ return;
+ }
+ main_settled_ua = pval.intval;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get aicl settled value rc=%d\n", rc);
+ return;
+ }
+ main_limited = pval.intval;
+
+ if ((main_limited && (main_settled_ua + chip->pl_settled_ua) < 1400000)
+ || (main_settled_ua == 0)
+ || ((total_current_ua >= 0) &&
+ (total_current_ua <= 1400000)))
+ vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
+ else
+ vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, true, 0);
+
if (get_effective_result(chip->pl_disable_votable))
return;
@@ -783,17 +848,10 @@
|| chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT) {
/*
* call aicl split only when USBIN_USBIN and enabled
- * and if aicl changed
+ * and if settled current has changed by more than 300mA
*/
- rc = power_supply_get_property(chip->main_psy,
- POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
- &pval);
- if (rc < 0) {
- pr_err("Couldn't get aicl settled value rc=%d\n", rc);
- return;
- }
- new_total_settled_ua = pval.intval + chip->pl_settled_ua;
+ new_total_settled_ua = main_settled_ua + chip->pl_settled_ua;
pl_dbg(chip, PR_PARALLEL,
"total_settled_ua=%d settled_ua=%d new_total_settled_ua=%d\n",
chip->total_settled_ua, pval.intval,
@@ -840,7 +898,7 @@
static void status_change_work(struct work_struct *work)
{
struct pl_data *chip = container_of(work,
- struct pl_data, status_change_work);
+ struct pl_data, status_change_work.work);
if (!chip->main_psy && is_main_available(chip)) {
/*
@@ -878,7 +936,7 @@
if ((strcmp(psy->desc->name, "parallel") == 0)
|| (strcmp(psy->desc->name, "battery") == 0)
|| (strcmp(psy->desc->name, "main") == 0))
- schedule_work(&chip->status_change_work);
+ schedule_delayed_work(&chip->status_change_work, 0);
return NOTIFY_OK;
}
@@ -899,7 +957,7 @@
static int pl_determine_initial_status(struct pl_data *chip)
{
- status_change_work(&chip->status_change_work);
+ status_change_work(&chip->status_change_work.work);
return 0;
}
@@ -968,7 +1026,18 @@
goto destroy_votable;
}
- INIT_WORK(&chip->status_change_work, status_change_work);
+ chip->pl_enable_votable_indirect = create_votable("PL_ENABLE_INDIRECT",
+ VOTE_SET_ANY,
+ pl_enable_indirect_vote_callback,
+ chip);
+ if (IS_ERR(chip->pl_enable_votable_indirect)) {
+ rc = PTR_ERR(chip->pl_enable_votable_indirect);
+ return rc;
+ }
+
+ vote(chip->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
+
+ INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
INIT_DELAYED_WORK(&chip->pl_taper_work, pl_taper_work);
INIT_WORK(&chip->pl_disable_forever_work, pl_disable_forever_work);
@@ -1001,6 +1070,7 @@
unreg_notifier:
power_supply_unreg_notifier(&chip->nb);
destroy_votable:
+ destroy_votable(chip->pl_enable_votable_indirect);
destroy_votable(chip->pl_awake_votable);
destroy_votable(chip->pl_disable_votable);
destroy_votable(chip->fv_votable);
@@ -1020,11 +1090,12 @@
if (chip == NULL)
return;
- cancel_work_sync(&chip->status_change_work);
+ cancel_delayed_work_sync(&chip->status_change_work);
cancel_delayed_work_sync(&chip->pl_taper_work);
cancel_work_sync(&chip->pl_disable_forever_work);
power_supply_unreg_notifier(&chip->nb);
+ destroy_votable(chip->pl_enable_votable_indirect);
destroy_votable(chip->pl_awake_votable);
destroy_votable(chip->pl_disable_votable);
destroy_votable(chip->fv_votable);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 24dcbde..7a6f2ea 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -266,8 +266,9 @@
debug_mask, __debug_mask, int, 0600
);
-#define MICRO_1P5A 1500000
-#define MICRO_P1A 100000
+#define MICRO_1P5A 1500000
+#define MICRO_P1A 100000
+#define OTG_DEFAULT_DEGLITCH_TIME_MS 50
static int smb2_parse_dt(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
@@ -397,6 +398,12 @@
chg->suspend_input_on_debug_batt = of_property_read_bool(node,
"qcom,suspend-input-on-debug-batt");
+
+ rc = of_property_read_u32(node, "qcom,otg-deglitch-time-ms",
+ &chg->otg_delay_ms);
+ if (rc < 0)
+ chg->otg_delay_ms = OTG_DEFAULT_DEGLITCH_TIME_MS;
+
return 0;
}
@@ -1040,7 +1047,8 @@
rc = smblib_get_prop_batt_voltage_now(chg, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER);
+ val->intval = get_client_vote(chg->fv_votable,
+ BATT_PROFILE_VOTER);
break;
case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
rc = smblib_get_prop_charge_qnovo_enable(chg, val);
@@ -1058,7 +1066,7 @@
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
val->intval = get_client_vote(chg->fcc_votable,
- DEFAULT_VOTER);
+ BATT_PROFILE_VOTER);
break;
case POWER_SUPPLY_PROP_TEMP:
rc = smblib_get_prop_batt_temp(chg, val);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 69d509e..20ff26a 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -1046,16 +1046,6 @@
return 0;
}
-static int smblib_pl_enable_indirect_vote_callback(struct votable *votable,
- void *data, int chg_enable, const char *client)
-{
- struct smb_charger *chg = data;
-
- vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, !chg_enable, 0);
-
- return 0;
-}
-
static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
void *data,
int hvdcp_enable, const char *client)
@@ -1621,6 +1611,7 @@
{
union power_supply_propval pval;
int rc;
+ int effective_fv_uv;
u8 stat;
rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat);
@@ -1639,10 +1630,11 @@
* If Vbatt is within 40mV above Vfloat, then don't
* treat it as overvoltage.
*/
- if (pval.intval >=
- get_effective_result(chg->fv_votable) + 40000) {
+ effective_fv_uv = get_effective_result(chg->fv_votable);
+ if (pval.intval >= effective_fv_uv + 40000) {
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- smblib_err(chg, "battery over-voltage\n");
+ smblib_err(chg, "battery over-voltage vbat_fg = %duV, fv = %duV\n",
+ pval.intval, effective_fv_uv);
goto done;
}
}
@@ -3730,26 +3722,6 @@
smblib_typec_mode_name[pval.intval]);
}
-irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_3_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n",
- stat, !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)));
-
- extcon_set_cable_state_(chg->extcon, EXTCON_USB_HOST,
- !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)));
- power_supply_changed(chg->usb_psy);
-
- return IRQ_HANDLED;
-}
-
static void smblib_usb_typec_change(struct smb_charger *chg)
{
int rc;
@@ -3784,7 +3756,11 @@
struct smb_charger *chg = irq_data->parent_data;
if (chg->micro_usb_mode) {
- smblib_handle_usb_typec_change_for_uusb(chg);
+ cancel_delayed_work_sync(&chg->uusb_otg_work);
+ vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0);
+ smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n");
+ schedule_delayed_work(&chg->uusb_otg_work,
+ msecs_to_jiffies(chg->otg_delay_ms));
return IRQ_HANDLED;
}
@@ -3869,6 +3845,30 @@
/***************
* Work Queues *
***************/
+static void smblib_uusb_otg_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ uusb_otg_work.work);
+ int rc;
+ u8 stat;
+ bool otg;
+
+ rc = smblib_read(chg, TYPE_C_STATUS_3_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc);
+ goto out;
+ }
+
+ otg = !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT));
+ extcon_set_cable_state_(chg->extcon, EXTCON_USB_HOST, otg);
+ smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n",
+ stat, otg);
+ power_supply_changed(chg->usb_psy);
+
+out:
+ vote(chg->awake_votable, OTG_DELAY_VOTER, false, 0);
+}
+
static void smblib_hvdcp_detect_work(struct work_struct *work)
{
@@ -4177,8 +4177,6 @@
}
power_supply_changed(chg->usb_main_psy);
- vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER,
- settled_ua >= USB_WEAK_INPUT_UA, 0);
smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua);
}
@@ -4274,7 +4272,16 @@
smblib_err(chg, "Couldn't find votable PL_DISABLE rc=%d\n", rc);
return rc;
}
- vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
+
+ chg->pl_enable_votable_indirect = find_votable("PL_ENABLE_INDIRECT");
+ if (chg->pl_enable_votable_indirect == NULL) {
+ rc = -EINVAL;
+ smblib_err(chg,
+ "Couldn't find votable PL_ENABLE_INDIRECT rc=%d\n",
+ rc);
+ return rc;
+ }
+
vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
chg->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY,
@@ -4324,14 +4331,6 @@
return rc;
}
- chg->pl_enable_votable_indirect = create_votable("PL_ENABLE_INDIRECT",
- VOTE_SET_ANY,
- smblib_pl_enable_indirect_vote_callback,
- chg);
- if (IS_ERR(chg->pl_enable_votable_indirect)) {
- rc = PTR_ERR(chg->pl_enable_votable_indirect);
- return rc;
- }
chg->hvdcp_disable_votable_indirect = create_votable(
"HVDCP_DISABLE_INDIRECT",
@@ -4407,8 +4406,6 @@
destroy_votable(chg->awake_votable);
if (chg->chg_disable_votable)
destroy_votable(chg->chg_disable_votable);
- if (chg->pl_enable_votable_indirect)
- destroy_votable(chg->pl_enable_votable_indirect);
if (chg->apsd_disable_votable)
destroy_votable(chg->apsd_disable_votable);
if (chg->hvdcp_hw_inov_dis_votable)
@@ -4450,6 +4447,7 @@
INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
+ INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work);
chg->fake_capacity = -EINVAL;
chg->fake_input_current_limited = -EINVAL;
@@ -4504,6 +4502,7 @@
cancel_delayed_work_sync(&chg->icl_change_work);
cancel_delayed_work_sync(&chg->pl_enable_work);
cancel_work_sync(&chg->legacy_detection_work);
+ cancel_delayed_work_sync(&chg->uusb_otg_work);
power_supply_unreg_notifier(&chg->nb);
smblib_destroy_votables(chg);
qcom_batt_deinit();
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 398513b..0c8e661 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -36,9 +36,7 @@
#define PL_USBIN_USBIN_VOTER "PL_USBIN_USBIN_VOTER"
#define USB_PSY_VOTER "USB_PSY_VOTER"
#define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER"
-#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
#define PL_QNOVO_VOTER "PL_QNOVO_VOTER"
-#define USBIN_I_VOTER "USBIN_I_VOTER"
#define USBIN_V_VOTER "USBIN_V_VOTER"
#define CHG_STATE_VOTER "CHG_STATE_VOTER"
#define TYPEC_SRC_VOTER "TYPEC_SRC_VOTER"
@@ -64,6 +62,8 @@
#define CC2_WA_VOTER "CC2_WA_VOTER"
#define QNOVO_VOTER "QNOVO_VOTER"
#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER"
+#define OTG_DELAY_VOTER "OTG_DELAY_VOTER"
+#define USBIN_I_VOTER "USBIN_I_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -229,6 +229,7 @@
enum smb_mode mode;
struct smb_chg_freq chg_freq;
int smb_version;
+ int otg_delay_ms;
/* locks */
struct mutex lock;
@@ -290,6 +291,7 @@
struct delayed_work icl_change_work;
struct delayed_work pl_enable_work;
struct work_struct legacy_detection_work;
+ struct delayed_work uusb_otg_work;
/* cached status */
int voltage_min_uv;
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 3c4759c..453faa8 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -702,7 +702,8 @@
err = true;
} else if (intent->data == NULL) {
if (einfo->intentless) {
- intent->data = kmalloc(cmd.frag_size, GFP_ATOMIC);
+ intent->data = kmalloc(cmd.frag_size,
+ __GFP_ATOMIC | __GFP_HIGH);
if (!intent->data) {
err = true;
GLINK_ERR(
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index ecfc4ef..6b05b7b 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -76,6 +76,14 @@
return next_target;
}
+ /*
+ * If there is no new throttle request and if the thermal zone
+ * wasn't requesting any previous mitigation, then skip the
+ * evaluation.
+ */
+ if (instance->target == THERMAL_NO_TARGET && !throttle)
+ return next_target;
+
switch (trend) {
case THERMAL_TREND_RAISING:
if (throttle) {
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 2e12c3f..94ba2c3e 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -327,6 +327,11 @@
mb();
}
+static unsigned int msm_geni_cons_get_mctrl(struct uart_port *uport)
+{
+ return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
static unsigned int msm_geni_serial_get_mctrl(struct uart_port *uport)
{
u32 geni_ios = 0;
@@ -610,6 +615,8 @@
{
struct uart_port *uport;
struct msm_geni_serial_port *port;
+ int locked = 1;
+ unsigned long flags;
WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
@@ -618,9 +625,15 @@
return;
uport = &port->uport;
- spin_lock(&uport->lock);
- __msm_geni_serial_console_write(uport, s, count);
- spin_unlock(&uport->lock);
+ if (oops_in_progress)
+ locked = spin_trylock_irqsave(&uport->lock, flags);
+ else
+ spin_lock_irqsave(&uport->lock, flags);
+
+ if (locked) {
+ __msm_geni_serial_console_write(uport, s, count);
+ spin_unlock_irqrestore(&uport->lock, flags);
+ }
}
static int handle_rx_console(struct uart_port *uport,
@@ -1014,13 +1027,20 @@
static void msm_geni_serial_shutdown(struct uart_port *uport)
{
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+ unsigned long flags;
+ /* Stop the console before stopping the current tx */
+ if (uart_console(uport))
+ console_stop(uport->cons);
+
+ spin_lock_irqsave(&uport->lock, flags);
msm_geni_serial_stop_tx(uport);
msm_geni_serial_stop_rx(uport);
+ spin_unlock_irqrestore(&uport->lock, flags);
+
disable_irq(uport->irq);
free_irq(uport->irq, msm_port);
if (uart_console(uport)) {
- console_stop(uport->cons);
se_geni_resources_off(&msm_port->serial_rsc);
} else {
if (msm_port->wakeup_irq > 0) {
@@ -1572,6 +1592,7 @@
.shutdown = msm_geni_serial_shutdown,
.type = msm_geni_serial_get_type,
.set_mctrl = msm_geni_cons_set_mctrl,
+ .get_mctrl = msm_geni_cons_get_mctrl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = msm_geni_serial_get_char,
.poll_put_char = msm_geni_serial_poll_put_char,
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 38614fa..228d8af 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -3008,8 +3008,9 @@
ret = devm_request_threaded_irq(&pdev->dev, mdwc->ss_phy_irq,
msm_dwc3_pwr_irq,
msm_dwc3_pwr_irq_thread,
- IRQF_TRIGGER_RISING | IRQF_EARLY_RESUME
- | IRQF_ONESHOT, "ss_phy_irq", mdwc);
+ IRQF_TRIGGER_HIGH | IRQ_TYPE_LEVEL_HIGH
+ | IRQF_EARLY_RESUME | IRQF_ONESHOT,
+ "ss_phy_irq", mdwc);
if (ret) {
dev_err(&pdev->dev, "irqreq ss_phy_irq failed: %d\n",
ret);
@@ -3519,13 +3520,16 @@
dev_dbg(mdwc->dev, "%s: turn on host\n", __func__);
mdwc->hs_phy->flags |= PHY_HOST_MODE;
- if (dwc->maximum_speed == USB_SPEED_SUPER)
+ if (dwc->maximum_speed == USB_SPEED_SUPER) {
mdwc->ss_phy->flags |= PHY_HOST_MODE;
+ usb_phy_notify_connect(mdwc->ss_phy,
+ USB_SPEED_SUPER);
+ }
+ usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
pm_runtime_get_sync(mdwc->dev);
dbg_event(0xFF, "StrtHost gync",
atomic_read(&mdwc->dev->power.usage_count));
- usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
if (!IS_ERR(mdwc->vbus_reg))
ret = regulator_enable(mdwc->vbus_reg);
if (ret) {
@@ -3614,8 +3618,13 @@
dbg_event(0xFF, "StopHost gsync",
atomic_read(&mdwc->dev->power.usage_count));
usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
+ if (mdwc->ss_phy->flags & PHY_HOST_MODE) {
+ usb_phy_notify_disconnect(mdwc->ss_phy,
+ USB_SPEED_SUPER);
+ mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
+ }
+
mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
- mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
platform_device_del(dwc->xhci);
usb_unregister_notify(&mdwc->host_nb);
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index a50e327..562cc53 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -187,6 +187,8 @@
#define PD_MAX_MSG_ID 7
+#define PD_MAX_DATA_OBJ 7
+
#define PD_MSG_HDR(type, dr, pr, id, cnt, rev) \
(((type) & 0xF) | ((dr) << 5) | (rev << 6) | \
((pr) << 8) | ((id) << 9) | ((cnt) << 12))
@@ -308,7 +310,7 @@
struct list_head rx_q;
spinlock_t rx_lock;
- u32 received_pdos[7];
+ u32 received_pdos[PD_MAX_DATA_OBJ];
u16 src_cap_id;
u8 selected_pdo;
u8 requested_pdo;
@@ -490,13 +492,12 @@
ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15);
/* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */
- /* MessageID incremented regardless of Tx error */
- pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID;
-
if (ret < 0)
return ret;
else if (ret != num_data * sizeof(u32))
return -EIO;
+
+ pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID;
return 0;
}
@@ -556,6 +557,7 @@
static int pd_eval_src_caps(struct usbpd *pd)
{
+ int obj_cnt;
union power_supply_propval val;
u32 first_pdo = pd->received_pdos[0];
@@ -572,6 +574,13 @@
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val);
+ for (obj_cnt = 1; obj_cnt < PD_MAX_DATA_OBJ; obj_cnt++) {
+ if ((PD_SRC_PDO_TYPE(pd->received_pdos[obj_cnt]) ==
+ PD_SRC_PDO_TYPE_AUGMENTED) &&
+ !PD_APDO_PPS(pd->received_pdos[obj_cnt]))
+ pd->spec_rev = USBPD_REV_30;
+ }
+
/* Select the first PDO (vSafe5V) immediately. */
pd_select_pdo(pd, 1, 0, 0);
@@ -602,6 +611,8 @@
static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
{
+ union power_supply_propval val = {1};
+
if (type != HARD_RESET_SIG) {
usbpd_err(&pd->dev, "invalid signal (%d) received\n", type);
return;
@@ -612,6 +623,9 @@
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
set_power_role(pd, pd->current_pr);
pd->hard_reset_recvd = true;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
kick_sm(pd, 0);
}
@@ -662,12 +676,6 @@
return;
}
- /* if spec rev differs (i.e. is older), update PHY */
- if (PD_MSG_HDR_REV(header) < pd->spec_rev) {
- pd->spec_rev = PD_MSG_HDR_REV(header);
- pd_phy_update_spec_rev(pd->spec_rev);
- }
-
rx_msg = kzalloc(sizeof(*rx_msg), GFP_KERNEL);
if (!rx_msg)
return;
@@ -710,7 +718,6 @@
.shutdown_cb = phy_shutdown,
.frame_filter_val = FRAME_FILTER_EN_SOP |
FRAME_FILTER_EN_HARD_RESET,
- .spec_rev = USBPD_REV_20,
};
union power_supply_propval val = {0};
unsigned long flags;
@@ -748,8 +755,6 @@
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
- /* support only PD 2.0 as a source */
- pd->spec_rev = USBPD_REV_20;
pd_reset_protocol(pd);
if (!pd->in_pr_swap) {
@@ -760,7 +765,6 @@
phy_params.data_role = pd->current_dr;
phy_params.power_role = pd->current_pr;
- phy_params.spec_rev = pd->spec_rev;
ret = pd_phy_open(&phy_params);
if (ret) {
@@ -772,8 +776,6 @@
}
pd->pd_phy_opened = true;
- } else {
- pd_phy_update_spec_rev(pd->spec_rev);
}
pd->current_state = PE_SRC_SEND_CAPABILITIES;
@@ -905,11 +907,6 @@
if (!val.intval)
break;
- /*
- * support up to PD 3.0 as a sink; if source is 2.0,
- * phy_msg_received() will handle the downgrade.
- */
- pd->spec_rev = USBPD_REV_30;
pd_reset_protocol(pd);
if (!pd->in_pr_swap) {
@@ -920,7 +917,6 @@
phy_params.data_role = pd->current_dr;
phy_params.power_role = pd->current_pr;
- phy_params.spec_rev = pd->spec_rev;
ret = pd_phy_open(&phy_params);
if (ret) {
@@ -932,8 +928,6 @@
}
pd->pd_phy_opened = true;
- } else {
- pd_phy_update_spec_rev(pd->spec_rev);
}
pd->current_voltage = pd->requested_voltage = 5000000;
@@ -1625,9 +1619,16 @@
if (pd->hard_reset_recvd) {
pd->hard_reset_recvd = false;
- val.intval = 1;
+ if (pd->requested_current) {
+ val.intval = pd->requested_current = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
+ }
+
+ pd->requested_voltage = 5000000;
+ val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+ POWER_SUPPLY_PROP_VOLTAGE_MIN, &val);
pd->in_pr_swap = false;
pd->in_explicit_contract = false;
@@ -1733,14 +1734,8 @@
case PE_SRC_READY:
if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) {
- ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES,
- default_src_caps,
- ARRAY_SIZE(default_src_caps), SOP_MSG);
- if (ret) {
- usbpd_err(&pd->dev, "Error sending SRC CAPs\n");
- usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
- break;
- }
+ pd->current_state = PE_SRC_SEND_CAPABILITIES;
+ kick_sm(pd, 0);
} else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) {
ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
pd->sink_caps, pd->num_sink_caps,
@@ -2313,6 +2308,14 @@
sm_done:
kfree(rx_msg);
+ spin_lock_irqsave(&pd->rx_lock, flags);
+ ret = list_empty(&pd->rx_q);
+ spin_unlock_irqrestore(&pd->rx_lock, flags);
+
+ /* requeue if there are any new/pending RX messages */
+ if (!ret)
+ kick_sm(pd, 0);
+
if (!pd->sm_queued)
pm_relax(&pd->dev);
}
@@ -3309,6 +3312,8 @@
pd->dual_role->drv_data = pd;
}
+ /* default support as PD 2.0 source or sink */
+ pd->spec_rev = USBPD_REV_20;
pd->current_pr = PR_NONE;
pd->current_dr = DR_NONE;
list_add_tail(&pd->instance, &_usbpd);
diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
index 4caee72..1f5306f 100644
--- a/drivers/usb/pd/qpnp-pdphy.c
+++ b/drivers/usb/pd/qpnp-pdphy.c
@@ -108,6 +108,7 @@
int tx_status;
u8 frame_filter_val;
bool in_test_data_mode;
+ bool rx_busy;
enum data_role data_role;
enum power_role power_role;
@@ -334,15 +335,6 @@
}
EXPORT_SYMBOL(pd_phy_update_roles);
-int pd_phy_update_spec_rev(enum pd_spec_rev rev)
-{
- struct usb_pdphy *pdphy = __pdphy;
-
- return pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG,
- MSG_CONFIG_SPEC_REV_MASK, rev);
-}
-EXPORT_SYMBOL(pd_phy_update_spec_rev);
-
int pd_phy_open(struct pd_phy_params *params)
{
int ret;
@@ -377,7 +369,9 @@
if (ret)
return ret;
- ret = pd_phy_update_spec_rev(params->spec_rev);
+ /* PD 2.0 phy */
+ ret = pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG,
+ MSG_CONFIG_SPEC_REV_MASK, USBPD_REV_20);
if (ret)
return ret;
@@ -492,6 +486,12 @@
return -EINVAL;
}
+ ret = pdphy_reg_read(pdphy, &val, USB_PDPHY_RX_ACKNOWLEDGE, 1);
+ if (ret || val || pdphy->rx_busy) {
+ dev_err(pdphy->dev, "%s: RX message pending\n", __func__);
+ return -EBUSY;
+ }
+
pdphy->tx_status = -EINPROGRESS;
/* write 2 byte SOP message header */
@@ -664,6 +664,15 @@
BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE);
}
+static irqreturn_t pdphy_msg_rx_irq(int irq, void *data)
+{
+ struct usb_pdphy *pdphy = data;
+
+ pdphy->rx_busy = true;
+
+ return IRQ_WAKE_THREAD;
+}
+
static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data)
{
u8 size, rx_status, frame_type;
@@ -720,6 +729,7 @@
false);
pdphy->rx_bytes += size + 1;
done:
+ pdphy->rx_busy = false;
return IRQ_HANDLED;
}
@@ -805,7 +815,7 @@
return ret;
ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
- &pdphy->msg_rx_irq, "msg-rx", NULL,
+ &pdphy->msg_rx_irq, "msg-rx", pdphy_msg_rx_irq,
pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
if (ret < 0)
return ret;
diff --git a/drivers/usb/pd/usbpd.h b/drivers/usb/pd/usbpd.h
index b2663ad..1087017 100644
--- a/drivers/usb/pd/usbpd.h
+++ b/drivers/usb/pd/usbpd.h
@@ -68,7 +68,6 @@
enum data_role data_role;
enum power_role power_role;
u8 frame_filter_val;
- u8 spec_rev;
};
#if IS_ENABLED(CONFIG_QPNP_USB_PDPHY)
@@ -77,7 +76,6 @@
int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
enum pd_msg_type type, unsigned int timeout_ms);
int pd_phy_update_roles(enum data_role dr, enum power_role pr);
-int pd_phy_update_spec_rev(enum pd_spec_rev rev);
void pd_phy_close(void);
#else
static inline int pd_phy_open(struct pd_phy_params *params)
@@ -101,11 +99,6 @@
return -ENODEV;
}
-static inline int pd_phy_update_spec_rev(enum pd_spec_rev rev)
-{
- return -ENODEV;
-}
-
static inline void pd_phy_close(void)
{
}
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 8bdd9fd..59f5379 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -201,14 +201,16 @@
if (enable) {
msm_ssusb_qmp_clr_lfps_rxterm_int(phy);
+ val = readb_relaxed(phy->base + autonomous_mode_offset);
+ val |= ARCVR_DTCT_EN;
if (phy->phy.flags & DEVICE_IN_SS_MODE) {
- val =
- readb_relaxed(phy->base + autonomous_mode_offset);
- val |= ARCVR_DTCT_EN;
val |= ALFPS_DTCT_EN;
val &= ~ARCVR_DTCT_EVENT_SEL;
- writeb_relaxed(val, phy->base + autonomous_mode_offset);
+ } else {
+ val &= ~ALFPS_DTCT_EN;
+ val |= ARCVR_DTCT_EVENT_SEL;
}
+ writeb_relaxed(val, phy->base + autonomous_mode_offset);
msm_ssusb_qmp_clamp_enable(phy, true);
} else {
msm_ssusb_qmp_clamp_enable(phy, false);
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 0e1e050..cf86f52 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -440,6 +440,13 @@
struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
+void dm_lock_md_type(struct mapped_device *md);
+void dm_unlock_md_type(struct mapped_device *md);
+void dm_set_md_type(struct mapped_device *md, unsigned type);
+unsigned dm_get_md_type(struct mapped_device *md);
+int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t);
+unsigned dm_table_get_type(struct dm_table *t);
+
/*
* Geometry functions.
*/
diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c
index a557c5e..7760705 100644
--- a/init/do_mounts_dm.c
+++ b/init/do_mounts_dm.c
@@ -5,13 +5,17 @@
*
* This file is released under the GPL.
*/
+#include <linux/async.h>
+#include <linux/ctype.h>
#include <linux/device-mapper.h>
#include <linux/fs.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include "do_mounts.h"
-#include "../drivers/md/dm.h"
+#define DM_MAX_DEVICES 256
+#define DM_MAX_TARGETS 256
#define DM_MAX_NAME 32
#define DM_MAX_UUID 129
#define DM_NO_UUID "none"
@@ -19,14 +23,47 @@
#define DM_MSG_PREFIX "init"
/* Separators used for parsing the dm= argument. */
-#define DM_FIELD_SEP ' '
-#define DM_LINE_SEP ','
+#define DM_FIELD_SEP " "
+#define DM_LINE_SEP ","
+#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP
/*
* When the device-mapper and any targets are compiled into the kernel
- * (not a module), one target may be created and used as the root device at
- * boot time with the parameters given with the boot line dm=...
- * The code for that is here.
+ * (not a module), one or more device-mappers may be created and used
+ * as the root device at boot time with the parameters given with the
+ * boot line dm=...
+ *
+ * Multiple device-mappers can be stacked specifing the number of
+ * devices. A device can have multiple targets if the the number of
+ * targets is specified.
+ *
+ * TODO(taysom:defect 32847)
+ * In the future, the <num> field will be mandatory.
+ *
+ * <device> ::= [<num>] <device-mapper>+
+ * <device-mapper> ::= <head> "," <target>+
+ * <head> ::= <name> <uuid> <mode> [<num>]
+ * <target> ::= <start> <length> <type> <options> ","
+ * <mode> ::= "ro" | "rw"
+ * <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none"
+ * <type> ::= "verity" | "bootcache" | ...
+ *
+ * Example:
+ * 2 vboot none ro 1,
+ * 0 1768000 bootcache
+ * device=aa55b119-2a47-8c45-946a-5ac57765011f+1
+ * signature=76e9be054b15884a9fa85973e9cb274c93afadb6
+ * cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000,
+ * vroot none ro 1,
+ * 0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1
+ * root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6
+ * salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe
+ *
+ * Notes:
+ * 1. uuid is a label for the device and we set it to "none".
+ * 2. The <num> field will be optional initially and assumed to be 1.
+ * Once all the scripts that set these fields have been set, it will
+ * be made mandatory.
*/
struct dm_setup_target {
@@ -38,381 +75,388 @@
struct dm_setup_target *next;
};
-static struct {
+struct dm_device {
int minor;
int ro;
char name[DM_MAX_NAME];
char uuid[DM_MAX_UUID];
- char *targets;
+ unsigned long num_targets;
struct dm_setup_target *target;
int target_count;
+ struct dm_device *next;
+};
+
+struct dm_option {
+ char *start;
+ char *next;
+ size_t len;
+ char delim;
+};
+
+static struct {
+ unsigned long num_devices;
+ char *str;
} dm_setup_args __initdata;
static __initdata int dm_early_setup;
-static size_t __init get_dm_option(char *str, char **next, char sep)
+static int __init get_dm_option(struct dm_option *opt, const char *accept)
{
- size_t len = 0;
- char *endp = NULL;
+ char *str = opt->next;
+ char *endp;
if (!str)
return 0;
- endp = strchr(str, sep);
+ str = skip_spaces(str);
+ opt->start = str;
+ endp = strpbrk(str, accept);
if (!endp) { /* act like strchrnul */
- len = strlen(str);
- endp = str + len;
+ opt->len = strlen(str);
+ endp = str + opt->len;
} else {
- len = endp - str;
+ opt->len = endp - str;
}
-
- if (endp == str)
- return 0;
-
- if (!next)
- return len;
-
+ opt->delim = *endp;
if (*endp == 0) {
/* Don't advance past the nul. */
- *next = endp;
+ opt->next = endp;
} else {
- *next = endp + 1;
+ opt->next = endp + 1;
}
- return len;
+ return opt->len != 0;
}
-static int __init dm_setup_args_init(void)
+static int __init dm_setup_cleanup(struct dm_device *devices)
{
- dm_setup_args.minor = 0;
- dm_setup_args.ro = 0;
- dm_setup_args.target = NULL;
- dm_setup_args.target_count = 0;
+ struct dm_device *dev = devices;
+
+ while (dev) {
+ struct dm_device *old_dev = dev;
+ struct dm_setup_target *target = dev->target;
+ while (target) {
+ struct dm_setup_target *old_target = target;
+ kfree(target->type);
+ kfree(target->params);
+ target = target->next;
+ kfree(old_target);
+ dev->target_count--;
+ }
+ BUG_ON(dev->target_count);
+ dev = dev->next;
+ kfree(old_dev);
+ }
return 0;
}
-static int __init dm_setup_cleanup(void)
+static char * __init dm_parse_device(struct dm_device *dev, char *str)
{
- struct dm_setup_target *target = dm_setup_args.target;
- struct dm_setup_target *old_target = NULL;
- while (target) {
- kfree(target->type);
- kfree(target->params);
- old_target = target;
- target = target->next;
- kfree(old_target);
- dm_setup_args.target_count--;
- }
- BUG_ON(dm_setup_args.target_count);
- return 0;
-}
-
-static char * __init dm_setup_parse_device_args(char *str)
-{
- char *next = NULL;
- size_t len = 0;
+ struct dm_option opt;
+ size_t len;
/* Grab the logical name of the device to be exported to udev */
- len = get_dm_option(str, &next, DM_FIELD_SEP);
- if (!len) {
+ opt.next = str;
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
DMERR("failed to parse device name");
goto parse_fail;
}
- len = min(len + 1, sizeof(dm_setup_args.name));
- strlcpy(dm_setup_args.name, str, len); /* includes nul */
- str = skip_spaces(next);
+ len = min(opt.len + 1, sizeof(dev->name));
+ strlcpy(dev->name, opt.start, len); /* includes nul */
/* Grab the UUID value or "none" */
- len = get_dm_option(str, &next, DM_FIELD_SEP);
- if (!len) {
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
DMERR("failed to parse device uuid");
goto parse_fail;
}
- len = min(len + 1, sizeof(dm_setup_args.uuid));
- strlcpy(dm_setup_args.uuid, str, len);
- str = skip_spaces(next);
+ len = min(opt.len + 1, sizeof(dev->uuid));
+ strlcpy(dev->uuid, opt.start, len);
/* Determine if the table/device will be read only or read-write */
- if (!strncmp("ro,", str, 3)) {
- dm_setup_args.ro = 1;
- } else if (!strncmp("rw,", str, 3)) {
- dm_setup_args.ro = 0;
+ get_dm_option(&opt, DM_ANY_SEP);
+ if (!strncmp("ro", opt.start, opt.len)) {
+ dev->ro = 1;
+ } else if (!strncmp("rw", opt.start, opt.len)) {
+ dev->ro = 0;
} else {
DMERR("failed to parse table mode");
goto parse_fail;
}
- str = skip_spaces(str + 3);
- return str;
+ /* Optional number field */
+ /* XXX: The <num> field will be mandatory in the next round */
+ if (opt.delim == DM_FIELD_SEP[0]) {
+ if (!get_dm_option(&opt, DM_LINE_SEP))
+ return NULL;
+ dev->num_targets = simple_strtoul(opt.start, NULL, 10);
+ } else {
+ dev->num_targets = 1;
+ }
+ if (dev->num_targets > DM_MAX_TARGETS) {
+ DMERR("too many targets %lu > %d",
+ dev->num_targets, DM_MAX_TARGETS);
+ }
+ return opt.next;
parse_fail:
return NULL;
}
-static void __init dm_substitute_devices(char *str, size_t str_len)
+static char * __init dm_parse_targets(struct dm_device *dev, char *str)
{
- char *candidate = str;
- char *candidate_end = str;
- char old_char;
- size_t len = 0;
- dev_t dev;
-
- if (str_len < 3)
- return;
-
- while (str && *str) {
- candidate = strchr(str, '/');
- if (!candidate)
- break;
-
- /* Avoid embedded slashes */
- if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) {
- str = strchr(candidate, DM_FIELD_SEP);
- continue;
- }
-
- len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP);
- str = skip_spaces(candidate_end);
- if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */
- continue;
-
- /* Temporarily terminate with a nul */
- if (*candidate_end)
- candidate_end--;
- old_char = *candidate_end;
- *candidate_end = '\0';
-
- DMDEBUG("converting candidate device '%s' to dev_t", candidate);
- /* Use the boot-time specific device naming */
- dev = name_to_dev_t(candidate);
- *candidate_end = old_char;
-
- DMDEBUG(" -> %u", dev);
- /* No suitable replacement found */
- if (!dev)
- continue;
-
- /* Rewrite the /dev/path as a major:minor */
- len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev));
- if (!len) {
- DMERR("error substituting device major/minor.");
- break;
- }
- candidate += len;
- /* Pad out with spaces (fixing our nul) */
- while (candidate < candidate_end)
- *(candidate++) = DM_FIELD_SEP;
- }
-}
-
-static int __init dm_setup_parse_targets(char *str)
-{
- char *next = NULL;
- size_t len = 0;
- struct dm_setup_target **target = NULL;
+ struct dm_option opt;
+ struct dm_setup_target **target = &dev->target;
+ unsigned long num_targets = dev->num_targets;
+ unsigned long i;
/* Targets are defined as per the table format but with a
* comma as a newline separator. */
- target = &dm_setup_args.target;
- while (str && *str) {
+ opt.next = str;
+ for (i = 0; i < num_targets; i++) {
*target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL);
if (!*target) {
- DMERR("failed to allocate memory for target %d",
- dm_setup_args.target_count);
+ DMERR("failed to allocate memory for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- dm_setup_args.target_count++;
+ dev->target_count++;
- (*target)->begin = simple_strtoull(str, &next, 10);
- if (!next || *next != DM_FIELD_SEP) {
- DMERR("failed to parse starting sector for target %d",
- dm_setup_args.target_count - 1);
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+ DMERR("failed to parse starting sector"
+ " for target %s<%ld>", dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next + 1);
+ (*target)->begin = simple_strtoull(opt.start, NULL, 10);
- (*target)->length = simple_strtoull(str, &next, 10);
- if (!next || *next != DM_FIELD_SEP) {
- DMERR("failed to parse length for target %d",
- dm_setup_args.target_count - 1);
+ if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+ DMERR("failed to parse length for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next + 1);
+ (*target)->length = simple_strtoull(opt.start, NULL, 10);
- len = get_dm_option(str, &next, DM_FIELD_SEP);
- if (!len ||
- !((*target)->type = kstrndup(str, len, GFP_KERNEL))) {
- DMERR("failed to parse type for target %d",
- dm_setup_args.target_count - 1);
+ if (get_dm_option(&opt, DM_FIELD_SEP))
+ (*target)->type = kstrndup(opt.start, opt.len,
+ GFP_KERNEL);
+ if (!((*target)->type)) {
+ DMERR("failed to parse type for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next);
-
- len = get_dm_option(str, &next, DM_LINE_SEP);
- if (!len ||
- !((*target)->params = kstrndup(str, len, GFP_KERNEL))) {
- DMERR("failed to parse params for target %d",
- dm_setup_args.target_count - 1);
+ if (get_dm_option(&opt, DM_LINE_SEP))
+ (*target)->params = kstrndup(opt.start, opt.len,
+ GFP_KERNEL);
+ if (!((*target)->params)) {
+ DMERR("failed to parse params for target %s<%ld>",
+ dev->name, i);
goto parse_fail;
}
- str = skip_spaces(next);
-
- /* Before moving on, walk through the copied target and
- * attempt to replace all /dev/xxx with the major:minor number.
- * It may not be possible to resolve them traditionally at
- * boot-time. */
- dm_substitute_devices((*target)->params, len);
-
target = &((*target)->next);
}
- DMDEBUG("parsed %d targets", dm_setup_args.target_count);
+ DMDEBUG("parsed %d targets", dev->target_count);
- return 0;
+ return opt.next;
parse_fail:
- return 1;
+ return NULL;
+}
+
+static struct dm_device * __init dm_parse_args(void)
+{
+ struct dm_device *devices = NULL;
+ struct dm_device **tail = &devices;
+ struct dm_device *dev;
+ char *str = dm_setup_args.str;
+ unsigned long num_devices = dm_setup_args.num_devices;
+ unsigned long i;
+
+ if (!str)
+ return NULL;
+ for (i = 0; i < num_devices; i++) {
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ DMERR("failed to allocated memory for dev");
+ goto error;
+ }
+ *tail = dev;
+ tail = &dev->next;
+ /*
+ * devices are given minor numbers 0 - n-1
+ * in the order they are found in the arg
+ * string.
+ */
+ dev->minor = i;
+ str = dm_parse_device(dev, str);
+ if (!str) /* NULL indicates error in parsing, bail */
+ goto error;
+
+ str = dm_parse_targets(dev, str);
+ if (!str)
+ goto error;
+ }
+ return devices;
+error:
+ dm_setup_cleanup(devices);
+ return NULL;
}
/*
* Parse the command-line parameters given our kernel, but do not
* actually try to invoke the DM device now; that is handled by
- * dm_setup_drive after the low-level disk drivers have initialised.
- * dm format is as follows:
- * dm="name uuid fmode,[table line 1],[table line 2],..."
- * May be used with root=/dev/dm-0 as it always uses the first dm minor.
+ * dm_setup_drives after the low-level disk drivers have initialised.
+ * dm format is described at the top of the file.
+ *
+ * Because dm minor numbers are assigned in assending order starting with 0,
+ * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1,
+ * and so forth.
*/
-
static int __init dm_setup(char *str)
{
- dm_setup_args_init();
+ struct dm_option opt;
+ unsigned long num_devices;
- str = dm_setup_parse_device_args(str);
if (!str) {
DMDEBUG("str is NULL");
goto parse_fail;
}
-
- /* Target parsing is delayed until we have dynamic memory */
- dm_setup_args.targets = str;
-
- printk(KERN_INFO "dm: will configure '%s' on dm-%d\n",
- dm_setup_args.name, dm_setup_args.minor);
-
+ opt.next = str;
+ if (!get_dm_option(&opt, DM_FIELD_SEP))
+ goto parse_fail;
+ if (isdigit(opt.start[0])) { /* XXX: Optional number field */
+ num_devices = simple_strtoul(opt.start, NULL, 10);
+ str = opt.next;
+ } else {
+ num_devices = 1;
+ /* Don't advance str */
+ }
+ if (num_devices > DM_MAX_DEVICES) {
+ DMDEBUG("too many devices %lu > %d",
+ num_devices, DM_MAX_DEVICES);
+ }
+ dm_setup_args.str = str;
+ dm_setup_args.num_devices = num_devices;
+ DMINFO("will configure %lu devices", num_devices);
dm_early_setup = 1;
return 1;
parse_fail:
- printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n");
+ DMWARN("Invalid arguments supplied to dm=.");
return 0;
}
-
-static void __init dm_setup_drive(void)
+static void __init dm_setup_drives(void)
{
struct mapped_device *md = NULL;
struct dm_table *table = NULL;
struct dm_setup_target *target;
- char *uuid = dm_setup_args.uuid;
+ struct dm_device *dev;
+ char *uuid = NULL;
fmode_t fmode = FMODE_READ;
+ struct dm_device *devices;
- /* Finish parsing the targets. */
- if (dm_setup_parse_targets(dm_setup_args.targets))
- goto parse_fail;
+ devices = dm_parse_args();
- if (dm_create(dm_setup_args.minor, &md)) {
- DMDEBUG("failed to create the device");
- goto dm_create_fail;
- }
- DMDEBUG("created device '%s'", dm_device_name(md));
-
- /* In addition to flagging the table below, the disk must be
- * set explicitly ro/rw. */
- set_disk_ro(dm_disk(md), dm_setup_args.ro);
-
- if (!dm_setup_args.ro)
- fmode |= FMODE_WRITE;
- if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) {
- DMDEBUG("failed to create the table");
- goto dm_table_create_fail;
- }
-
- dm_lock_md_type(md);
- target = dm_setup_args.target;
- while (target) {
- DMINFO("adding target '%llu %llu %s %s'",
- (unsigned long long) target->begin,
- (unsigned long long) target->length, target->type,
- target->params);
- if (dm_table_add_target(table, target->type, target->begin,
- target->length, target->params)) {
- DMDEBUG("failed to add the target to the table");
- goto add_target_fail;
+ for (dev = devices; dev; dev = dev->next) {
+ if (dm_create(dev->minor, &md)) {
+ DMDEBUG("failed to create the device");
+ goto dm_create_fail;
}
- target = target->next;
- }
+ DMDEBUG("created device '%s'", dm_device_name(md));
- if (dm_table_complete(table)) {
- DMDEBUG("failed to complete the table");
- goto table_complete_fail;
- }
+ /*
+ * In addition to flagging the table below, the disk must be
+ * set explicitly ro/rw.
+ */
+ set_disk_ro(dm_disk(md), dev->ro);
- if (dm_get_md_type(md) == DM_TYPE_NONE) {
+ if (!dev->ro)
+ fmode |= FMODE_WRITE;
+ if (dm_table_create(&table, fmode, dev->target_count, md)) {
+ DMDEBUG("failed to create the table");
+ goto dm_table_create_fail;
+ }
+
+ dm_lock_md_type(md);
+
+ for (target = dev->target; target; target = target->next) {
+ DMINFO("adding target '%llu %llu %s %s'",
+ (unsigned long long) target->begin,
+ (unsigned long long) target->length,
+ target->type, target->params);
+ if (dm_table_add_target(table, target->type,
+ target->begin,
+ target->length,
+ target->params)) {
+ DMDEBUG("failed to add the target"
+ " to the table");
+ goto add_target_fail;
+ }
+ }
+ if (dm_table_complete(table)) {
+ DMDEBUG("failed to complete the table");
+ goto table_complete_fail;
+ }
+
+ /* Suspend the device so that we can bind it to the table. */
+ if (dm_suspend(md, 0)) {
+ DMDEBUG("failed to suspend the device pre-bind");
+ goto suspend_fail;
+ }
+
+ /* Initial table load: acquire type of table. */
dm_set_md_type(md, dm_table_get_type(table));
+
+ /* Setup md->queue to reflect md's type. */
if (dm_setup_md_queue(md, table)) {
DMWARN("unable to set up device queue for new table.");
goto setup_md_queue_fail;
}
- } else if (dm_get_md_type(md) != dm_table_get_type(table)) {
- DMWARN("can't change device type after initial table load.");
- goto setup_md_queue_fail;
- }
- /* Suspend the device so that we can bind it to the table. */
- if (dm_suspend(md, 0)) {
- DMDEBUG("failed to suspend the device pre-bind");
- goto suspend_fail;
+ /*
+ * Bind the table to the device. This is the only way
+ * to associate md->map with the table and set the disk
+ * capacity directly.
+ */
+ if (dm_swap_table(md, table)) { /* should return NULL. */
+ DMDEBUG("failed to bind the device to the table");
+ goto table_bind_fail;
+ }
+
+ /* Finally, resume and the device should be ready. */
+ if (dm_resume(md)) {
+ DMDEBUG("failed to resume the device");
+ goto resume_fail;
+ }
+
+ /* Export the dm device via the ioctl interface */
+ if (!strcmp(DM_NO_UUID, dev->uuid))
+ uuid = NULL;
+ if (dm_ioctl_export(md, dev->name, uuid)) {
+ DMDEBUG("failed to export device with given"
+ " name and uuid");
+ goto export_fail;
+ }
+
+ dm_unlock_md_type(md);
+
+ DMINFO("dm-%d is ready", dev->minor);
}
-
- /* Bind the table to the device. This is the only way to associate
- * md->map with the table and set the disk capacity directly. */
- if (dm_swap_table(md, table)) { /* should return NULL. */
- DMDEBUG("failed to bind the device to the table");
- goto table_bind_fail;
- }
-
- /* Finally, resume and the device should be ready. */
- if (dm_resume(md)) {
- DMDEBUG("failed to resume the device");
- goto resume_fail;
- }
-
- /* Export the dm device via the ioctl interface */
- if (!strcmp(DM_NO_UUID, dm_setup_args.uuid))
- uuid = NULL;
- if (dm_ioctl_export(md, dm_setup_args.name, uuid)) {
- DMDEBUG("failed to export device with given name and uuid");
- goto export_fail;
- }
- printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor);
-
- dm_unlock_md_type(md);
- dm_setup_cleanup();
+ dm_setup_cleanup(devices);
return;
export_fail:
resume_fail:
table_bind_fail:
-suspend_fail:
setup_md_queue_fail:
+suspend_fail:
table_complete_fail:
add_target_fail:
dm_unlock_md_type(md);
dm_table_create_fail:
dm_put(md);
dm_create_fail:
- dm_setup_cleanup();
-parse_fail:
- printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n",
- dm_setup_args.minor, dm_setup_args.name);
+ DMWARN("starting dm-%d (%s) failed",
+ dev->minor, dev->name);
+ dm_setup_cleanup(devices);
}
__setup("dm=", dm_setup);
@@ -421,6 +465,6 @@
{
if (!dm_early_setup)
return;
- printk(KERN_INFO "dm: attempting early device configuration.\n");
- dm_setup_drive();
+ DMINFO("attempting early device configuration.");
+ dm_setup_drives();
}