linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Krishna Yarlagadda <kyarlagadda@nvidia.com>
To: <linux-tegra@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-doc@vger.kernel.org>, <linux-i2c@vger.kernel.org>,
	<linux-mmc@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Cc: <thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
	<robh@kernel.org>, <krzk+dt@kernel.org>, <conor+dt@kernel.org>,
	<corbet@lwn.net>, <andi.shyti@kernel.org>,
	<wsa+renesas@sang-engineering.com>, <ulf.hansson@linaro.org>,
	<adrian.hunter@intel.com>, <digetx@gmail.com>,
	<ldewangan@nvidia.com>, <kyarlagadda@nvidia.com>,
	<mkumard@nvidia.com>
Subject: [RFC PATCH 02/11] soc: tegra: Add config setting framework
Date: Tue, 7 May 2024 04:21:30 +0530	[thread overview]
Message-ID: <20240506225139.57647-3-kyarlagadda@nvidia.com> (raw)
In-Reply-To: <20240506225139.57647-1-kyarlagadda@nvidia.com>

Config settings are defined as a property per field and have different
modes per device. Each mode contains multiple properties and a device
can have multiple modes.
Config framework parses device tree and provides a list of register
settings with mask per mode to be applied by the controller.
Add APIs to parse list of register config settings and to get config
from the list by name to be applied.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Signed-off-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
---
 MAINTAINERS                   |   1 +
 drivers/soc/tegra/Makefile    |   1 +
 drivers/soc/tegra/tegra-cfg.c | 147 ++++++++++++++++++++++++++++++++++
 include/soc/tegra/tegra-cfg.h |  87 ++++++++++++++++++++
 4 files changed, 236 insertions(+)
 create mode 100644 drivers/soc/tegra/tegra-cfg.c
 create mode 100644 include/soc/tegra/tegra-cfg.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c788ff0506c3..9eeb749ef234 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21997,6 +21997,7 @@ R:	Laxman Dewangan <ldewangan@nvidia.com>
 R:	Krishna Yarlagadda <kyarlagadda@nvidia.com>
 S:	Supported
 F:	Documentation/misc-devices/tegra-cfg.rst
+F:	drivers/soc/tegra/tegra-cfg.c
 
 TEGRA PWM DRIVER
 M:	Thierry Reding <thierry.reding@gmail.com>
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index 01059619e764..8d0c8dc62c8c 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
 obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o
 obj-$(CONFIG_SOC_TEGRA30_VOLTAGE_COUPLER) += regulators-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_186_SOC) += ari-tegra186.o
