// SPDX-License-Identifier: GPL-2.0 /* * MMIO register bitfield-controlled multiplexer driver * Based on the linux mmio multiplexer driver * * Copyright (C) 2017 Pengutronix, Philipp Zabel * Copyright (C) 2019 Texas Instrument, Jean-jacques Hiblot */ #include #include #include #include #include #include #include #include #include #include #include #include static int mux_mmio_set(struct mux_control *mux, int state) { struct regmap_field **fields = dev_get_priv(mux->dev); return regmap_field_write(fields[mux_control_get_index(mux)], state); } static const struct mux_control_ops mux_mmio_ops = { .set = mux_mmio_set, }; static const struct udevice_id mmio_mux_of_match[] = { { .compatible = "mmio-mux" }, { /* sentinel */ }, }; static int mmio_mux_probe(struct udevice *dev) { struct regmap_field **fields; struct mux_chip *mux_chip = dev_get_uclass_priv(dev); struct regmap *regmap; u32 *mux_reg_masks; u32 *idle_states; int num_fields; int ret; int i; regmap = syscon_node_to_regmap(dev_ofnode(dev->parent)); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); dev_err(dev, "failed to get regmap: %d\n", ret); return ret; } num_fields = dev_read_size(dev, "mux-reg-masks"); if (num_fields < 0) return log_msg_ret("mux-reg-masks missing", -EINVAL); num_fields /= sizeof(u32); if (num_fields == 0 || num_fields % 2) ret = -EINVAL; num_fields = num_fields / 2; ret = mux_alloc_controllers(dev, num_fields); if (ret < 0) return log_msg_ret("mux_alloc_controllers", ret); fields = devm_kmalloc(dev, num_fields * sizeof(*fields), __GFP_ZERO); if (!fields) return -ENOMEM; dev_set_priv(dev, fields); mux_reg_masks = devm_kmalloc(dev, num_fields * 2 * sizeof(u32), __GFP_ZERO); if (!mux_reg_masks) return -ENOMEM; ret = dev_read_u32_array(dev, "mux-reg-masks", mux_reg_masks, num_fields * 2); if (ret < 0) return log_msg_ret("mux-reg-masks read", ret); idle_states = devm_kmalloc(dev, num_fields * sizeof(u32), __GFP_ZERO); if (!idle_states) return -ENOMEM; ret = dev_read_u32_array(dev, "idle-states", idle_states, num_fields); if (ret < 0) { log_err("idle-states"); devm_kfree(dev, idle_states); idle_states = NULL; } for (i = 0; i < num_fields; i++) { struct mux_control *mux = &mux_chip->mux[i]; struct reg_field field; u32 reg, mask; int bits; reg = mux_reg_masks[2 * i]; mask = mux_reg_masks[2 * i + 1]; field.reg = reg; field.msb = fls(mask) - 1; field.lsb = ffs(mask) - 1; if (mask != GENMASK(field.msb, field.lsb)) return log_msg_ret("invalid mask", -EINVAL); fields[i] = devm_regmap_field_alloc(dev, regmap, field); if (IS_ERR(fields[i])) { ret = PTR_ERR(fields[i]); return log_msg_ret("regmap_field_alloc", ret); } bits = 1 + field.msb - field.lsb; mux->states = 1 << bits; if (!idle_states) continue; if (idle_states[i] != MUX_IDLE_AS_IS && idle_states[i] >= mux->states) return log_msg_ret("idle-states range", -EINVAL); mux->idle_state = idle_states[i]; } devm_kfree(dev, mux_reg_masks); if (idle_states) devm_kfree(dev, idle_states); return 0; } U_BOOT_DRIVER(mmio_mux) = { .name = "mmio-mux", .id = UCLASS_MUX, .of_match = mmio_mux_of_match, .probe = mmio_mux_probe, .ops = &mux_mmio_ops, };