2012-05-11 06:50:59

by Jonghwa Lee

[permalink] [raw]
Subject: [PATCH v2 0/3] mfd: MAX77686: Add initial support for MAXIM 77686 mfd chip

This patchset adds suppport for MAX77686 which is a multifunction device including
regulator and rtc. It also contains drivers supporting rtc and regulator.
All drivers are based on MAX8997 drivers and use regmap to access to the inner registers.
To manage IRQs occured by max77686, it supports IRQ domain.

First patch is for mfd core driver ,and it is renewed.

v2
- Change I/O interface register to using regmap.
- Support IRQ domain, and remove irq_base from all referenced.
- Add module_param() macro to debug_mask parameter. It can be set various
masking value for specific purpose while debugging.
- Remove IRQ_TEST block and some unnecessary points.

second ,third patchs are for regulator and rtc. These are newly updated.

It is tested on TRATS boards.

Jonghwa Lee (3):
mfd: MAX77686: Add Maxim 77686 mfd driver
regulator: MAX77686: Add Maxim 77686 regulator driver
rtc: MAX77686: Add Maxim 77686 rtc driver

drivers/mfd/Kconfig | 2 +-
drivers/mfd/Makefile | 2 +-
drivers/mfd/max77686-irq.c | 109 ++---
drivers/mfd/max77686.c | 193 +--------
drivers/regulator/Kconfig | 8 +
drivers/regulator/Makefile | 1 +
drivers/regulator/max77686.c | 809 ++++++++++++++++++++++++++++++++++
drivers/rtc/Kconfig | 10 +
drivers/rtc/Makefile | 5 +-
drivers/rtc/rtc-max77686.c | 667 ++++++++++++++++++++++++++++
include/linux/mfd/max77686-private.h | 54 +--
include/linux/mfd/max77686.h | 23 +-
12 files changed, 1577 insertions(+), 306 deletions(-)
create mode 100644 drivers/regulator/max77686.c
create mode 100644 drivers/rtc/rtc-max77686.c

--
1.7.4.1


2012-05-11 06:51:22

by Jonghwa Lee

[permalink] [raw]
Subject: [PATCH v2 3/3] rtc: MAX77686: Add Maxim 77686 rtc driver

Add driver for support max77686 rtc.
MAX77686 rtc support smpl and wtsr mode. It has two alarm register,
and can be used for waking system up. It sharing i2c bus, regmap supplies
the way can get to the its register.

Signed-off-by: Chiwoong Byun <[email protected]>
Signed-off-by: Jonghwa Lee <[email protected]>
Signed-off-by: Myunjoo Ham <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
drivers/rtc/Kconfig | 10 +
drivers/rtc/Makefile | 5 +-
drivers/rtc/rtc-max77686.c | 667 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 680 insertions(+), 2 deletions(-)
create mode 100644 drivers/rtc/rtc-max77686.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 9a7072e..114e74c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -223,6 +223,16 @@ config RTC_DRV_MAX8997
This driver can also be built as a module. If so, the module
will be called rtc-max8997.

