Message ID | 20200611194506.7263-4-p.yadav@ti.com |
---|---|
State | New |
Headers | show |
Series | drivers: Add a framework for MUX drivers | expand |
Hi Pratyush, On Thu, 11 Jun 2020 at 13:45, Pratyush Yadav <p.yadav at ti.com> wrote: > > From: Jean-Jacques Hiblot <jjhiblot at ti.com> > > This adds a driver for mmio-based syscon multiplexers controlled by > bitfields in a syscon register range. > This is heavily based on the linux mmio-mux driver. > > Signed-off-by: Jean-Jacques Hiblot <jjhiblot at ti.com> > Reviewed-by: Simon Glass <sjg at chromium.org> > Signed-off-by: Pratyush Yadav <p.yadav at ti.com> > --- > drivers/mux/Kconfig | 14 +++++ > drivers/mux/Makefile | 1 + > drivers/mux/mmio.c | 143 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 158 insertions(+) > create mode 100644 drivers/mux/mmio.c Reviewed-by: Simon Glass <sjg at chromium.org> nit below > > diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig > index 35c1c5673c..f15ee4f833 100644 > --- a/drivers/mux/Kconfig > +++ b/drivers/mux/Kconfig > @@ -8,4 +8,18 @@ config MULTIPLEXER > controllers. It provides the same API as Linux and mux drivers should > be portable with a minimum effort. > > +if MULTIPLEXER > + > +config MUX_MMIO > + bool "MMIO register bitfield-controlled Multiplexer" > + depends on MULTIPLEXER && SYSCON > + help > + MMIO register bitfield-controlled Multiplexer controller. > + > + The driver builds multiplexer controllers for bitfields in a syscon > + register. For N bit wide bitfields, there will be 2^N possible > + multiplexer states. > + > +endif > + > endmenu > diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile > index 351e4363d3..78ebf04c7a 100644 > --- a/drivers/mux/Makefile > +++ b/drivers/mux/Makefile > @@ -4,3 +4,4 @@ > # Jean-Jacques Hiblot <jjhiblot at ti.com> > > obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux-uclass.o > +obj-$(CONFIG_$(SPL_)MUX_MMIO) += mmio.o > diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c > new file mode 100644 > index 0000000000..573e599dd1 > --- /dev/null > +++ b/drivers/mux/mmio.c > @@ -0,0 +1,143 @@ > +// 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 at pengutronix.de> > + * Copyright (C) 2019 Texas Instrument, Jean-jacques Hiblot <jjhiblot at ti.com> > + */ > +#include <common.h> > +#include <dm.h> > +#include <mux-internal.h> > +#include <regmap.h> > +#include <syscon.h> > +#include <dm/device.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" }, > + { /* 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 or invalid", -EINVAL); It occurred to me at some point that all we really need here is a short string, like "mux-reg-masks" since we only need to find the unique string within the function (which logging automatically records). If we use very long strings then enabling the message logging will cause a very large increase in code size. Regards, Simon
diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 35c1c5673c..f15ee4f833 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -8,4 +8,18 @@ config MULTIPLEXER controllers. It provides the same API as Linux and mux drivers should be portable with a minimum effort. +if MULTIPLEXER + +config MUX_MMIO + bool "MMIO register bitfield-controlled Multiplexer" + depends on MULTIPLEXER && SYSCON + help + MMIO register bitfield-controlled Multiplexer controller. + + The driver builds multiplexer controllers for bitfields in a syscon + register. For N bit wide bitfields, there will be 2^N possible + multiplexer states. + +endif + endmenu diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index 351e4363d3..78ebf04c7a 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -4,3 +4,4 @@ # Jean-Jacques Hiblot <jjhiblot at ti.com> obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux-uclass.o +obj-$(CONFIG_$(SPL_)MUX_MMIO) += mmio.o diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c new file mode 100644 index 0000000000..573e599dd1 --- /dev/null +++ b/drivers/mux/mmio.c @@ -0,0 +1,143 @@ +// 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 at pengutronix.de> + * Copyright (C) 2019 Texas Instrument, Jean-jacques Hiblot <jjhiblot at ti.com> + */ +#include <common.h> +#include <dm.h> +#include <mux-internal.h> +#include <regmap.h> +#include <syscon.h> +#include <dm/device.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" }, + { /* 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 or invalid", -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("failed to allocate mux controllers", ret); + + fields = devm_kmalloc(dev, num_fields * sizeof(*fields), __GFP_ZERO); + if (!fields) + return -ENOMEM; + dev->priv = 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("failed to read mux-reg-masks property", + 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("failed to read idle-states property: %d\n", ret); + 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("Failed to allocate regmap field", + 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("out of range idle state", -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, +};