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