1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * AMD Promontory GPIO driver 4 * 5 * Copyright (C) 2015 ASMedia Technology Inc. 6 * Author: YD Tseng <yd_tseng@asmedia.com.tw> 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/gpio/driver.h> 12 #include <linux/spinlock.h> 13 #include <linux/acpi.h> 14 #include <linux/platform_device.h> 15 16 #define PT_TOTAL_GPIO 8 17 #define PT_TOTAL_GPIO_EX 24 18 19 /* PCI-E MMIO register offsets */ 20 #define PT_DIRECTION_REG 0x00 21 #define PT_INPUTDATA_REG 0x04 22 #define PT_OUTPUTDATA_REG 0x08 23 #define PT_CLOCKRATE_REG 0x0C 24 #define PT_SYNC_REG 0x28 25 26 struct pt_gpio_chip { 27 struct gpio_chip gc; 28 void __iomem *reg_base; 29 }; 30 31 static int pt_gpio_request(struct gpio_chip *gc, unsigned offset) 32 { 33 struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc); 34 unsigned long flags; 35 u32 using_pins; 36 37 dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset); 38 39 raw_spin_lock_irqsave(&gc->bgpio_lock, flags); 40 41 using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 42 if (using_pins & BIT(offset)) { 43 dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n", 44 offset); 45 raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); 46 return -EINVAL; 47 } 48 49 writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG); 50 51 raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); 52 53 return 0; 54 } 55 56 static void pt_gpio_free(struct gpio_chip *gc, unsigned offset) 57 { 58 struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc); 59 unsigned long flags; 60 u32 using_pins; 61 62 raw_spin_lock_irqsave(&gc->bgpio_lock, flags); 63 64 using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 65 using_pins &= ~BIT(offset); 66 writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG); 67 68 raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); 69 70 dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset); 71 } 72 73 static int pt_gpio_probe(struct platform_device *pdev) 74 { 75 struct device *dev = &pdev->dev; 76 struct pt_gpio_chip *pt_gpio; 77 int ret = 0; 78 79 if (!ACPI_COMPANION(dev)) { 80 dev_err(dev, "PT GPIO device node not found\n"); 81 return -ENODEV; 82 } 83 84 pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL); 85 if (!pt_gpio) 86 return -ENOMEM; 87 88 pt_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0); 89 if (IS_ERR(pt_gpio->reg_base)) { 90 dev_err(dev, "Failed to map MMIO resource for PT GPIO.\n"); 91 return PTR_ERR(pt_gpio->reg_base); 92 } 93 94 ret = bgpio_init(&pt_gpio->gc, dev, 4, 95 pt_gpio->reg_base + PT_INPUTDATA_REG, 96 pt_gpio->reg_base + PT_OUTPUTDATA_REG, NULL, 97 pt_gpio->reg_base + PT_DIRECTION_REG, NULL, 98 BGPIOF_READ_OUTPUT_REG_SET); 99 if (ret) { 100 dev_err(dev, "bgpio_init failed\n"); 101 return ret; 102 } 103 104 pt_gpio->gc.owner = THIS_MODULE; 105 pt_gpio->gc.request = pt_gpio_request; 106 pt_gpio->gc.free = pt_gpio_free; 107 pt_gpio->gc.ngpio = (uintptr_t)device_get_match_data(dev); 108 109 ret = devm_gpiochip_add_data(dev, &pt_gpio->gc, pt_gpio); 110 if (ret) { 111 dev_err(dev, "Failed to register GPIO lib\n"); 112 return ret; 113 } 114 115 platform_set_drvdata(pdev, pt_gpio); 116 117 /* initialize register setting */ 118 writel(0, pt_gpio->reg_base + PT_SYNC_REG); 119 writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG); 120 121 dev_dbg(dev, "PT GPIO driver loaded\n"); 122 return ret; 123 } 124 125 static const struct acpi_device_id pt_gpio_acpi_match[] = { 126 { "AMDF030", PT_TOTAL_GPIO }, 127 { "AMDIF030", PT_TOTAL_GPIO }, 128 { "AMDIF031", PT_TOTAL_GPIO_EX }, 129 { }, 130 }; 131 MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match); 132 133 static struct platform_driver pt_gpio_driver = { 134 .driver = { 135 .name = "pt-gpio", 136 .acpi_match_table = ACPI_PTR(pt_gpio_acpi_match), 137 }, 138 .probe = pt_gpio_probe, 139 }; 140 141 module_platform_driver(pt_gpio_driver); 142 143 MODULE_LICENSE("GPL"); 144 MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>"); 145 MODULE_DESCRIPTION("AMD Promontory GPIO Driver"); 146