root/drivers/pinctrl/pinctrl-k230.c
// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
/*
 * Copyright (C) 2024 Canaan Bright Sight Co. Ltd
 * Copyright (C) 2024 Ze Huang <18771902331@163.com>
 */
#include <linux/module.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>

#include "core.h"
#include "pinconf.h"

#define K230_NPINS 64

#define K230_SHIFT_ST           (0)
#define K230_SHIFT_DS           (1)
#define K230_SHIFT_BIAS         (5)
#define K230_SHIFT_PD           (5)
#define K230_SHIFT_PU           (6)
#define K230_SHIFT_OE           (7)
#define K230_SHIFT_IE           (8)
#define K230_SHIFT_MSC          (9)
#define K230_SHIFT_SL           (10)
#define K230_SHIFT_SEL          (11)

#define K230_PC_ST              BIT(0)
#define K230_PC_DS              GENMASK(4, 1)
#define K230_PC_PD              BIT(5)
#define K230_PC_PU              BIT(6)
#define K230_PC_BIAS            GENMASK(6, 5)
#define K230_PC_OE              BIT(7)
#define K230_PC_IE              BIT(8)
#define K230_PC_MSC             BIT(9)
#define K230_PC_SL              BIT(10)
#define K230_PC_SEL             GENMASK(13, 11)

struct k230_pin_conf {
        unsigned int            func;
        unsigned long           *configs;
        unsigned int            nconfigs;
};

struct k230_pin_group {
        const char              *name;
        unsigned int            *pins;
        unsigned int            num_pins;

        struct k230_pin_conf    *data;
};

struct k230_pmx_func {
        const char              *name;
        const char              **groups;
        unsigned int            *group_idx;
        unsigned int            ngroups;
};

struct k230_pinctrl {
        struct device           *dev;
        struct pinctrl_desc     pctl;
        struct pinctrl_dev      *pctl_dev;
        struct regmap           *regmap_base;
        void __iomem            *base;
        struct k230_pin_group   *groups;
        unsigned int            ngroups;
        struct k230_pmx_func    *functions;
        unsigned int            nfunctions;
};

static const struct regmap_config k230_regmap_config = {
        .name           = "canaan,pinctrl",
        .reg_bits       = 32,
        .val_bits       = 32,
        .max_register   = 0x100,
        .reg_stride     = 4,
};

static int k230_get_groups_count(struct pinctrl_dev *pctldev)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

        return info->ngroups;
}

static const char *k230_get_group_name(struct pinctrl_dev *pctldev,
                                       unsigned int selector)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

        return info->groups[selector].name;
}

static int k230_get_group_pins(struct pinctrl_dev *pctldev,
                               unsigned int selector,
                               const unsigned int **pins,
                               unsigned int *num_pins)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

        if (selector >= info->ngroups)
                return -EINVAL;

        *pins = info->groups[selector].pins;
        *num_pins = info->groups[selector].num_pins;

        return 0;
}

static inline const struct k230_pmx_func *k230_name_to_funtion(
                const struct k230_pinctrl *info, const char *name)
{
        unsigned int i;

        for (i = 0; i < info->nfunctions; i++) {
                if (!strcmp(info->functions[i].name, name))
                        return &info->functions[i];
        }

        return NULL;
}