+config RTC_DRV_MAX77686
+ tristate "Maxim MAX77686"
+ depends on MFD_MAX77686
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX77686 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max77686.
+
config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 8810867..801ea07 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -63,10 +63,11 @@ obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
+obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
-obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
-obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
+obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
new file mode 100644
index 0000000..c5bc96a
--- /dev/null
+++ b/drivers/rtc/rtc-max77686.c
@@ -0,0 +1,667 @@
+/*
+ * RTC driver for Maxim MAX77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ * based on rtc-max8997.c
+ *
+ * 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT 0
+#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT 1
+#define MODEL24_MASK (1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT 0
+#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
+#define RTC_RBUDR_SHIFT 4
+#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
+/* WTSR and SMPL Register */
+#define WTSRT_SHIFT 0
+#define SMPLT_SHIFT 2
+#define WTSR_EN_SHIFT 6
+#define SMPL_EN_SHIFT 7
+#define WTSRT_MASK (3 << WTSRT_SHIFT)
+#define SMPLT_MASK (3 << SMPLT_SHIFT)
+#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT)
+#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT 6
+#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT 7
+#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
+
+#define MAX77686_RTC_UPDATE_DELAY 16
+#undef MAX77686_RTC_WTSR_SMPL
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_DATE,
+ RTC_NR_TIME
+};
+
+struct max77686_rtc_info {
+ struct device *dev;
+ struct max77686_dev *max77686;
+ struct i2c_client *rtc;
+ struct rtc_device *rtc_dev;
+ struct mutex lock;
+ int irq;
+ int virq;
+ int rtc_24hr_mode;
+};
+
+enum MAX77686_RTC_OP {
+ MAX77686_RTC_WRITE,
+ MAX77686_RTC_READ,
+};
+
+static inline int max77686_rtc_calculate_wday(u8 shifted)
+{
+ int counter = -1;
+ while (shifted) {
+ shifted >>= 1;
+ counter++;
+ }
+ return counter;
+}
+
+static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
+ int rtc_24hr_mode)
+{
+ tm->tm_sec = data[RTC_SEC] & 0x7f;
+ tm->tm_min = data[RTC_MIN] & 0x7f;
+ if (rtc_24hr_mode)
+ tm->tm_hour = data[RTC_HOUR] & 0x1f;
+ else {
+ tm->tm_hour = data[RTC_HOUR] & 0x0f;
+ if (data[RTC_HOUR] & HOUR_PM_MASK)
+ tm->tm_hour += 12;
+ }
+
+ tm->tm_wday = max77686_rtc_calculate_wday(data[RTC_WEEKDAY] & 0x7f);
+ tm->tm_mday = data[RTC_DATE] & 0x1f;
+ tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+ tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+}
+
+static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = tm->tm_sec;
+ data[RTC_MIN] = tm->tm_min;
+ data[RTC_HOUR] = tm->tm_hour;
+ data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+ data[RTC_DATE] = tm->tm_mday;
+ data[RTC_MONTH] = tm->tm_mon + 1;
+ data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0 ;
+
+ if (tm->tm_year < 100) {
+ pr_warn("%s: MAX77686 RTC cannot handle the year %d."
+ "Assume it's 2000.\n", __func__, 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static inline int max77686_rtc_update(struct max77686_rtc_info *info,
+ enum MAX77686_RTC_OP op)
+{
+ int ret;
+ u8 data;
+
+ switch (op) {
+ case MAX77686_RTC_WRITE:
+ data = 1 << RTC_UDR_SHIFT;
+ break;
+ case MAX77686_RTC_READ:
+ data = 1 << RTC_RBUDR_SHIFT;
+ break;
+ }
+
+ ret = max77686_update_reg(info->max77686->rtc_regmap,
+ MAX77686_RTC_UPDATE0, data, data);
+ if (ret < 0)
+ dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
+ __func__, ret, data);
+ else {
+ /* Minimum 16ms delay required before RTC update. */
+ msleep(MAX77686_RTC_UPDATE_DELAY);
+ }
+
+ return ret;
+}
+
+static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = max77686_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_RTC_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
+
+ ret = rtc_valid_tm(tm);
+
+ printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77686_rtc_tm_to_data(tm, data);
+ if (ret < 0)
+ return ret;
+
+ printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+
+ mutex_lock(&info->lock);
+
+ ret = max77686_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_RTC_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+ ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ u8 val;
+ int i, ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = max77686_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+
+ printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday,
+ alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec, alrm->time.tm_wday);
+
+ alrm->enabled = 0;
+ for (i = 0; i < RTC_NR_TIME; i++) {
+ if (data[i] & ALARM_ENABLE_MASK) {
+ alrm->enabled = 1;
+ break;
+ }
+ }
+
+ alrm->pending = 0;
+ ret = max77686_read_reg(info->max77686->regmap, MAX77686_REG_STATUS1, &val);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ if (val & (1 << 4)) /* RTCA1 */
+ alrm->pending = 1;
+
+out:
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
+{
+ u8 data[RTC_NR_TIME];
+ int ret, i;
+ struct rtc_time tm;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = max77686_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
+ printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
+
+ for (i = 0; i < RTC_NR_TIME; i++)
+ data[i] &= ~ALARM_ENABLE_MASK;
+
+ ret = max77686_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
+{
+ u8 data[RTC_NR_TIME];
+ int ret;
+ struct rtc_time tm;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = max77686_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
+ printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
+
+ data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
+ if (data[RTC_MONTH] & 0xf)
+ data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
+ if (data[RTC_YEAR] & 0x7f)
+ data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
+ if (data[RTC_DATE] & 0x1f)
+ data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
+
+ ret = max77686_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77686_rtc_tm_to_data(&alrm->time, data);
+ if (ret < 0)
+ return ret;
+
+ printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday,
+ alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec, alrm->time.tm_wday);
+
+ mutex_lock(&info->lock);
+
+ ret = max77686_rtc_stop_alarm(info);
+ if (ret < 0)
+ goto out;
+
+ ret = max77686_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, RTC_NR_TIME, data);
+
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+ if (ret < 0)
+ goto out;
+
+ if (alrm->enabled)
+ ret = max77686_rtc_start_alarm(info);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77686_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&info->lock);
+ if (enabled)
+ ret = max77686_rtc_start_alarm(info);
+ else
+ ret = max77686_rtc_stop_alarm(info);
+ mutex_unlock(&info->lock);
+
+ return ret;
+}
+
+static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data)
+{
+ struct max77686_rtc_info *info = data;
+
+ dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77686_rtc_ops = {
+ .read_time = max77686_rtc_read_time,
+ .set_time = max77686_rtc_set_time,
+ .read_alarm = max77686_rtc_read_alarm,
+ .set_alarm = max77686_rtc_set_alarm,
+ .alarm_irq_enable = max77686_rtc_alarm_irq_enable,
+};
+
+#ifdef MAX77686_RTC_WTSR_SMPL
+static void max77686_rtc_enable_wtsr(struct max77686_rtc_info *info, bool enable)
+{
+ int ret;
+ u8 val, mask;
+
+ if (enable)
+ val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT);
+ else
+ val = 0;
+
+ mask = WTSR_EN_MASK | WTSRT_MASK;
+
+ dev_info(info->dev, "%s: %s WTSR\n", __func__,
+ enable ? "enable" : "disable");
+
+ ret = max77686_update_reg(info->max77686->rtc_regmap,
+ MAX77686_WTSR_SMPL_CNTL, val, mask);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
+ __func__, ret);
+ return;
+ }
+
+ max77686_rtc_update(info, MAX77686_RTC_WRITE);
+}
+
+static void max77686_rtc_enable_smpl(struct max77686_rtc_info *info, bool enable)
+{
+ int ret;
+ u8 val, mask;
+
+ if (enable)
+ val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT);
+ else
+ val = 0;
+
+ mask = SMPL_EN_MASK | SMPLT_MASK;
+
+ dev_info(info->dev, "%s: %s SMPL\n", __func__,
+ enable ? "enable" : "disable");
+
+ ret = max77686_update_reg(info->max77686->rtc_regmap,
+ MAX77686_WTSR_SMPL_CNTL, val, mask);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
+ __func__, ret);
+ return;
+ }
+
+ max77686_rtc_update(info, MAX77686_RTC_WRITE);
+
+ val = 0;
+ max77686_read_reg(info->max77686->rtc_regmap,
+ MAX77686_WTSR_SMPL_CNTL, &val);
+ pr_info("%s: WTSR_SMPL(0x%02x)\n", __func__, val);
+}
+#endif /* MAX77686_RTC_WTSR_SMPL */
+
+static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
+{
+ u8 data[2];
+ int ret;
+
+ /* Set RTC control register : Binary mode, 24hour mdoe */
+ data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+ data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+ info->rtc_24hr_mode = 1;
+
+ ret = max77686_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_RTC_CONTROLM, 2, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+ return ret;
+}
+
+static struct regmap_config max77686_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int __devinit max77686_rtc_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent);
+ struct max77686_rtc_info *info;
+ int ret, virq;
+
+ printk(KERN_INFO "%s\n", __func__);
+
+ info = kzalloc(sizeof(struct max77686_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ mutex_init(&info->lock);
+ info->dev = &pdev->dev;
+ info->max77686 = max77686;
+ info->rtc = max77686->rtc;
+ info->max77686->rtc_regmap = regmap_init_i2c(info->max77686->rtc,
+ &max77686_rtc_regmap_config);
+ if (IS_ERR(info->max77686->rtc_regmap)) {
+ ret = PTR_ERR(info->max77686->rtc_regmap);
+ dev_err(info->max77686->dev, "Failed to allocate register map: %d\n",
+ ret);
+ kfree(info);
+ return ret;
+ }
+ platform_set_drvdata(pdev, info);
+
+ ret = max77686_rtc_init_reg(info);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+ goto err_rtc;
+ }
+
+#ifdef MAX77686_RTC_WTSR_SMPL
+ max77686_rtc_enable_wtsr(info, true);
+ max77686_rtc_enable_smpl(info, true);
+#endif
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = rtc_device_register("max77686-rtc", &pdev->dev,
+ &max77686_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ printk(KERN_INFO "%s: fail\n", __func__);
+
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ if (ret == 0)
+ ret = -EINVAL;
+ goto err_rtc;
+ }
+ virq = irq_create_mapping(max77686->irq_domain, MAX77686_RTCIRQ_RTCA1);
+ if (!virq)
+ goto err_rtc;
+ info->virq = virq;
+
+ ret = request_threaded_irq(virq, NULL, max77686_rtc_alarm_irq, 0,
+ "rtc-alarm0", info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->irq, ret);
+ goto err_rtc;
+ }
+
+ goto out;
+err_rtc:
+ kfree(info);
+ return ret;
+out:
+ return ret;
+}
+
+static int __devexit max77686_rtc_remove(struct platform_device *pdev)
+{
+ struct max77686_rtc_info *info = platform_get_drvdata(pdev);
+
+ if (info) {
+ free_irq(info->virq, info);
+ rtc_device_unregister(info->rtc_dev);
+ kfree(info);
+ }
+
+ return 0;
+}
+
+static void max77686_rtc_shutdown(struct platform_device *pdev)
+{
+#ifdef MAX77686_RTC_WTSR_SMPL
+ struct max77686_rtc_info *info = platform_get_drvdata(pdev);
+ int i;
+ u8 val = 0;
+
+ for (i = 0; i < 3; i++) {
+ max77686_rtc_enable_wtsr(info, false);
+ max77686_read_reg(info->max77686->rtc_regmap,
+ MAX77686_WTSR_SMPL_CNTL, &val);
+ pr_info("%s: WTSR_SMPL reg(0x%02x)\n", __func__, val);
+ if (val & WTSR_EN_MASK)
+ pr_emerg("%s: fail to disable WTSR\n", __func__);
+ else {
+ pr_info("%s: success to disable WTSR\n", __func__);
+ break;
+ }
+ }
+
+ /* Disable SMPL when power off */
+ max77686_rtc_enable_smpl(info, false);
+#endif /* MAX77686_RTC_WTSR_SMPL */
+}
+
+static const struct platform_device_id rtc_id[] = {
+ { "max77686-rtc", 0 },
+ {},
+};
+
+static struct platform_driver max77686_rtc_driver = {
+ .driver = {
+ .name = "max77686-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = max77686_rtc_probe,
+ .remove = __devexit_p(max77686_rtc_remove),
+ .shutdown = max77686_rtc_shutdown,
+ .id_table = rtc_id,
+};
+
+static int __init max77686_rtc_init(void)
+{
+ return platform_driver_register(&max77686_rtc_driver);
+}
+module_init(max77686_rtc_init);
+
+static void __exit max77686_rtc_exit(void)
+{
+ platform_driver_unregister(&max77686_rtc_driver);
+}
+module_exit(max77686_rtc_exit);
+
+MODULE_DESCRIPTION("Maxim MAX77686 RTC driver");
+MODULE_AUTHOR("<[email protected]>");
+MODULE_LICENSE("GPL");
--
1.7.4.1

2012-05-11 06:51:20

by Jonghwa Lee

[permalink] [raw]
Subject: [PATCH v2 2/3] regulator: MAX77686: Add Maxim 77686 regulator driver

Add driver for support max77686 regulator.
MAX77686 provides LDOs[1~26] and BUCKs[1~9]. It support to set or get the
volatege of regulator on max77686 chip with using regmap.

Signed-off-by: Chiwoong Byun <[email protected]>
Signed-off-by: Jonghwa Lee <[email protected]>
Signed-off-by: Myungjoo Ham <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
drivers/regulator/Kconfig | 8 +
drivers/regulator/Makefile | 1 +
drivers/regulator/max77686.c | 809 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 818 insertions(+), 0 deletions(-)
create mode 100644 drivers/regulator/max77686.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 36db5a4..8e2ebad 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -195,6 +195,14 @@ config REGULATOR_MAX8998
via I2C bus. The provided regulator is suitable for S3C6410
and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.

+config REGULATOR_MAX77686
+ tristate "Maxim 77686 regulator"
+ depends on MFD_MAX77686
+ help
+ This driver controls a Maxim 77686 regulator
+ via I2C bus. The provided regulator is suitable for
+ Exynos-4 chips to control VARM and VINT voltages.
+
config REGULATOR_PCAP
tristate "Motorola PCAP2 regulator driver"
depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 94b5274..008931b 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
+obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
new file mode 100644
index 0000000..9ab7a4c
--- /dev/null
+++ b/drivers/regulator/max77686.c
@@ -0,0 +1,809 @@
+/*
+ * max77686.c - Regulator driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Chiwoong Byun <[email protected]>
+ * Jonghwa Lee <[email protected]>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997.c
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+#define PMIC_DEBUG KERN_INFO
+#define PMIC_REG_DEBUG KERN_DEBUG
+
+#define MAX77686_OPMODE_SHIFT 6
+#define MAX77686_OPMODE_BUCK234_SHIFT 4
+#define MAX77686_OPMODE_MASK 0x3
+
+struct max77686_data {
+ struct device *dev;
+ struct max77686_dev *iodev;
+ int num_regulators;
+ struct regulator_dev **rdev;
+ int ramp_delay; /* in mV/us */
+
+ struct max77686_opmode_data *opmode_data;
+
+ bool buck2_gpiodvs;
+ bool buck3_gpiodvs;
+ bool buck4_gpiodvs;
+ u8 buck2_vol[8];
+ u8 buck3_vol[8];
+ u8 buck4_vol[8];
+ int buck234_gpios_dvs[3];
+ int buck234_gpios_selb[3];
+ int buck234_gpioindex;
+ bool ignore_gpiodvs_side_effect;
+
+ u8 saved_states[MAX77686_REG_MAX];
+};
+
+static inline void max77686_set_gpio(struct max77686_data *max77686)
+{
+ int set3 = (max77686->buck234_gpioindex) & 0x1;
+ int set2 = ((max77686->buck234_gpioindex) >> 1) & 0x1;
+ int set1 = ((max77686->buck234_gpioindex) >> 2) & 0x1;
+
+ if (max77686->buck234_gpios_dvs[0])
+ gpio_set_value(max77686->buck234_gpios_dvs[0], set1);
+ if (max77686->buck234_gpios_dvs[1])
+ gpio_set_value(max77686->buck234_gpios_dvs[1], set2);
+ if (max77686->buck234_gpios_dvs[2])
+ gpio_set_value(max77686->buck234_gpios_dvs[2], set3);
+}
+
+struct voltage_map_desc {
+ int min;
+ int max;
+ int step;
+ unsigned int n_bits;
+};
+
+/* LDO3 ~ 5, 9 ~ 14, 16 ~ 26 (uV) */
+static const struct voltage_map_desc ldo_voltage_map_desc = {
+ .min = 800000, .max = 3950000, .step = 50000, .n_bits = 6,
+};
+
+/* LDO1 ~ 2, 6 ~ 8, 15 (uV) */
+static const struct voltage_map_desc ldo_low_voltage_map_desc = {
+ .min = 800000, .max = 2375000, .step = 25000, .n_bits = 6,
+};
+
+/* Buck2, 3, 4 (uV) */
+static const struct voltage_map_desc buck_dvs_voltage_map_desc = {
+ .min = 600000, .max = 3787500, .step = 12500, .n_bits = 8,
+};
+
+/* Buck1, 5 ~ 9 (uV) */
+static const struct voltage_map_desc buck_voltage_map_desc = {
+ .min = 750000, .max = 3900000, .step = 50000, .n_bits = 6,
+};
+
+static const struct voltage_map_desc *reg_voltage_map[] = {
+ [MAX77686_LDO1] = &ldo_low_voltage_map_desc,
+ [MAX77686_LDO2] = &ldo_low_voltage_map_desc,
+ [MAX77686_LDO3] = &ldo_voltage_map_desc,
+ [MAX77686_LDO4] = &ldo_voltage_map_desc,
+ [MAX77686_LDO5] = &ldo_voltage_map_desc,
+ [MAX77686_LDO6] = &ldo_low_voltage_map_desc,
+ [MAX77686_LDO7] = &ldo_low_voltage_map_desc,
+ [MAX77686_LDO8] = &ldo_low_voltage_map_desc,
+ [MAX77686_LDO9] = &ldo_voltage_map_desc,
+ [MAX77686_LDO10] = &ldo_voltage_map_desc,
+ [MAX77686_LDO11] = &ldo_voltage_map_desc,
+ [MAX77686_LDO12] = &ldo_voltage_map_desc,
+ [MAX77686_LDO13] = &ldo_voltage_map_desc,
+ [MAX77686_LDO14] = &ldo_voltage_map_desc,
+ [MAX77686_LDO15] = &ldo_low_voltage_map_desc,
+ [MAX77686_LDO16] = &ldo_voltage_map_desc,
+ [MAX77686_LDO17] = &ldo_voltage_map_desc,
+ [MAX77686_LDO18] = &ldo_voltage_map_desc,
+ [MAX77686_LDO19] = &ldo_voltage_map_desc,
+ [MAX77686_LDO20] = &ldo_voltage_map_desc,
+ [MAX77686_LDO21] = &ldo_voltage_map_desc,
+ [MAX77686_LDO22] = &ldo_voltage_map_desc,
+ [MAX77686_LDO23] = &ldo_voltage_map_desc,
+ [MAX77686_LDO24] = &ldo_voltage_map_desc,
+ [MAX77686_LDO25] = &ldo_voltage_map_desc,
+ [MAX77686_LDO26] = &ldo_voltage_map_desc,
+ [MAX77686_BUCK1] = &buck_voltage_map_desc,
+ [MAX77686_BUCK2] = &buck_dvs_voltage_map_desc,
+ [MAX77686_BUCK3] = &buck_dvs_voltage_map_desc,
+ [MAX77686_BUCK4] = &buck_dvs_voltage_map_desc,
+ [MAX77686_BUCK5] = &buck_voltage_map_desc,
+ [MAX77686_BUCK6] = &buck_voltage_map_desc,
+ [MAX77686_BUCK7] = &buck_voltage_map_desc,
+ [MAX77686_BUCK8] = &buck_voltage_map_desc,
+ [MAX77686_BUCK9] = &buck_voltage_map_desc,
+ [MAX77686_EN32KHZ_AP] = NULL,
+ [MAX77686_EN32KHZ_CP] = NULL,
+ [MAX77686_P32KH] = NULL,
+};
+
+static int max77686_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ const struct voltage_map_desc *desc;
+ int rid = rdev_get_id(rdev);
+ int val;
+
+ if (rid >= ARRAY_SIZE(reg_voltage_map) ||
+ rid < 0)
+ return -EINVAL;
+
+ desc = reg_voltage_map[rid];
+ if (desc == NULL)
+ return -EINVAL;
+
+ val = desc->min + desc->step * selector;
+ if (val > desc->max)
+ return -EINVAL;
+
+ return val;
+}
+
+/*
+ * TODO
+ * Reaction to the LP/Standby for each regulator should be defined by
+ * each consumer, not by the regulator device driver if it depends
+ * on which device is attached to which regulator. Here we are
+ * creating possible PM-wise regression with board changes.Also,
+ * we can do the same effect without creating issues with board
+ * changes by carefully defining .state_mem at bsp and suspend ops
+ * callbacks.
+ */
+unsigned int max77686_opmode_reg[][3] = {
+ /* LDO1 ... LDO26 */
+ /* {NORMAL, LP, STANDBY} */
+ {0x3, 0x2, 0x2}, /* LDO1 */
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x1}, /* LDO11 */
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2}, /* LDO21 */
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ {0x3, 0x2, 0x2},
+ /* BUCK1 ... BUCK9 */
+ {0x3, 0x2, 0x1}, /* BUCK1 */
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x2, 0x1},
+ {0x3, 0x3, 0x3},
+ {0x3, 0x3, 0x3},
+ {0x3, 0x3, 0x3},
+ {0x3, 0x3, 0x3},
+ {0x3, 0x3, 0x3},
+ /* 32KHZ */
+ {0x1, 0x1, 0x1},
+ {0x1, 0x1, 0x1},
+ {0x1, 0x1, 0x1},
+};
+
+static int max77686_get_enable_register(struct regulator_dev *rdev,
+ int *reg, int *mask, int *pattern)
+{
+ unsigned int rid = rdev_get_id(rdev);
+ unsigned int mode;
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+
+ if (rid > ARRAY_SIZE(max77686_opmode_reg))
+ return -EINVAL;
+
+ mode = max77686->opmode_data[rid].mode;
+ pr_debug("rid=%d, mode=%d, size=%d\n",
+ rid, mode, ARRAY_SIZE(max77686_opmode_reg));
+
+ switch (rid) {
+ case MAX77686_LDO1 ... MAX77686_LDO26:
+ *reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1);
+ *mask = MAX77686_OPMODE_MASK << MAX77686_OPMODE_SHIFT;
+ *pattern = max77686_opmode_reg[rid][mode] << MAX77686_OPMODE_SHIFT;
+ break;
+ case MAX77686_BUCK1:
+ *reg = MAX77686_REG_BUCK1CTRL;
+ *mask = MAX77686_OPMODE_MASK;
+ *pattern = max77686_opmode_reg[rid][mode];
+ break;
+ case MAX77686_BUCK2:
+ case MAX77686_BUCK3:
+ case MAX77686_BUCK4:
+ *reg = MAX77686_REG_BUCK2CTRL1 + (rid - MAX77686_BUCK2)*10;
+ *mask = MAX77686_OPMODE_MASK << MAX77686_OPMODE_BUCK234_SHIFT;
+ *pattern = max77686_opmode_reg[rid][mode] << MAX77686_OPMODE_BUCK234_SHIFT;
+ break;
+ case MAX77686_BUCK5 ... MAX77686_BUCK9:
+ *reg = MAX77686_REG_BUCK5CTRL + (rid - MAX77686_BUCK5) * 2;
+ *mask = MAX77686_OPMODE_MASK;
+ *pattern = max77686_opmode_reg[rid][mode];
+ break;
+ case MAX77686_EN32KHZ_AP ... MAX77686_P32KH:
+ *reg = MAX77686_REG_32KHZ;
+ *mask = 0x01 << (rid - MAX77686_EN32KHZ_AP);
+ *pattern = 0x01 << (rid - MAX77686_EN32KHZ_AP);
+ break;
+ default:
+ /* Not controllable or not exists */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max77686_reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ int ret, reg, mask, pattern;
+ u8 val;
+
+ ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+ if (ret == -EINVAL)
+ return 1; /* "not controllable" */
+ else if (ret)
+ return ret;
+
+
+ ret = max77686_read_reg(max77686->iodev->regmap, reg, &val);
+
+ if (ret)
+ return ret;
+
+ pr_debug("id=%d, ret=%d, val=%x, mask=%x, pattern=%x\n",
+ rdev_get_id(rdev), (val & mask) == pattern,
+ val, mask, pattern);
+
+ return (val & mask) == pattern;
+}
+
+static int max77686_reg_enable(struct regulator_dev *rdev)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ int ret, reg, mask, pattern;
+
+ ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+ if (ret)
+ return ret;
+
+ printk(PMIC_DEBUG "%s: id=%d, pattern=%x\n",
+ __func__, rdev_get_id(rdev), pattern);
+
+ return max77686_update_reg(max77686->iodev->regmap, reg, pattern, mask);
+}
+
+static int max77686_reg_disable(struct regulator_dev *rdev)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ int ret, reg, mask, pattern;
+
+ ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
+ if (ret)
+ return ret;
+
+ printk(PMIC_DEBUG "%s: id=%d, pattern=%x\n",
+ __func__, rdev_get_id(rdev), pattern);
+
+ return max77686_update_reg(max77686->iodev->regmap, reg, ~mask, mask);
+}
+
+static int max77686_get_voltage_register(struct regulator_dev *rdev,
+ int *_reg, int *_shift, int *_mask)
+{
+ int rid = rdev_get_id(rdev);
+ int reg, shift = 0, mask = 0x3f;
+
+ switch (rid) {
+ case MAX77686_LDO1 ... MAX77686_LDO26:
+ reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1);
+ break;
+ case MAX77686_BUCK1:
+ reg = MAX77686_REG_BUCK1OUT;
+ break;
+ case MAX77686_BUCK2:
+ reg = MAX77686_REG_BUCK2DVS1;
+ mask = 0xff;
+ break;
+ case MAX77686_BUCK3:
+ reg = MAX77686_REG_BUCK3DVS1;
+ mask = 0xff;
+ break;
+ case MAX77686_BUCK4:
+ reg = MAX77686_REG_BUCK4DVS1;
+ mask = 0xff;
+ break;
+ case MAX77686_BUCK5 ... MAX77686_BUCK9:
+ reg = MAX77686_REG_BUCK5OUT + (rid - MAX77686_BUCK5) * 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *_reg = reg;
+ *_shift = shift;
+ *_mask = mask;
+
+ return 0;
+}
+
+static int max77686_get_voltage(struct regulator_dev *rdev)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ int reg, shift, mask, ret;
+ int rid = rdev_get_id(rdev);
+ u8 val;
+
+ ret = max77686_get_voltage_register(rdev, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ if ((rid == MAX77686_BUCK2 && max77686->buck2_gpiodvs) ||
+ (rid == MAX77686_BUCK3 && max77686->buck3_gpiodvs) ||
+ (rid == MAX77686_BUCK4 && max77686->buck4_gpiodvs))
+ reg += max77686->buck234_gpioindex;
+
+ ret = max77686_read_reg(max77686->iodev->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ val >>= shift;
+ val &= mask;
+
+ printk(PMIC_REG_DEBUG "%s: id=%d, reg=%x, mask=%x, val=%x\n",
+ __func__, rid, reg, mask, val);
+
+ return max77686_list_voltage(rdev, val);
+}
+
+static inline int max77686_get_voltage_proper_val(
+ const struct voltage_map_desc *desc,
+ int min_vol, int max_vol)
+{
+ int i = 0;
+
+ if (desc == NULL)
+ return -EINVAL;
+
+ if (max_vol < desc->min || min_vol > desc->max)
+ return -EINVAL;
+
+ while (desc->min + desc->step * i < min_vol &&
+ desc->min + desc->step * i < desc->max)
+ i++;
+
+ if (desc->min + desc->step * i > max_vol)
+ return -EINVAL;
+
+ if (i >= (1 << desc->n_bits))
+ return -EINVAL;
+
+ return i;
+}
+
+static int max77686_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ const struct voltage_map_desc *desc;
+ int rid = rdev_get_id(rdev);
+ int reg, shift = 0, mask, ret;
+ int i;
+ u8 org;
+
+ desc = reg_voltage_map[rid];
+
+ i = max77686_get_voltage_proper_val(desc, min_uV, max_uV);
+ if (i < 0)
+ return i;
+
+ ret = max77686_get_voltage_register(rdev, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ /* TODO: If GPIO-DVS is being used, this won't work. */
+
+ max77686_read_reg(max77686->iodev->regmap, reg, &org);
+ org = (org & mask) >> shift;
+
+ ret = max77686_update_reg(max77686->iodev->regmap, reg, i << shift, mask << shift);
+ *selector = i;
+
+ switch (rid) {
+ case MAX77686_BUCK2 ... MAX77686_BUCK4:
+ if (org < i)
+ udelay(DIV_ROUND_UP(desc->step * (i - org),
+ max77686->ramp_delay * 1000));
+ break;
+ case MAX77686_BUCK1:
+ case MAX77686_BUCK5 ... MAX77686_BUCK9:
+ /* Unconditionally 100 mV/us */
+ if (org < i)
+ udelay(DIV_ROUND_UP(desc->step * (i - org), 100000));
+ break;
+ default:
+ break;
+ }
+
+ printk(PMIC_REG_DEBUG "%s: id=%d, reg=%x, mask=%x, org=%x, val=%x\n",
+ __func__, rdev_get_id(rdev), reg, mask, org, i);
+
+ return ret;
+}
+
+static int max77686_reg_do_nothing(struct regulator_dev *rdev)
+{
+ return 0;
+}
+
+static struct regulator_ops max77686_ldo_ops = {
+ .list_voltage = max77686_list_voltage,
+ .is_enabled = max77686_reg_is_enabled,
+ .enable = max77686_reg_enable,
+ .disable = max77686_reg_disable,
+ .get_voltage = max77686_get_voltage,
+ .set_voltage = max77686_set_voltage,
+ /* TODO: set 0xC0 -> 0x40 with suspend_enable. for 0x0, ->0x0 */
+ .set_suspend_enable = max77686_reg_do_nothing,
+ /* LDO's ON(0xC0) means "ON at normal, OFF at suspend" */
+ .set_suspend_disable = max77686_reg_do_nothing,
+};
+
+static struct regulator_ops max77686_buck_ops = {
+ .list_voltage = max77686_list_voltage,
+ .is_enabled = max77686_reg_is_enabled,
+ .enable = max77686_reg_enable,
+ .disable = max77686_reg_disable,
+ .get_voltage = max77686_get_voltage,
+ .set_voltage = max77686_set_voltage,
+ /* Interpret suspend_enable as "keep on if it was enabled." */
+ .set_suspend_enable = max77686_reg_do_nothing,
+ .set_suspend_disable = max77686_reg_disable,
+};
+
+static struct regulator_ops max77686_fixedvolt_ops = {
+ .list_voltage = max77686_list_voltage,
+ .is_enabled = max77686_reg_is_enabled,
+ .enable = max77686_reg_enable,
+ .disable = max77686_reg_disable,
+ /* Interpret suspend_enable as "keep on if it was enabled." */
+ .set_suspend_enable = max77686_reg_do_nothing,
+ .set_suspend_disable = max77686_reg_disable,
+};
+
+#define regulator_desc_ldo(num) { \
+ .name = "LDO"#num, \
+ .id = MAX77686_LDO##num, \
+ .ops = &max77686_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+}
+#define regulator_desc_buck(num) { \
+ .name = "BUCK"#num, \
+ .id = MAX77686_BUCK##num, \
+ .ops = &max77686_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+}
+
+static struct regulator_desc regulators[] = {
+ regulator_desc_ldo(1),
+ regulator_desc_ldo(2),
+ regulator_desc_ldo(3),
+ regulator_desc_ldo(4),
+ regulator_desc_ldo(5),
+ regulator_desc_ldo(6),
+ regulator_desc_ldo(7),
+ regulator_desc_ldo(8),
+ regulator_desc_ldo(9),
+ regulator_desc_ldo(10),
+ regulator_desc_ldo(11),
+ regulator_desc_ldo(12),
+ regulator_desc_ldo(13),
+ regulator_desc_ldo(14),
+ regulator_desc_ldo(15),
+ regulator_desc_ldo(16),
+ regulator_desc_ldo(17),
+ regulator_desc_ldo(18),
+ regulator_desc_ldo(19),
+ regulator_desc_ldo(20),
+ regulator_desc_ldo(21),
+ regulator_desc_ldo(22),
+ regulator_desc_ldo(23),
+ regulator_desc_ldo(24),
+ regulator_desc_ldo(25),
+ regulator_desc_ldo(26),
+ regulator_desc_buck(1),
+ regulator_desc_buck(2),
+ regulator_desc_buck(3),
+ regulator_desc_buck(4),
+ regulator_desc_buck(5),
+ regulator_desc_buck(6),
+ regulator_desc_buck(7),
+ regulator_desc_buck(8),
+ regulator_desc_buck(9),
+ {
+ .name = "EN32KHz AP",
+ .id = MAX77686_EN32KHZ_AP,
+ .ops = &max77686_fixedvolt_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "EN32KHz CP",
+ .id = MAX77686_EN32KHZ_CP,
+ .ops = &max77686_fixedvolt_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "EN32KHz PMIC",
+ .id = MAX77686_P32KH,
+ .ops = &max77686_fixedvolt_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+};
+
+static __devinit int max77686_pmic_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct regulator_dev **rdev;
+ struct max77686_data *max77686;
+ struct i2c_client *i2c;
+ int i, ret, size;
+ u8 data = 0;
+
+ printk(PMIC_DEBUG "%s\n", __func__);
+
+ if (!pdata) {
+ dev_err(pdev->dev.parent, "No platform init data supplied.\n");
+ return -ENODEV;
+ }
+
+ max77686 = kzalloc(sizeof(struct max77686_data), GFP_KERNEL);
+ if (!max77686)
+ return -ENOMEM;
+
+ size = sizeof(struct regulator_dev *) * pdata->num_regulators;
+ max77686->rdev = kzalloc(size, GFP_KERNEL);
+ if (!max77686->rdev) {
+ kfree(max77686);
+ return -ENOMEM;
+ }
+
+ rdev = max77686->rdev;
+ max77686->dev = &pdev->dev;
+ max77686->iodev = iodev;
+ max77686->num_regulators = pdata->num_regulators;
+ platform_set_drvdata(pdev, max77686);
+ i2c = max77686->iodev->i2c;
+
+ max77686->opmode_data = pdata->opmode_data;
+
+ max77686->ramp_delay = 100; /* Set 0x3 for RAMP */
+ max77686_update_reg(max77686->iodev->regmap, MAX77686_REG_BUCK2CTRL1, 0xC0, 0xC0);
+ max77686_update_reg(max77686->iodev->regmap, MAX77686_REG_BUCK3CTRL1, 0xC0, 0xC0);
+ max77686_update_reg(max77686->iodev->regmap, MAX77686_REG_BUCK4CTRL1, 0xC0, 0xC0);
+
+ max77686_read_reg(max77686->iodev->regmap, MAX77686_REG_DEVICE_ID, &data);
+ printk(PMIC_DEBUG "%s: DEVICE ID=0x%x\n", __func__, data);
+
+ /*
+ * TODO
+ * This disables GPIO-DVS. Later we may need to implement GPIO-DVS..
+ * or we do not?
+ */
+ max77686->buck2_gpiodvs = false;
+ max77686->buck3_gpiodvs = false;
+ max77686->buck4_gpiodvs = false;
+ for (i = 0; i < 3; i++) {
+ char buf[80];
+
+ sprintf(buf, "MAX77686 DVS%d", i);
+
+ if (gpio_is_valid(pdata->buck234_gpio_dvs[i])) {
+ max77686->buck234_gpios_dvs[i] =
+ pdata->buck234_gpio_dvs[i];
+ gpio_request(pdata->buck234_gpio_dvs[i], buf);
+ gpio_direction_output(pdata->buck234_gpio_dvs[i], 0);
+ } else {
+ dev_info(&pdev->dev, "GPIO %s ignored (%d)\n",
+ buf, pdata->buck234_gpio_dvs[i]);
+ }
+
+ sprintf(buf, "MAX77686 SELB%d", i);
+
+ if (gpio_is_valid(pdata->buck234_gpio_selb[i])) {
+ max77686->buck234_gpios_selb[i] =
+ pdata->buck234_gpio_selb[i];
+ gpio_request(pdata->buck234_gpio_selb[i], buf);
+ gpio_direction_output(pdata->buck234_gpio_selb[i], 0);
+ } else {
+ dev_info(&pdev->dev, "GPIO %s ignored (%d)\n",
+ buf, pdata->buck234_gpio_dvs[i]);
+ }
+ }
+ max77686->buck234_gpioindex = 0;
+
+ for (i = 0; i < 8; i++) {
+ ret = max77686_get_voltage_proper_val(
+ &buck_dvs_voltage_map_desc,
+ pdata->buck2_voltage[i],
+ pdata->buck2_voltage[i]
+ + buck_dvs_voltage_map_desc.step);
+ /* 1.1V as default for safety */
+ if (ret < 0)
+ max77686->buck2_vol[i] = 0x28;
+ else
+ max77686->buck2_vol[i] = ret;
+ max77686_write_reg(max77686->iodev->regmap, MAX77686_REG_BUCK2DVS1 + i,
+ max77686->buck2_vol[i]);
+
+ ret = max77686_get_voltage_proper_val(
+ &buck_dvs_voltage_map_desc,
+ pdata->buck3_voltage[i],
+ pdata->buck3_voltage[i]
+ + buck_dvs_voltage_map_desc.step);
+ /* 1.1V as default for safety */
+ if (ret < 0)
+ max77686->buck3_vol[i] = 0x28;
+ else
+ max77686->buck3_vol[i] = ret;
+ max77686_write_reg(max77686->iodev->regmap, MAX77686_REG_BUCK3DVS1 + i,
+ max77686->buck3_vol[i]);
+ ret = max77686_get_voltage_proper_val(
+ &buck_dvs_voltage_map_desc,
+ pdata->buck4_voltage[i],
+ pdata->buck4_voltage[i]
+ + buck_dvs_voltage_map_desc.step);
+ /* 1.1V as default for safety */
+ if (ret < 0)
+ max77686->buck4_vol[i] = 0x28;
+ else
+ max77686->buck4_vol[i] = ret;
+ max77686_write_reg(max77686->iodev->regmap, MAX77686_REG_BUCK4DVS1 + i,
+ max77686->buck4_vol[i]);
+ }
+
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ const struct voltage_map_desc *desc;
+ int id = pdata->regulators[i].id;
+
+ desc = reg_voltage_map[id];
+ if (desc) {
+ regulators[id].n_voltages =
+ (desc->max - desc->min) / desc->step + 1;
+
+ printk(PMIC_DEBUG "%s: desc=%p, id=%d, n_vol=%d, max=%d, min=%d, step=%d\n",
+ __func__, desc, id, regulators[id].n_voltages,
+ desc->max, desc->min, desc->step);
+ }
+
+ rdev[i] = regulator_register(&regulators[id], max77686->dev,
+ pdata->regulators[i].initdata,
+ max77686, NULL);
+ if (IS_ERR(rdev[i])) {
+ ret = PTR_ERR(rdev[i]);
+ dev_err(max77686->dev, "regulator init failed for %d\n",
+ id);
+ rdev[i] = NULL;
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ for (i = 0; i < 3; i++) {
+ if (max77686->buck234_gpios_dvs[i])
+ gpio_free(max77686->buck234_gpios_dvs[i]);
+ if (max77686->buck234_gpios_dvs[i])
+ gpio_free(max77686->buck234_gpios_selb[i]);
+ }
+
+ for (i = 0; i < max77686->num_regulators; i++)
+ if (rdev[i])
+ regulator_unregister(rdev[i]);
+
+ kfree(max77686->rdev);
+ kfree(max77686);
+
+ return ret;
+}
+
+static int __devexit max77686_pmic_remove(struct platform_device *pdev)
+{
+ struct max77686_data *max77686 = platform_get_drvdata(pdev);
+ struct regulator_dev **rdev = max77686->rdev;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (max77686->buck234_gpios_dvs[i])
+ gpio_free(max77686->buck234_gpios_dvs[i]);
+ if (max77686->buck234_gpios_dvs[i])
+ gpio_free(max77686->buck234_gpios_selb[i]);
+ }
+
+ for (i = 0; i < max77686->num_regulators; i++)
+ if (rdev[i])
+ regulator_unregister(rdev[i]);
+
+ kfree(max77686->rdev);
+ kfree(max77686);
+
+ return 0;
+}
+
+static const struct platform_device_id max77686_pmic_id[] = {
+ { "max77686-pmic", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(platform, max77686_pmic_id);
+
+static struct platform_driver max77686_pmic_driver = {
+ .driver = {
+ .name = "max77686-pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = max77686_pmic_probe,
+ .remove = __devexit_p(max77686_pmic_remove),
+ .id_table = max77686_pmic_id,
+};
+
+static int __init max77686_pmic_init(void)
+{
+ printk(PMIC_DEBUG "%s\n", __func__);
+
+ return platform_driver_register(&max77686_pmic_driver);
+}
+subsys_initcall(max77686_pmic_init);
+
+static void __exit max77686_pmic_cleanup(void)
+{
+ platform_driver_unregister(&max77686_pmic_driver);
+}
+module_exit(max77686_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver");
+MODULE_AUTHOR("Chiwoong Byun <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.7.4.1

2012-05-11 06:52:00

by Jonghwa Lee

[permalink] [raw]
Subject: [PATCH v2 1/3] mfd: MAX77686: Add Maxim 77686 mfd driver

This patch is device driver for MAX77686 chip.
MAX77686 is PMIC and includes regulator and rtc on it.
This driver is core of MAX77686 chip, so provides common support for accessing
on-chip devices. It uses irq_domain to manage irq and regmap to read/write data
to its register with i2c bus.

v2
- Change I/O interface register to using regmap.
- Support IRQ domain, and remove irq_base from all referenced.
- Add module_param() macro to debug_mask parameter. It can be set various
masking value for specific purpose while debugging.
- Remove IRQ_TEST block and some unnecessary points.

Signed-off-by: Chiwoong Byun <[email protected]>
Signed-off-by: Jonghwa Lee <[email protected]>
Signed-off-by: Myungjoo Ham <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
drivers/mfd/Kconfig | 2 +-
drivers/mfd/Makefile | 2 +-
drivers/mfd/max77686-irq.c | 109 +++++++++-----------
drivers/mfd/max77686.c | 193 +--------------------------------
include/linux/mfd/max77686-private.h | 54 ++++------
include/linux/mfd/max77686.h | 23 +----
6 files changed, 79 insertions(+), 304 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 998356f..df1ef8f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -443,7 +443,7 @@ config MFD_MAX8998

config MFD_MAX77686
bool "Maxim Semiconductor MAX77686 PMIC Support"
- depends on I2C=y && GENERIC_HARDIRQS
+ depends on I2C && GENERIC_HARDIRQS
select MFD_CORE
help
Say yes here to support for Maxim Semiconductor MAX77686.
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d6e57ea..a793ebd 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -79,8 +79,8 @@ max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
-obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
+obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o

pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
index 255ad69..ebe24dd 100644
--- a/drivers/mfd/max77686-irq.c
+++ b/drivers/mfd/max77686-irq.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
* Chiwoong Byun <[email protected]>
+ * Jonghwa Lee <[email protected]>
*
* 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
@@ -27,17 +28,18 @@
#include <linux/gpio.h>
#include <linux/mfd/max77686.h>
#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
#include <linux/regmap.h>

-
enum {
MAX77686_DEBUG_IRQ_INFO = 1 << 0,
MAX77686_DEBUG_IRQ_MASK = 1 << 1,
MAX77686_DEBUG_IRQ_INT = 1 << 2,
};

-static int debug_mask = MAX77686_DEBUG_IRQ_INFO | MAX77686_DEBUG_IRQ_MASK |
- MAX77686_DEBUG_IRQ_INT;
+static int debug_mask = 0;
+module_param(debug_mask, int, 0);
+MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO 0x2=IRQ_MASK 0x4=IRQ_INI)");

static const u8 max77686_mask_reg[] = {
[PMIC_INT1] = MAX77686_REG_INT1MSK,
@@ -73,15 +75,15 @@ static const struct max77686_irq_data max77686_irqs[] = {
DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4),
DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5),
DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6),
- DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7),
- DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0),
- DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1),
- DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0),
- DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1),
- DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2),
- DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3),
- DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4),
- DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5),
+ DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7),
+ DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0),
+ DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2),
+ DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4),
+ DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5),
};

