| /* |
| * driver/mfd/tps6591x.c |
| * |
| * Core driver for TI TPS6591x PMIC family |
| * |
| * Copyright (C) 2011 NVIDIA Corporation |
| * |
| * 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. |
| * |
| */ |
| |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/mutex.h> |
| #include <linux/slab.h> |
| #include <linux/gpio.h> |
| #include <linux/i2c.h> |
| |
| #include <linux/mfd/core.h> |
| #include <linux/mfd/tps6591x.h> |
| |
| //=================stree test================= |
| #include <linux/miscdevice.h> |
| #include <linux/ioctl.h> |
| #include <linux/fs.h> |
| //=================stree test end ================= |
| #include <linux/delay.h> |
| #define TPS6591X_RETRY_LIMIT (3) |
| #define TPS6591X_RETRY_DELAY (5) |
| |
| /* device control registers */ |
| #define TPS6591X_DEVCTRL 0x3F |
| #define DEVCTRL_PWR_OFF_SEQ (1 << 7) |
| #define DEVCTRL_DEV_ON (1 << 2) |
| #define DEVCTRL_DEV_SLP (1 << 1) |
| #define DEVCTRL_DEV_OFF (1 << 0) |
| #define TPS6591X_DEVCTRL2 0x40 |
| |
| #define TPS6591X_VDDCTRL_ADD 0x27 |
| #define TPS6591X_VDDCTRL_STATE_OFF 0x0 |
| #define TPS6591X_VDDCTRL_STATE_MASK 0x3 |
| |
| /* device sleep on registers */ |
| #define TPS6591X_SLEEP_KEEP_ON 0x42 |
| #define SLEEP_KEEP_ON_THERM (1 << 7) |
| #define SLEEP_KEEP_ON_CLKOUT32K (1 << 6) |
| #define SLEEP_KEEP_ON_VRTC (1 << 5) |
| #define SLEEP_KEEP_ON_I2CHS (1 << 4) |
| |
| /* interrupt status registers */ |
| #define TPS6591X_INT_STS 0x50 |
| #define TPS6591X_INT_STS2 0x52 |
| #define TPS6591X_INT_STS3 0x54 |
| |
| /* interrupt mask registers */ |
| #define TPS6591X_INT_MSK 0x51 |
| #define TPS6591X_INT_MSK2 0x53 |
| #define TPS6591X_INT_MSK3 0x55 |
| |
| /* GPIO register base address */ |
| #define TPS6591X_GPIO_BASE_ADDR 0x60 |
| |
| /* silicon version number */ |
| #define TPS6591X_VERNUM 0x80 |
| |
| #define TPS6591X_GPIO_SLEEP 7 |
| #define TPS6591X_GPIO_PDEN 3 |
| #define TPS6591X_GPIO_DIR 2 |
| |
| #define GPIO4_F_IT_MSK (1 << 1) |
| #define RTC_ALARM_IT_MSK (1 << 6) |
| |
| enum irq_type { |
| EVENT, |
| GPIO, |
| }; |
| |
| struct tps6591x_irq_data { |
| u8 mask_reg; |
| u8 mask_pos; |
| enum irq_type type; |
| }; |
| |
| #define TPS6591X_IRQ(_reg, _mask_pos, _type) \ |
| { \ |
| .mask_reg = (_reg), \ |
| .mask_pos = (_mask_pos), \ |
| .type = (_type), \ |
| } |
| |
| static const struct tps6591x_irq_data tps6591x_irqs[] = { |
| [TPS6591X_INT_PWRHOLD_F] = TPS6591X_IRQ(0, 0, EVENT), |
| [TPS6591X_INT_VMBHI] = TPS6591X_IRQ(0, 1, EVENT), |
| [TPS6591X_INT_PWRON] = TPS6591X_IRQ(0, 2, EVENT), |
| [TPS6591X_INT_PWRON_LP] = TPS6591X_IRQ(0, 3, EVENT), |
| [TPS6591X_INT_PWRHOLD_R] = TPS6591X_IRQ(0, 4, EVENT), |
| [TPS6591X_INT_HOTDIE] = TPS6591X_IRQ(0, 5, EVENT), |
| [TPS6591X_INT_RTC_ALARM] = TPS6591X_IRQ(0, 6, EVENT), |
| [TPS6591X_INT_RTC_PERIOD] = TPS6591X_IRQ(0, 7, EVENT), |
| [TPS6591X_INT_GPIO0] = TPS6591X_IRQ(1, 0, GPIO), |
| [TPS6591X_INT_GPIO1] = TPS6591X_IRQ(1, 2, GPIO), |
| [TPS6591X_INT_GPIO2] = TPS6591X_IRQ(1, 4, GPIO), |
| [TPS6591X_INT_GPIO3] = TPS6591X_IRQ(1, 6, GPIO), |
| [TPS6591X_INT_GPIO4] = TPS6591X_IRQ(2, 0, GPIO), |
| [TPS6591X_INT_GPIO5] = TPS6591X_IRQ(2, 2, GPIO), |
| [TPS6591X_INT_WTCHDG] = TPS6591X_IRQ(2, 4, EVENT), |
| [TPS6591X_INT_VMBCH2_H] = TPS6591X_IRQ(2, 5, EVENT), |
| [TPS6591X_INT_VMBCH2_L] = TPS6591X_IRQ(2, 6, EVENT), |
| [TPS6591X_INT_PWRDN] = TPS6591X_IRQ(2, 7, EVENT), |
| }; |
| |
| struct tps6591x { |
| struct mutex lock; |
| struct device *dev; |
| struct i2c_client *client; |
| |
| struct gpio_chip gpio; |
| struct irq_chip irq_chip; |
| struct mutex irq_lock; |
| int irq_base; |
| int irq_main; |
| u32 irq_en; |
| u8 mask_cache[3]; |
| u8 mask_reg[3]; |
| //=================stree test================= |
| int i2c_status; |
| struct delayed_work stress_test; |
| struct miscdevice tps6591x_misc; |
| //=================stree test end================= |
| }; |
| |
| static inline int __tps6591x_read(struct i2c_client *client, |
| int reg, uint8_t *val) |
| { |
| int ret, retry_time = 0; |
| |
| ret = i2c_smbus_read_byte_data(client, reg); |
| |
| while(ret < 0) |
| { |
| if(retry_time < TPS6591X_RETRY_LIMIT) |
| { |
| udelay(TPS6591X_RETRY_DELAY); |
| retry_time ++; |
| pr_info("%s : retry %d times\n", __func__, retry_time); |
| ret = i2c_smbus_read_byte_data(client, reg); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "failed reading at 0x%02x\n", reg); |
| return ret; |
| } |
| |
| *val = (uint8_t)ret; |
| return 0; |
| } |
| |
| static inline int __tps6591x_reads(struct i2c_client *client, int reg, |
| int len, uint8_t *val) |
| { |
| int ret, retry_time = 0; |
| |
| ret = i2c_smbus_read_i2c_block_data(client, reg, len, val); |
| |
| while(ret < 0) |
| { |
| if(retry_time < TPS6591X_RETRY_LIMIT) |
| { |
| udelay(TPS6591X_RETRY_DELAY); |
| retry_time ++; |
| pr_info("%s : retry %d times\n", __func__, retry_time); |
| ret = i2c_smbus_read_i2c_block_data(client, reg, len, val); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "failed reading from 0x%02x\n", reg); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static inline int __tps6591x_write(struct i2c_client *client, |
| int reg, uint8_t val) |
| { |
| int ret, retry_time = 0; |
| |
| ret = i2c_smbus_write_byte_data(client, reg, val); |
| |
| while(ret < 0) |
| { |
| if(retry_time < TPS6591X_RETRY_LIMIT) |
| { |
| udelay(TPS6591X_RETRY_DELAY); |
| retry_time ++; |
| pr_info("%s : retry %d times\n", __func__, retry_time); |
| ret = i2c_smbus_write_byte_data(client, reg, val); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n", |
| val, reg); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static inline int __tps6591x_writes(struct i2c_client *client, int reg, |
| int len, uint8_t *val) |
| { |
| int ret, retry_time = 0; |
| |
| ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); |
| |
| while(ret < 0) |
| { |
| if(retry_time < TPS6591X_RETRY_LIMIT) |
| { |
| udelay(TPS6591X_RETRY_DELAY); |
| retry_time ++; |
| pr_info("%s : retry %d times\n", __func__, retry_time); |
| ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "failed writings to 0x%02x\n", reg); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| int tps6591x_write(struct device *dev, int reg, uint8_t val) |
| { |
| struct tps6591x *tps6591x = dev_get_drvdata(dev); |
| int ret = 0; |
| |
| |
| mutex_lock(&tps6591x->lock); |
| ret = __tps6591x_write(to_i2c_client(dev), reg, val); |
| mutex_unlock(&tps6591x->lock); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(tps6591x_write); |
| |
| int tps6591x_writes(struct device *dev, int reg, int len, uint8_t *val) |
| { |
| struct tps6591x *tps6591x = dev_get_drvdata(dev); |
| int ret = 0; |
| |
| mutex_lock(&tps6591x->lock); |
| ret = __tps6591x_writes(to_i2c_client(dev), reg, len, val); |
| mutex_unlock(&tps6591x->lock); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(tps6591x_writes); |
| |
| int tps6591x_read(struct device *dev, int reg, uint8_t *val) |
| { |
| return __tps6591x_read(to_i2c_client(dev), reg, val); |
| } |
| EXPORT_SYMBOL_GPL(tps6591x_read); |
| |
| int tps6591x_reads(struct device *dev, int reg, int len, uint8_t *val) |
| { |
| return __tps6591x_reads(to_i2c_client(dev), reg, len, val); |
| } |
| EXPORT_SYMBOL_GPL(tps6591x_reads); |
| |
| int tps6591x_set_bits(struct device *dev, int reg, uint8_t bit_mask) |
| { |
| struct tps6591x *tps6591x = dev_get_drvdata(dev); |
| uint8_t reg_val; |
| int ret = 0; |
| |
| mutex_lock(&tps6591x->lock); |
| |
| ret = __tps6591x_read(to_i2c_client(dev), reg, ®_val); |
| if (ret) |
| goto out; |
| |
| if ((reg_val & bit_mask) != bit_mask) { |
| reg_val |= bit_mask; |
| ret = __tps6591x_write(to_i2c_client(dev), reg, reg_val); |
| } |
| out: |
| mutex_unlock(&tps6591x->lock); |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(tps6591x_set_bits); |
| |
| int tps6591x_clr_bits(struct device *dev, int reg, uint8_t bit_mask) |
| { |
| struct tps6591x *tps6591x = dev_get_drvdata(dev); |
| uint8_t reg_val; |
| int ret = 0; |
| |
| mutex_lock(&tps6591x->lock); |
| |
| ret = __tps6591x_read(to_i2c_client(dev), reg, ®_val); |
| if (ret) |
| goto out; |
| |
| if (reg_val & bit_mask) { |
| reg_val &= ~bit_mask; |
| ret = __tps6591x_write(to_i2c_client(dev), reg, reg_val); |
| } |
| out: |
| mutex_unlock(&tps6591x->lock); |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(tps6591x_clr_bits); |
| |
| int tps6591x_update(struct device *dev, int reg, uint8_t val, uint8_t mask) |
| { |
| struct tps6591x *tps6591x = dev_get_drvdata(dev); |
| uint8_t reg_val; |
| int ret = 0; |
| |
| mutex_lock(&tps6591x->lock); |
| |
| ret = __tps6591x_read(tps6591x->client, reg, ®_val); |
| if (ret) |
| goto out; |
| |
| if ((reg_val & mask) != val) { |
| reg_val = (reg_val & ~mask) | (val & mask); |
| ret = __tps6591x_write(tps6591x->client, reg, reg_val); |
| } |
| out: |
| mutex_unlock(&tps6591x->lock); |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(tps6591x_update); |
| |
| static struct i2c_client *tps6591x_i2c_client; |
| static void tps6591x_power_off(void) |
| { |
| struct device *dev = NULL; |
| int ret; |
| pr_err("%s ++\n", __func__); |
| |
| if (!tps6591x_i2c_client) |
| return; |
| |
| dev = &tps6591x_i2c_client->dev; |
| |
| pr_err("%s(): Setting power off seq\n", __func__); |
| ret = tps6591x_set_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_PWR_OFF_SEQ); |
| if (ret < 0) |
| return ret; |
| |
| pr_err("%s(): Clearing DEV_SLP\n", __func__); |
| ret = tps6591x_clr_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_DEV_SLP); |
| if (ret < 0) |
| return ret; |
| |
| pr_err("%s(): Setting device off and clearing dev-on\n", __func__); |
| ret = tps6591x_update(dev, TPS6591X_DEVCTRL, DEVCTRL_DEV_OFF, |
| DEVCTRL_DEV_OFF | DEVCTRL_DEV_ON); |
| return ret; |
| } |
| |
| static int tps6591x_gpio_get(struct gpio_chip *gc, unsigned offset) |
| { |
| struct tps6591x *tps6591x = container_of(gc, struct tps6591x, gpio); |
| uint8_t val; |
| int ret; |
| |
| ret = __tps6591x_read(tps6591x->client, TPS6591X_GPIO_BASE_ADDR + |
| offset, &val); |
| if (ret) |
| return ret; |
| |
| if (val & 0x4) |
| return val & 0x1; |
| else |
| return (val & 0x2) ? 1 : 0; |
| } |
| |
| static void tps6591x_gpio_set(struct gpio_chip *chip, unsigned offset, |
| int value) |
| { |
| |
| struct tps6591x *tps6591x = container_of(chip, struct tps6591x, gpio); |
| |
| tps6591x_update(tps6591x->dev, TPS6591X_GPIO_BASE_ADDR + offset, |
| value, 0x1); |
| } |
| |
| static int tps6591x_gpio_input(struct gpio_chip *gc, unsigned offset) |
| { |
| struct tps6591x *tps6591x = container_of(gc, struct tps6591x, gpio); |
| uint8_t reg_val; |
| int ret; |
| |
| ret = __tps6591x_read(tps6591x->client, TPS6591X_GPIO_BASE_ADDR + |
| offset, ®_val); |
| if (ret) |
| return ret; |
| |
| reg_val &= ~0x4; |
| return __tps6591x_write(tps6591x->client, TPS6591X_GPIO_BASE_ADDR + |
| offset, reg_val); |
| } |
| |
| static int tps6591x_gpio_output(struct gpio_chip *gc, unsigned offset, |
| int value) |
| { |
| struct tps6591x *tps6591x = container_of(gc, struct tps6591x, gpio); |
| uint8_t reg_val, val; |
| int ret; |
| |
| ret = __tps6591x_read(tps6591x->client, TPS6591X_GPIO_BASE_ADDR + |
| offset, ®_val); |
| if (ret) |
| return ret; |
| |
| reg_val &= ~0x1; |
| val = (value & 0x1) | 0x4; |
| reg_val = reg_val | val; |
| return __tps6591x_write(tps6591x->client, TPS6591X_GPIO_BASE_ADDR + |
| offset, reg_val); |
| } |
| |
| static int tps6591x_gpio_to_irq(struct gpio_chip *gc, unsigned off) |
| { |
| struct tps6591x *tps6591x; |
| tps6591x = container_of(gc, struct tps6591x, gpio); |
| |
| if ((off >= 0) && (off <= TPS6591X_INT_GPIO5 - TPS6591X_INT_GPIO0)) |
| return tps6591x->irq_base + TPS6591X_INT_GPIO0 + off; |
| |
| return -EIO; |
| } |
| |
| static void tps6591x_gpio_init(struct tps6591x *tps6591x, |
| struct tps6591x_platform_data *pdata) |
| { |
| int ret; |
| int gpio_base = pdata->gpio_base; |
| int i; |
| u8 gpio_reg; |
| struct tps6591x_gpio_init_data *ginit; |
| |
| if (gpio_base <= 0) |
| return; |
| |
| for (i = 0; i < pdata->num_gpioinit_data; ++i) { |
| ginit = &pdata->gpio_init_data[i]; |
| if (!ginit->init_apply) |
| continue; |
| gpio_reg = (ginit->sleep_en << TPS6591X_GPIO_SLEEP) | |
| (ginit->pulldn_en << TPS6591X_GPIO_PDEN) | |
| (ginit->output_mode_en << TPS6591X_GPIO_DIR); |
| |
| if (ginit->output_mode_en) |
| gpio_reg |= ginit->output_val; |
| |
| ret = __tps6591x_write(tps6591x->client, |
| TPS6591X_GPIO_BASE_ADDR + i, gpio_reg); |
| if (ret < 0) |
| dev_err(&tps6591x->client->dev, "Gpio %d init " |
| "configuration failed: %d\n", i, ret); |
| } |
| |
| tps6591x->gpio.owner = THIS_MODULE; |
| tps6591x->gpio.label = tps6591x->client->name; |
| tps6591x->gpio.dev = tps6591x->dev; |
| tps6591x->gpio.base = gpio_base; |
| tps6591x->gpio.ngpio = TPS6591X_GPIO_NR; |
| tps6591x->gpio.can_sleep = 1; |
| |
| tps6591x->gpio.direction_input = tps6591x_gpio_input; |
| tps6591x->gpio.direction_output = tps6591x_gpio_output; |
| tps6591x->gpio.set = tps6591x_gpio_set; |
| tps6591x->gpio.get = tps6591x_gpio_get; |
| tps6591x->gpio.to_irq = tps6591x_gpio_to_irq; |
| |
| ret = gpiochip_add(&tps6591x->gpio); |
| if (ret) |
| dev_warn(tps6591x->dev, "GPIO registration failed: %d\n", ret); |
| } |
| |
| static int __remove_subdev(struct device *dev, void *unused) |
| { |
| platform_device_unregister(to_platform_device(dev)); |
| return 0; |
| } |
| |
| static int tps6591x_remove_subdevs(struct tps6591x *tps6591x) |
| { |
| return device_for_each_child(tps6591x->dev, NULL, __remove_subdev); |
| } |
| |
| static void tps6591x_irq_lock(struct irq_data *data) |
| { |
| struct tps6591x *tps6591x = irq_data_get_irq_chip_data(data); |
| |
| mutex_lock(&tps6591x->irq_lock); |
| } |
| |
| static void tps6591x_irq_mask(struct irq_data *irq_data) |
| { |
| struct tps6591x *tps6591x = irq_data_get_irq_chip_data(irq_data); |
| unsigned int __irq = irq_data->irq - tps6591x->irq_base; |
| const struct tps6591x_irq_data *data = &tps6591x_irqs[__irq]; |
| |
| if (data->type == EVENT) |
| tps6591x->mask_reg[data->mask_reg] |= (1 << data->mask_pos); |
| else |
| tps6591x->mask_reg[data->mask_reg] |= (3 << data->mask_pos); |
| |
| tps6591x->irq_en &= ~(1 << __irq); |
| } |
| |
| static void tps6591x_irq_unmask(struct irq_data *irq_data) |
| { |
| struct tps6591x *tps6591x = irq_data_get_irq_chip_data(irq_data); |
| |
| unsigned int __irq = irq_data->irq - tps6591x->irq_base; |
| const struct tps6591x_irq_data *data = &tps6591x_irqs[__irq]; |
| |
| if (data->type == EVENT) { |
| tps6591x->mask_reg[data->mask_reg] &= ~(1 << data->mask_pos); |
| tps6591x->irq_en |= (1 << __irq); |
| } |
| } |
| |
| static void tps6591x_irq_sync_unlock(struct irq_data *data) |
| { |
| struct tps6591x *tps6591x = irq_data_get_irq_chip_data(data); |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(tps6591x->mask_reg); i++) { |
| if (tps6591x->mask_reg[i] != tps6591x->mask_cache[i]) { |
| if (!WARN_ON(tps6591x_write(tps6591x->dev, |
| TPS6591X_INT_MSK + 2*i, |
| tps6591x->mask_reg[i]))) |
| tps6591x->mask_cache[i] = tps6591x->mask_reg[i]; |
| } |
| } |
| |
| mutex_unlock(&tps6591x->irq_lock); |
| } |
| |
| static int tps6591x_irq_set_type(struct irq_data *irq_data, unsigned int type) |
| { |
| struct tps6591x *tps6591x = irq_data_get_irq_chip_data(irq_data); |
| |
| unsigned int __irq = irq_data->irq - tps6591x->irq_base; |
| const struct tps6591x_irq_data *data = &tps6591x_irqs[__irq]; |
| |
| if (data->type == GPIO) { |
| if (type & IRQ_TYPE_EDGE_FALLING) |
| tps6591x->mask_reg[data->mask_reg] |
| &= ~(1 << data->mask_pos); |
| else |
| tps6591x->mask_reg[data->mask_reg] |
| |= (1 << data->mask_pos); |
| |
| if (type & IRQ_TYPE_EDGE_RISING) |
| tps6591x->mask_reg[data->mask_reg] |
| &= ~(2 << data->mask_pos); |
| else |
| tps6591x->mask_reg[data->mask_reg] |
| |= (2 << data->mask_pos); |
| |
| tps6591x->irq_en |= (1 << __irq); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM_SLEEP |
| static int tps6591x_irq_set_wake(struct irq_data *irq_data, unsigned int on) |
| { |
| struct tps6591x *tps6591x = irq_data_get_irq_chip_data(irq_data); |
| return irq_set_irq_wake(tps6591x->irq_main, on); |
| } |
| #else |
| #define tps6591x_irq_set_wake NULL |
| #endif |
| |
| |
| static irqreturn_t tps6591x_irq(int irq, void *data) |
| { |
| struct tps6591x *tps6591x = data; |
| int ret = 0; |
| u8 tmp[3]; |
| u8 int_ack; |
| u32 acks, mask = 0; |
| int i; |
| |
| for (i = 0; i < 3; i++) { |
| ret = tps6591x_read(tps6591x->dev, TPS6591X_INT_STS + 2*i, |
| &tmp[i]); |
| if (ret < 0) { |
| dev_err(tps6591x->dev, |
| "failed to read interrupt status\n"); |
| return IRQ_NONE; |
| } |
| if (tmp[i]) { |
| /* Ack only those interrupts which are enabled */ |
| int_ack = tmp[i] & (~(tps6591x->mask_cache[i])); |
| ret = tps6591x_write(tps6591x->dev, |
| TPS6591X_INT_STS + 2*i, int_ack); |
| if (ret < 0) { |
| dev_err(tps6591x->dev, |
| "failed to write interrupt status\n"); |
| return IRQ_NONE; |
| } |
| } |
| } |
| |
| acks = (tmp[2] << 16) | (tmp[1] << 8) | tmp[0]; |
| |
| for (i = 0; i < ARRAY_SIZE(tps6591x_irqs); i++) { |
| if (tps6591x_irqs[i].type == GPIO) |
| mask = (3 << (tps6591x_irqs[i].mask_pos |
| + tps6591x_irqs[i].mask_reg*8)); |
| else if (tps6591x_irqs[i].type == EVENT) |
| mask = (1 << (tps6591x_irqs[i].mask_pos |
| + tps6591x_irqs[i].mask_reg*8)); |
| |
| if ((acks & mask) && (tps6591x->irq_en & (1 << i))) |
| handle_nested_irq(tps6591x->irq_base + i); |
| } |
| return IRQ_HANDLED; |
| } |
| |
| static int __devinit tps6591x_irq_init(struct tps6591x *tps6591x, int irq, |
| int irq_base) |
| { |
| int i, ret; |
| |
| if (!irq_base) { |
| dev_warn(tps6591x->dev, "No interrupt support on IRQ base\n"); |
| return -EINVAL; |
| } |
| |
| mutex_init(&tps6591x->irq_lock); |
| |
| tps6591x->mask_reg[0] = 0xFF; |
| tps6591x->mask_reg[1] = 0xFF; |
| tps6591x->mask_reg[2] = 0xFF; |
| for (i = 0; i < 3; i++) { |
| tps6591x->mask_cache[i] = tps6591x->mask_reg[i]; |
| tps6591x_write(tps6591x->dev, TPS6591X_INT_MSK + 2*i, |
| tps6591x->mask_cache[i]); |
| } |
| |
| for (i = 0; i < 3; i++) |
| tps6591x_write(tps6591x->dev, TPS6591X_INT_STS + 2*i, 0xff); |
| |
| tps6591x->irq_base = irq_base; |
| tps6591x->irq_main = irq; |
| |
| tps6591x->irq_chip.name = "tps6591x"; |
| tps6591x->irq_chip.irq_mask = tps6591x_irq_mask; |
| tps6591x->irq_chip.irq_unmask = tps6591x_irq_unmask; |
| tps6591x->irq_chip.irq_bus_lock = tps6591x_irq_lock; |
| tps6591x->irq_chip.irq_bus_sync_unlock = tps6591x_irq_sync_unlock; |
| tps6591x->irq_chip.irq_set_type = tps6591x_irq_set_type; |
| tps6591x->irq_chip.irq_set_wake = tps6591x_irq_set_wake; |
| |
| for (i = 0; i < ARRAY_SIZE(tps6591x_irqs); i++) { |
| int __irq = i + tps6591x->irq_base; |
| irq_set_chip_data(__irq, tps6591x); |
| irq_set_chip_and_handler(__irq, &tps6591x->irq_chip, |
| handle_simple_irq); |
| irq_set_nested_thread(__irq, 1); |
| #ifdef CONFIG_ARM |
| set_irq_flags(__irq, IRQF_VALID); |
| #endif |
| } |
| |
| ret = request_threaded_irq(irq, NULL, tps6591x_irq, IRQF_ONESHOT, |
| "tps6591x", tps6591x); |
| if (!ret) { |
| device_init_wakeup(tps6591x->dev, 1); |
| enable_irq_wake(irq); |
| } |
| |
| return ret; |
| } |
| |
| static int __devinit tps6591x_add_subdevs(struct tps6591x *tps6591x, |
| struct tps6591x_platform_data *pdata) |
| { |
| struct tps6591x_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 = tps6591x->dev; |
| pdev->dev.platform_data = subdev->platform_data; |
| |
| ret = platform_device_add(pdev); |
| if (ret) |
| goto failed; |
| } |
| return 0; |
| |
| failed: |
| tps6591x_remove_subdevs(tps6591x); |
| 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 = __tps6591x_read(client, i, ®_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 tps6591x *tps = s->private; |
| struct i2c_client *client = tps->client; |
| |
| seq_printf(s, "TPS6591x Registers\n"); |
| seq_printf(s, "------------------\n"); |
| |
| print_regs("Timing Regs", s, client, 0x0, 0x6); |
| print_regs("Alarm Regs", s, client, 0x8, 0xD); |
| print_regs("RTC Regs", s, client, 0x10, 0x16); |
| print_regs("BCK Regs", s, client, 0x17, 0x1B); |
| print_regs("PUADEN Regs", s, client, 0x18, 0x18); |
| print_regs("REF Regs", s, client, 0x1D, 0x1D); |
| print_regs("VDD Regs", s, client, 0x1E, 0x29); |
| print_regs("LDO Regs", s, client, 0x30, 0x37); |
| print_regs("THERM Regs", s, client, 0x38, 0x38); |
| print_regs("BBCH Regs", s, client, 0x39, 0x39); |
| print_regs("DCDCCNTRL Regs", s, client, 0x3E, 0x3E); |
| print_regs("DEV_CNTRL Regs", s, client, 0x3F, 0x40); |
| print_regs("SLEEP Regs", s, client, 0x41, 0x44); |
| print_regs("EN1 Regs", s, client, 0x45, 0x48); |
| print_regs("INT Regs", s, client, 0x50, 0x55); |
| print_regs("GPIO Regs", s, client, 0x60, 0x68); |
| print_regs("WATCHDOG Regs", s, client, 0x69, 0x69); |
| print_regs("VMBCH Regs", s, client, 0x6A, 0x6B); |
| print_regs("LED_CTRL Regs", s, client, 0x6c, 0x6D); |
| print_regs("PWM_CTRL Regs", s, client, 0x6E, 0x6F); |
| print_regs("SPARE Regs", s, client, 0x70, 0x70); |
| print_regs("VERNUM Regs", s, client, 0x80, 0x80); |
| 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 tps6591x_debuginit(struct tps6591x *tps) |
| { |
| (void)debugfs_create_file("tps6591x", S_IRUGO, NULL, |
| tps, &debug_fops); |
| } |
| #else |
| static void __init tps6591x_debuginit(struct tps6591x *tpsi) |
| { |
| return; |
| } |
| #endif |
| |
| static int __init tps6591x_sleepinit(struct tps6591x *tpsi, |
| struct tps6591x_platform_data *pdata) |
| { |
| struct device *dev = NULL; |
| int ret = 0; |
| |
| dev = tpsi->dev; |
| |
| if (!pdata->dev_slp_en) |
| goto no_err_return; |
| |
| /* pmu dev_slp_en is set. Make sure slp_keepon is available before |
| * allowing SLEEP device state */ |
| if (!pdata->slp_keepon) { |
| dev_err(dev, "slp_keepon_data required for slp_en\n"); |
| goto err_sleep_init; |
| } |
| |
| /* enabling SLEEP device state */ |
| ret = tps6591x_set_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_DEV_SLP); |
| if (ret < 0) { |
| dev_err(dev, "set dev_slp failed: %d\n", ret); |
| goto err_sleep_init; |
| } |
| |
| if (pdata->slp_keepon->therm_keepon) { |
| ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON, |
| SLEEP_KEEP_ON_THERM); |
| if (ret < 0) { |
| dev_err(dev, "set therm_keepon failed: %d\n", ret); |
| goto disable_dev_slp; |
| } |
| } |
| |
| if (pdata->slp_keepon->clkout32k_keepon) { |
| ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON, |
| SLEEP_KEEP_ON_CLKOUT32K); |
| if (ret < 0) { |
| dev_err(dev, "set clkout32k_keepon failed: %d\n", ret); |
| goto disable_dev_slp; |
| } |
| } |
| |
| |
| if (pdata->slp_keepon->vrtc_keepon) { |
| ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON, |
| SLEEP_KEEP_ON_VRTC); |
| if (ret < 0) { |
| dev_err(dev, "set vrtc_keepon failed: %d\n", ret); |
| goto disable_dev_slp; |
| } |
| } |
| |
| if (pdata->slp_keepon->i2chs_keepon) { |
| ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON, |
| SLEEP_KEEP_ON_I2CHS); |
| if (ret < 0) { |
| dev_err(dev, "set i2chs_keepon failed: %d\n", ret); |
| goto disable_dev_slp; |
| } |
| } |
| |
| no_err_return: |
| return 0; |
| |
| disable_dev_slp: |
| tps6591x_clr_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_DEV_SLP); |
| |
| err_sleep_init: |
| return ret; |
| } |
| //=================stree test================= |
| struct tps6591x *temp_tps6591x=NULL; |
| |
| static ssize_t show_tps6591x_i2c_status(struct device *dev, struct device_attribute *devattr, char *buf) |
| { |
| if(temp_tps6591x) |
| return sprintf(buf, "%d\n", temp_tps6591x->i2c_status); |
| else |
| return sprintf(buf, "%d\n", 0); |
| } |
| static DEVICE_ATTR(tps6591x_i2c_status, S_IWUSR | S_IRUGO,show_tps6591x_i2c_status,NULL); |
| |
| static struct attribute *tps6591x_i2c_attributes[] = { |
| |
| &dev_attr_tps6591x_i2c_status.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group tps6591x_i2c_group = { |
| .attrs = tps6591x_i2c_attributes, |
| }; |
| |
| |
| #define TPS6591X_IOC_MAGIC 0xFB |
| #define TPS6591X_IOC_MAXNR 5 |
| #define TPS6591X_POLLING_DATA _IOR(TPS6591X_IOC_MAGIC, 1,int) |
| |
| #define TEST_END (0) |
| #define START_NORMAL (1) |
| #define START_HEAVY (2) |
| #define IOCTL_ERROR (-1) |
| struct workqueue_struct *tps6591x_strees_work_queue=NULL; |
| |
| void tps6591x_read_stress_test(struct work_struct *work) |
| { |
| uint8_t reg_val; |
| int ret = 0; |
| |
| mutex_lock(&temp_tps6591x->lock); |
| |
| ret = __tps6591x_read(temp_tps6591x->client, TPS6591X_VERNUM, ®_val); |
| if (ret < 0) { |
| printk("failed ps6591x_read_stress_test \n"); |
| } |
| |
| mutex_unlock(&temp_tps6591x->lock); |
| queue_delayed_work(tps6591x_strees_work_queue, &temp_tps6591x->stress_test, 2*HZ); |
| return ; |
| } |
| long tps6591x_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
| { |
| if (_IOC_TYPE(cmd) ==TPS6591X_IOC_MAGIC) |
| printk(" tps6591x_ioctl vaild magic \n"); |
| else { |
| printk(" tps65991x_ioctl invaild magic \n"); |
| return -ENOTTY; |
| } |
| |
| switch(cmd) |
| { |
| case TPS6591X_POLLING_DATA : |
| if ((arg==START_NORMAL)||(arg==START_HEAVY)){ |
| printk(" tps6591x stress test start (%s)\n",(arg==START_NORMAL)?"normal":"heavy"); |
| queue_delayed_work(tps6591x_strees_work_queue, &temp_tps6591x->stress_test, 2*HZ); |
| } else { |
| printk(" t tps6591x tress test end\n"); |
| cancel_delayed_work_sync(&temp_tps6591x->stress_test); |
| } |
| break; |
| default: /* redundant, as cmd was checked against MAXNR */ |
| printk(" TPS6591X: unknow i2c stress test command cmd=%x arg=%lu\n",cmd,arg); |
| return -ENOTTY; |
| } |
| return 0; |
| } |
| int tps6591x_open(struct inode *inode, struct file *filp) |
| { |
| return 0; |
| } |
| struct file_operations tps6591x_fops = { |
| .owner = THIS_MODULE, |
| .unlocked_ioctl = tps6591x_ioctl, |
| .open = tps6591x_open, |
| }; |
| //=================stree test end================= |
| |
| static int __devinit tps6591x_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct tps6591x_platform_data *pdata = client->dev.platform_data; |
| struct tps6591x *tps6591x; |
| int ret; |
| //=================stree test =================== |
| int rc; |
| //=================stree test end================= |
| if (!pdata) { |
| dev_err(&client->dev, "tps6591x requires platform data\n"); |
| return -ENOTSUPP; |
| } |
| |
| ret = i2c_smbus_read_byte_data(client, TPS6591X_VERNUM); |
| if (ret < 0) { |
| dev_err(&client->dev, "Silicon version number read" |
| " failed: %d\n", ret); |
| return -EIO; |
| } |
| |
| dev_info(&client->dev, "VERNUM is %02x\n", ret); |
| |
| tps6591x = kzalloc(sizeof(struct tps6591x), GFP_KERNEL); |
| if (tps6591x == NULL) |
| return -ENOMEM; |
| |
| tps6591x->client = client; |
| tps6591x->dev = &client->dev; |
| i2c_set_clientdata(client, tps6591x); |
| |
| mutex_init(&tps6591x->lock); |
| |
| if (client->irq) { |
| ret = tps6591x_irq_init(tps6591x, client->irq, |
| pdata->irq_base); |
| if (ret) { |
| dev_err(&client->dev, "IRQ init failed: %d\n", ret); |
| goto err_irq_init; |
| } |
| } |
| |
| ret = tps6591x_add_subdevs(tps6591x, pdata); |
| if (ret) { |
| dev_err(&client->dev, "add devices failed: %d\n", ret); |
| goto err_add_devs; |
| } |
| |
| tps6591x_gpio_init(tps6591x, pdata); |
| |
| tps6591x_debuginit(tps6591x); |
| |
| tps6591x_sleepinit(tps6591x, pdata); |
| |
| if (pdata->use_power_off && !pm_power_off) |
| pm_power_off = tps6591x_power_off; |
| |
| tps6591x_i2c_client = client; |
| //=================stree test================= |
| temp_tps6591x=tps6591x; |
| temp_tps6591x->i2c_status=1; |
| if (sysfs_create_group(&client->dev.kobj, &tps6591x_i2c_group)) { |
| dev_err(&client->dev, "tps6591x_i2c_probe:Not able to create the sysfs\n"); |
| } |
| INIT_DELAYED_WORK(&temp_tps6591x->stress_test, tps6591x_read_stress_test) ; |
| tps6591x_strees_work_queue = create_singlethread_workqueue("tps6591x_strees_test_workqueue"); |
| |
| temp_tps6591x->tps6591x_misc.minor = MISC_DYNAMIC_MINOR; |
| temp_tps6591x->tps6591x_misc.name = "tps6591x"; |
| temp_tps6591x->tps6591x_misc.fops = &tps6591x_fops; |
| rc=misc_register(&temp_tps6591x->tps6591x_misc); |
| printk(KERN_INFO "tps6591x register misc device for I2C stress test rc=%x\n", rc); |
| //=================stree test end================= |
| return 0; |
| |
| err_add_devs: |
| if (client->irq) |
| free_irq(client->irq, tps6591x); |
| err_irq_init: |
| kfree(tps6591x); |
| return ret; |
| } |
| |
| int tps6591x_set_reg_enable_record(void) |
| { |
| int ret = 0; |
| |
| tps6591x_write(temp_tps6591x->dev, 0x3e, 0x11); |
| printk("%s : set 0x3e to 0x11 ret = %d\n", __func__, ret); |
| return ret; |
| } |
| |
| int tps6591x_set_reg_disable_record(void) |
| { |
| int ret = 0; |
| |
| tps6591x_write(temp_tps6591x->dev, 0x3e, 0x39); |
| printk("%s : set 0x3e to 0x39 ret = %d\n", __func__, ret); |
| return ret; |
| } |
| |
| static int __devexit tps6591x_i2c_remove(struct i2c_client *client) |
| { |
| struct tps6591x *tps6591x = i2c_get_clientdata(client); |
| |
| if (client->irq) |
| free_irq(client->irq, tps6591x); |
| |
| if (gpiochip_remove(&tps6591x->gpio) < 0) |
| dev_err(&client->dev, "Error in removing the gpio driver\n"); |
| |
| kfree(tps6591x); |
| return 0; |
| } |
| #ifdef CONFIG_PM |
| |
| static int tps6591x_i2c_suspend(struct i2c_client *client, pm_message_t state) |
| { |
| if (client->irq) |
| disable_irq(client->irq); |
| return 0; |
| } |
| |
| static int tps6591x_i2c_resume(struct i2c_client *client) |
| { |
| if (client->irq) |
| enable_irq(client->irq); |
| return 0; |
| } |
| #endif |
| |
| static int tps6591x_i2c_shutdown(struct i2c_client *client) |
| { |
| int ret; |
| |
| ret = tps6591x_set_bits(&client->dev, TPS6591X_INT_MSK3, GPIO4_F_IT_MSK); |
| if (ret < 0) |
| pr_err("%s(): Setting GPIO4_F_IT_MSK fail.\n", __func__); |
| ret = tps6591x_set_bits(&client->dev, TPS6591X_INT_MSK, RTC_ALARM_IT_MSK); |
| if (ret < 0) |
| pr_err("%s(): Setting RTC_ALARM_IT_MSK fail.\n", __func__); |
| return ret; |
| } |
| |
| |
| static const struct i2c_device_id tps6591x_id_table[] = { |
| { "tps6591x", 0 }, |
| { }, |
| }; |
| MODULE_DEVICE_TABLE(i2c, tps6591x_id_table); |
| |
| static struct i2c_driver tps6591x_driver = { |
| .driver = { |
| .name = "tps6591x", |
| .owner = THIS_MODULE, |
| }, |
| .probe = tps6591x_i2c_probe, |
| .remove = __devexit_p(tps6591x_i2c_remove), |
| #ifdef CONFIG_PM |
| .suspend = tps6591x_i2c_suspend, |
| .resume = tps6591x_i2c_resume, |
| #endif |
| .shutdown = tps6591x_i2c_shutdown, |
| .id_table = tps6591x_id_table, |
| }; |
| |
| static int __init tps6591x_init(void) |
| { |
| return i2c_add_driver(&tps6591x_driver); |
| } |
| subsys_initcall(tps6591x_init); |
| |
| static void __exit tps6591x_exit(void) |
| { |
| i2c_del_driver(&tps6591x_driver); |
| } |
| module_exit(tps6591x_exit); |
| |
| MODULE_DESCRIPTION("TPS6591X core driver"); |
| MODULE_LICENSE("GPL"); |