static struct pinctrl_pin_desc k230_pins[] = {
        PINCTRL_PIN(0,  "IO0"),  PINCTRL_PIN(1,  "IO1"),  PINCTRL_PIN(2,  "IO2"),
        PINCTRL_PIN(3,  "IO3"),  PINCTRL_PIN(4,  "IO4"),  PINCTRL_PIN(5,  "IO5"),
        PINCTRL_PIN(6,  "IO6"),  PINCTRL_PIN(7,  "IO7"),  PINCTRL_PIN(8,  "IO8"),
        PINCTRL_PIN(9,  "IO9"),  PINCTRL_PIN(10, "IO10"), PINCTRL_PIN(11, "IO11"),
        PINCTRL_PIN(12, "IO12"), PINCTRL_PIN(13, "IO13"), PINCTRL_PIN(14, "IO14"),
        PINCTRL_PIN(15, "IO15"), PINCTRL_PIN(16, "IO16"), PINCTRL_PIN(17, "IO17"),
        PINCTRL_PIN(18, "IO18"), PINCTRL_PIN(19, "IO19"), PINCTRL_PIN(20, "IO20"),
        PINCTRL_PIN(21, "IO21"), PINCTRL_PIN(22, "IO22"), PINCTRL_PIN(23, "IO23"),
        PINCTRL_PIN(24, "IO24"), PINCTRL_PIN(25, "IO25"), PINCTRL_PIN(26, "IO26"),
        PINCTRL_PIN(27, "IO27"), PINCTRL_PIN(28, "IO28"), PINCTRL_PIN(29, "IO29"),
        PINCTRL_PIN(30, "IO30"), PINCTRL_PIN(31, "IO31"), PINCTRL_PIN(32, "IO32"),
        PINCTRL_PIN(33, "IO33"), PINCTRL_PIN(34, "IO34"), PINCTRL_PIN(35, "IO35"),
        PINCTRL_PIN(36, "IO36"), PINCTRL_PIN(37, "IO37"), PINCTRL_PIN(38, "IO38"),
        PINCTRL_PIN(39, "IO39"), PINCTRL_PIN(40, "IO40"), PINCTRL_PIN(41, "IO41"),
        PINCTRL_PIN(42, "IO42"), PINCTRL_PIN(43, "IO43"), PINCTRL_PIN(44, "IO44"),
        PINCTRL_PIN(45, "IO45"), PINCTRL_PIN(46, "IO46"), PINCTRL_PIN(47, "IO47"),
        PINCTRL_PIN(48, "IO48"), PINCTRL_PIN(49, "IO49"), PINCTRL_PIN(50, "IO50"),
        PINCTRL_PIN(51, "IO51"), PINCTRL_PIN(52, "IO52"), PINCTRL_PIN(53, "IO53"),
        PINCTRL_PIN(54, "IO54"), PINCTRL_PIN(55, "IO55"), PINCTRL_PIN(56, "IO56"),
        PINCTRL_PIN(57, "IO57"), PINCTRL_PIN(58, "IO58"), PINCTRL_PIN(59, "IO59"),
        PINCTRL_PIN(60, "IO60"), PINCTRL_PIN(61, "IO61"), PINCTRL_PIN(62, "IO62"),
        PINCTRL_PIN(63, "IO63")
};

static void k230_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
                                      struct seq_file *s, unsigned int offset)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
        u32 val, bias, drive, input, slew, schmitt, power;
        struct k230_pin_group *grp = k230_pins[offset].drv_data;
        static const char * const biasing[] = {
                        "pull none", "pull down", "pull up", "" };
        static const char * const enable[] = {
                        "disable", "enable" };
        static const char * const power_source[] = {
                        "3V3", "1V8" };

        regmap_read(info->regmap_base, offset * 4, &val);

        drive   = (val & K230_PC_DS) >> K230_SHIFT_DS;
        bias    = (val & K230_PC_BIAS) >> K230_SHIFT_BIAS;
        input   = (val & K230_PC_IE) >> K230_SHIFT_IE;
        slew    = (val & K230_PC_SL) >> K230_SHIFT_SL;
        schmitt = (val & K230_PC_ST) >> K230_SHIFT_ST;
        power   = (val & K230_PC_MSC) >> K230_SHIFT_MSC;

        seq_printf(s, "%s - strength %d - %s - %s - slewrate %s - schmitt %s - %s",
                   grp ? grp->name : "unknown",
                   drive,
                   biasing[bias],
                   input ? "input" : "output",
                   enable[slew],
                   enable[schmitt],
                   power_source[power]);
}

