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