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
20struct intel_platform_pins {
21 struct pinctrl_pin_desc *pins;
22 size_t npins;
23};
24
25static 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, prefix: name, n: size);
34 if (IS_ERR(ptr: pin_names))
35 return PTR_ERR(ptr: pin_names);
36
37 descs = devm_krealloc_array(dev, p: pins->pins, new_n: base + size, new_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(str: pin_name, old: '-', new: '_');
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
60static 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(fwnode: child, propname: "intc-gpio-group-name", val: &name);
71 if (ret)
72 return ret;
73
74 ret = fwnode_property_read_u32(fwnode: child, propname: "intc-gpio-pad-count", val: &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
89static 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, propname: "intc-gpio-pad-ownership-offset", val: &offset);
101 if (ret)
102 return ret;
103 community->padown_offset = offset;
104
105 ret = device_property_read_u32(dev, propname: "intc-gpio-pad-configuration-lock-offset", val: &offset);
106 if (ret)
107 return ret;
108 community->padcfglock_offset = offset;
109
110 ret = device_property_read_u32(dev, propname: "intc-gpio-host-software-pad-ownership-offset", val: &offset);
111 if (ret)
112 return ret;
113 community->hostown_offset = offset;
114
115 ret = device_property_read_u32(dev, propname: "intc-gpio-gpi-interrupt-status-offset", val: &offset);
116 if (ret)
117 return ret;
118 community->is_offset = offset;
119
120 ret = device_property_read_u32(dev, propname: "intc-gpio-gpi-interrupt-enable-offset", val: &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, n: ngpps, size: 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
152static 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, n: ncommunities, size: 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: &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
189static 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, size: 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, soc_data: data);
204}
205
206static const struct acpi_device_id intel_platform_pinctrl_acpi_match[] = {
207 { "INTC105F" },
208 { }
209};
210MODULE_DEVICE_TABLE(acpi, intel_platform_pinctrl_acpi_match);
211
212static 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};
220module_platform_driver(intel_platform_pinctrl_driver);
221
222MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
223MODULE_DESCRIPTION("Intel PCH pinctrl/GPIO driver");
224MODULE_LICENSE("GPL v2");
225MODULE_IMPORT_NS(PINCTRL_INTEL);
226

source code of linux/drivers/pinctrl/intel/pinctrl-intel-platform.c