static int k230_dt_node_to_map(struct pinctrl_dev *pctldev,
                               struct device_node *np_config,
                               struct pinctrl_map **map,
                               unsigned int *num_maps)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
        struct device *dev = info->pctl_dev->dev;
        const struct k230_pmx_func *func;
        const struct k230_pin_group *grp;
        struct pinctrl_map *new_map;
        int map_num, i, j, idx;
        unsigned int grp_id;

        func = k230_name_to_funtion(info, np_config->name);
        if (!func) {
                dev_err(dev, "function %s not found\n", np_config->name);
                return -EINVAL;
        }

        map_num = 0;
        for (i = 0; i < func->ngroups; ++i) {
                grp_id = func->group_idx[i];
                /* npins of config map plus a mux map */
                map_num += info->groups[grp_id].num_pins + 1;
        }

        new_map = kzalloc_objs(*new_map, map_num);
        if (!new_map)
                return -ENOMEM;
        *map = new_map;
        *num_maps = map_num;

        idx = 0;
        for (i = 0; i < func->ngroups; ++i) {
                grp_id = func->group_idx[i];
                grp = &info->groups[grp_id];
                new_map[idx].type = PIN_MAP_TYPE_MUX_GROUP;
                new_map[idx].data.mux.group = grp->name;
                new_map[idx].data.mux.function = np_config->name;
                idx++;

                for (j = 0; j < grp->num_pins; ++j) {
                        new_map[idx].type = PIN_MAP_TYPE_CONFIGS_PIN;
                        new_map[idx].data.configs.group_or_pin =
                                pin_get_name(pctldev, grp->pins[j]);
                        new_map[idx].data.configs.configs =
                                grp->data[j].configs;
                        new_map[idx].data.configs.num_configs =
                                grp->data[j].nconfigs;
                        idx++;
                }
        }

        return 0;
}

static void k230_dt_free_map(struct pinctrl_dev *pctldev,
                             struct pinctrl_map *map, unsigned int num_maps)
{
        kfree(map);
}

static const struct pinctrl_ops k230_pctrl_ops = {
        .get_groups_count       = k230_get_groups_count,
        .get_group_name         = k230_get_group_name,
        .get_group_pins         = k230_get_group_pins,
        .pin_dbg_show           = k230_pinctrl_pin_dbg_show,
        .dt_node_to_map         = k230_dt_node_to_map,
        .dt_free_map            = k230_dt_free_map,
};

static int k230_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
                            unsigned long *config)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
        enum pin_config_param param = pinconf_to_config_param(*config);
        unsigned int val, arg;

        regmap_read(info->regmap_base, pin * 4, &val);

        switch (param) {
        case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
                arg = (val & K230_PC_ST) ? 1 : 0;
                break;
        case PIN_CONFIG_DRIVE_STRENGTH:
                arg = (val & K230_PC_DS) >> K230_SHIFT_DS;
                break;
        case PIN_CONFIG_BIAS_DISABLE:
                arg = (val & K230_PC_BIAS) ? 0 : 1;
                break;
        case PIN_CONFIG_BIAS_PULL_DOWN:
                arg = (val & K230_PC_PD) ? 1 : 0;
                break;
        case PIN_CONFIG_BIAS_PULL_UP:
                arg = (val & K230_PC_PU) ? 1 : 0;
                break;
        case PIN_CONFIG_OUTPUT_ENABLE:
                arg = (val & K230_PC_OE) ? 1 : 0;
                break;
        case PIN_CONFIG_INPUT_ENABLE:
                arg = (val & K230_PC_IE) ? 1 : 0;
                break;
        case PIN_CONFIG_POWER_SOURCE:
                arg = (val & K230_PC_MSC) ? 1 : 0;
                break;
        case PIN_CONFIG_SLEW_RATE:
                arg = (val & K230_PC_SL) ? 1 : 0;
                break;
        default:
                return -EINVAL;
        }

        *config = pinconf_to_config_packed(param, arg);

        return 0;
}

static int k230_pinconf_set_param(struct pinctrl_dev *pctldev, unsigned int pin,
                                  enum pin_config_param param, unsigned int arg)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
        unsigned int val;

        regmap_read(info->regmap_base, pin * 4, &val);

        switch (param) {
        case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
                if (arg)
                        val |= K230_PC_ST;
                else
                        val &= ~K230_PC_ST;
                break;
        case PIN_CONFIG_DRIVE_STRENGTH:
                val &= ~K230_PC_DS;
                val |= (arg << K230_SHIFT_DS) & K230_PC_DS;
                break;
        case PIN_CONFIG_BIAS_DISABLE:
                val &= ~K230_PC_BIAS;
                break;
        case PIN_CONFIG_BIAS_PULL_DOWN:
                if (!arg)
                        return -EINVAL;
                val |= K230_PC_PD;
                break;
        case PIN_CONFIG_BIAS_PULL_UP:
                if (!arg)
                        return -EINVAL;
                val |= K230_PC_PU;
                break;
        case PIN_CONFIG_OUTPUT_ENABLE:
                if (!arg)
                        return -EINVAL;
                val |= K230_PC_OE;
                break;
        case PIN_CONFIG_INPUT_ENABLE:
                if (!arg)
                        return -EINVAL;
                val |= K230_PC_IE;
                break;
        case PIN_CONFIG_POWER_SOURCE:
                if (arg)
                        val |= K230_PC_MSC;
                else
                        val &= ~K230_PC_MSC;
                break;
        case PIN_CONFIG_SLEW_RATE:
                if (arg)
                        val |= K230_PC_SL;
                else
                        val &= ~K230_PC_SL;
                break;
        default:
                return -EINVAL;
        }

        regmap_write(info->regmap_base, pin * 4, val);

        return 0;
}

