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/gpio/generic.h>
8 #include <linux/mod_devicetable.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/property.h>
12
13 #define AIROHA_GPIO_MAX 32
14
15 /**
16 * struct airoha_gpio_ctrl - Airoha GPIO driver data
17 * @gen_gc: Associated gpio_generic_chip instance.
18 * @data: The data register.
19 * @dir: [0] The direction register for the lower 16 pins.
20 * [1]: The direction register for the higher 16 pins.
21 * @output: The output enable register.
22 */
23 struct airoha_gpio_ctrl {
24 struct gpio_generic_chip gen_gc;
25 void __iomem *data;
26 void __iomem *dir[2];
27 void __iomem *output;
28 };
29
airoha_dir_set(struct gpio_chip * gc,unsigned int gpio,int val,int out)30 static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio,
31 int val, int out)
32 {
33 struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc);
34 u32 dir = ioread32(ctrl->dir[gpio / 16]);
35 u32 output = ioread32(ctrl->output);
36 u32 mask = BIT((gpio % 16) * 2);
37
38 if (out) {
39 dir |= mask;
40 output |= BIT(gpio);
41 } else {
42 dir &= ~mask;
43 output &= ~BIT(gpio);
44 }
45
46 iowrite32(dir, ctrl->dir[gpio / 16]);
47
48 if (out)
49 gpio_generic_chip_set(&ctrl->gen_gc, gpio, val);
50
51 iowrite32(output, ctrl->output);
52
53 return 0;
54 }
55
airoha_dir_out(struct gpio_chip * gc,unsigned int gpio,int val)56 static int airoha_dir_out(struct gpio_chip *gc, unsigned int gpio,
57 int val)
58 {
59 return airoha_dir_set(gc, gpio, val, 1);
60 }
61
airoha_dir_in(struct gpio_chip * gc,unsigned int gpio)62 static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio)
63 {
64 return airoha_dir_set(gc, gpio, 0, 0);
65 }
66
airoha_get_dir(struct gpio_chip * gc,unsigned int gpio)67 static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio)
68 {
69 struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc);
70 u32 dir = ioread32(ctrl->dir[gpio / 16]);
71 u32 mask = BIT((gpio % 16) * 2);
72
73 return (dir & mask) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
74 }
75
airoha_gpio_probe(struct platform_device * pdev)76 static int airoha_gpio_probe(struct platform_device *pdev)
77 {
78 struct gpio_generic_chip_config config = { };
79 struct device *dev = &pdev->dev;
80 struct airoha_gpio_ctrl *ctrl;
81 int err;
82
83 ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
84 if (!ctrl)
85 return -ENOMEM;
86
87 ctrl->data = devm_platform_ioremap_resource(pdev, 0);
88 if (IS_ERR(ctrl->data))
89 return PTR_ERR(ctrl->data);
90
91 ctrl->dir[0] = devm_platform_ioremap_resource(pdev, 1);
92 if (IS_ERR(ctrl->dir[0]))
93 return PTR_ERR(ctrl->dir[0]);
94
95 ctrl->dir[1] = devm_platform_ioremap_resource(pdev, 2);
96 if (IS_ERR(ctrl->dir[1]))
97 return PTR_ERR(ctrl->dir[1]);
98
99 ctrl->output = devm_platform_ioremap_resource(pdev, 3);
100 if (IS_ERR(ctrl->output))
101 return PTR_ERR(ctrl->output);
102
103 config.dev = dev;
104 config.sz = 4;
105 config.dat = ctrl->data;
106
107 err = gpio_generic_chip_init(&ctrl->gen_gc, &config);
108 if (err)
109 return dev_err_probe(dev, err, "unable to init generic GPIO");
110
111 ctrl->gen_gc.gc.ngpio = AIROHA_GPIO_MAX;
112 ctrl->gen_gc.gc.owner = THIS_MODULE;
113 ctrl->gen_gc.gc.direction_output = airoha_dir_out;
114 ctrl->gen_gc.gc.direction_input = airoha_dir_in;
115 ctrl->gen_gc.gc.get_direction = airoha_get_dir;
116
117 return devm_gpiochip_add_data(dev, &ctrl->gen_gc.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