summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2011-07-07 20:31:46 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-08-18 16:40:23 -0700
commitceee35acb43eac80425873f44f18a963747015c4 (patch)
tree680b03ddf2a2bf0e3b733cae4504b2a2a9e079e1 /drivers/mfd
parent401734cc0ab7dbb06c8744a1120f03b18d6d2b84 (diff)
mfd: ricoh583: Add core driver
Adding core driver for the Powermanagement system device RICOH 583. bug 822562 Change-Id: I8a21b42800bd9043f901689ed354618165c42534 Reviewed-on: http://git-master/r/40035 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig12
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/ricoh583.c1006
3 files changed, 1019 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 8c8e4559b9d8..e6837bafdbd1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -604,6 +604,18 @@ config MFD_TPS80031
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_RICOH583
+ bool "Ricoh RC5T583 Power Management system device"
+ depends on I2C && GPIOLIB && GENERIC_HARDIRQS
+ select MFD_CORE
+ default n
+ help
+ If you say yes here you get support for the RICOH583 Power
+ Management system device.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index dbf66a66cb0f..5d3f39a6f32c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -82,3 +82,4 @@ obj-$(CONFIG_MFD_MAX8907C) += max8907c.o
obj-$(CONFIG_MFD_MAX8907C) += max8907c-irq.o
obj-$(CONFIG_MFD_MAX77663) += max77663-core.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_RICOH583) += ricoh583.o
diff --git a/drivers/mfd/ricoh583.c b/drivers/mfd/ricoh583.c
new file mode 100644
index 000000000000..edbbf80630a9
--- /dev/null
+++ b/drivers/mfd/ricoh583.c
@@ -0,0 +1,1006 @@
+/*
+ * driver/mfd/ricoh583.c
+ *
+ * Core driver implementation to access RICOH583 power management chip.
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * Copyright (C) 2011 RICOH COMPANY,LTD
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+/*#define DEBUG 1*/
+/*#define VERBOSE_DEBUG 1*/
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ricoh583.h>
+
+/* Interrupt enable register */
+#define RICOH583_INT_EN_SYS1 0x19
+#define RICOH583_INT_EN_SYS2 0x1D
+#define RICOH583_INT_EN_DCDC 0x41
+#define RICOH583_INT_EN_RTC 0xED
+#define RICOH583_INT_EN_ADC1 0x90
+#define RICOH583_INT_EN_ADC2 0x91
+#define RICOH583_INT_EN_ADC3 0x92
+#define RICOH583_INT_EN_GPIO 0xA8
+
+/* interrupt status registers (monitor regs in Ricoh)*/
+#define RICOH583_INTC_INTPOL 0xAD
+#define RICOH583_INTC_INTEN 0xAE
+#define RICOH583_INTC_INTMON 0xAF
+
+#define RICOH583_INT_MON_GRP 0xAF
+#define RICOH583_INT_MON_SYS1 0x1B
+#define RICOH583_INT_MON_SYS2 0x1F
+#define RICOH583_INT_MON_DCDC 0x43
+#define RICOH583_INT_MON_RTC 0xEE
+
+/* interrupt clearing registers */
+#define RICOH583_INT_IR_SYS1 0x1A
+#define RICOH583_INT_IR_SYS2 0x1E
+#define RICOH583_INT_IR_DCDC 0x42
+#define RICOH583_INT_IR_RTC 0xEE
+#define RICOH583_INT_IR_ADCL 0x94
+#define RICOH583_INT_IR_ADCH 0x95
+#define RICOH583_INT_IR_ADCEND 0x96
+#define RICOH583_INT_IR_GPIOR 0xA9
+#define RICOH583_INT_IR_GPIOF 0xAA
+
+/* GPIO register base address */
+#define RICOH583_GPIO_IOSEL 0xA0
+#define RICOH583_GPIO_PDEN 0xA1
+#define RICOH583_GPIO_IOOUT 0xA2
+#define RICOH583_GPIO_PGSEL 0xA3
+#define RICOH583_GPIO_GPINV 0xA4
+#define RICOH583_GPIO_GPDEB 0xA5
+#define RICOH583_GPIO_GPEDGE1 0xA6
+#define RICOH583_GPIO_GPEDGE2 0xA7
+#define RICOH583_GPIO_EN_GPIR 0xA8
+#define RICOH583_GPIO_MON_IOIN 0xAB
+#define RICOH583_GPIO_GPOFUNC 0xAC
+#define RICOH583_INTC_INTEN 0xAE
+
+enum int_type {
+ SYS_INT = 0x1,
+ DCDC_INT = 0x2,
+ RTC_INT = 0x4,
+ ADC_INT = 0x8,
+ GPIO_INT = 0x10,
+};
+
+struct ricoh583_irq_data {
+ u8 int_type;
+ u8 master_bit;
+ u8 int_en_bit;
+ u8 mask_reg_index;
+ int grp_index;
+};
+
+#define RICOH583_IRQ(_int_type, _master_bit, _grp_index, _int_bit, _mask_ind) \
+ { \
+ .int_type = _int_type, \
+ .master_bit = _master_bit, \
+ .grp_index = _grp_index, \
+ .int_en_bit = _int_bit, \
+ .mask_reg_index = _mask_ind, \
+ }
+
+static const struct ricoh583_irq_data ricoh583_irqs[] = {
+ [RICOH583_IRQ_ONKEY] = RICOH583_IRQ(SYS_INT, 0, 0, 0, 0),
+ [RICOH583_IRQ_ACOK] = RICOH583_IRQ(SYS_INT, 0, 1, 1, 0),
+ [RICOH583_IRQ_LIDOPEN] = RICOH583_IRQ(SYS_INT, 0, 2, 2, 0),
+ [RICOH583_IRQ_PREOT] = RICOH583_IRQ(SYS_INT, 0, 3, 3, 0),
+ [RICOH583_IRQ_CLKSTP] = RICOH583_IRQ(SYS_INT, 0, 4, 4, 0),
+ [RICOH583_IRQ_ONKEY_OFF] = RICOH583_IRQ(SYS_INT, 0, 5, 5, 0),
+ [RICOH583_IRQ_WD] = RICOH583_IRQ(SYS_INT, 0, 7, 7, 0),
+ [RICOH583_IRQ_EN_PWRREQ1] = RICOH583_IRQ(SYS_INT, 0, 8, 0, 1),
+ [RICOH583_IRQ_EN_PWRREQ2] = RICOH583_IRQ(SYS_INT, 0, 9, 1, 1),
+ [RICOH583_IRQ_PRE_VINDET] = RICOH583_IRQ(SYS_INT, 0, 10, 2, 1),
+
+ [RICOH583_IRQ_DC0LIM] = RICOH583_IRQ(DCDC_INT, 1, 0, 0, 2),
+ [RICOH583_IRQ_DC1LIM] = RICOH583_IRQ(DCDC_INT, 1, 1, 1, 2),
+ [RICOH583_IRQ_DC2LIM] = RICOH583_IRQ(DCDC_INT, 1, 2, 2, 2),
+ [RICOH583_IRQ_DC3LIM] = RICOH583_IRQ(DCDC_INT, 1, 3, 3, 2),
+
+ [RICOH583_IRQ_CTC] = RICOH583_IRQ(RTC_INT, 2, 0, 0, 3),
+ [RICOH583_IRQ_YALE] = RICOH583_IRQ(RTC_INT, 2, 5, 5, 3),
+ [RICOH583_IRQ_DALE] = RICOH583_IRQ(RTC_INT, 2, 6, 6, 3),
+ [RICOH583_IRQ_WALE] = RICOH583_IRQ(RTC_INT, 2, 7, 7, 3),
+
+ [RICOH583_IRQ_AIN1L] = RICOH583_IRQ(ADC_INT, 3, 0, 0, 4),
+ [RICOH583_IRQ_AIN2L] = RICOH583_IRQ(ADC_INT, 3, 1, 1, 4),
+ [RICOH583_IRQ_AIN3L] = RICOH583_IRQ(ADC_INT, 3, 2, 2, 4),
+ [RICOH583_IRQ_VBATL] = RICOH583_IRQ(ADC_INT, 3, 3, 3, 4),
+ [RICOH583_IRQ_VIN3L] = RICOH583_IRQ(ADC_INT, 3, 4, 4, 4),
+ [RICOH583_IRQ_VIN8L] = RICOH583_IRQ(ADC_INT, 3, 5, 5, 4),
+ [RICOH583_IRQ_AIN1H] = RICOH583_IRQ(ADC_INT, 3, 6, 0, 5),
+ [RICOH583_IRQ_AIN2H] = RICOH583_IRQ(ADC_INT, 3, 7, 1, 5),
+ [RICOH583_IRQ_AIN3H] = RICOH583_IRQ(ADC_INT, 3, 8, 2, 5),
+ [RICOH583_IRQ_VBATH] = RICOH583_IRQ(ADC_INT, 3, 9, 3, 5),
+ [RICOH583_IRQ_VIN3H] = RICOH583_IRQ(ADC_INT, 3, 10, 4, 5),
+ [RICOH583_IRQ_VIN8H] = RICOH583_IRQ(ADC_INT, 3, 11, 5, 5),
+ [RICOH583_IRQ_ADCEND] = RICOH583_IRQ(ADC_INT, 3, 12, 0, 6),
+
+ [RICOH583_IRQ_GPIO0] = RICOH583_IRQ(GPIO_INT, 4, 0, 0, 7),
+ [RICOH583_IRQ_GPIO1] = RICOH583_IRQ(GPIO_INT, 4, 1, 1, 7),
+ [RICOH583_IRQ_GPIO2] = RICOH583_IRQ(GPIO_INT, 4, 2, 2, 7),
+ [RICOH583_IRQ_GPIO3] = RICOH583_IRQ(GPIO_INT, 4, 3, 3, 7),
+ [RICOH583_IRQ_GPIO4] = RICOH583_IRQ(GPIO_INT, 4, 4, 4, 7),
+ [RICOH583_IRQ_GPIO5] = RICOH583_IRQ(GPIO_INT, 4, 5, 5, 7),
+ [RICOH583_IRQ_GPIO6] = RICOH583_IRQ(GPIO_INT, 4, 6, 6, 7),
+ [RICOH583_IRQ_GPIO7] = RICOH583_IRQ(GPIO_INT, 4, 7, 7, 7),
+ [RICOH583_NR_IRQS] = RICOH583_IRQ(GPIO_INT, 4, 8, 8, 7),
+};
+
+#define MAX_INTERRUPT_MASKS 8
+#define MAX_MAIN_INTERRUPT 5
+
+struct ricoh583 {
+ struct device *dev;
+ struct i2c_client *client;
+ struct mutex io_lock;
+ int gpio_base;
+ struct gpio_chip gpio;
+ int irq_base;
+ struct irq_chip irq_chip;
+ struct mutex irq_lock;
+ unsigned long group_irq_en[MAX_MAIN_INTERRUPT];
+
+ /* For main interrupt bits in INTC */
+ u8 intc_inten_cache;
+ u8 intc_inten_reg;
+
+ /* For group interrupt bits and address */
+ u8 irq_en_cache[MAX_INTERRUPT_MASKS];
+ u8 irq_en_reg[MAX_INTERRUPT_MASKS];
+ u8 irq_en_add[MAX_INTERRUPT_MASKS];
+
+ /* Interrupt monitor and clear register */
+ u8 irq_mon_add[MAX_INTERRUPT_MASKS + 1];
+ u8 irq_clr_add[MAX_INTERRUPT_MASKS + 1];
+ u8 main_int_type[MAX_INTERRUPT_MASKS + 1];
+
+ /* For gpio edge */
+ u8 gpedge_cache[2];
+ u8 gpedge_reg[2];
+ u8 gpedge_add[2];
+};
+
+static inline int __ricoh583_read(struct i2c_client *client,
+ u8 reg, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+ return ret;
+ }
+
+ *val = (uint8_t)ret;
+ dev_dbg(&client->dev, "ricoh583: reg read reg=%x, val=%x\n",
+ reg, *val);
+ return 0;
+}
+
+static inline int __ricoh583_bulk_reads(struct i2c_client *client, u8 reg,
+ int len, uint8_t *val)
+{
+ int ret;
+ int i;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
+ return ret;
+ }
+ for (i = 0; i < len; ++i) {
+ dev_dbg(&client->dev, "ricoh583: reg read reg=%x, val=%x\n",
+ reg + i, *(val + i));
+ }
+ return 0;
+}
+
+static inline int __ricoh583_write(struct i2c_client *client,
+ u8 reg, uint8_t val)
+{
+ int ret;
+
+ dev_dbg(&client->dev, "ricoh583: reg write reg=%x, val=%x\n",
+ reg, val);
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
+ val, reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int __ricoh583_bulk_writes(struct i2c_client *client, u8 reg,
+ int len, uint8_t *val)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ dev_dbg(&client->dev, "ricoh583: reg write reg=%x, val=%x\n",
+ reg + i, *(val + i));
+ }
+
+ ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+int ricoh583_write(struct device *dev, u8 reg, uint8_t val)
+{
+ struct ricoh583 *ricoh583 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&ricoh583->io_lock);
+ ret = __ricoh583_write(to_i2c_client(dev), reg, val);
+ mutex_unlock(&ricoh583->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ricoh583_write);
+
+int ricoh583_bulk_writes(struct device *dev, u8 reg, int len, uint8_t *val)
+{
+ struct ricoh583 *ricoh583 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&ricoh583->io_lock);
+ ret = __ricoh583_bulk_writes(to_i2c_client(dev), reg, len, val);
+ mutex_unlock(&ricoh583->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ricoh583_bulk_writes);
+
+int ricoh583_read(struct device *dev, u8 reg, uint8_t *val)
+{
+ return __ricoh583_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(ricoh583_read);
+
+int ricoh583_bulk_reads(struct device *dev, u8 reg, int len, uint8_t *val)
+{
+ return __ricoh583_bulk_reads(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(ricoh583_bulk_reads);
+
+int ricoh583_set_bits(struct device *dev, u8 reg, uint8_t bit_mask)
+{
+ struct ricoh583 *ricoh583 = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&ricoh583->io_lock);
+
+ ret = __ricoh583_read(to_i2c_client(dev), reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if ((reg_val & bit_mask) != bit_mask) {
+ reg_val |= bit_mask;
+ ret = __ricoh583_write(to_i2c_client(dev), reg, reg_val);
+ }
+out:
+ mutex_unlock(&ricoh583->io_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ricoh583_set_bits);
+
+int ricoh583_clr_bits(struct device *dev, u8 reg, uint8_t bit_mask)
+{
+ struct ricoh583 *ricoh583 = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&ricoh583->io_lock);
+
+ ret = __ricoh583_read(to_i2c_client(dev), reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if (reg_val & bit_mask) {
+ reg_val &= ~bit_mask;
+ ret = __ricoh583_write(to_i2c_client(dev), reg, reg_val);
+ }
+out:
+ mutex_unlock(&ricoh583->io_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ricoh583_clr_bits);
+
+int ricoh583_update(struct device *dev, u8 reg, uint8_t val, uint8_t mask)
+{
+ struct ricoh583 *ricoh583 = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&ricoh583->io_lock);
+
+ ret = __ricoh583_read(ricoh583->client, reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if ((reg_val & mask) != val) {
+ reg_val = (reg_val & ~mask) | (val & mask);
+ ret = __ricoh583_write(ricoh583->client, reg, reg_val);
+ }
+out:
+ mutex_unlock(&ricoh583->io_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ricoh583_update);
+
+static struct i2c_client *ricoh583_i2c_client;
+int ricoh583_power_off(void)
+{
+ if (!ricoh583_i2c_client)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ricoh583_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct ricoh583 *ricoh583 = container_of(gc, struct ricoh583, gpio);
+ uint8_t val;
+ int ret;
+
+ ret = __ricoh583_read(ricoh583->client, RICOH583_GPIO_MON_IOIN, &val);
+ if (ret < 0)
+ return ret;
+
+ return ((val & (0x1 << offset)) != 0);
+}
+
+static void ricoh583_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct ricoh583 *ricoh583 = container_of(chip, struct ricoh583, gpio);
+ if (value)
+ ricoh583_clr_bits(ricoh583->dev, RICOH583_GPIO_IOOUT,
+ 1 << offset);
+ else
+ ricoh583_set_bits(ricoh583->dev, RICOH583_GPIO_IOOUT,
+ 1 << offset);
+}
+
+static int ricoh583_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct ricoh583 *ricoh583 = container_of(chip, struct ricoh583, gpio);
+
+ return ricoh583_clr_bits(ricoh583->dev, RICOH583_GPIO_IOSEL,
+ 1 << offset);
+}
+
+static int ricoh583_gpio_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct ricoh583 *ricoh583 = container_of(chip, struct ricoh583, gpio);
+
+ ricoh583_gpio_set(chip, offset, value);
+ return ricoh583_set_bits(ricoh583->dev, RICOH583_GPIO_IOSEL,
+ 1 << offset);
+}
+
+static int ricoh583_gpio_to_irq(struct gpio_chip *chip, unsigned off)
+{
+ struct ricoh583 *ricoh583 = container_of(chip, struct ricoh583, gpio);
+
+ if ((off >= 0) && (off < 8))
+ return ricoh583->irq_base + RICOH583_IRQ_GPIO0 + off;
+
+ return -EIO;
+}
+
+static void __devinit ricoh583_gpio_init(struct ricoh583 *ricoh583,
+ struct ricoh583_platform_data *pdata)
+{
+ int ret;
+ int i;
+ struct ricoh583_gpio_init_data *ginit;
+
+ if (pdata->gpio_base <= 0)
+ return;
+
+ for (i = 0; i < pdata->num_gpioinit_data; ++i) {
+ ginit = &pdata->gpio_init_data[i];
+ if (!ginit->init_apply)
+ continue;
+ if (ginit->pulldn_en)
+ ret = ricoh583_set_bits(ricoh583->dev,
+ RICOH583_GPIO_PDEN, 1 << i);
+ else
+ ret = ricoh583_clr_bits(ricoh583->dev,
+ RICOH583_GPIO_PDEN, 1 << i);
+ if (ret < 0)
+ dev_err(ricoh583->dev, "Gpio %d init "
+ "pden configuration failed: %d\n", i, ret);
+
+ if (ginit->output_mode_en) {
+ if (ginit->output_val)
+ ret = ricoh583_clr_bits(ricoh583->dev,
+ RICOH583_GPIO_IOOUT, 1 << i);
+ else
+ ret = ricoh583_set_bits(ricoh583->dev,
+ RICOH583_GPIO_IOOUT, 1 << i);
+ if (!ret)
+ ret = ricoh583_set_bits(ricoh583->dev,
+ RICOH583_GPIO_IOSEL, 1 << i);
+ } else
+ ret = ricoh583_clr_bits(ricoh583->dev,
+ RICOH583_GPIO_IOSEL, 1 << i);
+
+ if (ret < 0)
+ dev_err(ricoh583->dev, "Gpio %d init "
+ "dir configuration failed: %d\n", i, ret);
+ }
+
+ ricoh583->gpio.owner = THIS_MODULE;
+ ricoh583->gpio.label = ricoh583->client->name;
+ ricoh583->gpio.dev = ricoh583->dev;
+ ricoh583->gpio.base = pdata->gpio_base;
+ ricoh583->gpio.ngpio = RICOH583_NR_GPIO;
+ ricoh583->gpio.can_sleep = 1;
+
+ ricoh583->gpio.direction_input = ricoh583_gpio_input;
+ ricoh583->gpio.direction_output = ricoh583_gpio_output;
+ ricoh583->gpio.set = ricoh583_gpio_set;
+ ricoh583->gpio.get = ricoh583_gpio_get;
+ ricoh583->gpio.to_irq = ricoh583_gpio_to_irq;
+
+ ret = gpiochip_add(&ricoh583->gpio);
+ if (ret)
+ dev_warn(ricoh583->dev, "GPIO registration failed: %d\n", ret);
+}
+
+static void ricoh583_irq_lock(unsigned int irq)
+{
+ struct ricoh583 *ricoh583 = get_irq_chip_data(irq);
+
+ mutex_lock(&ricoh583->irq_lock);
+}
+
+static void ricoh583_irq_unmask(unsigned int irq)
+{
+ struct ricoh583 *ricoh583 = get_irq_chip_data(irq);
+ unsigned int __irq = irq - ricoh583->irq_base;
+ const struct ricoh583_irq_data *data = &ricoh583_irqs[__irq];
+
+ ricoh583->group_irq_en[data->grp_index] |= (1 << data->grp_index);
+ if (ricoh583->group_irq_en[data->grp_index])
+ ricoh583->intc_inten_reg |= 1 << data->master_bit;
+
+ ricoh583->irq_en_reg[data->mask_reg_index] |= 1 << data->int_en_bit;
+}
+
+static void ricoh583_irq_mask(unsigned int irq)
+{
+ struct ricoh583 *ricoh583 = get_irq_chip_data(irq);
+ unsigned int __irq = irq - ricoh583->irq_base;
+ const struct ricoh583_irq_data *data = &ricoh583_irqs[__irq];
+
+ ricoh583->group_irq_en[data->grp_index] &= ~(1 << data->grp_index);
+ if (!ricoh583->group_irq_en[data->grp_index])
+ ricoh583->intc_inten_reg &= ~(1 << data->master_bit);
+
+ ricoh583->irq_en_reg[data->mask_reg_index] &= ~(1 << data->int_en_bit);
+}
+
+static void ricoh583_irq_sync_unlock(unsigned int irq)
+{
+ struct ricoh583 *ricoh583 = get_irq_chip_data(irq);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ricoh583->gpedge_reg); i++) {
+ if (ricoh583->gpedge_reg[i] != ricoh583->gpedge_cache[i]) {
+ if (!WARN_ON(__ricoh583_write(ricoh583->client,
+ ricoh583->gpedge_add[i],
+ ricoh583->gpedge_reg[i])))
+ ricoh583->gpedge_cache[i] =
+ ricoh583->gpedge_reg[i];
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ricoh583->irq_en_reg); i++) {
+ if (ricoh583->irq_en_reg[i] != ricoh583->irq_en_cache[i]) {
+ if (!WARN_ON(__ricoh583_write(ricoh583->client,
+ ricoh583->irq_en_add[i],
+ ricoh583->irq_en_reg[i])))
+ ricoh583->irq_en_cache[i] =
+ ricoh583->irq_en_reg[i];
+ }
+ }
+
+ if (ricoh583->intc_inten_reg != ricoh583->intc_inten_cache) {
+ if (!WARN_ON(__ricoh583_write(ricoh583->client,
+ RICOH583_INTC_INTEN, ricoh583->intc_inten_reg)))
+ ricoh583->intc_inten_reg = ricoh583->intc_inten_cache;
+ }
+
+ mutex_unlock(&ricoh583->irq_lock);
+}
+
+static int ricoh583_irq_set_type(unsigned int irq, unsigned int type)
+{
+ struct ricoh583 *ricoh583 = get_irq_chip_data(irq);
+ unsigned int __irq = irq - ricoh583->irq_base;
+ const struct ricoh583_irq_data *data = &ricoh583_irqs[__irq];
+ int val = 0;
+ int gpedge_index;
+ int gpedge_bit_pos;
+
+ if (data->int_type & GPIO_INT) {
+ gpedge_index = data->int_en_bit / 4;
+ gpedge_bit_pos = data->int_en_bit % 4;
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ val |= 0x2;
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ val |= 0x1;
+
+ ricoh583->gpedge_reg[gpedge_index] &= ~(3 << gpedge_bit_pos);
+ ricoh583->gpedge_reg[gpedge_index] |= (val << gpedge_bit_pos);
+ ricoh583_irq_unmask(irq);
+ }
+ return 0;
+}
+
+static irqreturn_t ricoh583_irq(int irq, void *data)
+{
+ struct ricoh583 *ricoh583 = data;
+ u8 int_sts[9];
+ u8 master_int;
+ int i;
+ int ret;
+ u8 rtc_int_sts = 0;
+
+ /* Clear the status */
+ for (i = 0; i < 9; i++)
+ int_sts[i] = 0;
+
+ ret = __ricoh583_read(ricoh583->client, RICOH583_INTC_INTMON,
+ &master_int);
+ if (ret < 0) {
+ dev_err(ricoh583->dev, "Error in reading reg 0x%02x "
+ "error: %d\n", RICOH583_INTC_INTMON, ret);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < 9; ++i) {
+ if (!(master_int & ricoh583->main_int_type[i]))
+ continue;
+ ret = __ricoh583_read(ricoh583->client,
+ ricoh583->irq_mon_add[i], &int_sts[i]);
+ if (ret < 0) {
+ dev_err(ricoh583->dev, "Error in reading reg 0x%02x "
+ "error: %d\n", ricoh583->irq_mon_add[i], ret);
+ int_sts[i] = 0;
+ continue;
+ }
+
+ if (ricoh583->main_int_type[i] & RTC_INT) {
+ rtc_int_sts = 0;
+ if (int_sts[i] & 0x1)
+ rtc_int_sts |= BIT(6);
+ if (int_sts[i] & 0x2)
+ rtc_int_sts |= BIT(7);
+ if (int_sts[i] & 0x4)
+ rtc_int_sts |= BIT(0);
+ if (int_sts[i] & 0x8)
+ rtc_int_sts |= BIT(5);
+ }
+
+ ret = __ricoh583_write(ricoh583->client,
+ ricoh583->irq_clr_add[i], ~int_sts[i]);
+ if (ret < 0) {
+ dev_err(ricoh583->dev, "Error in reading reg 0x%02x "
+ "error: %d\n", ricoh583->irq_clr_add[i], ret);
+ }
+ if (ricoh583->main_int_type[i] & RTC_INT)
+ int_sts[i] = rtc_int_sts;
+ }
+
+ /* Merge gpio interrupts for rising and falling case*/
+ int_sts[7] |= int_sts[8];
+
+ /* Call interrupt handler if enabled */
+ for (i = 0; i < RICOH583_NR_IRQS; ++i) {
+ const struct ricoh583_irq_data *data = &ricoh583_irqs[i];
+ if ((int_sts[data->mask_reg_index] & (1 << data->int_en_bit)) &&
+ (ricoh583->group_irq_en[data->master_bit] &
+ (1 << data->grp_index)))
+ handle_nested_irq(ricoh583->irq_base + i);
+ }
+ return IRQ_HANDLED;
+}
+
+static int __devinit ricoh583_irq_init(struct ricoh583 *ricoh583, int irq,
+ int irq_base)
+{
+ int i, ret;
+
+ if (!irq_base) {
+ dev_warn(ricoh583->dev, "No interrupt support on IRQ base\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&ricoh583->irq_lock);
+
+ /* Initialize all locals to 0 */
+ for (i = 0; i < MAX_INTERRUPT_MASKS; i++) {
+ ricoh583->irq_en_cache[i] = 0;
+ ricoh583->irq_en_reg[i] = 0;
+ }
+ ricoh583->intc_inten_cache = 0;
+ ricoh583->intc_inten_reg = 0;
+ for (i = 0; i < 2; i++) {
+ ricoh583->gpedge_cache[i] = 0;
+ ricoh583->gpedge_reg[i] = 0;
+ }
+
+ /* Interrupt enable register */
+ ricoh583->gpedge_add[0] = RICOH583_GPIO_GPEDGE2;
+ ricoh583->gpedge_add[1] = RICOH583_GPIO_GPEDGE1;
+ ricoh583->irq_en_add[0] = RICOH583_INT_EN_SYS1;
+ ricoh583->irq_en_add[1] = RICOH583_INT_EN_SYS2;
+ ricoh583->irq_en_add[2] = RICOH583_INT_EN_DCDC;
+ ricoh583->irq_en_add[3] = RICOH583_INT_EN_RTC;
+ ricoh583->irq_en_add[4] = RICOH583_INT_EN_ADC1;
+ ricoh583->irq_en_add[5] = RICOH583_INT_EN_ADC2;
+ ricoh583->irq_en_add[6] = RICOH583_INT_EN_ADC3;
+ ricoh583->irq_en_add[7] = RICOH583_INT_EN_GPIO;
+
+ /* Interrupt status monitor register */
+ ricoh583->irq_mon_add[0] = RICOH583_INT_MON_SYS1;
+ ricoh583->irq_mon_add[1] = RICOH583_INT_MON_SYS2;
+ ricoh583->irq_mon_add[2] = RICOH583_INT_MON_DCDC;
+ ricoh583->irq_mon_add[3] = RICOH583_INT_MON_RTC;
+ ricoh583->irq_mon_add[4] = RICOH583_INT_IR_ADCL;
+ ricoh583->irq_mon_add[5] = RICOH583_INT_IR_ADCH;
+ ricoh583->irq_mon_add[6] = RICOH583_INT_IR_ADCEND;
+ ricoh583->irq_mon_add[7] = RICOH583_INT_IR_GPIOF;
+ ricoh583->irq_mon_add[8] = RICOH583_INT_IR_GPIOR;
+
+ /* Interrupt status clear register */
+ ricoh583->irq_clr_add[0] = RICOH583_INT_IR_SYS1;
+ ricoh583->irq_clr_add[1] = RICOH583_INT_IR_SYS2;
+ ricoh583->irq_clr_add[2] = RICOH583_INT_IR_DCDC;
+ ricoh583->irq_clr_add[3] = RICOH583_INT_IR_RTC;
+ ricoh583->irq_clr_add[4] = RICOH583_INT_IR_ADCL;
+ ricoh583->irq_clr_add[5] = RICOH583_INT_IR_ADCH;
+ ricoh583->irq_clr_add[6] = RICOH583_INT_IR_ADCEND;
+ ricoh583->irq_clr_add[7] = RICOH583_INT_IR_GPIOF;
+ ricoh583->irq_clr_add[8] = RICOH583_INT_IR_GPIOR;
+
+ ricoh583->main_int_type[0] = SYS_INT;
+ ricoh583->main_int_type[1] = SYS_INT;
+ ricoh583->main_int_type[2] = DCDC_INT;
+ ricoh583->main_int_type[3] = RTC_INT;
+ ricoh583->main_int_type[4] = ADC_INT;
+ ricoh583->main_int_type[5] = ADC_INT;
+ ricoh583->main_int_type[6] = ADC_INT;
+ ricoh583->main_int_type[7] = GPIO_INT;
+ ricoh583->main_int_type[8] = GPIO_INT;
+
+ /* Initailize all int register to 0 */
+ for (i = 0; i < MAX_INTERRUPT_MASKS; i++) {
+ ret = __ricoh583_write(ricoh583->client,
+ ricoh583->irq_en_add[i],
+ ricoh583->irq_en_reg[i]);
+ if (ret < 0)
+ dev_err(ricoh583->dev, "Error in writin reg 0x%02x "
+ "error: %d\n", ricoh583->irq_en_add[i], ret);
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = __ricoh583_write(ricoh583->client,
+ ricoh583->gpedge_add[i],
+ ricoh583->gpedge_reg[i]);
+ if (ret < 0)
+ dev_err(ricoh583->dev, "Error in writin reg 0x%02x "
+ "error: %d\n", ricoh583->gpedge_add[i], ret);
+ }
+
+ ret = __ricoh583_write(ricoh583->client, RICOH583_INTC_INTEN, 0x0);
+ if (ret < 0)
+ dev_err(ricoh583->dev, "Error in writin reg 0x%02x "
+ "error: %d\n", RICOH583_INTC_INTEN, ret);
+
+ /* Clear all interrupts in case they woke up active. */
+ for (i = 0; i < 9; i++) {
+ ret = __ricoh583_write(ricoh583->client,
+ ricoh583->irq_clr_add[i], 0);
+ if (ret < 0)
+ dev_err(ricoh583->dev, "Error in writin reg 0x%02x "
+ "error: %d\n", ricoh583->irq_clr_add[i], ret);
+ }
+
+ ricoh583->irq_base = irq_base;
+ ricoh583->irq_chip.name = "ricoh583";
+ ricoh583->irq_chip.mask = ricoh583_irq_mask;
+ ricoh583->irq_chip.unmask = ricoh583_irq_unmask;
+ ricoh583->irq_chip.bus_lock = ricoh583_irq_lock;
+ ricoh583->irq_chip.bus_sync_unlock = ricoh583_irq_sync_unlock;
+ ricoh583->irq_chip.set_type = ricoh583_irq_set_type;
+
+ for (i = 0; i < RICOH583_NR_IRQS; i++) {
+ int __irq = i + ricoh583->irq_base;
+ set_irq_chip_data(__irq, ricoh583);
+ set_irq_chip_and_handler(__irq, &ricoh583->irq_chip,
+ handle_simple_irq);
+ set_irq_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(__irq, IRQF_VALID);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, ricoh583_irq, IRQF_ONESHOT,
+ "ricoh583", ricoh583);
+ if (ret < 0)
+ dev_err(ricoh583->dev, "Error in registering interrupt "
+ "error: %d\n", ret);
+ if (!ret) {
+ device_init_wakeup(ricoh583->dev, 1);
+ enable_irq_wake(irq);
+ }
+ return ret;
+}
+
+static int ricoh583_remove_subdev(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
+ return 0;
+}
+
+static int ricoh583_remove_subdevs(struct ricoh583 *ricoh583)
+{
+ return device_for_each_child(ricoh583->dev, NULL,
+ ricoh583_remove_subdev);
+}
+
+static int __devinit ricoh583_add_subdevs(struct ricoh583 *ricoh583,
+ struct ricoh583_platform_data *pdata)
+{
+ struct ricoh583_subdev_info *subdev;
+ struct platform_device *pdev;
+ int i, ret = 0;
+
+ for (i = 0; i < pdata->num_subdevs; i++) {
+ subdev = &pdata->subdevs[i];
+
+ pdev = platform_device_alloc(subdev->name, subdev->id);
+
+ pdev->dev.parent = ricoh583->dev;
+ pdev->dev.platform_data = subdev->platform_data;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto failed;
+ }
+ return 0;
+
+failed:
+ ricoh583_remove_subdevs(ricoh583);
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+static void print_regs(const char *header, struct seq_file *s,
+ struct i2c_client *client, int start_offset,
+ int end_offset)
+{
+ uint8_t reg_val;
+ int i;
+ int ret;
+
+ seq_printf(s, "%s\n", header);
+ for (i = start_offset; i <= end_offset; ++i) {
+ ret = __ricoh583_read(client, i, &reg_val);
+ if (ret >= 0)
+ seq_printf(s, "Reg 0x%02x Value 0x%02x\n", i, reg_val);
+ }
+ seq_printf(s, "------------------\n");
+}
+
+static int dbg_tps_show(struct seq_file *s, void *unused)
+{
+ struct ricoh583 *tps = s->private;
+ struct i2c_client *client = tps->client;
+
+ seq_printf(s, "RICOH583 Registers\n");
+ seq_printf(s, "------------------\n");
+
+ print_regs("System Regs", s, client, 0x0, 0xF);
+ print_regs("Power Control Regs", s, client, 0x10, 0x2B);
+ print_regs("DCDC1 Regs", s, client, 0x30, 0x43);
+ print_regs("DCDC1 Regs", s, client, 0x60, 0x63);
+ print_regs("LDO Regs", s, client, 0x50, 0x5F);
+ print_regs("LDO Regs", s, client, 0x64, 0x6D);
+ print_regs("ADC Regs", s, client, 0x70, 0x72);
+ print_regs("ADC Regs", s, client, 0x74, 0x8B);
+ print_regs("ADC Regs", s, client, 0x90, 0x96);
+ print_regs("GPIO Regs", s, client, 0xA0, 0xAC);
+ print_regs("INTC Regs", s, client, 0xAD, 0xAF);
+ print_regs("RTC Regs", s, client, 0xE0, 0xEE);
+ print_regs("RTC Regs", s, client, 0xF0, 0xF4);
+ return 0;
+}
+
+static int dbg_tps_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_tps_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = dbg_tps_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+static void __init ricoh583_debuginit(struct ricoh583 *tps)
+{
+ (void)debugfs_create_file("ricoh583", S_IRUGO, NULL,
+ tps, &debug_fops);
+}
+#else
+static void __init ricoh583_debuginit(struct ricoh583 *tpsi)
+{
+ return;
+}
+#endif
+
+static int ricoh583_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ricoh583 *ricoh583;
+ struct ricoh583_platform_data *pdata = i2c->dev.platform_data;
+ int ret;
+
+ ricoh583 = kzalloc(sizeof(struct ricoh583), GFP_KERNEL);
+ if (ricoh583 == NULL)
+ return -ENOMEM;
+
+ ricoh583->client = i2c;
+ ricoh583->dev = &i2c->dev;
+ i2c_set_clientdata(i2c, ricoh583);
+
+ mutex_init(&ricoh583->io_lock);
+
+ if (i2c->irq) {
+ ret = ricoh583_irq_init(ricoh583, i2c->irq, pdata->irq_base);
+ if (ret) {
+ dev_err(&i2c->dev, "IRQ init failed: %d\n", ret);
+ goto err_irq_init;
+ }
+ }
+
+ ret = ricoh583_add_subdevs(ricoh583, pdata);
+ if (ret) {
+ dev_err(&i2c->dev, "add devices failed: %d\n", ret);
+ goto err_add_devs;
+ }
+
+ ricoh583_gpio_init(ricoh583, pdata);
+
+ ricoh583_debuginit(ricoh583);
+
+ ricoh583_i2c_client = i2c;
+ return 0;
+
+err_add_devs:
+ if (i2c->irq)
+ free_irq(i2c->irq, ricoh583);
+err_irq_init:
+ kfree(ricoh583);
+ return ret;
+}
+
+static int __devexit ricoh583_i2c_remove(struct i2c_client *i2c)
+{
+ struct ricoh583 *ricoh583 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, ricoh583);
+
+ ricoh583_remove_subdevs(ricoh583);
+ kfree(ricoh583);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ricoh583_i2c_suspend(struct i2c_client *i2c, pm_message_t state)
+{
+ if (i2c->irq)
+ disable_irq(i2c->irq);
+ return 0;
+}
+
+
+static int ricoh583_i2c_resume(struct i2c_client *i2c)
+{
+ if (i2c->irq)
+ enable_irq(i2c->irq);
+ return 0;
+}
+
+#endif
+
+static const struct i2c_device_id ricoh583_i2c_id[] = {
+ {"ricoh583", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ricoh583_i2c_id);
+
+static struct i2c_driver ricoh583_i2c_driver = {
+ .driver = {
+ .name = "ricoh583",
+ .owner = THIS_MODULE,
+ },
+ .probe = ricoh583_i2c_probe,
+ .remove = __devexit_p(ricoh583_i2c_remove),
+#ifdef CONFIG_PM
+ .suspend = ricoh583_i2c_suspend,
+ .resume = ricoh583_i2c_resume,
+#endif
+ .id_table = ricoh583_i2c_id,
+};
+
+
+static int __init ricoh583_i2c_init(void)
+{
+ int ret = -ENODEV;
+ ret = i2c_add_driver(&ricoh583_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register I2C driver: %d\n", ret);
+
+ return ret;
+}
+
+subsys_initcall(ricoh583_i2c_init);
+
+static void __exit ricoh583_i2c_exit(void)
+{
+ i2c_del_driver(&ricoh583_i2c_driver);
+}
+
+module_exit(ricoh583_i2c_exit);
+
+MODULE_DESCRIPTION("RICOH583 multi-function core driver");
+MODULE_LICENSE("GPL");