~ubuntu-branches/ubuntu/saucy/linux-ti-omap4/saucy-proposed

« back to all changes in this revision

Viewing changes to arch/arm/plat-samsung/s5p-irq-gpioint.c

  • Committer: Package Import Robot
  • Author(s): Paolo Pisati, Paolo Pisati, Stefan Bader, Upstream Kernel Changes
  • Date: 2012-08-15 17:17:43 UTC
  • Revision ID: package-import@ubuntu.com-20120815171743-h5wnuf51xe7pvdid
Tags: 3.5.0-207.13
[ Paolo Pisati ]

* Start new release

[ Stefan Bader ]

* (config) Enable getabis to use local package copies

[ Upstream Kernel Changes ]

* fixup: gargabe collect iva_seq[0|1] init
* [Config] enable all SND_OMAP_SOC_*s
* fixup: cm2xxx_3xxx.o is needed for omap2_cm_read|write_reg
* fixup: add some snd_soc_dai* helper functions
* fixup: s/snd_soc_dpcm_params/snd_soc_dpcm/g
* fixup: typo, no_host_mode and useless SDP4430 init
* fixup: enable again aess hwmod

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
 
3
 * Author: Kyungmin Park <kyungmin.park@samsung.com>
 
4
 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
 
5
 * Author: Marek Szyprowski <m.szyprowski@samsung.com>
 
6
 *
 
7
 *  This program is free software; you can redistribute  it and/or modify it
 
8
 *  under  the terms of  the GNU General  Public License as published by the
 
9
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 
10
 *  option) any later version.
 
11
 *
 
12
 */
 
13
 
 
14
#include <linux/kernel.h>
 
15
#include <linux/interrupt.h>
 
16
#include <linux/irq.h>
 
17
#include <linux/io.h>
 
18
#include <linux/gpio.h>
 
19
#include <linux/slab.h>
 
20
 
 
21
#include <mach/map.h>
 
22
#include <plat/gpio-core.h>
 
23
#include <plat/gpio-cfg.h>
 
24
 
 
25
#include <asm/mach/irq.h>
 
26
 
 
27
#define GPIO_BASE(chip)         (((unsigned long)(chip)->base) & 0xFFFFF000u)
 
28
 
 
29
#define CON_OFFSET              0x700
 
30
#define MASK_OFFSET             0x900
 
31
#define PEND_OFFSET             0xA00
 
32
#define REG_OFFSET(x)           ((x) << 2)
 
33
 
 
34
struct s5p_gpioint_bank {
 
35
        struct list_head        list;
 
36
        int                     start;
 
37
        int                     nr_groups;
 
38
        int                     irq;
 
39
        struct samsung_gpio_chip        **chips;
 
40
        void                    (*handler)(unsigned int, struct irq_desc *);
 
41
};
 
42
 
 
43
static LIST_HEAD(banks);
 
44
 
 
45
static int s5p_gpioint_set_type(struct irq_data *d, unsigned int type)
 
46
{
 
47
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 
48
        struct irq_chip_type *ct = gc->chip_types;
 
49
        unsigned int shift = (d->irq - gc->irq_base) << 2;
 
50
 
 
51
        switch (type) {
 
52
        case IRQ_TYPE_EDGE_RISING:
 
53
                type = S5P_IRQ_TYPE_EDGE_RISING;
 
54
                break;
 
55
        case IRQ_TYPE_EDGE_FALLING:
 
56
                type = S5P_IRQ_TYPE_EDGE_FALLING;
 
57
                break;
 
58
        case IRQ_TYPE_EDGE_BOTH:
 
59
                type = S5P_IRQ_TYPE_EDGE_BOTH;
 
60
                break;
 
61
        case IRQ_TYPE_LEVEL_HIGH:
 
62
                type = S5P_IRQ_TYPE_LEVEL_HIGH;
 
63
                break;
 
64
        case IRQ_TYPE_LEVEL_LOW:
 
65
                type = S5P_IRQ_TYPE_LEVEL_LOW;
 
66
                break;
 
67
        case IRQ_TYPE_NONE:
 
68
        default:
 
69
                printk(KERN_WARNING "No irq type\n");
 
70
                return -EINVAL;
 
71
        }
 
72
 
 
73
        gc->type_cache &= ~(0x7 << shift);
 
74
        gc->type_cache |= type << shift;
 
75
        writel(gc->type_cache, gc->reg_base + ct->regs.type);
 
76
        return 0;
 
77
}
 
78
 
 
79
static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc)
 
80
{
 
81
        struct s5p_gpioint_bank *bank = irq_get_handler_data(irq);
 
82
        int group, pend_offset, mask_offset;
 
83
        unsigned int pend, mask;
 
84
 
 
85
        struct irq_chip *chip = irq_get_chip(irq);
 
86
        chained_irq_enter(chip, desc);
 
87
 
 
88
        for (group = 0; group < bank->nr_groups; group++) {
 
89
                struct samsung_gpio_chip *chip = bank->chips[group];
 
90
                if (!chip)
 
91
                        continue;
 
92
 
 
93
                pend_offset = REG_OFFSET(group);
 
94
                pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset);
 
95
                if (!pend)
 
96
                        continue;
 
97
 
 
98
                mask_offset = REG_OFFSET(group);
 
99
                mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
 
100
                pend &= ~mask;
 
101
 
 
102
                while (pend) {
 
103
                        int offset = fls(pend) - 1;
 
104
                        int real_irq = chip->irq_base + offset;
 
105
                        generic_handle_irq(real_irq);
 
106
                        pend &= ~BIT(offset);
 
107
                }
 
108
        }
 
