xref: /openbmc/linux/drivers/gpio/gpio-visconti.c (revision 0bf49ffb)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Toshiba Visconti GPIO Support
4  *
5  * (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation
6  * (C) Copyright 2020 TOSHIBA CORPORATION
7  *
8  * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
9  */
10 
11 #include <linux/gpio/driver.h>
12 #include <linux/init.h>
13 #include <linux/interrupt.h>
14 #include <linux/module.h>
15 #include <linux/io.h>
16 #include <linux/of_irq.h>
17 #include <linux/platform_device.h>
18 #include <linux/bitops.h>
19 
20 /* register offset */
21 #define GPIO_DIR	0x00
22 #define GPIO_IDATA	0x08
23 #define GPIO_ODATA	0x10
24 #define GPIO_OSET	0x18
25 #define GPIO_OCLR	0x20
26 #define GPIO_INTMODE	0x30
27 
28 #define BASE_HW_IRQ 24
29 
30 struct visconti_gpio {
31 	void __iomem *base;
32 	spinlock_t lock; /* protect gpio register */
33 	struct gpio_chip gpio_chip;
34 	struct irq_chip irq_chip;
35 };
36 
37 static int visconti_gpio_irq_set_type(struct irq_data *d, unsigned int type)
38 {
39 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
40 	struct visconti_gpio *priv = gpiochip_get_data(gc);
41 	u32 offset = irqd_to_hwirq(d);
42 	u32 bit = BIT(offset);
43 	u32 intc_type = IRQ_TYPE_EDGE_RISING;
44 	u32 intmode, odata;
45 	int ret = 0;
46 	unsigned long flags;
47 
48 	spin_lock_irqsave(&priv->lock, flags);
49 
50 	odata = readl(priv->base + GPIO_ODATA);
51 	intmode = readl(priv->base + GPIO_INTMODE);
52 
53 	switch (type) {
54 	case IRQ_TYPE_EDGE_RISING:
55 		odata &= ~bit;
56 		intmode &= ~bit;
57 		break;
58 	case IRQ_TYPE_EDGE_FALLING:
59 		odata |= bit;
60 		intmode &= ~bit;
61 		break;
62 	case IRQ_TYPE_EDGE_BOTH:
63 		intmode |= bit;
64 		break;
65 	case IRQ_TYPE_LEVEL_HIGH:
66 		intc_type = IRQ_TYPE_LEVEL_HIGH;
67 		odata &= ~bit;
68 		intmode &= ~bit;
69 		break;
70 	case IRQ_TYPE_LEVEL_LOW:
71 		intc_type = IRQ_TYPE_LEVEL_HIGH;
72 		odata |= bit;
73 		intmode &= ~bit;
74 		break;
75 	default:
76 		ret = -EINVAL;
77 		goto err;
78 	}
79 
80 	writel(odata, priv->base + GPIO_ODATA);
81 	writel(intmode, priv->base + GPIO_INTMODE);
82 	irq_set_irq_type(offset, intc_type);
83 
84 	ret = irq_chip_set_type_parent(d, type);
85 err:
86 	spin_unlock_irqrestore(&priv->lock, flags);
87 	return ret;
88 }
89 
90 static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
91 					       unsigned int child,
92 					       unsigned int child_type,
93 					       unsigned int *parent,
94 					       unsigned int *parent_type)
95 {
96 	/* Interrupts 0..15 mapped to interrupts 24..39 on the GIC */
97 	if (child < 16) {
98 		/* All these interrupts are level high in the CPU */
99 		*parent_type = IRQ_TYPE_LEVEL_HIGH;
100 		*parent = child + BASE_HW_IRQ;
101 		return 0;
102 	}
103 	return -EINVAL;
104 }
105 
106 static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
107 						  unsigned int parent_hwirq,
108 						  unsigned int parent_type)
109 {
110 	struct irq_fwspec *fwspec;
111 
112 	fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
113 	if (!fwspec)
114 		return NULL;
115 
116 	fwspec->fwnode = chip->irq.parent_domain->fwnode;
117 	fwspec->param_count = 3;
118 	fwspec->param[0] = 0;
119 	fwspec->param[1] = parent_hwirq;
120 	fwspec->param[2] = parent_type;
121 
122 	return fwspec;
123 }
124 
125 static int visconti_gpio_probe(struct platform_device *pdev)
126 {
127 	struct device *dev = &pdev->dev;
128 	struct visconti_gpio *priv;
129 	struct irq_chip *irq_chip;
130 	struct gpio_irq_chip *girq;
131 	struct irq_domain *parent;
132 	struct device_node *irq_parent;
133 	struct fwnode_handle *fwnode;
134 	int ret;
135 
136 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
137 	if (!priv)
138 		return -ENOMEM;
139 
140 	spin_lock_init(&priv->lock);
141 
142 	priv->base = devm_platform_ioremap_resource(pdev, 0);
143 	if (IS_ERR(priv->base))
144 		return PTR_ERR(priv->base);
145 
146 	irq_parent = of_irq_find_parent(dev->of_node);
147 	if (!irq_parent) {
148 		dev_err(dev, "No IRQ parent node\n");
149 		return -ENODEV;
150 	}
151 
152 	parent = irq_find_host(irq_parent);
153 	if (!parent) {
154 		dev_err(dev, "No IRQ parent domain\n");
155 		return -ENODEV;
156 	}
157 
158 	fwnode = of_node_to_fwnode(irq_parent);
159 	of_node_put(irq_parent);
160 
161 	ret = bgpio_init(&priv->gpio_chip, dev, 4,
162 			 priv->base + GPIO_IDATA,
163 			 priv->base + GPIO_OSET,
164 			 priv->base + GPIO_OCLR,
165 			 priv->base + GPIO_DIR,
166 			 NULL,
167 			 0);
168 	if (ret) {
169 		dev_err(dev, "unable to init generic GPIO\n");
170 		return ret;
171 	}
172 
173 	irq_chip = &priv->irq_chip;
174 	irq_chip->name = dev_name(dev);
175 	irq_chip->irq_mask = irq_chip_mask_parent;
176 	irq_chip->irq_unmask = irq_chip_unmask_parent;
177 	irq_chip->irq_eoi = irq_chip_eoi_parent;
178 	irq_chip->irq_set_type = visconti_gpio_irq_set_type;
179 	irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
180 
181 	girq = &priv->gpio_chip.irq;
182 	girq->chip = irq_chip;
183 	girq->fwnode = fwnode;
184 	girq->parent_domain = parent;
185 	girq->child_to_parent_hwirq = visconti_gpio_child_to_parent_hwirq;
186 	girq->populate_parent_alloc_arg = visconti_gpio_populate_parent_fwspec;
187 	girq->default_type = IRQ_TYPE_NONE;
188 	girq->handler = handle_level_irq;
189 
190 	ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
191 	if (ret) {
192 		dev_err(dev, "failed to add GPIO chip\n");
193 		return ret;
194 	}
195 
196 	platform_set_drvdata(pdev, priv);
197 
198 	return ret;
199 }
200 
201 static const struct of_device_id visconti_gpio_of_match[] = {
202 	{ .compatible = "toshiba,gpio-tmpv7708", },
203 	{ /* end of table */ }
204 };
205 MODULE_DEVICE_TABLE(of, visconti_gpio_of_match);
206 
207 static struct platform_driver visconti_gpio_driver = {
208 	.probe		= visconti_gpio_probe,
209 	.driver		= {
210 		.name	= "visconti_gpio",
211 		.of_match_table = of_match_ptr(visconti_gpio_of_match),
212 	}
213 };
214 module_platform_driver(visconti_gpio_driver);
215 
216 MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
217 MODULE_DESCRIPTION("Toshiba Visconti GPIO Driver");
218 MODULE_LICENSE("GPL v2");
219