1
2
3
4
5
6
7
8
9
10#include <linux/acpi.h>
11#include <linux/bitfield.h>
12#include <linux/device.h>
13#include <linux/err.h>
14#include <linux/gpio/driver.h>
15#include <linux/interrupt.h>
16#include <linux/io.h>
17#include <linux/irq.h>
18#include <linux/irqdomain.h>
19#include <linux/irqreturn.h>
20#include <linux/platform_device.h>
21#include <linux/property.h>
22
23#include "mlxbf_gige.h"
24#include "mlxbf_gige_regs.h"
25
26#define MLXBF_GIGE_GPIO_CAUSE_FALL_EN 0x48
27#define MLXBF_GIGE_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80
28#define MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0 0x94
29#define MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE 0x98
30
31static void mlxbf_gige_gpio_enable(struct mlxbf_gige *priv)
32{
33 unsigned long flags;
34 u32 val;
35
36 spin_lock_irqsave(&priv->gpio_lock, flags);
37 val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
38 val |= priv->phy_int_gpio_mask;
39 writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
40
41
42
43
44
45 val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_FALL_EN);
46 val |= priv->phy_int_gpio_mask;
47 writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_FALL_EN);
48
49
50 val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
51 val |= priv->phy_int_gpio_mask;
52 writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
53 spin_unlock_irqrestore(&priv->gpio_lock, flags);
54}
55
56static void mlxbf_gige_gpio_disable(struct mlxbf_gige *priv)
57{
58 unsigned long flags;
59 u32 val;
60
61 spin_lock_irqsave(&priv->gpio_lock, flags);
62 val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
63 val &= ~priv->phy_int_gpio_mask;
64 writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0);
65 spin_unlock_irqrestore(&priv->gpio_lock, flags);
66}
67
68static irqreturn_t mlxbf_gige_gpio_handler(int irq, void *ptr)
69{
70 struct mlxbf_gige *priv;
71 u32 val;
72
73 priv = ptr;
74
75
76
77
78 val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CAUSE_EVTEN0);
79 if (!(val & priv->phy_int_gpio_mask))
80 return IRQ_NONE;
81
82
83
84
85 val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
86 val |= priv->phy_int_gpio_mask;
87 writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE);
88
89 generic_handle_irq(priv->phy_irq);
90
91 return IRQ_HANDLED;
92}
93
94static void mlxbf_gige_gpio_mask(struct irq_data *irqd)
95{
96 struct mlxbf_gige *priv = irq_data_get_irq_chip_data(irqd);
97
98 mlxbf_gige_gpio_disable(priv);
99}
100
101static void mlxbf_gige_gpio_unmask(struct irq_data *irqd)
102{
103 struct mlxbf_gige *priv = irq_data_get_irq_chip_data(irqd);
104
105 mlxbf_gige_gpio_enable(priv);
106}
107
108static struct irq_chip mlxbf_gige_gpio_chip = {
109 .name = "mlxbf_gige_phy",
110 .irq_mask = mlxbf_gige_gpio_mask,
111 .irq_unmask = mlxbf_gige_gpio_unmask,
112};
113
114static int mlxbf_gige_gpio_domain_map(struct irq_domain *d,
115 unsigned int irq,
116 irq_hw_number_t hwirq)
117{
118 irq_set_chip_data(irq, d->host_data);
119 irq_set_chip_and_handler(irq, &mlxbf_gige_gpio_chip, handle_simple_irq);
120 irq_set_noprobe(irq);
121
122 return 0;
123}
124
125static const struct irq_domain_ops mlxbf_gige_gpio_domain_ops = {
126 .map = mlxbf_gige_gpio_domain_map,
127 .xlate = irq_domain_xlate_twocell,
128};
129
130#ifdef CONFIG_ACPI
131static int mlxbf_gige_gpio_resources(struct acpi_resource *ares,
132 void *data)
133{
134 struct acpi_resource_gpio *gpio;
135 u32 *phy_int_gpio = data;
136
137 if (ares->type == ACPI_RESOURCE_TYPE_GPIO) {
138 gpio = &ares->data.gpio;
139 *phy_int_gpio = gpio->pin_table[0];
140 }
141
142 return 1;
143}
144#endif
145
146void mlxbf_gige_gpio_free(struct mlxbf_gige *priv)
147{
148 irq_dispose_mapping(priv->phy_irq);
149 irq_domain_remove(priv->irqdomain);
150}
151
152int mlxbf_gige_gpio_init(struct platform_device *pdev,
153 struct mlxbf_gige *priv)
154{
155 struct device *dev = &pdev->dev;
156 struct resource *res;
157 u32 phy_int_gpio = 0;
158 int ret;
159
160 LIST_HEAD(resources);
161
162 res = platform_get_resource(pdev, IORESOURCE_MEM, MLXBF_GIGE_RES_GPIO0);
163 if (!res)
164 return -ENODEV;
165
166 priv->gpio_io = devm_ioremap(dev, res->start, resource_size(res));
167 if (!priv->gpio_io)
168 return -ENOMEM;
169
170#ifdef CONFIG_ACPI
171 ret = acpi_dev_get_resources(ACPI_COMPANION(dev),
172 &resources, mlxbf_gige_gpio_resources,
173 &phy_int_gpio);
174 acpi_dev_free_resource_list(&resources);
175 if (ret < 0 || !phy_int_gpio) {
176 dev_err(dev, "Error retrieving the gpio phy pin");
177 return -EINVAL;
178 }
179#endif
180
181 priv->phy_int_gpio_mask = BIT(phy_int_gpio);
182
183 mlxbf_gige_gpio_disable(priv);
184
185 priv->hw_phy_irq = platform_get_irq(pdev, MLXBF_GIGE_PHY_INT_N);
186
187 priv->irqdomain = irq_domain_add_simple(NULL, 1, 0,
188 &mlxbf_gige_gpio_domain_ops,
189 priv);
190 if (!priv->irqdomain) {
191 dev_err(dev, "Failed to add IRQ domain\n");
192 return -ENOMEM;
193 }
194
195 priv->phy_irq = irq_create_mapping(priv->irqdomain, 0);
196 if (!priv->phy_irq) {
197 irq_domain_remove(priv->irqdomain);
198 priv->irqdomain = NULL;
199 dev_err(dev, "Error mapping PHY IRQ\n");
200 return -EINVAL;
201 }
202
203 ret = devm_request_irq(dev, priv->hw_phy_irq, mlxbf_gige_gpio_handler,
204 IRQF_ONESHOT | IRQF_SHARED, "mlxbf_gige_phy", priv);
205 if (ret) {
206 dev_err(dev, "Failed to request PHY IRQ");
207 mlxbf_gige_gpio_free(priv);
208 return ret;
209 }
210
211 return ret;
212}
213