1 /* 2 * AMD Promontory GPIO driver 3 * 4 * Copyright (C) 2015 ASMedia Technology Inc. 5 * Author: YD Tseng <yd_tseng@asmedia.com.tw> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/gpio/driver.h> 15 #include <linux/spinlock.h> 16 #include <linux/acpi.h> 17 #include <linux/platform_device.h> 18 19 #define PT_TOTAL_GPIO 8 20 21 /* PCI-E MMIO register offsets */ 22 #define PT_DIRECTION_REG 0x00 23 #define PT_INPUTDATA_REG 0x04 24 #define PT_OUTPUTDATA_REG 0x08 25 #define PT_CLOCKRATE_REG 0x0C 26 #define PT_SYNC_REG 0x28 27 28 struct pt_gpio_chip { 29 struct gpio_chip gc; 30 void __iomem *reg_base; 31 spinlock_t lock; 32 }; 33 34 #define to_pt_gpio(c) container_of(c, struct pt_gpio_chip, gc) 35 36 static int pt_gpio_request(struct gpio_chip *gc, unsigned offset) 37 { 38 struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc); 39 unsigned long flags; 40 u32 using_pins; 41 42 dev_dbg(gc->dev, "pt_gpio_request offset=%x\n", offset); 43 44 spin_lock_irqsave(&pt_gpio->lock, flags); 45 46 using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 47 if (using_pins & BIT(offset)) { 48 dev_warn(gc->dev, "PT GPIO pin %x reconfigured\n", 49 offset); 50 spin_unlock_irqrestore(&pt_gpio->lock, flags); 51 return -EINVAL; 52 } 53 54 writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG); 55 56 spin_unlock_irqrestore(&pt_gpio->lock, flags); 57 58 return 0; 59 } 60 61 static void pt_gpio_free(struct gpio_chip *gc, unsigned offset) 62 { 63 struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc); 64 unsigned long flags; 65 u32 using_pins; 66 67 spin_lock_irqsave(&pt_gpio->lock, flags); 68 69 using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG); 70 using_pins &= ~BIT(offset); 71 writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG); 72 73 spin_unlock_irqrestore(&pt_gpio->lock, flags); 74 75 dev_dbg(gc->dev, "pt_gpio_free offset=%x\n", offset); 76 } 77 78 static void pt_gpio_set_value(struct gpio_chip *gc, unsigned offset, int value) 79 { 80 struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc); 81 unsigned long flags; 82 u32 data; 83 84 dev_dbg(gc->dev, "pt_gpio_set_value offset=%x, value=%x\n", 85 offset, value); 86 87 spin_lock_irqsave(&pt_gpio->lock, flags); 88 89 data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG); 90 data &= ~BIT(offset); 91 if (value) 92 data |= BIT(offset); 93 writel(data, pt_gpio->reg_base + PT_OUTPUTDATA_REG); 94 95 spin_unlock_irqrestore(&pt_gpio->lock, flags); 96 } 97 98 static int pt_gpio_get_value(struct gpio_chip *gc, unsigned offset) 99 { 100 struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc); 101 unsigned long flags; 102 u32 data; 103 104 spin_lock_irqsave(&pt_gpio->lock, flags); 105 106 data = readl(pt_gpio->reg_base + PT_DIRECTION_REG); 107 108 /* configure as output */ 109 if (data & BIT(offset)) 110 data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG); 111 else /* configure as input */ 112 data = readl(pt_gpio->reg_base + PT_INPUTDATA_REG); 113 114 spin_unlock_irqrestore(&pt_gpio->lock, flags); 115 116 data >>= offset; 117 data &= 1; 118 119 dev_dbg(gc->dev, "pt_gpio_get_value offset=%x, value=%x\n", 120 offset, data); 121 122 return data; 123 } 124 125 static int pt_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 126 { 127 struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc); 128 unsigned long flags; 129 u32 data; 130 131 dev_dbg(gc->dev, "pt_gpio_dirction_input offset=%x\n", offset); 132 133 spin_lock_irqsave(&pt_gpio->lock, flags); 134 135 data = readl(pt_gpio->reg_base + PT_DIRECTION_REG); 136 data &= ~BIT(offset); 137 writel(data, pt_gpio->reg_base + PT_DIRECTION_REG); 138 139 spin_unlock_irqrestore(&pt_gpio->lock, flags); 140 141 return 0; 142 } 143 144 static int pt_gpio_direction_output(struct gpio_chip *gc, 145 unsigned offset, int value) 146 { 147 struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc); 148 unsigned long flags; 149 u32 data; 150 151 dev_dbg(gc->dev, "pt_gpio_direction_output offset=%x, value=%x\n", 152 offset, value); 153 154 spin_lock_irqsave(&pt_gpio->lock, flags); 155 156 data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG); 157 if (value) 158 data |= BIT(offset); 159 else 160 data &= ~BIT(offset); 161 writel(data, pt_gpio->reg_base + PT_OUTPUTDATA_REG); 162 163 data = readl(pt_gpio->reg_base + PT_DIRECTION_REG); 164 data |= BIT(offset); 165 writel(data, pt_gpio->reg_base + PT_DIRECTION_REG); 166 167 spin_unlock_irqrestore(&pt_gpio->lock, flags); 168 169 return 0; 170 } 171 172 static int pt_gpio_probe(struct platform_device *pdev) 173 { 174 struct device *dev = &pdev->dev; 175 struct acpi_device *acpi_dev; 176 acpi_handle handle = ACPI_HANDLE(dev); 177 struct pt_gpio_chip *pt_gpio; 178 struct resource *res_mem; 179 int ret = 0; 180 181 if (acpi_bus_get_device(handle, &acpi_dev)) { 182 dev_err(dev, "PT GPIO device node not found\n"); 183 return -ENODEV; 184 } 185 186 pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL); 187 if (!pt_gpio) 188 return -ENOMEM; 189 190 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 191 if (!res_mem) { 192 dev_err(&pdev->dev, "Failed to get MMIO resource for PT GPIO.\n"); 193 return -EINVAL; 194 } 195 pt_gpio->reg_base = devm_ioremap_resource(dev, res_mem); 196 if (IS_ERR(pt_gpio->reg_base)) { 197 dev_err(&pdev->dev, "Failed to map MMIO resource for PT GPIO.\n"); 198 return PTR_ERR(pt_gpio->reg_base); 199 } 200 201 spin_lock_init(&pt_gpio->lock); 202 203 pt_gpio->gc.label = pdev->name; 204 pt_gpio->gc.owner = THIS_MODULE; 205 pt_gpio->gc.dev = dev; 206 pt_gpio->gc.request = pt_gpio_request; 207 pt_gpio->gc.free = pt_gpio_free; 208 pt_gpio->gc.direction_input = pt_gpio_direction_input; 209 pt_gpio->gc.direction_output = pt_gpio_direction_output; 210 pt_gpio->gc.get = pt_gpio_get_value; 211 pt_gpio->gc.set = pt_gpio_set_value; 212 pt_gpio->gc.base = -1; 213 pt_gpio->gc.ngpio = PT_TOTAL_GPIO; 214 #if defined(CONFIG_OF_GPIO) 215 pt_gpio->gc.of_node = pdev->dev.of_node; 216 #endif 217 ret = gpiochip_add(&pt_gpio->gc); 218 if (ret) { 219 dev_err(&pdev->dev, "Failed to register GPIO lib\n"); 220 return ret; 221 } 222 223 platform_set_drvdata(pdev, pt_gpio); 224 225 /* initialize register setting */ 226 writel(0, pt_gpio->reg_base + PT_SYNC_REG); 227 writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG); 228 229 dev_dbg(&pdev->dev, "PT GPIO driver loaded\n"); 230 return ret; 231 } 232 233 static int pt_gpio_remove(struct platform_device *pdev) 234 { 235 struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev); 236 237 gpiochip_remove(&pt_gpio->gc); 238 239 return 0; 240 } 241 242 static const struct acpi_device_id pt_gpio_acpi_match[] = { 243 { "AMDF030", 0 }, 244 { }, 245 }; 246 MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match); 247 248 static struct platform_driver pt_gpio_driver = { 249 .driver = { 250 .name = "pt-gpio", 251 .acpi_match_table = ACPI_PTR(pt_gpio_acpi_match), 252 }, 253 .probe = pt_gpio_probe, 254 .remove = pt_gpio_remove, 255 }; 256 257 module_platform_driver(pt_gpio_driver); 258 259 MODULE_LICENSE("GPL"); 260 MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>"); 261 MODULE_DESCRIPTION("AMD Promontory GPIO Driver"); 262