xref: /openbmc/linux/drivers/gpio/gpio-amdpt.c (revision 58383c78)
16057d40fSYD Tseng /*
26057d40fSYD Tseng  * AMD Promontory GPIO driver
36057d40fSYD Tseng  *
46057d40fSYD Tseng  * Copyright (C) 2015 ASMedia Technology Inc.
56057d40fSYD Tseng  * Author: YD Tseng <yd_tseng@asmedia.com.tw>
66057d40fSYD Tseng  *
76057d40fSYD Tseng  * This program is free software; you can redistribute it and/or modify
86057d40fSYD Tseng  * it under the terms of the GNU General Public License version 2 as
96057d40fSYD Tseng  * published by the Free Software Foundation.
106057d40fSYD Tseng  */
116057d40fSYD Tseng 
126057d40fSYD Tseng #include <linux/kernel.h>
136057d40fSYD Tseng #include <linux/module.h>
146057d40fSYD Tseng #include <linux/gpio/driver.h>
156057d40fSYD Tseng #include <linux/spinlock.h>
166057d40fSYD Tseng #include <linux/acpi.h>
176057d40fSYD Tseng #include <linux/platform_device.h>
186057d40fSYD Tseng 
196057d40fSYD Tseng #define PT_TOTAL_GPIO 8
206057d40fSYD Tseng 
216057d40fSYD Tseng /* PCI-E MMIO register offsets */
226057d40fSYD Tseng #define PT_DIRECTION_REG   0x00
236057d40fSYD Tseng #define PT_INPUTDATA_REG   0x04
246057d40fSYD Tseng #define PT_OUTPUTDATA_REG  0x08
256057d40fSYD Tseng #define PT_CLOCKRATE_REG   0x0C
266057d40fSYD Tseng #define PT_SYNC_REG        0x28
276057d40fSYD Tseng 
286057d40fSYD Tseng struct pt_gpio_chip {
296057d40fSYD Tseng 	struct gpio_chip         gc;
306057d40fSYD Tseng 	void __iomem             *reg_base;
316057d40fSYD Tseng 	spinlock_t               lock;
326057d40fSYD Tseng };
336057d40fSYD Tseng 
346057d40fSYD Tseng #define to_pt_gpio(c)	container_of(c, struct pt_gpio_chip, gc)
356057d40fSYD Tseng 
366057d40fSYD Tseng static int pt_gpio_request(struct gpio_chip *gc, unsigned offset)
376057d40fSYD Tseng {
386057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
396057d40fSYD Tseng 	unsigned long flags;
406057d40fSYD Tseng 	u32 using_pins;
416057d40fSYD Tseng 
4258383c78SLinus Walleij 	dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset);
436057d40fSYD Tseng 
446057d40fSYD Tseng 	spin_lock_irqsave(&pt_gpio->lock, flags);
456057d40fSYD Tseng 
466057d40fSYD Tseng 	using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
476057d40fSYD Tseng 	if (using_pins & BIT(offset)) {
4858383c78SLinus Walleij 		dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n",
496057d40fSYD Tseng 			 offset);
506057d40fSYD Tseng 		spin_unlock_irqrestore(&pt_gpio->lock, flags);
516057d40fSYD Tseng 		return -EINVAL;
526057d40fSYD Tseng 	}
536057d40fSYD Tseng 
546057d40fSYD Tseng 	writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG);
556057d40fSYD Tseng 
566057d40fSYD Tseng 	spin_unlock_irqrestore(&pt_gpio->lock, flags);
576057d40fSYD Tseng 
586057d40fSYD Tseng 	return 0;
596057d40fSYD Tseng }
606057d40fSYD Tseng 
616057d40fSYD Tseng static void pt_gpio_free(struct gpio_chip *gc, unsigned offset)
626057d40fSYD Tseng {
636057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
646057d40fSYD Tseng 	unsigned long flags;
656057d40fSYD Tseng 	u32 using_pins;
666057d40fSYD Tseng 
676057d40fSYD Tseng 	spin_lock_irqsave(&pt_gpio->lock, flags);
686057d40fSYD Tseng 
696057d40fSYD Tseng 	using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
706057d40fSYD Tseng 	using_pins &= ~BIT(offset);
716057d40fSYD Tseng 	writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG);
726057d40fSYD Tseng 
736057d40fSYD Tseng 	spin_unlock_irqrestore(&pt_gpio->lock, flags);
746057d40fSYD Tseng 
7558383c78SLinus Walleij 	dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset);
766057d40fSYD Tseng }
776057d40fSYD Tseng 
786057d40fSYD Tseng static void pt_gpio_set_value(struct gpio_chip *gc, unsigned offset, int value)
796057d40fSYD Tseng {
806057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
816057d40fSYD Tseng 	unsigned long flags;
826057d40fSYD Tseng 	u32 data;
836057d40fSYD Tseng 
8458383c78SLinus Walleij 	dev_dbg(gc->parent, "pt_gpio_set_value offset=%x, value=%x\n",
856057d40fSYD Tseng 		offset, value);
866057d40fSYD Tseng 
876057d40fSYD Tseng 	spin_lock_irqsave(&pt_gpio->lock, flags);
886057d40fSYD Tseng 
896057d40fSYD Tseng 	data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG);
906057d40fSYD Tseng 	data &= ~BIT(offset);
916057d40fSYD Tseng 	if (value)
926057d40fSYD Tseng 		data |= BIT(offset);
936057d40fSYD Tseng 	writel(data, pt_gpio->reg_base + PT_OUTPUTDATA_REG);
946057d40fSYD Tseng 
956057d40fSYD Tseng 	spin_unlock_irqrestore(&pt_gpio->lock, flags);
966057d40fSYD Tseng }
976057d40fSYD Tseng 
986057d40fSYD Tseng static int pt_gpio_get_value(struct gpio_chip *gc, unsigned offset)
996057d40fSYD Tseng {
1006057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
1016057d40fSYD Tseng 	unsigned long flags;
1026057d40fSYD Tseng 	u32 data;
1036057d40fSYD Tseng 
1046057d40fSYD Tseng 	spin_lock_irqsave(&pt_gpio->lock, flags);
1056057d40fSYD Tseng 
1066057d40fSYD Tseng 	data = readl(pt_gpio->reg_base + PT_DIRECTION_REG);
1076057d40fSYD Tseng 
1086057d40fSYD Tseng 	/* configure as output */
1096057d40fSYD Tseng 	if (data & BIT(offset))
1106057d40fSYD Tseng 		data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG);
1116057d40fSYD Tseng 	else	/* configure as input */
1126057d40fSYD Tseng 		data = readl(pt_gpio->reg_base + PT_INPUTDATA_REG);
1136057d40fSYD Tseng 
1146057d40fSYD Tseng 	spin_unlock_irqrestore(&pt_gpio->lock, flags);
1156057d40fSYD Tseng 
1166057d40fSYD Tseng 	data >>= offset;
1176057d40fSYD Tseng 	data &= 1;
1186057d40fSYD Tseng 
11958383c78SLinus Walleij 	dev_dbg(gc->parent, "pt_gpio_get_value offset=%x, value=%x\n",
1206057d40fSYD Tseng 		offset, data);
1216057d40fSYD Tseng 
1226057d40fSYD Tseng 	return data;
1236057d40fSYD Tseng }
1246057d40fSYD Tseng 
1256057d40fSYD Tseng static int pt_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
1266057d40fSYD Tseng {
1276057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
1286057d40fSYD Tseng 	unsigned long flags;
1296057d40fSYD Tseng 	u32 data;
1306057d40fSYD Tseng 
13158383c78SLinus Walleij 	dev_dbg(gc->parent, "pt_gpio_dirction_input offset=%x\n", offset);
1326057d40fSYD Tseng 
1336057d40fSYD Tseng 	spin_lock_irqsave(&pt_gpio->lock, flags);
1346057d40fSYD Tseng 
1356057d40fSYD Tseng 	data = readl(pt_gpio->reg_base + PT_DIRECTION_REG);
1366057d40fSYD Tseng 	data &= ~BIT(offset);
1376057d40fSYD Tseng 	writel(data, pt_gpio->reg_base + PT_DIRECTION_REG);
1386057d40fSYD Tseng 
1396057d40fSYD Tseng 	spin_unlock_irqrestore(&pt_gpio->lock, flags);
1406057d40fSYD Tseng 
1416057d40fSYD Tseng 	return 0;
1426057d40fSYD Tseng }
1436057d40fSYD Tseng 
1446057d40fSYD Tseng static int pt_gpio_direction_output(struct gpio_chip *gc,
1456057d40fSYD Tseng 					unsigned offset, int value)
1466057d40fSYD Tseng {
1476057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio = to_pt_gpio(gc);
1486057d40fSYD Tseng 	unsigned long flags;
1496057d40fSYD Tseng 	u32 data;
1506057d40fSYD Tseng 
15158383c78SLinus Walleij 	dev_dbg(gc->parent, "pt_gpio_direction_output offset=%x, value=%x\n",
1526057d40fSYD Tseng 		offset, value);
1536057d40fSYD Tseng 
1546057d40fSYD Tseng 	spin_lock_irqsave(&pt_gpio->lock, flags);
1556057d40fSYD Tseng 
1566057d40fSYD Tseng 	data = readl(pt_gpio->reg_base + PT_OUTPUTDATA_REG);
1576057d40fSYD Tseng 	if (value)
1586057d40fSYD Tseng 		data |= BIT(offset);
1596057d40fSYD Tseng 	else
1606057d40fSYD Tseng 		data &= ~BIT(offset);
1616057d40fSYD Tseng 	writel(data, pt_gpio->reg_base + PT_OUTPUTDATA_REG);
1626057d40fSYD Tseng 
1636057d40fSYD Tseng 	data = readl(pt_gpio->reg_base + PT_DIRECTION_REG);
1646057d40fSYD Tseng 	data |= BIT(offset);
1656057d40fSYD Tseng 	writel(data, pt_gpio->reg_base + PT_DIRECTION_REG);
1666057d40fSYD Tseng 
1676057d40fSYD Tseng 	spin_unlock_irqrestore(&pt_gpio->lock, flags);
1686057d40fSYD Tseng 
1696057d40fSYD Tseng 	return 0;
1706057d40fSYD Tseng }
1716057d40fSYD Tseng 
1726057d40fSYD Tseng static int pt_gpio_probe(struct platform_device *pdev)
1736057d40fSYD Tseng {
1746057d40fSYD Tseng 	struct device *dev = &pdev->dev;
1756057d40fSYD Tseng 	struct acpi_device *acpi_dev;
1766057d40fSYD Tseng 	acpi_handle handle = ACPI_HANDLE(dev);
1776057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio;
1786057d40fSYD Tseng 	struct resource *res_mem;
1796057d40fSYD Tseng 	int ret = 0;
1806057d40fSYD Tseng 
1816057d40fSYD Tseng 	if (acpi_bus_get_device(handle, &acpi_dev)) {
1826057d40fSYD Tseng 		dev_err(dev, "PT GPIO device node not found\n");
1836057d40fSYD Tseng 		return -ENODEV;
1846057d40fSYD Tseng 	}
1856057d40fSYD Tseng 
1866057d40fSYD Tseng 	pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL);
1876057d40fSYD Tseng 	if (!pt_gpio)
1886057d40fSYD Tseng 		return -ENOMEM;
1896057d40fSYD Tseng 
1906057d40fSYD Tseng 	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1916057d40fSYD Tseng 	if (!res_mem) {
1926057d40fSYD Tseng 		dev_err(&pdev->dev, "Failed to get MMIO resource for PT GPIO.\n");
1936057d40fSYD Tseng 		return -EINVAL;
1946057d40fSYD Tseng 	}
1956057d40fSYD Tseng 	pt_gpio->reg_base = devm_ioremap_resource(dev, res_mem);
1966057d40fSYD Tseng 	if (IS_ERR(pt_gpio->reg_base)) {
1976057d40fSYD Tseng 		dev_err(&pdev->dev, "Failed to map MMIO resource for PT GPIO.\n");
1986057d40fSYD Tseng 		return PTR_ERR(pt_gpio->reg_base);
1996057d40fSYD Tseng 	}
2006057d40fSYD Tseng 
2016057d40fSYD Tseng 	spin_lock_init(&pt_gpio->lock);
2026057d40fSYD Tseng 
2036057d40fSYD Tseng 	pt_gpio->gc.label            = pdev->name;
2046057d40fSYD Tseng 	pt_gpio->gc.owner            = THIS_MODULE;
20558383c78SLinus Walleij 	pt_gpio->gc.parent              = dev;
2066057d40fSYD Tseng 	pt_gpio->gc.request          = pt_gpio_request;
2076057d40fSYD Tseng 	pt_gpio->gc.free             = pt_gpio_free;
2086057d40fSYD Tseng 	pt_gpio->gc.direction_input  = pt_gpio_direction_input;
2096057d40fSYD Tseng 	pt_gpio->gc.direction_output = pt_gpio_direction_output;
2106057d40fSYD Tseng 	pt_gpio->gc.get              = pt_gpio_get_value;
2116057d40fSYD Tseng 	pt_gpio->gc.set              = pt_gpio_set_value;
2126057d40fSYD Tseng 	pt_gpio->gc.base             = -1;
2136057d40fSYD Tseng 	pt_gpio->gc.ngpio            = PT_TOTAL_GPIO;
2146057d40fSYD Tseng #if defined(CONFIG_OF_GPIO)
2156057d40fSYD Tseng 	pt_gpio->gc.of_node          = pdev->dev.of_node;
2166057d40fSYD Tseng #endif
2176057d40fSYD Tseng 	ret = gpiochip_add(&pt_gpio->gc);
2186057d40fSYD Tseng 	if (ret) {
2196057d40fSYD Tseng 		dev_err(&pdev->dev, "Failed to register GPIO lib\n");
2206057d40fSYD Tseng 		return ret;
2216057d40fSYD Tseng 	}
2226057d40fSYD Tseng 
2236057d40fSYD Tseng 	platform_set_drvdata(pdev, pt_gpio);
2246057d40fSYD Tseng 
2256057d40fSYD Tseng 	/* initialize register setting */
2266057d40fSYD Tseng 	writel(0, pt_gpio->reg_base + PT_SYNC_REG);
2276057d40fSYD Tseng 	writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG);
2286057d40fSYD Tseng 
2296057d40fSYD Tseng 	dev_dbg(&pdev->dev, "PT GPIO driver loaded\n");
2306057d40fSYD Tseng 	return ret;
2316057d40fSYD Tseng }
2326057d40fSYD Tseng 
2336057d40fSYD Tseng static int pt_gpio_remove(struct platform_device *pdev)
2346057d40fSYD Tseng {
2356057d40fSYD Tseng 	struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev);
2366057d40fSYD Tseng 
2376057d40fSYD Tseng 	gpiochip_remove(&pt_gpio->gc);
2386057d40fSYD Tseng 
2396057d40fSYD Tseng 	return 0;
2406057d40fSYD Tseng }
2416057d40fSYD Tseng 
2426057d40fSYD Tseng static const struct acpi_device_id pt_gpio_acpi_match[] = {
2436057d40fSYD Tseng 	{ "AMDF030", 0 },
2446057d40fSYD Tseng 	{ },
2456057d40fSYD Tseng };
2466057d40fSYD Tseng MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match);
2476057d40fSYD Tseng 
2486057d40fSYD Tseng static struct platform_driver pt_gpio_driver = {
2496057d40fSYD Tseng 	.driver = {
2506057d40fSYD Tseng 		.name = "pt-gpio",
2516057d40fSYD Tseng 		.acpi_match_table = ACPI_PTR(pt_gpio_acpi_match),
2526057d40fSYD Tseng 	},
2536057d40fSYD Tseng 	.probe = pt_gpio_probe,
2546057d40fSYD Tseng 	.remove = pt_gpio_remove,
2556057d40fSYD Tseng };
2566057d40fSYD Tseng 
2576057d40fSYD Tseng module_platform_driver(pt_gpio_driver);
2586057d40fSYD Tseng 
2596057d40fSYD Tseng MODULE_LICENSE("GPL");
2606057d40fSYD Tseng MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>");
2616057d40fSYD Tseng MODULE_DESCRIPTION("AMD Promontory GPIO Driver");
262