diff options
author | Magnus Damm <damm@igel.co.jp> | 2008-12-25 04:17:26 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-01-27 00:49:10 -0500 |
commit | 3292094e88ce6b76714dad8ec4b43d7c5c12ada2 (patch) | |
tree | 0469a3523b1794a9ecb9f5df99ce3d726d442b2a /arch/sh/kernel/gpio.c | |
parent | 0fc64cc0a27288e77ee8e12648d59632649371fc (diff) |
sh: lockless gpio_set_value()
This patch optimizes the gpio data register handling for gpio_set_value().
Instead of using the good old spinlock-plus-read-modify-write strategy
we now use a shadow register and atomic operations.
This improves the bitbanging mmc performance on Migo-R from 26 Kbytes/s
to 40 Kbytes/s.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel/gpio.c')
-rw-r--r-- | arch/sh/kernel/gpio.c | 106 |
1 files changed, 81 insertions, 25 deletions
diff --git a/arch/sh/kernel/gpio.c b/arch/sh/kernel/gpio.c index f8397a09491c..280135673726 100644 --- a/arch/sh/kernel/gpio.c +++ b/arch/sh/kernel/gpio.c | |||
@@ -46,6 +46,62 @@ static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) | |||
46 | return 1; | 46 | return 1; |
47 | } | 47 | } |
48 | 48 | ||
49 | static unsigned long gpio_read_raw_reg(unsigned long reg, | ||
50 | unsigned long reg_width) | ||
51 | { | ||
52 | switch (reg_width) { | ||
53 | case 8: | ||
54 | return ctrl_inb(reg); | ||
55 | case 16: | ||
56 | return ctrl_inw(reg); | ||
57 | case 32: | ||
58 | return ctrl_inl(reg); | ||
59 | } | ||
60 | |||
61 | BUG(); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static void gpio_write_raw_reg(unsigned long reg, | ||
66 | unsigned long reg_width, | ||
67 | unsigned long data) | ||
68 | { | ||
69 | switch (reg_width) { | ||
70 | case 8: | ||
71 | ctrl_outb(data, reg); | ||
72 | return; | ||
73 | case 16: | ||
74 | ctrl_outw(data, reg); | ||
75 | return; | ||
76 | case 32: | ||
77 | ctrl_outl(data, reg); | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | BUG(); | ||
82 | } | ||
83 | |||
84 | static void gpio_write_bit(struct pinmux_data_reg *dr, | ||
85 | unsigned long in_pos, unsigned long value) | ||
86 | { | ||
87 | unsigned long pos; | ||
88 | |||
89 | pos = dr->reg_width - (in_pos + 1); | ||
90 | |||
91 | #ifdef DEBUG | ||
92 | pr_info("write_bit addr = %lx, value = %ld, pos = %ld, " | ||
93 | "r_width = %ld\n", | ||
94 | dr->reg, !!value, pos, dr->reg_width); | ||
95 | #endif | ||
96 | |||
97 | if (value) | ||
98 | set_bit(pos, &dr->reg_shadow); | ||
99 | else | ||
100 | clear_bit(pos, &dr->reg_shadow); | ||
101 | |||
102 | gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow); | ||
103 | } | ||
104 | |||
49 | static int gpio_read_reg(unsigned long reg, unsigned long reg_width, | 105 | static int gpio_read_reg(unsigned long reg, unsigned long reg_width, |
50 | unsigned long field_width, unsigned long in_pos) | 106 | unsigned long field_width, unsigned long in_pos) |
51 | { | 107 | { |
@@ -61,18 +117,7 @@ static int gpio_read_reg(unsigned long reg, unsigned long reg_width, | |||
61 | reg, pos, reg_width, field_width); | 117 | reg, pos, reg_width, field_width); |
62 | #endif | 118 | #endif |
63 | 119 | ||
64 | switch (reg_width) { | 120 | data = gpio_read_raw_reg(reg, reg_width); |
65 | case 8: | ||
66 | data = ctrl_inb(reg); | ||
67 | break; | ||
68 | case 16: | ||
69 | data = ctrl_inw(reg); | ||
70 | break; | ||
71 | case 32: | ||
72 | data = ctrl_inl(reg); | ||
73 | break; | ||
74 | } | ||
75 | |||
76 | return (data >> pos) & mask; | 121 | return (data >> pos) & mask; |
77 | } | 122 | } |
78 | 123 | ||
@@ -140,6 +185,26 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) | |||
140 | return -1; | 185 | return -1; |
141 | } | 186 | } |
142 | 187 | ||
188 | static void setup_data_regs(struct pinmux_info *gpioc) | ||
189 | { | ||
190 | struct pinmux_data_reg *drp; | ||
191 | int k; | ||
192 | |||
193 | for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) | ||
194 | setup_data_reg(gpioc, k); | ||
195 | |||
196 | k = 0; | ||
197 | while (1) { | ||
198 | drp = gpioc->data_regs + k; | ||
199 | |||
200 | if (!drp->reg_width) | ||
201 | break; | ||
202 | |||
203 | drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width); | ||
204 | k++; | ||
205 | } | ||
206 | } | ||
207 | |||
143 | static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, | 208 | static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, |
144 | struct pinmux_data_reg **drp, int *bitp) | 209 | struct pinmux_data_reg **drp, int *bitp) |
145 | { | 210 | { |
@@ -465,7 +530,7 @@ static void __gpio_set_value(struct pinmux_info *gpioc, | |||
465 | if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) | 530 | if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) |
466 | BUG(); | 531 | BUG(); |
467 | else | 532 | else |
468 | gpio_write_reg(dr->reg, dr->reg_width, 1, bit, !!value); | 533 | gpio_write_bit(dr, bit, value); |
469 | } | 534 | } |
470 | 535 | ||
471 | int gpio_direction_output(unsigned gpio, int value) | 536 | int gpio_direction_output(unsigned gpio, int value) |
@@ -474,8 +539,8 @@ int gpio_direction_output(unsigned gpio, int value) | |||
474 | unsigned long flags; | 539 | unsigned long flags; |
475 | int ret; | 540 | int ret; |
476 | 541 | ||
477 | spin_lock_irqsave(&gpio_lock, flags); | ||
478 | __gpio_set_value(gpioc, gpio, value); | 542 | __gpio_set_value(gpioc, gpio, value); |
543 | spin_lock_irqsave(&gpio_lock, flags); | ||
479 | ret = pinmux_direction(gpioc, gpio, PINMUX_TYPE_OUTPUT); | 544 | ret = pinmux_direction(gpioc, gpio, PINMUX_TYPE_OUTPUT); |
480 | spin_unlock_irqrestore(&gpio_lock, flags); | 545 | spin_unlock_irqrestore(&gpio_lock, flags); |
481 | 546 | ||
@@ -504,25 +569,16 @@ EXPORT_SYMBOL(gpio_get_value); | |||
504 | 569 | ||
505 | void gpio_set_value(unsigned gpio, int value) | 570 | void gpio_set_value(unsigned gpio, int value) |
506 | { | 571 | { |
507 | struct pinmux_info *gpioc = gpio_controller(gpio); | 572 | __gpio_set_value(gpio_controller(gpio), gpio, value); |
508 | unsigned long flags; | ||
509 | |||
510 | spin_lock_irqsave(&gpio_lock, flags); | ||
511 | __gpio_set_value(gpioc, gpio, value); | ||
512 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
513 | } | 573 | } |
514 | EXPORT_SYMBOL(gpio_set_value); | 574 | EXPORT_SYMBOL(gpio_set_value); |
515 | 575 | ||
516 | int register_pinmux(struct pinmux_info *pip) | 576 | int register_pinmux(struct pinmux_info *pip) |
517 | { | 577 | { |
518 | int k; | ||
519 | |||
520 | registered_gpio = pip; | 578 | registered_gpio = pip; |
579 | setup_data_regs(pip); | ||
521 | pr_info("pinmux: %s handling gpio %d -> %d\n", | 580 | pr_info("pinmux: %s handling gpio %d -> %d\n", |
522 | pip->name, pip->first_gpio, pip->last_gpio); | 581 | pip->name, pip->first_gpio, pip->last_gpio); |
523 | 582 | ||
524 | for (k = pip->first_gpio; k <= pip->last_gpio; k++) | ||
525 | setup_data_reg(pip, k); | ||
526 | |||
527 | return 0; | 583 | return 0; |
528 | } | 584 | } |