static void max77686_irq_lock(struct irq_data *data)
@@ -89,7 +91,7 @@ static void max77686_irq_lock(struct irq_data *data)
struct max77686_dev *max77686 = irq_get_chip_data(data->irq);

if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
- pr_debug("%s\n", __func__);
+ pr_info("%s\n", __func__);

mutex_lock(&max77686->irqlock);
}
@@ -120,8 +122,7 @@ static void max77686_irq_sync_unlock(struct irq_data *data)
mutex_unlock(&max77686->irqlock);
}

-static const inline struct max77686_irq_data *
-irq_to_max77686_irq(struct max77686_dev *max77686, int irq)
+static const inline struct max77686_irq_data *to_max77686_irq(int irq)
{
struct irq_data *data = irq_get_irq_data(irq);
return &max77686_irqs[data->hwirq];
@@ -130,8 +131,7 @@ irq_to_max77686_irq(struct max77686_dev *max77686, int irq)
static void max77686_irq_mask(struct irq_data *data)
{
struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
- const struct max77686_irq_data *irq_data = irq_to_max77686_irq(max77686,
- data->irq);
+ const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);

max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;

@@ -144,8 +144,7 @@ static void max77686_irq_mask(struct irq_data *data)
static void max77686_irq_unmask(struct irq_data *data)
{
struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
- const struct max77686_irq_data *irq_data = irq_to_max77686_irq(max77686,
- data->irq);
+ const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);

max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;

@@ -182,27 +181,25 @@ static irqreturn_t max77686_irq_thread(int irq, void *data)
if (debug_mask & MAX77686_DEBUG_IRQ_INT)
pr_info("%s: irq_src=0x%x\n", __func__, irq_src);

- /* MAX77686_IRQSRC_RTC may be set even if there are pending at INT1/2 */
- ret = max77686_read_reg(max77686->regmap,
- MAX77686_REG_INT1, &irq_reg[0]);
- ret = max77686_read_reg(max77686->regmap,
- MAX77686_REG_INT2, &irq_reg[1]);
- if (ret < 0) {
- dev_err(max77686->dev, "Failed to read pmic interrupt: %d\n",
- ret);
- return IRQ_NONE;
- }
+ if (irq_src == MAX77686_IRQSRC_PMIC) {
+ ret = max77686_bulk_read(max77686->rtc_regmap,
+ MAX77686_REG_INT1, 2, irq_reg);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }

- if (debug_mask & MAX77686_DEBUG_IRQ_INT)
- pr_info("%s: int1=0x%x, int2=0x%x\n",
- __func__, irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: int1=0x%x, int2=0x%x\n", __func__,
+ irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+ }

if (irq_src & MAX77686_IRQSRC_RTC) {
ret = max77686_read_reg(max77686->rtc_regmap,
- MAX77686_RTC_INT, &irq_reg[RTC_INT]);
-
+ MAX77686_RTC_INT, &irq_reg[RTC_INT]);
if (ret < 0) {
- dev_err(max77686->dev, "Failed to read rtc interrupt: %d\n",
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
ret);
return IRQ_NONE;
}
@@ -213,26 +210,17 @@ static irqreturn_t max77686_irq_thread(int irq, void *data)

}

