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, 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
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(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
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, "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
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, 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
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, 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
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