1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2015-2023 Texas Instruments Incorporated - https://www.ti.com/ |
4 | * Andrew Davis <afd@ti.com> |
5 | */ |
6 | |
7 | #include <linux/gpio/driver.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/module.h> |
10 | #include <linux/mutex.h> |
11 | |
12 | #define TPIC2810_WS_COMMAND 0x44 |
13 | |
14 | /** |
15 | * struct tpic2810 - GPIO driver data |
16 | * @chip: GPIO controller chip |
17 | * @client: I2C device pointer |
18 | * @buffer: Buffer for device register |
19 | * @lock: Protects write sequences |
20 | */ |
21 | struct tpic2810 { |
22 | struct gpio_chip chip; |
23 | struct i2c_client *client; |
24 | u8 buffer; |
25 | struct mutex lock; |
26 | }; |
27 | |
28 | static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value); |
29 | |
30 | static int tpic2810_get_direction(struct gpio_chip *chip, |
31 | unsigned offset) |
32 | { |
33 | /* This device always output */ |
34 | return GPIO_LINE_DIRECTION_OUT; |
35 | } |
36 | |
37 | static int tpic2810_direction_input(struct gpio_chip *chip, |
38 | unsigned offset) |
39 | { |
40 | /* This device is output only */ |
41 | return -EINVAL; |
42 | } |
43 | |
44 | static int tpic2810_direction_output(struct gpio_chip *chip, |
45 | unsigned offset, int value) |
46 | { |
47 | /* This device always output */ |
48 | tpic2810_set(chip, offset, value); |
49 | return 0; |
50 | } |
51 | |
52 | static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) |
53 | { |
54 | struct tpic2810 *gpio = gpiochip_get_data(gc: chip); |
55 | u8 buffer; |
56 | int err; |
57 | |
58 | mutex_lock(&gpio->lock); |
59 | |
60 | buffer = gpio->buffer & ~mask; |
61 | buffer |= (mask & bits); |
62 | |
63 | err = i2c_smbus_write_byte_data(client: gpio->client, TPIC2810_WS_COMMAND, |
64 | value: buffer); |
65 | if (!err) |
66 | gpio->buffer = buffer; |
67 | |
68 | mutex_unlock(lock: &gpio->lock); |
69 | } |
70 | |
71 | static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value) |
72 | { |
73 | tpic2810_set_mask_bits(chip, BIT(offset), bits: value ? BIT(offset) : 0); |
74 | } |
75 | |
76 | static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, |
77 | unsigned long *bits) |
78 | { |
79 | tpic2810_set_mask_bits(chip, mask: *mask, bits: *bits); |
80 | } |
81 | |
82 | static const struct gpio_chip template_chip = { |
83 | .label = "tpic2810" , |
84 | .owner = THIS_MODULE, |
85 | .get_direction = tpic2810_get_direction, |
86 | .direction_input = tpic2810_direction_input, |
87 | .direction_output = tpic2810_direction_output, |
88 | .set = tpic2810_set, |
89 | .set_multiple = tpic2810_set_multiple, |
90 | .base = -1, |
91 | .ngpio = 8, |
92 | .can_sleep = true, |
93 | }; |
94 | |
95 | static const struct of_device_id tpic2810_of_match_table[] = { |
96 | { .compatible = "ti,tpic2810" }, |
97 | { /* sentinel */ } |
98 | }; |
99 | MODULE_DEVICE_TABLE(of, tpic2810_of_match_table); |
100 | |
101 | static int tpic2810_probe(struct i2c_client *client) |
102 | { |
103 | struct tpic2810 *gpio; |
104 | |
105 | gpio = devm_kzalloc(dev: &client->dev, size: sizeof(*gpio), GFP_KERNEL); |
106 | if (!gpio) |
107 | return -ENOMEM; |
108 | |
109 | gpio->chip = template_chip; |
110 | gpio->chip.parent = &client->dev; |
111 | |
112 | gpio->client = client; |
113 | |
114 | mutex_init(&gpio->lock); |
115 | |
116 | return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); |
117 | } |
118 | |
119 | static const struct i2c_device_id tpic2810_id_table[] = { |
120 | { "tpic2810" , }, |
121 | { /* sentinel */ } |
122 | }; |
123 | MODULE_DEVICE_TABLE(i2c, tpic2810_id_table); |
124 | |
125 | static struct i2c_driver tpic2810_driver = { |
126 | .driver = { |
127 | .name = "tpic2810" , |
128 | .of_match_table = tpic2810_of_match_table, |
129 | }, |
130 | .probe = tpic2810_probe, |
131 | .id_table = tpic2810_id_table, |
132 | }; |
133 | module_i2c_driver(tpic2810_driver); |
134 | |
135 | MODULE_AUTHOR("Andrew Davis <afd@ti.com>" ); |
136 | MODULE_DESCRIPTION("TPIC2810 8-Bit LED Driver GPIO Driver" ); |
137 | MODULE_LICENSE("GPL v2" ); |
138 | |