- /* Report */
for (i = 0; i < MAX77686_IRQ_NR; i++) {
if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) {
cur_irq = irq_find_mapping(max77686->irq_domain, i);
if (cur_irq)
handle_nested_irq(cur_irq);
-
}
}

return IRQ_HANDLED;
}

-int max77686_irq_resume(struct max77686_dev *max77686)
-{
- if (max77686->irq && max77686->irq_domain)
- max77686_irq_thread(0, max77686);
- return 0;
-}
-
static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
@@ -259,6 +247,7 @@ int max77686_irq_init(struct max77686_dev *max77686)
int i;
int ret;
int val;
+ struct regmap *map;

if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
pr_info("%s+\n", __func__);
@@ -267,30 +256,29 @@ int max77686_irq_init(struct max77686_dev *max77686)
dev_warn(max77686->dev, "No interrupt gpio specified.\n");
return 0;
}
-
mutex_init(&max77686->irqlock);

max77686->irq = gpio_to_irq(max77686->irq_gpio);
- ret = gpio_request(max77686->irq_gpio, "pmic_irq");
- if (ret < 0 && ret != -EBUSY) {
- dev_err(max77686->dev,
- "Failed to request gpio %d with ret: %d\n",
- max77686->irq_gpio, ret);
- return IRQ_NONE;
- }
- if (ret == -EBUSY)
- dev_warn(max77686->dev, "gpio pmic_irq is already requested\n");

