diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
| commit | f4cdf7ca9a1fdcca413157df19753f388a5a224e (patch) | |
| tree | 60a31895b2446cd7fa27514e8c2843534a0de0da /drivers/pinctrl/pinconf.c | |
| download | linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip | |
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab:
- v4l2-core: added ISP statistics support and per-block validation
- v4l2-core: Allow unknown HDR10 white point and luminance
- New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor
- New codec: Milos: VPU v2.0 codec support
- isp driver: gained support for Dreamchip RPPX1 ISP framework
- vsp1 driver: gained support for RZ/T2H and RZ/N2H
- Novalake driver: gained CVS support for new NVL hardware
- dvb-core: fix feed leak on failed DMX_ADD_PID
- several driver fixes, cleanups and minor improvements
* tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits)
media: ipu-bridge: check all DMI entries when overriding sensor rotation
media: v4l2-async: avoid deleting unlinked ASC entry on link error
media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement
media: intel/ipu6: fix async notifier cleanup leak on parse error
media: staging/ipu7: fix async notifier UAF on probe error path
media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path
media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem()
media: rcar-isp: Fix VSPX reference leaks
media: rcar-isp: Release ISPCORE resources
media: i2c: imx415: Release runtime PM reference on VBLANK error
media: i2c: imx415: Return test pattern write errors
media: renesas: vsp1: Declare index variables in for loop statement
media: renesas: vsp1: Make reset control optional to support platforms without a reset line
media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs
media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs
media: nxp: imx8-isi: Add additional 32-bit RGB format support
media: nxp: imx8-isi: Add 16-bit raw Bayer format support
media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams
media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks
media: nxp: imx8-isi: Correct color map between V4L2 and ISI
...
Diffstat (limited to 'drivers/pinctrl/pinconf.c')
| -rw-r--r-- | drivers/pinctrl/pinconf.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c new file mode 100644 index 000000000..81686844d --- /dev/null +++ b/drivers/pinctrl/pinconf.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Core driver for the pin config portions of the pin control subsystem + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij <linus.walleij@linaro.org> + */ +#define pr_fmt(fmt) "pinconfig core: " fmt + +#include <linux/array_size.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/slab.h> + +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> + +#include "core.h" +#include "pinconf.h" + +int pinconf_check_ops(struct pinctrl_dev *pctldev) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + + /* We have to be able to config the pins in SOME way */ + if (!ops->pin_config_set && !ops->pin_config_group_set) { + dev_err(pctldev->dev, + "pinconf has to be able to set a pins config\n"); + return -EINVAL; + } + return 0; +} + +int pinconf_validate_map(const struct pinctrl_map *map, int i) +{ + if (!map->data.configs.group_or_pin) { + pr_err("failed to register map %s (%d): no group/pin given\n", + map->name, i); + return -EINVAL; + } + + if (!map->data.configs.num_configs || + !map->data.configs.configs) { + pr_err("failed to register map %s (%d): no configs given\n", + map->name, i); + return -EINVAL; + } + + return 0; +} + +int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + + if (!ops || !ops->pin_config_get) { + dev_dbg(pctldev->dev, + "cannot get pin configuration, .pin_config_get missing in driver\n"); + return -ENOTSUPP; + } + + return ops->pin_config_get(pctldev, pin, config); +} + +int pin_config_group_get(const char *dev_name, const char *pin_group, + unsigned long *config) +{ + struct pinctrl_dev *pctldev; + const struct pinconf_ops *ops; + int selector, ret; + + pctldev = get_pinctrl_dev_from_devname(dev_name); + if (!pctldev) { + ret = -EINVAL; + return ret; + } + + mutex_lock(&pctldev->mutex); + + ops = pctldev->desc->confops; + + if (!ops || !ops->pin_config_group_get) { + dev_dbg(pctldev->dev, + "cannot get configuration for pin group, missing group config get function in driver\n"); + ret = -ENOTSUPP; + goto unlock; + } + + selector = pinctrl_get_group_selector(pctldev, pin_group); + if (selector < 0) { + ret = selector; + goto unlock; + } + + ret = ops->pin_config_group_get(pctldev, selector, config); + +unlock: + mutex_unlock(&pctldev->mutex); + return ret; +} + +int pinconf_map_to_setting(const struct pinctrl_map *map, + struct pinctrl_setting *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; + int pin; + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + pin = pin_get_from_name(pctldev, + map->data.configs.group_or_pin); + if (pin < 0) { + dev_err(pctldev->dev, "could not map pin config for \"%s\"", + map->data.configs.group_or_pin); + return pin; + } + setting->data.configs.group_or_pin = pin; + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + pin = pinctrl_get_group_selector(pctldev, + map->data.configs.group_or_pin); + if (pin < 0) { + dev_err(pctldev->dev, "could not map group config for \"%s\"", + map->data.configs.group_or_pin); + return pin; + } + setting->data.configs.group_or_pin = pin; + break; + default: + return -EINVAL; + } + + setting->data.configs.num_configs = map->data.configs.num_configs; + setting->data.configs.configs = map->data.configs.configs; + + return 0; +} + +void pinconf_free_setting(const struct pinctrl_setting *setting) +{ +} + +int pinconf_apply_setting(const struct pinctrl_setting *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; + const struct pinconf_ops *ops = pctldev->desc->confops; + int ret; + + if (!ops) { + dev_err(pctldev->dev, "missing confops\n"); + return -EINVAL; + } + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + if (!ops->pin_config_set) { + dev_err(pctldev->dev, "missing pin_config_set op\n"); + return -EINVAL; + } + ret = ops->pin_config_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs, + setting->data.configs.num_configs); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_set op failed for pin %d\n", + setting->data.configs.group_or_pin); + return ret; + } + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + if (!ops->pin_config_group_set) { + dev_err(pctldev->dev, + "missing pin_config_group_set op\n"); + return -EINVAL; + } + ret = ops->pin_config_group_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs, + setting->data.configs.num_configs); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_group_set op failed for group %d\n", + setting->data.configs.group_or_pin); + return ret; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, size_t nconfigs) +{ + const struct pinconf_ops *ops; + + ops = pctldev->desc->confops; + if (!ops || !ops->pin_config_set) + return -ENOTSUPP; + + return ops->pin_config_set(pctldev, pin, configs, nconfigs); +} + +#ifdef CONFIG_DEBUG_FS + +static void pinconf_show_config(struct seq_file *s, struct pinctrl_dev *pctldev, + unsigned long *configs, unsigned int num_configs) +{ + const struct pinconf_ops *confops; + int i; + + if (pctldev) + confops = pctldev->desc->confops; + else + confops = NULL; + + for (i = 0; i < num_configs; i++) { + seq_puts(s, "config "); + if (confops && confops->pin_config_config_dbg_show) + confops->pin_config_config_dbg_show(pctldev, s, + configs[i]); + else + seq_printf(s, "%08lx", configs[i]); + seq_putc(s, '\n'); + } +} + +void pinconf_show_map(struct seq_file *s, const struct pinctrl_map *map) +{ + struct pinctrl_dev *pctldev; + + pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); + + switch (map->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + seq_puts(s, "pin "); + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + seq_puts(s, "group "); + break; + default: + break; + } + + seq_printf(s, "%s\n", map->data.configs.group_or_pin); + + pinconf_show_config(s, pctldev, map->data.configs.configs, + map->data.configs.num_configs); +} + +void pinconf_show_setting(struct seq_file *s, + const struct pinctrl_setting *setting) +{ + struct pinctrl_dev *pctldev = setting->pctldev; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + struct pin_desc *desc; + + switch (setting->type) { + case PIN_MAP_TYPE_CONFIGS_PIN: + desc = pin_desc_get(setting->pctldev, + setting->data.configs.group_or_pin); + seq_printf(s, "pin %s (%d)", desc->name, + setting->data.configs.group_or_pin); + break; + case PIN_MAP_TYPE_CONFIGS_GROUP: + seq_printf(s, "group %s (%d)", + pctlops->get_group_name(pctldev, + setting->data.configs.group_or_pin), + setting->data.configs.group_or_pin); + break; + default: + break; + } + + /* + * FIXME: We should really get the pin controller to dump the config + * values, so they can be decoded to something meaningful. + */ + pinconf_show_config(s, pctldev, setting->data.configs.configs, + setting->data.configs.num_configs); +} + +static void pinconf_dump_pin(struct pinctrl_dev *pctldev, + struct seq_file *s, int pin) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + + /* no-op when not using generic pin config */ + pinconf_generic_dump_pins(pctldev, s, NULL, pin); + if (ops && ops->pin_config_dbg_show) + ops->pin_config_dbg_show(pctldev, s, pin); +} + +static int pinconf_pins_show(struct seq_file *s, void *what) +{ + struct pinctrl_dev *pctldev = s->private; + unsigned int i, pin; + + seq_puts(s, "Pin config settings per pin\n"); + seq_puts(s, "Format: pin (name): configs\n"); + + mutex_lock(&pctldev->mutex); + + /* The pin number can be retrived from the pin controller descriptor */ + for (i = 0; i < pctldev->desc->npins; i++) { + struct pin_desc *desc; + + pin = pctldev->desc->pins[i].number; + desc = pin_desc_get(pctldev, pin); + /* Skip if we cannot search the pin */ + if (!desc) + continue; + + seq_printf(s, "pin %d (%s): ", pin, desc->name); + + pinconf_dump_pin(pctldev, s, pin); + seq_putc(s, '\n'); + } + + mutex_unlock(&pctldev->mutex); + + return 0; +} + +static void pinconf_dump_group(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int selector, + const char *gname) +{ + const struct pinconf_ops *ops = pctldev->desc->confops; + + /* no-op when not using generic pin config */ + pinconf_generic_dump_pins(pctldev, s, gname, 0); + if (ops && ops->pin_config_group_dbg_show) + ops->pin_config_group_dbg_show(pctldev, s, selector); +} + +static int pinconf_groups_show(struct seq_file *s, void *what) +{ + struct pinctrl_dev *pctldev = s->private; + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + unsigned int selector = 0; + unsigned int ngroups; + + if (!pctlops->get_groups_count || !pctlops->get_group_name) + return 0; + + ngroups = pctlops->get_groups_count(pctldev); + + seq_puts(s, "Pin config settings per pin group\n"); + seq_puts(s, "Format: group (name): configs\n"); + + while (selector < ngroups) { + const char *gname = pctlops->get_group_name(pctldev, selector); + + seq_printf(s, "%u (%s): ", selector, gname); + pinconf_dump_group(pctldev, s, selector, gname); + seq_putc(s, '\n'); + selector++; + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(pinconf_pins); +DEFINE_SHOW_ATTRIBUTE(pinconf_groups); + +void pinconf_init_device_debugfs(struct dentry *devroot, + struct pinctrl_dev *pctldev) +{ + debugfs_create_file("pinconf-pins", 0444, + devroot, pctldev, &pinconf_pins_fops); + debugfs_create_file("pinconf-groups", 0444, + devroot, pctldev, &pinconf_groups_fops); +} + +#endif |
