uboot/drivers/gpio/pca953x_gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Take linux kernel driver drivers/gpio/gpio-pca953x.c for reference.
   4 *
   5 * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
   6 *
   7 */
   8
   9/*
  10 * Note:
  11 * The driver's compatible table is borrowed from Linux Kernel,
  12 * but now max supported gpio pins is 24 and only PCA953X_TYPE
  13 * is supported. PCA957X_TYPE is not supported now.
  14 * Also the Polarity Inversion feature is not supported now.
  15 *
  16 * TODO:
  17 * 1. Support PCA957X_TYPE
  18 * 2. Support Polarity Inversion
  19 */
  20
  21#include <common.h>
  22#include <errno.h>
  23#include <dm.h>
  24#include <fdtdec.h>
  25#include <i2c.h>
  26#include <malloc.h>
  27#include <asm/gpio.h>
  28#include <asm/io.h>
  29#include <dm/device_compat.h>
  30#include <dt-bindings/gpio/gpio.h>
  31#include <linux/bitops.h>
  32
  33#define PCA953X_INPUT           0
  34#define PCA953X_OUTPUT          1
  35#define PCA953X_INVERT          2
  36#define PCA953X_DIRECTION       3
  37
  38#define PCA_GPIO_MASK           0x00FF
  39#define PCA_INT                 0x0100
  40#define PCA953X_TYPE            0x1000
  41#define PCA957X_TYPE            0x2000
  42#define PCA_TYPE_MASK           0xF000
  43#define PCA_CHIP_TYPE(x)        ((x) & PCA_TYPE_MASK)
  44
  45enum {
  46        PCA953X_DIRECTION_IN,
  47        PCA953X_DIRECTION_OUT,
  48};
  49
  50#define MAX_BANK 5
  51#define BANK_SZ 8
  52
  53/*
  54 * struct pca953x_info - Data for pca953x
  55 *
  56 * @dev: udevice structure for the device
  57 * @addr: i2c slave address
  58 * @invert: Polarity inversion or not
  59 * @gpio_count: the number of gpio pins that the device supports
  60 * @chip_type: indicate the chip type,PCA953X or PCA957X
  61 * @bank_count: the number of banks that the device supports
  62 * @reg_output: array to hold the value of output registers
  63 * @reg_direction: array to hold the value of direction registers
  64 */
  65struct pca953x_info {
  66        struct udevice *dev;
  67        int addr;
  68        int invert;
  69        int gpio_count;
  70        int chip_type;
  71        int bank_count;
  72        u8 reg_output[MAX_BANK];
  73        u8 reg_direction[MAX_BANK];
  74};
  75
  76static int pca953x_write_single(struct udevice *dev, int reg, u8 val,
  77                                int offset)
  78{
  79        struct pca953x_info *info = dev_get_platdata(dev);
  80        int bank_shift = fls((info->gpio_count - 1) / BANK_SZ);
  81        int off = offset / BANK_SZ;
  82        int ret = 0;
  83
  84        ret = dm_i2c_write(dev, (reg << bank_shift) + off, &val, 1);
  85        if (ret) {
  86                dev_err(dev, "%s error\n", __func__);
  87                return ret;
  88        }
  89
  90        return 0;
  91}
  92
  93static int pca953x_read_single(struct udevice *dev, int reg, u8 *val,
  94                               int offset)
  95{
  96        struct pca953x_info *info = dev_get_platdata(dev);
  97        int bank_shift = fls((info->gpio_count - 1) / BANK_SZ);
  98        int off = offset / BANK_SZ;
  99        int ret;
 100        u8 byte;
 101
 102        ret = dm_i2c_read(dev, (reg << bank_shift) + off, &byte, 1);
 103        if (ret) {
 104                dev_err(dev, "%s error\n", __func__);
 105                return ret;
 106        }
 107
 108        *val = byte;
 109
 110        return 0;
 111}
 112
 113static int pca953x_read_regs(struct udevice *dev, int reg, u8 *val)
 114{
 115        struct pca953x_info *info = dev_get_platdata(dev);
 116        int ret = 0;
 117
 118        if (info->gpio_count <= 8) {
 119                ret = dm_i2c_read(dev, reg, val, 1);
 120        } else if (info->gpio_count <= 16) {
 121                ret = dm_i2c_read(dev, reg << 1, val, info->bank_count);
 122        } else if (info->gpio_count <= 24) {
 123                /* Auto increment */
 124                ret = dm_i2c_read(dev, (reg << 2) | 0x80, val,
 125                                  info->bank_count);
 126        } else if (info->gpio_count == 40) {
 127                /* Auto increment */
 128                ret = dm_i2c_read(dev, (reg << 3) | 0x80, val,
 129                                  info->bank_count);
 130        } else {
 131                dev_err(dev, "Unsupported now\n");
 132                return -EINVAL;
 133        }
 134
 135        return ret;
 136}
 137
 138static int pca953x_write_regs(struct udevice *dev, int reg, u8 *val)
 139{
 140        struct pca953x_info *info = dev_get_platdata(dev);
 141        int ret = 0;
 142
 143        if (info->gpio_count <= 8) {
 144                ret = dm_i2c_write(dev, reg, val, 1);
 145        } else if (info->gpio_count <= 16) {
 146                ret = dm_i2c_write(dev, reg << 1, val, info->bank_count);
 147        } else if (info->gpio_count <= 24) {
 148                /* Auto increment */
 149                ret = dm_i2c_write(dev, (reg << 2) | 0x80, val,
 150                                   info->bank_count);
 151        } else if (info->gpio_count == 40) {
 152                /* Auto increment */
 153                ret = dm_i2c_write(dev, (reg << 3) | 0x80, val, info->bank_count);
 154        } else {
 155                return -EINVAL;
 156        }
 157
 158        return ret;
 159}
 160
 161static int pca953x_is_output(struct udevice *dev, int offset)
 162{
 163        struct pca953x_info *info = dev_get_platdata(dev);
 164
 165        int bank = offset / BANK_SZ;
 166        int off = offset % BANK_SZ;
 167
 168        /*0: output; 1: input */
 169        return !(info->reg_direction[bank] & (1 << off));
 170}
 171
 172static int pca953x_get_value(struct udevice *dev, uint offset)
 173{
 174        int ret;
 175        u8 val = 0;
 176
 177        int off = offset % BANK_SZ;
 178
 179        ret = pca953x_read_single(dev, PCA953X_INPUT, &val, offset);
 180        if (ret)
 181                return ret;
 182
 183        return (val >> off) & 0x1;
 184}
 185
 186static int pca953x_set_value(struct udevice *dev, uint offset, int value)
 187{
 188        struct pca953x_info *info = dev_get_platdata(dev);
 189        int bank = offset / BANK_SZ;
 190        int off = offset % BANK_SZ;
 191        u8 val;
 192        int ret;
 193
 194        if (value)
 195                val = info->reg_output[bank] | (1 << off);
 196        else
 197                val = info->reg_output[bank] & ~(1 << off);
 198
 199        ret = pca953x_write_single(dev, PCA953X_OUTPUT, val, offset);
 200        if (ret)
 201                return ret;
 202
 203        info->reg_output[bank] = val;
 204
 205        return 0;
 206}
 207
 208static int pca953x_set_direction(struct udevice *dev, uint offset, int dir)
 209{
 210        struct pca953x_info *info = dev_get_platdata(dev);
 211        int bank = offset / BANK_SZ;
 212        int off = offset % BANK_SZ;
 213        u8 val;
 214        int ret;
 215
 216        if (dir == PCA953X_DIRECTION_IN)
 217                val = info->reg_direction[bank] | (1 << off);
 218        else
 219                val = info->reg_direction[bank] & ~(1 << off);
 220
 221        ret = pca953x_write_single(dev, PCA953X_DIRECTION, val, offset);
 222        if (ret)
 223                return ret;
 224
 225        info->reg_direction[bank] = val;
 226
 227        return 0;
 228}
 229
 230static int pca953x_direction_input(struct udevice *dev, uint offset)
 231{
 232        return pca953x_set_direction(dev, offset, PCA953X_DIRECTION_IN);
 233}
 234
 235static int pca953x_direction_output(struct udevice *dev, uint offset, int value)
 236{
 237        /* Configure output value. */
 238        pca953x_set_value(dev, offset, value);
 239
 240        /* Configure direction as output. */
 241        pca953x_set_direction(dev, offset, PCA953X_DIRECTION_OUT);
 242
 243        return 0;
 244}
 245
 246static int pca953x_get_function(struct udevice *dev, uint offset)
 247{
 248        if (pca953x_is_output(dev, offset))
 249                return GPIOF_OUTPUT;
 250        else
 251                return GPIOF_INPUT;
 252}
 253
 254static int pca953x_xlate(struct udevice *dev, struct gpio_desc *desc,
 255                         struct ofnode_phandle_args *args)
 256{
 257        desc->offset = args->args[0];
 258        desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
 259
 260        return 0;
 261}
 262
 263static const struct dm_gpio_ops pca953x_ops = {
 264        .direction_input        = pca953x_direction_input,
 265        .direction_output       = pca953x_direction_output,
 266        .get_value              = pca953x_get_value,
 267        .set_value              = pca953x_set_value,
 268        .get_function           = pca953x_get_function,
 269        .xlate                  = pca953x_xlate,
 270};
 271
 272static int pca953x_probe(struct udevice *dev)
 273{
 274        struct pca953x_info *info = dev_get_platdata(dev);
 275        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 276        char name[32], label[8], *str;
 277        int addr;
 278        ulong driver_data;
 279        int ret;
 280        int size;
 281        const u8 *tmp;
 282        u8 val[MAX_BANK];
 283
 284        addr = dev_read_addr(dev);
 285        if (addr == 0)
 286                return -ENODEV;
 287
 288        info->addr = addr;
 289
 290        driver_data = dev_get_driver_data(dev);
 291
 292        info->gpio_count = driver_data & PCA_GPIO_MASK;
 293        if (info->gpio_count > MAX_BANK * BANK_SZ) {
 294                dev_err(dev, "Max support %d pins now\n", MAX_BANK * BANK_SZ);
 295                return -EINVAL;
 296        }
 297
 298        info->chip_type = PCA_CHIP_TYPE(driver_data);
 299        if (info->chip_type != PCA953X_TYPE) {
 300                dev_err(dev, "Only support PCA953X chip type now.\n");
 301                return -EINVAL;
 302        }
 303
 304        info->bank_count = DIV_ROUND_UP(info->gpio_count, BANK_SZ);
 305
 306        ret = pca953x_read_regs(dev, PCA953X_OUTPUT, info->reg_output);
 307        if (ret) {
 308                dev_err(dev, "Error reading output register\n");
 309                return ret;
 310        }
 311
 312        ret = pca953x_read_regs(dev, PCA953X_DIRECTION, info->reg_direction);
 313        if (ret) {
 314                dev_err(dev, "Error reading direction register\n");
 315                return ret;
 316        }
 317
 318        tmp = dev_read_prop(dev, "label", &size);
 319
 320        if (tmp) {
 321                memcpy(label, tmp, sizeof(label) - 1);
 322                label[sizeof(label) - 1] = '\0';
 323                snprintf(name, sizeof(name), "%s@%x_", label, info->addr);
 324        } else {
 325                snprintf(name, sizeof(name), "gpio@%x_", info->addr);
 326        }
 327
 328        /* Clear the polarity registers to no invert */
 329        memset(val, 0, MAX_BANK);
 330        ret = pca953x_write_regs(dev, PCA953X_INVERT, val);
 331        if (ret < 0) {
 332                dev_err(dev, "Error writing invert register\n");
 333                return ret;
 334        }
 335
 336        str = strdup(name);
 337        if (!str)
 338                return -ENOMEM;
 339        uc_priv->bank_name = str;
 340        uc_priv->gpio_count = info->gpio_count;
 341
 342        dev_dbg(dev, "%s is ready\n", str);
 343
 344        return 0;
 345}
 346
 347#define OF_953X(__nrgpio, __int) (ulong)(__nrgpio | PCA953X_TYPE | __int)
 348#define OF_957X(__nrgpio, __int) (ulong)(__nrgpio | PCA957X_TYPE | __int)
 349
 350static const struct udevice_id pca953x_ids[] = {
 351        { .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), },
 352        { .compatible = "nxp,pca9534", .data = OF_953X(8, PCA_INT), },
 353        { .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), },
 354        { .compatible = "nxp,pca9536", .data = OF_953X(4, 0), },
 355        { .compatible = "nxp,pca9537", .data = OF_953X(4, PCA_INT), },
 356        { .compatible = "nxp,pca9538", .data = OF_953X(8, PCA_INT), },
 357        { .compatible = "nxp,pca9539", .data = OF_953X(16, PCA_INT), },
 358        { .compatible = "nxp,pca9554", .data = OF_953X(8, PCA_INT), },
 359        { .compatible = "nxp,pca9555", .data = OF_953X(16, PCA_INT), },
 360        { .compatible = "nxp,pca9556", .data = OF_953X(8, 0), },
 361        { .compatible = "nxp,pca9557", .data = OF_953X(8, 0), },
 362        { .compatible = "nxp,pca9574", .data = OF_957X(8, PCA_INT), },
 363        { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
 364        { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
 365
 366        { .compatible = "maxim,max7310", .data = OF_953X(8, 0), },
 367        { .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), },
 368        { .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), },
 369        { .compatible = "maxim,max7315", .data = OF_953X(8, PCA_INT), },
 370
 371        { .compatible = "ti,pca6107", .data = OF_953X(8, PCA_INT), },
 372        { .compatible = "ti,tca6408", .data = OF_953X(8, PCA_INT), },
 373        { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
 374        { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
 375        { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), },
 376
 377        { .compatible = "onsemi,pca9654", .data = OF_953X(8, PCA_INT), },
 378
 379        { .compatible = "exar,xra1202", .data = OF_953X(8, 0), },
 380        { }
 381};
 382
 383U_BOOT_DRIVER(pca953x) = {
 384        .name           = "pca953x",
 385        .id             = UCLASS_GPIO,
 386        .ops            = &pca953x_ops,
 387        .probe          = pca953x_probe,
 388        .platdata_auto_alloc_size = sizeof(struct pca953x_info),
 389        .of_match       = pca953x_ids,
 390};
 391