- gpio_direction_input(max77686->irq_gpio);
- val = gpio_get_value(max77686->irq_gpio);
- gpio_free(max77686->irq_gpio);
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT) {
+ ret = gpio_request(max77686->irq_gpio, "pmic_irq");
+ if (ret < 0) {
+ dev_err(max77686->dev,
+ "Failed to request gpio %d with ret: %d\n",
+ max77686->irq_gpio, ret);
+ return IRQ_NONE;
+ }

- if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+
+ gpio_direction_input(max77686->irq_gpio);
+ val = gpio_get_value(max77686->irq_gpio);
+ gpio_free(max77686->irq_gpio);
pr_info("%s: gpio_irq=%x\n", __func__, val);
+ }
+

/* Mask individual interrupt sources */
for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
- struct regmap *map;

max77686->irq_masks_cur[i] = 0xff;
max77686->irq_masks_cache[i] = 0xff;
@@ -303,9 +291,8 @@ int max77686_irq_init(struct max77686_dev *max77686)

max77686_write_reg(map, max77686_mask_reg[i], 0xff);
}
-
domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
- &max77686_irq_domain_ops, &max77686);
+ &max77686_irq_domain_ops, max77686);
if (!domain) {
dev_err(max77686->dev, "could not create irq domain\n");
return -ENODEV;
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index d8f44b2..a87e508 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2012 Samsung Electronics
* Chiwoong Byun <[email protected]>
+ * Jonghwa Lee <[email protected]>
*
* 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
@@ -21,16 +22,15 @@
* This driver is based on max8997.c
*/

+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max77686.h>
#include <linux/mfd/max77686-private.h>
-#include <linux/interrupt.h>
#include <linux/err.h>

#define I2C_ADDR_RTC (0x0C >> 1)
@@ -97,7 +97,6 @@ int max77686_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask)
}
EXPORT_SYMBOL_GPL(max77686_update_reg);

