mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-22 01:48:52 +09:00
![Walter Lozano](/assets/img/avatar_default.png)
Currently when using OF_PLATDATA the binding between devices and drivers is done trying to match the compatible string in the node with a driver name. However, usually a single driver supports multiple compatible strings which causes that only devices which its compatible string matches a driver name get bound. To overcome this issue, this patch adds the U_BOOT_DRIVER_ALIAS macro, which generates no code at all, but allows an easy way to declare driver name aliases. Thanks to this, dtoc could be improve to look for the driver name based on its alias when it populates the U_BOOT_DEVICE entry. Signed-off-by: Walter Lozano <walter.lozano@collabora.com> Reviewed-by: Simon Glass <sjg@chromium.org>
317 lines
7.8 KiB
C
317 lines
7.8 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Freescale i.MX28 GPIO control code
|
|
*
|
|
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
|
|
* on behalf of DENX Software Engineering GmbH
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/errno.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/iomux.h>
|
|
#include <asm/arch/imx-regs.h>
|
|
|
|
#if defined(CONFIG_MX23)
|
|
#define PINCTRL_BANKS 3
|
|
#define PINCTRL_DOUT(n) (0x0500 + ((n) * 0x10))
|
|
#define PINCTRL_DIN(n) (0x0600 + ((n) * 0x10))
|
|
#define PINCTRL_DOE(n) (0x0700 + ((n) * 0x10))
|
|
#define PINCTRL_PIN2IRQ(n) (0x0800 + ((n) * 0x10))
|
|
#define PINCTRL_IRQEN(n) (0x0900 + ((n) * 0x10))
|
|
#define PINCTRL_IRQSTAT(n) (0x0c00 + ((n) * 0x10))
|
|
#elif defined(CONFIG_MX28)
|
|
#define PINCTRL_BANKS 5
|
|
#define PINCTRL_DOUT(n) (0x0700 + ((n) * 0x10))
|
|
#define PINCTRL_DIN(n) (0x0900 + ((n) * 0x10))
|
|
#define PINCTRL_DOE(n) (0x0b00 + ((n) * 0x10))
|
|
#define PINCTRL_PIN2IRQ(n) (0x1000 + ((n) * 0x10))
|
|
#define PINCTRL_IRQEN(n) (0x1100 + ((n) * 0x10))
|
|
#define PINCTRL_IRQSTAT(n) (0x1400 + ((n) * 0x10))
|
|
#else
|
|
#error "Please select CONFIG_MX23 or CONFIG_MX28"
|
|
#endif
|
|
|
|
#define GPIO_INT_FALL_EDGE 0x0
|
|
#define GPIO_INT_LOW_LEV 0x1
|
|
#define GPIO_INT_RISE_EDGE 0x2
|
|
#define GPIO_INT_HIGH_LEV 0x3
|
|
#define GPIO_INT_LEV_MASK (1 << 0)
|
|
#define GPIO_INT_POL_MASK (1 << 1)
|
|
|
|
void mxs_gpio_init(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < PINCTRL_BANKS; i++) {
|
|
writel(0, MXS_PINCTRL_BASE + PINCTRL_PIN2IRQ(i));
|
|
writel(0, MXS_PINCTRL_BASE + PINCTRL_IRQEN(i));
|
|
/* Use SCT address here to clear the IRQSTAT bits */
|
|
writel(0xffffffff, MXS_PINCTRL_BASE + PINCTRL_IRQSTAT(i) + 8);
|
|
}
|
|
}
|
|
|
|
#if !CONFIG_IS_ENABLED(DM_GPIO)
|
|
int gpio_get_value(unsigned gpio)
|
|
{
|
|
uint32_t bank = PAD_BANK(gpio);
|
|
uint32_t offset = PINCTRL_DIN(bank);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
|
|
|
|
return (readl(®->reg) >> PAD_PIN(gpio)) & 1;
|
|
}
|
|
|
|
void gpio_set_value(unsigned gpio, int value)
|
|
{
|
|
uint32_t bank = PAD_BANK(gpio);
|
|
uint32_t offset = PINCTRL_DOUT(bank);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
|
|
|
|
if (value)
|
|
writel(1 << PAD_PIN(gpio), ®->reg_set);
|
|
else
|
|
writel(1 << PAD_PIN(gpio), ®->reg_clr);
|
|
}
|
|
|
|
int gpio_direction_input(unsigned gpio)
|
|
{
|
|
uint32_t bank = PAD_BANK(gpio);
|
|
uint32_t offset = PINCTRL_DOE(bank);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
|
|
|
|
writel(1 << PAD_PIN(gpio), ®->reg_clr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gpio_direction_output(unsigned gpio, int value)
|
|
{
|
|
uint32_t bank = PAD_BANK(gpio);
|
|
uint32_t offset = PINCTRL_DOE(bank);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
|
|
|
|
gpio_set_value(gpio, value);
|
|
|
|
writel(1 << PAD_PIN(gpio), ®->reg_set);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gpio_request(unsigned gpio, const char *label)
|
|
{
|
|
if (PAD_BANK(gpio) >= PINCTRL_BANKS)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gpio_free(unsigned gpio)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int name_to_gpio(const char *name)
|
|
{
|
|
unsigned bank, pin;
|
|
char *end;
|
|
|
|
bank = simple_strtoul(name, &end, 10);
|
|
|
|
if (!*end || *end != ':')
|
|
return bank;
|
|
|
|
pin = simple_strtoul(end + 1, NULL, 10);
|
|
|
|
return (bank << MXS_PAD_BANK_SHIFT) | (pin << MXS_PAD_PIN_SHIFT);
|
|
}
|
|
#else /* DM_GPIO */
|
|
#include <dm.h>
|
|
#include <asm/gpio.h>
|
|
#include <dt-structs.h>
|
|
#include <asm/arch/gpio.h>
|
|
#define MXS_MAX_GPIO_PER_BANK 32
|
|
|
|
#ifdef CONFIG_MX28
|
|
#define dtd_fsl_imx_gpio dtd_fsl_imx28_gpio
|
|
#else /* CONFIG_MX23 */
|
|
#define dtd_fsl_imx_gpio dtd_fsl_imx23_gpio
|
|
#endif
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
/*
|
|
* According to i.MX28 Reference Manual:
|
|
* 'i.MX28 Applications Processor Reference Manual, Rev. 1, 2010'
|
|
* The i.MX28 has following number of GPIOs available:
|
|
* Bank 0: 0-28 -> 29 PINS
|
|
* Bank 1: 0-31 -> 32 PINS
|
|
* Bank 2: 0-27 -> 28 PINS
|
|
* Bank 3: 0-30 -> 31 PINS
|
|
* Bank 4: 0-20 -> 21 PINS
|
|
*/
|
|
|
|
struct mxs_gpio_platdata {
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
struct dtd_fsl_imx_gpio dtplat;
|
|
#endif
|
|
unsigned int bank;
|
|
int gpio_ranges;
|
|
};
|
|
|
|
struct mxs_gpio_priv {
|
|
unsigned int bank;
|
|
};
|
|
|
|
static int mxs_gpio_get_value(struct udevice *dev, unsigned offset)
|
|
{
|
|
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
|
PINCTRL_DIN(priv->bank));
|
|
|
|
return (readl(®->reg) >> offset) & 1;
|
|
}
|
|
|
|
static int mxs_gpio_set_value(struct udevice *dev, unsigned offset,
|
|
int value)
|
|
{
|
|
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
|
PINCTRL_DOUT(priv->bank));
|
|
if (value)
|
|
writel(BIT(offset), ®->reg_set);
|
|
else
|
|
writel(BIT(offset), ®->reg_clr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxs_gpio_direction_input(struct udevice *dev, unsigned offset)
|
|
{
|
|
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
|
PINCTRL_DOE(priv->bank));
|
|
|
|
writel(BIT(offset), ®->reg_clr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxs_gpio_direction_output(struct udevice *dev, unsigned offset,
|
|
int value)
|
|
{
|
|
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
|
PINCTRL_DOE(priv->bank));
|
|
|
|
mxs_gpio_set_value(dev, offset, value);
|
|
|
|
writel(BIT(offset), ®->reg_set);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxs_gpio_get_function(struct udevice *dev, unsigned offset)
|
|
{
|
|
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
|
struct mxs_register_32 *reg =
|
|
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
|
PINCTRL_DOE(priv->bank));
|
|
bool is_output = !!(readl(®->reg) >> offset);
|
|
|
|
return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
|
|
}
|
|
|
|
static const struct dm_gpio_ops gpio_mxs_ops = {
|
|
.direction_input = mxs_gpio_direction_input,
|
|
.direction_output = mxs_gpio_direction_output,
|
|
.get_value = mxs_gpio_get_value,
|
|
.set_value = mxs_gpio_set_value,
|
|
.get_function = mxs_gpio_get_function,
|
|
};
|
|
|
|
static int mxs_gpio_probe(struct udevice *dev)
|
|
{
|
|
struct mxs_gpio_platdata *plat = dev_get_platdata(dev);
|
|
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
|
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
char name[16], *str;
|
|
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
struct dtd_fsl_imx_gpio *dtplat = &plat->dtplat;
|
|
priv->bank = (unsigned int)dtplat->reg[0];
|
|
uc_priv->gpio_count = dtplat->gpio_ranges[3];
|
|
#else
|
|
priv->bank = (unsigned int)plat->bank;
|
|
uc_priv->gpio_count = plat->gpio_ranges;
|
|
#endif
|
|
snprintf(name, sizeof(name), "GPIO%d_", priv->bank);
|
|
str = strdup(name);
|
|
if (!str)
|
|
return -ENOMEM;
|
|
|
|
uc_priv->bank_name = str;
|
|
|
|
debug("%s: %s: %d pins base: 0x%x\n", __func__, uc_priv->bank_name,
|
|
uc_priv->gpio_count, priv->bank);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
static int mxs_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct mxs_gpio_platdata *plat = dev->platdata;
|
|
struct fdtdec_phandle_args args;
|
|
int node = dev_of_offset(dev);
|
|
int ret;
|
|
|
|
plat->bank = devfdt_get_addr(dev);
|
|
if (plat->bank == FDT_ADDR_T_NONE) {
|
|
printf("%s: No 'reg' property defined!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges",
|
|
NULL, 3, 0, &args);
|
|
if (ret)
|
|
printf("%s: 'gpio-ranges' not defined - using default!\n",
|
|
__func__);
|
|
|
|
plat->gpio_ranges = ret == 0 ? args.args[2] : MXS_MAX_GPIO_PER_BANK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id mxs_gpio_ids[] = {
|
|
{ .compatible = "fsl,imx23-gpio" },
|
|
{ .compatible = "fsl,imx28-gpio" },
|
|
{ }
|
|
};
|
|
#endif
|
|
|
|
U_BOOT_DRIVER(fsl_imx23_gpio) = {
|
|
.name = "fsl_imx23_gpio",
|
|
.id = UCLASS_GPIO,
|
|
.ops = &gpio_mxs_ops,
|
|
.probe = mxs_gpio_probe,
|
|
.priv_auto_alloc_size = sizeof(struct mxs_gpio_priv),
|
|
.platdata_auto_alloc_size = sizeof(struct mxs_gpio_platdata),
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
.of_match = mxs_gpio_ids,
|
|
.ofdata_to_platdata = mxs_ofdata_to_platdata,
|
|
#endif
|
|
};
|
|
|
|
U_BOOT_DRIVER_ALIAS(fsl_imx23_gpio, fsl_imx28_gpio)
|
|
#endif /* DM_GPIO */
|