summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/pinctrl-single.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/pinctrl-single.c')
-rw-r--r--drivers/pinctrl/pinctrl-single.c514
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);