The ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The AIN pins can be used as ADC inputs, or as general purpose outputs. Support changing pin function (GPO / ADC) and the gpo output control. Signed-off-by: Matti Vaittinen <mazziesaccount@xxxxxxxxx> --- NOTE: This patch is not properly tested. More thorough testing is to be done prior v2 if this pinmux approach makes sense. drivers/pinctrl/Kconfig | 11 ++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-bd79124.c | 276 ++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-bd79124.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 95a8e2b9a614..7dd9bb0d1ab4 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -145,6 +145,17 @@ config PINCTRL_AW9523 Say yes to enable pinctrl and GPIO support for the AW9523(B). +config PINCTRL_BD79124 + tristate "Rohm BD79124 ADC/GPO" + depends on MFD_ROHM_BD79124 + select PINMUX + select GPIOLIB + help + The Rohm BD79124 is a 12-bit, 8-channel, SAR ADC. The analog input + pins can also be configured to be used as general purpose outputs. + + Say yes to enable the pinmux and GPOs. + config PINCTRL_BM1880 bool "Bitmain BM1880 Pinctrl driver" depends on OF && (ARCH_BITMAIN || COMPILE_TEST) diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fba1c56624c0..0caf6dc3d2c1 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o obj-$(CONFIG_PINCTRL_AW9523) += pinctrl-aw9523.o obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o obj-$(CONFIG_PINCTRL_BM1880) += pinctrl-bm1880.o +obj-$(CONFIG_PINCTRL_BD79124) += pinctrl-bd79124.o obj-$(CONFIG_PINCTRL_CY8C95X0) += pinctrl-cy8c95x0.o obj-$(CONFIG_PINCTRL_DA850_PUPD) += pinctrl-da850-pupd.o obj-$(CONFIG_PINCTRL_DA9062) += pinctrl-da9062.o diff --git a/drivers/pinctrl/pinctrl-bd79124.c b/drivers/pinctrl/pinctrl-bd79124.c new file mode 100644 index 000000000000..8d25b1c5345f --- /dev/null +++ b/drivers/pinctrl/pinctrl-bd79124.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ROHM BD79124 ADC / GPO pinmux. + * + * Copyright (c) 2025, ROHM Semiconductor. + * + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/mfd/rohm-bd79124.h> +#include "pinctrl-utils.h" + +/* + * The driver expects pins top have 1 to 1 mapping to the groups. + * Eg, pin 'ID 0' is AIN0, and can be directly mapped to the group "ain0", which + * also uses group ID 0. The driver mix and match the pin and group IDs. This + * works because we don't have any specific multi-pin groups. If I knew typical + * use cases better I might've been able to create some funtionally meaningful + * groups - but as I don't, I just decided to create per-pin groups for toggling + * and individual pin to ADC-input or GPO mode. I believe this gives the + * flexibility for generic use-cases. + * + * If this is a false assumption and special groups are needed, then the pin <=> + * group mapping in this driver must be reworked. Meanwhile just keep the pin + * and group IDs matching! + */ +static const struct pinctrl_pin_desc bd79124_pins[] = { + PINCTRL_PIN(0, "ain0"), + PINCTRL_PIN(1, "ain1"), + PINCTRL_PIN(2, "ain2"), + PINCTRL_PIN(3, "ain3"), + PINCTRL_PIN(4, "ain4"), + PINCTRL_PIN(5, "ain5"), + PINCTRL_PIN(6, "ain6"), + PINCTRL_PIN(7, "ain7"), +}; + +static const char * const bd79124_pin_groups[] = { + "ain0", + "ain1", + "ain2", + "ain3", + "ain4", + "ain5", + "ain6", + "ain7", +}; + +static int bd79124_get_groups_count(struct pinctrl_dev *pcdev) +{ + return ARRAY_SIZE(bd79124_pin_groups); +} + +static const char *bd79124_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group) +{ + return bd79124_pin_groups[group]; +} + +enum { + BD79124_FUNC_GPO, + BD79124_FUNC_ADC, + BD79124_FUNC_AMOUNT +}; + +static const char * const bd79124_functions[BD79124_FUNC_AMOUNT] = { + [BD79124_FUNC_GPO] = "gpo", + [BD79124_FUNC_ADC] = "adc", +}; + +struct bd79124_mux_data { + struct device *dev; + struct regmap *map; + struct pinctrl_dev *pcdev; + struct gpio_chip gc; +}; + +static int bd79124_pmx_get_functions_count(struct pinctrl_dev *pcdev) +{ + return BD79124_FUNC_AMOUNT; +} + +static const char *bd79124_pmx_get_function_name(struct pinctrl_dev *pcdev, + unsigned int selector) +{ + return bd79124_functions[selector]; +} + +static int bd79124_pmx_get_function_groups(struct pinctrl_dev *pcdev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + *groups = &bd79124_pin_groups[0]; + *num_groups = ARRAY_SIZE(bd79124_pin_groups); + + return 0; +} + +static int bd79124_pmx_set(struct pinctrl_dev *pcdev, unsigned int func, + unsigned int group) +{ + struct bd79124_mux_data *d = pinctrl_dev_get_drvdata(pcdev); + + /* We use 1 to 1 mapping for grp <=> pin */ + if (func == BD79124_FUNC_GPO) + return regmap_set_bits(d->map, BD79124_REG_PINCFG, BIT(group)); + + return regmap_clear_bits(d->map, BD79124_REG_PINCFG, BIT(group)); +} + +/* + * Check that the pinmux has set this pin as GPO before allowing it to be used. + * NOTE: There is no locking in the pinctrl driver to ensure the pin _stays_ + * appropriately muxed. It is the responsibility of the device using this GPO + * (or ADC) to reserve the pin from the pinmux. + */ +static bool bd79124_is_gpo(struct bd79124_mux_data *d, unsigned int offset) +{ + int ret, val; + + ret = regmap_read(d->map, BD79124_REG_PINCFG, &val); + /* + * If read fails, don't allow setting GPO value as we don't know if + * pin is used as AIN. (In which case we might upset the device being + * measured - although I suppose the BD79124 would ignore the set value + * if pin is used as AIN - but better safe than sorry, right? + */ + if (ret) + return 0; + + return (val & BIT(offset)); +} + +static int bd79124gpo_direction_get(struct gpio_chip *gc, unsigned int offset) +{ + struct bd79124_mux_data *d = gpiochip_get_data(gc); + + if (!bd79124_is_gpo(d, offset)) + return -EINVAL; + + return GPIO_LINE_DIRECTION_OUT; +} + +static void bd79124gpo_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct bd79124_mux_data *d = gpiochip_get_data(gc); + + if (!bd79124_is_gpo(d, offset)) { + dev_dbg(d->dev, "Bad GPO mux mode\n"); + return; + } + + if (value) + regmap_set_bits(d->map, BD79124_REG_GPO_VAL, BIT(offset)); + + regmap_clear_bits(d->map, BD79124_REG_GPO_VAL, BIT(offset)); +} + +static void bd79124gpo_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) +{ + int ret, val; + struct bd79124_mux_data *d = gpiochip_get_data(gc); + + /* Ensure all GPIOs in 'mask' are set to be GPIOs */ + ret = regmap_read(d->map, BD79124_REG_PINCFG, &val); + if (ret) + return; + + if ((val & *mask) != *mask) { + dev_dbg(d->dev, "Invalid mux config. Can't set value.\n"); + /* Do not set value for pins configured as ADC inputs */ + *mask &= val; + } + + regmap_update_bits(d->map, BD79124_REG_GPO_VAL, *mask, *bits); +} + +/* Template for GPIO chip */ +static const struct gpio_chip bd79124gpo_chip = { + .label = "bd79124-gpo", + .get_direction = bd79124gpo_direction_get, + .set = bd79124gpo_set, + .set_multiple = bd79124gpo_set_multiple, + .can_sleep = true, + .ngpio = 8, + .base = -1, +}; + +static const struct pinmux_ops bd79124_pmxops = { + .get_functions_count = bd79124_pmx_get_functions_count, + .get_function_name = bd79124_pmx_get_function_name, + .get_function_groups = bd79124_pmx_get_function_groups, + .set_mux = bd79124_pmx_set, +}; + +static const struct pinctrl_ops bd79124_pctlops = { + .get_groups_count = bd79124_get_groups_count, + .get_group_name = bd79124_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinctrl_utils_free_map, +}; + +static const struct pinctrl_desc bd79124_pdesc = { + .name = "bd79124-pinctrl", + .pins = &bd79124_pins[0], + .npins = ARRAY_SIZE(bd79124_pins), + .pmxops = &bd79124_pmxops, + .pctlops = &bd79124_pctlops, +}; + +static int bd79124_probe(struct platform_device *pdev) +{ + struct bd79124_mux_data *d; + int ret; + + d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + d->dev = &pdev->dev; + d->map = dev_get_regmap(d->dev->parent, NULL); + if (!d->map) + return dev_err_probe(d->dev, -ENODEV, "No regmap\n"); + + d->gc = bd79124gpo_chip; + + ret = devm_pinctrl_register_and_init(d->dev->parent, + (struct pinctrl_desc *)&bd79124_pdesc, d, &d->pcdev); + if (ret) + return dev_err_probe(d->dev, ret, "pincontrol registration failed\n"); + ret = pinctrl_enable(d->pcdev); + if (ret) + return dev_err_probe(d->dev, ret, "pincontrol enabling failed\n"); + + ret = devm_gpiochip_add_data(d->dev, &d->gc, d); + if (ret) + return dev_err_probe(d->dev, ret, "gpio init Failed\n"); + + return 0; +} + +static const struct platform_device_id bd79124_mux_id[] = { + { "bd79124-pinmux", }, + { } +}; +MODULE_DEVICE_TABLE(platform, bd79124_mux_id); + +static struct platform_driver bd79124_mux_driver = { + .driver = { + .name = "bd79124-pinmux", + /* + * Probing explicitly requires a few millisecond of sleep. + * Enabling the VDD regulator may include ramp up rates. + */ + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = bd79124_probe, + .id_table = bd79124_mux_id, +}; +module_platform_driver(bd79124_mux_driver); + +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@xxxxxxxxx>"); +MODULE_DESCRIPTION("Pinmux/GPO Driver for ROHM BD79124"); +MODULE_LICENSE("GPL"); -- 2.48.1
Attachment:
signature.asc
Description: PGP signature