/* * Generic device tree based pinctrl driver for one register per pin * type pinmux controllers * * Copyright (C) 2012 Texas Instruments, Inc. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied.
*/
/** * struct pcs_function - pinctrl function * @name: pinctrl function name * @vals: register and vals array * @nvals: number of entries in vals array * @conf: array of pin configurations * @nconfs: number of pin configurations available * @node: list node
*/ struct pcs_function { constchar *name; struct pcs_func_vals *vals; unsigned nvals; struct pcs_conf_vals *conf; int nconfs; struct list_head node;
};
/** * struct pcs_gpiofunc_range - pin ranges with same mux value of gpio function * @offset: offset base of pins * @npins: number pins with the same mux value of gpio function * @gpiofunc: mux value of gpio function * @node: list node
*/ struct pcs_gpiofunc_range { unsigned offset; unsigned npins; unsigned gpiofunc; struct list_head node;
};
/** * struct pcs_data - wrapper for data needed by pinctrl framework * @pa: pindesc array * @cur: index to current element * * REVISIT: We should be able to drop this eventually by adding * support for registering pins individually in the pinctrl * framework for those drivers that don't need a static array.
*/ struct pcs_data { struct pinctrl_pin_desc *pa; int cur;
};
/** * struct pcs_soc_data - SoC specific settings * @flags: initial SoC specific PCS_FEAT_xxx values * @irq: optional interrupt for the controller * @irq_enable_mask: optional SoC specific interrupt enable mask * @irq_status_mask: optional SoC specific interrupt status mask * @rearm: optional SoC specific wake-up rearm function
*/ struct pcs_soc_data { unsigned flags; int irq; unsigned irq_enable_mask; unsigned irq_status_mask; void (*rearm)(void);
};
/** * 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 * @pctl: pin controller device * @flags: mask of PCS_FEAT_xxx values * @missing_nr_pinctrl_cells: for legacy binding, may go away * @socdata: soc specific data * @lock: spinlock for register access * @mutex: mutex protecting the lists * @width: bits per mux register * @fmask: function register mask * @fshift: function register shift * @foff: value to turn mux off * @fmax: max number of functions in fmask * @bits_per_mux: number of bits per mux * @bits_per_pin: number of bits per pin * @pins: physical pins on the SoC * @gpiofuncs: list of gpio functions * @irqs: list of interrupt registers * @chip: chip container for this instance * @domain: IRQ domain for this instance * @desc: pin controller descriptor * @read: register read function to use * @write: register write function to use
*/ 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) struct property *missing_nr_pinctrl_cells; struct pcs_soc_data socdata;
raw_spinlock_t lock; struct mutex mutex; unsigned width; unsigned fmask; unsigned fshift; unsigned foff; unsigned fmax; bool bits_per_mux; unsigned bits_per_pin; struct pcs_data pins; struct list_head gpiofuncs; struct list_head irqs; struct irq_chip chip; struct irq_domain *domain; struct pinctrl_desc desc; unsigned (*read)(void __iomem *reg); void (*write)(unsigned val, void __iomem *reg);
};
/* * This lock class tells lockdep that irqchip core that this single * pinctrl can be in a different category than its parents, so it won't * report false recursion.
*/ staticstruct lock_class_key pcs_lock_class;
/* Class for the IRQ request mutex */ staticstruct 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 * critical as they may need to be remuxed every time before and after * idle. Adding tests for register access width for every read and * write like regmap is doing is not desired, and caching the registers * does not help in this case.
*/
/* If pin is not described in DTS & enabled, mux_setting is NULL. */
setting = pdesc->mux_setting; if (!setting) 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",
__func__, fselector); return -ENOTSUPP;
} return 0;
}
pcs = pinctrl_dev_get_drvdata(pctldev); /* If function mask is null, needn't enable it. */ if (!pcs->fmask) return 0;
function = pinmux_generic_get_function(pctldev, fselector); if (!function) return -EINVAL;
func = function->data; if (!func) return -EINVAL;
/* 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 (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 (param) { /* 2 parameters */ case PIN_CONFIG_INPUT_SCHMITT: case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_SLEW_RATE: 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_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: if (arg) {
pcs_pinconf_clear_bias(pctldev, pin);
data = pcs->read(pcs->base + offset);
}
fallthrough; case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
data &= ~func->conf[i].mask; if (arg)
data |= func->conf[i].enable; else
data |= func->conf[i].disable; break; default: return -ENOTSUPP;
}
pcs->write(data, pcs->base + offset);
break;
} if (i >= func->nconfs) return -ENOTSUPP;
} /* for each config */
return 0;
}
staticint pcs_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned group, unsignedlong *config)
{ constunsigned *pins; unsigned npins, old = 0; int i, ret;
ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); if (ret) return ret; for (i = 0; i < npins; i++) { if (pcs_pinconf_get(pctldev, pins[i], config)) return -ENOTSUPP; /* configs do not match between two pins */ if (i && (old != *config)) return -ENOTSUPP;
old = *config;
} return 0;
}
staticint pcs_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, unsignedlong *configs, unsigned num_configs)
{ constunsigned *pins; unsigned npins; int i, ret;
ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); if (ret) return ret; for (i = 0; i < npins; i++) { if (pcs_pinconf_set(pctldev, pins[i], configs, num_configs)) return -ENOTSUPP;
} return 0;
}
/** * pcs_allocate_pin_table() - adds all the pins for the pinctrl driver * @pcs: pcs driver instance * * In case of errors, resources are freed in pcs_free_resources. * * If your hardware needs holes in the address space, then just set * up multiple driver instances.
*/ staticint pcs_allocate_pin_table(struct pcs_device *pcs)
{ int mux_bytes, nr_pins, i;
/** * pcs_add_function() - adds a new function to the function list * @pcs: pcs driver instance * @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.
*/ staticint pcs_add_function(struct pcs_device *pcs, struct pcs_function **fcn, constchar *name, struct pcs_func_vals *vals, unsignedint nvals, constchar **pgnames, unsignedint npgnames)
{ struct pcs_function *function; int selector;
function = devm_kzalloc(pcs->dev, sizeof(*function), GFP_KERNEL); if (!function) return -ENOMEM;
/** * pcs_get_pin_by_offset() - get a pin index based on the register offset * @pcs: pcs driver instance * @offset: register offset from the base * * Note that this is OK as long as the pins are in a static array.
*/ staticint pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset)
{ unsigned index;
if (offset >= pcs->size) {
dev_err(pcs->dev, "mux offset out of range: 0x%x (0x%x)\n",
offset, pcs->size); return -EINVAL;
}
if (pcs->bits_per_mux)
index = (offset * BITS_PER_BYTE) / pcs->bits_per_pin; else
index = offset / (pcs->width / BITS_PER_BYTE);
return index;
}
/* * check whether data matches enable bits or disable bits * Return value: 1 for matching enable bits, 0 for matching disable bits, * and negative value for matching failure.
*/ staticint pcs_config_match(unsigned data, unsigned enable, unsigned disable)
{ int ret = -EINVAL;
if (data == enable)
ret = 1; elseif (data == disable)
ret = 0; return ret;
}
/* value to set, enable, disable, mask */
ret = of_property_read_u32_array(np, name, value, 4); if (ret) return; if (!value[3]) {
dev_err(pcs->dev, "mask field of the property can't be 0\n"); return;
}
value[0] &= value[3];
value[1] &= value[3];
value[2] &= value[3];
ret = pcs_config_match(value[0], value[1], value[2]); if (ret < 0)
dev_dbg(pcs->dev, "failed to match enable or disable bits\n");
add_config(conf, param, value[0], value[1], value[2], value[3]);
add_setting(settings, param, ret);
}
/* If pinconf isn't supported, don't parse properties in below. */ if (!PCS_HAS_PINCONF) return -ENOTSUPP;
/* cacluate how much properties are supported in current node */ for (i = 0; i < ARRAY_SIZE(prop2); i++) { if (of_property_present(np, prop2[i].name))
nconfs++;
} for (i = 0; i < ARRAY_SIZE(prop4); i++) { if (of_property_present(np, prop4[i].name))
nconfs++;
} if (!nconfs) return -ENOTSUPP;
for (i = 0; i < ARRAY_SIZE(prop2); i++)
pcs_add_conf2(pcs, np, prop2[i].name, prop2[i].param,
&conf, &s); for (i = 0; i < ARRAY_SIZE(prop4); i++)
pcs_add_conf4(pcs, np, prop4[i].name, prop4[i].param,
&conf, &s);
m->type = PIN_MAP_TYPE_CONFIGS_GROUP;
m->data.configs.group_or_pin = np->name;
m->data.configs.configs = settings;
m->data.configs.num_configs = nconfs; return 0;
}
/** * 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 * @num_maps: number of map * @pgnames: pingroup names * * Note that this binding currently supports only sets of one register + value. * * Also note that this driver tries to avoid understanding pin and function * names because of the extra bloat they would cause especially in the case of * a large number of pins. This driver just sets what is specified for the board * in the .dts file. Further user space debugging tools can be developed to * decipher the pin and function names using debugfs. * * If you are concerned about the boot time, set up the static pins in * the bootloader, and only set up selected pins as device tree entries.
*/ staticint pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, struct device_node *np, struct pinctrl_map **map, unsigned *num_maps, constchar **pgnames)
{ constchar *name = "pinctrl-single,pins"; struct pcs_func_vals *vals; 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) {
dev_err(pcs->dev, "Invalid number of rows: %d\n", rows); return -EINVAL;
}
vals = devm_kcalloc(pcs->dev, rows, sizeof(*vals), GFP_KERNEL); if (!vals) return -ENOMEM;
pins = devm_kcalloc(pcs->dev, rows, sizeof(*pins), GFP_KERNEL); if (!pins) goto free_vals;
for (i = 0; i < rows; i++) { struct of_phandle_args pinctrl_spec; unsignedint offset; int pin;
res = pinctrl_parse_index_with_args(np, name, i, &pinctrl_spec); if (res) return res;
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;
}
return res;
} /** * pcs_dt_node_to_map() - allocates and parses pinctrl maps * @pctldev: pinctrl instance * @np_config: device tree pinmux entry * @map: array of map entries * @num_maps: number of maps
*/ staticint pcs_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps)
{ struct pcs_device *pcs; constchar **pgnames; int ret;
pcs = pinctrl_dev_get_drvdata(pctldev);
/* create 2 maps. One is for pinmux, and the other is for pinconf. */
*map = devm_kcalloc(pcs->dev, 2, sizeof(**map), GFP_KERNEL); if (!*map) return -ENOMEM;
*num_maps = 0;
pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL); if (!pgnames) {
ret = -ENOMEM; goto free_map;
}
if (pcs->bits_per_mux) {
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 %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 %pOFn\n",
np_config); goto free_pgnames;
}
}
for (i = 0; ; i++) {
ret = of_parse_phandle_with_args(node, propname, cellname,
i, &gpiospec); /* Do not treat it as error. Only treat it as end condition. */ if (ret) {
ret = 0; break;
}
range = devm_kzalloc(pcs->dev, sizeof(*range), GFP_KERNEL); if (!range) {
ret = -ENOMEM; break;
}
range->offset = gpiospec.args[0];
range->npins = gpiospec.args[1];
range->gpiofunc = gpiospec.args[2];
mutex_lock(&pcs->mutex);
list_add_tail(&range->node, &pcs->gpiofuncs);
mutex_unlock(&pcs->mutex);
} return ret;
}
/** * struct pcs_interrupt * @reg: virtual address of interrupt register * @hwirq: hardware irq number * @irq: virtual irq number * @node: list node
*/ struct pcs_interrupt { void __iomem *reg;
irq_hw_number_t hwirq; unsignedint irq; struct list_head node;
};
/** * 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.
*/ staticinlinevoid pcs_irq_set(struct pcs_soc_data *pcs_soc, int irq, constbool enable)
{ struct pcs_device *pcs; struct list_head *pos; unsigned mask;
/** * pcs_irq_set_wake() - toggle the suspend and resume wake up * @d: interrupt data * @state: wake-up state * * Note that this should be called only for suspend and resume. * For runtime PM, the wake-up events should be enabled by default.
*/ staticint pcs_irq_set_wake(struct irq_data *d, unsignedint state)
{ if (state)
pcs_irq_unmask(d); else
pcs_irq_mask(d);
return 0;
}
/** * pcs_irq_handle() - common interrupt handler * @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. * For more complex interrupts different handlers can be specified.
*/ staticint pcs_irq_handle(struct pcs_soc_data *pcs_soc)
{ struct pcs_device *pcs; struct list_head *pos; int count = 0;
/** * pcs_irq_handler() - handler for the shared interrupt case * @irq: interrupt * @d: data * * Use this for cases where multiple instances of * pinctrl-single share a single interrupt like on omaps.
*/ static irqreturn_t pcs_irq_handler(int irq, void *d)
{ struct pcs_soc_data *pcs_soc = d;
/** * pcs_irq_chain_handler() - handler for the dedicated chained interrupt case * @desc: interrupt descriptor * * Use this if you have a separate interrupt for each * pinctrl-single instance.
*/ staticvoid pcs_irq_chain_handler(struct irq_desc *desc)
{ struct pcs_soc_data *pcs_soc = irq_desc_get_handler_data(desc); struct irq_chip *chip;
/* * We can use the register offset as the hardirq * 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;
staticint pcs_save_context(struct pcs_device *pcs)
{ int i, mux_bytes;
u64 *regsl;
u32 *regsw;
u16 *regshw;
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;
}
staticvoid 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;
}
}
/** * pcs_quirk_missing_pinctrl_cells - handle legacy binding * @pcs: pinctrl driver instance * @np: device tree node * @cells: number of cells * * Handle legacy binding with no #pinctrl-cells. This should be * always two pinctrl-single,bit-per-mux and one for others. * At some point we may want to consider removing this.
*/ staticint pcs_quirk_missing_pinctrl_cells(struct pcs_device *pcs, struct device_node *np, int cells)
{ struct property *p; constchar *name = "#pinctrl-cells"; int error;
u32 val;
error = of_property_read_u32(np, name, &val); if (!error) return 0;
dev_warn(pcs->dev, "please update dts to use %s = <%i>\n",
name, cells);
p = devm_kzalloc(pcs->dev, sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM;
ret = pcs_allocate_pin_table(pcs); if (ret < 0) goto free;
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;
}
ret = pcs_add_gpio_func(np, pcs); if (ret < 0) goto free;
pcs->socdata.irq = irq_of_parse_and_map(np, 0); if (pcs->socdata.irq)
pcs->flags |= PCS_FEAT_IRQ;
/* We still need auxdata for some omaps for PRM interrupts */
pdata = dev_get_platdata(&pdev->dev); if (pdata) { if (pdata->rearm)
pcs->socdata.rearm = pdata->rearm; if (pdata->irq) {
pcs->socdata.irq = pdata->irq;
pcs->flags |= PCS_FEAT_IRQ;
}
}
if (PCS_HAS_IRQ) {
ret = pcs_irq_init_chained_handler(pcs, np); if (ret < 0)
dev_warn(pcs->dev, "initialized with no interrupts\n");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.