-
static struct regmap_config max77686_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -128,6 +127,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
max77686->dev = &i2c->dev;
max77686->i2c = i2c;
max77686->type = id->driver_data;
+
if (!pdata) {
ret = -EIO;
goto err;
@@ -135,7 +135,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c,

max77686->wakeup = pdata->wakeup;
max77686->irq_gpio = pdata->irq_gpio;
- max77686->wtsr_smpl = pdata->wtsr_smpl;

mutex_init(&max77686->iolock);

@@ -150,6 +149,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c,

max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
i2c_set_clientdata(max77686->rtc, max77686);
+
max77686_irq_init(max77686);

ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
@@ -158,8 +158,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
goto err_mfd;

- device_init_wakeup(max77686->dev, pdata->wakeup);
-
return ret;

err_mfd:
@@ -187,189 +185,10 @@ static const struct i2c_device_id max77686_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);

-#ifdef CONFIG_PM
-static int max77686_suspend(struct device *dev)
-{
- struct max77686_dev *max77686 = dev_get_drvdata(dev);
-
- disable_irq(max77686->irq);
-
- if (device_may_wakeup(dev))
- enable_irq_wake(max77686->irq);
-
- return 0;
-}
-
-static int max77686_resume(struct device *dev)
-{
- struct max77686_dev *max77686 = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(max77686->irq);
-
- enable_irq(max77686->irq);
-
- return max77686_irq_resume(max77686);
-}
-#else
-#define max77686_suspend NULL
-#define max77686_resume NULL
-#endif /* CONFIG_PM */
-
-#ifdef CONFIG_HIBERNATION
-
-u8 max77686_dumpaddr_pmic[] = {
- MAX77686_REG_INT1MSK,
- MAX77686_REG_INT2MSK,
- MAX77686_REG_ONOFF_DELAY,
- MAX77686_REG_MRSTB ,
- /* Reserved: 0x0B-0x0F */
- MAX77686_REG_BUCK1CTRL,
- MAX77686_REG_BUCK1OUT,
- MAX77686_REG_BUCK2CTRL1,
- MAX77686_REG_BUCK234FREQ,
- MAX77686_REG_BUCK2DVS1,
- MAX77686_REG_BUCK2DVS2,
- MAX77686_REG_BUCK2DVS3,
- MAX77686_REG_BUCK2DVS4,
- MAX77686_REG_BUCK2DVS5,
- MAX77686_REG_BUCK2DVS6,
- MAX77686_REG_BUCK2DVS7,
- MAX77686_REG_BUCK2DVS8,
- MAX77686_REG_BUCK3CTRL1,
- /* Reserved: 0x1D */
- MAX77686_REG_BUCK3DVS1,
- MAX77686_REG_BUCK3DVS2,
- MAX77686_REG_BUCK3DVS3,
- MAX77686_REG_BUCK3DVS4,
- MAX77686_REG_BUCK3DVS5,
- MAX77686_REG_BUCK3DVS6,
- MAX77686_REG_BUCK3DVS7,
- MAX77686_REG_BUCK3DVS8,
- MAX77686_REG_BUCK4CTRL1,
- /* Reserved: 0x27 */
- MAX77686_REG_BUCK4DVS1,
- MAX77686_REG_BUCK4DVS2,
- MAX77686_REG_BUCK4DVS3,
- MAX77686_REG_BUCK4DVS4,
- MAX77686_REG_BUCK4DVS5,
- MAX77686_REG_BUCK4DVS6,
- MAX77686_REG_BUCK4DVS7,
- MAX77686_REG_BUCK4DVS8,
- MAX77686_REG_BUCK5CTRL,
- MAX77686_REG_BUCK5OUT,
- MAX77686_REG_BUCK6CTRL,
- MAX77686_REG_BUCK6OUT,
- MAX77686_REG_BUCK7CTRL,
- MAX77686_REG_BUCK7OUT,
- MAX77686_REG_BUCK8CTRL,
- MAX77686_REG_BUCK8OUT,
- MAX77686_REG_BUCK9CTRL,
- MAX77686_REG_BUCK9OUT,
- /* Reserved: 0x3A-0x3F */
- MAX77686_REG_LDO1CTRL1 ,
- MAX77686_REG_LDO2CTRL1 ,
- MAX77686_REG_LDO3CTRL1 ,
- MAX77686_REG_LDO4CTRL1 ,
- MAX77686_REG_LDO5CTRL1 ,
- MAX77686_REG_LDO6CTRL1,
- MAX77686_REG_LDO7CTRL1 ,
- MAX77686_REG_LDO8CTRL1 ,
- MAX77686_REG_LDO9CTRL1 ,
- MAX77686_REG_LDO10CTRL1,
- MAX77686_REG_LDO11CTRL1,
- MAX77686_REG_LDO12CTRL1,
- MAX77686_REG_LDO13CTRL1,
- MAX77686_REG_LDO14CTRL1,
- MAX77686_REG_LDO15CTRL1,
- MAX77686_REG_LDO16CTRL1,
- MAX77686_REG_LDO17CTRL1,
- MAX77686_REG_LDO18CTRL1,
- MAX77686_REG_LDO19CTRL1,
- MAX77686_REG_LDO20CTRL1,
- MAX77686_REG_LDO21CTRL1,
- MAX77686_REG_LDO22CTRL1,
- MAX77686_REG_LDO23CTRL1,
- MAX77686_REG_LDO24CTRL1,
- MAX77686_REG_LDO25CTRL1,
- MAX77686_REG_LDO26CTRL1,
- /* Reserved: 0x5A-0x5F */
- MAX77686_REG_LDO1CTRL2 ,
- MAX77686_REG_LDO2CTRL2 ,
- MAX77686_REG_LDO3CTRL2 ,
- MAX77686_REG_LDO4CTRL2 ,
- MAX77686_REG_LDO5CTRL2 ,
- MAX77686_REG_LDO6CTRL2,
- MAX77686_REG_LDO7CTRL2 ,
- MAX77686_REG_LDO8CTRL2 ,
- MAX77686_REG_LDO9CTRL2 ,
- MAX77686_REG_LDO10CTRL2,
- MAX77686_REG_LDO11CTRL2,
- MAX77686_REG_LDO12CTRL2,
- MAX77686_REG_LDO13CTRL2,
- MAX77686_REG_LDO14CTRL2,
- MAX77686_REG_LDO15CTRL2,
- MAX77686_REG_LDO16CTRL2,
- MAX77686_REG_LDO17CTRL2,
- MAX77686_REG_LDO18CTRL2,
- MAX77686_REG_LDO19CTRL2,
- MAX77686_REG_LDO20CTRL2,
- MAX77686_REG_LDO21CTRL2,
- MAX77686_REG_LDO22CTRL2,
- MAX77686_REG_LDO23CTRL2,
- MAX77686_REG_LDO24CTRL2,
- MAX77686_REG_LDO25CTRL2,
- MAX77686_REG_LDO26CTRL2,
- MAX77686_REG_BBAT_CHG,
- MAX77686_REG_32KHZ,
-};
-
-static int max77686_freeze(struct device *dev)
-{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
- struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(max77686_dumpaddr_pmic); i++)
- max77686_read_reg(i2c, max77686_dumpaddr_pmic[i],
- &max77686->reg_dump[i]);
-
- disable_irq(max77686->irq);
-
- return 0;
-}
-
-static int max77686_restore(struct device *dev)
-{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
- struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
- int i;
-
- enable_irq(max77686->irq);
-
- for (i = 0; i < ARRAY_SIZE(max77686_dumpaddr_pmic); i++)
- max77686_write_reg(i2c, max77686_dumpaddr_pmic[i],
- max77686->reg_dump[i]);
-
- return 0;
-}
-#endif
-
-const struct dev_pm_ops max77686_pm = {
- .suspend = max77686_suspend,
- .resume = max77686_resume,
-#ifdef CONFIG_HIBERNATION
- .freeze = max77686_freeze,
- .thaw = max77686_restore,
- .restore = max77686_restore,
-#endif
-};
-
static struct i2c_driver max77686_i2c_driver = {
.driver = {
- .name = "max77686",
- .owner = THIS_MODULE,
- .pm = &max77686_pm,
+ .name = "max77686",
+ .owner = THIS_MODULE,
},
.probe = max77686_i2c_probe,
.remove = max77686_i2c_remove,
diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h
index ab369e9..2f62d2a 100644
--- a/include/linux/mfd/max77686-private.h
+++ b/include/linux/mfd/max77686-private.h
@@ -4,8 +4,6 @@
* Copyright (C) 2012 Samsung Electrnoics
* Chiwoong Byun <[email protected]>
*
- * This program is not provided / owned by Maxim Integrated Products.
- *
* 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
@@ -25,8 +23,8 @@
#define __LINUX_MFD_MAX77686_PRIV_H

#include <linux/i2c.h>
-#include <linux/export.h>
-#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+#include <linux/module.h>

#define MAX77686_REG_INVALID (0xff)

@@ -147,35 +145,35 @@ enum max77686_pmic_reg {
/* Reserved: 0x7A-0x7D */

MAX77686_REG_BBAT_CHG = 0x7E,
- MAX77686_REG_32KHZ = 0x7F,
+ MAX77686_REG_32KHZ = 0x7F,

MAX77686_REG_PMIC_END = 0x80,
};

enum max77686_rtc_reg {
- MAX77686_RTC_INT = 0x00,
- MAX77686_RTC_INTM = 0x01,
+ MAX77686_RTC_INT = 0x00,
+ MAX77686_RTC_INTM = 0x01,
MAX77686_RTC_CONTROLM = 0x02,
MAX77686_RTC_CONTROL = 0x03,
MAX77686_RTC_UPDATE0 = 0x04,
/* Reserved: 0x5 */
MAX77686_WTSR_SMPL_CNTL = 0x06,
- MAX77686_RTC_SEC = 0x07,
- MAX77686_RTC_MIN = 0x08,
- MAX77686_RTC_HOUR = 0x09,
+ MAX77686_RTC_SEC = 0x07,
+ MAX77686_RTC_MIN = 0x08,
+ MAX77686_RTC_HOUR = 0x09,
MAX77686_RTC_WEEKDAY = 0x0A,
- MAX77686_RTC_MONTH = 0x0B,
- MAX77686_RTC_YEAR = 0x0C,
- MAX77686_RTC_DATE = 0x0D,
- MAX77686_ALARM1_SEC = 0x0E,
- MAX77686_ALARM1_MIN = 0x0F,
+ MAX77686_RTC_MONTH = 0x0B,
+ MAX77686_RTC_YEAR = 0x0C,
+ MAX77686_RTC_DATE = 0x0D,
+ MAX77686_ALARM1_SEC = 0x0E,
+ MAX77686_ALARM1_MIN = 0x0F,
MAX77686_ALARM1_HOUR = 0x10,
MAX77686_ALARM1_WEEKDAY = 0x11,
MAX77686_ALARM1_MONTH = 0x12,
MAX77686_ALARM1_YEAR = 0x13,
MAX77686_ALARM1_DATE = 0x14,
- MAX77686_ALARM2_SEC = 0x15,
- MAX77686_ALARM2_MIN = 0x16,
+ MAX77686_ALARM2_SEC = 0x15,
+ MAX77686_ALARM2_MIN = 0x16,
MAX77686_ALARM2_HOUR = 0x17,
MAX77686_ALARM2_WEEKDAY = 0x18,
MAX77686_ALARM2_MONTH = 0x19,
@@ -186,11 +184,6 @@ enum max77686_rtc_reg {
#define MAX77686_IRQSRC_PMIC (0)
#define MAX77686_IRQSRC_RTC (1 << 0)

-#define MAX77686_REG_RAMP_RATE_100MV (0x3<<6)
-#define MAX77686_REG_RAMP_RATE_55MV (0x2<<6)
-#define MAX77686_REG_RAMP_RATE_27MV (0x1<<6)
-#define MAX77686_REG_RAMP_RATE_13MV (0x0<<6)
-
enum max77686_irq_source {
PMIC_INT1 = 0,
PMIC_INT2,
@@ -224,26 +217,23 @@ enum max77686_irq {

struct max77686_dev {
struct device *dev;
- struct regmap *regmap;
- struct regmap *rtc_regmap;
struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
struct i2c_client *rtc; /* slave addr 0x0c */
struct mutex iolock;

int type;

+ struct regmap *regmap; /* regmap for mfd */
+ struct regmap *rtc_regmap; /* regmap for rtc */
+
+ struct irq_domain *irq_domain;
+
int irq;
int irq_gpio;
- struct irq_domain *irq_domain;
bool wakeup;
struct mutex irqlock;
int irq_masks_cur[MAX77686_IRQ_GROUP_NR];
int irq_masks_cache[MAX77686_IRQ_GROUP_NR];
- int wtsr_smpl;
-
-#ifdef CONFIG_HIBERNATION
- u8 reg_dump[MAX77686_REG_PMIC_END];
-#endif
};

enum max77686_types {
@@ -260,6 +250,6 @@ extern int max77686_bulk_read(struct regmap *map, u8 reg, int count,
extern int max77686_write_reg(struct regmap *map, u8 reg, u8 value);
extern int max77686_bulk_write(struct regmap *map, u8 reg, int count,
u8 *buf);
-extern int max77686_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask);
-
+extern int max77686_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask)
+;
#endif /* __LINUX_MFD_MAX77686_PRIV_H */
diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h
index a151fc6..fcf3126 100644
--- a/include/linux/mfd/max77686.h
+++ b/include/linux/mfd/max77686.h
@@ -4,8 +4,6 @@
* Copyright (C) 2012 Samsung Electrnoics
* Chiwoong Byun <[email protected]>
*
- * This program is not provided / owned by Maxim Integrated Products.
- *
* 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
@@ -32,9 +30,6 @@

#include <linux/regulator/consumer.h>

-#define MAX77686_SMPL_ENABLE (0x1)
-#define MAX77686_WTSR_ENABLE (0x2)
-
/* MAX77686 regulator IDs */
enum max77686_regulators {
MAX77686_LDO1 = 0,
@@ -90,23 +85,11 @@ enum max77686_opmode {
MAX77686_OPMODE_STANDBY,
};

-enum max77686_ramp_rate {
- MAX77686_RAMP_RATE_100MV,
- MAX77686_RAMP_RATE_13MV,
- MAX77686_RAMP_RATE_27MV,
- MAX77686_RAMP_RATE_55MV,
-};
-
struct max77686_opmode_data {
int id;
int mode;
};

-struct max77686_buck234_gpio_data {
- int gpio;
- int data;
-};
-
struct max77686_platform_data {
/* IRQ */
int irq_gpio;
@@ -116,19 +99,15 @@ struct max77686_platform_data {
/* ---- PMIC ---- */
struct max77686_regulator_data *regulators;
int num_regulators;
- int has_full_constraints;

struct max77686_opmode_data *opmode_data;
- int ramp_rate;
- int wtsr_smpl;

/*
* GPIO-DVS feature is not enabled with the current version of
* MAX77686 driver. Buck2/3/4_voltages[0] is used as the default
* voltage at probe. DVS/SELB gpios are set as OUTPUT-LOW.
*/
- struct max77686_buck234_gpio_data buck234_gpio_dvs[3];
- /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */
+ int buck234_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */
int buck234_gpio_selb[3]; /* [0]SELB2, [1]SELB3, [2]SELB4 */
unsigned int buck2_voltage[8]; /* buckx_voltage in uV */
unsigned int buck3_voltage[8];
--
1.7.4.1

2012-05-12 10:43:44

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] regulator: MAX77686: Add Maxim 77686 regulator driver

On Fri, May 11, 2012 at 03:50:44PM +0900, Jonghwa Lee wrote:

> +/* LDO3 ~ 5, 9 ~ 14, 16 ~ 26 (uV) */
> +static const struct voltage_map_desc ldo_voltage_map_desc = {
> + .min = 800000, .max = 3950000, .step = 50000, .n_bits = 6,
> +};

As I indicated I'd do in reply to Yadwinder Singh Brar's posting of a
version of this driver the other day this is now factored out into the
core - you should be able to set this all up using regulator_desc and
regulator_map_voltage_linear() which will appear in -next on Monday (I
appreciate it's not something you should have been aware of!). This
should also allow you to use regulator_{set,get}_voltage_regmap.

> + [MAX77686_EN32KHZ_AP] = NULL,
> + [MAX77686_EN32KHZ_CP] = NULL,
> + [MAX77686_P32KH] = NULL,

These should be being moved over to the generic clock API now it's in
mainline.

> +/*
> + * TODO
> + * Reaction to the LP/Standby for each regulator should be defined by
> + * each consumer, not by the regulator device driver if it depends
> + * on which device is attached to which regulator. Here we are
> + * creating possible PM-wise regression with board changes.Also,
> + * we can do the same effect without creating issues with board
> + * changes by carefully defining .state_mem at bsp and suspend ops
> + * callbacks.
> + */

The various set_suspend() calls are supposed to be for this, though in
practice they're rarely used in systems so we probably need a bunch of
work there. We certainly don't have any aribtration between consumers
yet.

> +static int max77686_reg_is_enabled(struct regulator_dev *rdev)
> +{
> + struct max77686_data *max77686 = rdev_get_drvdata(rdev);
> + int ret, reg, mask, pattern;
> + u8 val;
> +
> + ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
> + if (ret == -EINVAL)
> + return 1; /* "not controllable" */

Just have separate ops structures for these regulators.

> +static int max77686_get_voltage_register(struct regulator_dev *rdev,
> + int *_reg, int *_shift, int *_mask)

> +static int max77686_get_voltage(struct regulator_dev *rdev)
> +{

These look like they should be done using regulator_get_voltage_regmap()
so you only need data in the driver.

> +static inline int max77686_get_voltage_proper_val(
> + const struct voltage_map_desc *desc,
> + int min_vol, int max_vol)

Pretty big function for inline, and the core will do this for you anyway
if you use the new features I was mentioning further up.

> + switch (rid) {
> + case MAX77686_BUCK2 ... MAX77686_BUCK4:
> + if (org < i)
> + udelay(DIV_ROUND_UP(desc->step * (i - org),
> + max77686->ramp_delay * 1000));
> + break;
> + case MAX77686_BUCK1:
> + case MAX77686_BUCK5 ... MAX77686_BUCK9:
> + /* Unconditionally 100 mV/us */
> + if (org < i)
> + udelay(DIV_ROUND_UP(desc->step * (i - org), 100000));
> + break;
> + default:
> + break;
> + }

Implement set_voltage_time_sel() instead and let the core do the delays.

> +static int max77686_reg_do_nothing(struct regulator_dev *rdev)
> +{
> + return 0;
> +}

Remove this, you should never have empty functions like this.

> + if (!pdata) {
> + dev_err(pdev->dev.parent, "No platform init data supplied.\n");
> + return -ENODEV;
> + }

You should just carry on unless there's some strong device-specific
reason for not doing so.

> + max77686 = kzalloc(sizeof(struct max77686_data), GFP_KERNEL);
> + if (!max77686)
> + return -ENOMEM;

devm_kzalloc().

> + size = sizeof(struct regulator_dev *) * pdata->num_regulators;
> + max77686->rdev = kzalloc(size, GFP_KERNEL);
> + if (!max77686->rdev) {
> + kfree(max77686);
> + return -ENOMEM;
> + }

You should just unconditionally register all regulators the chip has,
this allows users to inspect the state of the regulators even if they're
not being controlled by software.

> + max77686->opmode_data = pdata->opmode_data;

Shouldn't this be being handled by regulator_set_mode()? If not what
does it do?

> + printk(PMIC_DEBUG "%s: DEVICE ID=0x%x\n", __func__, data);

This needs cleaning up - it should at least be a dev_ printk.

> +static int __init max77686_pmic_init(void)
> +{
> + printk(PMIC_DEBUG "%s\n", __func__);

This can be removed too.


Attachments:
(No filename) (4.05 kB)
signature.asc (836.00 B)
Digital signature
Download all attachments

2012-05-17 11:43:13

by Jonghwa Lee

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] regulator: MAX77686: Add Maxim 77686 regulator driver

On 2012년 05월 12일 19:43, Mark Brown wrote:
Hi,Mark,
I'm sorry my reply is too late.

> On Fri, May 11, 2012 at 03:50:44PM +0900, Jonghwa Lee wrote:
>
>> +/* LDO3 ~ 5, 9 ~ 14, 16 ~ 26 (uV) */
>> +static const struct voltage_map_desc ldo_voltage_map_desc = {
>> + .min = 800000, .max = 3950000, .step = 50000, .n_bits = 6,
>> +};
>
> As I indicated I'd do in reply to Yadwinder Singh Brar's posting of a
> version of this driver the other day this is now factored out into the
> core - you should be able to set this all up using regulator_desc and
> regulator_map_voltage_linear() which will appear in -next on Monday (I
> appreciate it's not something you should have been aware of!). This
> should also allow you to use regulator_{set,get}_voltage_regmap.
>

I'll updated it as soon as those patches are merged to mainline.

>> + [MAX77686_EN32KHZ_AP] = NULL,
>> + [MAX77686_EN32KHZ_CP] = NULL,
>> + [MAX77686_P32KH] = NULL,
>
> These should be being moved over to the generic clock API now it's in
> mainline.
>
I couldn't check this yet, i'll retouch that.
>> +/*
>> + * TODO
>> + * Reaction to the LP/Standby for each regulator should be defined by
>> + * each consumer, not by the regulator device driver if it depends
>> + * on which device is attached to which regulator. Here we are
>> + * creating possible PM-wise regression with board changes.Also,
>> + * we can do the same effect without creating issues with board
>> + * changes by carefully defining .state_mem at bsp and suspend ops
>> + * callbacks.
>> + */
>
> The various set_suspend() calls are supposed to be for this, though in
> practice they're rarely used in systems so we probably need a bunch of
> work there. We certainly don't have any aribtration between consumers
> yet.
>

When system goes into suspended mode, it doesn't need any detail to
enable or disable a regulator in MAX77686. So i think it'll be fine to
use enable/diable function whichever situation we are in. To prevent
confusion i just removed function pointers for suspending.

>> +static int max77686_reg_is_enabled(struct regulator_dev *rdev)
>> +{
>> + struct max77686_data *max77686 = rdev_get_drvdata(rdev);
>> + int ret, reg, mask, pattern;
>> + u8 val;
>> +
>> + ret = max77686_get_enable_register(rdev, &reg, &mask, &pattern);
>> + if (ret == -EINVAL)
>> + return 1; /* "not controllable" */
>
> Just have separate ops structures for these regulators.
>

I think that procedure is useless, so i removed it.

>> +static int max77686_get_voltage_register(struct regulator_dev *rdev,
>> + int *_reg, int *_shift, int *_mask)
>
>> +static int max77686_get_voltage(struct regulator_dev *rdev)
>> +{
>
> These look like they should be done using regulator_get_voltage_regmap()
> so you only need data in the driver.
>

This also will be updated ,,

>> +static inline int max77686_get_voltage_proper_val(
>> + const struct voltage_map_desc *desc,
>> + int min_vol, int max_vol)
>
> Pretty big function for inline, and the core will do this for you anyway
> if you use the new features I was mentioning further up.
>

I removed it, it isn't needed any more.

>> + switch (rid) {
>> + case MAX77686_BUCK2 ... MAX77686_BUCK4:
>> + if (org < i)
>> + udelay(DIV_ROUND_UP(desc->step * (i - org),
>> + max77686->ramp_delay * 1000));
>> + break;
>> + case MAX77686_BUCK1:
>> + case MAX77686_BUCK5 ... MAX77686_BUCK9:
>> + /* Unconditionally 100 mV/us */
>> + if (org < i)
>> + udelay(DIV_ROUND_UP(desc->step * (i - org), 100000));
>> + break;
>> + default:
>> + break;
>> + }
>
> Implement set_voltage_time_sel() instead and let the core do the delays.
>

I applied it. And i also converted set/get_voltage() to
set/get_voltage_sel().

>> +static int max77686_reg_do_nothing(struct regulator_dev *rdev)
>> +{
>> + return 0;
>> +}
>
> Remove this, you should never have empty functions like this.
>

Removed.

>> + if (!pdata) {
>> + dev_err(pdev->dev.parent, "No platform init data supplied.\n");
>> + return -ENODEV;
>> + }
>
> You should just carry on unless there's some strong device-specific
> reason for not doing so.
>

Modified driver to keep probing whether pdata exists or not.

>> + max77686 = kzalloc(sizeof(struct max77686_data), GFP_KERNEL);
>> + if (!max77686)
>> + return -ENOMEM;
>
> devm_kzalloc().
>

Modified.

>> + size = sizeof(struct regulator_dev *) * pdata->num_regulators;
>> + max77686->rdev = kzalloc(size, GFP_KERNEL);
>> + if (!max77686->rdev) {
>> + kfree(max77686);
>> + return -ENOMEM;
>> + }
>
> You should just unconditionally register all regulators the chip has,
> this allows users to inspect the state of the regulators even if they're
> not being controlled by software.
>

Modified. Now driver registers all regulator unconditionally.

>> + max77686->opmode_data = pdata->opmode_data;
>
> Shouldn't this be being handled by regulator_set_mode()? If not what
> does it do?
>

I consider this for a while, but i decided just let it be. Because
opmode_data is only used in max77686_get_enable_register() to get
proper opcode value. To use regulator_set_mode, it needs too much
modification. Is there some strict purpose to use that, i'll change it.

>> + printk(PMIC_DEBUG "%s: DEVICE ID=0x%x\n", __func__, data);
>
> This needs cleaning up - it should at least be a dev_ printk.
>
>> +static int __init max77686_pmic_init(void)
>> +{
>> + printk(PMIC_DEBUG "%s\n", __func__);
>
> This can be removed too.

Those all were cleaned.


Best regards,
LEE