// SPDX-License-Identifier: GPL-2.0-only /* * pinctrl-single - Generic device tree based pinctrl driver for one * register per pin type pinmux controllers * * Copyright (c) 2013 Sascha Hauer */ #include #include #include #include #include struct pinctrl_single { void __iomem *base; struct pinctrl_device pinctrl; unsigned (*read)(void __iomem *reg); void (*write)(unsigned val, void __iomem *reg); unsigned int width; unsigned int fmask; unsigned int fshift; unsigned int fmax; bool bits_per_mux; unsigned int bits_per_pin; unsigned int args_count; }; static unsigned __maybe_unused pcs_readb(void __iomem *reg) { return readb(reg); } static unsigned __maybe_unused pcs_readw(void __iomem *reg) { return readw(reg); } static unsigned __maybe_unused pcs_readl(void __iomem *reg) { return readl(reg); } static void __maybe_unused pcs_writeb(unsigned val, void __iomem *reg) { writeb(val, reg); } static void __maybe_unused pcs_writew(unsigned val, void __iomem *reg) { writew(val, reg); } static void __maybe_unused pcs_writel(unsigned val, void __iomem *reg) { writel(val, reg); } static int pcs_set_state(struct pinctrl_device *pdev, struct device_node *np) { struct pinctrl_single *pcs = container_of(pdev, struct pinctrl_single, pinctrl); unsigned size = 0, index = 0; unsigned int offset, val, rows, mask, reg, i; const __be32 *mux; dev_dbg(pcs->pinctrl.dev, "set state: %pOF\n", np); if (pcs->bits_per_mux) { mux = of_get_property(np, "pinctrl-single,bits", &size); if (size % 3 != 0) dev_err(pcs->pinctrl.dev, "invalid args_count for spec: %u\n", size); size /= sizeof(*mux); /* Number of elements in array */ rows = size / 3; for (i = 0; i < rows; i++) { offset = be32_to_cpup(mux + index++); val = be32_to_cpup(mux + index++); mask = be32_to_cpup(mux + index++); reg = pcs->read(pcs->base + offset); reg &= ~mask; reg |= val; pcs->write(reg, pcs->base + offset); } } else { mux = of_get_property(np, "pinctrl-single,pins", &size); size /= sizeof(*mux); /* Number of elements in array */ if (!mux || !size || (size % (pcs->args_count + 1))) { dev_err(pcs->pinctrl.dev, "bad data for mux %pOF\n", np); return -EINVAL; } while (index < size) { unsigned int offset, val; offset = be32_to_cpup(mux + index++); val = be32_to_cpup(mux + index++); if (pcs->args_count > 1) { /* If a 2nd data cell is present, it's ORed into * the 1st. Additional cells are undefined, * just skip them. */ val |= be32_to_cpup(mux + index); index += pcs->args_count - 1; } pcs->write(val, pcs->base + offset); } } return 0; } static struct pinctrl_ops pcs_ops = { .set_state = pcs_set_state, }; int pinctrl_single_probe(struct device *dev) { struct resource *iores; struct pinctrl_single *pcs; struct device_node *np = dev->of_node; int ret = 0; pcs = xzalloc(sizeof(*pcs)); iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); pcs->base = IOMEM(iores->start); pcs->pinctrl.dev = dev; pcs->pinctrl.ops = &pcs_ops; ret = of_property_read_u32(np, "pinctrl-single,register-width", &pcs->width); if (ret) { dev_dbg(dev, "no pinctrl-single,register-width property\n"); goto out; } switch (pcs->width) { case 8: pcs->read = pcs_readb; pcs->write = pcs_writeb; break; case 16: pcs->read = pcs_readw; pcs->write = pcs_writew; break; case 32: pcs->read = pcs_readl; pcs->write = pcs_writel; break; default: ret = -EINVAL; dev_dbg(dev, "invalid register width: %d\n", pcs->width); goto out; } ret = of_property_read_u32(np, "pinctrl-single,function-mask", &pcs->fmask); if (!ret) { pcs->fshift = __ffs(pcs->fmask); pcs->fmax = pcs->fmask >> pcs->fshift; } else { /* If mask property doesn't exist, function mux is invalid. */ pcs->fmask = 0; pcs->fshift = 0; pcs->fmax = 0; } pcs->bits_per_mux = of_property_read_bool(np, "pinctrl-single,bit-per-mux"); if (pcs->bits_per_mux) pcs->bits_per_pin = fls(pcs->fmask); /* If no pinctrl-cells is present, default to old style of 2 cells with * bits per mux and 1 cell otherwise. */ ret = of_property_read_u32(np, "#pinctrl-cells", &pcs->args_count); if (ret) pcs->args_count = pcs->bits_per_mux ? 2 : 1; ret = pinctrl_register(&pcs->pinctrl); if (ret) goto out; return 0; out: free(pcs); return ret; } static __maybe_unused struct of_device_id pcs_dt_ids[] = { { .compatible = "pinctrl-single", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pcs_dt_ids); static struct driver pcs_driver = { .name = "pinctrl-single", .probe = pinctrl_single_probe, .of_compatible = DRV_OF_COMPAT(pcs_dt_ids), }; core_platform_driver(pcs_driver);