1b866526dSAndrew F. Davis /* 2b866526dSAndrew F. Davis * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ 3b866526dSAndrew F. Davis * Andrew F. Davis <afd@ti.com> 4b866526dSAndrew F. Davis * 5b866526dSAndrew F. Davis * This program is free software; you can redistribute it and/or 6b866526dSAndrew F. Davis * modify it under the terms of the GNU General Public License version 2 as 7b866526dSAndrew F. Davis * published by the Free Software Foundation. 8b866526dSAndrew F. Davis * 9b866526dSAndrew F. Davis * This program is distributed "as is" WITHOUT ANY WARRANTY of any 10b866526dSAndrew F. Davis * kind, whether expressed or implied; without even the implied warranty 11b866526dSAndrew F. Davis * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12b866526dSAndrew F. Davis * GNU General Public License version 2 for more details. 13b866526dSAndrew F. Davis */ 14b866526dSAndrew F. Davis 15b866526dSAndrew F. Davis #include <linux/gpio/driver.h> 16b866526dSAndrew F. Davis #include <linux/i2c.h> 17b866526dSAndrew F. Davis #include <linux/module.h> 18b866526dSAndrew F. Davis #include <linux/mutex.h> 19b866526dSAndrew F. Davis 20b866526dSAndrew F. Davis #define TPIC2810_WS_COMMAND 0x44 21b866526dSAndrew F. Davis 22b866526dSAndrew F. Davis /** 23b866526dSAndrew F. Davis * struct tpic2810 - GPIO driver data 24b866526dSAndrew F. Davis * @chip: GPIO controller chip 25b866526dSAndrew F. Davis * @client: I2C device pointer 26b866526dSAndrew F. Davis * @buffer: Buffer for device register 27b866526dSAndrew F. Davis * @lock: Protects write sequences 28b866526dSAndrew F. Davis */ 29b866526dSAndrew F. Davis struct tpic2810 { 30b866526dSAndrew F. Davis struct gpio_chip chip; 31b866526dSAndrew F. Davis struct i2c_client *client; 32b866526dSAndrew F. Davis u8 buffer; 33b866526dSAndrew F. Davis struct mutex lock; 34b866526dSAndrew F. Davis }; 35b866526dSAndrew F. Davis 36ce02d18fSAxel Lin static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value); 37ce02d18fSAxel Lin 38b866526dSAndrew F. Davis static int tpic2810_get_direction(struct gpio_chip *chip, 39b866526dSAndrew F. Davis unsigned offset) 40b866526dSAndrew F. Davis { 41b866526dSAndrew F. Davis /* This device always output */ 42b866526dSAndrew F. Davis return 0; 43b866526dSAndrew F. Davis } 44b866526dSAndrew F. Davis 45b866526dSAndrew F. Davis static int tpic2810_direction_input(struct gpio_chip *chip, 46b866526dSAndrew F. Davis unsigned offset) 47b866526dSAndrew F. Davis { 48b866526dSAndrew F. Davis /* This device is output only */ 49b866526dSAndrew F. Davis return -EINVAL; 50b866526dSAndrew F. Davis } 51b866526dSAndrew F. Davis 52b866526dSAndrew F. Davis static int tpic2810_direction_output(struct gpio_chip *chip, 53b866526dSAndrew F. Davis unsigned offset, int value) 54b866526dSAndrew F. Davis { 55b866526dSAndrew F. Davis /* This device always output */ 56ce02d18fSAxel Lin tpic2810_set(chip, offset, value); 57b866526dSAndrew F. Davis return 0; 58b866526dSAndrew F. Davis } 59b866526dSAndrew F. Davis 606e66a659SAxel Lin static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) 61b866526dSAndrew F. Davis { 62b866526dSAndrew F. Davis struct tpic2810 *gpio = gpiochip_get_data(chip); 636e66a659SAxel Lin u8 buffer; 646e66a659SAxel Lin int err; 65b866526dSAndrew F. Davis 66b866526dSAndrew F. Davis mutex_lock(&gpio->lock); 67b866526dSAndrew F. Davis 686e66a659SAxel Lin buffer = gpio->buffer & ~mask; 696e66a659SAxel Lin buffer |= (mask & bits); 70b866526dSAndrew F. Davis 716e66a659SAxel Lin err = i2c_smbus_write_byte_data(gpio->client, TPIC2810_WS_COMMAND, 726e66a659SAxel Lin buffer); 736e66a659SAxel Lin if (!err) 746e66a659SAxel Lin gpio->buffer = buffer; 75b866526dSAndrew F. Davis 76b866526dSAndrew F. Davis mutex_unlock(&gpio->lock); 77b866526dSAndrew F. Davis } 78b866526dSAndrew F. Davis 796e66a659SAxel Lin static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value) 806e66a659SAxel Lin { 816e66a659SAxel Lin tpic2810_set_mask_bits(chip, BIT(offset), value ? BIT(offset) : 0); 826e66a659SAxel Lin } 836e66a659SAxel Lin 84b866526dSAndrew F. Davis static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, 85b866526dSAndrew F. Davis unsigned long *bits) 86b866526dSAndrew F. Davis { 876e66a659SAxel Lin tpic2810_set_mask_bits(chip, *mask, *bits); 88b866526dSAndrew F. Davis } 89b866526dSAndrew F. Davis 90b866526dSAndrew F. Davis static struct gpio_chip template_chip = { 91b866526dSAndrew F. Davis .label = "tpic2810", 92b866526dSAndrew F. Davis .owner = THIS_MODULE, 93b866526dSAndrew F. Davis .get_direction = tpic2810_get_direction, 94b866526dSAndrew F. Davis .direction_input = tpic2810_direction_input, 95b866526dSAndrew F. Davis .direction_output = tpic2810_direction_output, 96b866526dSAndrew F. Davis .set = tpic2810_set, 97b866526dSAndrew F. Davis .set_multiple = tpic2810_set_multiple, 98b866526dSAndrew F. Davis .base = -1, 99b866526dSAndrew F. Davis .ngpio = 8, 100b866526dSAndrew F. Davis .can_sleep = true, 101b866526dSAndrew F. Davis }; 102b866526dSAndrew F. Davis 103b866526dSAndrew F. Davis static const struct of_device_id tpic2810_of_match_table[] = { 104b866526dSAndrew F. Davis { .compatible = "ti,tpic2810" }, 105b866526dSAndrew F. Davis { /* sentinel */ } 106b866526dSAndrew F. Davis }; 107b866526dSAndrew F. Davis MODULE_DEVICE_TABLE(of, tpic2810_of_match_table); 108b866526dSAndrew F. Davis 109b866526dSAndrew F. Davis static int tpic2810_probe(struct i2c_client *client, 110b866526dSAndrew F. Davis const struct i2c_device_id *id) 111b866526dSAndrew F. Davis { 112b866526dSAndrew F. Davis struct tpic2810 *gpio; 113b866526dSAndrew F. Davis int ret; 114b866526dSAndrew F. Davis 115b866526dSAndrew F. Davis gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); 116b866526dSAndrew F. Davis if (!gpio) 117b866526dSAndrew F. Davis return -ENOMEM; 118b866526dSAndrew F. Davis 119b866526dSAndrew F. Davis i2c_set_clientdata(client, gpio); 120b866526dSAndrew F. Davis 121b866526dSAndrew F. Davis gpio->chip = template_chip; 122b866526dSAndrew F. Davis gpio->chip.parent = &client->dev; 123b866526dSAndrew F. Davis 124b866526dSAndrew F. Davis gpio->client = client; 125b866526dSAndrew F. Davis 126b866526dSAndrew F. Davis mutex_init(&gpio->lock); 127b866526dSAndrew F. Davis 128b866526dSAndrew F. Davis ret = gpiochip_add_data(&gpio->chip, gpio); 129b866526dSAndrew F. Davis if (ret < 0) { 130b866526dSAndrew F. Davis dev_err(&client->dev, "Unable to register gpiochip\n"); 131b866526dSAndrew F. Davis return ret; 132b866526dSAndrew F. Davis } 133b866526dSAndrew F. Davis 134b866526dSAndrew F. Davis return 0; 135b866526dSAndrew F. Davis } 136b866526dSAndrew F. Davis 137b866526dSAndrew F. Davis static int tpic2810_remove(struct i2c_client *client) 138b866526dSAndrew F. Davis { 139b866526dSAndrew F. Davis struct tpic2810 *gpio = i2c_get_clientdata(client); 140b866526dSAndrew F. Davis 141b866526dSAndrew F. Davis gpiochip_remove(&gpio->chip); 142b866526dSAndrew F. Davis 143b866526dSAndrew F. Davis return 0; 144b866526dSAndrew F. Davis } 145b866526dSAndrew F. Davis 146b866526dSAndrew F. Davis static const struct i2c_device_id tpic2810_id_table[] = { 147b866526dSAndrew F. Davis { "tpic2810", }, 148b866526dSAndrew F. Davis { /* sentinel */ } 149b866526dSAndrew F. Davis }; 150b866526dSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, tpic2810_id_table); 151b866526dSAndrew F. Davis 152b866526dSAndrew F. Davis static struct i2c_driver tpic2810_driver = { 153b866526dSAndrew F. Davis .driver = { 154b866526dSAndrew F. Davis .name = "tpic2810", 155b866526dSAndrew F. Davis .of_match_table = tpic2810_of_match_table, 156b866526dSAndrew F. Davis }, 157b866526dSAndrew F. Davis .probe = tpic2810_probe, 158b866526dSAndrew F. Davis .remove = tpic2810_remove, 159b866526dSAndrew F. Davis .id_table = tpic2810_id_table, 160b866526dSAndrew F. Davis }; 161b866526dSAndrew F. Davis module_i2c_driver(tpic2810_driver); 162b866526dSAndrew F. Davis 163b866526dSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 164b866526dSAndrew F. Davis MODULE_DESCRIPTION("TPIC2810 8-Bit LED Driver GPIO Driver"); 165b866526dSAndrew F. Davis MODULE_LICENSE("GPL v2"); 166