X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fgpio%2Fmxc_gpio.c;h=5c8ee0f99b118a95f28b43bb01d2a61cb797e570;hb=80f0fd8a4e8990ad05261e9d36b6e7e1a4d97245;hp=e9bf27841cdbabdf7719050b5929bf7f35e027f2;hpb=5dafa4543c399d329c7b01df1afa98437861cac0;p=karo-tx-uboot.git diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c index e9bf27841c..5c8ee0f99b 100644 --- a/drivers/gpio/mxc_gpio.c +++ b/drivers/gpio/mxc_gpio.c @@ -5,51 +5,55 @@ * Copyright (C) 2011 * Stefano Babic, DENX Software Engineering, * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include +#include +#include +#include #include #include #include -#include enum mxc_gpio_direction { MXC_GPIO_DIRECTION_IN, MXC_GPIO_DIRECTION_OUT, }; -#define GPIO_TO_PORT(n) (n / 32) +#define GPIO_PER_BANK 32 + +struct mxc_gpio_plat { + int bank_index; + struct gpio_regs *regs; +}; + +struct mxc_bank_info { + struct gpio_regs *regs; +}; + +#ifndef CONFIG_DM_GPIO +#define GPIO_TO_PORT(n) ((n) / 32) /* GPIO port description */ static unsigned long gpio_ports[] = { [0] = GPIO1_BASE_ADDR, [1] = GPIO2_BASE_ADDR, [2] = GPIO3_BASE_ADDR, -#if defined(CONFIG_MX25) || defined(CONFIG_MX51) || defined(CONFIG_MX53) || \ - defined(CONFIG_MX6Q) +#if defined(CONFIG_SOC_MX25) || defined(CONFIG_SOC_MX27) || defined(CONFIG_SOC_MX51) || \ + defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) [3] = GPIO4_BASE_ADDR, #endif -#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q) +#if defined(CONFIG_SOC_MX27) || defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) [4] = GPIO5_BASE_ADDR, +#ifndef CONFIG_SOX_MX6UL [5] = GPIO6_BASE_ADDR, +#endif +#endif +#if defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) +#if !(defined(CONFIG_SOC_MX6UL) || defined(CONFIG_SOC_MX6ULL)) [6] = GPIO7_BASE_ADDR, #endif +#endif }; static int mxc_gpio_direction(unsigned int gpio, @@ -59,8 +63,10 @@ static int mxc_gpio_direction(unsigned int gpio, struct gpio_regs *regs; u32 l; - if (port >= ARRAY_SIZE(gpio_ports)) + if (port >= ARRAY_SIZE(gpio_ports)) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); return -1; + } gpio &= 0x1f; @@ -86,8 +92,10 @@ int gpio_set_value(unsigned gpio, int value) struct gpio_regs *regs; u32 l; - if (port >= ARRAY_SIZE(gpio_ports)) + if (port >= ARRAY_SIZE(gpio_ports)) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); return -1; + } gpio &= 0x1f; @@ -109,28 +117,42 @@ int gpio_get_value(unsigned gpio) struct gpio_regs *regs; u32 val; - if (port >= ARRAY_SIZE(gpio_ports)) + if (port >= ARRAY_SIZE(gpio_ports)) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); return -1; + } gpio &= 0x1f; regs = (struct gpio_regs *)gpio_ports[port]; - val = (readl(®s->gpio_psr) >> gpio) & 0x01; - + if (readl(®s->gpio_dir) & (1 << gpio)) { + printf("WARNING: Reading status of output GPIO_%d_%d\n", + port - GPIO_TO_PORT(0), gpio); + val = (readl(®s->gpio_dr) >> gpio) & 0x01; + } else { + val = (readl(®s->gpio_psr) >> gpio) & 0x01; + } return val; } int gpio_request(unsigned gpio, const char *label) { unsigned int port = GPIO_TO_PORT(gpio); - if (port >= ARRAY_SIZE(gpio_ports)) + if (port >= ARRAY_SIZE(gpio_ports)) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); return -1; + } return 0; } int gpio_free(unsigned gpio) { + unsigned int port = GPIO_TO_PORT(gpio); + if (port >= ARRAY_SIZE(gpio_ports)) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } return 0; } @@ -141,10 +163,230 @@ int gpio_direction_input(unsigned gpio) int gpio_direction_output(unsigned gpio, int value) { - int ret = mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT); + int ret = gpio_set_value(gpio, value); if (ret < 0) return ret; - return gpio_set_value(gpio, value); + return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT); +} +#endif + +#ifdef CONFIG_DM_GPIO +#include +DECLARE_GLOBAL_DATA_PTR; + +static int mxc_gpio_is_output(struct gpio_regs *regs, int offset) +{ + u32 val; + + val = readl(®s->gpio_dir); + + return val & (1 << offset) ? 1 : 0; +} + +static void mxc_gpio_bank_direction(struct gpio_regs *regs, int offset, + enum mxc_gpio_direction direction) +{ + u32 l; + + l = readl(®s->gpio_dir); + + switch (direction) { + case MXC_GPIO_DIRECTION_OUT: + l |= 1 << offset; + break; + case MXC_GPIO_DIRECTION_IN: + l &= ~(1 << offset); + } + writel(l, ®s->gpio_dir); +} + +static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset, + int value) +{ + u32 l; + + l = readl(®s->gpio_dr); + if (value) + l |= 1 << offset; + else + l &= ~(1 << offset); + writel(l, ®s->gpio_dr); +} + +static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset) +{ + return (readl(®s->gpio_psr) >> offset) & 0x01; +} + +/* set GPIO pin 'gpio' as an input */ +static int mxc_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct mxc_bank_info *bank = dev_get_priv(dev); + + /* Configure GPIO direction as input. */ + mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_IN); + + return 0; +} + +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +static int mxc_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct mxc_bank_info *bank = dev_get_priv(dev); + + /* Configure GPIO output value. */ + mxc_gpio_bank_set_value(bank->regs, offset, value); + + /* Configure GPIO direction as output. */ + mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_OUT); + + return 0; +} + +/* read GPIO IN value of pin 'gpio' */ +static int mxc_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct mxc_bank_info *bank = dev_get_priv(dev); + + return mxc_gpio_bank_get_value(bank->regs, offset); } + +/* write GPIO OUT value to pin 'gpio' */ +static int mxc_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct mxc_bank_info *bank = dev_get_priv(dev); + + mxc_gpio_bank_set_value(bank->regs, offset, value); + + return 0; +} + +static int mxc_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct mxc_bank_info *bank = dev_get_priv(dev); + + /* GPIOF_FUNC is not implemented yet */ + if (mxc_gpio_is_output(bank->regs, offset)) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static const struct dm_gpio_ops gpio_mxc_ops = { + .direction_input = mxc_gpio_direction_input, + .direction_output = mxc_gpio_direction_output, + .get_value = mxc_gpio_get_value, + .set_value = mxc_gpio_set_value, + .get_function = mxc_gpio_get_function, +}; + +static int mxc_gpio_probe(struct udevice *dev) +{ + struct mxc_bank_info *bank = dev_get_priv(dev); + struct mxc_gpio_plat *plat = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + int banknum; + char name[18], *str; + + banknum = plat->bank_index; + sprintf(name, "GPIO%d_", banknum + 1); + str = strdup(name); + if (!str) + return -ENOMEM; + uc_priv->bank_name = str; + uc_priv->gpio_count = GPIO_PER_BANK; + bank->regs = plat->regs; + + return 0; +} + +static int mxc_gpio_bind(struct udevice *dev) +{ + struct mxc_gpio_plat *plat = dev->platdata; + fdt_addr_t addr; + + /* + * If platdata already exsits, directly return. + * Actually only when DT is not supported, platdata + * is statically initialized in U_BOOT_DEVICES.Here + * will return. + */ + if (plat) + return 0; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -ENODEV; + + /* + * TODO: + * When every board is converted to driver model and DT is supported, + * this can be done by auto-alloc feature, but not using calloc + * to alloc memory for platdata. + */ + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + + plat->regs = (struct gpio_regs *)addr; + plat->bank_index = dev->req_seq; + dev->platdata = plat; + + return 0; +} + +static const struct udevice_id mxc_gpio_ids[] = { + { .compatible = "fsl,imx35-gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_mxc) = { + .name = "gpio_mxc", + .id = UCLASS_GPIO, + .ops = &gpio_mxc_ops, + .probe = mxc_gpio_probe, + .priv_auto_alloc_size = sizeof(struct mxc_bank_info), + .of_match = mxc_gpio_ids, + .bind = mxc_gpio_bind, +}; + +#if !CONFIG_IS_ENABLED(OF_CONTROL) +static const struct mxc_gpio_plat mxc_plat[] = { + { 0, (struct gpio_regs *)GPIO1_BASE_ADDR }, + { 1, (struct gpio_regs *)GPIO2_BASE_ADDR }, + { 2, (struct gpio_regs *)GPIO3_BASE_ADDR }, +#if defined(CONFIG_SOC_MX25) || defined(CONFIG_SOC_MX27) || defined(CONFIG_SOC_MX51) || \ + defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) + { 3, (struct gpio_regs *)GPIO4_BASE_ADDR }, +#endif +#if defined(CONFIG_SOC_MX27) || defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) + { 4, (struct gpio_regs *)GPIO5_BASE_ADDR }, + { 5, (struct gpio_regs *)GPIO6_BASE_ADDR }, +#endif +#if defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) + { 6, (struct gpio_regs *)GPIO7_BASE_ADDR }, +#endif +}; + +U_BOOT_DEVICES(mxc_gpios) = { + { "gpio_mxc", &mxc_plat[0] }, + { "gpio_mxc", &mxc_plat[1] }, + { "gpio_mxc", &mxc_plat[2] }, +#if defined(CONFIG_SOC_MX25) || defined(CONFIG_SOC_MX27) || defined(CONFIG_SOC_MX51) || \ + defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) + { "gpio_mxc", &mxc_plat[3] }, +#endif +#if defined(CONFIG_SOC_MX27) || defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) + { "gpio_mxc", &mxc_plat[4] }, + { "gpio_mxc", &mxc_plat[5] }, +#endif +#if defined(CONFIG_SOC_MX53) || defined(CONFIG_ARCH_MX6) + { "gpio_mxc", &mxc_plat[6] }, +#endif +}; +#endif +#endif