1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26057d40fSYD Tseng /* 36057d40fSYD Tseng * AMD Promontory GPIO driver 46057d40fSYD Tseng * 56057d40fSYD Tseng * Copyright (C) 2015 ASMedia Technology Inc. 66057d40fSYD Tseng * Author: YD Tseng <yd_tseng@asmedia.com.tw> 76057d40fSYD Tseng */ 86057d40fSYD Tseng 96057d40fSYD Tseng #include <linux/kernel.h> 106057d40fSYD Tseng #include <linux/module.h> 116057d40fSYD Tseng #include <linux/gpio/driver.h> 126057d40fSYD Tseng #include <linux/spinlock.h> 136057d40fSYD Tseng #include <linux/acpi.h> 146057d40fSYD Tseng #include <linux/platform_device.h> 156057d40fSYD Tseng 166057d40fSYD Tseng #define PT_TOTAL_GPIO 8 176057d40fSYD Tseng 186057d40fSYD Tseng /* PCI-E MMIO register offsets */ 196057d40fSYD Tseng #define PT_DIRECTION_REG 0x00 206057d40fSYD Tseng #define PT_INPUTDATA_REG 0x04 216057d40fSYD Tseng #define PT_OUTPUTDATA_REG 0x08 226057d40fSYD Tseng #define PT_CLOCKRATE_REG 0x0C 236057d40fSYD Tseng #define PT_SYNC_REG 0x28 246057d40fSYD Tseng 256057d40fSYD Tseng struct pt_gpio_chip { 266057d40fSYD Tseng struct gpio_chip gc; 276057d40fSYD Tseng void __iomem *reg_base; 286057d40fSYD Tseng }; 296057d40fSYD Tseng 306057d40fSYD Tseng static int pt_gpio_request(struct gpio_chip *gc, unsigned offset) 316057d40fSYD Tseng { 32fb722887SLinus Walleij struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc); 336057d40fSYD Tseng unsigned long flags; 346057d40fSYD Tseng u32 using_pins; 356057d40fSYD Tseng 3658383c78SLinus Walleij dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset); 376057d40fSYD Tseng 38574b782eSAxel Lin spin_lock_irqsave(&gc->bgpio_lock, flags); 396057d40fSYD Tseng 406057d40fSYD Tseng using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 416057d40fSYD Tseng if (using_pins & BIT(offset)) { 4258383c78SLinus Walleij dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n", 436057d40fSYD Tseng offset); 44574b782eSAxel Lin spin_unlock_irqrestore(&gc->bgpio_lock, flags); 456057d40fSYD Tseng return -EINVAL; 466057d40fSYD Tseng } 476057d40fSYD Tseng 486057d40fSYD Tseng writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG); 496057d40fSYD Tseng 50574b782eSAxel Lin spin_unlock_irqrestore(&gc->bgpio_lock, flags); 516057d40fSYD Tseng 526057d40fSYD Tseng return 0; 536057d40fSYD Tseng } 546057d40fSYD Tseng 556057d40fSYD Tseng static void pt_gpio_free(struct gpio_chip *gc, unsigned offset) 566057d40fSYD Tseng { 57fb722887SLinus Walleij struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc); 586057d40fSYD Tseng unsigned long flags; 596057d40fSYD Tseng u32 using_pins; 606057d40fSYD Tseng 61574b782eSAxel Lin spin_lock_irqsave(&gc->bgpio_lock, flags); 626057d40fSYD Tseng 636057d40fSYD Tseng using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 646057d40fSYD Tseng using_pins &= ~BIT(offset); 656057d40fSYD Tseng writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG); 666057d40fSYD Tseng 67574b782eSAxel Lin spin_unlock_irqrestore(&gc->bgpio_lock, flags); 686057d40fSYD Tseng 6958383c78SLinus Walleij dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset); 706057d40fSYD Tseng } 716057d40fSYD Tseng 726057d40fSYD Tseng static int pt_gpio_probe(struct platform_device *pdev) 736057d40fSYD Tseng { 746057d40fSYD Tseng struct device *dev = &pdev->dev; 756057d40fSYD Tseng struct pt_gpio_chip *pt_gpio; 766057d40fSYD Tseng int ret = 0; 776057d40fSYD Tseng 78*f0b2731bSRafael J. Wysocki if (!ACPI_COMPANION(dev)) { 796057d40fSYD Tseng dev_err(dev, "PT GPIO device node not found\n"); 806057d40fSYD Tseng return -ENODEV; 816057d40fSYD Tseng } 826057d40fSYD Tseng 836057d40fSYD Tseng pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL); 846057d40fSYD Tseng if (!pt_gpio) 856057d40fSYD Tseng return -ENOMEM; 866057d40fSYD Tseng 87bb17a27aSEnrico Weigelt, metux IT consult pt_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0); 886057d40fSYD Tseng if (IS_ERR(pt_gpio->reg_base)) { 892b3fee36SEnrico Weigelt, metux IT consult dev_err(dev, "Failed to map MMIO resource for PT GPIO.\n"); 906057d40fSYD Tseng return PTR_ERR(pt_gpio->reg_base); 916057d40fSYD Tseng } 926057d40fSYD Tseng 93574b782eSAxel Lin ret = bgpio_init(&pt_gpio->gc, dev, 4, 94574b782eSAxel Lin pt_gpio->reg_base + PT_INPUTDATA_REG, 95574b782eSAxel Lin pt_gpio->reg_base + PT_OUTPUTDATA_REG, NULL, 96574b782eSAxel Lin pt_gpio->reg_base + PT_DIRECTION_REG, NULL, 97574b782eSAxel Lin BGPIOF_READ_OUTPUT_REG_SET); 98574b782eSAxel Lin if (ret) { 992b3fee36SEnrico Weigelt, metux IT consult dev_err(dev, "bgpio_init failed\n"); 100574b782eSAxel Lin return ret; 101574b782eSAxel Lin } 1026057d40fSYD Tseng 1036057d40fSYD Tseng pt_gpio->gc.owner = THIS_MODULE; 1046057d40fSYD Tseng pt_gpio->gc.request = pt_gpio_request; 1056057d40fSYD Tseng pt_gpio->gc.free = pt_gpio_free; 1066057d40fSYD Tseng pt_gpio->gc.ngpio = PT_TOTAL_GPIO; 1076057d40fSYD Tseng #if defined(CONFIG_OF_GPIO) 1082b3fee36SEnrico Weigelt, metux IT consult pt_gpio->gc.of_node = dev->of_node; 1096057d40fSYD Tseng #endif 110fb722887SLinus Walleij ret = gpiochip_add_data(&pt_gpio->gc, pt_gpio); 1116057d40fSYD Tseng if (ret) { 1122b3fee36SEnrico Weigelt, metux IT consult dev_err(dev, "Failed to register GPIO lib\n"); 1136057d40fSYD Tseng return ret; 1146057d40fSYD Tseng } 1156057d40fSYD Tseng 1166057d40fSYD Tseng platform_set_drvdata(pdev, pt_gpio); 1176057d40fSYD Tseng 1186057d40fSYD Tseng /* initialize register setting */ 1196057d40fSYD Tseng writel(0, pt_gpio->reg_base + PT_SYNC_REG); 1206057d40fSYD Tseng writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG); 1216057d40fSYD Tseng 1222b3fee36SEnrico Weigelt, metux IT consult dev_dbg(dev, "PT GPIO driver loaded\n"); 1236057d40fSYD Tseng return ret; 1246057d40fSYD Tseng } 1256057d40fSYD Tseng 1266057d40fSYD Tseng static int pt_gpio_remove(struct platform_device *pdev) 1276057d40fSYD Tseng { 1286057d40fSYD Tseng struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev); 1296057d40fSYD Tseng 1306057d40fSYD Tseng gpiochip_remove(&pt_gpio->gc); 1316057d40fSYD Tseng 1326057d40fSYD Tseng return 0; 1336057d40fSYD Tseng } 1346057d40fSYD Tseng 1356057d40fSYD Tseng static const struct acpi_device_id pt_gpio_acpi_match[] = { 1366057d40fSYD Tseng { "AMDF030", 0 }, 137ca27379fSYD Tseng { "AMDIF030", 0 }, 1386057d40fSYD Tseng { }, 1396057d40fSYD Tseng }; 1406057d40fSYD Tseng MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match); 1416057d40fSYD Tseng 1426057d40fSYD Tseng static struct platform_driver pt_gpio_driver = { 1436057d40fSYD Tseng .driver = { 1446057d40fSYD Tseng .name = "pt-gpio", 1456057d40fSYD Tseng .acpi_match_table = ACPI_PTR(pt_gpio_acpi_match), 1466057d40fSYD Tseng }, 1476057d40fSYD Tseng .probe = pt_gpio_probe, 1486057d40fSYD Tseng .remove = pt_gpio_remove, 1496057d40fSYD Tseng }; 1506057d40fSYD Tseng 1516057d40fSYD Tseng module_platform_driver(pt_gpio_driver); 1526057d40fSYD Tseng 1536057d40fSYD Tseng MODULE_LICENSE("GPL"); 1546057d40fSYD Tseng MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>"); 1556057d40fSYD Tseng MODULE_DESCRIPTION("AMD Promontory GPIO Driver"); 156