225 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Intel PCH pinctrl/GPIO driver4 *5 * Copyright (C) 2021-2023, Intel Corporation6 * 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 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 151static 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 188static 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 205static const struct acpi_device_id intel_platform_pinctrl_acpi_match[] = {206 { "INTC105F" },207 { }208};209MODULE_DEVICE_TABLE(acpi, intel_platform_pinctrl_acpi_match);210 211static 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};219module_platform_driver(intel_platform_pinctrl_driver);220 221MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");222MODULE_DESCRIPTION("Intel PCH pinctrl/GPIO driver");223MODULE_LICENSE("GPL v2");224MODULE_IMPORT_NS(PINCTRL_INTEL);225