xref: /linux/drivers/pinctrl/intel/pinctrl-intel-platform.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 intel_padgroup *gpps;
94 	unsigned int group;
95 	size_t ngpps;
96 	u32 offset;
97 	int ret;
98 
99 	ret = device_property_read_u32(dev, "intc-gpio-pad-ownership-offset", &offset);
100 	if (ret)
101 		return ret;
102 	community->padown_offset = offset;
103 
104 	ret = device_property_read_u32(dev, "intc-gpio-pad-configuration-lock-offset", &offset);
105 	if (ret)
106 		return ret;
107 	community->padcfglock_offset = offset;
108 
109 	ret = device_property_read_u32(dev, "intc-gpio-host-software-pad-ownership-offset", &offset);
110 	if (ret)
111 		return ret;
112 	community->hostown_offset = offset;
113 
114 	ret = device_property_read_u32(dev, "intc-gpio-gpi-interrupt-status-offset", &offset);
115 	if (ret)
116 		return ret;
117 	community->is_offset = offset;
118 
119 	ret = device_property_read_u32(dev, "intc-gpio-gpi-interrupt-enable-offset", &offset);
120 	if (ret)
121 		return ret;
122 	community->ie_offset = offset;
123 
124 	ngpps = device_get_child_node_count(dev);
125 	if (!ngpps)
126 		return -ENODEV;
127 
128 	gpps = devm_kcalloc(dev, ngpps, sizeof(*gpps), GFP_KERNEL);
129 	if (!gpps)
130 		return -ENOMEM;
131 
132 	group = 0;
133 	device_for_each_child_node_scoped(dev, child) {
134 		struct intel_padgroup *gpp = &gpps[group];
135 
136 		gpp->reg_num = group;
137 
138 		ret = intel_platform_pinctrl_prepare_group(dev, child, gpp, pins);
139 		if (ret)
140 			return ret;
141 
142 		group++;
143 	}
144 
145 	community->ngpps = ngpps;
146 	community->gpps = gpps;
147 
148 	return 0;
149 }
150 
151 static int intel_platform_pinctrl_prepare_soc_data(struct device *dev,
152 						   struct intel_pinctrl_soc_data *data)
153 {
154 	struct intel_platform_pins pins = {};
155 	struct intel_community *communities;
156 	size_t ncommunities;
157 	unsigned int i;
158 	int ret;
159 
160 	/* Version 1.0 of the specification assumes only a single community per device node */
161 	ncommunities = 1;
162 	communities = devm_kcalloc(dev, ncommunities, sizeof(*communities), GFP_KERNEL);
163 	if (!communities)
164 		return -ENOMEM;
165 
166 	for (i = 0; i < ncommunities; i++) {
167 		struct intel_community *community = &communities[i];
168 
169 		community->barno = i;
170 		community->pin_base = pins.npins;
171 
172 		ret = intel_platform_pinctrl_prepare_community(dev, community, &pins);
173 		if (ret)
174 			return ret;
175 
176 		community->npins = pins.npins - community->pin_base;
177 	}
178 
179 	data->ncommunities = ncommunities;
180 	data->communities = communities;
181 
182 	data->npins = pins.npins;
183 	data->pins = pins.pins;
184 
185 	return 0;
186 }
187 
188 static int intel_platform_pinctrl_probe(struct platform_device *pdev)
189 {
190 	struct intel_pinctrl_soc_data *data;
191 	struct device *dev = &pdev->dev;
192 	int ret;
193 
194 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
195 	if (!data)
196 		return -ENOMEM;
197 
198 	ret = intel_platform_pinctrl_prepare_soc_data(dev, data);
199 	if (ret)
200 		return ret;
201 
202 	return intel_pinctrl_probe(pdev, data);
203 }
204 
205 static const struct acpi_device_id intel_platform_pinctrl_acpi_match[] = {
206 	{ "INTC105F" },
207 	{ }
208 };
209 MODULE_DEVICE_TABLE(acpi, intel_platform_pinctrl_acpi_match);
210 
211 static struct platform_driver intel_platform_pinctrl_driver = {
212 	.probe = intel_platform_pinctrl_probe,
213 	.driver = {
214 		.name = "intel-pinctrl",
215 		.acpi_match_table = intel_platform_pinctrl_acpi_match,
216 		.pm = pm_sleep_ptr(&intel_pinctrl_pm_ops),
217 	},
218 };
219 module_platform_driver(intel_platform_pinctrl_driver);
220 
221 MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
222 MODULE_DESCRIPTION("Intel PCH pinctrl/GPIO driver");
223 MODULE_LICENSE("GPL v2");
224 MODULE_IMPORT_NS(PINCTRL_INTEL);
225