1*abd46274SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b866526dSAndrew F. Davis /* 3b866526dSAndrew F. Davis * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ 4b866526dSAndrew F. Davis * Andrew F. Davis <afd@ti.com> 5b866526dSAndrew F. Davis */ 6b866526dSAndrew F. Davis 7b866526dSAndrew F. Davis #include <linux/gpio/driver.h> 8b866526dSAndrew F. Davis #include <linux/i2c.h> 9b866526dSAndrew F. Davis #include <linux/module.h> 10b866526dSAndrew F. Davis #include <linux/mutex.h> 11b866526dSAndrew F. Davis 12b866526dSAndrew F. Davis #define TPIC2810_WS_COMMAND 0x44 13b866526dSAndrew F. Davis 14b866526dSAndrew F. Davis /** 15b866526dSAndrew F. Davis * struct tpic2810 - GPIO driver data 16b866526dSAndrew F. Davis * @chip: GPIO controller chip 17b866526dSAndrew F. Davis * @client: I2C device pointer 18b866526dSAndrew F. Davis * @buffer: Buffer for device register 19b866526dSAndrew F. Davis * @lock: Protects write sequences 20b866526dSAndrew F. Davis */ 21b866526dSAndrew F. Davis struct tpic2810 { 22b866526dSAndrew F. Davis struct gpio_chip chip; 23b866526dSAndrew F. Davis struct i2c_client *client; 24b866526dSAndrew F. Davis u8 buffer; 25b866526dSAndrew F. Davis struct mutex lock; 26b866526dSAndrew F. Davis }; 27b866526dSAndrew F. Davis 28ce02d18fSAxel Lin static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value); 29ce02d18fSAxel Lin 30b866526dSAndrew F. Davis static int tpic2810_get_direction(struct gpio_chip *chip, 31b866526dSAndrew F. Davis unsigned offset) 32b866526dSAndrew F. Davis { 33b866526dSAndrew F. Davis /* This device always output */ 34e42615ecSMatti Vaittinen return GPIO_LINE_DIRECTION_OUT; 35b866526dSAndrew F. Davis } 36b866526dSAndrew F. Davis 37b866526dSAndrew F. Davis static int tpic2810_direction_input(struct gpio_chip *chip, 38b866526dSAndrew F. Davis unsigned offset) 39b866526dSAndrew F. Davis { 40b866526dSAndrew F. Davis /* This device is output only */ 41b866526dSAndrew F. Davis return -EINVAL; 42b866526dSAndrew F. Davis } 43b866526dSAndrew F. Davis 44b866526dSAndrew F. Davis static int tpic2810_direction_output(struct gpio_chip *chip, 45b866526dSAndrew F. Davis unsigned offset, int value) 46b866526dSAndrew F. Davis { 47b866526dSAndrew F. Davis /* This device always output */ 48ce02d18fSAxel Lin tpic2810_set(chip, offset, value); 49b866526dSAndrew F. Davis return 0; 50b866526dSAndrew F. Davis } 51b866526dSAndrew F. Davis 526e66a659SAxel Lin static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) 53b866526dSAndrew F. Davis { 54b866526dSAndrew F. Davis struct tpic2810 *gpio = gpiochip_get_data(chip); 556e66a659SAxel Lin u8 buffer; 566e66a659SAxel Lin int err; 57b866526dSAndrew F. Davis 58b866526dSAndrew F. Davis mutex_lock(&gpio->lock); 59b866526dSAndrew F. Davis 606e66a659SAxel Lin buffer = gpio->buffer & ~mask; 616e66a659SAxel Lin buffer |= (mask & bits); 62b866526dSAndrew F. Davis 636e66a659SAxel Lin err = i2c_smbus_write_byte_data(gpio->client, TPIC2810_WS_COMMAND, 646e66a659SAxel Lin buffer); 656e66a659SAxel Lin if (!err) 666e66a659SAxel Lin gpio->buffer = buffer; 67b866526dSAndrew F. Davis 68b866526dSAndrew F. Davis mutex_unlock(&gpio->lock); 69b866526dSAndrew F. Davis } 70b866526dSAndrew F. Davis 716e66a659SAxel Lin static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value) 726e66a659SAxel Lin { 736e66a659SAxel Lin tpic2810_set_mask_bits(chip, BIT(offset), value ? BIT(offset) : 0); 746e66a659SAxel Lin } 756e66a659SAxel Lin 76b866526dSAndrew F. Davis static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, 77b866526dSAndrew F. Davis unsigned long *bits) 78b866526dSAndrew F. Davis { 796e66a659SAxel Lin tpic2810_set_mask_bits(chip, *mask, *bits); 80b866526dSAndrew F. Davis } 81b866526dSAndrew F. Davis 82e35b5ab0SJulia Lawall static const struct gpio_chip template_chip = { 83b866526dSAndrew F. Davis .label = "tpic2810", 84b866526dSAndrew F. Davis .owner = THIS_MODULE, 85b866526dSAndrew F. Davis .get_direction = tpic2810_get_direction, 86b866526dSAndrew F. Davis .direction_input = tpic2810_direction_input, 87b866526dSAndrew F. Davis .direction_output = tpic2810_direction_output, 88b866526dSAndrew F. Davis .set = tpic2810_set, 89b866526dSAndrew F. Davis .set_multiple = tpic2810_set_multiple, 90b866526dSAndrew F. Davis .base = -1, 91b866526dSAndrew F. Davis .ngpio = 8, 92b866526dSAndrew F. Davis .can_sleep = true, 93b866526dSAndrew F. Davis }; 94b866526dSAndrew F. Davis 95b866526dSAndrew F. Davis static const struct of_device_id tpic2810_of_match_table[] = { 96b866526dSAndrew F. Davis { .compatible = "ti,tpic2810" }, 97b866526dSAndrew F. Davis { /* sentinel */ } 98b866526dSAndrew F. Davis }; 99b866526dSAndrew F. Davis MODULE_DEVICE_TABLE(of, tpic2810_of_match_table); 100b866526dSAndrew F. Davis 101b866526dSAndrew F. Davis static int tpic2810_probe(struct i2c_client *client, 102b866526dSAndrew F. Davis const struct i2c_device_id *id) 103b866526dSAndrew F. Davis { 104b866526dSAndrew F. Davis struct tpic2810 *gpio; 105b866526dSAndrew F. Davis int ret; 106b866526dSAndrew F. Davis 107b866526dSAndrew F. Davis gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); 108b866526dSAndrew F. Davis if (!gpio) 109b866526dSAndrew F. Davis return -ENOMEM; 110b866526dSAndrew F. Davis 111b866526dSAndrew F. Davis i2c_set_clientdata(client, gpio); 112b866526dSAndrew F. Davis 113b866526dSAndrew F. Davis gpio->chip = template_chip; 114b866526dSAndrew F. Davis gpio->chip.parent = &client->dev; 115b866526dSAndrew F. Davis 116b866526dSAndrew F. Davis gpio->client = client; 117b866526dSAndrew F. Davis 118b866526dSAndrew F. Davis mutex_init(&gpio->lock); 119b866526dSAndrew F. Davis 120b866526dSAndrew F. Davis ret = gpiochip_add_data(&gpio->chip, gpio); 121b866526dSAndrew F. Davis if (ret < 0) { 122b866526dSAndrew F. Davis dev_err(&client->dev, "Unable to register gpiochip\n"); 123b866526dSAndrew F. Davis return ret; 124b866526dSAndrew F. Davis } 125b866526dSAndrew F. Davis 126b866526dSAndrew F. Davis return 0; 127b866526dSAndrew F. Davis } 128b866526dSAndrew F. Davis 129b866526dSAndrew F. Davis static int tpic2810_remove(struct i2c_client *client) 130b866526dSAndrew F. Davis { 131b866526dSAndrew F. Davis struct tpic2810 *gpio = i2c_get_clientdata(client); 132b866526dSAndrew F. Davis 133b866526dSAndrew F. Davis gpiochip_remove(&gpio->chip); 134b866526dSAndrew F. Davis 135b866526dSAndrew F. Davis return 0; 136b866526dSAndrew F. Davis } 137b866526dSAndrew F. Davis 138b866526dSAndrew F. Davis static const struct i2c_device_id tpic2810_id_table[] = { 139b866526dSAndrew F. Davis { "tpic2810", }, 140b866526dSAndrew F. Davis { /* sentinel */ } 141b866526dSAndrew F. Davis }; 142b866526dSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, tpic2810_id_table); 143b866526dSAndrew F. Davis 144b866526dSAndrew F. Davis static struct i2c_driver tpic2810_driver = { 145b866526dSAndrew F. Davis .driver = { 146b866526dSAndrew F. Davis .name = "tpic2810", 147b866526dSAndrew F. Davis .of_match_table = tpic2810_of_match_table, 148b866526dSAndrew F. Davis }, 149b866526dSAndrew F. Davis .probe = tpic2810_probe, 150b866526dSAndrew F. Davis .remove = tpic2810_remove, 151b866526dSAndrew F. Davis .id_table = tpic2810_id_table, 152b866526dSAndrew F. Davis }; 153b866526dSAndrew F. Davis module_i2c_driver(tpic2810_driver); 154b866526dSAndrew F. Davis 155b866526dSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 156b866526dSAndrew F. Davis MODULE_DESCRIPTION("TPIC2810 8-Bit LED Driver GPIO Driver"); 157b866526dSAndrew F. Davis MODULE_LICENSE("GPL v2"); 158