static int k230_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
                            unsigned long *configs, unsigned int num_configs)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
        struct device *dev = info->pctl_dev->dev;
        enum pin_config_param param;
        unsigned int arg, i;
        int ret;

        if (pin >= K230_NPINS) {
                dev_err(dev, "pin number out of range\n");
                return -EINVAL;
        }

        for (i = 0; i < num_configs; i++) {
                param = pinconf_to_config_param(configs[i]);
                arg = pinconf_to_config_argument(configs[i]);
                ret = k230_pinconf_set_param(pctldev, pin, param, arg);
                if (ret)
                        return ret;
        }

        return 0;
}

static void k230_pconf_dbg_show(struct pinctrl_dev *pctldev,
                                struct seq_file *s, unsigned int pin)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
        unsigned int val;

        regmap_read(info->regmap_base, pin * 4, &val);

        seq_printf(s, " 0x%08x", val);
}

static const struct pinconf_ops k230_pinconf_ops = {
        .is_generic             = true,
        .pin_config_get         = k230_pinconf_get,
        .pin_config_set         = k230_pinconf_set,
        .pin_config_dbg_show    = k230_pconf_dbg_show,
};

static int k230_get_functions_count(struct pinctrl_dev *pctldev)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

        return info->nfunctions;
}

static const char *k230_get_fname(struct pinctrl_dev *pctldev,
                                  unsigned int selector)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

        return info->functions[selector].name;
}

static int k230_get_groups(struct pinctrl_dev *pctldev, unsigned int selector,
                           const char * const **groups, unsigned int *num_groups)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

        *groups = info->functions[selector].groups;
        *num_groups = info->functions[selector].ngroups;

        return 0;
}

static int k230_set_mux(struct pinctrl_dev *pctldev, unsigned int selector,
                        unsigned int group)
{
        struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
        const struct k230_pin_conf *data = info->groups[group].data;
        struct k230_pin_group *grp = &info->groups[group];
        const unsigned int *pins = grp->pins;
        struct regmap *regmap;
        unsigned int value, mask;
        int cnt, reg;

        regmap = info->regmap_base;

        for (cnt = 0; cnt < grp->num_pins; cnt++) {
                reg = pins[cnt] * 4;
                value = data[cnt].func << K230_SHIFT_SEL;
                mask = K230_PC_SEL;
                regmap_update_bits(regmap, reg, mask, value);
                k230_pins[pins[cnt]].drv_data = grp;
        }

        return 0;
}

static const struct pinmux_ops k230_pmxops = {
        .get_functions_count    = k230_get_functions_count,
        .get_function_name      = k230_get_fname,
        .get_function_groups    = k230_get_groups,
        .set_mux                = k230_set_mux,
        .strict                 = true,
};

static int k230_pinctrl_parse_groups(struct device_node *np,
                                     struct k230_pin_group *grp,
                                     struct k230_pinctrl *info,
                                     unsigned int index)
{
        struct device *dev = info->dev;
        const __be32 *list;
        int size, i, ret;

        grp->name = np->name;

        list = of_get_property(np, "pinmux", &size);
        if (!list) {
                dev_err(dev, "failed to get pinmux property\n");
                return -EINVAL;
        }
        size /= sizeof(*list);

        grp->num_pins = size;
        grp->pins = devm_kcalloc(dev, grp->num_pins, sizeof(*grp->pins),
                                 GFP_KERNEL);
        grp->data = devm_kcalloc(dev, grp->num_pins, sizeof(*grp->data),
                                 GFP_KERNEL);
        if (!grp->pins || !grp->data)
                return -ENOMEM;

        for (i = 0; i < size; i++) {
                unsigned int mux_data = be32_to_cpu(*list++);

                grp->pins[i] = (mux_data >> 8);
                grp->data[i].func = (mux_data & 0xff);

                ret = pinconf_generic_parse_dt_config(np, NULL,
                                                      &grp->data[i].configs,
                                                      &grp->data[i].nconfigs);
                if (ret)
                        return ret;
        }

        return 0;
}

