1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * TI TPS6591x GPIO driver |
4 | * |
5 | * Copyright 2010 Texas Instruments Inc. |
6 | * |
7 | * Author: Graeme Gregory <gg@slimlogic.co.uk> |
8 | * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/gpio/driver.h> |
15 | #include <linux/i2c.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/mfd/tps65910.h> |
18 | #include <linux/of.h> |
19 | |
20 | struct tps65910_gpio { |
21 | struct gpio_chip gpio_chip; |
22 | struct tps65910 *tps65910; |
23 | }; |
24 | |
25 | static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) |
26 | { |
27 | struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); |
28 | struct tps65910 *tps65910 = tps65910_gpio->tps65910; |
29 | unsigned int val; |
30 | |
31 | regmap_read(map: tps65910->regmap, TPS65910_GPIO0 + offset, val: &val); |
32 | |
33 | if (val & GPIO_STS_MASK) |
34 | return 1; |
35 | |
36 | return 0; |
37 | } |
38 | |
39 | static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, |
40 | int value) |
41 | { |
42 | struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); |
43 | struct tps65910 *tps65910 = tps65910_gpio->tps65910; |
44 | |
45 | if (value) |
46 | regmap_set_bits(map: tps65910->regmap, TPS65910_GPIO0 + offset, |
47 | GPIO_SET_MASK); |
48 | else |
49 | regmap_clear_bits(map: tps65910->regmap, TPS65910_GPIO0 + offset, |
50 | GPIO_SET_MASK); |
51 | } |
52 | |
53 | static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, |
54 | int value) |
55 | { |
56 | struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); |
57 | struct tps65910 *tps65910 = tps65910_gpio->tps65910; |
58 | |
59 | /* Set the initial value */ |
60 | tps65910_gpio_set(gc, offset, value); |
61 | |
62 | return regmap_set_bits(map: tps65910->regmap, TPS65910_GPIO0 + offset, |
63 | GPIO_CFG_MASK); |
64 | } |
65 | |
66 | static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset) |
67 | { |
68 | struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); |
69 | struct tps65910 *tps65910 = tps65910_gpio->tps65910; |
70 | |
71 | return regmap_clear_bits(map: tps65910->regmap, TPS65910_GPIO0 + offset, |
72 | GPIO_CFG_MASK); |
73 | } |
74 | |
75 | #ifdef CONFIG_OF |
76 | static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev, |
77 | struct tps65910 *tps65910, int chip_ngpio) |
78 | { |
79 | struct tps65910_board *tps65910_board = tps65910->of_plat_data; |
80 | unsigned int prop_array[TPS6591X_MAX_NUM_GPIO]; |
81 | int ngpio = min(chip_ngpio, TPS6591X_MAX_NUM_GPIO); |
82 | int ret; |
83 | int idx; |
84 | |
85 | tps65910_board->gpio_base = -1; |
86 | ret = of_property_read_u32_array(np: tps65910->dev->of_node, |
87 | propname: "ti,en-gpio-sleep" , out_values: prop_array, sz: ngpio); |
88 | if (ret < 0) { |
89 | dev_dbg(dev, "ti,en-gpio-sleep not specified\n" ); |
90 | return tps65910_board; |
91 | } |
92 | |
93 | for (idx = 0; idx < ngpio; idx++) |
94 | tps65910_board->en_gpio_sleep[idx] = (prop_array[idx] != 0); |
95 | |
96 | return tps65910_board; |
97 | } |
98 | #else |
99 | static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev, |
100 | struct tps65910 *tps65910, int chip_ngpio) |
101 | { |
102 | return NULL; |
103 | } |
104 | #endif |
105 | |
106 | static int tps65910_gpio_probe(struct platform_device *pdev) |
107 | { |
108 | struct tps65910 *tps65910 = dev_get_drvdata(dev: pdev->dev.parent); |
109 | struct tps65910_board *pdata = dev_get_platdata(dev: tps65910->dev); |
110 | struct tps65910_gpio *tps65910_gpio; |
111 | int ret; |
112 | int i; |
113 | |
114 | device_set_node(dev: &pdev->dev, dev_fwnode(pdev->dev.parent)); |
115 | |
116 | tps65910_gpio = devm_kzalloc(dev: &pdev->dev, |
117 | size: sizeof(*tps65910_gpio), GFP_KERNEL); |
118 | if (!tps65910_gpio) |
119 | return -ENOMEM; |
120 | |
121 | tps65910_gpio->tps65910 = tps65910; |
122 | |
123 | tps65910_gpio->gpio_chip.owner = THIS_MODULE; |
124 | tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name; |
125 | |
126 | switch (tps65910_chip_id(tps65910)) { |
127 | case TPS65910: |
128 | tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO; |
129 | break; |
130 | case TPS65911: |
131 | tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO; |
132 | break; |
133 | default: |
134 | return -EINVAL; |
135 | } |
136 | tps65910_gpio->gpio_chip.can_sleep = true; |
137 | tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input; |
138 | tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output; |
139 | tps65910_gpio->gpio_chip.set = tps65910_gpio_set; |
140 | tps65910_gpio->gpio_chip.get = tps65910_gpio_get; |
141 | tps65910_gpio->gpio_chip.parent = &pdev->dev; |
142 | |
143 | if (pdata && pdata->gpio_base) |
144 | tps65910_gpio->gpio_chip.base = pdata->gpio_base; |
145 | else |
146 | tps65910_gpio->gpio_chip.base = -1; |
147 | |
148 | if (!pdata && tps65910->dev->of_node) |
149 | pdata = tps65910_parse_dt_for_gpio(dev: &pdev->dev, tps65910, |
150 | chip_ngpio: tps65910_gpio->gpio_chip.ngpio); |
151 | |
152 | if (!pdata) |
153 | goto skip_init; |
154 | |
155 | /* Configure sleep control for gpios if provided */ |
156 | for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) { |
157 | if (!pdata->en_gpio_sleep[i]) |
158 | continue; |
159 | |
160 | ret = regmap_set_bits(map: tps65910->regmap, |
161 | TPS65910_GPIO0 + i, GPIO_SLEEP_MASK); |
162 | if (ret < 0) |
163 | dev_warn(tps65910->dev, |
164 | "GPIO Sleep setting failed with err %d\n" , ret); |
165 | } |
166 | |
167 | skip_init: |
168 | return devm_gpiochip_add_data(&pdev->dev, &tps65910_gpio->gpio_chip, |
169 | tps65910_gpio); |
170 | } |
171 | |
172 | static struct platform_driver tps65910_gpio_driver = { |
173 | .driver.name = "tps65910-gpio" , |
174 | .probe = tps65910_gpio_probe, |
175 | }; |
176 | |
177 | static int __init tps65910_gpio_init(void) |
178 | { |
179 | return platform_driver_register(&tps65910_gpio_driver); |
180 | } |
181 | subsys_initcall(tps65910_gpio_init); |
182 | |