-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 212603 b: refs/heads/master c: 2783cc2 h: refs/heads/master i: 212601: 0983302 212599: ad9827e v: v3
- Loading branch information
Gregory Bean
authored and
Daniel Walker
committed
Oct 6, 2010
1 parent
27bee23
commit d3f66ca
Showing
4 changed files
with
640 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: ab78cde589e89afa039a13bc75d23d249f1c1200 | ||
refs/heads/master: 2783cc265cc57c4bbf788b75fa8c3f06259dffd1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,358 @@ | ||
/* linux/arch/arm/mach-msm/gpio.c | ||
* | ||
* Copyright (C) 2007 Google, Inc. | ||
* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. | ||
* | ||
* This software is licensed under the terms of the GNU General Public | ||
* License version 2, as published by the Free Software Foundation, and | ||
* may be copied, distributed, and modified under those terms. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
*/ | ||
|
||
#include <linux/bitops.h> | ||
#include <linux/gpio.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/io.h> | ||
#include <linux/irq.h> | ||
#include <linux/module.h> | ||
#include "gpio_hw.h" | ||
|
||
#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0) | ||
|
||
#define MSM_GPIO_BANK(bank, first, last) \ | ||
{ \ | ||
.regs = { \ | ||
.out = MSM_GPIO_OUT_##bank, \ | ||
.in = MSM_GPIO_IN_##bank, \ | ||
.int_status = MSM_GPIO_INT_STATUS_##bank, \ | ||
.int_clear = MSM_GPIO_INT_CLEAR_##bank, \ | ||
.int_en = MSM_GPIO_INT_EN_##bank, \ | ||
.int_edge = MSM_GPIO_INT_EDGE_##bank, \ | ||
.int_pos = MSM_GPIO_INT_POS_##bank, \ | ||
.oe = MSM_GPIO_OE_##bank, \ | ||
}, \ | ||
.chip = { \ | ||
.base = (first), \ | ||
.ngpio = (last) - (first) + 1, \ | ||
.get = msm_gpio_get, \ | ||
.set = msm_gpio_set, \ | ||
.direction_input = msm_gpio_direction_input, \ | ||
.direction_output = msm_gpio_direction_output, \ | ||
.to_irq = msm_gpio_to_irq, \ | ||
} \ | ||
} | ||
|
||
#define MSM_GPIO_BROKEN_INT_CLEAR 1 | ||
|
||
struct msm_gpio_regs { | ||
void __iomem *out; | ||
void __iomem *in; | ||
void __iomem *int_status; | ||
void __iomem *int_clear; | ||
void __iomem *int_en; | ||
void __iomem *int_edge; | ||
void __iomem *int_pos; | ||
void __iomem *oe; | ||
}; | ||
|
||
struct msm_gpio_chip { | ||
spinlock_t lock; | ||
struct gpio_chip chip; | ||
struct msm_gpio_regs regs; | ||
#if MSM_GPIO_BROKEN_INT_CLEAR | ||
unsigned int_status_copy; | ||
#endif | ||
unsigned int both_edge_detect; | ||
unsigned int int_enable[2]; /* 0: awake, 1: sleep */ | ||
}; | ||
|
||
static int msm_gpio_write(struct msm_gpio_chip *msm_chip, | ||
unsigned offset, unsigned on) | ||
{ | ||
unsigned mask = BIT(offset); | ||
unsigned val; | ||
|
||
val = readl(msm_chip->regs.out); | ||
if (on) | ||
writel(val | mask, msm_chip->regs.out); | ||
else | ||
writel(val & ~mask, msm_chip->regs.out); | ||
return 0; | ||
} | ||
|
||
static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip) | ||
{ | ||
int loop_limit = 100; | ||
unsigned pol, val, val2, intstat; | ||
do { | ||
val = readl(msm_chip->regs.in); | ||
pol = readl(msm_chip->regs.int_pos); | ||
pol = (pol & ~msm_chip->both_edge_detect) | | ||
(~val & msm_chip->both_edge_detect); | ||
writel(pol, msm_chip->regs.int_pos); | ||
intstat = readl(msm_chip->regs.int_status); | ||
val2 = readl(msm_chip->regs.in); | ||
if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0) | ||
return; | ||
} while (loop_limit-- > 0); | ||
printk(KERN_ERR "msm_gpio_update_both_edge_detect, " | ||
"failed to reach stable state %x != %x\n", val, val2); | ||
} | ||
|
||
static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip, | ||
unsigned offset) | ||
{ | ||
unsigned bit = BIT(offset); | ||
|
||
#if MSM_GPIO_BROKEN_INT_CLEAR | ||
/* Save interrupts that already triggered before we loose them. */ | ||
/* Any interrupt that triggers between the read of int_status */ | ||
/* and the write to int_clear will still be lost though. */ | ||
msm_chip->int_status_copy |= readl(msm_chip->regs.int_status); | ||
msm_chip->int_status_copy &= ~bit; | ||
#endif | ||
writel(bit, msm_chip->regs.int_clear); | ||
msm_gpio_update_both_edge_detect(msm_chip); | ||
return 0; | ||
} | ||
|
||
static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
{ | ||
struct msm_gpio_chip *msm_chip; | ||
unsigned long irq_flags; | ||
|
||
msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe); | ||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
return 0; | ||
} | ||
|
||
static int | ||
msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) | ||
{ | ||
struct msm_gpio_chip *msm_chip; | ||
unsigned long irq_flags; | ||
|
||
msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
msm_gpio_write(msm_chip, offset, value); | ||
writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe); | ||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
return 0; | ||
} | ||
|
||
static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
{ | ||
struct msm_gpio_chip *msm_chip; | ||
|
||
msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0; | ||
} | ||
|
||
static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
{ | ||
struct msm_gpio_chip *msm_chip; | ||
unsigned long irq_flags; | ||
|
||
msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
msm_gpio_write(msm_chip, offset, value); | ||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
} | ||
|
||
static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | ||
{ | ||
return MSM_GPIO_TO_INT(chip->base + offset); | ||
} | ||
|
||
struct msm_gpio_chip msm_gpio_chips[] = { | ||
#if defined(CONFIG_ARCH_MSM7X00A) | ||
MSM_GPIO_BANK(0, 0, 15), | ||
MSM_GPIO_BANK(1, 16, 42), | ||
MSM_GPIO_BANK(2, 43, 67), | ||
MSM_GPIO_BANK(3, 68, 94), | ||
MSM_GPIO_BANK(4, 95, 106), | ||
MSM_GPIO_BANK(5, 107, 121), | ||
#elif defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X27) | ||
MSM_GPIO_BANK(0, 0, 15), | ||
MSM_GPIO_BANK(1, 16, 42), | ||
MSM_GPIO_BANK(2, 43, 67), | ||
MSM_GPIO_BANK(3, 68, 94), | ||
MSM_GPIO_BANK(4, 95, 106), | ||
MSM_GPIO_BANK(5, 107, 132), | ||
#elif defined(CONFIG_ARCH_MSM7X30) | ||
MSM_GPIO_BANK(0, 0, 15), | ||
MSM_GPIO_BANK(1, 16, 43), | ||
MSM_GPIO_BANK(2, 44, 67), | ||
MSM_GPIO_BANK(3, 68, 94), | ||
MSM_GPIO_BANK(4, 95, 106), | ||
MSM_GPIO_BANK(5, 107, 133), | ||
MSM_GPIO_BANK(6, 134, 150), | ||
MSM_GPIO_BANK(7, 151, 181), | ||
#elif defined(CONFIG_ARCH_QSD8X50) | ||
MSM_GPIO_BANK(0, 0, 15), | ||
MSM_GPIO_BANK(1, 16, 42), | ||
MSM_GPIO_BANK(2, 43, 67), | ||
MSM_GPIO_BANK(3, 68, 94), | ||
MSM_GPIO_BANK(4, 95, 103), | ||
MSM_GPIO_BANK(5, 104, 121), | ||
MSM_GPIO_BANK(6, 122, 152), | ||
MSM_GPIO_BANK(7, 153, 164), | ||
#endif | ||
}; | ||
|
||
static void msm_gpio_irq_ack(unsigned int irq) | ||
{ | ||
unsigned long irq_flags; | ||
struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
msm_gpio_clear_detect_status(msm_chip, | ||
irq - gpio_to_irq(msm_chip->chip.base)); | ||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
} | ||
|
||
static void msm_gpio_irq_mask(unsigned int irq) | ||
{ | ||
unsigned long irq_flags; | ||
struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
|
||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
/* level triggered interrupts are also latched */ | ||
if (!(readl(msm_chip->regs.int_edge) & BIT(offset))) | ||
msm_gpio_clear_detect_status(msm_chip, offset); | ||
msm_chip->int_enable[0] &= ~BIT(offset); | ||
writel(msm_chip->int_enable[0], msm_chip->regs.int_en); | ||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
} | ||
|
||
static void msm_gpio_irq_unmask(unsigned int irq) | ||
{ | ||
unsigned long irq_flags; | ||
struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
|
||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
/* level triggered interrupts are also latched */ | ||
if (!(readl(msm_chip->regs.int_edge) & BIT(offset))) | ||
msm_gpio_clear_detect_status(msm_chip, offset); | ||
msm_chip->int_enable[0] |= BIT(offset); | ||
writel(msm_chip->int_enable[0], msm_chip->regs.int_en); | ||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
} | ||
|
||
static int msm_gpio_irq_set_wake(unsigned int irq, unsigned int on) | ||
{ | ||
unsigned long irq_flags; | ||
struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
|
||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
|
||
if (on) | ||
msm_chip->int_enable[1] |= BIT(offset); | ||
else | ||
msm_chip->int_enable[1] &= ~BIT(offset); | ||
|
||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
return 0; | ||
} | ||
|
||
static int msm_gpio_irq_set_type(unsigned int irq, unsigned int flow_type) | ||
{ | ||
unsigned long irq_flags; | ||
struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
unsigned val, mask = BIT(offset); | ||
|
||
spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
val = readl(msm_chip->regs.int_edge); | ||
if (flow_type & IRQ_TYPE_EDGE_BOTH) { | ||
writel(val | mask, msm_chip->regs.int_edge); | ||
irq_desc[irq].handle_irq = handle_edge_irq; | ||
} else { | ||
writel(val & ~mask, msm_chip->regs.int_edge); | ||
irq_desc[irq].handle_irq = handle_level_irq; | ||
} | ||
if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { | ||
msm_chip->both_edge_detect |= mask; | ||
msm_gpio_update_both_edge_detect(msm_chip); | ||
} else { | ||
msm_chip->both_edge_detect &= ~mask; | ||
val = readl(msm_chip->regs.int_pos); | ||
if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) | ||
writel(val | mask, msm_chip->regs.int_pos); | ||
else | ||
writel(val & ~mask, msm_chip->regs.int_pos); | ||
} | ||
spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
return 0; | ||
} | ||
|
||
static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) | ||
{ | ||
int i, j, mask; | ||
unsigned val; | ||
|
||
for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { | ||
struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i]; | ||
val = readl(msm_chip->regs.int_status); | ||
val &= msm_chip->int_enable[0]; | ||
while (val) { | ||
mask = val & -val; | ||
j = fls(mask) - 1; | ||
/* printk("%s %08x %08x bit %d gpio %d irq %d\n", | ||
__func__, v, m, j, msm_chip->chip.start + j, | ||
FIRST_GPIO_IRQ + msm_chip->chip.start + j); */ | ||
val &= ~mask; | ||
generic_handle_irq(FIRST_GPIO_IRQ + | ||
msm_chip->chip.base + j); | ||
} | ||
} | ||
desc->chip->ack(irq); | ||
} | ||
|
||
static struct irq_chip msm_gpio_irq_chip = { | ||
.name = "msmgpio", | ||
.ack = msm_gpio_irq_ack, | ||
.mask = msm_gpio_irq_mask, | ||
.unmask = msm_gpio_irq_unmask, | ||
.set_wake = msm_gpio_irq_set_wake, | ||
.set_type = msm_gpio_irq_set_type, | ||
}; | ||
|
||
static int __init msm_init_gpio(void) | ||
{ | ||
int i, j = 0; | ||
|
||
for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) { | ||
if (i - FIRST_GPIO_IRQ >= | ||
msm_gpio_chips[j].chip.base + | ||
msm_gpio_chips[j].chip.ngpio) | ||
j++; | ||
set_irq_chip_data(i, &msm_gpio_chips[j]); | ||
set_irq_chip(i, &msm_gpio_irq_chip); | ||
set_irq_handler(i, handle_edge_irq); | ||
set_irq_flags(i, IRQF_VALID); | ||
} | ||
|
||
for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { | ||
spin_lock_init(&msm_gpio_chips[i].lock); | ||
writel(0, msm_gpio_chips[i].regs.int_en); | ||
gpiochip_add(&msm_gpio_chips[i].chip); | ||
} | ||
|
||
set_irq_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler); | ||
set_irq_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler); | ||
set_irq_wake(INT_GPIO_GROUP1, 1); | ||
set_irq_wake(INT_GPIO_GROUP2, 2); | ||
return 0; | ||
} | ||
|
||
postcore_initcall(msm_init_gpio); |
Oops, something went wrong.