123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Driver for PCA9570 I2C GPO expander
- *
- * Copyright (C) 2020 Sungbo Eo <[email protected]>
- *
- * Based on gpio-tpic2810.c
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
- * Andrew F. Davis <[email protected]>
- */
- #include <linux/gpio/driver.h>
- #include <linux/i2c.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/property.h>
- /**
- * struct pca9570 - GPIO driver data
- * @chip: GPIO controller chip
- * @lock: Protects write sequences
- * @out: Buffer for device register
- */
- struct pca9570 {
- struct gpio_chip chip;
- struct mutex lock;
- u8 out;
- };
- static int pca9570_read(struct pca9570 *gpio, u8 *value)
- {
- struct i2c_client *client = to_i2c_client(gpio->chip.parent);
- int ret;
- ret = i2c_smbus_read_byte(client);
- if (ret < 0)
- return ret;
- *value = ret;
- return 0;
- }
- static int pca9570_write(struct pca9570 *gpio, u8 value)
- {
- struct i2c_client *client = to_i2c_client(gpio->chip.parent);
- return i2c_smbus_write_byte(client, value);
- }
- static int pca9570_get_direction(struct gpio_chip *chip,
- unsigned offset)
- {
- /* This device always output */
- return GPIO_LINE_DIRECTION_OUT;
- }
- static int pca9570_get(struct gpio_chip *chip, unsigned offset)
- {
- struct pca9570 *gpio = gpiochip_get_data(chip);
- u8 buffer;
- int ret;
- ret = pca9570_read(gpio, &buffer);
- if (ret)
- return ret;
- return !!(buffer & BIT(offset));
- }
- static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value)
- {
- struct pca9570 *gpio = gpiochip_get_data(chip);
- u8 buffer;
- int ret;
- mutex_lock(&gpio->lock);
- buffer = gpio->out;
- if (value)
- buffer |= BIT(offset);
- else
- buffer &= ~BIT(offset);
- ret = pca9570_write(gpio, buffer);
- if (ret)
- goto out;
- gpio->out = buffer;
- out:
- mutex_unlock(&gpio->lock);
- }
- static int pca9570_probe(struct i2c_client *client)
- {
- struct pca9570 *gpio;
- gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
- if (!gpio)
- return -ENOMEM;
- gpio->chip.label = client->name;
- gpio->chip.parent = &client->dev;
- gpio->chip.owner = THIS_MODULE;
- gpio->chip.get_direction = pca9570_get_direction;
- gpio->chip.get = pca9570_get;
- gpio->chip.set = pca9570_set;
- gpio->chip.base = -1;
- gpio->chip.ngpio = (uintptr_t)device_get_match_data(&client->dev);
- gpio->chip.can_sleep = true;
- mutex_init(&gpio->lock);
- /* Read the current output level */
- pca9570_read(gpio, &gpio->out);
- i2c_set_clientdata(client, gpio);
- return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio);
- }
- static const struct i2c_device_id pca9570_id_table[] = {
- { "pca9570", 4 },
- { "pca9571", 8 },
- { /* sentinel */ }
- };
- MODULE_DEVICE_TABLE(i2c, pca9570_id_table);
- static const struct of_device_id pca9570_of_match_table[] = {
- { .compatible = "nxp,pca9570", .data = (void *)4 },
- { .compatible = "nxp,pca9571", .data = (void *)8 },
- { /* sentinel */ }
- };
- MODULE_DEVICE_TABLE(of, pca9570_of_match_table);
- static struct i2c_driver pca9570_driver = {
- .driver = {
- .name = "pca9570",
- .of_match_table = pca9570_of_match_table,
- },
- .probe_new = pca9570_probe,
- .id_table = pca9570_id_table,
- };
- module_i2c_driver(pca9570_driver);
- MODULE_AUTHOR("Sungbo Eo <[email protected]>");
- MODULE_DESCRIPTION("GPIO expander driver for PCA9570");
- MODULE_LICENSE("GPL v2");
|