mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-04 02:20:25 +00:00
41575d8e4c
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
195 lines
4.2 KiB
C
195 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2019 DENX Software Engineering
|
|
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <log.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(fsl_imx23_pinctrl) = {
|
|
.name = "fsl_imx23_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 = sizeof(struct mxs_pinctrl_priv),
|
|
.ops = &mxs_pinctrl_ops,
|
|
};
|
|
|
|
U_BOOT_DRIVER_ALIAS(fsl_imx23_pinctrl, fsl_imx28_pinctrl)
|