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