Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | // 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 <kernel@pengutronix.de> * Copyright (C) 2019 Texas Instrument, Jean-jacques Hiblot <jjhiblot@ti.com> */ #include <dm.h> #include <mux-internal.h> #include <regmap.h> #include <syscon.h> #include <dm/device.h> #include <dm/device-internal.h> #include <dm/device_compat.h> #include <dm/read.h> #include <dm/devres.h> #include <dt-bindings/mux/mux.h> #include <linux/bitops.h> 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" }, { .compatible = "reg-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; if (ofnode_device_is_compatible(dev_ofnode(dev), "mmio-mux")) regmap = syscon_node_to_regmap(dev_ofnode(dev->parent)); else regmap_init_mem(dev_ofnode(dev), ®map); 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, }; |