xref: /linux/drivers/pinctrl/intel/pinctrl-intel-platform.c (revision bfb921b2a9d5d1123d1d10b196a39db629ddef87)
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);
226