diff options
Diffstat (limited to 'drivers/pinctrl/pinctrl-single.c')
| -rw-r--r-- | drivers/pinctrl/pinctrl-single.c | 514 |
1 files changed, 331 insertions, 183 deletions
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index b8b3d932cd73..998f23d6c317 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -12,21 +12,20 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/io.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/list.h> #include <linux/interrupt.h> - #include <linux/irqchip/chained_irq.h> - #include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/seq_file.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> -#include <linux/pinctrl/pinconf-generic.h> #include <linux/platform_data/pinctrl-single.h> @@ -42,6 +41,7 @@ * struct pcs_func_vals - mux function register offset and value pair * @reg: register virtual address * @val: register value + * @mask: mask */ struct pcs_func_vals { void __iomem *reg; @@ -81,16 +81,14 @@ struct pcs_conf_type { * @name: pinctrl function name * @vals: register and vals array * @nvals: number of entries in vals array - * @pgnames: array of pingroup names the function uses - * @npgnames: number of pingroup names the function uses + * @conf: array of pin configurations + * @nconfs: number of pin configurations available * @node: list node */ struct pcs_function { const char *name; struct pcs_func_vals *vals; unsigned nvals; - const char **pgnames; - int npgnames; struct pcs_conf_vals *conf; int nconfs; struct list_head node; @@ -144,6 +142,7 @@ struct pcs_soc_data { * struct pcs_device - pinctrl device instance * @res: resources * @base: virtual address of the controller + * @saved_vals: saved values for the controller * @size: size of the ioremapped area * @dev: device entry * @np: device tree node @@ -172,11 +171,13 @@ struct pcs_soc_data { struct pcs_device { struct resource *res; void __iomem *base; + void *saved_vals; unsigned size; struct device *dev; struct device_node *np; struct pinctrl_dev *pctl; unsigned flags; +#define PCS_CONTEXT_LOSS_OFF (1 << 3) #define PCS_QUIRK_SHARED_IRQ (1 << 2) #define PCS_FEAT_IRQ (1 << 1) #define PCS_FEAT_PINCONF (1 << 0) @@ -222,6 +223,9 @@ static enum pin_config_param pcs_bias[] = { */ static struct lock_class_key pcs_lock_class; +/* Class for the IRQ request mutex */ +static struct lock_class_key pcs_request_class; + /* * REVISIT: Reads and writes could eventually use regmap or something * generic. But at least on omaps, some mux registers are performance @@ -231,50 +235,74 @@ static struct lock_class_key pcs_lock_class; * does not help in this case. */ -static unsigned __maybe_unused pcs_readb(void __iomem *reg) +static unsigned int pcs_readb(void __iomem *reg) { return readb(reg); } -static unsigned __maybe_unused pcs_readw(void __iomem *reg) +static unsigned int pcs_readw(void __iomem *reg) { return readw(reg); } -static unsigned __maybe_unused pcs_readl(void __iomem *reg) +static unsigned int pcs_readl(void __iomem *reg) { return readl(reg); } -static void __maybe_unused pcs_writeb(unsigned val, void __iomem *reg) +static void pcs_writeb(unsigned int val, void __iomem *reg) { writeb(val, reg); } -static void __maybe_unused pcs_writew(unsigned val, void __iomem *reg) +static void pcs_writew(unsigned int val, void __iomem *reg) { writew(val, reg); } -static void __maybe_unused pcs_writel(unsigned val, void __iomem *reg) +static void pcs_writel(unsigned int val, void __iomem *reg) { writel(val, reg); } +static unsigned int pcs_pin_reg_offset_get(struct pcs_device *pcs, + unsigned int pin) +{ + unsigned int mux_bytes = pcs->width / BITS_PER_BYTE; + + if (pcs->bits_per_mux) { + unsigned int pin_offset_bytes; + + pin_offset_bytes = (pcs->bits_per_pin * pin) / BITS_PER_BYTE; + return (pin_offset_bytes / mux_bytes) * mux_bytes; + } + + return pin * mux_bytes; +} + +static unsigned int pcs_pin_shift_reg_get(struct pcs_device *pcs, + unsigned int pin) +{ + return (pin % (pcs->width / pcs->bits_per_pin)) * pcs->bits_per_pin; +} + static void pcs_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned pin) { struct pcs_device *pcs; - unsigned val, mux_bytes; + unsigned int val; unsigned long offset; size_t pa; pcs = pinctrl_dev_get_drvdata(pctldev); - mux_bytes = pcs->width / BITS_PER_BYTE; - offset = pin * mux_bytes; + offset = pcs_pin_reg_offset_get(pcs, pin); val = pcs->read(pcs->base + offset); + + if (pcs->bits_per_mux) + val &= pcs->fmask << pcs_pin_shift_reg_get(pcs, pin); + pa = pcs->res->start + offset; seq_printf(s, "%zx %08x %s ", pa, val, DRIVER_NAME); @@ -308,7 +336,7 @@ static int pcs_get_function(struct pinctrl_dev *pctldev, unsigned pin, struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); struct pin_desc *pdesc = pin_desc_get(pctldev, pin); const struct pinctrl_setting_mux *setting; - struct function_desc *function; + const struct function_desc *function; unsigned fselector; /* If pin is not described in DTS & enabled, mux_setting is NULL. */ @@ -317,6 +345,8 @@ static int pcs_get_function(struct pinctrl_dev *pctldev, unsigned pin, return -ENOTSUPP; fselector = setting->func; function = pinmux_generic_get_function(pctldev, fselector); + if (!function) + return -EINVAL; *func = function->data; if (!(*func)) { dev_err(pcs->dev, "%s could not find function%i\n", @@ -330,7 +360,7 @@ static int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector, unsigned group) { struct pcs_device *pcs; - struct function_desc *function; + const struct function_desc *function; struct pcs_function *func; int i; @@ -339,6 +369,8 @@ static int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector, if (!pcs->fmask) return 0; function = pinmux_generic_get_function(pctldev, fselector); + if (!function) + return -EINVAL; func = function->data; if (!func) return -EINVAL; @@ -375,7 +407,6 @@ static int pcs_request_gpio(struct pinctrl_dev *pctldev, struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); struct pcs_gpiofunc_range *frange = NULL; struct list_head *pos, *tmp; - int mux_bytes = 0; unsigned data; /* If function mask is null, return directly. */ @@ -383,14 +414,28 @@ static int pcs_request_gpio(struct pinctrl_dev *pctldev, return -ENOTSUPP; list_for_each_safe(pos, tmp, &pcs->gpiofuncs) { + u32 offset; + frange = list_entry(pos, struct pcs_gpiofunc_range, node); if (pin >= frange->offset + frange->npins || pin < frange->offset) continue; - mux_bytes = pcs->width / BITS_PER_BYTE; - data = pcs->read(pcs->base + pin * mux_bytes) & ~pcs->fmask; - data |= frange->gpiofunc; - pcs->write(data, pcs->base + pin * mux_bytes); + + offset = pcs_pin_reg_offset_get(pcs, pin); + + if (pcs->bits_per_mux) { + int pin_shift = pcs_pin_shift_reg_get(pcs, pin); + + data = pcs->read(pcs->base + offset); + data &= ~(pcs->fmask << pin_shift); + data |= frange->gpiofunc << pin_shift; + pcs->write(data, pcs->base + offset); + } else { + data = pcs->read(pcs->base + offset); + data &= ~pcs->fmask; + data |= frange->gpiofunc; + pcs->write(data, pcs->base + offset); + } break; } return 0; @@ -440,7 +485,8 @@ static int pcs_pinconf_get(struct pinctrl_dev *pctldev, struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); struct pcs_function *func; enum pin_config_param param; - unsigned offset = 0, data = 0, i, j, ret; + unsigned offset = 0, data = 0, i, j; + int ret; ret = pcs_get_function(pctldev, pin, &func); if (ret) @@ -487,7 +533,8 @@ static int pcs_pinconf_get(struct pinctrl_dev *pctldev, break; case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_SLEW_RATE: - case PIN_CONFIG_LOW_POWER_MODE: + case PIN_CONFIG_MODE_LOW_POWER: + case PIN_CONFIG_INPUT_ENABLE: default: *config = data; break; @@ -503,42 +550,51 @@ static int pcs_pinconf_set(struct pinctrl_dev *pctldev, { struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); struct pcs_function *func; - unsigned offset = 0, shift = 0, i, data, ret; + unsigned offset = 0, shift = 0, i, data; u32 arg; - int j; + int j, ret; + enum pin_config_param param; ret = pcs_get_function(pctldev, pin, &func); if (ret) return ret; for (j = 0; j < num_configs; j++) { + param = pinconf_to_config_param(configs[j]); + + /* BIAS_DISABLE has no entry in the func->conf table */ + if (param == PIN_CONFIG_BIAS_DISABLE) { + /* This just disables all bias entries */ + pcs_pinconf_clear_bias(pctldev, pin); + continue; + } + for (i = 0; i < func->nconfs; i++) { - if (pinconf_to_config_param(configs[j]) - != func->conf[i].param) + if (param != func->conf[i].param) continue; offset = pin * (pcs->width / BITS_PER_BYTE); data = pcs->read(pcs->base + offset); arg = pinconf_to_config_argument(configs[j]); - switch (func->conf[i].param) { + switch (param) { /* 2 parameters */ case PIN_CONFIG_INPUT_SCHMITT: case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_SLEW_RATE: - case PIN_CONFIG_LOW_POWER_MODE: + case PIN_CONFIG_MODE_LOW_POWER: + case PIN_CONFIG_INPUT_ENABLE: shift = ffs(func->conf[i].mask) - 1; data &= ~func->conf[i].mask; data |= (arg << shift) & func->conf[i].mask; break; /* 4 parameters */ - case PIN_CONFIG_BIAS_DISABLE: - pcs_pinconf_clear_bias(pctldev, pin); - break; case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: - if (arg) + if (arg) { pcs_pinconf_clear_bias(pctldev, pin); - /* fall through */ + data = pcs->read(pcs->base + offset); + } + fallthrough; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: data &= ~func->conf[i].mask; if (arg) @@ -632,8 +688,7 @@ static const struct pinconf_ops pcs_pinconf_ops = { * @pcs: pcs driver instance * @offset: register offset from base */ -static int pcs_add_pin(struct pcs_device *pcs, unsigned offset, - unsigned pin_pos) +static int pcs_add_pin(struct pcs_device *pcs, unsigned int offset) { struct pcs_soc_data *pcs_soc = &pcs->socdata; struct pinctrl_pin_desc *pin; @@ -677,21 +732,19 @@ static int pcs_add_pin(struct pcs_device *pcs, unsigned offset, static int pcs_allocate_pin_table(struct pcs_device *pcs) { int mux_bytes, nr_pins, i; - int num_pins_in_register = 0; mux_bytes = pcs->width / BITS_PER_BYTE; - if (pcs->bits_per_mux) { + if (pcs->bits_per_mux && pcs->fmask) { pcs->bits_per_pin = fls(pcs->fmask); nr_pins = (pcs->size * BITS_PER_BYTE) / pcs->bits_per_pin; - num_pins_in_register = pcs->width / pcs->bits_per_pin; } else { nr_pins = pcs->size / mux_bytes; } dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins); - pcs->pins.pa = devm_kzalloc(pcs->dev, - sizeof(*pcs->pins.pa) * nr_pins, + pcs->pins.pa = devm_kcalloc(pcs->dev, + nr_pins, sizeof(*pcs->pins.pa), GFP_KERNEL); if (!pcs->pins.pa) return -ENOMEM; @@ -702,17 +755,9 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs) for (i = 0; i < pcs->desc.npins; i++) { unsigned offset; int res; - int byte_num; - int pin_pos = 0; - if (pcs->bits_per_mux) { - byte_num = (pcs->bits_per_pin * i) / BITS_PER_BYTE; - offset = (byte_num / mux_bytes) * mux_bytes; - pin_pos = i % num_pins_in_register; - } else { - offset = i * mux_bytes; - } - res = pcs_add_pin(pcs, offset, pin_pos); + offset = pcs_pin_reg_offset_get(pcs, i); + res = pcs_add_pin(pcs, offset); if (res < 0) { dev_err(pcs->dev, "error adding pins: %i\n", res); return res; @@ -725,38 +770,45 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs) /** * pcs_add_function() - adds a new function to the function list * @pcs: pcs driver instance - * @np: device node of the mux entry + * @fcn: new function allocated * @name: name of the function * @vals: array of mux register value pairs used by the function * @nvals: number of mux register value pairs * @pgnames: array of pingroup names for the function * @npgnames: number of pingroup names + * + * Caller must take care of locking. */ -static struct pcs_function *pcs_add_function(struct pcs_device *pcs, - struct device_node *np, - const char *name, - struct pcs_func_vals *vals, - unsigned nvals, - const char **pgnames, - unsigned npgnames) +static int pcs_add_function(struct pcs_device *pcs, + struct pcs_function **fcn, + const char *name, + struct pcs_func_vals *vals, + unsigned int nvals, + const char **pgnames, + unsigned int npgnames) { struct pcs_function *function; - int res; + int selector; function = devm_kzalloc(pcs->dev, sizeof(*function), GFP_KERNEL); if (!function) - return NULL; + return -ENOMEM; function->vals = vals; function->nvals = nvals; + function->name = name; + + selector = pinmux_generic_add_function(pcs->pctl, name, + pgnames, npgnames, + function); + if (selector < 0) { + devm_kfree(pcs->dev, function); + *fcn = NULL; + } else { + *fcn = function; + } - res = pinmux_generic_add_function(pcs->pctl, name, - pgnames, npgnames, - function); - if (res) - return NULL; - - return function; + return selector; } /** @@ -873,13 +925,14 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, int i = 0, nconfs = 0; unsigned long *settings = NULL, *s = NULL; struct pcs_conf_vals *conf = NULL; - struct pcs_conf_type prop2[] = { + static const struct pcs_conf_type prop2[] = { { "pinctrl-single,drive-strength", PIN_CONFIG_DRIVE_STRENGTH, }, { "pinctrl-single,slew-rate", PIN_CONFIG_SLEW_RATE, }, + { "pinctrl-single,input-enable", PIN_CONFIG_INPUT_ENABLE, }, { "pinctrl-single,input-schmitt", PIN_CONFIG_INPUT_SCHMITT, }, - { "pinctrl-single,low-power-mode", PIN_CONFIG_LOW_POWER_MODE, }, + { "pinctrl-single,low-power-mode", PIN_CONFIG_MODE_LOW_POWER, }, }; - struct pcs_conf_type prop4[] = { + static const struct pcs_conf_type prop4[] = { { "pinctrl-single,bias-pullup", PIN_CONFIG_BIAS_PULL_UP, }, { "pinctrl-single,bias-pulldown", PIN_CONFIG_BIAS_PULL_DOWN, }, { "pinctrl-single,input-schmitt-enable", @@ -888,29 +941,29 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, /* If pinconf isn't supported, don't parse properties in below. */ if (!PCS_HAS_PINCONF) - return 0; + return -ENOTSUPP; /* cacluate how much properties are supported in current node */ for (i = 0; i < ARRAY_SIZE(prop2); i++) { - if (of_find_property(np, prop2[i].name, NULL)) + if (of_property_present(np, prop2[i].name)) nconfs++; } for (i = 0; i < ARRAY_SIZE(prop4); i++) { - if (of_find_property(np, prop4[i].name, NULL)) + if (of_property_present(np, prop4[i].name)) nconfs++; } if (!nconfs) - return 0; + return -ENOTSUPP; - func->conf = devm_kzalloc(pcs->dev, - sizeof(struct pcs_conf_vals) * nconfs, + func->conf = devm_kcalloc(pcs->dev, + nconfs, sizeof(struct pcs_conf_vals), GFP_KERNEL); if (!func->conf) return -ENOMEM; func->nconfs = nconfs; conf = &(func->conf[0]); m++; - settings = devm_kzalloc(pcs->dev, sizeof(unsigned long) * nconfs, + settings = devm_kcalloc(pcs->dev, nconfs, sizeof(unsigned long), GFP_KERNEL); if (!settings) return -ENOMEM; @@ -930,8 +983,7 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, } /** - * smux_parse_one_pinctrl_entry() - parses a device tree mux entry - * @pctldev: pin controller device + * pcs_parse_one_pinctrl_entry() - parses a device tree mux entry * @pcs: pinctrl driver instance * @np: device node of the mux entry * @map: map entry @@ -957,8 +1009,8 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, { const char *name = "pinctrl-single,pins"; struct pcs_func_vals *vals; - int rows, *pins, found = 0, res = -ENOMEM, i; - struct pcs_function *function; + int rows, *pins, found = 0, res = -ENOMEM, i, fsel, gsel; + struct pcs_function *function = NULL; rows = pinctrl_count_index_with_args(np, name); if (rows <= 0) { @@ -966,11 +1018,11 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, return -EINVAL; } - vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL); + vals = devm_kcalloc(pcs->dev, rows, sizeof(*vals), GFP_KERNEL); if (!vals) return -ENOMEM; - pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows, GFP_KERNEL); + pins = devm_kcalloc(pcs->dev, rows, sizeof(*pins), GFP_KERNEL); if (!pins) goto free_vals; @@ -983,62 +1035,78 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, if (res) return res; - if (pinctrl_spec.args_count < 2) { + if (pinctrl_spec.args_count < 2 || pinctrl_spec.args_count > 3) { dev_err(pcs->dev, "invalid args_count for spec: %i\n", pinctrl_spec.args_count); break; } - /* Index plus one value cell */ offset = pinctrl_spec.args[0]; vals[found].reg = pcs->base + offset; - vals[found].val = pinctrl_spec.args[1]; - dev_dbg(pcs->dev, "%s index: 0x%x value: 0x%x\n", - pinctrl_spec.np->name, offset, pinctrl_spec.args[1]); + switch (pinctrl_spec.args_count) { + case 2: + vals[found].val = pinctrl_spec.args[1]; + break; + case 3: + vals[found].val = (pinctrl_spec.args[1] | pinctrl_spec.args[2]); + break; + } + + dev_dbg(pcs->dev, "%pOFn index: 0x%x value: 0x%x\n", + pinctrl_spec.np, offset, vals[found].val); pin = pcs_get_pin_by_offset(pcs, offset); if (pin < 0) { dev_err(pcs->dev, - "could not add functions for %s %ux\n", - np->name, offset); + "could not add functions for %pOFn %ux\n", + np, offset); break; } pins[found++] = pin; } pgnames[0] = np->name; - function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1); - if (!function) { - res = -ENOMEM; + mutex_lock(&pcs->mutex); + fsel = pcs_add_function(pcs, &function, np->name, vals, found, + pgnames, 1); + if (fsel < 0) { + res = fsel; goto free_pins; } - res = pinctrl_generic_add_group(pcs->pctl, np->name, pins, found, pcs); - if (res < 0) + gsel = pinctrl_generic_add_group(pcs->pctl, np->name, pins, found, pcs); + if (gsel < 0) { + res = gsel; goto free_function; + } (*map)->type = PIN_MAP_TYPE_MUX_GROUP; (*map)->data.mux.group = np->name; (*map)->data.mux.function = np->name; - if (PCS_HAS_PINCONF) { + if (PCS_HAS_PINCONF && function) { res = pcs_parse_pinconf(pcs, np, function, map); - if (res) + if (res == 0) + *num_maps = 2; + else if (res == -ENOTSUPP) + *num_maps = 1; + else goto free_pingroups; - *num_maps = 2; } else { *num_maps = 1; } + mutex_unlock(&pcs->mutex); + return 0; free_pingroups: - pinctrl_generic_remove_last_group(pcs->pctl); + pinctrl_generic_remove_group(pcs->pctl, gsel); *num_maps = 1; free_function: - pinmux_generic_remove_last_function(pcs->pctl); - + pinmux_generic_remove_function(pcs->pctl, fsel); free_pins: + mutex_unlock(&pcs->mutex); devm_kfree(pcs->dev, pins); free_vals: @@ -1055,9 +1123,9 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, { const char *name = "pinctrl-single,bits"; struct pcs_func_vals *vals; - int rows, *pins, found = 0, res = -ENOMEM, i; + int rows, *pins, found = 0, res = -ENOMEM, i, fsel; int npins_in_row; - struct pcs_function *function; + struct pcs_function *function = NULL; rows = pinctrl_count_index_with_args(np, name); if (rows <= 0) { @@ -1065,15 +1133,22 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, return -EINVAL; } + if (PCS_HAS_PINCONF) { + dev_err(pcs->dev, "pinconf not supported\n"); + return -ENOTSUPP; + } + npins_in_row = pcs->width / pcs->bits_per_pin; - vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows * npins_in_row, - GFP_KERNEL); + vals = devm_kzalloc(pcs->dev, + array3_size(rows, npins_in_row, sizeof(*vals)), + GFP_KERNEL); if (!vals) return -ENOMEM; - pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows * npins_in_row, - GFP_KERNEL); + pins = devm_kzalloc(pcs->dev, + array3_size(rows, npins_in_row, sizeof(*pins)), + GFP_KERNEL); if (!pins) goto free_vals; @@ -1099,8 +1174,8 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, val = pinctrl_spec.args[1]; mask = pinctrl_spec.args[2]; - dev_dbg(pcs->dev, "%s index: 0x%x value: 0x%x mask: 0x%x\n", - pinctrl_spec.np->name, offset, val, mask); + dev_dbg(pcs->dev, "%pOFn index: 0x%x value: 0x%x mask: 0x%x\n", + pinctrl_spec.np, offset, val, mask); /* Parse pins in each row from LSB */ while (mask) { @@ -1112,8 +1187,8 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, if ((mask & mask_pos) == 0) { dev_err(pcs->dev, - "Invalid mask for %s at 0x%x\n", - np->name, offset); + "Invalid mask for %pOFn at 0x%x\n", + np, offset); break; } @@ -1121,8 +1196,8 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, if (submask != mask_pos) { dev_warn(pcs->dev, - "Invalid submask 0x%x for %s at 0x%x\n", - submask, np->name, offset); + "Invalid submask 0x%x for %pOFn at 0x%x\n", + submask, np, offset); continue; } @@ -1133,8 +1208,8 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, pin = pcs_get_pin_by_offset(pcs, offset); if (pin < 0) { dev_err(pcs->dev, - "could not add functions for %s %ux\n", - np->name, offset); + "could not add functions for %pOFn %ux\n", + np, offset); break; } pins[found++] = pin + pin_num_from_lsb; @@ -1142,9 +1217,11 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, } pgnames[0] = np->name; - function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1); - if (!function) { - res = -ENOMEM; + mutex_lock(&pcs->mutex); + fsel = pcs_add_function(pcs, &function, np->name, vals, found, + pgnames, 1); + if (fsel < 0) { + res = fsel; goto free_pins; } @@ -1156,20 +1233,15 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, (*map)->data.mux.group = np->name; (*map)->data.mux.function = np->name; - if (PCS_HAS_PINCONF) { - dev_err(pcs->dev, "pinconf not supported\n"); - goto free_pingroups; - } - *num_maps = 1; + mutex_unlock(&pcs->mutex); + return 0; -free_pingroups: - pinctrl_generic_remove_last_group(pcs->pctl); - *num_maps = 1; free_function: - pinmux_generic_remove_last_function(pcs->pctl); + pinmux_generic_remove_function(pcs->pctl, fsel); free_pins: + mutex_unlock(&pcs->mutex); devm_kfree(pcs->dev, pins); free_vals: @@ -1195,7 +1267,7 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, pcs = pinctrl_dev_get_drvdata(pctldev); /* create 2 maps. One is for pinmux, and the other is for pinconf. */ - *map = devm_kzalloc(pcs->dev, sizeof(**map) * 2, GFP_KERNEL); + *map = devm_kcalloc(pcs->dev, 2, sizeof(**map), GFP_KERNEL); if (!*map) return -ENOMEM; @@ -1211,16 +1283,16 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, ret = pcs_parse_bits_in_pinctrl_entry(pcs, np_config, map, num_maps, pgnames); if (ret < 0) { - dev_err(pcs->dev, "no pins entries for %s\n", - np_config->name); + dev_err(pcs->dev, "no pins entries for %pOFn\n", + np_config); goto free_pgnames; } } else { ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, num_maps, pgnames); if (ret < 0) { - dev_err(pcs->dev, "no pins entries for %s\n", - np_config->name); + dev_err(pcs->dev, "no pins entries for %pOFn\n", + np_config); goto free_pgnames; } } @@ -1262,7 +1334,6 @@ static void pcs_irq_free(struct pcs_device *pcs) static void pcs_free_resources(struct pcs_device *pcs) { pcs_irq_free(pcs); - pinctrl_unregister(pcs->pctl); #if IS_BUILTIN(CONFIG_PINCTRL_SINGLE) if (pcs->missing_nr_pinctrl_cells) @@ -1300,7 +1371,9 @@ static int pcs_add_gpio_func(struct device_node *node, struct pcs_device *pcs) } return ret; } + /** + * struct pcs_interrupt * @reg: virtual address of interrupt register * @hwirq: hardware irq number * @irq: virtual irq number @@ -1315,6 +1388,9 @@ struct pcs_interrupt { /** * pcs_irq_set() - enables or disables an interrupt + * @pcs_soc: SoC specific settings + * @irq: interrupt + * @enable: enable or disable the interrupt * * Note that this currently assumes one interrupt per pinctrl * register that is typically used for wake-up events. @@ -1395,7 +1471,7 @@ static int pcs_irq_set_wake(struct irq_data *d, unsigned int state) /** * pcs_irq_handle() - common interrupt handler - * @pcs_irq: interrupt data + * @pcs_soc: SoC specific settings * * Note that this currently assumes we have one interrupt bit per * mux register. This interrupt is typically used for wake-up events. @@ -1417,8 +1493,8 @@ static int pcs_irq_handle(struct pcs_soc_data *pcs_soc) mask = pcs->read(pcswi->reg); raw_spin_unlock(&pcs->lock); if (mask & pcs_soc->irq_status_mask) { - generic_handle_irq(irq_find_mapping(pcs->domain, - pcswi->hwirq)); + generic_handle_domain_irq(pcs->domain, + pcswi->hwirq); count++; } } @@ -1442,8 +1518,7 @@ static irqreturn_t pcs_irq_handler(int irq, void *d) } /** - * pcs_irq_handle() - handler for the dedicated chained interrupt case - * @irq: interrupt + * pcs_irq_chain_handler() - handler for the dedicated chained interrupt case * @desc: interrupt descriptor * * Use this if you have a separate interrupt for each @@ -1459,8 +1534,6 @@ static void pcs_irq_chain_handler(struct irq_desc *desc) pcs_irq_handle(pcs_soc); /* REVISIT: export and add handle_bad_irq(irq, desc)? */ chained_irq_exit(chip, desc); - - return; } static int pcs_irqdomain_map(struct irq_domain *d, unsigned int irq, @@ -1486,7 +1559,7 @@ static int pcs_irqdomain_map(struct irq_domain *d, unsigned int irq, irq_set_chip_data(irq, pcs_soc); irq_set_chip_and_handler(irq, &pcs->chip, handle_level_irq); - irq_set_lockdep_class(irq, &pcs_lock_class); + irq_set_lockdep_class(irq, &pcs_lock_class, &pcs_request_class); irq_set_noprobe(irq); return 0; @@ -1541,15 +1614,16 @@ static int pcs_irq_init_chained_handler(struct pcs_device *pcs, /* * We can use the register offset as the hardirq - * number as irq_domain_add_simple maps them lazily. + * number as irq_domain_create_simple maps them lazily. * This way we can easily support more than one * interrupt per function if needed. */ num_irqs = pcs->size; - pcs->domain = irq_domain_add_simple(np, num_irqs, 0, - &pcs_irqdomain_ops, - pcs_soc); + pcs->domain = irq_domain_create_simple(of_fwnode_handle(np), + num_irqs, 0, + &pcs_irqdomain_ops, + pcs_soc); if (!pcs->domain) { irq_set_chained_handler(pcs_soc->irq, NULL); return -EINVAL; @@ -1558,30 +1632,98 @@ static int pcs_irq_init_chained_handler(struct pcs_device *pcs, return 0; } -#ifdef CONFIG_PM -static int pinctrl_single_suspend(struct platform_device *pdev, - pm_message_t state) +static int pcs_save_context(struct pcs_device *pcs) { - struct pcs_device *pcs; + int i, mux_bytes; + u64 *regsl; + u32 *regsw; + u16 *regshw; - pcs = platform_get_drvdata(pdev); - if (!pcs) - return -EINVAL; + mux_bytes = pcs->width / BITS_PER_BYTE; + + if (!pcs->saved_vals) { + pcs->saved_vals = devm_kzalloc(pcs->dev, pcs->size, GFP_ATOMIC); + if (!pcs->saved_vals) + return -ENOMEM; + } + + switch (pcs->width) { + case 64: + regsl = pcs->saved_vals; + for (i = 0; i < pcs->size; i += mux_bytes) + *regsl++ = pcs->read(pcs->base + i); + break; + case 32: + regsw = pcs->saved_vals; + for (i = 0; i < pcs->size; i += mux_bytes) + *regsw++ = pcs->read(pcs->base + i); + break; + case 16: + regshw = pcs->saved_vals; + for (i = 0; i < pcs->size; i += mux_bytes) + *regshw++ = pcs->read(pcs->base + i); + break; + } + + return 0; +} + +static void pcs_restore_context(struct pcs_device *pcs) +{ + int i, mux_bytes; + u64 *regsl; + u32 *regsw; + u16 *regshw; + + mux_bytes = pcs->width / BITS_PER_BYTE; + + switch (pcs->width) { + case 64: + regsl = pcs->saved_vals; + for (i = 0; i < pcs->size; i += mux_bytes) + pcs->write(*regsl++, pcs->base + i); + break; + case 32: + regsw = pcs->saved_vals; + for (i = 0; i < pcs->size; i += mux_bytes) + pcs->write(*regsw++, pcs->base + i); + break; + case 16: + regshw = pcs->saved_vals; + for (i = 0; i < pcs->size; i += mux_bytes) + pcs->write(*regshw++, pcs->base + i); + break; + } +} + +static int pinctrl_single_suspend_noirq(struct device *dev) +{ + struct pcs_device *pcs = dev_get_drvdata(dev); + + if (pcs->flags & PCS_CONTEXT_LOSS_OFF) { + int ret; + + ret = pcs_save_context(pcs); + if (ret < 0) + return ret; + } return pinctrl_force_sleep(pcs->pctl); } -static int pinctrl_single_resume(struct platform_device *pdev) +static int pinctrl_single_resume_noirq(struct device *dev) { - struct pcs_device *pcs; + struct pcs_device *pcs = dev_get_drvdata(dev); - pcs = platform_get_drvdata(pdev); - if (!pcs) - return -EINVAL; + if (pcs->flags & PCS_CONTEXT_LOSS_OFF) + pcs_restore_context(pcs); return pinctrl_force_default(pcs->pctl); } -#endif + +static DEFINE_NOIRQ_DEV_PM_OPS(pinctrl_single_pm_ops, + pinctrl_single_suspend_noirq, + pinctrl_single_resume_noirq); /** * pcs_quirk_missing_pinctrl_cells - handle legacy binding @@ -1646,10 +1788,9 @@ static int pcs_probe(struct platform_device *pdev) return -EINVAL; pcs = devm_kzalloc(&pdev->dev, sizeof(*pcs), GFP_KERNEL); - if (!pcs) { - dev_err(&pdev->dev, "could not allocate\n"); + if (!pcs) return -ENOMEM; - } + pcs->dev = &pdev->dev; pcs->np = np; raw_spin_lock_init(&pcs->lock); @@ -1743,7 +1884,7 @@ static int pcs_probe(struct platform_device *pdev) if (ret < 0) goto free; - ret = pinctrl_register_and_init(&pcs->desc, pcs->dev, pcs, &pcs->pctl); + ret = devm_pinctrl_register_and_init(pcs->dev, &pcs->desc, pcs, &pcs->pctl); if (ret) { dev_err(pcs->dev, "could not register single pinctrl driver\n"); goto free; @@ -1774,27 +1915,24 @@ static int pcs_probe(struct platform_device *pdev) dev_warn(pcs->dev, "initialized with no interrupts\n"); } - dev_info(pcs->dev, "%i pins at pa %p size %u\n", - pcs->desc.npins, pcs->base, pcs->size); + dev_info(pcs->dev, "%i pins, size %u\n", pcs->desc.npins, pcs->size); - return pinctrl_enable(pcs->pctl); + ret = pinctrl_enable(pcs->pctl); + if (ret) + goto free; + return 0; free: pcs_free_resources(pcs); return ret; } -static int pcs_remove(struct platform_device *pdev) +static void pcs_remove(struct platform_device *pdev) { struct pcs_device *pcs = platform_get_drvdata(pdev); - if (!pcs) - return 0; - pcs_free_resources(pcs); - - return 0; } static const struct pcs_soc_data pinctrl_single_omap_wkup = { @@ -1809,11 +1947,21 @@ static const struct pcs_soc_data pinctrl_single_dra7 = { }; static const struct pcs_soc_data pinctrl_single_am437x = { - .flags = PCS_QUIRK_SHARED_IRQ, + .flags = PCS_QUIRK_SHARED_IRQ | PCS_CONTEXT_LOSS_OFF, .irq_enable_mask = (1 << 29), /* OMAP_WAKEUP_EN */ .irq_status_mask = (1 << 30), /* OMAP_WAKEUP_EVENT */ }; +static const struct pcs_soc_data pinctrl_single_am654 = { + .flags = PCS_QUIRK_SHARED_IRQ | PCS_CONTEXT_LOSS_OFF, + .irq_enable_mask = (1 << 29), /* WKUP_EN */ + .irq_status_mask = (1 << 30), /* WKUP_EVT */ +}; + +static const struct pcs_soc_data pinctrl_single_j7200 = { + .flags = PCS_CONTEXT_LOSS_OFF, +}; + static const struct pcs_soc_data pinctrl_single = { }; @@ -1822,11 +1970,14 @@ static const struct pcs_soc_data pinconf_single = { }; static const struct of_device_id pcs_of_match[] = { + { .compatible = "marvell,pxa1908-padconf", .data = &pinconf_single }, + { .compatible = "ti,am437-padconf", .data = &pinctrl_single_am437x }, + { .compatible = "ti,am654-padconf", .data = &pinctrl_single_am654 }, + { .compatible = "ti,dra7-padconf", .data = &pinctrl_single_dra7 }, { .compatible = "ti,omap3-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap4-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap5-padconf", .data = &pinctrl_single_omap_wkup }, - { .compatible = "ti,dra7-padconf", .data = &pinctrl_single_dra7 }, - { .compatible = "ti,am437-padconf", .data = &pinctrl_single_am437x }, + { .compatible = "ti,j7200-padconf", .data = &pinctrl_single_j7200 }, { .compatible = "pinctrl-single", .data = &pinctrl_single }, { .compatible = "pinconf-single", .data = &pinconf_single }, { }, @@ -1839,11 +1990,8 @@ static struct platform_driver pcs_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = pcs_of_match, + .pm = pm_sleep_ptr(&pinctrl_single_pm_ops), }, -#ifdef CONFIG_PM - .suspend = pinctrl_single_suspend, - .resume = pinctrl_single_resume, -#endif }; module_platform_driver(pcs_driver); |
