Viktar Palstsiuk | 7d9ec6a | 2013-10-31 11:16:59 +0300 | [diff] [blame] | 1 | /* |
| 2 | * (C) Copyright 2013 |
| 3 | * Viktar Palstsiuk, Promwad, viktar.palstsiuk@promwad.com |
| 4 | * |
| 5 | * SPDX-License-Identifier: GPL-2.0+ |
| 6 | */ |
| 7 | |
| 8 | /* |
| 9 | * Driver for Semtech SX151x SPI GPIO Expanders |
| 10 | */ |
| 11 | |
| 12 | #include <common.h> |
| 13 | #include <spi.h> |
| 14 | #include <sx151x.h> |
| 15 | |
| 16 | #ifndef CONFIG_SX151X_SPI_BUS |
| 17 | #define CONFIG_SX151X_SPI_BUS 0 |
| 18 | #endif |
| 19 | |
| 20 | /* |
| 21 | * The SX151x registers |
| 22 | */ |
| 23 | |
| 24 | #ifdef CONFIG_SX151X_GPIO_COUNT_8 |
| 25 | /* 8bit: SX1511 */ |
| 26 | #define SX151X_REG_DIR 0x07 |
| 27 | #define SX151X_REG_DATA 0x08 |
| 28 | #else |
| 29 | /* 16bit: SX1512 */ |
| 30 | #define SX151X_REG_DIR 0x0F |
| 31 | #define SX151X_REG_DATA 0x11 |
| 32 | #endif |
| 33 | #define SX151X_REG_RESET 0x7D |
| 34 | |
| 35 | static int sx151x_spi_write(int chip, unsigned char reg, unsigned char val) |
| 36 | { |
| 37 | struct spi_slave *slave; |
| 38 | unsigned char buf[2]; |
| 39 | int ret; |
| 40 | |
| 41 | slave = spi_setup_slave(CONFIG_SX151X_SPI_BUS, chip, 1000000, |
| 42 | SPI_MODE_0); |
| 43 | if (!slave) |
| 44 | return 0; |
| 45 | |
| 46 | spi_claim_bus(slave); |
| 47 | |
| 48 | buf[0] = reg; |
| 49 | buf[1] = val; |
| 50 | |
| 51 | ret = spi_xfer(slave, 16, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END); |
| 52 | if (ret < 0) |
| 53 | printf("spi%d.%d write fail: can't write %02x to %02x: %d\n", |
| 54 | CONFIG_SX151X_SPI_BUS, chip, val, reg, ret); |
| 55 | else |
| 56 | printf("spi%d.%d write 0x%02x to register 0x%02x\n", |
| 57 | CONFIG_SX151X_SPI_BUS, chip, val, reg); |
| 58 | spi_release_bus(slave); |
| 59 | spi_free_slave(slave); |
| 60 | |
| 61 | return ret; |
| 62 | } |
| 63 | |
| 64 | static int sx151x_spi_read(int chip, unsigned char reg) |
| 65 | { |
| 66 | struct spi_slave *slave; |
| 67 | int ret; |
| 68 | |
| 69 | slave = spi_setup_slave(CONFIG_SX151X_SPI_BUS, chip, 1000000, |
| 70 | SPI_MODE_0); |
| 71 | if (!slave) |
| 72 | return 0; |
| 73 | |
| 74 | spi_claim_bus(slave); |
| 75 | |
| 76 | ret = spi_w8r8(slave, reg | 0x80); |
| 77 | if (ret < 0) |
| 78 | printf("spi%d.%d read fail: can't read %02x: %d\n", |
| 79 | CONFIG_SX151X_SPI_BUS, chip, reg, ret); |
| 80 | else |
| 81 | printf("spi%d.%d read register 0x%02x: 0x%02x\n", |
| 82 | CONFIG_SX151X_SPI_BUS, chip, reg, ret); |
| 83 | |
| 84 | spi_release_bus(slave); |
| 85 | spi_free_slave(slave); |
| 86 | |
| 87 | return ret; |
| 88 | } |
| 89 | |
| 90 | static inline void sx151x_find_cfg(int gpio, unsigned char *reg, unsigned char *mask) |
| 91 | { |
| 92 | *reg -= gpio / 8; |
| 93 | *mask = 1 << (gpio % 8); |
| 94 | } |
| 95 | |
| 96 | static int sx151x_write_cfg(int chip, unsigned char gpio, unsigned char reg, int val) |
| 97 | { |
| 98 | unsigned char mask; |
| 99 | unsigned char data; |
| 100 | int ret; |
| 101 | |
| 102 | sx151x_find_cfg(gpio, ®, &mask); |
| 103 | ret = sx151x_spi_read(chip, reg); |
| 104 | if (ret < 0) |
| 105 | return ret; |
| 106 | else |
| 107 | data = ret; |
| 108 | data &= ~mask; |
| 109 | data |= (val << (gpio % 8)) & mask; |
| 110 | return sx151x_spi_write(chip, reg, data); |
| 111 | } |
| 112 | |
| 113 | int sx151x_get_value(int chip, int gpio) |
| 114 | { |
| 115 | unsigned char reg = SX151X_REG_DATA; |
| 116 | unsigned char mask; |
| 117 | int ret; |
| 118 | |
| 119 | sx151x_find_cfg(gpio, ®, &mask); |
| 120 | ret = sx151x_spi_read(chip, reg); |
| 121 | if (ret >= 0) |
| 122 | ret = (ret & mask) != 0 ? 1 : 0; |
| 123 | |
| 124 | return ret; |
| 125 | } |
| 126 | |
| 127 | int sx151x_set_value(int chip, int gpio, int val) |
| 128 | { |
| 129 | return sx151x_write_cfg(chip, gpio, SX151X_REG_DATA, (val ? 1 : 0)); |
| 130 | } |
| 131 | |
| 132 | int sx151x_direction_input(int chip, int gpio) |
| 133 | { |
| 134 | return sx151x_write_cfg(chip, gpio, SX151X_REG_DIR, 1); |
| 135 | } |
| 136 | |
| 137 | int sx151x_direction_output(int chip, int gpio) |
| 138 | { |
| 139 | return sx151x_write_cfg(chip, gpio, SX151X_REG_DIR, 0); |
| 140 | } |
| 141 | |
| 142 | int sx151x_reset(int chip) |
| 143 | { |
| 144 | int err; |
| 145 | |
| 146 | err = sx151x_spi_write(chip, SX151X_REG_RESET, 0x12); |
| 147 | if (err < 0) |
| 148 | return err; |
| 149 | |
| 150 | err = sx151x_spi_write(chip, SX151X_REG_RESET, 0x34); |
| 151 | return err; |
| 152 | } |
| 153 | |
| 154 | #ifdef CONFIG_CMD_SX151X |
| 155 | |
| 156 | int do_sx151x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| 157 | { |
| 158 | int ret = CMD_RET_USAGE, chip = 0, gpio = 0, val = 0; |
| 159 | |
| 160 | if (argc < 3) |
| 161 | return CMD_RET_USAGE; |
| 162 | |
| 163 | /* arg2 used as chip number */ |
| 164 | chip = simple_strtoul(argv[2], NULL, 10); |
| 165 | |
| 166 | if (strcmp(argv[1], "reset") == 0) { |
| 167 | ret = sx151x_reset(chip); |
| 168 | if (!ret) { |
| 169 | printf("Device at spi%d.%d was reset\n", |
| 170 | CONFIG_SX151X_SPI_BUS, chip); |
| 171 | } |
| 172 | return ret; |
| 173 | } |
| 174 | |
| 175 | if (argc < 4) |
| 176 | return CMD_RET_USAGE; |
| 177 | |
| 178 | /* arg3 used as gpio number */ |
| 179 | gpio = simple_strtoul(argv[3], NULL, 10); |
| 180 | |
| 181 | if (strcmp(argv[1], "get") == 0) { |
| 182 | ret = sx151x_get_value(chip, gpio); |
| 183 | if (ret < 0) |
| 184 | printf("Failed to get value at spi%d.%d gpio %d\n", |
| 185 | CONFIG_SX151X_SPI_BUS, chip, gpio); |
| 186 | else { |
| 187 | printf("Value at spi%d.%d gpio %d is %d\n", |
| 188 | CONFIG_SX151X_SPI_BUS, chip, gpio, ret); |
| 189 | ret = 0; |
| 190 | } |
| 191 | return ret; |
| 192 | } |
| 193 | |
| 194 | if (argc < 5) |
| 195 | return CMD_RET_USAGE; |
| 196 | |
| 197 | /* arg4 used as value or direction */ |
| 198 | val = simple_strtoul(argv[4], NULL, 10); |
| 199 | |
| 200 | if (strcmp(argv[1], "set") == 0) { |
| 201 | ret = sx151x_set_value(chip, gpio, val); |
| 202 | if (ret < 0) |
| 203 | printf("Failed to set value at spi%d.%d gpio %d\n", |
| 204 | CONFIG_SX151X_SPI_BUS, chip, gpio); |
| 205 | else |
| 206 | printf("New value at spi%d.%d gpio %d is %d\n", |
| 207 | CONFIG_SX151X_SPI_BUS, chip, gpio, val); |
| 208 | return ret; |
| 209 | } else if (strcmp(argv[1], "dir") == 0) { |
| 210 | if (val == 0) |
| 211 | ret = sx151x_direction_output(chip, gpio); |
| 212 | else |
| 213 | ret = sx151x_direction_input(chip, gpio); |
| 214 | |
| 215 | if (ret < 0) |
| 216 | printf("Failed to set direction of spi%d.%d gpio %d\n", |
| 217 | CONFIG_SX151X_SPI_BUS, chip, gpio); |
| 218 | else |
| 219 | printf("New direction of spi%d.%d gpio %d is %d\n", |
| 220 | CONFIG_SX151X_SPI_BUS, chip, gpio, val); |
| 221 | return ret; |
| 222 | } |
| 223 | |
| 224 | printf("Please see usage\n"); |
| 225 | |
| 226 | return ret; |
| 227 | } |
| 228 | |
| 229 | U_BOOT_CMD( |
| 230 | sx151x, 5, 1, do_sx151x, |
| 231 | "sx151x gpio access", |
| 232 | "dir chip gpio 0|1\n" |
| 233 | " - set gpio direction (0 for output, 1 for input)\n" |
| 234 | "sx151x get chip gpio\n" |
| 235 | " - get gpio value\n" |
| 236 | "sx151x set chip gpio 0|1\n" |
| 237 | " - set gpio value\n" |
| 238 | "sx151x reset chip\n" |
| 239 | " - reset chip" |
| 240 | ); |
| 241 | |
| 242 | #endif /* CONFIG_CMD_SX151X */ |