summaryrefslogtreecommitdiff
path: root/zephyr/drivers/cros_cbi
diff options
context:
space:
mode:
authorDawid Niedzwiecki <dn@semihalf.com>2021-05-11 13:37:40 +0200
committerCommit Bot <commit-bot@chromium.org>2021-05-14 10:25:25 +0000
commit6e1449eb477f73dbba8bddf4c3dae907a93bb065 (patch)
treea0bf9fae326690f1c4e02f5a1ee950e9eb7e4a2d /zephyr/drivers/cros_cbi
parent4ab13c794e5f5c8a1b85ec08f932e6751f3b4516 (diff)
downloadchrome-ec-6e1449eb477f73dbba8bddf4c3dae907a93bb065.tar.gz
zephyr: Rework CBI to be used as a driver
Rework the CBI support in Zephyr to be used via driver API. Change also approach what to do with SSFC - let sensor drivers decide how to handle alternative sensors. BUG=b:183990188 BRANCH=none TEST=Add alternative motion sensors to the device tree, modify CBI SSFC with 'cbi set 8 value 4', reboot EC and verify that the new sensors are used with the 'accelinfo' command. Signed-off-by: Dawid Niedzwiecki <dn@semihalf.com> Change-Id: I701af96bfa7a17333220530a5c63b8e8aaeb0d6b Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2862727 Reviewed-by: Yuval Peress <peress@chromium.org>
Diffstat (limited to 'zephyr/drivers/cros_cbi')
-rw-r--r--zephyr/drivers/cros_cbi/CMakeLists.txt3
-rw-r--r--zephyr/drivers/cros_cbi/cros_cbi.c218
2 files changed, 221 insertions, 0 deletions
diff --git a/zephyr/drivers/cros_cbi/CMakeLists.txt b/zephyr/drivers/cros_cbi/CMakeLists.txt
new file mode 100644
index 0000000000..1ef8eccf1f
--- /dev/null
+++ b/zephyr/drivers/cros_cbi/CMakeLists.txt
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: Apache-2.0
+
+zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_CBI cros_cbi.c)
diff --git a/zephyr/drivers/cros_cbi/cros_cbi.c b/zephyr/drivers/cros_cbi/cros_cbi.c
new file mode 100644
index 0000000000..0aaa5e2b1a
--- /dev/null
+++ b/zephyr/drivers/cros_cbi/cros_cbi.c
@@ -0,0 +1,218 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <drivers/cros_cbi.h>
+#include "cros_board_info.h"
+#include <logging/log.h>
+
+LOG_MODULE_REGISTER(cros_cbi, LOG_LEVEL_ERR);
+
+/* CBI SSFC part */
+
+/* This part of the driver is about CBI SSFC part.
+ * Actually, two "compatible" values are handle here -
+ * named_cbi_ssfc_value and named_cbi_ssfc. named_cbi_ssfc_value nodes are
+ * grandchildren of the named_cbi_ssfc node. named_cbi_ssfc_value is introduced
+ * to iterate over grandchildren of the named_cbi_ssfc(macro
+ * DT_FOREACH_CHILD can not be nested) and it can be pointed by a sensor dts to
+ * indicate alternative usage.
+ */
+#define DT_DRV_COMPAT named_cbi_ssfc_value
+
+BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(named_cbi_ssfc) < 2,
+ "More than 1 CBI SSFS node");
+#define CBI_SSFC_NODE DT_INST(0, named_cbi_ssfc)
+
+#define CBI_SSFC_INIT_DEFAULT_ID(id) \
+ do { \
+ if (DT_PROP(id, default)) { \
+ cached_ssfc.CBI_SSFC_UNION_ENTRY_NAME(DT_PARENT(id)) = \
+ DT_PROP(id, value); \
+ } \
+ } while (0);
+
+#define CBI_SSFC_INIT_DEFAULT(inst) CBI_SSFC_INIT_DEFAULT_ID(DT_DRV_INST(inst))
+
+#define CBI_SSFC_VALUE_ARRAY_ID(id) \
+ [CBI_SSFC_VALUE_ID(id)] = DT_PROP(id, value),
+
+#define CBI_SSFC_VALUE_ARRAY(inst) CBI_SSFC_VALUE_ARRAY_ID(DT_DRV_INST(inst))
+
+#define CBI_SSFC_VALUE_BUILD_ASSERT(inst) \
+ BUILD_ASSERT(DT_INST_PROP(inst, value) <= UINT8_MAX, \
+ "CBI SSFS value too big");
+
+#define CBI_SSFC_PARENT_VALUE_CASE_GENERATE(value_id, value_parent) \
+ case value_id: \
+ return value_parent;
+
+#define CBI_SSFC_PARENT_VALUE_CASE_ID(id) \
+ CBI_SSFC_PARENT_VALUE_CASE_GENERATE( \
+ CBI_SSFC_VALUE_ID(id), \
+ cached_ssfc.CBI_SSFC_UNION_ENTRY_NAME(DT_PARENT(id)))
+
+#define CBI_SSFC_PARENT_VALUE_CASE(inst) \
+ CBI_SSFC_PARENT_VALUE_CASE_ID(DT_DRV_INST(inst))
+
+#define CBI_SSFC_UNION_ENTRY_NAME(id) DT_CAT(cbi_ssfc_, id)
+#define CBI_SSFC_UNION_ENTRY(id) \
+ uint32_t CBI_SSFC_UNION_ENTRY_NAME(id) \
+ : DT_PROP(id, size);
+
+#define CBI_SSFC_PLUS_FIELD_SIZE(id) +DT_PROP(id, size)
+#define CBI_SSFC_FIELDS_SIZE \
+ (0 COND_CODE_1( \
+ DT_NODE_EXISTS(CBI_SSFC_NODE), \
+ (DT_FOREACH_CHILD(CBI_SSFC_NODE, CBI_SSFC_PLUS_FIELD_SIZE)), \
+ ()))
+
+BUILD_ASSERT(CBI_SSFC_FIELDS_SIZE <= 32, "CBI SSFS is bigger than 32 bits");
+
+/*
+ * Define union bit fields based on the device tree entries. Example:
+ * cbi-ssfc {
+ * compatible = "named-cbi-ssfc";
+ *
+ * base_sensor {
+ * enum-name = "BASE_SENSOR";
+ * size = <3>;
+ * bmi160 {
+ * compatible = "named-cbi-ssfc-value";
+ * status = "okay";
+ * value = <1>;
+ * };
+ * };
+ * lid_sensor {
+ * enum-name = "LID_SENSOR";
+ * size = <3>;
+ * bma255 {
+ * compatible = "named-cbi-ssfc-value";
+ * status = "okay";
+ * value = <1>;
+ * };
+ * };
+ * lightbar {
+ * enum-name = "LIGHTBAR";
+ * size = <2>;
+ * 10_led {
+ * compatible = "named-cbi-ssfc-value";
+ * status = "okay";
+ * value = <1>;
+ * };
+ * };
+ * };
+ * Should be converted into
+ * union cbi_ssfc {
+ * struct {
+ * uint32_t cbi_ssfc_DT_N_S_cbi_ssfc_S_base_sensor:3
+ * uint32_t cbi_ssfc_DT_N_S_cbi_ssfc_S_lid_sensor:3
+ * uint32_t cbi_ssfc_DT_N_S_cbi_ssfc_S_lightbar:2
+ * uint32_t reserved : 24;
+ * };
+ * uint32_t raw_value;
+ * };
+ */
+union cbi_ssfc {
+ struct {
+#if DT_NODE_EXISTS(CBI_SSFC_NODE)
+ DT_FOREACH_CHILD(CBI_SSFC_NODE, CBI_SSFC_UNION_ENTRY)
+ uint32_t reserved : (32 - CBI_SSFC_FIELDS_SIZE);
+#endif
+ };
+ uint32_t raw_value;
+};
+
+BUILD_ASSERT(sizeof(union cbi_ssfc) == sizeof(uint32_t),
+ "CBI SSFS structure exceedes 32 bits");
+
+DT_INST_FOREACH_STATUS_OKAY(CBI_SSFC_VALUE_BUILD_ASSERT)
+
+static const uint8_t ssfc_values[] = {
+ DT_INST_FOREACH_STATUS_OKAY(CBI_SSFC_VALUE_ARRAY)
+};
+static union cbi_ssfc cached_ssfc;
+
+/* CBI SSFC part end */
+
+/* Device config */
+struct cros_cbi_config {
+ /* SSFC values for specific configs */
+ const uint8_t *ssfc_values;
+};
+
+/* Device data */
+struct cros_cbi_data {
+ /* Cached SSFC configs */
+ union cbi_ssfc cached_ssfc;
+};
+
+/* CBI SSFC part */
+
+static void cros_cbi_ssfc_init(const struct device *dev)
+{
+ struct cros_cbi_data *data = (struct cros_cbi_data *)(dev->data);
+
+ if (cbi_get_ssfc(&data->cached_ssfc.raw_value) != EC_SUCCESS) {
+ DT_INST_FOREACH_STATUS_OKAY(CBI_SSFC_INIT_DEFAULT)
+ }
+
+ LOG_INF("Read CBI SSFC : 0x%08X\n", data->cached_ssfc.raw_value);
+}
+
+static uint32_t cros_cbi_ssfc_get_parent_field_value(union cbi_ssfc cached_ssfc,
+ enum cbi_ssfc_value_id value_id)
+{
+ switch (value_id) {
+ DT_INST_FOREACH_STATUS_OKAY(CBI_SSFC_PARENT_VALUE_CASE)
+ default:
+ LOG_ERR("CBI SSFC parent field value not found: %d\n",
+ value_id);
+ return 0;
+ }
+}
+
+static int cros_cbi_ec_ssfc_check_match(const struct device *dev,
+ enum cbi_ssfc_value_id value_id)
+{
+ struct cros_cbi_data *data = (struct cros_cbi_data *)(dev->data);
+ struct cros_cbi_config *cfg = (struct cros_cbi_config *)(dev->config);
+
+ return cros_cbi_ssfc_get_parent_field_value(data->cached_ssfc,
+ value_id) ==
+ cfg->ssfc_values[value_id];
+}
+
+/* CBI SSFC part end */
+#undef DT_DRV_COMPAT
+
+static int cros_cbi_ec_init(const struct device *dev)
+{
+ cros_cbi_ssfc_init(dev);
+
+ return 0;
+}
+
+/* cros ec cbi driver registration */
+static const struct cros_cbi_driver_api cros_cbi_driver_api = {
+ .init = cros_cbi_ec_init,
+ .ssfc_check_match = cros_cbi_ec_ssfc_check_match,
+};
+
+static int cbi_init(const struct device *dev)
+{
+ ARG_UNUSED(dev);
+
+ return 0;
+}
+
+static const struct cros_cbi_config cros_cbi_cfg = {
+ .ssfc_values = ssfc_values,
+};
+
+static struct cros_cbi_data cros_cbi_data;
+
+DEVICE_DEFINE(cros_cbi, CROS_CBI_LABEL, cbi_init, NULL, &cros_cbi_data,
+ &cros_cbi_cfg, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
+ &cros_cbi_driver_api);