blob: 375440c5afb9df1f24fa5a2fe3fac31e00112629 [file] [log] [blame]
/* arch/arm/mach-msm/board-sapphire-gpio.c
* Copyright (C) 2007-2009 HTC Corporation.
* Author: Thomas Tsai <thomas_tsai@htc.com>
*
* 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/kernel.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/sysdev.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <asm/mach-types.h>
#include "gpio_chip.h"
#include "board-sapphire.h"
#ifdef DEBUG_SAPPHIRE_GPIO
#define DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n", __func__, ## arg)
#else
#define DBG(fmt, arg...) do {} while (0)
#endif
#define SAPPHIRE_CPLD_INT_STATUS (SAPPHIRE_CPLD_BASE + 0x0E)
#define SAPPHIRE_CPLD_INT_LEVEL (SAPPHIRE_CPLD_BASE + 0x08)
#define SAPPHIRE_CPLD_INT_MASK (SAPPHIRE_CPLD_BASE + 0x0C)
/*CPLD misc reg offset*/
static const int _g_CPLD_MISCn_Offset[] = { 0x0A, /*misc1 reg*/
0x00, /*misc2 reg*/
0x02, /*misc3 reg*/
0x04, /*misc4 reg*/
0x06}; /*misc5 reg*/
/*CPLD INT Bank*/
/*BANK0: int1 status, int2 level, int3 mask*/
static const int _g_INT_BANK_Offset[][3] = {{0x0E, 0x08, 0x0C} };
static uint8_t sapphire_cpld_initdata[4] = {
[0] = 0x80, /* for serial debug UART3, low current misc2*/
[1] = 0x34, /* jog & tp enable, I2C pull misc3*/
[3] = 0x04, /* mmdi 32k en misc5*/
};
/*save current working int mask, so the value can be restored after resume.
Sapphire has only bank0.*/
static uint8_t sapphire_int_mask[] = {
[0] = 0xfb, /* enable all interrupts, bit 2 is not used */
};
/*Sleep have to prepare the wake up source in advance.
default to disable all wakeup sources when suspend.*/
static uint8_t sapphire_sleep_int_mask[] = {
[0] = 0x00, /* bit2 is not used */
};
static int sapphire_suspended;
static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n)
{
if (n < SAPPHIRE_GPIO_INT_B0_BASE) /*MISCn*/
return !!(readb(CPLD_GPIO_REG(n)) & CPLD_GPIO_BIT_POS_MASK(n));
else if (n <= SAPPHIRE_GPIO_END) /*gpio n is INT pin*/
return !!(readb(CPLD_INT_LEVEL_REG_G(n)) &
CPLD_GPIO_BIT_POS_MASK(n));
return 0;
}
/*CPLD Write only register :MISC2, MISC3, MISC4, MISC5 => reg=0,2,4,6
Reading from write-only registers is undefined, so the writing value
should be kept in shadow for later usage.*/
int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on)
{
unsigned long flags;
uint8_t reg_val;
if (n > SAPPHIRE_GPIO_END)
return -1;
local_irq_save(flags);
reg_val = readb(CPLD_GPIO_REG(n));
if (on)
reg_val |= CPLD_GPIO_BIT_POS_MASK(n);
else
reg_val &= ~CPLD_GPIO_BIT_POS_MASK(n);
writeb(reg_val, CPLD_GPIO_REG(n));
DBG("gpio=%d, l=0x%x\r\n", n, readb(SAPPHIRE_CPLD_INT_LEVEL));
local_irq_restore(flags);
return 0;
}
static int sapphire_gpio_configure(struct gpio_chip *chip, unsigned int gpio,
unsigned long flags)
{
if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH))
sapphire_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH);
DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL));
return 0;
}
static int sapphire_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio,
unsigned int *irqp, unsigned long *irqnumflagsp)
{
DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL));
DBG("SAPPHIRE_GPIO_INT_B0_BASE=%d, SAPPHIRE_GPIO_LAST_INT=%d\r\n",
SAPPHIRE_GPIO_INT_B0_BASE, SAPPHIRE_GPIO_LAST_INT);
if ((gpio < SAPPHIRE_GPIO_INT_B0_BASE) ||
(gpio > SAPPHIRE_GPIO_LAST_INT))
return -ENOENT;
*irqp = SAPPHIRE_GPIO_TO_INT(gpio);
DBG("*irqp=%d\r\n", *irqp);
if (irqnumflagsp)
*irqnumflagsp = 0;
return 0;
}
/*write 1 to clear INT status bit.*/
static void sapphire_gpio_irq_ack(unsigned int irq)
{
/*write 1 to clear*/
writeb(SAPPHIRE_INT_BIT_MASK(irq), CPLD_INT_STATUS_REG(irq));
}
/*unmask/enable the INT
static void sapphire_gpio_irq_unmask(unsigned int irq)*/
static void sapphire_gpio_irq_enable(unsigned int irq)
{
unsigned long flags;
uint8_t reg_val;
local_irq_save(flags); /*disabling all interrupts*/
reg_val = readb(CPLD_INT_MASK_REG(irq)) | SAPPHIRE_INT_BIT_MASK(irq);
DBG("(irq=%d,0x%x, 0x%x)\r\n", irq, CPLD_INT_MASK_REG(irq),
SAPPHIRE_INT_BIT_MASK(irq));
DBG("sapphire_suspended=%d\r\n", sapphire_suspended);
/*printk(KERN_INFO "sapphire_gpio_irq_mask irq %d => %d:%02x\n",
irq, bank, reg_val);*/
if (!sapphire_suspended)
writeb(reg_val, CPLD_INT_MASK_REG(irq));
reg_val = readb(CPLD_INT_MASK_REG(irq));
DBG("reg_val= 0x%x\r\n", reg_val);
DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL));
local_irq_restore(flags); /*restore the interrupts*/
}
/*mask/disable INT
static void sapphire_gpio_irq_mask(unsigned int irq)*/
static void sapphire_gpio_irq_disable(unsigned int irq)
{
unsigned long flags;
uint8_t reg_val;
local_irq_save(flags);
reg_val = readb(CPLD_INT_MASK_REG(irq)) & ~SAPPHIRE_INT_BIT_MASK(irq);
/*CPLD INT MASK is r/w now.*/
/*printk(KERN_INFO "sapphire_gpio_irq_unmask irq %d => %d:%02x\n",
irq, bank, reg_val);*/
DBG("(%d,0x%x, 0x%x, 0x%x)\r\n", irq, reg_val, CPLD_INT_MASK_REG(irq),
SAPPHIRE_INT_BIT_MASK(irq));
DBG("sapphire_suspended=%d\r\n", sapphire_suspended);
if (!sapphire_suspended)
writeb(reg_val, CPLD_INT_MASK_REG(irq));
reg_val = readb(CPLD_INT_MASK_REG(irq));
DBG("reg_val= 0x%x\r\n", reg_val);
DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL));
local_irq_restore(flags);
}
/*preparing enable/disable wake source before sleep*/
int sapphire_gpio_irq_set_wake(unsigned int irq, unsigned int on)
{
unsigned long flags;
uint8_t mask = SAPPHIRE_INT_BIT_MASK(irq);
local_irq_save(flags);
if (on) /*wake on -> mask the bit*/
sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] |= mask;
else /*no wake -> unmask the bit*/
sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] &= ~mask;
local_irq_restore(flags);
return 0;
}
/*Sapphire has only one INT Bank.*/
static void sapphire_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
int j;
unsigned v;
int int_base = SAPPHIRE_INT_START;
v = readb(SAPPHIRE_CPLD_INT_STATUS); /*INT1 status reg, BANK0*/
for (j = 0; j < 8 ; j++) { /*8 bit per bank*/
if (v & (1U << j)) { /*got the INT Bit*/
DBG("generic_handle_irq j=0x%x\r\n", j);
generic_handle_irq(int_base + j);
}
}
desc->chip->ack(irq); /*clear CPLD INT in SOC side.*/
DBG("irq=%d, l=0x%x\r\n", irq, readb(SAPPHIRE_CPLD_INT_LEVEL));
}
/*Save current working sources before sleep, so we can restore it after
* resume.*/
static int sapphire_sysdev_suspend(struct sys_device *dev, pm_message_t state)
{
sapphire_suspended = 1;
/*save current masking*/
sapphire_int_mask[0] = readb(SAPPHIRE_CPLD_BASE +
SAPPHIRE_GPIO_INT_B0_MASK_REG);
/*set waking source before sleep.*/
writeb(sapphire_sleep_int_mask[0],
SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG);
return 0;
}
/*All the registers will be kept till a power loss...*/
int sapphire_sysdev_resume(struct sys_device *dev)
{
/*restore the working mask saved before sleep*/
writeb(sapphire_int_mask[0], SAPPHIRE_CPLD_BASE +
SAPPHIRE_GPIO_INT_B0_MASK_REG);
sapphire_suspended = 0;
return 0;
}
/**
* linux/irq.h :: struct irq_chip
* @enable: enable the interrupt (defaults to chip->unmask if NULL)
* @disable: disable the interrupt (defaults to chip->mask if NULL)
* @ack: start of a new interrupt
* @mask: mask an interrupt source
* @mask_ack: ack and mask an interrupt source
* @unmask: unmask an interrupt source
*/
static struct irq_chip sapphire_gpio_irq_chip = {
.name = "sapphiregpio",
.ack = sapphire_gpio_irq_ack,
.mask = sapphire_gpio_irq_disable, /*sapphire_gpio_irq_mask,*/
.unmask = sapphire_gpio_irq_enable, /*sapphire_gpio_irq_unmask,*/
.set_wake = sapphire_gpio_irq_set_wake,
/*.set_type = sapphire_gpio_irq_set_type,*/
};
/*Thomas:For CPLD*/
static struct gpio_chip sapphire_gpio_chip = {
.start = SAPPHIRE_GPIO_START,
.end = SAPPHIRE_GPIO_END,
.configure = sapphire_gpio_configure,
.get_irq_num = sapphire_gpio_get_irq_num,
.read = sapphire_gpio_read,
.write = sapphire_gpio_write,
/* .read_detect_status = sapphire_gpio_read_detect_status,
.clear_detect_status = sapphire_gpio_clear_detect_status */
};
struct sysdev_class sapphire_sysdev_class = {
.name = "sapphiregpio_irq",
.suspend = sapphire_sysdev_suspend,
.resume = sapphire_sysdev_resume,
};
static struct sys_device sapphire_irq_device = {
.cls = &sapphire_sysdev_class,
};
int sapphire_init_gpio(void)
{
int i;
if (!machine_is_sapphire())
return 0;
DBG("%d,%d\r\n", SAPPHIRE_INT_START, SAPPHIRE_INT_END);
DBG("NR_MSM_IRQS=%d, NR_GPIO_IRQS=%d\r\n", NR_MSM_IRQS, NR_GPIO_IRQS);
for (i = SAPPHIRE_INT_START; i <= SAPPHIRE_INT_END; i++) {
set_irq_chip(i, &sapphire_gpio_irq_chip);
set_irq_handler(i, handle_edge_irq);
set_irq_flags(i, IRQF_VALID);
}
register_gpio_chip(&sapphire_gpio_chip);
/*setup CPLD INT connecting to SOC's gpio 17 */
set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH);
set_irq_chained_handler(MSM_GPIO_TO_INT(17), sapphire_gpio_irq_handler);
set_irq_wake(MSM_GPIO_TO_INT(17), 1);
if (sysdev_class_register(&sapphire_sysdev_class) == 0)
sysdev_register(&sapphire_irq_device);
return 0;
}
int sapphire_init_cpld(unsigned int sys_rev)
{
int i;
for (i = 0; i < ARRAY_SIZE(sapphire_cpld_initdata); i++)
writeb(sapphire_cpld_initdata[i], SAPPHIRE_CPLD_BASE + i * 2);
return 0;
}
postcore_initcall(sapphire_init_gpio);