summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHolmes Chou <holmeschou@google.com>2023-02-09 07:55:02 +0000
committerTreeHugger Robot <treehugger-gerrit@google.com>2023-03-29 08:18:51 +0000
commit4711d94f29bf4536a40b3e1fd8d273cec00557fd (patch)
tree5c76ebf78b8984179d58b8d281bfb90174c54fd4
parentdec46b2ff43597ef1e7fc174aaf2ec750aeb703a (diff)
downloadlwis-4711d94f29bf4536a40b3e1fd8d273cec00557fd.tar.gz
LWIS: Unifying device power sequences
Sensor modules (which consist of OIS, actuator, image sensor and EEPROM) usually share the same power sequence. It would be advantageous for us to unify the definition of the power sequences into one place, and properly reference count them. So that we won't wasting cycles performing power up (or down) sequence multiple times. Bug: 254536545 Test: GCA, CTS Change-Id: I69d46dd17a2a751bc50d12219cc3b3adc1cce710 Signed-off-by: Holmes Chou <holmeschou@google.com>
-rw-r--r--lwis_device.c207
-rw-r--r--lwis_device.h14
-rw-r--r--lwis_dt.c76
3 files changed, 276 insertions, 21 deletions
diff --git a/lwis_device.c b/lwis_device.c
index 81d7ada..e28c1aa 100644
--- a/lwis_device.c
+++ b/lwis_device.c
@@ -390,6 +390,177 @@ static void lwis_assign_top_to_other(struct lwis_device *top_dev)
mutex_unlock(&core.lock);
}
+static bool need_to_power_up(struct lwis_device *lwis_dev)
+{
+ int i;
+
+ if (lwis_dev->power_seq_handler == NULL) {
+ return true;
+ }
+
+ mutex_lock(&core.lock);
+ for (i = 0; i < MAX_UNIFIED_POWER_DEVICE; i++) {
+ if (core.unified_dev_pwr_map[i].dev_node_seq == NULL) {
+ break;
+ }
+ if (core.unified_dev_pwr_map[i].dev_node_seq == lwis_dev->power_seq_handler) {
+ if (core.unified_dev_pwr_map[i].count == 0) {
+ break;
+ }
+ mutex_unlock(&core.lock);
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: Already power up\n", __func__);
+#endif
+ return false;
+ }
+ }
+ mutex_unlock(&core.lock);
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: Need power up\n", __func__);
+#endif
+ return true;
+}
+
+static bool need_to_power_down(struct lwis_device *lwis_dev)
+{
+ int i;
+
+ if (lwis_dev->power_seq_handler == NULL) {
+ return true;
+ }
+
+ mutex_lock(&core.lock);
+ for (i = 0; i < MAX_UNIFIED_POWER_DEVICE; i++) {
+ if (core.unified_dev_pwr_map[i].dev_node_seq == NULL) {
+ break;
+ }
+ if (core.unified_dev_pwr_map[i].dev_node_seq == lwis_dev->power_seq_handler) {
+ if (core.unified_dev_pwr_map[i].count == 1) {
+ break;
+ }
+ mutex_unlock(&core.lock);
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: No need power down\n", __func__);
+#endif
+ return false;
+ }
+ }
+ mutex_unlock(&core.lock);
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: Ready to power down\n", __func__);
+#endif
+ return true;
+}
+
+static int increase_unified_power_count(struct lwis_device *lwis_dev)
+{
+ int i;
+
+ if (lwis_dev->power_seq_handler == NULL) {
+ return 0;
+ }
+
+ mutex_lock(&core.lock);
+ for (i = 0; i < MAX_UNIFIED_POWER_DEVICE; i++) {
+ if (core.unified_dev_pwr_map[i].dev_node_seq == NULL) {
+ break;
+ }
+ if (core.unified_dev_pwr_map[i].dev_node_seq == lwis_dev->power_seq_handler) {
+ core.unified_dev_pwr_map[i].count++;
+ if (core.unified_dev_pwr_map[i].count == 1) {
+ core.unified_dev_pwr_map[i].hold_dev = lwis_dev;
+ }
+ mutex_unlock(&core.lock);
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: power counter = %d\n", __func__,
+ core.unified_dev_pwr_map[i].count);
+#endif
+ return 0;
+ }
+ }
+ if (i >= MAX_UNIFIED_POWER_DEVICE) {
+ dev_err(lwis_dev->dev, "Unified power sequence map overflow\n");
+ mutex_unlock(&core.lock);
+ return -EOVERFLOW;
+ }
+
+ core.unified_dev_pwr_map[i].dev_node_seq = lwis_dev->power_seq_handler;
+ core.unified_dev_pwr_map[i].hold_dev = lwis_dev;
+ core.unified_dev_pwr_map[i].count++;
+ mutex_unlock(&core.lock);
+
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: power counter = %d\n", __func__,
+ core.unified_dev_pwr_map[i].count);
+#endif
+ return 0;
+}
+
+static int decrease_unified_power_count(struct lwis_device *lwis_dev)
+{
+ int i;
+
+ if (lwis_dev->power_seq_handler == NULL) {
+ return 0;
+ }
+
+ mutex_lock(&core.lock);
+ for (i = 0; i < MAX_UNIFIED_POWER_DEVICE; i++) {
+ if (core.unified_dev_pwr_map[i].dev_node_seq == NULL) {
+ break;
+ }
+ if (core.unified_dev_pwr_map[i].dev_node_seq == lwis_dev->power_seq_handler) {
+ if (core.unified_dev_pwr_map[i].count > 0) {
+ core.unified_dev_pwr_map[i].count--;
+ if (core.unified_dev_pwr_map[i].count == 0) {
+ core.unified_dev_pwr_map[i].hold_dev = NULL;
+ }
+ }
+ mutex_unlock(&core.lock);
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: power counter = %d\n", __func__,
+ core.unified_dev_pwr_map[i].count);
+#endif
+ return 0;
+ }
+ }
+ mutex_unlock(&core.lock);
+ dev_err(lwis_dev->dev, "Unified power sequence not found\n");
+ return -ENODEV;
+}
+
+static struct lwis_device *get_power_down_dev(struct lwis_device *lwis_dev)
+{
+ int i;
+
+ if (lwis_dev->power_seq_handler == NULL) {
+ return lwis_dev;
+ }
+
+ mutex_lock(&core.lock);
+ for (i = 0; i < MAX_UNIFIED_POWER_DEVICE; i++) {
+ if (core.unified_dev_pwr_map[i].dev_node_seq == NULL) {
+ break;
+ }
+ if (core.unified_dev_pwr_map[i].dev_node_seq == lwis_dev->power_seq_handler) {
+ mutex_unlock(&core.lock);
+#ifdef LWIS_PWR_SEQ_DEBUG
+ dev_info(lwis_dev->dev, "%s: power dev = %s\n", __func__,
+ core.unified_dev_pwr_map[i].hold_dev->name);
+#endif
+ return core.unified_dev_pwr_map[i].hold_dev;
+ }
+ }
+ if (i >= MAX_UNIFIED_POWER_DEVICE) {
+ dev_err(lwis_dev->dev, "Unified power sequence not found\n");
+ mutex_unlock(&core.lock);
+ return lwis_dev;
+ }
+ mutex_unlock(&core.lock);
+
+ return lwis_dev;
+}
+
int lwis_dev_process_power_sequence(struct lwis_device *lwis_dev,
struct lwis_device_power_sequence_list *list, bool set_active,
bool skip_error)
@@ -865,15 +1036,20 @@ int lwis_dev_power_up_locked(struct lwis_device *lwis_dev)
mutex_lock(i2c_dev->group_i2c_lock);
}
if (lwis_dev->power_up_sequence) {
- ret = lwis_dev_process_power_sequence(lwis_dev, lwis_dev->power_up_sequence,
- /*set_active=*/true, /*skip_error=*/false);
- if (ret) {
- dev_err(lwis_dev->dev, "Error lwis_dev_process_power_sequence (%d)\n", ret);
- if (lwis_dev->type == DEVICE_TYPE_I2C) {
- mutex_unlock(i2c_dev->group_i2c_lock);
+ if (need_to_power_up(lwis_dev)) {
+ ret = lwis_dev_process_power_sequence(lwis_dev, lwis_dev->power_up_sequence,
+ /*set_active=*/true,
+ /*skip_error=*/false);
+ if (ret) {
+ dev_err(lwis_dev->dev,
+ "Error lwis_dev_process_power_sequence (%d)\n", ret);
+ if (lwis_dev->type == DEVICE_TYPE_I2C) {
+ mutex_unlock(i2c_dev->group_i2c_lock);
+ }
+ goto error_power_up;
}
- goto error_power_up;
}
+ increase_unified_power_count(lwis_dev);
} else {
ret = lwis_dev_power_up_by_default(lwis_dev);
if (ret) {
@@ -1058,12 +1234,19 @@ int lwis_dev_power_down_locked(struct lwis_device *lwis_dev)
mutex_lock(i2c_dev->group_i2c_lock);
}
if (lwis_dev->power_down_sequence) {
- ret = lwis_dev_process_power_sequence(lwis_dev, lwis_dev->power_down_sequence,
- /*set_active=*/false, /*skip_error=*/true);
- if (ret) {
- dev_err(lwis_dev->dev, "Error lwis_dev_process_power_sequence (%d)\n", ret);
- last_error = ret;
+ if (need_to_power_down(lwis_dev)) {
+ struct lwis_device *power_dev = get_power_down_dev(lwis_dev);
+ ret = lwis_dev_process_power_sequence(power_dev,
+ power_dev->power_down_sequence,
+ /*set_active=*/false,
+ /*skip_error=*/true);
+ if (ret) {
+ dev_err(lwis_dev->dev,
+ "Error lwis_dev_process_power_sequence (%d)\n", ret);
+ last_error = ret;
+ }
}
+ decrease_unified_power_count(lwis_dev);
} else {
ret = lwis_dev_power_down_by_default(lwis_dev);
if (ret) {
diff --git a/lwis_device.h b/lwis_device.h
index 1cfa5c4..62cb199 100644
--- a/lwis_device.h
+++ b/lwis_device.h
@@ -45,6 +45,7 @@
#define TRANSACTION_HASH_BITS 8
#define PERIODIC_IO_HASH_BITS 8
#define BTS_UNSUPPORTED -1
+#define MAX_UNIFIED_POWER_DEVICE 8
/* Forward declaration for lwis_device. This is needed for the declaration for
lwis_device_subclass_operations data struct. */
@@ -59,6 +60,16 @@ int lwis_allocator_init(struct lwis_device *lwis_dev);
void lwis_allocator_release(struct lwis_device *lwis_dev);
/*
+ * struct lwis_dev_pwr_ref_cnt
+ * This struct is to store the power up/down sequence reference count
+ */
+struct lwis_dev_pwr_ref_cnt {
+ struct device_node *dev_node_seq;
+ struct lwis_device *hold_dev;
+ int count;
+};
+
+/*
* struct lwis_core
* This struct applies to all LWIS devices that are defined in the
* device tree.
@@ -71,6 +82,7 @@ struct lwis_core {
dev_t lwis_devt;
int device_major;
struct list_head lwis_dev_list;
+ struct lwis_dev_pwr_ref_cnt unified_dev_pwr_map[MAX_UNIFIED_POWER_DEVICE];
struct dentry *dbg_root;
};
@@ -258,6 +270,8 @@ struct lwis_device {
/* BTS scenario index */
unsigned int bts_scenario;
+ /* Power sequence handler */
+ struct device_node *power_seq_handler;
/* Power up sequence information */
struct lwis_device_power_sequence_list *power_up_sequence;
/* Power down sequence information */
diff --git a/lwis_dt.c b/lwis_dt.c
index bdfb6ba..da27d00 100644
--- a/lwis_dt.c
+++ b/lwis_dt.c
@@ -881,7 +881,8 @@ static void parse_bitwidths(struct lwis_device *lwis_dev)
}
static int parse_power_seqs(struct lwis_device *lwis_dev, const char *seq_name,
- struct lwis_device_power_sequence_list **list)
+ struct lwis_device_power_sequence_list **list,
+ struct device_node *dev_node_seq)
{
char str_seq_name[LWIS_MAX_NAME_STRING_LEN];
char str_seq_type[LWIS_MAX_NAME_STRING_LEN];
@@ -906,6 +907,9 @@ static int parse_power_seqs(struct lwis_device *lwis_dev, const char *seq_name,
dev = &lwis_dev->plat_dev->dev;
dev_node = dev->of_node;
*list = NULL;
+ if (dev_node_seq) {
+ dev_node = dev_node_seq;
+ }
power_seq_count = of_property_count_strings(dev_node, str_seq_name);
power_seq_type_count = of_property_count_strings(dev_node, str_seq_type);
@@ -1057,6 +1061,49 @@ error_parse_power_seqs:
return ret;
}
+static int parse_unified_power_seqs(struct lwis_device *lwis_dev)
+{
+ struct device *dev;
+ struct device_node *dev_node;
+ struct device_node *dev_node_seq;
+ int count;
+ int ret = 0;
+
+ dev = &lwis_dev->plat_dev->dev;
+ dev_node = dev->of_node;
+
+ count = of_property_count_elems_of_size(dev_node, "power-seq", sizeof(u32));
+
+ /* No power-seq found, or entry does not exist, just return */
+ if (count <= 0) {
+ lwis_dev->power_seq_handler = NULL;
+ return 0;
+ }
+
+ dev_node_seq = of_parse_phandle(dev_node, "power-seq", 0);
+ if (!dev_node_seq) {
+ pr_err("Can't get power-seq node\n");
+ return -EINVAL;
+ }
+
+ ret = parse_power_seqs(lwis_dev, "power-up", &lwis_dev->power_up_sequence, dev_node_seq);
+ if (ret) {
+ pr_err("Error parsing power-up-seqs\n");
+ return ret;
+ }
+
+ ret = parse_power_seqs(lwis_dev, "power-down", &lwis_dev->power_down_sequence,
+ dev_node_seq);
+ if (ret) {
+ pr_err("Error parsing power-down-seqs\n");
+ return ret;
+ }
+
+ lwis_dev->power_seq_handler = dev_node_seq;
+
+ return ret;
+}
+
static int parse_pm_hibernation(struct lwis_device *lwis_dev)
{
struct device_node *dev_node;
@@ -1167,25 +1214,36 @@ int lwis_base_parse_dt(struct lwis_device *lwis_dev)
return ret;
}
- ret = parse_power_seqs(lwis_dev, "power-up", &lwis_dev->power_up_sequence);
+ ret = parse_unified_power_seqs(lwis_dev);
if (ret) {
- pr_err("Error parsing power-up-seqs\n");
+ pr_err("Error parse_unified_power_seqs\n");
return ret;
}
- ret = parse_power_seqs(lwis_dev, "power-down", &lwis_dev->power_down_sequence);
- if (ret) {
- pr_err("Error parsing power-down-seqs\n");
- return ret;
+ if (lwis_dev->power_up_sequence == NULL) {
+ ret = parse_power_seqs(lwis_dev, "power-up", &lwis_dev->power_up_sequence, NULL);
+ if (ret) {
+ pr_err("Error parsing power-up-seqs\n");
+ return ret;
+ }
+ }
+
+ if (lwis_dev->power_down_sequence == NULL) {
+ ret = parse_power_seqs(lwis_dev, "power-down", &lwis_dev->power_down_sequence,
+ NULL);
+ if (ret) {
+ pr_err("Error parsing power-down-seqs\n");
+ return ret;
+ }
}
- ret = parse_power_seqs(lwis_dev, "suspend", &lwis_dev->suspend_sequence);
+ ret = parse_power_seqs(lwis_dev, "suspend", &lwis_dev->suspend_sequence, NULL);
if (ret) {
pr_err("Error parsing suspend-seqs\n");
return ret;
}
- ret = parse_power_seqs(lwis_dev, "resume", &lwis_dev->resume_sequence);
+ ret = parse_power_seqs(lwis_dev, "resume", &lwis_dev->resume_sequence, NULL);
if (ret) {
pr_err("Error parsing resume-seqs\n");
return ret;