Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | // SPDX-License-Identifier: GPL-2.0-only /* * Driver for PCA9570 I2C GPO expander * * Copyright (C) 2020 Sungbo Eo <mans0n@gorani.run> * * Based on gpio-tpic2810.c * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis <afd@ti.com> */ #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/property.h> #define SLG7XL45106_GPO_REG 0xDB /** * struct pca9570_chip_data - GPIO platformdata * @ngpio: no of gpios * @command: Command to be sent */ struct pca9570_chip_data { u16 ngpio; u32 command; }; /** * struct pca9570 - GPIO driver data * @chip: GPIO controller chip * @chip_data: GPIO controller platform data * @lock: Protects write sequences * @out: Buffer for device register */ struct pca9570 { struct gpio_chip chip; const struct pca9570_chip_data *chip_data; 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; if (gpio->chip_data->command != 0) ret = i2c_smbus_read_byte_data(client, gpio->chip_data->command); else 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); if (gpio->chip_data->command != 0) return i2c_smbus_write_byte_data(client, gpio->chip_data->command, value); 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_data = device_get_match_data(&client->dev); gpio->chip.ngpio = gpio->chip_data->ngpio; 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 pca9570_chip_data pca9570_gpio = { .ngpio = 4, }; static const struct pca9570_chip_data pca9571_gpio = { .ngpio = 8, }; static const struct pca9570_chip_data slg7xl45106_gpio = { .ngpio = 8, .command = SLG7XL45106_GPO_REG, }; static const struct i2c_device_id pca9570_id_table[] = { { "pca9570", (kernel_ulong_t)&pca9570_gpio}, { "pca9571", (kernel_ulong_t)&pca9571_gpio }, { "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9570_id_table); static const struct of_device_id pca9570_of_match_table[] = { { .compatible = "dlg,slg7xl45106", .data = &slg7xl45106_gpio}, { .compatible = "nxp,pca9570", .data = &pca9570_gpio }, { .compatible = "nxp,pca9571", .data = &pca9571_gpio }, { /* 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 = pca9570_probe, .id_table = pca9570_id_table, }; module_i2c_driver(pca9570_driver); MODULE_AUTHOR("Sungbo Eo <mans0n@gorani.run>"); MODULE_DESCRIPTION("GPIO expander driver for PCA9570"); MODULE_LICENSE("GPL v2"); |