18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2015, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * GPIO and pin control functions on this SOC are handled by the "TLMM"
68c2ecf20Sopenharmony_ci * device.  The driver which controls this device is pinctrl-msm.c.  Each
78c2ecf20Sopenharmony_ci * SOC with a TLMM is expected to create a client driver that registers
88c2ecf20Sopenharmony_ci * with pinctrl-msm.c.  This means that all TLMM drivers are pin control
98c2ecf20Sopenharmony_ci * drivers.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This pin control driver is intended to be used only an ACPI-enabled
128c2ecf20Sopenharmony_ci * system.  As such, UEFI will handle all pin control configuration, so
138c2ecf20Sopenharmony_ci * this driver does not provide pin control functions.  It is effectively
148c2ecf20Sopenharmony_ci * a GPIO-only driver.  The alternative is to duplicate the GPIO code of
158c2ecf20Sopenharmony_ci * pinctrl-msm.c into another driver.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
218c2ecf20Sopenharmony_ci#include <linux/acpi.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "pinctrl-msm.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* A maximum of 256 allows us to use a u8 array to hold the GPIO numbers */
268c2ecf20Sopenharmony_ci#define MAX_GPIOS	256
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* maximum size of each gpio name (enough room for "gpioXXX" + null) */
298c2ecf20Sopenharmony_ci#define NAME_SIZE	8
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int qdf2xxx_pinctrl_probe(struct platform_device *pdev)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct msm_pinctrl_soc_data *pinctrl;
348c2ecf20Sopenharmony_ci	struct pinctrl_pin_desc *pins;
358c2ecf20Sopenharmony_ci	struct msm_pingroup *groups;
368c2ecf20Sopenharmony_ci	char (*names)[NAME_SIZE];
378c2ecf20Sopenharmony_ci	unsigned int i;
388c2ecf20Sopenharmony_ci	u32 num_gpios;
398c2ecf20Sopenharmony_ci	unsigned int avail_gpios; /* The number of GPIOs we support */
408c2ecf20Sopenharmony_ci	u8 gpios[MAX_GPIOS];      /* An array of supported GPIOs */
418c2ecf20Sopenharmony_ci	int ret;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Query the number of GPIOs from ACPI */
448c2ecf20Sopenharmony_ci	ret = device_property_read_u32(&pdev->dev, "num-gpios", &num_gpios);
458c2ecf20Sopenharmony_ci	if (ret < 0) {
468c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "missing 'num-gpios' property\n");
478c2ecf20Sopenharmony_ci		return ret;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci	if (!num_gpios || num_gpios > MAX_GPIOS) {
508c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "invalid 'num-gpios' property\n");
518c2ecf20Sopenharmony_ci		return -ENODEV;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* The number of GPIOs in the approved list */
558c2ecf20Sopenharmony_ci	ret = device_property_count_u8(&pdev->dev, "gpios");
568c2ecf20Sopenharmony_ci	if (ret < 0) {
578c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "missing 'gpios' property\n");
588c2ecf20Sopenharmony_ci		return ret;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * The number of available GPIOs should be non-zero, and no
628c2ecf20Sopenharmony_ci	 * more than the total number of GPIOS.
638c2ecf20Sopenharmony_ci	 */
648c2ecf20Sopenharmony_ci	if (!ret || ret > num_gpios) {
658c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "invalid 'gpios' property\n");
668c2ecf20Sopenharmony_ci		return -ENODEV;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci	avail_gpios = ret;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret = device_property_read_u8_array(&pdev->dev, "gpios", gpios,
718c2ecf20Sopenharmony_ci					    avail_gpios);
728c2ecf20Sopenharmony_ci	if (ret < 0) {
738c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not read list of GPIOs\n");
748c2ecf20Sopenharmony_ci		return ret;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
788c2ecf20Sopenharmony_ci	pins = devm_kcalloc(&pdev->dev, num_gpios,
798c2ecf20Sopenharmony_ci		sizeof(struct pinctrl_pin_desc), GFP_KERNEL);
808c2ecf20Sopenharmony_ci	groups = devm_kcalloc(&pdev->dev, num_gpios,
818c2ecf20Sopenharmony_ci		sizeof(struct msm_pingroup), GFP_KERNEL);
828c2ecf20Sopenharmony_ci	names = devm_kcalloc(&pdev->dev, avail_gpios, NAME_SIZE, GFP_KERNEL);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (!pinctrl || !pins || !groups || !names)
858c2ecf20Sopenharmony_ci		return -ENOMEM;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * Initialize the array.  GPIOs not listed in the 'gpios' array
898c2ecf20Sopenharmony_ci	 * still need a number, but nothing else.
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci	for (i = 0; i < num_gpios; i++) {
928c2ecf20Sopenharmony_ci		pins[i].number = i;
938c2ecf20Sopenharmony_ci		groups[i].pins = &pins[i].number;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* Populate the entries that are meant to be exposed as GPIOs. */
978c2ecf20Sopenharmony_ci	for (i = 0; i < avail_gpios; i++) {
988c2ecf20Sopenharmony_ci		unsigned int gpio = gpios[i];
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		groups[gpio].npins = 1;
1018c2ecf20Sopenharmony_ci		snprintf(names[i], NAME_SIZE, "gpio%u", gpio);
1028c2ecf20Sopenharmony_ci		pins[gpio].name = names[i];
1038c2ecf20Sopenharmony_ci		groups[gpio].name = names[i];
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		groups[gpio].ctl_reg = 0x10000 * gpio;
1068c2ecf20Sopenharmony_ci		groups[gpio].io_reg = 0x04 + 0x10000 * gpio;
1078c2ecf20Sopenharmony_ci		groups[gpio].intr_cfg_reg = 0x08 + 0x10000 * gpio;
1088c2ecf20Sopenharmony_ci		groups[gpio].intr_status_reg = 0x0c + 0x10000 * gpio;
1098c2ecf20Sopenharmony_ci		groups[gpio].intr_target_reg = 0x08 + 0x10000 * gpio;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		groups[gpio].mux_bit = 2;
1128c2ecf20Sopenharmony_ci		groups[gpio].pull_bit = 0;
1138c2ecf20Sopenharmony_ci		groups[gpio].drv_bit = 6;
1148c2ecf20Sopenharmony_ci		groups[gpio].oe_bit = 9;
1158c2ecf20Sopenharmony_ci		groups[gpio].in_bit = 0;
1168c2ecf20Sopenharmony_ci		groups[gpio].out_bit = 1;
1178c2ecf20Sopenharmony_ci		groups[gpio].intr_enable_bit = 0;
1188c2ecf20Sopenharmony_ci		groups[gpio].intr_status_bit = 0;
1198c2ecf20Sopenharmony_ci		groups[gpio].intr_target_bit = 5;
1208c2ecf20Sopenharmony_ci		groups[gpio].intr_target_kpss_val = 1;
1218c2ecf20Sopenharmony_ci		groups[gpio].intr_raw_status_bit = 4;
1228c2ecf20Sopenharmony_ci		groups[gpio].intr_polarity_bit = 1;
1238c2ecf20Sopenharmony_ci		groups[gpio].intr_detection_bit = 2;
1248c2ecf20Sopenharmony_ci		groups[gpio].intr_detection_width = 2;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	pinctrl->pins = pins;
1288c2ecf20Sopenharmony_ci	pinctrl->groups = groups;
1298c2ecf20Sopenharmony_ci	pinctrl->npins = num_gpios;
1308c2ecf20Sopenharmony_ci	pinctrl->ngroups = num_gpios;
1318c2ecf20Sopenharmony_ci	pinctrl->ngpios = num_gpios;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return msm_pinctrl_probe(pdev, pinctrl);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic const struct acpi_device_id qdf2xxx_acpi_ids[] = {
1378c2ecf20Sopenharmony_ci	{"QCOM8002"},
1388c2ecf20Sopenharmony_ci	{},
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, qdf2xxx_acpi_ids);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic struct platform_driver qdf2xxx_pinctrl_driver = {
1438c2ecf20Sopenharmony_ci	.driver = {
1448c2ecf20Sopenharmony_ci		.name = "qdf2xxx-pinctrl",
1458c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(qdf2xxx_acpi_ids),
1468c2ecf20Sopenharmony_ci	},
1478c2ecf20Sopenharmony_ci	.probe = qdf2xxx_pinctrl_probe,
1488c2ecf20Sopenharmony_ci	.remove = msm_pinctrl_remove,
1498c2ecf20Sopenharmony_ci};
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int __init qdf2xxx_pinctrl_init(void)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	return platform_driver_register(&qdf2xxx_pinctrl_driver);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ciarch_initcall(qdf2xxx_pinctrl_init);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void __exit qdf2xxx_pinctrl_exit(void)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	platform_driver_unregister(&qdf2xxx_pinctrl_driver);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_cimodule_exit(qdf2xxx_pinctrl_exit);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Technologies QDF2xxx pin control driver");
1648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
165