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