18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AMD Promontory GPIO driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 ASMedia Technology Inc. 68c2ecf20Sopenharmony_ci * Author: YD Tseng <yd_tseng@asmedia.com.tw> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci#include <linux/acpi.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define PT_TOTAL_GPIO 8 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* PCI-E MMIO register offsets */ 198c2ecf20Sopenharmony_ci#define PT_DIRECTION_REG 0x00 208c2ecf20Sopenharmony_ci#define PT_INPUTDATA_REG 0x04 218c2ecf20Sopenharmony_ci#define PT_OUTPUTDATA_REG 0x08 228c2ecf20Sopenharmony_ci#define PT_CLOCKRATE_REG 0x0C 238c2ecf20Sopenharmony_ci#define PT_SYNC_REG 0x28 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct pt_gpio_chip { 268c2ecf20Sopenharmony_ci struct gpio_chip gc; 278c2ecf20Sopenharmony_ci void __iomem *reg_base; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int pt_gpio_request(struct gpio_chip *gc, unsigned offset) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc); 338c2ecf20Sopenharmony_ci unsigned long flags; 348c2ecf20Sopenharmony_ci u32 using_pins; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci spin_lock_irqsave(&gc->bgpio_lock, flags); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 418c2ecf20Sopenharmony_ci if (using_pins & BIT(offset)) { 428c2ecf20Sopenharmony_ci dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n", 438c2ecf20Sopenharmony_ci offset); 448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gc->bgpio_lock, flags); 458c2ecf20Sopenharmony_ci return -EINVAL; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gc->bgpio_lock, flags); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void pt_gpio_free(struct gpio_chip *gc, unsigned offset) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc); 588c2ecf20Sopenharmony_ci unsigned long flags; 598c2ecf20Sopenharmony_ci u32 using_pins; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci spin_lock_irqsave(&gc->bgpio_lock, flags); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 648c2ecf20Sopenharmony_ci using_pins &= ~BIT(offset); 658c2ecf20Sopenharmony_ci writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gc->bgpio_lock, flags); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int pt_gpio_probe(struct platform_device *pdev) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 758c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev; 768c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 778c2ecf20Sopenharmony_ci struct pt_gpio_chip *pt_gpio; 788c2ecf20Sopenharmony_ci int ret = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (acpi_bus_get_device(handle, &acpi_dev)) { 818c2ecf20Sopenharmony_ci dev_err(dev, "PT GPIO device node not found\n"); 828c2ecf20Sopenharmony_ci return -ENODEV; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL); 868c2ecf20Sopenharmony_ci if (!pt_gpio) 878c2ecf20Sopenharmony_ci return -ENOMEM; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci pt_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0); 908c2ecf20Sopenharmony_ci if (IS_ERR(pt_gpio->reg_base)) { 918c2ecf20Sopenharmony_ci dev_err(dev, "Failed to map MMIO resource for PT GPIO.\n"); 928c2ecf20Sopenharmony_ci return PTR_ERR(pt_gpio->reg_base); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ret = bgpio_init(&pt_gpio->gc, dev, 4, 968c2ecf20Sopenharmony_ci pt_gpio->reg_base + PT_INPUTDATA_REG, 978c2ecf20Sopenharmony_ci pt_gpio->reg_base + PT_OUTPUTDATA_REG, NULL, 988c2ecf20Sopenharmony_ci pt_gpio->reg_base + PT_DIRECTION_REG, NULL, 998c2ecf20Sopenharmony_ci BGPIOF_READ_OUTPUT_REG_SET); 1008c2ecf20Sopenharmony_ci if (ret) { 1018c2ecf20Sopenharmony_ci dev_err(dev, "bgpio_init failed\n"); 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci pt_gpio->gc.owner = THIS_MODULE; 1068c2ecf20Sopenharmony_ci pt_gpio->gc.request = pt_gpio_request; 1078c2ecf20Sopenharmony_ci pt_gpio->gc.free = pt_gpio_free; 1088c2ecf20Sopenharmony_ci pt_gpio->gc.ngpio = PT_TOTAL_GPIO; 1098c2ecf20Sopenharmony_ci#if defined(CONFIG_OF_GPIO) 1108c2ecf20Sopenharmony_ci pt_gpio->gc.of_node = dev->of_node; 1118c2ecf20Sopenharmony_ci#endif 1128c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&pt_gpio->gc, pt_gpio); 1138c2ecf20Sopenharmony_ci if (ret) { 1148c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register GPIO lib\n"); 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pt_gpio); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* initialize register setting */ 1218c2ecf20Sopenharmony_ci writel(0, pt_gpio->reg_base + PT_SYNC_REG); 1228c2ecf20Sopenharmony_ci writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci dev_dbg(dev, "PT GPIO driver loaded\n"); 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int pt_gpio_remove(struct platform_device *pdev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci gpiochip_remove(&pt_gpio->gc); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct acpi_device_id pt_gpio_acpi_match[] = { 1388c2ecf20Sopenharmony_ci { "AMDF030", 0 }, 1398c2ecf20Sopenharmony_ci { "AMDIF030", 0 }, 1408c2ecf20Sopenharmony_ci { }, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct platform_driver pt_gpio_driver = { 1458c2ecf20Sopenharmony_ci .driver = { 1468c2ecf20Sopenharmony_ci .name = "pt-gpio", 1478c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(pt_gpio_acpi_match), 1488c2ecf20Sopenharmony_ci }, 1498c2ecf20Sopenharmony_ci .probe = pt_gpio_probe, 1508c2ecf20Sopenharmony_ci .remove = pt_gpio_remove, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cimodule_platform_driver(pt_gpio_driver); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1568c2ecf20Sopenharmony_ciMODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>"); 1578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD Promontory GPIO Driver"); 158