diff options
Diffstat (limited to 'drivers/pinctrl/nxp/pinctrl-s32cc.c')
| -rw-r--r-- | drivers/pinctrl/nxp/pinctrl-s32cc.c | 748 |
1 files changed, 668 insertions, 80 deletions
diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c index 56be6e8d624e..0025add68495 100644 --- a/drivers/pinctrl/nxp/pinctrl-s32cc.c +++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c @@ -2,7 +2,7 @@ /* * Core driver for the S32 CC (Common Chassis) pin controller * - * Copyright 2017-2022,2024-2025 NXP + * Copyright 2017-2022,2024-2026 NXP * Copyright (C) 2022 SUSE LLC * Copyright 2015-2016 Freescale Semiconductor, Inc. */ @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/err.h> #include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> @@ -39,6 +40,40 @@ #define S32_MSCR_ODE BIT(20) #define S32_MSCR_OBE BIT(21) +#define S32_GPIO_OP_SHIFT 16 +#define S32_GPIO_OP_MASK GENMASK(19, 16) + +#define S32_GPIO_OP_DIR 0 /* MSCR direction */ +#define S32_GPIO_OP_DAT BIT(S32_GPIO_OP_SHIFT) /* PGPDI read */ +#define S32_GPIO_OP_SET BIT(S32_GPIO_OP_SHIFT + 1) /* PGPDO write */ + +/* + * [15:12] = GPIO bank / gpio range index + * [11:0] = real register offset or pin id + */ +#define S32_GPIO_BANK_SHIFT 12 +#define S32_GPIO_BANK_MASK GENMASK(15, 12) +#define S32_GPIO_REG_MASK GENMASK(11, 0) + +#define S32_GPIO_ENCODE(bank, off) \ + ((((bank) << S32_GPIO_BANK_SHIFT) & S32_GPIO_BANK_MASK) | \ + ((off) & S32_GPIO_REG_MASK)) + +#define S32_GPIO_DECODE_BANK(reg) \ + (((reg) & S32_GPIO_BANK_MASK) >> S32_GPIO_BANK_SHIFT) + +#define S32_GPIO_DECODE_OFF(reg) \ + ((reg) & S32_GPIO_REG_MASK) + +/* + * PGPDOs are 16bit registers that come in big endian + * order if they are grouped in pairs of two. + * + * For example, the order is PGPDO1, PGPDO0, PGPDO3, PGPDO2... + */ +#define S32_PGPD(N) (((N) ^ 1) * 2) +#define S32_PGPD_SIZE 16 + enum s32_write_type { S32_PINCONF_UPDATE_ONLY, S32_PINCONF_OVERWRITE, @@ -60,6 +95,12 @@ static u32 get_pin_func(u32 pinmux) return pinmux & GENMASK(3, 0); } +/* + * struct s32_pinctrl_mem_region - memory region for a set of SIUL2 registers + * @map: regmap used for this range + * @pin_range: the pins controlled by these registers + * @name: name of the current range + */ struct s32_pinctrl_mem_region { struct regmap *map; const struct s32_pin_range *pin_range; @@ -67,7 +108,19 @@ struct s32_pinctrl_mem_region { }; /* - * Holds pin configuration for GPIO's. + * struct s32_gpio_regmaps - GPIO register maps for a SIUL2 instance + * @pgpdo: regmap for Parallel GPIO Pad Data Out registers + * @pgpdi: regmap for Parallel GPIO Pad Data In registers + * @range: GPIO range info + */ +struct s32_gpio_regmaps { + struct regmap *pgpdo; + struct regmap *pgpdi; + const struct s32_gpio_range *range; +}; + +/* + * struct gpio_pin_config - holds pin configuration for GPIO's * @pin_id: Pin ID for this GPIO * @config: Pin settings * @list: Linked list entry for each gpio pin @@ -79,26 +132,40 @@ struct gpio_pin_config { }; /* - * Pad config save/restore for power suspend/resume. + * struct s32_pinctrl_context - pad config save/restore for suspend/resume + * @pads: saved values for the pads */ struct s32_pinctrl_context { unsigned int *pads; }; /* + * struct s32_pinctrl - private driver data * @dev: a pointer back to containing device * @pctl: a pointer to the pinctrl device structure * @regions: reserved memory regions with start/end pin * @info: structure containing information about the pin - * @gpio_configs: Saved configurations for GPIO pins - * @gpiop_configs_lock: lock for the `gpio_configs` list - * @s32_pinctrl_context: Configuration saved over system sleep + * @gpio_regmaps: PGPDO/PGPDI regmaps for each SIUL2 module + * @num_gpio_regmaps: number of GPIO regmap entries + * @gpio_regmap: regmap bridging gpio-regmap to SIUL2 registers + * @gpio_rgm: gpio-regmap instance registered for this controller + * @ngpio: total number of GPIO line offsets + * @gpio_names: GPIO line names array passed to gpio-regmap + * @gpio_configs: saved configurations for GPIO pins + * @gpio_configs_lock: lock for the `gpio_configs` list + * @saved_context: configuration saved over system sleep */ struct s32_pinctrl { struct device *dev; struct pinctrl_dev *pctl; struct s32_pinctrl_mem_region *regions; struct s32_pinctrl_soc_info *info; + struct s32_gpio_regmaps *gpio_regmaps; + unsigned int num_gpio_regmaps; + struct regmap *gpio_regmap; + struct gpio_regmap *gpio_rgm; + unsigned int ngpio; + const char *const *gpio_names; struct list_head gpio_configs; spinlock_t gpio_configs_lock; #ifdef CONFIG_PM_SLEEP @@ -123,13 +190,13 @@ s32_get_region(struct pinctrl_dev *pctldev, unsigned int pin) return NULL; } -static inline int s32_check_pin(struct pinctrl_dev *pctldev, - unsigned int pin) +static int s32_check_pin(struct pinctrl_dev *pctldev, + unsigned int pin) { return s32_get_region(pctldev, pin) ? 0 : -EINVAL; } -static inline int s32_regmap_read(struct pinctrl_dev *pctldev, +static int s32_regmap_read(struct pinctrl_dev *pctldev, unsigned int pin, unsigned int *val) { struct s32_pinctrl_mem_region *region; @@ -145,7 +212,7 @@ static inline int s32_regmap_read(struct pinctrl_dev *pctldev, return regmap_read(region->map, offset, val); } -static inline int s32_regmap_write(struct pinctrl_dev *pctldev, +static int s32_regmap_write(struct pinctrl_dev *pctldev, unsigned int pin, unsigned int val) { @@ -163,7 +230,7 @@ static inline int s32_regmap_write(struct pinctrl_dev *pctldev, } -static inline int s32_regmap_update(struct pinctrl_dev *pctldev, unsigned int pin, +static int s32_regmap_update(struct pinctrl_dev *pctldev, unsigned int pin, unsigned int mask, unsigned int val) { struct s32_pinctrl_mem_region *region; @@ -348,88 +415,84 @@ static int s32_pmx_get_funcs_count(struct pinctrl_dev *pctldev) return info->nfunctions; } -static const char *s32_pmx_get_func_name(struct pinctrl_dev *pctldev, - unsigned int selector) -{ - struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); - const struct s32_pinctrl_soc_info *info = ipctl->info; - - return info->functions[selector].name; -} - -static int s32_pmx_get_groups(struct pinctrl_dev *pctldev, - unsigned int selector, - const char * const **groups, - unsigned int * const num_groups) -{ - struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); - const struct s32_pinctrl_soc_info *info = ipctl->info; - - *groups = info->functions[selector].groups; - *num_groups = info->functions[selector].ngroups; - - return 0; -} - static int s32_pmx_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned int offset) + unsigned int pin) { struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); - struct gpio_pin_config *gpio_pin; + struct gpio_pin_config *gpio_pin __free(kfree) = NULL; unsigned int config; - unsigned long flags; int ret; - ret = s32_regmap_read(pctldev, offset, &config); + ret = s32_regmap_read(pctldev, pin, &config); if (ret) return ret; - /* Save current configuration */ - gpio_pin = kmalloc_obj(*gpio_pin); + gpio_pin = kmalloc_obj(*gpio_pin, GFP_KERNEL); if (!gpio_pin) return -ENOMEM; - gpio_pin->pin_id = offset; + gpio_pin->pin_id = pin; gpio_pin->config = config; - INIT_LIST_HEAD(&gpio_pin->list); - - spin_lock_irqsave(&ipctl->gpio_configs_lock, flags); - list_add(&gpio_pin->list, &ipctl->gpio_configs); - spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags); /* GPIO pin means SSS = 0 */ - config &= ~S32_MSCR_SSS_MASK; + ret = s32_regmap_update(pctldev, pin, + S32_MSCR_SSS_MASK | S32_MSCR_IBE, + S32_MSCR_IBE); + if (ret) + return ret; - return s32_regmap_write(pctldev, offset, config); + scoped_guard(spinlock_irqsave, &ipctl->gpio_configs_lock) + list_add(&no_free_ptr(gpio_pin)->list, &ipctl->gpio_configs); + + return 0; } static void s32_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned int offset) + unsigned int pin) { struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); - struct gpio_pin_config *gpio_pin, *tmp; + struct gpio_pin_config *gpio_pin, *found = NULL; unsigned long flags; - int ret; spin_lock_irqsave(&ipctl->gpio_configs_lock, flags); - - list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) { - if (gpio_pin->pin_id == offset) { - ret = s32_regmap_write(pctldev, gpio_pin->pin_id, - gpio_pin->config); - if (ret != 0) - goto unlock; - + list_for_each_entry(gpio_pin, &ipctl->gpio_configs, list) { + if (gpio_pin->pin_id == pin) { list_del(&gpio_pin->list); - kfree(gpio_pin); + found = gpio_pin; break; } } - -unlock: spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags); + + if (found) { + s32_regmap_write(pctldev, found->pin_id, found->config); + kfree(found); + } +} + +static const char *s32_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct s32_pinctrl_soc_info *info = ipctl->info; + + return info->functions[selector].name; +} + +static int s32_pmx_get_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct s32_pinctrl_soc_info *info = ipctl->info; + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; } static int s32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, @@ -476,8 +539,8 @@ static int s32_get_slew_regval(int arg) return -EINVAL; } -static inline void s32_pin_set_pull(enum pin_config_param param, - unsigned int *mask, unsigned int *config) +static void s32_pin_set_pull(enum pin_config_param param, + unsigned int *mask, unsigned int *config) { switch (param) { case PIN_CONFIG_BIAS_DISABLE: @@ -641,9 +704,9 @@ static void s32_pinconf_dbg_show(struct pinctrl_dev *pctldev, ret = s32_regmap_read(pctldev, pin_id, &config); if (ret) - return; - - seq_printf(s, "0x%x", config); + seq_printf(s, "error %d", ret); + else + seq_printf(s, "0x%x", config); } static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, @@ -661,8 +724,11 @@ static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, for (i = 0; i < grp->data.npins; i++) { name = pin_get_name(pctldev, grp->data.pins[i]); ret = s32_regmap_read(pctldev, grp->data.pins[i], &config); - if (ret) - return; + if (ret) { + seq_printf(s, "%s: error %d\n", name, ret); + continue; + } + seq_printf(s, "%s: 0x%x\n", name, config); } } @@ -675,6 +741,477 @@ static const struct pinconf_ops s32_pinconf_ops = { .pin_config_group_dbg_show = s32_pinconf_group_dbg_show, }; +static void s32_gpio_free_saved_configs(void *data) +{ + struct s32_pinctrl *ipctl = data; + struct gpio_pin_config *gpio_pin, *tmp; + unsigned long flags; + + spin_lock_irqsave(&ipctl->gpio_configs_lock, flags); + list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) { + list_del(&gpio_pin->list); + kfree(gpio_pin); + } + spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags); +} + +static unsigned int s32_pin2pad(unsigned int pin) +{ + return pin / S32_PGPD_SIZE; +} + +static u16 s32_pin2mask(unsigned int pin) +{ + /* + * From Reference manual : + * PGPDOx[PPDOy] = GPDO(x × 16) + (15 - y)[PDO_(x × 16) + (15 - y)] + */ + return BIT(S32_PGPD_SIZE - 1 - pin % S32_PGPD_SIZE); +} + +static int s32_gpio_get_range(struct s32_pinctrl *ipctl, + unsigned int gpio, + unsigned int *pin, + unsigned int *bank) +{ + const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data; + const struct s32_gpio_range *range; + int i; + + for (i = 0; i < soc_data->num_gpio_ranges; i++) { + range = &soc_data->gpio_ranges[i]; + + if (gpio < range->gpio_base || + gpio >= range->gpio_base + range->gpio_num) + continue; + + if (pin) + *pin = range->pin_base + gpio - range->gpio_base; + + if (bank) + *bank = i; + + return 0; + } + + return -EINVAL; +} + +static int s32_gpio_pad_map_xlate(struct s32_pinctrl *ipctl, + unsigned int gpio, + unsigned int *reg_offset, + u16 *mask) +{ + const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data; + const struct s32_gpio_pad_map *map; + unsigned int bit; + int i; + + if (!soc_data->gpio_pad_maps || !soc_data->num_gpio_pad_maps) + return -EINVAL; + + for (i = 0; i < soc_data->num_gpio_pad_maps; i++) { + map = &soc_data->gpio_pad_maps[i]; + + if (gpio < map->gpio_start || gpio > map->gpio_end) + continue; + + bit = gpio - map->gpio_start; + *mask = BIT(S32_PGPD_SIZE - 1 - bit); + *reg_offset = S32_PGPD(map->pad); + + return 0; + } + + return -EINVAL; +} + +static bool s32_gpio_pin_is_sparse(struct s32_pinctrl *ipctl, unsigned int pin) +{ + const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data; + const struct s32_gpio_range *range; + int i; + + for (i = 0; i < soc_data->num_gpio_ranges; i++) { + range = &soc_data->gpio_ranges[i]; + if (pin >= range->pin_base && + pin < range->pin_base + range->gpio_num) + return range->sparse; + } + + return false; +} + +static int s32_gpio_xlate_pgpd(struct s32_pinctrl *ipctl, + unsigned int pin, + unsigned int *reg_offset, + u16 *mask) +{ + int ret; + + /* + * Try the pad map first. For sparse ranges (SIUL2_1), only pins + * listed in the pad map are valid, return the error directly without + * falling back to the linear layout. + * For linear ranges (SIUL2_0), fall back to the linear pad-to-PGPD + * formula if no pad map entry matches. + */ + ret = s32_gpio_pad_map_xlate(ipctl, pin, reg_offset, mask); + if (ret != -EINVAL) + return ret; + + if (s32_gpio_pin_is_sparse(ipctl, pin)) + return -EINVAL; + + /* Linear layout fallback for non-sparse ranges. */ + *mask = s32_pin2mask(pin); + *reg_offset = S32_PGPD(s32_pin2pad(pin)); + + return 0; +} + +static int s32_gpio_reg_mask_xlate(struct gpio_regmap *gpio, + unsigned int base, unsigned int offset, + unsigned int *reg, unsigned int *mask) +{ + struct s32_pinctrl *ipctl = gpio_regmap_get_drvdata(gpio); + unsigned int pgpd_reg, pin, bank; + u16 pgpd_mask; + int ret; + + ret = s32_gpio_get_range(ipctl, offset, &pin, &bank); + if (ret) + return ret; + + switch (base) { + case S32_GPIO_OP_DIR: + /* + * Direction is controlled through MSCR OBE. + * Encode the real pin id in the virtual register. + */ + *reg = S32_GPIO_OP_DIR | pin; + *mask = S32_MSCR_OBE; + return 0; + + case S32_GPIO_OP_DAT: + case S32_GPIO_OP_SET: + ret = s32_gpio_xlate_pgpd(ipctl, pin, &pgpd_reg, &pgpd_mask); + if (ret) + return ret; + /* + * Encode both the GPIO bank and the real PGPD register offset. + */ + *reg = base | S32_GPIO_ENCODE(bank, pgpd_reg); + *mask = pgpd_mask; + return 0; + default: + return -EINVAL; + } +} + +static int s32_gpio_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct s32_pinctrl *ipctl = context; + unsigned int op = reg & S32_GPIO_OP_MASK; + unsigned int vreg = reg & ~S32_GPIO_OP_MASK; + unsigned int bank; + unsigned int offset; + struct regmap *map; + + switch (op) { + case S32_GPIO_OP_DIR: + /* + * Lower bits contain the real MSCR pin id. + */ + offset = S32_GPIO_DECODE_OFF(vreg); + + return s32_regmap_read(ipctl->pctl, offset, val); + + case S32_GPIO_OP_DAT: + bank = S32_GPIO_DECODE_BANK(vreg); + offset = S32_GPIO_DECODE_OFF(vreg); + + if (bank >= ipctl->num_gpio_regmaps) + return -EINVAL; + + map = ipctl->gpio_regmaps[bank].pgpdi; + if (!map) + return -ENODEV; + + return regmap_read(map, offset, val); + + case S32_GPIO_OP_SET: + /* + * gpio-regmap uses update_bits() for set, so it needs to read + * the output register before writing the updated value. + */ + bank = S32_GPIO_DECODE_BANK(vreg); + offset = S32_GPIO_DECODE_OFF(vreg); + + if (bank >= ipctl->num_gpio_regmaps) + return -EINVAL; + + map = ipctl->gpio_regmaps[bank].pgpdo; + if (!map) + return -ENODEV; + + return regmap_read(map, offset, val); + + default: + return -EINVAL; + } +} + +static int s32_gpio_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct s32_pinctrl *ipctl = context; + unsigned int op = reg & S32_GPIO_OP_MASK; + unsigned int vreg = reg & ~S32_GPIO_OP_MASK; + unsigned int bank, offset, config; + struct regmap *map; + + switch (op) { + case S32_GPIO_OP_DIR: + /* + * gpio-regmap sets S32_MSCR_OBE for output and clears it for + * input. Keep IBE enabled for GPIOs in both cases. + */ + offset = S32_GPIO_DECODE_OFF(vreg); + + config = S32_MSCR_IBE; + if (val & S32_MSCR_OBE) + config |= S32_MSCR_OBE; + + return s32_regmap_update(ipctl->pctl, offset, + S32_MSCR_OBE | S32_MSCR_IBE, + config); + + case S32_GPIO_OP_SET: + bank = S32_GPIO_DECODE_BANK(vreg); + offset = S32_GPIO_DECODE_OFF(vreg); + + if (bank >= ipctl->num_gpio_regmaps) + return -EINVAL; + + map = ipctl->gpio_regmaps[bank].pgpdo; + if (!map) + return -ENODEV; + + return regmap_write(map, offset, val); + + default: + return -EINVAL; + } +} + +static const struct regmap_bus s32_gpio_regmap_bus = { + .reg_read = s32_gpio_reg_read, + .reg_write = s32_gpio_reg_write, +}; + +static const struct regmap_config s32_gpio_regmap_config = { + .name = "s32-gpio", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 1, + .max_register = S32_GPIO_OP_SET | S32_GPIO_BANK_MASK | S32_GPIO_REG_MASK, + .cache_type = REGCACHE_NONE, + .fast_io = true, +}; + +static int s32_gpio_get_ngpio(const struct s32_pinctrl_soc_data *soc_data, + unsigned int *ngpio) +{ + const struct s32_gpio_range *range; + unsigned int end, max = 0; + int i; + + if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges) + return -EINVAL; + + for (i = 0; i < soc_data->num_gpio_ranges; i++) { + range = &soc_data->gpio_ranges[i]; + + if (!range->gpio_num) + return -EINVAL; + + end = range->gpio_base + range->gpio_num; + + /* + * gpio_ranges must be ordered by gpio_base and must not overlap. + * The GPIO line space size is derived from the highest range end. + */ + if (i > 0 && range->gpio_base < max) + return -EINVAL; + + if (end > max) + max = end; + } + + *ngpio = max; + + return 0; +} + +static int s32_init_gpio_regmap(struct platform_device *pdev, + struct s32_pinctrl *ipctl) +{ + ipctl->gpio_regmap = + devm_regmap_init(&pdev->dev, &s32_gpio_regmap_bus, + ipctl, &s32_gpio_regmap_config); + if (IS_ERR(ipctl->gpio_regmap)) + return dev_err_probe(&pdev->dev, + PTR_ERR(ipctl->gpio_regmap), + "Failed to init GPIO regmap\n"); + + return 0; +} + +static int s32_init_valid_mask(struct gpio_chip *chip, unsigned long *mask, + unsigned int ngpios) +{ + struct gpio_regmap *gpio = gpiochip_get_data(chip); + struct s32_pinctrl *ipctl = gpio_regmap_get_drvdata(gpio); + unsigned int gpio_num, pin, reg_offset; + u16 pgpd_mask; + int ret; + + bitmap_zero(mask, ngpios); + + for (gpio_num = 0; gpio_num < ngpios; gpio_num++) { + ret = s32_gpio_get_range(ipctl, gpio_num, &pin, NULL); + if (ret) + continue; + + ret = s32_gpio_xlate_pgpd(ipctl, pin, ®_offset, &pgpd_mask); + if (ret) + continue; + + bitmap_set(mask, gpio_num, 1); + } + + return 0; +} + +static int s32_gpio_populate_names(struct s32_pinctrl *ipctl) +{ + char **names; + unsigned int gpio; + unsigned int pin; + char port; + int ret; + + names = devm_kcalloc(ipctl->dev, ipctl->ngpio, sizeof(*names), + GFP_KERNEL); + if (!names) + return -ENOMEM; + + for (gpio = 0; gpio < ipctl->ngpio; gpio++) { + ret = s32_gpio_get_range(ipctl, gpio, &pin, NULL); + if (ret) + continue; + + port = 'A' + pin / 16; + + names[gpio] = devm_kasprintf(ipctl->dev, GFP_KERNEL, + "P%c_%02u", port, pin & 0xf); + if (!names[gpio]) + return -ENOMEM; + } + + ipctl->gpio_names = (const char *const *)names; + + return 0; +} + +static int s32_pinctrl_init_gpio_regmaps(struct platform_device *pdev, + struct s32_pinctrl *ipctl) +{ + const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data; + static const struct regmap_config pgpd_config = { + .reg_bits = 32, + .val_bits = 16, + .reg_stride = 2, + }; + struct regmap_config cfg; + struct resource *res; + void __iomem *base; + unsigned int pgpdo_idx, pgpdi_idx; + unsigned int i; + + if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges) + return 0; + + ipctl->num_gpio_regmaps = soc_data->num_gpio_ranges; + ipctl->gpio_regmaps = devm_kcalloc(&pdev->dev, ipctl->num_gpio_regmaps, + sizeof(*ipctl->gpio_regmaps), + GFP_KERNEL); + if (!ipctl->gpio_regmaps) + return -ENOMEM; + + for (i = 0; i < ipctl->num_gpio_regmaps; i++) { + ipctl->gpio_regmaps[i].range = &soc_data->gpio_ranges[i]; + + /* + * GPIO resources are placed after the pinctrl regions + */ + pgpdo_idx = soc_data->mem_regions + i * 2; + pgpdi_idx = soc_data->mem_regions + i * 2 + 1; + + /* PGPDO */ + res = platform_get_resource(pdev, IORESOURCE_MEM, pgpdo_idx); + if (!res) + return dev_err_probe(&pdev->dev, -ENOENT, + "Missing PGPDO resource %u\n", i); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + cfg = pgpd_config; + cfg.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "pgpdo%u", i); + if (!cfg.name) + return -ENOMEM; + + cfg.max_register = resource_size(res) - cfg.reg_stride; + + ipctl->gpio_regmaps[i].pgpdo = + devm_regmap_init_mmio(&pdev->dev, base, &cfg); + if (IS_ERR(ipctl->gpio_regmaps[i].pgpdo)) + return dev_err_probe(&pdev->dev, + PTR_ERR(ipctl->gpio_regmaps[i].pgpdo), + "Failed to init PGPDO regmap %u\n", i); + + /* PGPDI */ + res = platform_get_resource(pdev, IORESOURCE_MEM, pgpdi_idx); + if (!res) + return dev_err_probe(&pdev->dev, -ENOENT, + "Missing PGPDI resource %u\n", i); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + cfg = pgpd_config; + cfg.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "pgpdi%u", i); + if (!cfg.name) + return -ENOMEM; + + cfg.max_register = resource_size(res) - cfg.reg_stride; + + ipctl->gpio_regmaps[i].pgpdi = + devm_regmap_init_mmio(&pdev->dev, base, &cfg); + if (IS_ERR(ipctl->gpio_regmaps[i].pgpdi)) + return dev_err_probe(&pdev->dev, + PTR_ERR(ipctl->gpio_regmaps[i].pgpdi), + "Failed to init PGPDI regmap %u\n", i); + } + + return 0; +} + #ifdef CONFIG_PM_SLEEP static bool s32_pinctrl_should_save(struct s32_pinctrl *ipctl, unsigned int pin) @@ -701,8 +1238,7 @@ int s32_pinctrl_suspend(struct device *dev) const struct pinctrl_pin_desc *pin; const struct s32_pinctrl_soc_info *info = ipctl->info; struct s32_pinctrl_context *saved_context = &ipctl->saved_context; - int i; - int ret; + int i, ret; unsigned int config; for (i = 0; i < info->soc_data->npins; i++) { @@ -713,7 +1249,7 @@ int s32_pinctrl_suspend(struct device *dev) ret = s32_regmap_read(ipctl->pctl, pin->number, &config); if (ret) - return -EINVAL; + return ret; saved_context->pads[i] = config; } @@ -728,7 +1264,7 @@ int s32_pinctrl_resume(struct device *dev) const struct s32_pinctrl_soc_info *info = ipctl->info; const struct pinctrl_pin_desc *pin; struct s32_pinctrl_context *saved_context = &ipctl->saved_context; - int ret, i; + int i, ret; for (i = 0; i < info->soc_data->npins; i++) { pin = &info->soc_data->pins[i]; @@ -737,7 +1273,7 @@ int s32_pinctrl_resume(struct device *dev) continue; ret = s32_regmap_write(ipctl->pctl, pin->number, - saved_context->pads[i]); + saved_context->pads[i]); if (ret) return ret; } @@ -920,9 +1456,11 @@ int s32_pinctrl_probe(struct platform_device *pdev, #ifdef CONFIG_PM_SLEEP struct s32_pinctrl_context *saved_context; #endif + struct gpio_regmap_config gpio_cfg = {}; struct pinctrl_desc *s32_pinctrl_desc; struct s32_pinctrl_soc_info *info; struct s32_pinctrl *ipctl; + unsigned int ngpio; int ret; if (!soc_data || !soc_data->pins || !soc_data->npins) @@ -948,6 +1486,11 @@ int s32_pinctrl_probe(struct platform_device *pdev, INIT_LIST_HEAD(&ipctl->gpio_configs); spin_lock_init(&ipctl->gpio_configs_lock); + ret = devm_add_action_or_reset(&pdev->dev, + s32_gpio_free_saved_configs, ipctl); + if (ret) + return ret; + s32_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*s32_pinctrl_desc), GFP_KERNEL); if (!s32_pinctrl_desc) @@ -966,11 +1509,16 @@ int s32_pinctrl_probe(struct platform_device *pdev, return dev_err_probe(&pdev->dev, ret, "Fail to probe dt properties\n"); - ipctl->pctl = devm_pinctrl_register(&pdev->dev, s32_pinctrl_desc, - ipctl); - if (IS_ERR(ipctl->pctl)) - return dev_err_probe(&pdev->dev, PTR_ERR(ipctl->pctl), - "could not register s32 pinctrl driver\n"); + ret = s32_pinctrl_init_gpio_regmaps(pdev, ipctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to init GPIO regmaps\n"); + + ret = devm_pinctrl_register_and_init(&pdev->dev, s32_pinctrl_desc, + ipctl, &ipctl->pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Could not register s32 pinctrl driver\n"); #ifdef CONFIG_PM_SLEEP saved_context = &ipctl->saved_context; @@ -982,7 +1530,47 @@ int s32_pinctrl_probe(struct platform_device *pdev, return -ENOMEM; #endif - dev_info(&pdev->dev, "initialized s32 pinctrl driver\n"); + ret = pinctrl_enable(ipctl->pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to enable pinctrl\n"); + + /* Setup GPIO if GPIO ranges are defined */ + if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges) + return 0; + + ret = s32_gpio_get_ngpio(soc_data, &ngpio); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Invalid GPIO ranges\n"); + + ipctl->ngpio = ngpio; + + ret = s32_gpio_populate_names(ipctl); + if (ret) + return ret; + + ret = s32_init_gpio_regmap(pdev, ipctl); + if (ret) + return ret; + + gpio_cfg.parent = &pdev->dev; + gpio_cfg.fwnode = dev_fwnode(&pdev->dev); + gpio_cfg.label = dev_name(&pdev->dev); + gpio_cfg.regmap = ipctl->gpio_regmap; + gpio_cfg.ngpio = ngpio; + gpio_cfg.names = ipctl->gpio_names; + gpio_cfg.reg_dir_out_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_DIR); + gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_DAT); + gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_SET); + gpio_cfg.reg_mask_xlate = s32_gpio_reg_mask_xlate; + gpio_cfg.init_valid_mask = s32_init_valid_mask; + gpio_cfg.drvdata = ipctl; + + ipctl->gpio_rgm = devm_gpio_regmap_register(&pdev->dev, &gpio_cfg); + if (IS_ERR(ipctl->gpio_rgm)) + return dev_err_probe(&pdev->dev, + PTR_ERR(ipctl->gpio_rgm), + "Unable to add gpio_regmap chip\n"); return 0; } |
