uboot/drivers/gpio/mpc8xxx_gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2016
   4 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
   5 *
   6 * based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is
   7 *
   8 * Copyright 2010 eXMeritus, A Boeing Company
   9 * Copyright 2020-2021 NXP
  10 */
  11
  12#include <common.h>
  13#include <dm.h>
  14#include <mapmem.h>
  15#include <asm/gpio.h>
  16#include <asm/io.h>
  17#include <dm/of_access.h>
  18
  19struct mpc8xxx_gpio_data {
  20        /* The bank's register base in memory */
  21        struct ccsr_gpio __iomem *base;
  22        /* The address of the registers; used to identify the bank */
  23        phys_addr_t addr;
  24        /* The GPIO count of the bank */
  25        uint gpio_count;
  26        /* The GPDAT register cannot be used to determine the value of output
  27         * pins on MPC8572/MPC8536, so we shadow it and use the shadowed value
  28         * for output pins
  29         */
  30        u32 dat_shadow;
  31        ulong type;
  32        bool  little_endian;
  33};
  34
  35enum {
  36        MPC8XXX_GPIO_TYPE,
  37        MPC5121_GPIO_TYPE,
  38};
  39
  40inline u32 gpio_mask(uint gpio)
  41{
  42        return (1U << (31 - (gpio)));
  43}
  44
  45static inline u32 mpc8xxx_gpio_get_val(struct udevice *dev, u32 mask)
  46{
  47        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
  48
  49        if (data->little_endian)
  50                return in_le32(&data->base->gpdat) & mask;
  51        else
  52                return in_be32(&data->base->gpdat) & mask;
  53}
  54
  55static inline u32 mpc8xxx_gpio_get_dir(struct udevice *dev, u32 mask)
  56{
  57        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
  58
  59        if (data->little_endian)
  60                return in_le32(&data->base->gpdir) & mask;
  61        else
  62                return in_be32(&data->base->gpdir) & mask;
  63}
  64
  65static inline int mpc8xxx_gpio_open_drain_val(struct udevice *dev, u32 mask)
  66{
  67        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
  68
  69        if (data->little_endian)
  70                return in_le32(&data->base->gpodr) & mask;
  71        else
  72                return in_be32(&data->base->gpodr) & mask;
  73}
  74
  75static inline void mpc8xxx_gpio_open_drain_on(struct udevice *dev, u32
  76                                              gpios)
  77{
  78        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
  79        /* GPODR register 1 -> open drain on */
  80        if (data->little_endian)
  81                setbits_le32(&data->base->gpodr, gpios);
  82        else
  83                setbits_be32(&data->base->gpodr, gpios);
  84}
  85
  86static inline void mpc8xxx_gpio_open_drain_off(struct udevice *dev,
  87                                               u32 gpios)
  88{
  89        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
  90        /* GPODR register 0 -> open drain off (actively driven) */
  91        if (data->little_endian)
  92                clrbits_le32(&data->base->gpodr, gpios);
  93        else
  94                clrbits_be32(&data->base->gpodr, gpios);
  95}
  96
  97static int mpc8xxx_gpio_direction_input(struct udevice *dev, uint gpio)
  98{
  99        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
 100        u32 mask = gpio_mask(gpio);
 101
 102        /* GPDIR register 0 -> input */
 103        if (data->little_endian)
 104                clrbits_le32(&data->base->gpdir, mask);
 105        else
 106                clrbits_be32(&data->base->gpdir, mask);
 107
 108        return 0;
 109}
 110
 111static int mpc8xxx_gpio_set_value(struct udevice *dev, uint gpio, int value)
 112{
 113        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
 114        struct ccsr_gpio *base = data->base;
 115        u32 mask = gpio_mask(gpio);
 116        u32 gpdir;
 117
 118        if (value) {
 119                data->dat_shadow |= mask;
 120        } else {
 121                data->dat_shadow &= ~mask;
 122        }
 123
 124        if (data->little_endian)
 125                gpdir = in_le32(&base->gpdir);
 126        else
 127                gpdir = in_be32(&base->gpdir);
 128
 129        gpdir |= gpio_mask(gpio);
 130
 131        if (data->little_endian) {
 132                out_le32(&base->gpdat, gpdir & data->dat_shadow);
 133                out_le32(&base->gpdir, gpdir);
 134        } else {
 135                out_be32(&base->gpdat, gpdir & data->dat_shadow);
 136                out_be32(&base->gpdir, gpdir);
 137        }
 138
 139        return 0;
 140}
 141
 142static int mpc8xxx_gpio_direction_output(struct udevice *dev, uint gpio,
 143                                         int value)
 144{
 145        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
 146
 147        /* GPIO 28..31 are input only on MPC5121 */
 148        if (data->type == MPC5121_GPIO_TYPE && gpio >= 28)
 149                return -EINVAL;
 150
 151        return mpc8xxx_gpio_set_value(dev, gpio, value);
 152}
 153
 154static int mpc8xxx_gpio_get_value(struct udevice *dev, uint gpio)
 155{
 156        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
 157
 158        if (!!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio))) {
 159                /* Output -> use shadowed value */
 160                return !!(data->dat_shadow & gpio_mask(gpio));
 161        }
 162
 163        /* Input -> read value from GPDAT register */
 164        return !!mpc8xxx_gpio_get_val(dev, gpio_mask(gpio));
 165}
 166
 167static int mpc8xxx_gpio_get_function(struct udevice *dev, uint gpio)
 168{
 169        int dir;
 170
 171        dir = !!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio));
 172        return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
 173}
 174
 175#if CONFIG_IS_ENABLED(OF_CONTROL)
 176static int mpc8xxx_gpio_of_to_plat(struct udevice *dev)
 177{
 178        struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
 179        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
 180
 181        if (dev_read_bool(dev, "little-endian"))
 182                data->little_endian = true;
 183
 184        plat->addr = dev_read_addr_size_index(dev, 0, (fdt_size_t *)&plat->size);
 185        plat->ngpios = dev_read_u32_default(dev, "ngpios", 32);
 186
 187        return 0;
 188}
 189#endif
 190
 191static int mpc8xxx_gpio_plat_to_priv(struct udevice *dev)
 192{
 193        struct mpc8xxx_gpio_data *priv = dev_get_priv(dev);
 194        struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
 195        unsigned long size = plat->size;
 196        ulong driver_data = dev_get_driver_data(dev);
 197
 198        if (size == 0)
 199                size = 0x100;
 200
 201        priv->addr = plat->addr;
 202        priv->base = map_sysmem(plat->addr, size);
 203
 204        if (!priv->base)
 205                return -ENOMEM;
 206
 207        priv->gpio_count = plat->ngpios;
 208        priv->dat_shadow = 0;
 209
 210        priv->type = driver_data;
 211
 212        return 0;
 213}
 214
 215static int mpc8xxx_gpio_probe(struct udevice *dev)
 216{
 217        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 218        struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
 219        char name[32], *str;
 220
 221        mpc8xxx_gpio_plat_to_priv(dev);
 222
 223        snprintf(name, sizeof(name), "MPC@%.8llx",
 224                 (unsigned long long)data->addr);
 225        str = strdup(name);
 226
 227        if (!str)
 228                return -ENOMEM;
 229
 230        if (device_is_compatible(dev, "fsl,qoriq-gpio")) {
 231                if (data->little_endian)
 232                        out_le32(&data->base->gpibe, 0xffffffff);
 233                else
 234                        out_be32(&data->base->gpibe, 0xffffffff);
 235        }
 236
 237        uc_priv->bank_name = str;
 238        uc_priv->gpio_count = data->gpio_count;
 239
 240        return 0;
 241}
 242
 243static const struct dm_gpio_ops gpio_mpc8xxx_ops = {
 244        .direction_input        = mpc8xxx_gpio_direction_input,
 245        .direction_output       = mpc8xxx_gpio_direction_output,
 246        .get_value              = mpc8xxx_gpio_get_value,
 247        .set_value              = mpc8xxx_gpio_set_value,
 248        .get_function           = mpc8xxx_gpio_get_function,
 249};
 250
 251static const struct udevice_id mpc8xxx_gpio_ids[] = {
 252        { .compatible = "fsl,pq3-gpio", .data = MPC8XXX_GPIO_TYPE },
 253        { .compatible = "fsl,mpc8308-gpio", .data = MPC8XXX_GPIO_TYPE },
 254        { .compatible = "fsl,mpc8349-gpio", .data = MPC8XXX_GPIO_TYPE },
 255        { .compatible = "fsl,mpc8572-gpio", .data = MPC8XXX_GPIO_TYPE},
 256        { .compatible = "fsl,mpc8610-gpio", .data = MPC8XXX_GPIO_TYPE},
 257        { .compatible = "fsl,mpc5121-gpio", .data = MPC5121_GPIO_TYPE, },
 258        { .compatible = "fsl,qoriq-gpio", .data = MPC8XXX_GPIO_TYPE },
 259        { /* sentinel */ }
 260};
 261
 262U_BOOT_DRIVER(gpio_mpc8xxx) = {
 263        .name   = "gpio_mpc8xxx",
 264        .id     = UCLASS_GPIO,
 265        .ops    = &gpio_mpc8xxx_ops,
 266#if CONFIG_IS_ENABLED(OF_CONTROL)
 267        .of_to_plat = mpc8xxx_gpio_of_to_plat,
 268        .plat_auto      = sizeof(struct mpc8xxx_gpio_plat),
 269        .of_match = mpc8xxx_gpio_ids,
 270#endif
 271        .probe  = mpc8xxx_gpio_probe,
 272        .priv_auto      = sizeof(struct mpc8xxx_gpio_data),
 273};
 274