+obj-y += tegra-cfg.o
diff --git a/drivers/soc/tegra/tegra-cfg.c b/drivers/soc/tegra/tegra-cfg.c
new file mode 100644
index 000000000000..53d181bff40a
--- /dev/null
+++ b/drivers/soc/tegra/tegra-cfg.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 NVIDIA CORPORATION.  All rights reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/tegra-cfg.h>
+
+static int tegra_cfg_update_reg_info(struct device_node *cfg_node,
+				     const struct tegra_cfg_field_desc *field,
+				     struct tegra_cfg_reg *regs,
+				     struct tegra_cfg *cfg)
+{
+	int ret;
+	unsigned int k, value = 0;
+
+	ret = of_property_read_u32(cfg_node, field->name, &value);
+	if (ret)
+		return ret;
+
+	/*
+	 * Find matching register for this field in register info. Field info
+	 * has details of register offset.
+	 */
+	for (k = 0; k < cfg->num_regs; ++k) {
+		if (regs[k].offset == field->offset)
+			break;
+	}
+
+	/* If register not found, add new at end of list */
+	if (k == cfg->num_regs) {
+		cfg->num_regs++;
+		regs[k].offset = field->offset;
+	}
+
+	/* add field value to register */
+	value = value << __ffs(field->mask);
+	regs[k].value |= value & field->mask;
+	regs[k].mask |= field->mask;
+
+	return 0;
+}
+
+/*
+ * Initialize config list. Parse config node for properties (register fields).
+ * Get list of configs and value of fields populated in tegra_cfg_desc.
+ * Consolidate field data in reg offset, mask & value format in tegra_cfg.
+ * Repeat for each config and store in tegra_cfg_list.
+ */
+static struct tegra_cfg_list *tegra_cfg_init(struct device *dev,
+					     const struct device_node *np,
+					     const struct tegra_cfg_desc *cfg_desc)
+{
+	struct device_node *np_cfg = NULL, *child;
+	struct tegra_cfg_reg *regs;
+	struct tegra_cfg_list *list;
+	struct tegra_cfg *cfg;
+	const struct tegra_cfg_field_desc *fields;
+	unsigned int count, i;
+	int ret;
+
+	if (np)
+		np_cfg = of_get_child_by_name(np, "config");
+	if (!np_cfg)
+		return ERR_PTR(-ENODEV);
+
+	count = of_get_child_count(np_cfg);
+	if (count <= 0) {
+		dev_dbg(dev, "Node %s: No child node for config settings\n",
+			np->name);
+		return ERR_PTR(-ENODEV);
+	}
+
+	list = devm_kzalloc(dev, sizeof(*list), GFP_KERNEL);
+	if (!list)
+		return ERR_PTR(-ENOMEM);
+	list->num_cfg = 0;
+	list->cfg = NULL;
+
+	/* allocate mem for all configurations */
+	list->cfg = devm_kcalloc(dev, count, sizeof(*list->cfg),
+				 GFP_KERNEL);
+	if (!list->cfg)
+		return ERR_PTR(-ENOMEM);
+
+	fields = cfg_desc->fields;
+	count = 0;
+	/*
+	 * Iterate through all configurations.
+	 */
+	for_each_available_child_of_node(np_cfg, child) {
+		cfg = &list->cfg[count];
+
+		regs = devm_kcalloc(dev, cfg_desc->num_regs,
+				    sizeof(*regs), GFP_KERNEL);
+		if (!regs)
+			return ERR_PTR(-ENOMEM);
+
+		cfg->name = child->name;
+		cfg->regs = regs;
+		cfg->num_regs = 0;
+
+		/* Look for all fields in 'child' config */
+		for (i = 0; i < cfg_desc->num_fields; i++) {
+			ret = tegra_cfg_update_reg_info(child, &fields[i],
+							regs, cfg);
+			if (ret < 0)
+				continue;
+		}
+		count++;
+	}
+
+	list->num_cfg = count;
+
+	return list;
+}
+
+struct tegra_cfg *
+tegra_cfg_get_by_name(struct device *dev,
+		      const struct tegra_cfg_list *list,
+		      const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < list->num_cfg; ++i) {
+		if (!strcmp(list->cfg[i].name, name))
+			return &list->cfg[i];
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(tegra_cfg_get_by_name);
+
+struct tegra_cfg_list *tegra_cfg_get(struct device *dev,
+				     const struct device_node *np,
+				     const struct tegra_cfg_desc *cfg_desc)
+{
+	return tegra_cfg_init(dev, np, cfg_desc);
+}
+EXPORT_SYMBOL(tegra_cfg_get);
diff --git a/include/soc/tegra/tegra-cfg.h b/include/soc/tegra/tegra-cfg.h
new file mode 100644
index 000000000000..ece6f63a83c1
--- /dev/null
+++ b/include/soc/tegra/tegra-cfg.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024, NVIDIA CORPORATION.  All rights reserved.
+ */
+
+#ifndef __SOC_TEGRA_CFG_H__
+#define __SOC_TEGRA_CFG_H__
+
+#include <linux/types.h>
+
+/**
+ * Config settings are a list of DT properties holding each field's recommended
+ * value. Field info is held in tegra_cfg_field and tegra_cfg_desc.
+ * Data of all fields in a single register are parsed and stored in
+ * tegra_cfg_reg. Struct tegra_cfg_list contains list of configurations
+ * and each config tegra_cfg contains register list.
+ * Client drivers provide field and register data through tegra_cfg_desc.
+ */
+
+/**
+ * Register field and DT property mapping.
+ * @name: device property name of the field.
+ * @offset: offset of register from base.
+ * @mask: mask of field within register.
+ */
+struct tegra_cfg_field_desc {
+	const char *name;
+	u32 offset;
+	u32 mask;
+};
+
+#define TEGRA_CFG_FIELD(fname, roffset, fmask)	\
+{						\
+	.name = fname,				\
+	.offset = roffset,			\
+	.mask = fmask,				\
+}
+
+/**
+ * Configuration setting from controller where it passes the total number of
+ * registers having config, and their register field names.
+ */
+struct tegra_cfg_desc {
+	unsigned int num_regs;
+	unsigned int num_fields;
+	const struct tegra_cfg_field_desc *fields;
+};
+
+/**
+ * Configuration register info generated by combining all field config settings
+ * in device tree of a register.
+ * @offset: offset of register from base.
+ * @mask: generated mask from aggregate of all field settings read from dt.
+ * @value: generated value by combining all field properties read from dt.
+ */
+struct tegra_cfg_reg {
+	u32 offset;
+	u32 mask;
+	u32 value;
+};
+
+/**
+ * Per config info of all registers.
+ */
+struct tegra_cfg {
+	const char *name;
+	unsigned int num_regs;
+	struct tegra_cfg_reg *regs;
+};
+
+/**
+ * Config settings list.
+ */
+struct tegra_cfg_list {
+	unsigned int num_cfg;
+	struct tegra_cfg *cfg;
+};
+
+struct tegra_cfg *
+tegra_cfg_get_by_name(struct device *dev,
+		      const struct tegra_cfg_list *list,
+		      const char *cfg_name);
+
+struct tegra_cfg_list *tegra_cfg_get(struct device *dev,
+				     const struct device_node *np,
+				     const struct tegra_cfg_desc *cfg_dev);
+#endif /* __SOC_TEGRA_CFG_H__ */
-- 
2.43.2


  parent reply	other threads:[~2024-05-06 22:52 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-06 22:51 [RFC PATCH 00/11] Introduce Tegra register config settings Krishna Yarlagadda
2024-05-06 22:51 ` [RFC PATCH 01/11] Documentation: Introduce config settings framework Krishna Yarlagadda
2024-05-06 22:51 ` Krishna Yarlagadda [this message]
2024-05-06 22:51 ` [RFC PATCH 03/11] soc: tegra: config settings binding document Krishna Yarlagadda
2024-05-07  6:32   ` Krzysztof Kozlowski
2024-05-24  8:01     ` Thierry Reding
2024-05-06 22:51 ` [RFC PATCH 04/11] i2c: dt-bindings: configuration settings Krishna Yarlagadda
2024-05-07  6:34   ` Krzysztof Kozlowski
2024-05-07 12:35   ` Rob Herring (Arm)
2024-05-06 22:51 ` [RFC PATCH 05/11] i2c: core: Avoid config node enumeration Krishna Yarlagadda
2024-05-07  6:35   ` Krzysztof Kozlowski
2024-05-06 22:51 ` [RFC PATCH 06/11] i2c: tegra: split clock initialization code Krishna Yarlagadda
2024-05-06 22:51 ` [RFC PATCH 07/11] i2c: tegra: config settings for interface timings Krishna Yarlagadda
2024-05-06 22:51 ` [RFC PATCH 08/11] arm64: tegra: I2C " Krishna Yarlagadda
2024-05-06 22:51 ` [RFC PATCH 09/11] sdhci: dt-bindings: configuration settings Krishna Yarlagadda
2024-05-07  6:37   ` Krzysztof Kozlowski
2024-05-06 22:51 ` [RFC PATCH 10/11] mmc: host: tegra: config settings for timing Krishna Yarlagadda
2024-05-06 22:51 ` [RFC PATCH 11/11] arm64: tegra: SDHCI timing settings Krishna Yarlagadda
2024-05-07  6:38 ` [RFC PATCH 00/11] Introduce Tegra register config settings Krzysztof Kozlowski
2024-05-24  7:52   ` Thierry Reding

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240506225139.57647-3-kyarlagadda@nvidia.com \
    --to=kyarlagadda@nvidia.com \
    --cc=adrian.hunter@intel.com \
    --cc=andi.shyti@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=devicetree@vger.kernel.org \
    --cc=digetx@gmail.com \
    --cc=jonathanh@nvidia.com \
    --cc=krzk+dt@kernel.org \
    --cc=ldewangan@nvidia.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=mkumard@nvidia.com \
    --cc=robh@kernel.org \
    --cc=thierry.reding@gmail.com \
    --cc=ulf.hansson@linaro.org \
    --cc=wsa+renesas@sang-engineering.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).