GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / pinctrl / intel / pinctrl-intel-platform.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Intel PCH pinctrl/GPIO driver
4  *
5  * Copyright (C) 2021-2023, Intel Corporation
6  * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
7  */
8
9 #include <linux/mod_devicetable.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm.h>
13 #include <linux/property.h>
14 #include <linux/string_helpers.h>
15
16 #include <linux/pinctrl/pinctrl.h>
17
18 #include "pinctrl-intel.h"
19
20 struct intel_platform_pins {
21         struct pinctrl_pin_desc *pins;
22         size_t npins;
23 };
24
25 static int intel_platform_pinctrl_prepare_pins(struct device *dev, size_t base,
26                                                const char *name, u32 size,
27                                                struct intel_platform_pins *pins)
28 {
29         struct pinctrl_pin_desc *descs;
30         char **pin_names;
31         unsigned int i;
32
33         pin_names = devm_kasprintf_strarray(dev, name, size);
34         if (IS_ERR(pin_names))
35                 return PTR_ERR(pin_names);
36
37         descs = devm_krealloc_array(dev, pins->pins, base + size, sizeof(*descs), GFP_KERNEL);
38         if (!descs)
39                 return -ENOMEM;
40
41         for (i = 0; i < size; i++) {
42                 unsigned int pin_number = base + i;
43                 char *pin_name = pin_names[i];
44                 struct pinctrl_pin_desc *desc;
45
46                 /* Unify delimiter for pin name */
47                 strreplace(pin_name, '-', '_');
48
49                 desc = &descs[pin_number];
50                 desc->number = pin_number;
51                 desc->name = pin_name;
52         }
53
54         pins->pins = descs;
55         pins->npins = base + size;
56
57         return 0;
58 }
59
60 static int intel_platform_pinctrl_prepare_group(struct device *dev,
61                                                 struct fwnode_handle *child,
62                                                 struct intel_padgroup *gpp,
63                                                 struct intel_platform_pins *pins)
64 {
65         size_t base = pins->npins;
66         const char *name;
67         u32 size;
68         int ret;
69
70         ret = fwnode_property_read_string(child, "intc-gpio-group-name", &name);
71         if (ret)
72                 return ret;
73
74         ret = fwnode_property_read_u32(child, "intc-gpio-pad-count", &size);
75         if (ret)
76                 return ret;
77
78         ret = intel_platform_pinctrl_prepare_pins(dev, base, name, size, pins);
79         if (ret)
80                 return ret;
81
82         gpp->base = base;
83         gpp->size = size;
84         gpp->gpio_base = INTEL_GPIO_BASE_MATCH;
85
86         return 0;
87 }
88
89 static int intel_platform_pinctrl_prepare_community(struct device *dev,
90                                                     struct intel_community *community,
91                                                     struct intel_platform_pins *pins)
92 {
93         struct fwnode_handle *child;
94         struct intel_padgroup *gpps;
95         unsigned int group;
96         size_t ngpps;
97         u32 offset;
98         int ret;
99
100         ret = device_property_read_u32(dev, "intc-gpio-pad-ownership-offset", &offset);
101         if (ret)
102                 return ret;
103         community->padown_offset = offset;
104
105         ret = device_property_read_u32(dev, "intc-gpio-pad-configuration-lock-offset", &offset);
106         if (ret)
107                 return ret;
108         community->padcfglock_offset = offset;
109
110         ret = device_property_read_u32(dev, "intc-gpio-host-software-pad-ownership-offset", &offset);
111         if (ret)
112                 return ret;
113         community->hostown_offset = offset;
114
115         ret = device_property_read_u32(dev, "intc-gpio-gpi-interrupt-status-offset", &offset);
116         if (ret)
117                 return ret;
118         community->is_offset = offset;
119
120         ret = device_property_read_u32(dev, "intc-gpio-gpi-interrupt-enable-offset", &offset);
121         if (ret)
122                 return ret;
123         community->ie_offset = offset;
124
125         ngpps = device_get_child_node_count(dev);
126         if (!ngpps)
127                 return -ENODEV;
128
129         gpps = devm_kcalloc(dev, ngpps, sizeof(*gpps), GFP_KERNEL);
130         if (!gpps)
131                 return -ENOMEM;
132
133         group = 0;
134         device_for_each_child_node(dev, child) {
135                 struct intel_padgroup *gpp = &gpps[group];
136
137                 gpp->reg_num = group;
138
139                 ret = intel_platform_pinctrl_prepare_group(dev, child, gpp, pins);
140                 if (ret)
141                         return ret;
142
143                 group++;
144         }
145
146         community->ngpps = ngpps;
147         community->gpps = gpps;
148
149         return 0;
150 }
151
152 static int intel_platform_pinctrl_prepare_soc_data(struct device *dev,
153                                                    struct intel_pinctrl_soc_data *data)
154 {
155         struct intel_platform_pins pins = {};
156         struct intel_community *communities;
157         size_t ncommunities;
158         unsigned int i;
159         int ret;
160
161         /* Version 1.0 of the specification assumes only a single community per device node */
162         ncommunities = 1,
163         communities = devm_kcalloc(dev, ncommunities, sizeof(*communities), GFP_KERNEL);
164         if (!communities)
165                 return -ENOMEM;
166
167         for (i = 0; i < ncommunities; i++) {
168                 struct intel_community *community = &communities[i];
169
170                 community->barno = i;
171                 community->pin_base = pins.npins;
172
173                 ret = intel_platform_pinctrl_prepare_community(dev, community, &pins);
174                 if (ret)
175                         return ret;
176
177                 community->npins = pins.npins - community->pin_base;
178         }
179
180         data->ncommunities = ncommunities;
181         data->communities = communities;
182
183         data->npins = pins.npins;
184         data->pins = pins.pins;
185
186         return 0;
187 }
188
189 static int intel_platform_pinctrl_probe(struct platform_device *pdev)
190 {
191         struct intel_pinctrl_soc_data *data;
192         struct device *dev = &pdev->dev;
193         int ret;
194
195         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
196         if (!data)
197                 return -ENOMEM;
198
199         ret = intel_platform_pinctrl_prepare_soc_data(dev, data);
200         if (ret)
201                 return ret;
202
203         return intel_pinctrl_probe(pdev, data);
204 }
205
206 static const struct acpi_device_id intel_platform_pinctrl_acpi_match[] = {
207         { "INTC105F" },
208         { }
209 };
210 MODULE_DEVICE_TABLE(acpi, intel_platform_pinctrl_acpi_match);
211
212 static struct platform_driver intel_platform_pinctrl_driver = {
213         .probe = intel_platform_pinctrl_probe,
214         .driver = {
215                 .name = "intel-pinctrl",
216                 .acpi_match_table = intel_platform_pinctrl_acpi_match,
217                 .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops),
218         },
219 };
220 module_platform_driver(intel_platform_pinctrl_driver);
221
222 MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
223 MODULE_DESCRIPTION("Intel PCH pinctrl/GPIO driver");
224 MODULE_LICENSE("GPL v2");
225 MODULE_IMPORT_NS(PINCTRL_INTEL);