aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c')
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c b/drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c
new file mode 100644
index 000000000000..0526aa3f8438
--- /dev/null
+++ b/drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sophgo sg2042 SoCs pinctrl driver.
+ *
+ * Copyright (C) 2024 Inochi Amaoto <inochiama@outlook.com>
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "../pinctrl-utils.h"
+#include "../pinmux.h"
+
+#include "pinctrl-sg2042.h"
+
+#define PIN_IO_PULL_ONE_ENABLE BIT(0)
+#define PIN_IO_PULL_DIR_UP (BIT(1) | PIN_IO_PULL_ONE_ENABLE)
+#define PIN_IO_PULL_DIR_DOWN (0 | PIN_IO_PULL_ONE_ENABLE)
+#define PIN_IO_PULL_ONE_MASK GENMASK(1, 0)
+
+#define PIN_IO_PULL_UP BIT(2)
+#define PIN_IO_PULL_UP_DONW BIT(3)
+#define PIN_IO_PULL_UP_MASK GENMASK(3, 2)
+
+#define PIN_IO_MUX GENMASK(5, 4)
+#define PIN_IO_DRIVE GENMASK(9, 6)
+#define PIN_IO_SCHMITT_ENABLE BIT(10)
+#define PIN_IO_OUTPUT_ENABLE BIT(11)
+
+struct sg2042_priv {
+ void __iomem *regs;
+};
+
+static u8 sg2042_dt_get_pin_mux(u32 value)
+{
+ return value >> 16;
+}
+
+static inline u32 sg2042_get_pin_reg(struct sophgo_pinctrl *pctrl,
+ const struct sophgo_pin *sp)
+{
+ struct sg2042_priv *priv = pctrl->priv_ctrl;
+ const struct sg2042_pin *pin = sophgo_to_sg2042_pin(sp);
+ void __iomem *reg = priv->regs + pin->offset;
+
+ if (sp->flags & PIN_FLAG_WRITE_HIGH)
+ return readl(reg) >> 16;
+ else
+ return readl(reg) & 0xffff;
+}
+
+static int sg2042_set_pin_reg(struct sophgo_pinctrl *pctrl,
+ const struct sophgo_pin *sp,
+ u32 value, u32 mask)
+{
+ struct sg2042_priv *priv = pctrl->priv_ctrl;
+ const struct sg2042_pin *pin = sophgo_to_sg2042_pin(sp);
+ void __iomem *reg = priv->regs + pin->offset;
+ u32 v = readl(reg);
+
+ if (sp->flags & PIN_FLAG_WRITE_HIGH) {
+ v &= ~(mask << 16);
+ v |= value << 16;
+ } else {
+ v &= ~mask;
+ v |= value;
+ }
+
+ writel(v, reg);
+
+ return 0;
+}
+
+static void sg2042_pctrl_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *seq, unsigned int pin_id)
+{
+ struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ const struct sophgo_pin *sp = sophgo_get_pin(pctrl, pin_id);
+ u32 value, mux;
+
+ value = sg2042_get_pin_reg(pctrl, sp);
+ mux = FIELD_GET(PIN_IO_MUX, value);
+ seq_printf(seq, "mux:%u reg:0x%04x ", mux, value);
+}
+
+const struct pinctrl_ops sg2042_pctrl_ops = {
+ .get_groups_count = pinctrl_generic_get_group_count,
+ .get_group_name = pinctrl_generic_get_group_name,
+ .get_group_pins = pinctrl_generic_get_group_pins,
+ .pin_dbg_show = sg2042_pctrl_dbg_show,
+ .dt_node_to_map = sophgo_pctrl_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+EXPORT_SYMBOL_GPL(sg2042_pctrl_ops);
+
+static void sg2042_set_pinmux_config(struct sophgo_pinctrl *pctrl,
+ const struct sophgo_pin *sp, u32 config)
+{
+ u32 mux = sg2042_dt_get_pin_mux(config);
+
+ if (!(sp->flags & PIN_FLAG_NO_PINMUX))
+ sg2042_set_pin_reg(pctrl, sp, mux, PIN_IO_MUX);
+}
+
+const struct pinmux_ops sg2042_pmx_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = sophgo_pmx_set_mux,
+ .strict = true,
+};
+EXPORT_SYMBOL_GPL(sg2042_pmx_ops);
+
+static int sg2042_pconf_get(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
+{
+ struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ int param = pinconf_to_config_param(*config);
+ const struct sophgo_pin *sp = sophgo_get_pin(pctrl, pin_id);
+ u32 value;
+ u32 arg;
+ bool enabled;
+ int ret;
+
+ if (!sp)
+ return -EINVAL;
+
+ value = sg2042_get_pin_reg(pctrl, sp);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (sp->flags & PIN_FLAG_ONLY_ONE_PULL)
+ arg = FIELD_GET(PIN_IO_PULL_ONE_ENABLE, value);
+ else
+ arg = FIELD_GET(PIN_IO_PULL_UP_MASK, value);
+ enabled = arg == 0;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) {
+ arg = FIELD_GET(PIN_IO_PULL_ONE_MASK, value);
+ enabled = arg == PIN_IO_PULL_DIR_DOWN;
+ } else {
+ enabled = FIELD_GET(PIN_IO_PULL_UP_DONW, value) != 0;
+ }
+ arg = sophgo_pinctrl_typical_pull_down(pctrl, sp, NULL);
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) {
+ arg = FIELD_GET(PIN_IO_PULL_ONE_MASK, value);
+ enabled = arg == PIN_IO_PULL_DIR_UP;
+ } else {
+ enabled = FIELD_GET(PIN_IO_PULL_UP, value) != 0;
+ }
+ arg = sophgo_pinctrl_typical_pull_up(pctrl, sp, NULL);
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH_UA:
+ enabled = FIELD_GET(PIN_IO_OUTPUT_ENABLE, value) != 0;
+ arg = FIELD_GET(PIN_IO_DRIVE, value);
+ ret = sophgo_pinctrl_reg2oc(pctrl, sp, NULL, arg);
+ if (ret < 0)
+ return ret;
+ arg = ret;
+ break;
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ arg = FIELD_GET(PIN_IO_SCHMITT_ENABLE, value);
+ enabled = arg != 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+
+ return enabled ? 0 : -EINVAL;
+}
+
+static int sg2042_pinconf_compute_config(struct sophgo_pinctrl *pctrl,
+ const struct sophgo_pin *sp,
+ unsigned long *configs,
+ unsigned int num_configs,
+ u32 *value, u32 *mask)
+{
+ int i;
+ u16 v = 0, m = 0;
+ int ret;
+
+ if (!sp)
+ return -EINVAL;
+
+ for (i = 0; i < num_configs; i++) {
+ int param = pinconf_to_config_param(configs[i]);
+ u32 arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) {
+ v &= ~PIN_IO_PULL_ONE_ENABLE;
+ m |= PIN_IO_PULL_ONE_ENABLE;
+ } else {
+ v &= ~PIN_IO_PULL_UP_MASK;
+ m |= PIN_IO_PULL_UP_MASK;
+ }
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) {
+ v &= ~PIN_IO_PULL_ONE_MASK;
+ v |= PIN_IO_PULL_DIR_DOWN;
+ m |= PIN_IO_PULL_ONE_MASK;
+ } else {
+ v |= PIN_IO_PULL_UP_DONW;
+ m |= PIN_IO_PULL_UP_DONW;
+ }
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) {
+ v &= ~PIN_IO_PULL_ONE_MASK;
+ v |= PIN_IO_PULL_DIR_UP;
+ m |= PIN_IO_PULL_ONE_MASK;
+ } else {
+ v |= PIN_IO_PULL_UP;
+ m |= PIN_IO_PULL_UP;
+ }
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH_UA:
+ v &= ~(PIN_IO_DRIVE | PIN_IO_OUTPUT_ENABLE);
+ if (arg != 0) {
+ ret = sophgo_pinctrl_oc2reg(pctrl, sp, NULL, arg);
+ if (ret < 0)
+ return ret;
+ if (!(sp->flags & PIN_FLAG_NO_OEX_EN))
+ v |= PIN_IO_OUTPUT_ENABLE;
+ v |= FIELD_PREP(PIN_IO_DRIVE, ret);
+ }
+ m |= PIN_IO_DRIVE | PIN_IO_OUTPUT_ENABLE;
+ break;
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ v |= PIN_IO_SCHMITT_ENABLE;
+ m |= PIN_IO_SCHMITT_ENABLE;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ }
+
+ *value = v;
+ *mask = m;
+
+ return 0;
+}
+
+const struct pinconf_ops sg2042_pconf_ops = {
+ .pin_config_get = sg2042_pconf_get,
+ .pin_config_set = sophgo_pconf_set,
+ .pin_config_group_set = sophgo_pconf_group_set,
+ .is_generic = true,
+};
+EXPORT_SYMBOL_GPL(sg2042_pconf_ops);
+
+static int sophgo_pinctrl_init(struct platform_device *pdev,
+ struct sophgo_pinctrl *pctrl)
+{
+ struct sg2042_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ pctrl->priv_ctrl = priv;
+
+ return 0;
+}
+
+const struct sophgo_cfg_ops sg2042_cfg_ops = {
+ .pctrl_init = sophgo_pinctrl_init,
+ .compute_pinconf_config = sg2042_pinconf_compute_config,
+ .set_pinconf_config = sg2042_set_pin_reg,
+ .set_pinmux_config = sg2042_set_pinmux_config,
+};
+EXPORT_SYMBOL_GPL(sg2042_cfg_ops);