diff options
author | Tomasz Figa <t.figa@samsung.com> | 2012-10-11 04:11:16 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2012-10-15 03:10:12 -0400 |
commit | 595be7268a85735d229451821b56f122d09d7bdc (patch) | |
tree | eefa13ddc638a1fc5e987dee179ac951aea7b90b /drivers/pinctrl | |
parent | 1b6056d6db2426cd612f03dabacf655ecb6a27ae (diff) |
pinctrl: exynos: Use one IRQ domain per pin bank
Instead of registering one IRQ domain for all pin banks of a pin
controller, this patch implements registration of per-bank domains.
At a cost of a little memory overhead (~2.5KiB for all GPIO interrupts
of Exynos4x12) it simplifies driver code and device tree sources,
because GPIO interrupts can be now specified per banks.
Example:
device {
/* ... */
interrupt-parent = <&gpa1>;
interrupts = <3 0>;
/* ... */
};
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Reviewed-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Thomas Abraham <thomas.abraham@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r-- | drivers/pinctrl/pinctrl-exynos.c | 117 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-exynos.h | 12 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-samsung.c | 4 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-samsung.h | 7 |
4 files changed, 35 insertions, 105 deletions
diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index bd9f1307a793..be757b1d4fcd 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c | |||
@@ -40,46 +40,46 @@ static const struct of_device_id exynos_wkup_irq_ids[] = { | |||
40 | 40 | ||
41 | static void exynos_gpio_irq_unmask(struct irq_data *irqd) | 41 | static void exynos_gpio_irq_unmask(struct irq_data *irqd) |
42 | { | 42 | { |
43 | struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; | 43 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
44 | struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); | 44 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
45 | unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset; | 45 | unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset; |
46 | unsigned long mask; | 46 | unsigned long mask; |
47 | 47 | ||
48 | mask = readl(d->virt_base + reg_mask); | 48 | mask = readl(d->virt_base + reg_mask); |
49 | mask &= ~(1 << edata->pin); | 49 | mask &= ~(1 << irqd->hwirq); |
50 | writel(mask, d->virt_base + reg_mask); | 50 | writel(mask, d->virt_base + reg_mask); |
51 | } | 51 | } |
52 | 52 | ||
53 | static void exynos_gpio_irq_mask(struct irq_data *irqd) | 53 | static void exynos_gpio_irq_mask(struct irq_data *irqd) |
54 | { | 54 | { |
55 | struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; | 55 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
56 | struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); | 56 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
57 | unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset; | 57 | unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset; |
58 | unsigned long mask; | 58 | unsigned long mask; |
59 | 59 | ||
60 | mask = readl(d->virt_base + reg_mask); | 60 | mask = readl(d->virt_base + reg_mask); |
61 | mask |= 1 << edata->pin; | 61 | mask |= 1 << irqd->hwirq; |
62 | writel(mask, d->virt_base + reg_mask); | 62 | writel(mask, d->virt_base + reg_mask); |
63 | } | 63 | } |
64 | 64 | ||
65 | static void exynos_gpio_irq_ack(struct irq_data *irqd) | 65 | static void exynos_gpio_irq_ack(struct irq_data *irqd) |
66 | { | 66 | { |
67 | struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; | 67 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
68 | struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); | 68 | struct samsung_pinctrl_drv_data *d = bank->drvdata; |
69 | unsigned long reg_pend = d->ctrl->geint_pend + edata->eint_offset; | 69 | unsigned long reg_pend = d->ctrl->geint_pend + bank->eint_offset; |
70 | 70 | ||
71 | writel(1 << edata->pin, d->virt_base + reg_pend); | 71 | writel(1 << irqd->hwirq, d->virt_base + reg_pend); |
72 | } | 72 | } |
73 | 73 | ||
74 | static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) | 74 | static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) |
75 | { | 75 | { |
76 | struct samsung_pinctrl_drv_data *d = irqd->domain->host_data; | 76 | struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); |
77 | struct samsung_pinctrl_drv_data *d = bank->drvdata; | ||
77 | struct samsung_pin_ctrl *ctrl = d->ctrl; | 78 | struct samsung_pin_ctrl *ctrl = d->ctrl; |
78 | struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd); | 79 | unsigned int pin = irqd->hwirq; |
79 | struct samsung_pin_bank *bank = edata->bank; | 80 | unsigned int shift = EXYNOS_EINT_CON_LEN * pin; |
80 | unsigned int shift = EXYNOS_EINT_CON_LEN * edata->pin; | ||
81 | unsigned int con, trig_type; | 81 | unsigned int con, trig_type; |
82 | unsigned long reg_con = ctrl->geint_con + edata->eint_offset; | 82 | unsigned long reg_con = ctrl->geint_con + bank->eint_offset; |
83 | unsigned int mask; | 83 | unsigned int mask; |
84 | 84 | ||
85 | switch (type) { | 85 | switch (type) { |
@@ -114,7 +114,7 @@ static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) | |||
114 | writel(con, d->virt_base + reg_con); | 114 | writel(con, d->virt_base + reg_con); |
115 | 115 | ||
116 | reg_con = bank->pctl_offset; | 116 | reg_con = bank->pctl_offset; |
117 | shift = edata->pin * bank->func_width; | 117 | shift = pin * bank->func_width; |
118 | mask = (1 << bank->func_width) - 1; | 118 | mask = (1 << bank->func_width) - 1; |
119 | 119 | ||
120 | con = readl(d->virt_base + reg_con); | 120 | con = readl(d->virt_base + reg_con); |
@@ -136,81 +136,23 @@ static struct irq_chip exynos_gpio_irq_chip = { | |||
136 | .irq_set_type = exynos_gpio_irq_set_type, | 136 | .irq_set_type = exynos_gpio_irq_set_type, |
137 | }; | 137 | }; |
138 | 138 | ||
139 | /* | ||
140 | * given a controller-local external gpio interrupt number, prepare the handler | ||
141 | * data for it. | ||
142 | */ | ||
143 | static struct exynos_geint_data *exynos_get_eint_data(irq_hw_number_t hw, | ||
144 | struct samsung_pinctrl_drv_data *d) | ||
145 | { | ||
146 | struct samsung_pin_bank *bank = d->ctrl->pin_banks; | ||
147 | struct exynos_geint_data *eint_data; | ||
148 | unsigned int nr_banks = d->ctrl->nr_banks, idx; | ||
149 | unsigned int irq_base = 0; | ||
150 | |||
151 | if (hw >= d->ctrl->nr_gint) { | ||
152 | dev_err(d->dev, "unsupported ext-gpio interrupt\n"); | ||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | for (idx = 0; idx < nr_banks; idx++, bank++) { | ||
157 | if (bank->eint_type != EINT_TYPE_GPIO) | ||
158 | continue; | ||
159 | if ((hw >= irq_base) && (hw < (irq_base + bank->nr_pins))) | ||
160 | break; | ||
161 | irq_base += bank->nr_pins; | ||
162 | } | ||
163 | |||
164 | if (idx == nr_banks) { | ||
165 | dev_err(d->dev, "pin bank not found for ext-gpio interrupt\n"); | ||
166 | return NULL; | ||
167 | } | ||
168 | |||
169 | eint_data = devm_kzalloc(d->dev, sizeof(*eint_data), GFP_KERNEL); | ||
170 | if (!eint_data) { | ||
171 | dev_err(d->dev, "no memory for eint-gpio data\n"); | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | eint_data->bank = bank; | ||
176 | eint_data->pin = hw - irq_base; | ||
177 | eint_data->eint_offset = bank->eint_offset; | ||
178 | return eint_data; | ||
179 | } | ||
180 | |||
181 | static int exynos_gpio_irq_map(struct irq_domain *h, unsigned int virq, | 139 | static int exynos_gpio_irq_map(struct irq_domain *h, unsigned int virq, |
182 | irq_hw_number_t hw) | 140 | irq_hw_number_t hw) |
183 | { | 141 | { |
184 | struct samsung_pinctrl_drv_data *d = h->host_data; | 142 | struct samsung_pin_bank *b = h->host_data; |
185 | struct exynos_geint_data *eint_data; | ||
186 | |||
187 | eint_data = exynos_get_eint_data(hw, d); | ||
188 | if (!eint_data) | ||
189 | return -EINVAL; | ||
190 | 143 | ||
191 | irq_set_handler_data(virq, eint_data); | 144 | irq_set_chip_data(virq, b); |
192 | irq_set_chip_data(virq, h->host_data); | ||
193 | irq_set_chip_and_handler(virq, &exynos_gpio_irq_chip, | 145 | irq_set_chip_and_handler(virq, &exynos_gpio_irq_chip, |
194 | handle_level_irq); | 146 | handle_level_irq); |
195 | set_irq_flags(virq, IRQF_VALID); | 147 | set_irq_flags(virq, IRQF_VALID); |
196 | return 0; | 148 | return 0; |
197 | } | 149 | } |
198 | 150 | ||
199 | static void exynos_gpio_irq_unmap(struct irq_domain *h, unsigned int virq) | ||
200 | { | ||
201 | struct samsung_pinctrl_drv_data *d = h->host_data; | ||
202 | struct exynos_geint_data *eint_data; | ||
203 | |||
204 | eint_data = irq_get_handler_data(virq); | ||
205 | devm_kfree(d->dev, eint_data); | ||
206 | } | ||
207 | |||
208 | /* | 151 | /* |
209 | * irq domain callbacks for external gpio interrupt controller. | 152 | * irq domain callbacks for external gpio interrupt controller. |
210 | */ | 153 | */ |
211 | static const struct irq_domain_ops exynos_gpio_irqd_ops = { | 154 | static const struct irq_domain_ops exynos_gpio_irqd_ops = { |
212 | .map = exynos_gpio_irq_map, | 155 | .map = exynos_gpio_irq_map, |
213 | .unmap = exynos_gpio_irq_unmap, | ||
214 | .xlate = irq_domain_xlate_twocell, | 156 | .xlate = irq_domain_xlate_twocell, |
215 | }; | 157 | }; |
216 | 158 | ||
@@ -229,7 +171,7 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) | |||
229 | return IRQ_HANDLED; | 171 | return IRQ_HANDLED; |
230 | bank += (group - 1); | 172 | bank += (group - 1); |
231 | 173 | ||
232 | virq = irq_linear_revmap(d->gpio_irqd, bank->irq_base + pin); | 174 | virq = irq_linear_revmap(bank->irq_domain, pin); |
233 | if (!virq) | 175 | if (!virq) |
234 | return IRQ_NONE; | 176 | return IRQ_NONE; |
235 | generic_handle_irq(virq); | 177 | generic_handle_irq(virq); |
@@ -242,8 +184,10 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) | |||
242 | */ | 184 | */ |
243 | static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) | 185 | static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) |
244 | { | 186 | { |
187 | struct samsung_pin_bank *bank; | ||
245 | struct device *dev = d->dev; | 188 | struct device *dev = d->dev; |
246 | unsigned int ret; | 189 | unsigned int ret; |
190 | unsigned int i; | ||
247 | 191 | ||
248 | if (!d->irq) { | 192 | if (!d->irq) { |
249 | dev_err(dev, "irq number not available\n"); | 193 | dev_err(dev, "irq number not available\n"); |
@@ -257,11 +201,16 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d) | |||
257 | return -ENXIO; | 201 | return -ENXIO; |
258 | } | 202 | } |
259 | 203 | ||
260 | d->gpio_irqd = irq_domain_add_linear(dev->of_node, d->ctrl->nr_gint, | 204 | bank = d->ctrl->pin_banks; |
261 | &exynos_gpio_irqd_ops, d); | 205 | for (i = 0; i < d->ctrl->nr_banks; ++i, ++bank) { |
262 | if (!d->gpio_irqd) { | 206 | if (bank->eint_type != EINT_TYPE_GPIO) |
263 | dev_err(dev, "gpio irq domain allocation failed\n"); | 207 | continue; |
264 | return -ENXIO; | 208 | bank->irq_domain = irq_domain_add_linear(bank->of_node, |
209 | bank->nr_pins, &exynos_gpio_irqd_ops, bank); | ||
210 | if (!bank->irq_domain) { | ||
211 | dev_err(dev, "gpio irq domain add failed\n"); | ||
212 | return -ENXIO; | ||
213 | } | ||
265 | } | 214 | } |
266 | 215 | ||
267 | return 0; | 216 | return 0; |
diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h index 5d8e380fbadf..f05efa074658 100644 --- a/drivers/pinctrl/pinctrl-exynos.h +++ b/drivers/pinctrl/pinctrl-exynos.h | |||
@@ -74,18 +74,6 @@ | |||
74 | } | 74 | } |
75 | 75 | ||
76 | /** | 76 | /** |
77 | * struct exynos_geint_data: gpio eint specific data for irq_chip callbacks. | ||
78 | * @bank: pin bank from which this gpio interrupt originates. | ||
79 | * @pin: pin number within the bank. | ||
80 | * @eint_offset: offset to be added to the con/pend/mask register bank base. | ||
81 | */ | ||
82 | struct exynos_geint_data { | ||
83 | struct samsung_pin_bank *bank; | ||
84 | u32 pin; | ||
85 | u32 eint_offset; | ||
86 | }; | ||
87 | |||
88 | /** | ||
89 | * struct exynos_weint_data: irq specific data for all the wakeup interrupts | 77 | * struct exynos_weint_data: irq specific data for all the wakeup interrupts |
90 | * generated by the external wakeup interrupt controller. | 78 | * generated by the external wakeup interrupt controller. |
91 | * @domain: irq domain representing the external wakeup interrupts | 79 | * @domain: irq domain representing the external wakeup interrupts |
diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 53493c3c35ff..e1ef5d28094d 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c | |||
@@ -813,10 +813,6 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( | |||
813 | bank->drvdata = d; | 813 | bank->drvdata = d; |
814 | bank->pin_base = ctrl->nr_pins; | 814 | bank->pin_base = ctrl->nr_pins; |
815 | ctrl->nr_pins += bank->nr_pins; | 815 | ctrl->nr_pins += bank->nr_pins; |
816 | if (bank->eint_type == EINT_TYPE_GPIO) { | ||
817 | bank->irq_base = ctrl->nr_gint; | ||
818 | ctrl->nr_gint += bank->nr_pins; | ||
819 | } | ||
820 | } | 816 | } |
821 | 817 | ||
822 | for_each_child_of_node(node, np) { | 818 | for_each_child_of_node(node, np) { |
diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index d77d9bcc5d7d..e56be22302cd 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h | |||
@@ -109,10 +109,10 @@ struct samsung_pinctrl_drv_data; | |||
109 | * @conpdn_width: width of the sleep mode function selector bin field. | 109 | * @conpdn_width: width of the sleep mode function selector bin field. |
110 | * @pudpdn_width: width of the sleep mode pull up/down selector bit field. | 110 | * @pudpdn_width: width of the sleep mode pull up/down selector bit field. |
111 | * @eint_type: type of the external interrupt supported by the bank. | 111 | * @eint_type: type of the external interrupt supported by the bank. |
112 | * @irq_base: starting controller local irq number of the bank. | ||
113 | * @name: name to be prefixed for each pin in this pin bank. | 112 | * @name: name to be prefixed for each pin in this pin bank. |
114 | * @of_node: OF node of the bank. | 113 | * @of_node: OF node of the bank. |
115 | * @drvdata: link to controller driver data | 114 | * @drvdata: link to controller driver data |
115 | * @irq_domain: IRQ domain of the bank. | ||
116 | */ | 116 | */ |
117 | struct samsung_pin_bank { | 117 | struct samsung_pin_bank { |
118 | u32 pctl_offset; | 118 | u32 pctl_offset; |
@@ -125,10 +125,10 @@ struct samsung_pin_bank { | |||
125 | u8 pudpdn_width; | 125 | u8 pudpdn_width; |
126 | enum eint_type eint_type; | 126 | enum eint_type eint_type; |
127 | u32 eint_offset; | 127 | u32 eint_offset; |
128 | u32 irq_base; | ||
129 | char *name; | 128 | char *name; |
130 | struct device_node *of_node; | 129 | struct device_node *of_node; |
131 | struct samsung_pinctrl_drv_data *drvdata; | 130 | struct samsung_pinctrl_drv_data *drvdata; |
131 | struct irq_domain *irq_domain; | ||
132 | }; | 132 | }; |
133 | 133 | ||
134 | /** | 134 | /** |
@@ -137,7 +137,6 @@ struct samsung_pin_bank { | |||
137 | * @nr_banks: number of pin banks. | 137 | * @nr_banks: number of pin banks. |
138 | * @base: starting system wide pin number. | 138 | * @base: starting system wide pin number. |
139 | * @nr_pins: number of pins supported by the controller. | 139 | * @nr_pins: number of pins supported by the controller. |
140 | * @nr_gint: number of external gpio interrupts supported. | ||
141 | * @nr_wint: number of external wakeup interrupts supported. | 140 | * @nr_wint: number of external wakeup interrupts supported. |
142 | * @geint_con: offset of the ext-gpio controller registers. | 141 | * @geint_con: offset of the ext-gpio controller registers. |
143 | * @geint_mask: offset of the ext-gpio interrupt mask registers. | 142 | * @geint_mask: offset of the ext-gpio interrupt mask registers. |
@@ -158,7 +157,6 @@ struct samsung_pin_ctrl { | |||
158 | 157 | ||
159 | u32 base; | 158 | u32 base; |
160 | u32 nr_pins; | 159 | u32 nr_pins; |
161 | u32 nr_gint; | ||
162 | u32 nr_wint; | 160 | u32 nr_wint; |
163 | 161 | ||
164 | u32 geint_con; | 162 | u32 geint_con; |
@@ -205,7 +203,6 @@ struct samsung_pinctrl_drv_data { | |||
205 | const struct samsung_pmx_func *pmx_functions; | 203 | const struct samsung_pmx_func *pmx_functions; |
206 | unsigned int nr_functions; | 204 | unsigned int nr_functions; |
207 | 205 | ||
208 | struct irq_domain *gpio_irqd; | ||
209 | struct irq_domain *wkup_irqd; | 206 | struct irq_domain *wkup_irqd; |
210 | 207 | ||
211 | struct gpio_chip *gc; | 208 | struct gpio_chip *gc; |