From 0f666533106adee28ef1848e845466a6907b3745 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Wed, 19 Jun 2019 17:31:06 +0200 Subject: [PATCH] pinctrl: mxs: Add support for i.MX2[38] mxs pinctrl driver The code responsible for setting proper values in the MUX registers (in the mxs_pinctrl_set_state()) has been ported from Linux kernel - SHA1: 17bb763e7eaf tag v5.1.11 from linux-stable. As the pinctrl node in the imx28.dtsi file has gpio pins nodes as subnodes, it was necessary to use 'dm_scan_fdt_dev()' (as a .bind method) to also make them 'visible' by the DM's "gpio_mxs" driver. Signed-off-by: Lukasz Majewski Reviewed-by: Marek Vasut --- drivers/pinctrl/nxp/Kconfig | 10 ++ drivers/pinctrl/nxp/Makefile | 1 + drivers/pinctrl/nxp/pinctrl-mxs.c | 190 ++++++++++++++++++++++++++++++ drivers/pinctrl/nxp/pinctrl-mxs.h | 61 ++++++++++ 4 files changed, 262 insertions(+) create mode 100644 drivers/pinctrl/nxp/pinctrl-mxs.c create mode 100644 drivers/pinctrl/nxp/pinctrl-mxs.h diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index 61f93be42d..f2e67ca231 100644 --- a/drivers/pinctrl/nxp/Kconfig +++ b/drivers/pinctrl/nxp/Kconfig @@ -89,6 +89,16 @@ config PINCTRL_IMX8M only parses the 'fsl,pins' property and configure related registers. +config PINCTRL_MXS + bool "NXP MXS pinctrl driver" + depends on ARCH_MX28 && PINCTRL_FULL + help + Say Y here to enable the i.MX mxs pinctrl driver + + This option provides a simple pinctrl driver for i.MX mxs SoC + familiy, e.g. i.MX28. This feature depends on device tree + configuration. + config PINCTRL_VYBRID bool "Vybrid (vf610) pinctrl driver" depends on ARCH_VF610 && PINCTRL_FULL diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile index b340d9448a..b86448aac9 100644 --- a/drivers/pinctrl/nxp/Makefile +++ b/drivers/pinctrl/nxp/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o obj-$(CONFIG_PINCTRL_IMX_SCU) += pinctrl-scu.o obj-$(CONFIG_PINCTRL_IMX8) += pinctrl-imx8.o obj-$(CONFIG_PINCTRL_IMX8M) += pinctrl-imx8m.o +obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o obj-$(CONFIG_PINCTRL_VYBRID) += pinctrl-vf610.o diff --git a/drivers/pinctrl/nxp/pinctrl-mxs.c b/drivers/pinctrl/nxp/pinctrl-mxs.c new file mode 100644 index 0000000000..6f6ca84674 --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-mxs.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 DENX Software Engineering + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#include +#include +#include +#include +#include +#include +#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, +}; diff --git a/drivers/pinctrl/nxp/pinctrl-mxs.h b/drivers/pinctrl/nxp/pinctrl-mxs.h new file mode 100644 index 0000000000..a398e43cbe --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-mxs.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + */ + +#ifndef __PINCTRL_MXS_H +#define __PINCTRL_MXS_H + +#include + +#define SET 0x4 +#define CLR 0x8 +#define TOG 0xc + +#define MXS_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin) +#define PINID(bank, pin) ((bank) * 32 + (pin)) + +/* + * pinmux-id bit field definitions + * + * bank: 15..12 (4) + * pin: 11..4 (8) + * muxsel: 3..0 (4) + */ +#define MUXID_TO_PINID(m) PINID((m) >> 12 & 0xf, (m) >> 4 & 0xff) +#define MUXID_TO_MUXSEL(m) ((m) & 0xf) + +#define PINID_TO_BANK(p) ((p) >> 5) +#define PINID_TO_PIN(p) ((p) % 32) + +/* + * pin config bit field definitions + * + * pull-up: 6..5 (2) + * voltage: 4..3 (2) + * mA: 2..0 (3) + * + * MSB of each field is presence bit for the config. + */ +#define PULL_PRESENT (1 << 6) +#define PULL_SHIFT 5 +#define VOL_PRESENT (1 << 4) +#define VOL_SHIFT 3 +#define MA_PRESENT (1 << 2) +#define MA_SHIFT 0 +#define CONFIG_TO_PULL(c) ((c) >> PULL_SHIFT & 0x1) +#define CONFIG_TO_VOL(c) ((c) >> VOL_SHIFT & 0x1) +#define CONFIG_TO_MA(c) ((c) >> MA_SHIFT & 0x3) + +struct mxs_regs { + u16 muxsel; + u16 drive; + u16 pull; +}; + +static inline void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift, + void __iomem *reg) +{ + clrsetbits_le32(reg, mask << shift, value << shift); +} +#endif /* __PINCTRL_MXS_H */ -- 2.25.1