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 <info@metux.net> |
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, gpio: 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(lock: &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(lock: &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(lock: &priv->lock, flags); |
94 | |
95 | return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; |
96 | } |
97 | |
98 | static void amd_fch_gpio_set(struct gpio_chip *gc, |
99 | unsigned int gpio, int value) |
100 | { |
101 | unsigned long flags; |
102 | struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); |
103 | void __iomem *ptr = amd_fch_gpio_addr(priv, gpio); |
104 | u32 mask; |
105 | |
106 | spin_lock_irqsave(&priv->lock, flags); |
107 | |
108 | mask = readl_relaxed(ptr); |
109 | if (value) |
110 | mask |= AMD_FCH_GPIO_FLAG_WRITE; |
111 | else |
112 | mask &= ~AMD_FCH_GPIO_FLAG_WRITE; |
113 | writel_relaxed(mask, ptr); |
114 | |
115 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
116 | } |
117 | |
118 | static int amd_fch_gpio_get(struct gpio_chip *gc, |
119 | unsigned int offset) |
120 | { |
121 | unsigned long flags; |
122 | int ret; |
123 | struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc); |
124 | void __iomem *ptr = amd_fch_gpio_addr(priv, gpio: offset); |
125 | |
126 | spin_lock_irqsave(&priv->lock, flags); |
127 | ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_READ); |
128 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
129 | |
130 | return ret; |
131 | } |
132 | |
133 | static int amd_fch_gpio_request(struct gpio_chip *chip, |
134 | unsigned int gpio_pin) |
135 | { |
136 | return 0; |
137 | } |
138 | |
139 | static int amd_fch_gpio_probe(struct platform_device *pdev) |
140 | { |
141 | struct amd_fch_gpio_priv *priv; |
142 | struct amd_fch_gpio_pdata *pdata; |
143 | |
144 | pdata = dev_get_platdata(dev: &pdev->dev); |
145 | if (!pdata) { |
146 | dev_err(&pdev->dev, "no platform_data\n" ); |
147 | return -ENOENT; |
148 | } |
149 | |
150 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
151 | if (!priv) |
152 | return -ENOMEM; |
153 | |
154 | priv->pdata = pdata; |
155 | |
156 | priv->gc.owner = THIS_MODULE; |
157 | priv->gc.parent = &pdev->dev; |
158 | priv->gc.label = dev_name(dev: &pdev->dev); |
159 | priv->gc.ngpio = priv->pdata->gpio_num; |
160 | priv->gc.names = priv->pdata->gpio_names; |
161 | priv->gc.base = -1; |
162 | priv->gc.request = amd_fch_gpio_request; |
163 | priv->gc.direction_input = amd_fch_gpio_direction_input; |
164 | priv->gc.direction_output = amd_fch_gpio_direction_output; |
165 | priv->gc.get_direction = amd_fch_gpio_get_direction; |
166 | priv->gc.get = amd_fch_gpio_get; |
167 | priv->gc.set = amd_fch_gpio_set; |
168 | |
169 | spin_lock_init(&priv->lock); |
170 | |
171 | priv->base = devm_ioremap_resource(dev: &pdev->dev, res: &amd_fch_gpio_iores); |
172 | if (IS_ERR(ptr: priv->base)) |
173 | return PTR_ERR(ptr: priv->base); |
174 | |
175 | platform_set_drvdata(pdev, data: priv); |
176 | |
177 | return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); |
178 | } |
179 | |
180 | static struct platform_driver amd_fch_gpio_driver = { |
181 | .driver = { |
182 | .name = AMD_FCH_GPIO_DRIVER_NAME, |
183 | }, |
184 | .probe = amd_fch_gpio_probe, |
185 | }; |
186 | |
187 | module_platform_driver(amd_fch_gpio_driver); |
188 | |
189 | MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>" ); |
190 | MODULE_DESCRIPTION("AMD G-series FCH GPIO driver" ); |
191 | MODULE_LICENSE("GPL" ); |
192 | MODULE_ALIAS("platform:" AMD_FCH_GPIO_DRIVER_NAME); |
193 | |