2 * AMD Promontory GPIO driver
4 * Copyright (C) 2015 ASMedia Technology Inc.
5 * Author: YD Tseng <yd_tseng@asmedia.com.tw>
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.
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>
19 #define PT_TOTAL_GPIO 8
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
30 void __iomem
*reg_base
;
34 #define to_pt_gpio(c) container_of(c, struct pt_gpio_chip, gc)
36 static int pt_gpio_request(struct gpio_chip
*gc
, unsigned offset
)
38 struct pt_gpio_chip
*pt_gpio
= to_pt_gpio(gc
);
42 dev_dbg(gc
->dev
, "pt_gpio_request offset=%x\n", offset
);
44 spin_lock_irqsave(&pt_gpio
->lock
, flags
);
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",
50 spin_unlock_irqrestore(&pt_gpio
->lock
, flags
);
54 writel(using_pins
| BIT(offset
), pt_gpio
->reg_base
+ PT_SYNC_REG
);
56 spin_unlock_irqrestore(&pt_gpio
->lock
, flags
);
61 static void pt_gpio_free(struct gpio_chip
*gc
, unsigned offset
)
63 struct pt_gpio_chip
*pt_gpio
= to_pt_gpio(gc
);
67 spin_lock_irqsave(&pt_gpio
->lock
, flags
);
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
);
73 spin_unlock_irqrestore(&pt_gpio
->lock
, flags
);
75 dev_dbg(gc
->dev
, "pt_gpio_free offset=%x\n", offset
);
78 static void pt_gpio_set_value(struct gpio_chip
*gc
, unsigned offset
, int value
)
80 struct pt_gpio_chip
*pt_gpio
= to_pt_gpio(gc
);
84 dev_dbg(gc
->dev
, "pt_gpio_set_value offset=%x, value=%x\n",
87 spin_lock_irqsave(&pt_gpio
->lock
, flags
);
89 data
= readl(pt_gpio
->reg_base
+ PT_OUTPUTDATA_REG
);
93 writel(data
, pt_gpio
->reg_base
+ PT_OUTPUTDATA_REG
);
95 spin_unlock_irqrestore(&pt_gpio
->lock
, flags
);
98 static int pt_gpio_get_value(struct gpio_chip
*gc
, unsigned offset
)
100 struct pt_gpio_chip
*pt_gpio
= to_pt_gpio(gc
);
104 spin_lock_irqsave(&pt_gpio
->lock
, flags
);
106 data
= readl(pt_gpio
->reg_base
+ PT_DIRECTION_REG
);
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
);
114 spin_unlock_irqrestore(&pt_gpio
->lock
, flags
);
119 dev_dbg(gc
->dev
, "pt_gpio_get_value offset=%x, value=%x\n",
125 static int pt_gpio_direction_input(struct gpio_chip
*gc
, unsigned offset
)
127 struct pt_gpio_chip
*pt_gpio
= to_pt_gpio(gc
);
131 dev_dbg(gc
->dev
, "pt_gpio_dirction_input offset=%x\n", offset
);
133 spin_lock_irqsave(&pt_gpio
->lock
, flags
);
135 data
= readl(pt_gpio
->reg_base
+ PT_DIRECTION_REG
);
136 data
&= ~BIT(offset
);
137 writel(data
, pt_gpio
->reg_base
+ PT_DIRECTION_REG
);
139 spin_unlock_irqrestore(&pt_gpio
->lock
, flags
);
144 static int pt_gpio_direction_output(struct gpio_chip
*gc
,
145 unsigned offset
, int value
)
147 struct pt_gpio_chip
*pt_gpio
= to_pt_gpio(gc
);
151 dev_dbg(gc
->dev
, "pt_gpio_direction_output offset=%x, value=%x\n",
154 spin_lock_irqsave(&pt_gpio
->lock
, flags
);
156 data
= readl(pt_gpio
->reg_base
+ PT_OUTPUTDATA_REG
);
160 data
&= ~BIT(offset
);
161 writel(data
, pt_gpio
->reg_base
+ PT_OUTPUTDATA_REG
);
163 data
= readl(pt_gpio
->reg_base
+ PT_DIRECTION_REG
);
165 writel(data
, pt_gpio
->reg_base
+ PT_DIRECTION_REG
);
167 spin_unlock_irqrestore(&pt_gpio
->lock
, flags
);
172 static int pt_gpio_probe(struct platform_device
*pdev
)
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
;
181 if (acpi_bus_get_device(handle
, &acpi_dev
)) {
182 dev_err(dev
, "PT GPIO device node not found\n");
186 pt_gpio
= devm_kzalloc(dev
, sizeof(struct pt_gpio_chip
), GFP_KERNEL
);
190 res_mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
192 dev_err(&pdev
->dev
, "Failed to get MMIO resource for PT GPIO.\n");
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
);
201 spin_lock_init(&pt_gpio
->lock
);
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
;
217 ret
= gpiochip_add(&pt_gpio
->gc
);
219 dev_err(&pdev
->dev
, "Failed to register GPIO lib\n");
223 platform_set_drvdata(pdev
, pt_gpio
);
225 /* initialize register setting */
226 writel(0, pt_gpio
->reg_base
+ PT_SYNC_REG
);
227 writel(0, pt_gpio
->reg_base
+ PT_CLOCKRATE_REG
);
229 dev_dbg(&pdev
->dev
, "PT GPIO driver loaded\n");
233 static int pt_gpio_remove(struct platform_device
*pdev
)
235 struct pt_gpio_chip
*pt_gpio
= platform_get_drvdata(pdev
);
237 gpiochip_remove(&pt_gpio
->gc
);
242 static const struct acpi_device_id pt_gpio_acpi_match
[] = {
246 MODULE_DEVICE_TABLE(acpi
, pt_gpio_acpi_match
);
248 static struct platform_driver pt_gpio_driver
= {
251 .acpi_match_table
= ACPI_PTR(pt_gpio_acpi_match
),
253 .probe
= pt_gpio_probe
,
254 .remove
= pt_gpio_remove
,
257 module_platform_driver(pt_gpio_driver
);
259 MODULE_LICENSE("GPL");
260 MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>");
261 MODULE_DESCRIPTION("AMD Promontory GPIO Driver");