1 // SPDX-License-Identifier: GPL-2.0+
3 * Loongson GPIO Support
5 * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/spinlock.h>
12 #include <linux/err.h>
13 #include <linux/gpio/driver.h>
14 #include <linux/platform_device.h>
15 #include <linux/bitops.h>
16 #include <asm/types.h>
18 enum loongson_gpio_mode
{
23 struct loongson_gpio_chip_data
{
25 enum loongson_gpio_mode mode
;
26 unsigned int conf_offset
;
27 unsigned int out_offset
;
28 unsigned int in_offset
;
29 unsigned int inten_offset
;
32 struct loongson_gpio_chip
{
33 struct gpio_chip chip
;
34 struct fwnode_handle
*fwnode
;
36 void __iomem
*reg_base
;
37 const struct loongson_gpio_chip_data
*chip_data
;
40 static inline struct loongson_gpio_chip
*to_loongson_gpio_chip(struct gpio_chip
*chip
)
42 return container_of(chip
, struct loongson_gpio_chip
, chip
);
45 static inline void loongson_commit_direction(struct loongson_gpio_chip
*lgpio
, unsigned int pin
,
48 u8 bval
= input
? 1 : 0;
50 writeb(bval
, lgpio
->reg_base
+ lgpio
->chip_data
->conf_offset
+ pin
);
53 static void loongson_commit_level(struct loongson_gpio_chip
*lgpio
, unsigned int pin
, int high
)
55 u8 bval
= high
? 1 : 0;
57 writeb(bval
, lgpio
->reg_base
+ lgpio
->chip_data
->out_offset
+ pin
);
60 static int loongson_gpio_direction_input(struct gpio_chip
*chip
, unsigned int pin
)
63 struct loongson_gpio_chip
*lgpio
= to_loongson_gpio_chip(chip
);
65 spin_lock_irqsave(&lgpio
->lock
, flags
);
66 loongson_commit_direction(lgpio
, pin
, 1);
67 spin_unlock_irqrestore(&lgpio
->lock
, flags
);
72 static int loongson_gpio_direction_output(struct gpio_chip
*chip
, unsigned int pin
, int value
)
75 struct loongson_gpio_chip
*lgpio
= to_loongson_gpio_chip(chip
);
77 spin_lock_irqsave(&lgpio
->lock
, flags
);
78 loongson_commit_level(lgpio
, pin
, value
);
79 loongson_commit_direction(lgpio
, pin
, 0);
80 spin_unlock_irqrestore(&lgpio
->lock
, flags
);
85 static int loongson_gpio_get(struct gpio_chip
*chip
, unsigned int pin
)
89 struct loongson_gpio_chip
*lgpio
= to_loongson_gpio_chip(chip
);
91 bval
= readb(lgpio
->reg_base
+ lgpio
->chip_data
->in_offset
+ pin
);
97 static int loongson_gpio_get_direction(struct gpio_chip
*chip
, unsigned int pin
)
100 struct loongson_gpio_chip
*lgpio
= to_loongson_gpio_chip(chip
);
102 bval
= readb(lgpio
->reg_base
+ lgpio
->chip_data
->conf_offset
+ pin
);
104 return GPIO_LINE_DIRECTION_IN
;
106 return GPIO_LINE_DIRECTION_OUT
;
109 static void loongson_gpio_set(struct gpio_chip
*chip
, unsigned int pin
, int value
)
112 struct loongson_gpio_chip
*lgpio
= to_loongson_gpio_chip(chip
);
114 spin_lock_irqsave(&lgpio
->lock
, flags
);
115 loongson_commit_level(lgpio
, pin
, value
);
116 spin_unlock_irqrestore(&lgpio
->lock
, flags
);
119 static int loongson_gpio_to_irq(struct gpio_chip
*chip
, unsigned int offset
)
122 struct platform_device
*pdev
= to_platform_device(chip
->parent
);
123 struct loongson_gpio_chip
*lgpio
= to_loongson_gpio_chip(chip
);
125 if (lgpio
->chip_data
->mode
== BIT_CTRL_MODE
) {
126 /* Get the register index from offset then multiply by bytes per register */
127 u
= readl(lgpio
->reg_base
+ lgpio
->chip_data
->inten_offset
+ (offset
/ 32) * 4);
128 u
|= BIT(offset
% 32);
129 writel(u
, lgpio
->reg_base
+ lgpio
->chip_data
->inten_offset
+ (offset
/ 32) * 4);
131 writeb(1, lgpio
->reg_base
+ lgpio
->chip_data
->inten_offset
+ offset
);
134 return platform_get_irq(pdev
, offset
);
137 static int loongson_gpio_init(struct device
*dev
, struct loongson_gpio_chip
*lgpio
,
138 void __iomem
*reg_base
)
143 lgpio
->reg_base
= reg_base
;
144 if (lgpio
->chip_data
->mode
== BIT_CTRL_MODE
) {
145 ret
= bgpio_init(&lgpio
->chip
, dev
, 8,
146 lgpio
->reg_base
+ lgpio
->chip_data
->in_offset
,
147 lgpio
->reg_base
+ lgpio
->chip_data
->out_offset
,
149 lgpio
->reg_base
+ lgpio
->chip_data
->conf_offset
,
152 dev_err(dev
, "unable to init generic GPIO\n");
156 lgpio
->chip
.direction_input
= loongson_gpio_direction_input
;
157 lgpio
->chip
.get
= loongson_gpio_get
;
158 lgpio
->chip
.get_direction
= loongson_gpio_get_direction
;
159 lgpio
->chip
.direction_output
= loongson_gpio_direction_output
;
160 lgpio
->chip
.set
= loongson_gpio_set
;
161 lgpio
->chip
.parent
= dev
;
162 device_property_read_u32(dev
, "ngpios", &ngpios
);
163 lgpio
->chip
.ngpio
= ngpios
;
164 spin_lock_init(&lgpio
->lock
);
167 lgpio
->chip
.label
= lgpio
->chip_data
->label
;
168 lgpio
->chip
.can_sleep
= false;
169 if (lgpio
->chip_data
->inten_offset
)
170 lgpio
->chip
.to_irq
= loongson_gpio_to_irq
;
172 return devm_gpiochip_add_data(dev
, &lgpio
->chip
, lgpio
);
175 static int loongson_gpio_probe(struct platform_device
*pdev
)
177 void __iomem
*reg_base
;
178 struct loongson_gpio_chip
*lgpio
;
179 struct device
*dev
= &pdev
->dev
;
181 lgpio
= devm_kzalloc(dev
, sizeof(*lgpio
), GFP_KERNEL
);
185 lgpio
->chip_data
= device_get_match_data(dev
);
187 reg_base
= devm_platform_ioremap_resource(pdev
, 0);
188 if (IS_ERR(reg_base
))
189 return PTR_ERR(reg_base
);
191 return loongson_gpio_init(dev
, lgpio
, reg_base
);
194 static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data
= {
195 .label
= "ls2k_gpio",
196 .mode
= BIT_CTRL_MODE
,
200 .inten_offset
= 0x30,
203 static const struct loongson_gpio_chip_data loongson_gpio_ls2k0500_data0
= {
204 .label
= "ls2k0500_gpio",
205 .mode
= BIT_CTRL_MODE
,
209 .inten_offset
= 0xb0,
212 static const struct loongson_gpio_chip_data loongson_gpio_ls2k0500_data1
= {
213 .label
= "ls2k0500_gpio",
214 .mode
= BIT_CTRL_MODE
,
218 .inten_offset
= 0x98,
221 static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0
= {
222 .label
= "ls2k2000_gpio",
223 .mode
= BIT_CTRL_MODE
,
229 static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1
= {
230 .label
= "ls2k2000_gpio",
231 .mode
= BIT_CTRL_MODE
,
237 static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2
= {
238 .label
= "ls2k2000_gpio",
239 .mode
= BIT_CTRL_MODE
,
245 static const struct loongson_gpio_chip_data loongson_gpio_ls3a5000_data
= {
246 .label
= "ls3a5000_gpio",
247 .mode
= BIT_CTRL_MODE
,
253 static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data
= {
254 .label
= "ls7a_gpio",
255 .mode
= BYTE_CTRL_MODE
,
256 .conf_offset
= 0x800,
261 static const struct of_device_id loongson_gpio_of_match
[] = {
263 .compatible
= "loongson,ls2k-gpio",
264 .data
= &loongson_gpio_ls2k_data
,
267 .compatible
= "loongson,ls2k0500-gpio0",
268 .data
= &loongson_gpio_ls2k0500_data0
,
271 .compatible
= "loongson,ls2k0500-gpio1",
272 .data
= &loongson_gpio_ls2k0500_data1
,
275 .compatible
= "loongson,ls2k2000-gpio0",
276 .data
= &loongson_gpio_ls2k2000_data0
,
279 .compatible
= "loongson,ls2k2000-gpio1",
280 .data
= &loongson_gpio_ls2k2000_data1
,
283 .compatible
= "loongson,ls2k2000-gpio2",
284 .data
= &loongson_gpio_ls2k2000_data2
,
287 .compatible
= "loongson,ls3a5000-gpio",
288 .data
= &loongson_gpio_ls3a5000_data
,
291 .compatible
= "loongson,ls7a-gpio",
292 .data
= &loongson_gpio_ls7a_data
,
296 MODULE_DEVICE_TABLE(of
, loongson_gpio_of_match
);
298 static const struct acpi_device_id loongson_gpio_acpi_match
[] = {
301 .driver_data
= (kernel_ulong_t
)&loongson_gpio_ls7a_data
,
305 .driver_data
= (kernel_ulong_t
)&loongson_gpio_ls3a5000_data
,
309 .driver_data
= (kernel_ulong_t
)&loongson_gpio_ls2k2000_data0
,
313 .driver_data
= (kernel_ulong_t
)&loongson_gpio_ls2k2000_data1
,
317 .driver_data
= (kernel_ulong_t
)&loongson_gpio_ls2k2000_data2
,
321 MODULE_DEVICE_TABLE(acpi
, loongson_gpio_acpi_match
);
323 static struct platform_driver loongson_gpio_driver
= {
325 .name
= "loongson-gpio",
326 .of_match_table
= loongson_gpio_of_match
,
327 .acpi_match_table
= loongson_gpio_acpi_match
,
329 .probe
= loongson_gpio_probe
,
332 static int __init
loongson_gpio_setup(void)
334 return platform_driver_register(&loongson_gpio_driver
);
336 postcore_initcall(loongson_gpio_setup
);
338 MODULE_DESCRIPTION("Loongson gpio driver");
339 MODULE_LICENSE("GPL");