static int k230_pinctrl_parse_functions(struct device_node *np,
                                        struct k230_pinctrl *info,
                                        unsigned int index)
{
        struct device *dev = info->dev;
        struct k230_pmx_func *func;
        struct k230_pin_group *grp;
        static unsigned int idx, i;
        int ret;

        func = &info->functions[index];

        func->name = np->name;
        func->ngroups = of_get_child_count(np);
        if (func->ngroups <= 0)
                return 0;

        func->groups = devm_kcalloc(dev, func->ngroups,
                                    sizeof(*func->groups), GFP_KERNEL);
        func->group_idx = devm_kcalloc(dev, func->ngroups,
                                       sizeof(*func->group_idx), GFP_KERNEL);
        if (!func->groups || !func->group_idx)
                return -ENOMEM;

        i = 0;

        for_each_child_of_node_scoped(np, child) {
                func->groups[i] = child->name;
                func->group_idx[i] = idx;
                grp = &info->groups[idx];
                idx++;
                ret = k230_pinctrl_parse_groups(child, grp, info, i++);
                if (ret)
                        return ret;
        }

        return 0;
}

static void k230_pinctrl_child_count(struct k230_pinctrl *info,
                                     struct device_node *np)
{
        for_each_child_of_node_scoped(np, child) {
                info->nfunctions++;
                info->ngroups += of_get_child_count(child);
        }
}

static int k230_pinctrl_parse_dt(struct platform_device *pdev,
                                 struct k230_pinctrl *info)
{
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        unsigned int i;
        int ret;

        k230_pinctrl_child_count(info, np);

        info->functions = devm_kcalloc(dev, info->nfunctions,
                                       sizeof(*info->functions), GFP_KERNEL);
        info->groups = devm_kcalloc(dev, info->ngroups,
                                    sizeof(*info->groups), GFP_KERNEL);
        if (!info->functions || !info->groups)
                return -ENOMEM;

        i = 0;

        for_each_child_of_node_scoped(np, child) {
                ret = k230_pinctrl_parse_functions(child, info, i++);
                if (ret) {
                        dev_err(dev, "failed to parse function\n");
                        return ret;
                }
        }

        return 0;
}

static int k230_pinctrl_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        struct k230_pinctrl *info;
        struct pinctrl_desc *pctl;
        int ret;

        info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;

        info->dev = dev;

        pctl = &info->pctl;

        pctl->name      = "k230-pinctrl";
        pctl->owner     = THIS_MODULE;
        pctl->pins      = k230_pins;
        pctl->npins     = ARRAY_SIZE(k230_pins);
        pctl->pctlops   = &k230_pctrl_ops;
        pctl->pmxops    = &k230_pmxops;
        pctl->confops   = &k230_pinconf_ops;

        info->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(info->base))
                return PTR_ERR(info->base);

        info->regmap_base = devm_regmap_init_mmio(dev, info->base,
                                                  &k230_regmap_config);
        if (IS_ERR(info->regmap_base))
                return dev_err_probe(dev, PTR_ERR(info->regmap_base),
                                     "failed to init regmap\n");

        ret = k230_pinctrl_parse_dt(pdev, info);
        if (ret)
                return ret;

        info->pctl_dev = devm_pinctrl_register(dev, pctl, info);
        if (IS_ERR(info->pctl_dev))
                return dev_err_probe(dev, PTR_ERR(info->pctl_dev),
                                     "devm_pinctrl_register failed\n");

        return 0;
}

static const struct of_device_id k230_dt_ids[] = {
        { .compatible = "canaan,k230-pinctrl", },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, k230_dt_ids);

static struct platform_driver k230_pinctrl_driver = {
        .probe = k230_pinctrl_probe,
        .driver = {
                .name = "k230-pinctrl",
                .of_match_table = k230_dt_ids,
        },
};
module_platform_driver(k230_pinctrl_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ze Huang <18771902331@163.com>");
MODULE_DESCRIPTION("Canaan K230 pinctrl driver");