xref: /openbmc/linux/drivers/gpio/gpio-amd-fch.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1e09d168fSEnrico Weigelt, metux IT consult // SPDX-License-Identifier: GPL-2.0+
2e09d168fSEnrico Weigelt, metux IT consult 
3e09d168fSEnrico Weigelt, metux IT consult /*
4e09d168fSEnrico Weigelt, metux IT consult  * GPIO driver for the AMD G series FCH (eg. GX-412TC)
5e09d168fSEnrico Weigelt, metux IT consult  *
6e09d168fSEnrico Weigelt, metux IT consult  * Copyright (C) 2018 metux IT consult
7e09d168fSEnrico Weigelt, metux IT consult  * Author: Enrico Weigelt, metux IT consult <info@metux.net>
8e09d168fSEnrico Weigelt, metux IT consult  *
9e09d168fSEnrico Weigelt, metux IT consult  */
10e09d168fSEnrico Weigelt, metux IT consult 
11e09d168fSEnrico Weigelt, metux IT consult #include <linux/err.h>
12e09d168fSEnrico Weigelt, metux IT consult #include <linux/io.h>
13e09d168fSEnrico Weigelt, metux IT consult #include <linux/kernel.h>
14e09d168fSEnrico Weigelt, metux IT consult #include <linux/module.h>
15e09d168fSEnrico Weigelt, metux IT consult #include <linux/platform_device.h>
16e09d168fSEnrico Weigelt, metux IT consult #include <linux/gpio/driver.h>
17e09d168fSEnrico Weigelt, metux IT consult #include <linux/platform_data/gpio/gpio-amd-fch.h>
18e09d168fSEnrico Weigelt, metux IT consult #include <linux/spinlock.h>
19e09d168fSEnrico Weigelt, metux IT consult 
20e09d168fSEnrico Weigelt, metux IT consult #define AMD_FCH_MMIO_BASE		0xFED80000
21e09d168fSEnrico Weigelt, metux IT consult #define AMD_FCH_GPIO_BANK0_BASE		0x1500
22e09d168fSEnrico Weigelt, metux IT consult #define AMD_FCH_GPIO_SIZE		0x0300
23e09d168fSEnrico Weigelt, metux IT consult 
24e09d168fSEnrico Weigelt, metux IT consult #define AMD_FCH_GPIO_FLAG_DIRECTION	BIT(23)
25e09d168fSEnrico Weigelt, metux IT consult #define AMD_FCH_GPIO_FLAG_WRITE		BIT(22)
26e09d168fSEnrico Weigelt, metux IT consult #define AMD_FCH_GPIO_FLAG_READ		BIT(16)
27e09d168fSEnrico Weigelt, metux IT consult 
2887b70378SEnrico Weigelt static const struct resource amd_fch_gpio_iores =
29e09d168fSEnrico Weigelt, metux IT consult 	DEFINE_RES_MEM_NAMED(
30e09d168fSEnrico Weigelt, metux IT consult 		AMD_FCH_MMIO_BASE + AMD_FCH_GPIO_BANK0_BASE,
31e09d168fSEnrico Weigelt, metux IT consult 		AMD_FCH_GPIO_SIZE,
32e09d168fSEnrico Weigelt, metux IT consult 		"amd-fch-gpio-iomem");
33e09d168fSEnrico Weigelt, metux IT consult 
34e09d168fSEnrico Weigelt, metux IT consult struct amd_fch_gpio_priv {
35e09d168fSEnrico Weigelt, metux IT consult 	struct gpio_chip		gc;
36e09d168fSEnrico Weigelt, metux IT consult 	void __iomem			*base;
37e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_pdata	*pdata;
38e09d168fSEnrico Weigelt, metux IT consult 	spinlock_t			lock;
39e09d168fSEnrico Weigelt, metux IT consult };
40e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_addr(struct amd_fch_gpio_priv * priv,unsigned int gpio)41e226e3c3SLinus Walleij static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv,
42e09d168fSEnrico Weigelt, metux IT consult 				       unsigned int gpio)
43e09d168fSEnrico Weigelt, metux IT consult {
44e09d168fSEnrico Weigelt, metux IT consult 	return priv->base + priv->pdata->gpio_reg[gpio]*sizeof(u32);
45e09d168fSEnrico Weigelt, metux IT consult }
46e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_direction_input(struct gpio_chip * gc,unsigned int offset)47e09d168fSEnrico Weigelt, metux IT consult static int amd_fch_gpio_direction_input(struct gpio_chip *gc,
48e09d168fSEnrico Weigelt, metux IT consult 					unsigned int offset)
49e09d168fSEnrico Weigelt, metux IT consult {
50e09d168fSEnrico Weigelt, metux IT consult 	unsigned long flags;
51e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
52e226e3c3SLinus Walleij 	void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
53e09d168fSEnrico Weigelt, metux IT consult 
54e09d168fSEnrico Weigelt, metux IT consult 	spin_lock_irqsave(&priv->lock, flags);
55e09d168fSEnrico Weigelt, metux IT consult 	writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
56e09d168fSEnrico Weigelt, metux IT consult 	spin_unlock_irqrestore(&priv->lock, flags);
57e09d168fSEnrico Weigelt, metux IT consult 
58e09d168fSEnrico Weigelt, metux IT consult 	return 0;
59e09d168fSEnrico Weigelt, metux IT consult }
60e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_direction_output(struct gpio_chip * gc,unsigned int gpio,int value)61e09d168fSEnrico Weigelt, metux IT consult static int amd_fch_gpio_direction_output(struct gpio_chip *gc,
62e09d168fSEnrico Weigelt, metux IT consult 					 unsigned int gpio, int value)
63e09d168fSEnrico Weigelt, metux IT consult {
64e09d168fSEnrico Weigelt, metux IT consult 	unsigned long flags;
65e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
66e226e3c3SLinus Walleij 	void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
67f777cda3SAxel Lin 	u32 val;
68e09d168fSEnrico Weigelt, metux IT consult 
69e09d168fSEnrico Weigelt, metux IT consult 	spin_lock_irqsave(&priv->lock, flags);
70f777cda3SAxel Lin 
71f777cda3SAxel Lin 	val = readl_relaxed(ptr);
72f777cda3SAxel Lin 	if (value)
73f777cda3SAxel Lin 		val |= AMD_FCH_GPIO_FLAG_WRITE;
74f777cda3SAxel Lin 	else
75f777cda3SAxel Lin 		val &= ~AMD_FCH_GPIO_FLAG_WRITE;
76f777cda3SAxel Lin 
77f777cda3SAxel Lin 	writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
78f777cda3SAxel Lin 
79e09d168fSEnrico Weigelt, metux IT consult 	spin_unlock_irqrestore(&priv->lock, flags);
80e09d168fSEnrico Weigelt, metux IT consult 
81e09d168fSEnrico Weigelt, metux IT consult 	return 0;
82e09d168fSEnrico Weigelt, metux IT consult }
83e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_get_direction(struct gpio_chip * gc,unsigned int gpio)84e09d168fSEnrico Weigelt, metux IT consult static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
85e09d168fSEnrico Weigelt, metux IT consult {
86e09d168fSEnrico Weigelt, metux IT consult 	int ret;
87e09d168fSEnrico Weigelt, metux IT consult 	unsigned long flags;
88e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
89e226e3c3SLinus Walleij 	void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
90e09d168fSEnrico Weigelt, metux IT consult 
91e09d168fSEnrico Weigelt, metux IT consult 	spin_lock_irqsave(&priv->lock, flags);
92e09d168fSEnrico Weigelt, metux IT consult 	ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION);
93e09d168fSEnrico Weigelt, metux IT consult 	spin_unlock_irqrestore(&priv->lock, flags);
94e09d168fSEnrico Weigelt, metux IT consult 
95*d25e8fdeSEd Wildgoose 	return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
96e09d168fSEnrico Weigelt, metux IT consult }
97e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_set(struct gpio_chip * gc,unsigned int gpio,int value)98e09d168fSEnrico Weigelt, metux IT consult static void amd_fch_gpio_set(struct gpio_chip *gc,
99e09d168fSEnrico Weigelt, metux IT consult 			     unsigned int gpio, int value)
100e09d168fSEnrico Weigelt, metux IT consult {
101e09d168fSEnrico Weigelt, metux IT consult 	unsigned long flags;
102e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
103e226e3c3SLinus Walleij 	void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
104e09d168fSEnrico Weigelt, metux IT consult 	u32 mask;
105e09d168fSEnrico Weigelt, metux IT consult 
106e09d168fSEnrico Weigelt, metux IT consult 	spin_lock_irqsave(&priv->lock, flags);
107e09d168fSEnrico Weigelt, metux IT consult 
108e09d168fSEnrico Weigelt, metux IT consult 	mask = readl_relaxed(ptr);
109e09d168fSEnrico Weigelt, metux IT consult 	if (value)
110e09d168fSEnrico Weigelt, metux IT consult 		mask |= AMD_FCH_GPIO_FLAG_WRITE;
111e09d168fSEnrico Weigelt, metux IT consult 	else
112e09d168fSEnrico Weigelt, metux IT consult 		mask &= ~AMD_FCH_GPIO_FLAG_WRITE;
113e09d168fSEnrico Weigelt, metux IT consult 	writel_relaxed(mask, ptr);
114e09d168fSEnrico Weigelt, metux IT consult 
115e09d168fSEnrico Weigelt, metux IT consult 	spin_unlock_irqrestore(&priv->lock, flags);
116e09d168fSEnrico Weigelt, metux IT consult }
117e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_get(struct gpio_chip * gc,unsigned int offset)118e09d168fSEnrico Weigelt, metux IT consult static int amd_fch_gpio_get(struct gpio_chip *gc,
119e09d168fSEnrico Weigelt, metux IT consult 			    unsigned int offset)
120e09d168fSEnrico Weigelt, metux IT consult {
121e09d168fSEnrico Weigelt, metux IT consult 	unsigned long flags;
122e09d168fSEnrico Weigelt, metux IT consult 	int ret;
123e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
124e226e3c3SLinus Walleij 	void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
125e09d168fSEnrico Weigelt, metux IT consult 
126e09d168fSEnrico Weigelt, metux IT consult 	spin_lock_irqsave(&priv->lock, flags);
127e09d168fSEnrico Weigelt, metux IT consult 	ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_READ);
128e09d168fSEnrico Weigelt, metux IT consult 	spin_unlock_irqrestore(&priv->lock, flags);
129e09d168fSEnrico Weigelt, metux IT consult 
130e09d168fSEnrico Weigelt, metux IT consult 	return ret;
131e09d168fSEnrico Weigelt, metux IT consult }
132e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_request(struct gpio_chip * chip,unsigned int gpio_pin)133e09d168fSEnrico Weigelt, metux IT consult static int amd_fch_gpio_request(struct gpio_chip *chip,
134e09d168fSEnrico Weigelt, metux IT consult 				unsigned int gpio_pin)
135e09d168fSEnrico Weigelt, metux IT consult {
136e09d168fSEnrico Weigelt, metux IT consult 	return 0;
137e09d168fSEnrico Weigelt, metux IT consult }
138e09d168fSEnrico Weigelt, metux IT consult 
amd_fch_gpio_probe(struct platform_device * pdev)139e09d168fSEnrico Weigelt, metux IT consult static int amd_fch_gpio_probe(struct platform_device *pdev)
140e09d168fSEnrico Weigelt, metux IT consult {
141e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_priv *priv;
142e09d168fSEnrico Weigelt, metux IT consult 	struct amd_fch_gpio_pdata *pdata;
143e09d168fSEnrico Weigelt, metux IT consult 
144e09d168fSEnrico Weigelt, metux IT consult 	pdata = dev_get_platdata(&pdev->dev);
145e09d168fSEnrico Weigelt, metux IT consult 	if (!pdata) {
146e09d168fSEnrico Weigelt, metux IT consult 		dev_err(&pdev->dev, "no platform_data\n");
147e09d168fSEnrico Weigelt, metux IT consult 		return -ENOENT;
148e09d168fSEnrico Weigelt, metux IT consult 	}
149e09d168fSEnrico Weigelt, metux IT consult 
150e09d168fSEnrico Weigelt, metux IT consult 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
151e09d168fSEnrico Weigelt, metux IT consult 	if (!priv)
152e09d168fSEnrico Weigelt, metux IT consult 		return -ENOMEM;
153e09d168fSEnrico Weigelt, metux IT consult 
154e09d168fSEnrico Weigelt, metux IT consult 	priv->pdata	= pdata;
155e09d168fSEnrico Weigelt, metux IT consult 
156e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.owner			= THIS_MODULE;
157e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.parent			= &pdev->dev;
158e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.label			= dev_name(&pdev->dev);
159e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.ngpio			= priv->pdata->gpio_num;
160e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.names			= priv->pdata->gpio_names;
161e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.base			= -1;
162e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.request		= amd_fch_gpio_request;
163e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.direction_input	= amd_fch_gpio_direction_input;
164e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.direction_output	= amd_fch_gpio_direction_output;
165e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.get_direction		= amd_fch_gpio_get_direction;
166e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.get			= amd_fch_gpio_get;
167e09d168fSEnrico Weigelt, metux IT consult 	priv->gc.set			= amd_fch_gpio_set;
168e09d168fSEnrico Weigelt, metux IT consult 
169e09d168fSEnrico Weigelt, metux IT consult 	spin_lock_init(&priv->lock);
170e09d168fSEnrico Weigelt, metux IT consult 
171e09d168fSEnrico Weigelt, metux IT consult 	priv->base = devm_ioremap_resource(&pdev->dev, &amd_fch_gpio_iores);
172e09d168fSEnrico Weigelt, metux IT consult 	if (IS_ERR(priv->base))
173e09d168fSEnrico Weigelt, metux IT consult 		return PTR_ERR(priv->base);
174e09d168fSEnrico Weigelt, metux IT consult 
175e09d168fSEnrico Weigelt, metux IT consult 	platform_set_drvdata(pdev, priv);
176e09d168fSEnrico Weigelt, metux IT consult 
177e09d168fSEnrico Weigelt, metux IT consult 	return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
178e09d168fSEnrico Weigelt, metux IT consult }
179e09d168fSEnrico Weigelt, metux IT consult 
180e09d168fSEnrico Weigelt, metux IT consult static struct platform_driver amd_fch_gpio_driver = {
181e09d168fSEnrico Weigelt, metux IT consult 	.driver = {
182e09d168fSEnrico Weigelt, metux IT consult 		.name = AMD_FCH_GPIO_DRIVER_NAME,
183e09d168fSEnrico Weigelt, metux IT consult 	},
184e09d168fSEnrico Weigelt, metux IT consult 	.probe = amd_fch_gpio_probe,
185e09d168fSEnrico Weigelt, metux IT consult };
186e09d168fSEnrico Weigelt, metux IT consult 
187e09d168fSEnrico Weigelt, metux IT consult module_platform_driver(amd_fch_gpio_driver);
188e09d168fSEnrico Weigelt, metux IT consult 
189e09d168fSEnrico Weigelt, metux IT consult MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
190e09d168fSEnrico Weigelt, metux IT consult MODULE_DESCRIPTION("AMD G-series FCH GPIO driver");
191e09d168fSEnrico Weigelt, metux IT consult MODULE_LICENSE("GPL");
192e09d168fSEnrico Weigelt, metux IT consult MODULE_ALIAS("platform:" AMD_FCH_GPIO_DRIVER_NAME);
193