uboot/drivers/gpio/pca953x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2008 Extreme Engineering Solutions, Inc.
   4 */
   5
   6/*
   7 * Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557,
   8 * pca9539, etc)
   9 */
  10
  11#include <common.h>
  12#include <i2c.h>
  13#include <pca953x.h>
  14
  15/* Default to an address that hopefully won't corrupt other i2c devices */
  16#ifndef CONFIG_SYS_I2C_PCA953X_ADDR
  17#define CONFIG_SYS_I2C_PCA953X_ADDR     (~0)
  18#endif
  19
  20enum {
  21        PCA953X_CMD_INFO,
  22        PCA953X_CMD_DEVICE,
  23        PCA953X_CMD_OUTPUT,
  24        PCA953X_CMD_INPUT,
  25        PCA953X_CMD_INVERT,
  26};
  27
  28#ifdef CONFIG_SYS_I2C_PCA953X_WIDTH
  29struct pca953x_chip_ngpio {
  30        uint8_t chip;
  31        uint8_t ngpio;
  32};
  33
  34static struct pca953x_chip_ngpio pca953x_chip_ngpios[] =
  35    CONFIG_SYS_I2C_PCA953X_WIDTH;
  36
  37/*
  38 * Determine the number of GPIO pins supported. If we don't know we assume
  39 * 8 pins.
  40 */
  41static int pca953x_ngpio(uint8_t chip)
  42{
  43        int i;
  44
  45        for (i = 0; i < ARRAY_SIZE(pca953x_chip_ngpios); i++)
  46                if (pca953x_chip_ngpios[i].chip == chip)
  47                        return pca953x_chip_ngpios[i].ngpio;
  48
  49        return 8;
  50}
  51#else
  52static int pca953x_ngpio(uint8_t chip)
  53{
  54        return 8;
  55}
  56#endif
  57
  58/*
  59 * Modify masked bits in register
  60 */
  61static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data)
  62{
  63        uint8_t valb;
  64        uint16_t valw;
  65
  66        if (pca953x_ngpio(chip) <= 8) {
  67                if (i2c_read(chip, addr, 1, &valb, 1))
  68                        return -1;
  69
  70                valb &= ~mask;
  71                valb |= data;
  72
  73                return i2c_write(chip, addr, 1, &valb, 1);
  74        } else {
  75                if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
  76                        return -1;
  77
  78                valw = le16_to_cpu(valw);
  79                valw &= ~mask;
  80                valw |= data;
  81                valw = cpu_to_le16(valw);
  82
  83                return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2);
  84        }
  85}
  86
  87static int pca953x_reg_read(uint8_t chip, uint addr, uint *data)
  88{
  89        uint8_t valb;
  90        uint16_t valw;
  91
  92        if (pca953x_ngpio(chip) <= 8) {
  93                if (i2c_read(chip, addr, 1, &valb, 1))
  94                        return -1;
  95                *data = (int)valb;
  96        } else {
  97                if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
  98                        return -1;
  99                *data = (uint)le16_to_cpu(valw);
 100        }
 101        return 0;
 102}
 103
 104/*
 105 * Set output value of IO pins in 'mask' to corresponding value in 'data'
 106 * 0 = low, 1 = high
 107 */
 108int pca953x_set_val(uint8_t chip, uint mask, uint data)
 109{
 110        return pca953x_reg_write(chip, PCA953X_OUT, mask, data);
 111}
 112
 113/*
 114 * Set read polarity of IO pins in 'mask' to corresponding value in 'data'
 115 * 0 = read pin value, 1 = read inverted pin value
 116 */
 117int pca953x_set_pol(uint8_t chip, uint mask, uint data)
 118{
 119        return pca953x_reg_write(chip, PCA953X_POL, mask, data);
 120}
 121
 122/*
 123 * Set direction of IO pins in 'mask' to corresponding value in 'data'
 124 * 0 = output, 1 = input
 125 */
 126int pca953x_set_dir(uint8_t chip, uint mask, uint data)
 127{
 128        return pca953x_reg_write(chip, PCA953X_CONF, mask, data);
 129}
 130
 131/*
 132 * Read current logic level of all IO pins
 133 */
 134int pca953x_get_val(uint8_t chip)
 135{
 136        uint val;
 137
 138        if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0)
 139                return -1;
 140
 141        return (int)val;
 142}
 143
 144#if defined(CONFIG_CMD_PCA953X) && !defined(CONFIG_SPL_BUILD)
 145/*
 146 * Display pca953x information
 147 */
 148static int pca953x_info(uint8_t chip)
 149{
 150        int i;
 151        uint data;
 152        int nr_gpio = pca953x_ngpio(chip);
 153        int msb = nr_gpio - 1;
 154
 155        printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio);
 156        printf("gpio pins: ");
 157        for (i = msb; i >= 0; i--)
 158                printf("%x", i);
 159        printf("\n");
 160        for (i = 11 + nr_gpio; i > 0; i--)
 161                printf("-");
 162        printf("\n");
 163
 164        if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0)
 165                return -1;
 166        printf("conf:      ");
 167        for (i = msb; i >= 0; i--)
 168                printf("%c", data & (1 << i) ? 'i' : 'o');
 169        printf("\n");
 170
 171        if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0)
 172                return -1;
 173        printf("invert:    ");
 174        for (i = msb; i >= 0; i--)
 175                printf("%c", data & (1 << i) ? '1' : '0');
 176        printf("\n");
 177
 178        if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0)
 179                return -1;
 180        printf("input:     ");
 181        for (i = msb; i >= 0; i--)
 182                printf("%c", data & (1 << i) ? '1' : '0');
 183        printf("\n");
 184
 185        if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0)
 186                return -1;
 187        printf("output:    ");
 188        for (i = msb; i >= 0; i--)
 189                printf("%c", data & (1 << i) ? '1' : '0');
 190        printf("\n");
 191
 192        return 0;
 193}
 194
 195static cmd_tbl_t cmd_pca953x[] = {
 196        U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""),
 197        U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""),
 198        U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""),
 199        U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""),
 200        U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""),
 201};
 202
 203static int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 204{
 205        static uint8_t chip = CONFIG_SYS_I2C_PCA953X_ADDR;
 206        int ret = CMD_RET_USAGE, val;
 207        ulong ul_arg2 = 0;
 208        ulong ul_arg3 = 0;
 209        cmd_tbl_t *c;
 210
 211        c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x));
 212
 213        /* All commands but "device" require 'maxargs' arguments */
 214        if (!c || !((argc == (c->maxargs)) ||
 215                (((long)c->cmd == PCA953X_CMD_DEVICE) &&
 216                 (argc == (c->maxargs - 1))))) {
 217                return CMD_RET_USAGE;
 218        }
 219
 220        /* arg2 used as chip number or pin number */
 221        if (argc > 2)
 222                ul_arg2 = simple_strtoul(argv[2], NULL, 16);
 223
 224        /* arg3 used as pin or invert value */
 225        if (argc > 3)
 226                ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1;
 227
 228        switch ((long)c->cmd) {
 229        case PCA953X_CMD_INFO:
 230                ret = pca953x_info(chip);
 231                if (ret)
 232                        ret = CMD_RET_FAILURE;
 233                break;
 234
 235        case PCA953X_CMD_DEVICE:
 236                if (argc == 3)
 237                        chip = (uint8_t)ul_arg2;
 238                printf("Current device address: 0x%x\n", chip);
 239                ret = CMD_RET_SUCCESS;
 240                break;
 241
 242        case PCA953X_CMD_INPUT:
 243                ret = pca953x_set_dir(chip, (1 << ul_arg2),
 244                                PCA953X_DIR_IN << ul_arg2);
 245                val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0;
 246
 247                if (ret)
 248                        ret = CMD_RET_FAILURE;
 249                else
 250                        printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2,
 251                                                                        val);
 252                break;
 253
 254        case PCA953X_CMD_OUTPUT:
 255                ret = pca953x_set_dir(chip, (1 << ul_arg2),
 256                                (PCA953X_DIR_OUT << ul_arg2));
 257                if (!ret)
 258                        ret = pca953x_set_val(chip, (1 << ul_arg2),
 259                                                (ul_arg3 << ul_arg2));
 260                if (ret)
 261                        ret = CMD_RET_FAILURE;
 262                break;
 263
 264        case PCA953X_CMD_INVERT:
 265                ret = pca953x_set_pol(chip, (1 << ul_arg2),
 266                                        (ul_arg3 << ul_arg2));
 267                if (ret)
 268                        ret = CMD_RET_FAILURE;
 269                break;
 270        }
 271
 272        if (ret == CMD_RET_FAILURE)
 273                eprintf("Error talking to chip at 0x%x\n", chip);
 274
 275        return ret;
 276}
 277
 278U_BOOT_CMD(
 279        pca953x,        5,      1,      do_pca953x,
 280        "pca953x gpio access",
 281        "device [dev]\n"
 282        "       - show or set current device address\n"
 283        "pca953x info\n"
 284        "       - display info for current chip\n"
 285        "pca953x output pin 0|1\n"
 286        "       - set pin as output and drive low or high\n"
 287        "pca953x invert pin 0|1\n"
 288        "       - disable/enable polarity inversion for reads\n"
 289        "pca953x input pin\n"
 290        "       - set pin as input and read value"
 291);
 292
 293#endif /* CONFIG_CMD_PCA953X */
 294