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