uboot/drivers/gpio/gpio-uniphier.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016-2017 Socionext Inc.
   4 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <linux/bitops.h>
  10#include <linux/io.h>
  11#include <linux/sizes.h>
  12#include <linux/errno.h>
  13#include <asm/global_data.h>
  14#include <asm/gpio.h>
  15#include <dt-bindings/gpio/uniphier-gpio.h>
  16
  17#define UNIPHIER_GPIO_PORT_DATA         0x0     /* data */
  18#define UNIPHIER_GPIO_PORT_DIR          0x4     /* direction (1:in, 0:out) */
  19#define UNIPHIER_GPIO_IRQ_EN            0x90    /* irq enable */
  20
  21struct uniphier_gpio_priv {
  22        void __iomem *regs;
  23};
  24
  25static unsigned int uniphier_gpio_bank_to_reg(unsigned int bank)
  26{
  27        unsigned int reg;
  28
  29        reg = (bank + 1) * 8;
  30
  31        /*
  32         * Unfortunately, the GPIO port registers are not contiguous because
  33         * offset 0x90-0x9f is used for IRQ.  Add 0x10 when crossing the region.
  34         */
  35        if (reg >= UNIPHIER_GPIO_IRQ_EN)
  36                reg += 0x10;
  37
  38        return reg;
  39}
  40
  41static void uniphier_gpio_get_bank_and_mask(unsigned int offset,
  42                                            unsigned int *bank, u32 *mask)
  43{
  44        *bank = offset / UNIPHIER_GPIO_LINES_PER_BANK;
  45        *mask = BIT(offset % UNIPHIER_GPIO_LINES_PER_BANK);
  46}
  47
  48static void uniphier_gpio_reg_update(struct uniphier_gpio_priv *priv,
  49                                     unsigned int reg, u32 mask, u32 val)
  50{
  51        u32 tmp;
  52
  53        tmp = readl(priv->regs + reg);
  54        tmp &= ~mask;
  55        tmp |= mask & val;
  56        writel(tmp, priv->regs + reg);
  57}
  58
  59static void uniphier_gpio_bank_write(struct udevice *dev, unsigned int bank,
  60                                     unsigned int reg, u32 mask, u32 val)
  61{
  62        struct uniphier_gpio_priv *priv = dev_get_priv(dev);
  63
  64        if (!mask)
  65                return;
  66
  67        uniphier_gpio_reg_update(priv, uniphier_gpio_bank_to_reg(bank) + reg,
  68                                 mask, val);
  69}
  70
  71static void uniphier_gpio_offset_write(struct udevice *dev, unsigned int offset,
  72                                       unsigned int reg, int val)
  73{
  74        unsigned int bank;
  75        u32 mask;
  76
  77        uniphier_gpio_get_bank_and_mask(offset, &bank, &mask);
  78
  79        uniphier_gpio_bank_write(dev, bank, reg, mask, val ? mask : 0);
  80}
  81
  82static int uniphier_gpio_offset_read(struct udevice *dev,
  83                                     unsigned int offset, unsigned int reg)
  84{
  85        struct uniphier_gpio_priv *priv = dev_get_priv(dev);
  86        unsigned int bank, reg_offset;
  87        u32 mask;
  88
  89        uniphier_gpio_get_bank_and_mask(offset, &bank, &mask);
  90        reg_offset = uniphier_gpio_bank_to_reg(bank) + reg;
  91
  92        return !!(readl(priv->regs + reg_offset) & mask);
  93}
  94
  95static int uniphier_gpio_get_function(struct udevice *dev, unsigned int offset)
  96{
  97        return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_PORT_DIR) ?
  98                                                GPIOF_INPUT : GPIOF_OUTPUT;
  99}
 100
 101static int uniphier_gpio_direction_input(struct udevice *dev,
 102                                         unsigned int offset)
 103{
 104        uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DIR, 1);
 105
 106        return 0;
 107}
 108
 109static int uniphier_gpio_direction_output(struct udevice *dev,
 110                                          unsigned int offset, int value)
 111{
 112        uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DATA, value);
 113        uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DIR, 0);
 114
 115        return 0;
 116}
 117
 118static int uniphier_gpio_get_value(struct udevice *dev, unsigned int offset)
 119{
 120        return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_PORT_DATA);
 121}
 122
 123static int uniphier_gpio_set_value(struct udevice *dev,
 124                                   unsigned int offset, int value)
 125{
 126        uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DATA, value);
 127
 128        return 0;
 129}
 130
 131static const struct dm_gpio_ops uniphier_gpio_ops = {
 132        .direction_input        = uniphier_gpio_direction_input,
 133        .direction_output       = uniphier_gpio_direction_output,
 134        .get_value              = uniphier_gpio_get_value,
 135        .set_value              = uniphier_gpio_set_value,
 136        .get_function           = uniphier_gpio_get_function,
 137};
 138
 139static int uniphier_gpio_probe(struct udevice *dev)
 140{
 141        struct uniphier_gpio_priv *priv = dev_get_priv(dev);
 142        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 143        fdt_addr_t addr;
 144
 145        addr = dev_read_addr(dev);
 146        if (addr == FDT_ADDR_T_NONE)
 147                return -EINVAL;
 148
 149        priv->regs = devm_ioremap(dev, addr, SZ_512);
 150        if (!priv->regs)
 151                return -ENOMEM;
 152
 153        uc_priv->gpio_count = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
 154                                              "ngpios", 0);
 155
 156        return 0;
 157}
 158
 159static const struct udevice_id uniphier_gpio_match[] = {
 160        { .compatible = "socionext,uniphier-gpio" },
 161        { /* sentinel */ }
 162};
 163
 164U_BOOT_DRIVER(uniphier_gpio) = {
 165        .name   = "uniphier-gpio",
 166        .id     = UCLASS_GPIO,
 167        .of_match = uniphier_gpio_match,
 168        .probe  = uniphier_gpio_probe,
 169        .priv_auto      = sizeof(struct uniphier_gpio_priv),
 170        .ops    = &uniphier_gpio_ops,
 171};
 172