diff options
Diffstat (limited to 'drivers/mux/mmio.c')
| -rw-r--r-- | drivers/mux/mmio.c | 97 |
1 files changed, 86 insertions, 11 deletions
diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c index 30a952c34365..e4ddb1e61923 100644 --- a/drivers/mux/mmio.c +++ b/drivers/mux/mmio.c @@ -15,11 +15,25 @@ #include <linux/property.h> #include <linux/regmap.h> +struct mux_mmio { + struct regmap_field **fields; + unsigned int *hardware_states; +}; + +static int mux_mmio_get(struct mux_control *mux, int *state) +{ + struct mux_mmio *mux_mmio = mux_chip_priv(mux->chip); + unsigned int index = mux_control_get_index(mux); + + return regmap_field_read(mux_mmio->fields[index], state); +} + static int mux_mmio_set(struct mux_control *mux, int state) { - struct regmap_field **fields = mux_chip_priv(mux->chip); + struct mux_mmio *mux_mmio = mux_chip_priv(mux->chip); + unsigned int index = mux_control_get_index(mux); - return regmap_field_write(fields[mux_control_get_index(mux)], state); + return regmap_field_write(mux_mmio->fields[index], state); } static const struct mux_control_ops mux_mmio_ops = { @@ -33,13 +47,20 @@ static const struct of_device_id mux_mmio_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mux_mmio_dt_ids); +static const struct regmap_config mux_mmio_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + static int mux_mmio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct regmap_field **fields; struct mux_chip *mux_chip; + struct mux_mmio *mux_mmio; struct regmap *regmap; + void __iomem *base; int num_fields; int ret; int i; @@ -47,7 +68,11 @@ static int mux_mmio_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "mmio-mux")) { regmap = syscon_node_to_regmap(np->parent); } else { - regmap = device_node_to_regmap(np); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + regmap = ERR_PTR(-ENODEV); + else + regmap = regmap_init_mmio(dev, base, &mux_mmio_regmap_cfg); /* Fallback to checking the parent node on "real" errors. */ if (IS_ERR(regmap) && regmap != ERR_PTR(-EPROBE_DEFER)) { regmap = dev_get_regmap(dev->parent, NULL); @@ -69,12 +94,20 @@ static int mux_mmio_probe(struct platform_device *pdev) } num_fields = ret / 2; - mux_chip = devm_mux_chip_alloc(dev, num_fields, num_fields * - sizeof(*fields)); + mux_chip = devm_mux_chip_alloc(dev, num_fields, sizeof(struct mux_mmio)); if (IS_ERR(mux_chip)) return PTR_ERR(mux_chip); - fields = mux_chip_priv(mux_chip); + mux_mmio = mux_chip_priv(mux_chip); + + mux_mmio->fields = devm_kmalloc(dev, num_fields * sizeof(*mux_mmio->fields), GFP_KERNEL); + if (IS_ERR(mux_mmio->fields)) + return PTR_ERR(mux_mmio->fields); + + mux_mmio->hardware_states = devm_kmalloc(dev, num_fields * + sizeof(*mux_mmio->hardware_states), GFP_KERNEL); + if (IS_ERR(mux_mmio->hardware_states)) + return PTR_ERR(mux_mmio->hardware_states); for (i = 0; i < num_fields; i++) { struct mux_control *mux = &mux_chip->mux[i]; @@ -104,10 +137,10 @@ static int mux_mmio_probe(struct platform_device *pdev) return -EINVAL; } - fields[i] = devm_regmap_field_alloc(dev, regmap, field); - if (IS_ERR(fields[i])) { - ret = PTR_ERR(fields[i]); - dev_err(dev, "bitfield %d: failed allocate: %d\n", + mux_mmio->fields[i] = devm_regmap_field_alloc(dev, regmap, field); + if (IS_ERR(mux_mmio->fields[i])) { + ret = PTR_ERR(mux_mmio->fields[i]); + dev_err(dev, "bitfield %d: failed to allocate: %d\n", i, ret); return ret; } @@ -130,13 +163,55 @@ static int mux_mmio_probe(struct platform_device *pdev) mux_chip->ops = &mux_mmio_ops; + dev_set_drvdata(dev, mux_chip); + return devm_mux_chip_register(dev, mux_chip); } +static int mux_mmio_suspend_noirq(struct device *dev) +{ + struct mux_chip *mux_chip = dev_get_drvdata(dev); + struct mux_mmio *mux_mmio = mux_chip_priv(mux_chip); + unsigned int state; + int ret, i; + + for (i = 0; i < mux_chip->controllers; i++) { + ret = mux_mmio_get(&mux_chip->mux[i], &state); + if (ret) { + dev_err(dev, "control %u: error saving mux: %d\n", i, ret); + return ret; + } + + mux_mmio->hardware_states[i] = state; + } + + return 0; +} + +static int mux_mmio_resume_noirq(struct device *dev) +{ + struct mux_chip *mux_chip = dev_get_drvdata(dev); + struct mux_mmio *mux_mmio = mux_chip_priv(mux_chip); + int ret, i; + + for (i = 0; i < mux_chip->controllers; i++) { + ret = mux_mmio_set(&mux_chip->mux[i], mux_mmio->hardware_states[i]); + if (ret) { + dev_err(dev, "control %u: error restoring mux: %d\n", i, ret); + return ret; + } + } + + return 0; +} + +static DEFINE_NOIRQ_DEV_PM_OPS(mux_mmio_pm_ops, mux_mmio_suspend_noirq, mux_mmio_resume_noirq); + static struct platform_driver mux_mmio_driver = { .driver = { .name = "mmio-mux", .of_match_table = mux_mmio_dt_ids, + .pm = pm_sleep_ptr(&mux_mmio_pm_ops), }, .probe = mux_mmio_probe, }; |