109
        chained_irq_exit(chip, desc);
 
110
}
 
111
 
 
112
static __init int s5p_gpioint_add(struct samsung_gpio_chip *chip)
 
113
{
 
114
        static int used_gpioint_groups = 0;
 
115
        int group = chip->group;
 
116
        struct s5p_gpioint_bank *b, *bank = NULL;
 
117
        struct irq_chip_generic *gc;
 
118
        struct irq_chip_type *ct;
 
119
 
 
120
        if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT)
 
121
                return -ENOMEM;
 
122
 
 
123
        list_for_each_entry(b, &banks, list) {
 
124
                if (group >= b->start && group < b->start + b->nr_groups) {
 
125
                        bank = b;
 
126
                        break;
 
127
                }
 
128
        }
 
129
        if (!bank)
 
130
                return -EINVAL;
 
131
 
 
132
        if (!bank->handler) {
 
133
                bank->chips = kzalloc(sizeof(struct samsung_gpio_chip *) *
 
134
                                      bank->nr_groups, GFP_KERNEL);
 
135
                if (!bank->chips)
 
136
                        return -ENOMEM;
 
137
 
 
138
                irq_set_chained_handler(bank->irq, s5p_gpioint_handler);
 
139
                irq_set_handler_data(bank->irq, bank);
 
140
                bank->handler = s5p_gpioint_handler;
 
141
                printk(KERN_INFO "Registered chained gpio int handler for interrupt %d.\n",
 
142
                       bank->irq);
 
143
        }
 
144
 
 
145
        /*
 
146
         * chained GPIO irq has been successfully registered, allocate new gpio
 
147
         * int group and assign irq nubmers
 
148
         */
 
149
        chip->irq_base = S5P_GPIOINT_BASE +
 
150
                         used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE;
 
151
        used_gpioint_groups++;
 
152
 
 
153
        bank->chips[group - bank->start] = chip;
 
154
 
 
155
        gc = irq_alloc_generic_chip("s5p_gpioint", 1, chip->irq_base,
 
156
                                    (void __iomem *)GPIO_BASE(chip),
 
157
                                    handle_level_irq);
 
158
        if (!gc)
 
159
                return -ENOMEM;
 
160
        ct = gc->chip_types;
 
161
        ct->chip.irq_ack = irq_gc_ack_set_bit;
 
162
        ct->chip.irq_mask = irq_gc_mask_set_bit;
 
163
        ct->chip.irq_unmask = irq_gc_mask_clr_bit;
 
164
        ct->chip.irq_set_type = s5p_gpioint_set_type,
 
165
        ct->regs.ack = PEND_OFFSET + REG_OFFSET(group - bank->start);
 
166
        ct->regs.mask = MASK_OFFSET + REG_OFFSET(group - bank->start);
 
167
        ct->regs.type = CON_OFFSET + REG_OFFSET(group - bank->start);
 
168
        irq_setup_generic_chip(gc, IRQ_MSK(chip->chip.ngpio),
 
169
                               IRQ_GC_INIT_MASK_CACHE,
 
170
                               IRQ_NOREQUEST | IRQ_NOPROBE, 0);
 
171
        return 0;
 
172
}
 
173
 
 
174
int __init s5p_register_gpio_interrupt(int pin)
 
175
{
 
176
        struct samsung_gpio_chip *my_chip = samsung_gpiolib_getchip(pin);
 
177
        int offset, group;
 
178
        int ret;
 
179
 
 
180
        if (!my_chip)
 
181
                return -EINVAL;
 
182
 
 
183
        offset = pin - my_chip->chip.base;
 
184
        group = my_chip->group;
 
185
 
 
186
        /* check if the group has been already registered */
 
187
        if (my_chip->irq_base)
 
188
                return my_chip->irq_base + offset;
 
189
 
 
190
        /* register gpio group */
 
191
        ret = s5p_gpioint_add(my_chip);
 
192
        if (ret == 0) {
 
193
                my_chip->chip.to_irq = samsung_gpiolib_to_irq;
 
194
                printk(KERN_INFO "Registered interrupt support for gpio group %d.\n",
 
195
                       group);
 
196
                return my_chip->irq_base + offset;
 
197
        }
 
198
        return ret;
 
199
}
 
200
 
 
201
int __init s5p_register_gpioint_bank(int chain_irq, int start, int nr_groups)
 
202
{
 
203
        struct s5p_gpioint_bank *bank;
 
204
 
 
205
        bank = kzalloc(sizeof(*bank), GFP_KERNEL);
 
206
        if (!bank)
 
207
                return -ENOMEM;
 
208
 
 
209
        bank->start = start;
 
210
        bank->nr_groups = nr_groups;
 
211
        bank->irq = chain_irq;
 
212
 
 
213
        list_add_tail(&bank->list, &banks);
 
214
        return 0;
 
215
}