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