18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MMIO register bitfield-controlled multiplexer driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitops.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/mux/driver.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/property.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int mux_mmio_set(struct mux_control *mux, int state) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct regmap_field **fields = mux_chip_priv(mux->chip); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci return regmap_field_write(fields[mux_control_get_index(mux)], state); 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const struct mux_control_ops mux_mmio_ops = { 268c2ecf20Sopenharmony_ci .set = mux_mmio_set, 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const struct of_device_id mux_mmio_dt_ids[] = { 308c2ecf20Sopenharmony_ci { .compatible = "mmio-mux", }, 318c2ecf20Sopenharmony_ci { .compatible = "reg-mux", }, 328c2ecf20Sopenharmony_ci { /* sentinel */ } 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mux_mmio_dt_ids); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int mux_mmio_probe(struct platform_device *pdev) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 398c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 408c2ecf20Sopenharmony_ci struct regmap_field **fields; 418c2ecf20Sopenharmony_ci struct mux_chip *mux_chip; 428c2ecf20Sopenharmony_ci struct regmap *regmap; 438c2ecf20Sopenharmony_ci int num_fields; 448c2ecf20Sopenharmony_ci int ret; 458c2ecf20Sopenharmony_ci int i; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "mmio-mux")) 488c2ecf20Sopenharmony_ci regmap = syscon_node_to_regmap(np->parent); 498c2ecf20Sopenharmony_ci else 508c2ecf20Sopenharmony_ci regmap = dev_get_regmap(dev->parent, NULL) ?: ERR_PTR(-ENODEV); 518c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 528c2ecf20Sopenharmony_ci ret = PTR_ERR(regmap); 538c2ecf20Sopenharmony_ci dev_err(dev, "failed to get regmap: %d\n", ret); 548c2ecf20Sopenharmony_ci return ret; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci ret = of_property_count_u32_elems(np, "mux-reg-masks"); 588c2ecf20Sopenharmony_ci if (ret == 0 || ret % 2) 598c2ecf20Sopenharmony_ci ret = -EINVAL; 608c2ecf20Sopenharmony_ci if (ret < 0) { 618c2ecf20Sopenharmony_ci dev_err(dev, "mux-reg-masks property missing or invalid: %d\n", 628c2ecf20Sopenharmony_ci ret); 638c2ecf20Sopenharmony_ci return ret; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci num_fields = ret / 2; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci mux_chip = devm_mux_chip_alloc(dev, num_fields, num_fields * 688c2ecf20Sopenharmony_ci sizeof(*fields)); 698c2ecf20Sopenharmony_ci if (IS_ERR(mux_chip)) 708c2ecf20Sopenharmony_ci return PTR_ERR(mux_chip); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci fields = mux_chip_priv(mux_chip); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci for (i = 0; i < num_fields; i++) { 758c2ecf20Sopenharmony_ci struct mux_control *mux = &mux_chip->mux[i]; 768c2ecf20Sopenharmony_ci struct reg_field field; 778c2ecf20Sopenharmony_ci s32 idle_state = MUX_IDLE_AS_IS; 788c2ecf20Sopenharmony_ci u32 reg, mask; 798c2ecf20Sopenharmony_ci int bits; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, "mux-reg-masks", 828c2ecf20Sopenharmony_ci 2 * i, ®); 838c2ecf20Sopenharmony_ci if (!ret) 848c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, "mux-reg-masks", 858c2ecf20Sopenharmony_ci 2 * i + 1, &mask); 868c2ecf20Sopenharmony_ci if (ret < 0) { 878c2ecf20Sopenharmony_ci dev_err(dev, "bitfield %d: failed to read mux-reg-masks property: %d\n", 888c2ecf20Sopenharmony_ci i, ret); 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci field.reg = reg; 938c2ecf20Sopenharmony_ci field.msb = fls(mask) - 1; 948c2ecf20Sopenharmony_ci field.lsb = ffs(mask) - 1; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (mask != GENMASK(field.msb, field.lsb)) { 978c2ecf20Sopenharmony_ci dev_err(dev, "bitfield %d: invalid mask 0x%x\n", 988c2ecf20Sopenharmony_ci i, mask); 998c2ecf20Sopenharmony_ci return -EINVAL; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci fields[i] = devm_regmap_field_alloc(dev, regmap, field); 1038c2ecf20Sopenharmony_ci if (IS_ERR(fields[i])) { 1048c2ecf20Sopenharmony_ci ret = PTR_ERR(fields[i]); 1058c2ecf20Sopenharmony_ci dev_err(dev, "bitfield %d: failed allocate: %d\n", 1068c2ecf20Sopenharmony_ci i, ret); 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci bits = 1 + field.msb - field.lsb; 1118c2ecf20Sopenharmony_ci mux->states = 1 << bits; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci of_property_read_u32_index(np, "idle-states", i, 1148c2ecf20Sopenharmony_ci (u32 *)&idle_state); 1158c2ecf20Sopenharmony_ci if (idle_state != MUX_IDLE_AS_IS) { 1168c2ecf20Sopenharmony_ci if (idle_state < 0 || idle_state >= mux->states) { 1178c2ecf20Sopenharmony_ci dev_err(dev, "bitfield: %d: out of range idle state %d\n", 1188c2ecf20Sopenharmony_ci i, idle_state); 1198c2ecf20Sopenharmony_ci return -EINVAL; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci mux->idle_state = idle_state; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mux_chip->ops = &mux_mmio_ops; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return devm_mux_chip_register(dev, mux_chip); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct platform_driver mux_mmio_driver = { 1328c2ecf20Sopenharmony_ci .driver = { 1338c2ecf20Sopenharmony_ci .name = "mmio-mux", 1348c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mux_mmio_dt_ids), 1358c2ecf20Sopenharmony_ci }, 1368c2ecf20Sopenharmony_ci .probe = mux_mmio_probe, 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_cimodule_platform_driver(mux_mmio_driver); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MMIO register bitfield-controlled multiplexer driver"); 1418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); 1428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 143