1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/types.h> |
4 | #include <linux/io.h> |
5 | #include <linux/bits.h> |
6 | #include <linux/gpio/driver.h> |
7 | #include <linux/mod_devicetable.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/property.h> |
11 | |
12 | #define AIROHA_GPIO_MAX 32 |
13 | |
14 | /** |
15 | * struct airoha_gpio_ctrl - Airoha GPIO driver data |
16 | * @gc: Associated gpio_chip instance. |
17 | * @data: The data register. |
18 | * @dir: [0] The direction register for the lower 16 pins. |
19 | * [1]: The direction register for the higher 16 pins. |
20 | * @output: The output enable register. |
21 | */ |
22 | struct airoha_gpio_ctrl { |
23 | struct gpio_chip gc; |
24 | void __iomem *data; |
25 | void __iomem *dir[2]; |
26 | void __iomem *output; |
27 | }; |
28 | |
29 | static struct airoha_gpio_ctrl *gc_to_ctrl(struct gpio_chip *gc) |
30 | { |
31 | return container_of(gc, struct airoha_gpio_ctrl, gc); |
32 | } |
33 | |
34 | static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio, |
35 | int val, int out) |
36 | { |
37 | struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); |
38 | u32 dir = ioread32(ctrl->dir[gpio / 16]); |
39 | u32 output = ioread32(ctrl->output); |
40 | u32 mask = BIT((gpio % 16) * 2); |
41 | |
42 | if (out) { |
43 | dir |= mask; |
44 | output |= BIT(gpio); |
45 | } else { |
46 | dir &= ~mask; |
47 | output &= ~BIT(gpio); |
48 | } |
49 | |
50 | iowrite32(dir, ctrl->dir[gpio / 16]); |
51 | |
52 | if (out) |
53 | gc->set(gc, gpio, val); |
54 | |
55 | iowrite32(output, ctrl->output); |
56 | |
57 | return 0; |
58 | } |
59 | |
60 | static int airoha_dir_out(struct gpio_chip *gc, unsigned int gpio, |
61 | int val) |
62 | { |
63 | return airoha_dir_set(gc, gpio, val, out: 1); |
64 | } |
65 | |
66 | static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio) |
67 | { |
68 | return airoha_dir_set(gc, gpio, val: 0, out: 0); |
69 | } |
70 | |
71 | static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio) |
72 | { |
73 | struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); |
74 | u32 dir = ioread32(ctrl->dir[gpio / 16]); |
75 | u32 mask = BIT((gpio % 16) * 2); |
76 | |
77 | return (dir & mask) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; |
78 | } |
79 | |
80 | static int airoha_gpio_probe(struct platform_device *pdev) |
81 | { |
82 | struct device *dev = &pdev->dev; |
83 | struct airoha_gpio_ctrl *ctrl; |
84 | int err; |
85 | |
86 | ctrl = devm_kzalloc(dev, size: sizeof(*ctrl), GFP_KERNEL); |
87 | if (!ctrl) |
88 | return -ENOMEM; |
89 | |
90 | ctrl->data = devm_platform_ioremap_resource(pdev, index: 0); |
91 | if (IS_ERR(ptr: ctrl->data)) |
92 | return PTR_ERR(ptr: ctrl->data); |
93 | |
94 | ctrl->dir[0] = devm_platform_ioremap_resource(pdev, index: 1); |
95 | if (IS_ERR(ptr: ctrl->dir[0])) |
96 | return PTR_ERR(ptr: ctrl->dir[0]); |
97 | |
98 | ctrl->dir[1] = devm_platform_ioremap_resource(pdev, index: 2); |
99 | if (IS_ERR(ptr: ctrl->dir[1])) |
100 | return PTR_ERR(ptr: ctrl->dir[1]); |
101 | |
102 | ctrl->output = devm_platform_ioremap_resource(pdev, index: 3); |
103 | if (IS_ERR(ptr: ctrl->output)) |
104 | return PTR_ERR(ptr: ctrl->output); |
105 | |
106 | err = bgpio_init(gc: &ctrl->gc, dev, sz: 4, dat: ctrl->data, NULL, |
107 | NULL, NULL, NULL, flags: 0); |
108 | if (err) |
109 | return dev_err_probe(dev, err, fmt: "unable to init generic GPIO" ); |
110 | |
111 | ctrl->gc.ngpio = AIROHA_GPIO_MAX; |
112 | ctrl->gc.owner = THIS_MODULE; |
113 | ctrl->gc.direction_output = airoha_dir_out; |
114 | ctrl->gc.direction_input = airoha_dir_in; |
115 | ctrl->gc.get_direction = airoha_get_dir; |
116 | |
117 | return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); |
118 | } |
119 | |
120 | static const struct of_device_id airoha_gpio_of_match[] = { |
121 | { .compatible = "airoha,en7523-gpio" }, |
122 | { } |
123 | }; |
124 | MODULE_DEVICE_TABLE(of, airoha_gpio_of_match); |
125 | |
126 | static struct platform_driver airoha_gpio_driver = { |
127 | .driver = { |
128 | .name = "airoha-gpio" , |
129 | .of_match_table = airoha_gpio_of_match, |
130 | }, |
131 | .probe = airoha_gpio_probe, |
132 | }; |
133 | module_platform_driver(airoha_gpio_driver); |
134 | |
135 | MODULE_DESCRIPTION("Airoha GPIO support" ); |
136 | MODULE_AUTHOR("John Crispin <john@phrozen.org>" ); |
137 | MODULE_LICENSE("GPL v2" ); |
138 | |