diff options
Diffstat (limited to 'drivers/pinctrl/stm32/pinctrl-stm32.c')
-rw-r--r-- | drivers/pinctrl/stm32/pinctrl-stm32.c | 230 |
1 files changed, 186 insertions, 44 deletions
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 5b7fa77c1184..823c8fe758e2 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -6,7 +6,9 @@ * * Heavily based on Mediatek's pinctrl driver */ +#include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/export.h> #include <linux/gpio/driver.h> #include <linux/hwspinlock.h> #include <linux/io.h> @@ -22,6 +24,7 @@ #include <linux/reset.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/machine.h> @@ -35,6 +38,8 @@ #include "../pinctrl-utils.h" #include "pinctrl-stm32.h" +#define STM32_GPIO_CID1 1 + #define STM32_GPIO_MODER 0x00 #define STM32_GPIO_TYPER 0x04 #define STM32_GPIO_SPEEDR 0x08 @@ -46,6 +51,8 @@ #define STM32_GPIO_AFRL 0x20 #define STM32_GPIO_AFRH 0x24 #define STM32_GPIO_SECCFGR 0x30 +#define STM32_GPIO_CIDCFGR(x) (0x50 + (0x8 * (x))) +#define STM32_GPIO_SEMCR(x) (0x54 + (0x8 * (x))) /* custom bitfield to backup pin status */ #define STM32_GPIO_BKP_MODE_SHIFT 0 @@ -59,6 +66,14 @@ #define STM32_GPIO_BKP_TYPE 10 #define STM32_GPIO_BKP_VAL 11 +#define STM32_GPIO_CIDCFGR_CFEN BIT(0) +#define STM32_GPIO_CIDCFGR_SEMEN BIT(1) +#define STM32_GPIO_CIDCFGR_SCID_MASK GENMASK(5, 4) +#define STM32_GPIO_CIDCFGR_SEMWL_CID1 BIT(16 + STM32_GPIO_CID1) + +#define STM32_GPIO_SEMCR_SEM_MUTEX BIT(0) +#define STM32_GPIO_SEMCR_SEMCID_MASK GENMASK(5, 4) + #define STM32_GPIO_PINS_PER_BANK 16 #define STM32_GPIO_IRQ_LINE 16 @@ -76,6 +91,7 @@ static const char * const stm32_gpio_functions[] = { "af8", "af9", "af10", "af11", "af12", "af13", "af14", "af15", "analog", + "reserved", }; struct stm32_pinctrl_group { @@ -86,7 +102,6 @@ struct stm32_pinctrl_group { struct stm32_gpio_bank { void __iomem *base; - struct clk *clk; struct reset_control *rstc; spinlock_t lock; struct gpio_chip gpio_chip; @@ -98,6 +113,7 @@ struct stm32_gpio_bank { u32 pin_backup[STM32_GPIO_PINS_PER_BANK]; u8 irq_type[STM32_GPIO_PINS_PER_BANK]; bool secure_control; + bool rif_control; }; struct stm32_pinctrl { @@ -108,6 +124,7 @@ struct stm32_pinctrl { unsigned ngroups; const char **grp_names; struct stm32_gpio_bank *banks; + struct clk_bulk_data *clks; unsigned nbanks; const struct stm32_pinctrl_match_data *match_data; struct irq_domain *domain; @@ -121,6 +138,8 @@ struct stm32_pinctrl { spinlock_t irqmux_lock; }; +static void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode, u32 *alt); + static inline int stm32_gpio_pin(int gpio) { return gpio % STM32_GPIO_PINS_PER_BANK; @@ -191,6 +210,80 @@ static void stm32_gpio_backup_bias(struct stm32_gpio_bank *bank, u32 offset, bank->pin_backup[offset] |= bias << STM32_GPIO_BKP_PUPD_SHIFT; } +/* RIF functions */ + +static bool stm32_gpio_rif_valid(struct stm32_gpio_bank *bank, unsigned int gpio_nr) +{ + u32 cid; + + cid = readl_relaxed(bank->base + STM32_GPIO_CIDCFGR(gpio_nr)); + + if (!(cid & STM32_GPIO_CIDCFGR_CFEN)) + return true; + + if (!(cid & STM32_GPIO_CIDCFGR_SEMEN)) { + if (FIELD_GET(STM32_GPIO_CIDCFGR_SCID_MASK, cid) == STM32_GPIO_CID1) + return true; + + return false; + } + + if (cid & STM32_GPIO_CIDCFGR_SEMWL_CID1) + return true; + + return false; +} + +static bool stm32_gpio_rif_acquire_semaphore(struct stm32_gpio_bank *bank, unsigned int gpio_nr) +{ + u32 cid, sem; + + cid = readl_relaxed(bank->base + STM32_GPIO_CIDCFGR(gpio_nr)); + + if (!(cid & STM32_GPIO_CIDCFGR_CFEN)) + return true; + + if (!(cid & STM32_GPIO_CIDCFGR_SEMEN)) { + if (FIELD_GET(STM32_GPIO_CIDCFGR_SCID_MASK, cid) == STM32_GPIO_CID1) + return true; + + return false; + } + + if (!(cid & STM32_GPIO_CIDCFGR_SEMWL_CID1)) + return false; + + sem = readl_relaxed(bank->base + STM32_GPIO_SEMCR(gpio_nr)); + if (sem & STM32_GPIO_SEMCR_SEM_MUTEX) { + if (FIELD_GET(STM32_GPIO_SEMCR_SEMCID_MASK, sem) == STM32_GPIO_CID1) + return true; + + return false; + } + + writel_relaxed(STM32_GPIO_SEMCR_SEM_MUTEX, bank->base + STM32_GPIO_SEMCR(gpio_nr)); + + sem = readl_relaxed(bank->base + STM32_GPIO_SEMCR(gpio_nr)); + if (sem & STM32_GPIO_SEMCR_SEM_MUTEX && + FIELD_GET(STM32_GPIO_SEMCR_SEMCID_MASK, sem) == STM32_GPIO_CID1) + return true; + + return false; +} + +static void stm32_gpio_rif_release_semaphore(struct stm32_gpio_bank *bank, unsigned int gpio_nr) +{ + u32 cid; + + cid = readl_relaxed(bank->base + STM32_GPIO_CIDCFGR(gpio_nr)); + + if (!(cid & STM32_GPIO_CIDCFGR_CFEN)) + return; + + if (cid & STM32_GPIO_CIDCFGR_SEMEN) + writel_relaxed(0, bank->base + STM32_GPIO_SEMCR(gpio_nr)); +} + /* GPIO functions */ static inline void __stm32_gpio_set(struct stm32_gpio_bank *bank, @@ -217,9 +310,26 @@ static int stm32_gpio_request(struct gpio_chip *chip, unsigned offset) return -EINVAL; } + if (bank->rif_control) { + if (!stm32_gpio_rif_acquire_semaphore(bank, offset)) { + dev_err(pctl->dev, "pin %d not available.\n", pin); + return -EINVAL; + } + } + return pinctrl_gpio_request(chip, offset); } +static void stm32_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + struct stm32_gpio_bank *bank = gpiochip_get_data(chip); + + pinctrl_gpio_free(chip, offset); + + if (bank->rif_control) + stm32_gpio_rif_release_semaphore(bank, offset); +} + static int stm32_gpio_get(struct gpio_chip *chip, unsigned offset) { struct stm32_gpio_bank *bank = gpiochip_get_data(chip); @@ -227,11 +337,14 @@ static int stm32_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(readl_relaxed(bank->base + STM32_GPIO_IDR) & BIT(offset)); } -static void stm32_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int stm32_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct stm32_gpio_bank *bank = gpiochip_get_data(chip); __stm32_gpio_set(bank, offset, value); + + return 0; } static int stm32_gpio_direction_output(struct gpio_chip *chip, @@ -300,12 +413,25 @@ static int stm32_gpio_init_valid_mask(struct gpio_chip *chip, } } + if (bank->rif_control) { + for (i = 0; i < ngpios; i++) { + if (!test_bit(i, valid_mask)) + continue; + + if (stm32_gpio_rif_valid(bank, i)) + continue; + + dev_dbg(pctl->dev, "RIF semaphore ownership conflict, GPIO %u", i); + clear_bit(i, valid_mask); + } + } + return 0; } static const struct gpio_chip stm32_gpio_template = { .request = stm32_gpio_request, - .free = pinctrl_gpio_free, + .free = stm32_gpio_free, .get = stm32_gpio_get, .set = stm32_gpio_set, .direction_input = pinctrl_gpio_direction_input, @@ -407,6 +533,7 @@ static struct irq_chip stm32_gpio_irq_chip = { .irq_set_wake = irq_chip_set_wake_parent, .irq_request_resources = stm32_gpio_irq_request_resources, .irq_release_resources = stm32_gpio_irq_release_resources, + .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? irq_chip_set_affinity_parent : NULL, }; static int stm32_gpio_domain_translate(struct irq_domain *d, @@ -537,6 +664,9 @@ static bool stm32_pctrl_is_function_valid(struct stm32_pinctrl *pctl, if (pin->pin.number != pin_num) continue; + if (fnum == STM32_PIN_RSVD) + return true; + for (k = 0; k < STM32_CONFIG_NUM; k++) { if (func->num == fnum) return true; @@ -794,8 +924,7 @@ unlock: return err; } -void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode, - u32 *alt) +static void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode, u32 *alt) { u32 val; int alt_shift = (pin % 8) * 4; @@ -837,6 +966,11 @@ static int stm32_pmx_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; } + if (function == STM32_PIN_RSVD) { + dev_dbg(pctl->dev, "Reserved pins, skipping HW update.\n"); + return 0; + } + bank = gpiochip_get_data(range->gc); pin = stm32_gpio_pin(g->pin); @@ -1217,7 +1351,7 @@ static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev, case 0: val = stm32_pconf_get(bank, offset, true); seq_printf(s, "- %s - %s", - val ? "high" : "low", + str_high_low(val), biasing[bias]); break; @@ -1227,7 +1361,7 @@ static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev, speed = stm32_pconf_get_speed(bank, offset); val = stm32_pconf_get(bank, offset, false); seq_printf(s, "- %s - %s - %s - %s %s", - val ? "high" : "low", + str_high_low(val), drive ? "open drain" : "push pull", biasing[bias], speeds[speed], "speed"); @@ -1308,12 +1442,6 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, struct fwnode if (IS_ERR(bank->base)) return PTR_ERR(bank->base); - err = clk_prepare_enable(bank->clk); - if (err) { - dev_err(dev, "failed to prepare_enable clk (%d)\n", err); - return err; - } - bank->gpio_chip = stm32_gpio_template; fwnode_property_read_string(fwnode, "st,bank-name", &bank->gpio_chip.label); @@ -1350,6 +1478,7 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, struct fwnode bank->bank_nr = bank_nr; bank->bank_ioport_nr = bank_ioport_nr; bank->secure_control = pctl->match_data->secure_control; + bank->rif_control = pctl->match_data->rif_control; spin_lock_init(&bank->lock); if (pctl->domain) { @@ -1360,26 +1489,20 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, struct fwnode bank->fwnode, &stm32_gpio_domain_ops, bank); - if (!bank->domain) { - err = -ENODEV; - goto err_clk; - } + if (!bank->domain) + return -ENODEV; } names = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL); - if (!names) { - err = -ENOMEM; - goto err_clk; - } + if (!names) + return -ENOMEM; for (i = 0; i < npins; i++) { stm32_pin = stm32_pctrl_get_desc_pin_from_gpio(pctl, bank, i); if (stm32_pin && stm32_pin->pin.name) { names[i] = devm_kasprintf(dev, GFP_KERNEL, "%s", stm32_pin->pin.name); - if (!names[i]) { - err = -ENOMEM; - goto err_clk; - } + if (!names[i]) + return -ENOMEM; } else { names[i] = NULL; } @@ -1390,15 +1513,11 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, struct fwnode err = gpiochip_add_data(&bank->gpio_chip, bank); if (err) { dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_nr); - goto err_clk; + return err; } dev_info(dev, "%s bank added\n", bank->gpio_chip.label); return 0; - -err_clk: - clk_disable_unprepare(bank->clk); - return err; } static struct irq_domain *stm32_pctrl_get_irq_domain(struct platform_device *pdev) @@ -1621,6 +1740,11 @@ int stm32_pctl_probe(struct platform_device *pdev) if (!pctl->banks) return -ENOMEM; + pctl->clks = devm_kcalloc(dev, banks, sizeof(*pctl->clks), + GFP_KERNEL); + if (!pctl->clks) + return -ENOMEM; + i = 0; for_each_gpiochip_node(dev, child) { struct stm32_gpio_bank *bank = &pctl->banks[i]; @@ -1632,24 +1756,27 @@ int stm32_pctl_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - bank->clk = of_clk_get_by_name(np, NULL); - if (IS_ERR(bank->clk)) { + pctl->clks[i].clk = of_clk_get_by_name(np, NULL); + if (IS_ERR(pctl->clks[i].clk)) { fwnode_handle_put(child); - return dev_err_probe(dev, PTR_ERR(bank->clk), + return dev_err_probe(dev, PTR_ERR(pctl->clks[i].clk), "failed to get clk\n"); } + pctl->clks[i].id = "pctl"; i++; } + ret = clk_bulk_prepare_enable(banks, pctl->clks); + if (ret) { + dev_err(dev, "failed to prepare_enable clk (%d)\n", ret); + return ret; + } + for_each_gpiochip_node(dev, child) { ret = stm32_gpiolib_register_bank(pctl, child); if (ret) { fwnode_handle_put(child); - - for (i = 0; i < pctl->nbanks; i++) - clk_disable_unprepare(pctl->banks[i].clk); - - return ret; + goto err_register; } pctl->nbanks++; @@ -1658,7 +1785,17 @@ int stm32_pctl_probe(struct platform_device *pdev) dev_info(dev, "Pinctrl STM32 initialized\n"); return 0; +err_register: + for (i = 0; i < pctl->nbanks; i++) { + struct stm32_gpio_bank *bank = &pctl->banks[i]; + + gpiochip_remove(&bank->gpio_chip); + } + + clk_bulk_disable_unprepare(banks, pctl->clks); + return ret; } +EXPORT_SYMBOL(stm32_pctl_probe); static int __maybe_unused stm32_pinctrl_restore_gpio_regs( struct stm32_pinctrl *pctl, u32 pin) @@ -1726,25 +1863,30 @@ static int __maybe_unused stm32_pinctrl_restore_gpio_regs( int __maybe_unused stm32_pinctrl_suspend(struct device *dev) { struct stm32_pinctrl *pctl = dev_get_drvdata(dev); - int i; - for (i = 0; i < pctl->nbanks; i++) - clk_disable(pctl->banks[i].clk); + clk_bulk_disable(pctl->nbanks, pctl->clks); return 0; } +EXPORT_SYMBOL(stm32_pinctrl_suspend); int __maybe_unused stm32_pinctrl_resume(struct device *dev) { struct stm32_pinctrl *pctl = dev_get_drvdata(dev); struct stm32_pinctrl_group *g = pctl->groups; - int i; + int i, ret; - for (i = 0; i < pctl->nbanks; i++) - clk_enable(pctl->banks[i].clk); + ret = clk_bulk_enable(pctl->nbanks, pctl->clks); + if (ret) + return ret; for (i = 0; i < pctl->ngroups; i++, g++) stm32_pinctrl_restore_gpio_regs(pctl, g->pin); return 0; } +EXPORT_SYMBOL(stm32_pinctrl_resume); + +MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@foss.st.com>"); +MODULE_DESCRIPTION("STM32 core pinctrl driver"); +MODULE_LICENSE("GPL"); |