// SPDX-License-Identifier: GPL-2.0 /* * Pin Control and GPIO driver for SuperH Pin Function Controller. * * Authors: Magnus Damm, Paul Mundt, Laurent Pinchart * * Copyright (C) 2008 Magnus Damm * Copyright (C) 2009 - 2012 Paul Mundt
*/
/* Count the MEM and IRQ resources. */ for (num_windows = 0;; num_windows++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, num_windows); if (!res) break;
} if (num_windows == 0) return -EINVAL;
num_irqs = platform_irq_count(pdev); if (num_irqs < 0) return num_irqs;
/* Allocate memory windows and IRQs arrays. */
windows = devm_kcalloc(pfc->dev, num_windows, sizeof(*windows),
GFP_KERNEL); if (windows == NULL) return -ENOMEM;
if (num_irqs) {
irqs = devm_kcalloc(pfc->dev, num_irqs, sizeof(*irqs),
GFP_KERNEL); if (irqs == NULL) return -ENOMEM;
pfc->num_irqs = num_irqs;
pfc->irqs = irqs;
}
/* Fill them. */ for (i = 0; i < num_windows; i++) {
windows->virt = devm_platform_get_and_ioremap_resource(pdev, i, &res); if (IS_ERR(windows->virt)) return -ENOMEM;
windows->phys = res->start;
windows->size = resource_size(res);
windows++;
} for (i = 0; i < num_irqs; i++)
*irqs++ = platform_get_irq(pdev, i);
for (k = 0; k < pfc->info->pinmux_data_size; k++) { if (data[k] == mark) {
*enum_idp = data[k + 1]; return k + 1;
}
}
dev_err(pfc->dev, "cannot locate data/mark enum_id for mark %d\n",
mark); return -EINVAL;
}
int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type)
{ conststruct pinmux_range *range; int pos = 0;
switch (pinmux_type) { case PINMUX_TYPE_GPIO: case PINMUX_TYPE_FUNCTION:
range = NULL; break;
#ifdef CONFIG_PINCTRL_SH_PFC_GPIO case PINMUX_TYPE_OUTPUT:
range = &pfc->info->output; break;
case PINMUX_TYPE_INPUT:
range = &pfc->info->input; break; #endif/* CONFIG_PINCTRL_SH_PFC_GPIO */
default: return -EINVAL;
}
/* Iterate over all the configuration fields we need to update. */ while (1) { conststruct pinmux_cfg_reg *cr; unsignedint field;
u16 enum_id;
u32 value; int in_range; int ret;
/* Check if the configuration field selects a function. If it * doesn't, skip the field if it's not applicable to the * requested pinmux type.
*/
in_range = sh_pfc_enum_in_range(enum_id, &pfc->info->function); if (!in_range) { if (pinmux_type == PINMUX_TYPE_FUNCTION) { /* Functions are allowed to modify all * fields.
*/
in_range = 1;
} elseif (pinmux_type != PINMUX_TYPE_GPIO) { /* Input/output types can only modify fields * that correspond to their respective ranges.
*/
in_range = sh_pfc_enum_in_range(enum_id, range);
/* * special case pass through for fixed * input-only or output-only pins without * function enum register association.
*/ if (in_range && enum_id == range->force) continue;
} /* GPIOs are only allowed to modify function fields. */
}
if (!in_range) continue;
ret = sh_pfc_get_config_reg(pfc, enum_id, &cr, &field, &value); if (ret < 0) return ret;
if (pfc->info->pins[0].pin == (u16)-1) { /* Pin number -1 denotes that the SoC doesn't report pin numbers * in its pin arrays yet. Consider the pin numbers range as * continuous and allocate a single range.
*/
pfc->nr_ranges = 1;
pfc->ranges = devm_kzalloc(pfc->dev, sizeof(*pfc->ranges),
GFP_KERNEL); if (pfc->ranges == NULL) return -ENOMEM;
/* Count, allocate and fill the ranges. The PFC SoC data pins array must * be sorted by pin numbers, and pins without a GPIO port must come * last.
*/ for (i = 1, nr_ranges = 1; i < pfc->info->nr_pins; ++i) { if (pfc->info->pins[i-1].pin != pfc->info->pins[i].pin - 1)
nr_ranges++;
}
staticunsignedint sh_pfc_walk_regs(struct sh_pfc *pfc, void (*do_reg)(struct sh_pfc *pfc, u32 reg, unsignedint idx))
{ unsignedint i, n = 0;
if (pfc->info->cfg_regs) for (i = 0; pfc->info->cfg_regs[i].reg; i++)
do_reg(pfc, pfc->info->cfg_regs[i].reg, n++);
if (pfc->info->drive_regs) for (i = 0; pfc->info->drive_regs[i].reg; i++)
do_reg(pfc, pfc->info->drive_regs[i].reg, n++);
if (pfc->info->bias_regs) for (i = 0; pfc->info->bias_regs[i].puen ||
pfc->info->bias_regs[i].pud; i++) { if (pfc->info->bias_regs[i].puen)
do_reg(pfc, pfc->info->bias_regs[i].puen, n++); if (pfc->info->bias_regs[i].pud)
do_reg(pfc, pfc->info->bias_regs[i].pud, n++);
}
if (pfc->info->ioctrl_regs) for (i = 0; pfc->info->ioctrl_regs[i].reg; i++)
do_reg(pfc, pfc->info->ioctrl_regs[i].reg, n++);
if (cfg_reg->field_width) {
fw = cfg_reg->field_width;
n = (cfg_reg->reg_width / fw) << fw; for (i = 0, r = 0; i < n; i += 1 << fw) { if (is0s(&cfg_reg->enum_ids[i], 1 << fw))
r++;
}
if ((r << fw) * sizeof(u16) > cfg_reg->reg_width / fw)
sh_pfc_warn("reg 0x%x can be described with variable-width reserved fields\n",
cfg_reg->reg);
/* Skip field checks (done at build time) */ goto check_enum_ids;
}
for (i = 0, n = 0, rw = 0; (fw = cfg_reg->var_field_width[i]); i++) { if (fw < 0) {
rw += -fw;
} else { if (is0s(&cfg_reg->enum_ids[n], 1 << fw))
sh_pfc_warn("reg 0x%x: field [%u:%u] can be described as reserved\n",
cfg_reg->reg, rw, rw + fw - 1);
n += 1 << fw;
rw += fw;
}
}
if (rw != cfg_reg->reg_width)
sh_pfc_err("reg 0x%x: var_field_width declares %u instead of %u bits\n",
cfg_reg->reg, rw, cfg_reg->reg_width);
if (n != cfg_reg->nr_enum_ids) {
sh_pfc_err("reg 0x%x: enum_ids[] has %u instead of %u values\n",
cfg_reg->reg, cfg_reg->nr_enum_ids, n);
n = cfg_reg->nr_enum_ids;
}
for (i = 0, bits = 0; i < ARRAY_SIZE(bias->pins); i++) if (bias->pins[i] != SH_PFC_PIN_NONE)
bits |= BIT(i);
if (bias->puen)
sh_pfc_check_reg(info->name, bias->puen, bits); if (bias->pud)
sh_pfc_check_reg(info->name, bias->pud, bits); for (i = 0; i < ARRAY_SIZE(bias->pins); i++) {
pin = sh_pfc_find_pin(info, bias->puen, bias->pins[i]); if (!pin) continue;
if (bias->puen && bias->pud) { /* * Pull-enable and pull-up/down control registers * As some SoCs have pins that support only pull-up * or pull-down, we just check for one of them
*/ if (!(pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN))
sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks one or more SH_PFC_PIN_CFG_PULL_* flags\n",
bias->puen, i, pin->name);
} elseif (bias->puen) { /* Pull-up control register only */ if (!(pin->configs & SH_PFC_PIN_CFG_PULL_UP))
sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks SH_PFC_PIN_CFG_PULL_UP flag\n",
bias->puen, i, pin->name);
} elseif (bias->pud) { /* Pull-down control register only */ if (!(pin->configs & SH_PFC_PIN_CFG_PULL_DOWN))
sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks SH_PFC_PIN_CFG_PULL_DOWN flag\n",
bias->pud, i, pin->name);
}
}
}
if (same_name(a->name, b->name))
sh_pfc_err("group %s: name conflict\n", a->name);
if (a->nr_pins > b->nr_pins)
swap(a, b);
len = a->nr_pins * sizeof(a->pins[0]); for (i = 0; i <= b->nr_pins - a->nr_pins; i++) { if (a->pins == b->pins + i || a->mux == b->mux + i ||
memcmp(a->pins, b->pins + i, len) ||
memcmp(a->mux, b->mux + i, len)) continue;
if (a->nr_pins == b->nr_pins)
sh_pfc_warn("group %s can be an alias for %s\n",
a->name, b->name); else
sh_pfc_warn("group %s is a subset of %s\n", a->name,
b->name);
}
}
if (pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN) { if (!info->ops || !info->ops->get_bias ||
!info->ops->set_bias)
sh_pfc_err_once(bias, "SH_PFC_PIN_CFG_PULL_* flag set but .[gs]et_bias() not implemented\n");
if (!bias_regs &&
(!info->ops || !info->ops->pin_to_portcr))
sh_pfc_err_once(bias, "SH_PFC_PIN_CFG_PULL_UP flag set but no bias_regs defined and .pin_to_portcr() not implemented\n");
}
if (!bias_reg ||
((pin->configs & SH_PFC_PIN_CFG_PULL_UP) &&
!bias_reg->puen))
sh_pfc_err("pin %s: SH_PFC_PIN_CFG_PULL_UP flag set but pin not in bias_regs\n",
pin->name);
if (!bias_reg ||
((pin->configs & SH_PFC_PIN_CFG_PULL_DOWN) &&
!bias_reg->pud))
sh_pfc_err("pin %s: SH_PFC_PIN_CFG_PULL_DOWN flag set but pin not in bias_regs\n",
pin->name);
}
if (pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH) { if (!drive_regs) {
sh_pfc_err_once(drive, "SH_PFC_PIN_CFG_DRIVE_STRENGTH flag set but drive_regs missing\n");
} else { for (j = 0; drive_reg(j); j++) { if (!drive_field(j).pin &&
!drive_field(j).offset &&
!drive_field(j).size) continue;
if (drive_field(j).pin == pin->pin) break;
}
if (!drive_reg(j))
sh_pfc_err("pin %s: SH_PFC_PIN_CFG_DRIVE_STRENGTH flag set but not in drive_regs\n",
pin->name);
}
}
if (pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE_MASK) { if (!info->ops || !info->ops->pin_to_pocctrl)
sh_pfc_err_once(power, "SH_PFC_PIN_CFG_IO_VOLTAGE set but .pin_to_pocctrl() not implemented\n"); elseif (info->ops->pin_to_pocctrl(pin->pin, &x) < 0)
sh_pfc_err("pin %s: SH_PFC_PIN_CFG_IO_VOLTAGE set but invalid pin_to_pocctrl()\n",
pin->name);
} elseif (info->ops && info->ops->pin_to_pocctrl &&
info->ops->pin_to_pocctrl(pin->pin, &x) >= 0) {
sh_pfc_warn("pin %s: SH_PFC_PIN_CFG_IO_VOLTAGE not set but valid pin_to_pocctrl()\n",
pin->name);
}
}
/* Check groups and functions */
refcnts = kcalloc(info->nr_groups, sizeof(*refcnts), GFP_KERNEL); if (!refcnts) return;
for (i = 0; i < info->nr_functions; i++) { conststruct sh_pfc_function *func = &info->functions[i];
if (!func->name) {
sh_pfc_err("empty function %u\n", i); continue;
} for (j = 0; j < i; j++) { if (same_name(func->name, info->functions[j].name))
sh_pfc_err("function %s: name conflict\n",
func->name);
} for (j = 0; j < func->nr_groups; j++) { for (k = 0; k < info->nr_groups; k++) { if (same_name(func->groups[j],
info->groups[k].name)) {
refcnts[k]++; break;
}
}
if (k == info->nr_groups)
sh_pfc_err("function %s: group %s not found\n",
func->name, func->groups[j]);
}
}
for (i = 0; i < info->nr_groups; i++) { conststruct sh_pfc_pin_group *group = &info->groups[i];
if (!group->name) {
sh_pfc_err("empty group %u\n", i); continue;
} for (j = 0; j < i; j++)
sh_pfc_compare_groups(drvname, group, &info->groups[j]);
if (!refcnts[i])
sh_pfc_err("orphan group %s\n", group->name); elseif (refcnts[i] > 1)
sh_pfc_warn("group %s referenced by %u functions\n",
group->name, refcnts[i]);
}
kfree(refcnts);
/* Check config register descriptions */ for (i = 0; info->cfg_regs && info->cfg_regs[i].reg; i++)
sh_pfc_check_cfg_reg(drvname, &info->cfg_regs[i]);
/* Check drive strength registers */ for (i = 0; drive_regs && drive_regs[i].reg; i++)
sh_pfc_check_drive_reg(info, &drive_regs[i]);
for (i = 0; drive_regs && drive_reg(i); i++) { if (!drive_field(i).pin && !drive_field(i).offset &&
!drive_field(i).size) continue;
ret = sh_pfc_map_resources(pfc, pdev); if (unlikely(ret < 0)) return ret;
spin_lock_init(&pfc->lock);
if (info->ops && info->ops->init) {
ret = info->ops->init(pfc); if (ret < 0) return ret;
/* .init() may have overridden pfc->info */
info = pfc->info;
}
ret = sh_pfc_suspend_init(pfc); if (ret) return ret;
/* Enable dummy states for those platforms without pinctrl support */ if (!of_have_populated_dt())
pinctrl_provide_dummies();
ret = sh_pfc_init_ranges(pfc); if (ret < 0) return ret;
/* * Initialize pinctrl bindings first
*/
ret = sh_pfc_register_pinctrl(pfc); if (unlikely(ret != 0)) return ret;
#ifdef CONFIG_PINCTRL_SH_PFC_GPIO /* * Then the GPIO chip
*/
ret = sh_pfc_register_gpiochip(pfc); if (unlikely(ret != 0)) { /* * If the GPIO chip fails to come up we still leave the * PFC state as it is, given that there are already * extant users of it that have succeeded by this point.
*/
dev_notice(pfc->dev, "failed to init GPIO chip, ignoring...\n");
} #endif
platform_set_drvdata(pdev, pfc);
dev_info(pfc->dev, "%s support registered\n", info->name);
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.