| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (C) 2019 DENX Software Engineering |
| * Lukasz Majewski, DENX Software Engineering, lukma@denx.de |
| */ |
| |
| #include <common.h> |
| #include <dm/device_compat.h> |
| #include <dm/devres.h> |
| #include <linux/io.h> |
| #include <linux/err.h> |
| #include <dm.h> |
| #include <dm/pinctrl.h> |
| #include <dm/read.h> |
| #include "pinctrl-mxs.h" |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| struct mxs_pinctrl_priv { |
| void __iomem *base; |
| const struct mxs_regs *regs; |
| }; |
| |
| static unsigned long mxs_dt_node_to_map(struct udevice *conf) |
| { |
| unsigned long config = 0; |
| int ret; |
| u32 val; |
| |
| ret = dev_read_u32(conf, "fsl,drive-strength", &val); |
| if (!ret) |
| config = val | MA_PRESENT; |
| |
| ret = dev_read_u32(conf, "fsl,voltage", &val); |
| if (!ret) |
| config |= val << VOL_SHIFT | VOL_PRESENT; |
| |
| ret = dev_read_u32(conf, "fsl,pull-up", &val); |
| if (!ret) |
| config |= val << PULL_SHIFT | PULL_PRESENT; |
| |
| return config; |
| } |
| |
| static int mxs_pinctrl_set_mux(struct udevice *dev, u32 val, int bank, int pin) |
| { |
| struct mxs_pinctrl_priv *iomux = dev_get_priv(dev); |
| int muxsel = MUXID_TO_MUXSEL(val), shift; |
| void __iomem *reg; |
| |
| reg = iomux->base + iomux->regs->muxsel; |
| reg += bank * 0x20 + pin / 16 * 0x10; |
| shift = pin % 16 * 2; |
| |
| mxs_pinctrl_rmwl(muxsel, 0x3, shift, reg); |
| debug(" mux %d,", muxsel); |
| |
| return 0; |
| } |
| |
| static int mxs_pinctrl_set_state(struct udevice *dev, struct udevice *conf) |
| { |
| struct mxs_pinctrl_priv *iomux = dev_get_priv(dev); |
| u32 *pin_data, val, ma, vol, pull; |
| int npins, size, i, ret; |
| unsigned long config; |
| |
| debug("\n%s: set state: %s\n", __func__, conf->name); |
| |
| size = dev_read_size(conf, "fsl,pinmux-ids"); |
| if (size < 0) |
| return size; |
| |
| if (!size || size % sizeof(int)) { |
| dev_err(dev, "Invalid fsl,pinmux-ids property in %s\n", |
| conf->name); |
| return -EINVAL; |
| } |
| |
| npins = size / sizeof(int); |
| |
| pin_data = devm_kzalloc(dev, size, 0); |
| if (!pin_data) |
| return -ENOMEM; |
| |
| ret = dev_read_u32_array(conf, "fsl,pinmux-ids", pin_data, npins); |
| if (ret) { |
| dev_err(dev, "Error reading pin data.\n"); |
| devm_kfree(dev, pin_data); |
| return -EINVAL; |
| } |
| |
| config = mxs_dt_node_to_map(conf); |
| |
| ma = CONFIG_TO_MA(config); |
| vol = CONFIG_TO_VOL(config); |
| pull = CONFIG_TO_PULL(config); |
| |
| for (i = 0; i < npins; i++) { |
| int pinid, bank, pin, shift; |
| void __iomem *reg; |
| |
| val = pin_data[i]; |
| |
| pinid = MUXID_TO_PINID(val); |
| bank = PINID_TO_BANK(pinid); |
| pin = PINID_TO_PIN(pinid); |
| |
| debug("(val: 0x%x) pin %d,", val, pinid); |
| /* Setup pinmux */ |
| mxs_pinctrl_set_mux(dev, val, bank, pin); |
| |
| debug(" ma: %d, vol: %d, pull: %d\n", ma, vol, pull); |
| |
| /* drive */ |
| reg = iomux->base + iomux->regs->drive; |
| reg += bank * 0x40 + pin / 8 * 0x10; |
| |
| /* mA */ |
| if (config & MA_PRESENT) { |
| shift = pin % 8 * 4; |
| mxs_pinctrl_rmwl(ma, 0x3, shift, reg); |
| } |
| |
| /* vol */ |
| if (config & VOL_PRESENT) { |
| shift = pin % 8 * 4 + 2; |
| if (vol) |
| writel(1 << shift, reg + SET); |
| else |
| writel(1 << shift, reg + CLR); |
| } |
| |
| /* pull */ |
| if (config & PULL_PRESENT) { |
| reg = iomux->base + iomux->regs->pull; |
| reg += bank * 0x10; |
| shift = pin; |
| if (pull) |
| writel(1 << shift, reg + SET); |
| else |
| writel(1 << shift, reg + CLR); |
| } |
| } |
| |
| devm_kfree(dev, pin_data); |
| return 0; |
| } |
| |
| static struct pinctrl_ops mxs_pinctrl_ops = { |
| .set_state = mxs_pinctrl_set_state, |
| }; |
| |
| static int mxs_pinctrl_probe(struct udevice *dev) |
| { |
| struct mxs_pinctrl_priv *iomux = dev_get_priv(dev); |
| |
| iomux->base = dev_read_addr_ptr(dev); |
| iomux->regs = (struct mxs_regs *)dev_get_driver_data(dev); |
| |
| return 0; |
| } |
| |
| static const struct mxs_regs imx23_regs = { |
| .muxsel = 0x100, |
| .drive = 0x200, |
| .pull = 0x400, |
| }; |
| |
| static const struct mxs_regs imx28_regs = { |
| .muxsel = 0x100, |
| .drive = 0x300, |
| .pull = 0x600, |
| }; |
| |
| static const struct udevice_id mxs_pinctrl_match[] = { |
| { .compatible = "fsl,imx23-pinctrl", .data = (ulong)&imx23_regs }, |
| { .compatible = "fsl,imx28-pinctrl", .data = (ulong)&imx28_regs }, |
| { /* sentinel */ } |
| }; |
| |
| U_BOOT_DRIVER(mxs_pinctrl) = { |
| .name = "mxs-pinctrl", |
| .id = UCLASS_PINCTRL, |
| .of_match = of_match_ptr(mxs_pinctrl_match), |
| .probe = mxs_pinctrl_probe, |
| #if !CONFIG_IS_ENABLED(OF_PLATDATA) |
| .bind = dm_scan_fdt_dev, |
| #endif |
| .priv_auto_alloc_size = sizeof(struct mxs_pinctrl_priv), |
| .ops = &mxs_pinctrl_ops, |
| }; |