18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> 68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/property.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define GEN_74X164_NUMBER_GPIOS 8 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct gen_74x164_chip { 218c2ecf20Sopenharmony_ci struct gpio_chip gpio_chip; 228c2ecf20Sopenharmony_ci struct mutex lock; 238c2ecf20Sopenharmony_ci struct gpio_desc *gpiod_oe; 248c2ecf20Sopenharmony_ci u32 registers; 258c2ecf20Sopenharmony_ci /* 268c2ecf20Sopenharmony_ci * Since the registers are chained, every byte sent will make 278c2ecf20Sopenharmony_ci * the previous byte shift to the next register in the 288c2ecf20Sopenharmony_ci * chain. Thus, the first byte sent will end up in the last 298c2ecf20Sopenharmony_ci * register at the end of the transfer. So, to have a logical 308c2ecf20Sopenharmony_ci * numbering, store the bytes in reverse order. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci u8 buffer[]; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int __gen_74x164_write_config(struct gen_74x164_chip *chip) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer, 388c2ecf20Sopenharmony_ci chip->registers); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct gen_74x164_chip *chip = gpiochip_get_data(gc); 448c2ecf20Sopenharmony_ci u8 bank = chip->registers - 1 - offset / 8; 458c2ecf20Sopenharmony_ci u8 pin = offset % 8; 468c2ecf20Sopenharmony_ci int ret; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 498c2ecf20Sopenharmony_ci ret = (chip->buffer[bank] >> pin) & 0x1; 508c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return ret; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void gen_74x164_set_value(struct gpio_chip *gc, 568c2ecf20Sopenharmony_ci unsigned offset, int val) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct gen_74x164_chip *chip = gpiochip_get_data(gc); 598c2ecf20Sopenharmony_ci u8 bank = chip->registers - 1 - offset / 8; 608c2ecf20Sopenharmony_ci u8 pin = offset % 8; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 638c2ecf20Sopenharmony_ci if (val) 648c2ecf20Sopenharmony_ci chip->buffer[bank] |= (1 << pin); 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci chip->buffer[bank] &= ~(1 << pin); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci __gen_74x164_write_config(chip); 698c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask, 738c2ecf20Sopenharmony_ci unsigned long *bits) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct gen_74x164_chip *chip = gpiochip_get_data(gc); 768c2ecf20Sopenharmony_ci unsigned long offset; 778c2ecf20Sopenharmony_ci unsigned long bankmask; 788c2ecf20Sopenharmony_ci size_t bank; 798c2ecf20Sopenharmony_ci unsigned long bitmask; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 828c2ecf20Sopenharmony_ci for_each_set_clump8(offset, bankmask, mask, chip->registers * 8) { 838c2ecf20Sopenharmony_ci bank = chip->registers - 1 - offset / 8; 848c2ecf20Sopenharmony_ci bitmask = bitmap_get_value8(bits, offset) & bankmask; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci chip->buffer[bank] &= ~bankmask; 878c2ecf20Sopenharmony_ci chip->buffer[bank] |= bitmask; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci __gen_74x164_write_config(chip); 908c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int gen_74x164_direction_output(struct gpio_chip *gc, 948c2ecf20Sopenharmony_ci unsigned offset, int val) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci gen_74x164_set_value(gc, offset, val); 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int gen_74x164_probe(struct spi_device *spi) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct gen_74x164_chip *chip; 1038c2ecf20Sopenharmony_ci u32 nregs; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* 1078c2ecf20Sopenharmony_ci * bits_per_word cannot be configured in platform data 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci spi->bits_per_word = 8; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ret = spi_setup(spi); 1128c2ecf20Sopenharmony_ci if (ret < 0) 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ret = device_property_read_u32(&spi->dev, "registers-number", &nregs); 1168c2ecf20Sopenharmony_ci if (ret) { 1178c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Missing 'registers-number' property.\n"); 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci chip = devm_kzalloc(&spi->dev, sizeof(*chip) + nregs, GFP_KERNEL); 1228c2ecf20Sopenharmony_ci if (!chip) 1238c2ecf20Sopenharmony_ci return -ENOMEM; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci chip->gpiod_oe = devm_gpiod_get_optional(&spi->dev, "enable", 1268c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 1278c2ecf20Sopenharmony_ci if (IS_ERR(chip->gpiod_oe)) 1288c2ecf20Sopenharmony_ci return PTR_ERR(chip->gpiod_oe); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(chip->gpiod_oe, 1); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci spi_set_drvdata(spi, chip); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci chip->gpio_chip.label = spi->modalias; 1358c2ecf20Sopenharmony_ci chip->gpio_chip.direction_output = gen_74x164_direction_output; 1368c2ecf20Sopenharmony_ci chip->gpio_chip.get = gen_74x164_get_value; 1378c2ecf20Sopenharmony_ci chip->gpio_chip.set = gen_74x164_set_value; 1388c2ecf20Sopenharmony_ci chip->gpio_chip.set_multiple = gen_74x164_set_multiple; 1398c2ecf20Sopenharmony_ci chip->gpio_chip.base = -1; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci chip->registers = nregs; 1428c2ecf20Sopenharmony_ci chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci chip->gpio_chip.can_sleep = true; 1458c2ecf20Sopenharmony_ci chip->gpio_chip.parent = &spi->dev; 1468c2ecf20Sopenharmony_ci chip->gpio_chip.owner = THIS_MODULE; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci mutex_init(&chip->lock); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = __gen_74x164_write_config(chip); 1518c2ecf20Sopenharmony_ci if (ret) { 1528c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Failed writing: %d\n", ret); 1538c2ecf20Sopenharmony_ci goto exit_destroy; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&chip->gpio_chip, chip); 1578c2ecf20Sopenharmony_ci if (!ret) 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciexit_destroy: 1618c2ecf20Sopenharmony_ci mutex_destroy(&chip->lock); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int gen_74x164_remove(struct spi_device *spi) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct gen_74x164_chip *chip = spi_get_drvdata(spi); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(chip->gpiod_oe, 0); 1718c2ecf20Sopenharmony_ci gpiochip_remove(&chip->gpio_chip); 1728c2ecf20Sopenharmony_ci mutex_destroy(&chip->lock); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct of_device_id gen_74x164_dt_ids[] = { 1788c2ecf20Sopenharmony_ci { .compatible = "fairchild,74hc595" }, 1798c2ecf20Sopenharmony_ci { .compatible = "nxp,74lvc594" }, 1808c2ecf20Sopenharmony_ci {}, 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, gen_74x164_dt_ids); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct spi_driver gen_74x164_driver = { 1858c2ecf20Sopenharmony_ci .driver = { 1868c2ecf20Sopenharmony_ci .name = "74x164", 1878c2ecf20Sopenharmony_ci .of_match_table = gen_74x164_dt_ids, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci .probe = gen_74x164_probe, 1908c2ecf20Sopenharmony_ci .remove = gen_74x164_remove, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_cimodule_spi_driver(gen_74x164_driver); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); 1958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>"); 1968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register"); 1978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 198