All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] regulator: Fix bug in regulator_mode_to_status() core function.
@ 2012-07-11 12:10 Krystian Garbaciak
  2012-07-11 13:28 ` Mark Brown
  0 siblings, 1 reply; 12+ messages in thread
From: Krystian Garbaciak @ 2012-07-11 12:10 UTC (permalink / raw)
  To: Mark Brown, Liam Girdwood; +Cc: linux-kernel

Fix typo for case REGULATOR_STATUS_STANDBY -> REGULATOR_MODE_STANDBY.
For undefined mode, return REGULATOR_STATUS_ERROR (0 is not valid status).

Signed-off-by: Krystian Garbaciak <krystian.garbaciak@diasemi.com>
---
 drivers/regulator/core.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 09a737c..b821a9f 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2909,10 +2909,10 @@ int regulator_mode_to_status(unsigned int mode)
 		return REGULATOR_STATUS_NORMAL;
 	case REGULATOR_MODE_IDLE:
 		return REGULATOR_STATUS_IDLE;
-	case REGULATOR_STATUS_STANDBY:
+	case REGULATOR_MODE_STANDBY:
 		return REGULATOR_STATUS_STANDBY;
 	default:
-		return 0;
+		return REGULATOR_STATUS_ERROR;
 	}
 }
 EXPORT_SYMBOL_GPL(regulator_mode_to_status);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread
* [RFC PATCH 3/8] rtc: Add RTC driver for DA906x PMIC.
@ 2012-08-24 14:00 Krystian Garbaciak
  2012-08-24 14:05 ` [RFC PATCH 4/8] hwmon: Add DA906x hardware monitoring support Krystian Garbaciak
  0 siblings, 1 reply; 12+ messages in thread
From: Krystian Garbaciak @ 2012-08-24 14:00 UTC (permalink / raw)
  To: linux-kernel, rtc-linux, lm-sensors, linux-input, linux-watchdog,
	linux-leds
  Cc: Alessandro Zummo, Andrew Jones, Dmitry Torokhov, Samuel Ortiz,
	Ashish Jangam, Mark Brown, Donggeun Kim, Wim Van Sebroeck,
	Richard Purdie <rpurdie@rpsys.net> Anthony Olech, Bryan Wu,
	Liam Girdwood

DA906x RTC driver supports date/time and alarm.

In hardware, PMIC supports alarm setting with a resolution of one minute and
tick event (every second update event). The driver combines it, providing alarm
with one second resolution.

The driver requires MFD core driver for operation.

Signed-off-by: Krystian Garbaciak <krystian.garbaciak@diasemi.com>
---
 drivers/rtc/Kconfig      |    7 +
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-da906x.c |  379 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 387 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-da906x.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index fabc99a..e6037cd 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -571,6 +571,13 @@ config RTC_DRV_DA9052
 	  Say y here to support the RTC driver for Dialog Semiconductor
 	  DA9052-BC and DA9053-AA/Bx PMICs.
 
+config RTC_DRV_DA906X
+	tristate "Dialog DA906X RTC"
+	depends on MFD_DA906X
+	help
+	  Say y here to support the RTC driver for
+	  Dialog Semiconductor DA906x PMIC.
+
 config RTC_DRV_EFI
 	tristate "EFI RTC"
 	depends on IA64
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 0d5b2b6..d9c1e9f 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_RTC_DRV_BQ4802)	+= rtc-bq4802.o
 obj-$(CONFIG_RTC_DRV_CMOS)	+= rtc-cmos.o
 obj-$(CONFIG_RTC_DRV_COH901331)	+= rtc-coh901331.o
 obj-$(CONFIG_RTC_DRV_DA9052)	+= rtc-da9052.o
+obj-$(CONFIG_RTC_DRV_DA906X)	+= rtc-da906x.o
 obj-$(CONFIG_RTC_DRV_DAVINCI)	+= rtc-davinci.o
 obj-$(CONFIG_RTC_DRV_DM355EVM)	+= rtc-dm355evm.o
 obj-$(CONFIG_RTC_DRV_VRTC)	+= rtc-mrst.o
diff --git a/drivers/rtc/rtc-da906x.c b/drivers/rtc/rtc-da906x.c
new file mode 100644
index 0000000..0b4fecc
--- /dev/null
+++ b/drivers/rtc/rtc-da906x.c
@@ -0,0 +1,379 @@
+/*
+ * Real Time Clock driver for DA906x PMIC family
+ *
+ * Copyright 2012 Dialog Semiconductors Ltd.
+ *
+ * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.com>
+ *
+ *  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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mfd/da906x/registers.h>
+#include <linux/mfd/da906x/core.h>
+
+#define YEARS_TO_DA906X(year)		((year) - 100)
+#define MONTHS_TO_DA906X(month)		((month) + 1)
+#define YEARS_FROM_DA906X(year)		((year) + 100)
+#define MONTHS_FROM_DA906X(month)	((month) - 1)
+
+#define CLOCK_DATA_LEN	(DA906X_REG_COUNT_Y - DA906X_REG_COUNT_S + 1)
+#define ALARM_DATA_LEN	(DA906X_REG_ALARM_Y - DA906X_REG_ALARM_MI + 1)
+enum {
+	DATA_SEC = 0,
+	DATA_MIN,
+	DATA_HOUR,
+	DATA_DAY,
+	DATA_MONTH,
+	DATA_YEAR,
+};
+
+struct da906x_rtc {
+	struct rtc_device	*rtc_dev;
+	struct da906x		*hw;
+	int			irq_alarm;
+	int			irq_tick;
+
+	/* Config flag */
+	int			tick_wake;
+
+	/* Used to expand alarm precision from minutes up to seconds
+	   using hardware ticks */
+	unsigned int		alarmSecs;
+	unsigned int		alarmTicks;
+};
+
+static void da906x_data_to_tm(u8 *data, struct rtc_time *tm)
+{
+	tm->tm_sec = data[DATA_SEC] & DA906X_COUNT_SEC_MASK;
+	tm->tm_min = data[DATA_MIN] & DA906X_COUNT_MIN_MASK;
+	tm->tm_hour = data[DATA_HOUR] & DA906X_COUNT_HOUR_MASK;
+	tm->tm_mday = data[DATA_DAY] & DA906X_COUNT_DAY_MASK;
+	tm->tm_mon = MONTHS_FROM_DA906X(data[DATA_MONTH] &
+					 DA906X_COUNT_MONTH_MASK);
+	tm->tm_year = YEARS_FROM_DA906X(data[DATA_YEAR] &
+					 DA906X_COUNT_YEAR_MASK);
+}
+
+static void da906x_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+	data[DATA_SEC] &= ~DA906X_COUNT_SEC_MASK;
+	data[DATA_SEC] |= tm->tm_sec & DA906X_COUNT_SEC_MASK;
+	data[DATA_MIN] &= ~DA906X_COUNT_MIN_MASK;
+	data[DATA_MIN] |= tm->tm_min & DA906X_COUNT_MIN_MASK;
+	data[DATA_HOUR] &= ~DA906X_COUNT_HOUR_MASK;
+	data[DATA_HOUR] |= tm->tm_hour & DA906X_COUNT_HOUR_MASK;
+	data[DATA_DAY] &= ~DA906X_COUNT_DAY_MASK;
+	data[DATA_DAY] |= tm->tm_mday & DA906X_COUNT_DAY_MASK;
+	data[DATA_MONTH] &= ~DA906X_COUNT_MONTH_MASK;
+	data[DATA_MONTH] |= MONTHS_TO_DA906X(tm->tm_mon) &
+			    DA906X_COUNT_MONTH_MASK;
+	data[DATA_YEAR] &= ~DA906X_COUNT_YEAR_MASK;
+	data[DATA_YEAR] |= YEARS_TO_DA906X(tm->tm_year) &
+			   DA906X_COUNT_YEAR_MASK;
+}
+
+#define DA906X_ALARM_DELAY	INT_MAX
+static int da906x_rtc_test_delay(struct rtc_time *alarm, struct rtc_time *cur)
+{
+	unsigned long a_time, c_time;
+
+	rtc_tm_to_time(alarm, &a_time);
+	rtc_tm_to_time(cur, &c_time);
+
+	/* Alarm time has already passed */
+	if (a_time < c_time)
+		return -1;
+
+	/* If alarm is set for current minute, return ticks to count down.
+	   If alarm is set for following minutes, return DA906X_ALARM_DELAY
+	   to set alarm first.
+	   But when it is less than 2 seconds for the former to become true,
+	   return ticks, because alarm needs some time to synchronise. */
+	if (a_time - c_time < alarm->tm_sec + 2)
+		return a_time - c_time;
+	else
+		return DA906X_ALARM_DELAY;
+}
+
+static int da906x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct da906x_rtc *rtc = dev_get_drvdata(dev);
+	u8 data[CLOCK_DATA_LEN];
+	int ret;
+
+	ret = da906x_block_read(rtc->hw,
+				DA906X_REG_COUNT_S, CLOCK_DATA_LEN, data);
+	if (ret < 0)
+		return ret;
+
+	/* Check, if RTC logic is initialised */
+	if (!(data[DATA_SEC] & DA906X_RTC_READ))
+		return -EBUSY;
+
+	da906x_data_to_tm(data, tm);
+
+	return rtc_valid_tm(tm);
+}
+
+static int da906x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct da906x_rtc *rtc = dev_get_drvdata(dev);
+	u8 data[CLOCK_DATA_LEN] = { [0 ... (CLOCK_DATA_LEN - 1)] = 0 };
+	int ret;
+
+	da906x_tm_to_data(tm, data);
+
+	ret = da906x_block_write(rtc->hw,
+				 DA906X_REG_COUNT_S, CLOCK_DATA_LEN, data);
+
+	return ret;
+}
+
+static int da906x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct da906x_rtc *rtc = dev_get_drvdata(dev);
+	u8 data[CLOCK_DATA_LEN];
+	int ret;
+
+	ret = da906x_block_read(rtc->hw, DA906X_REG_ALARM_MI, ALARM_DATA_LEN,
+				&data[DATA_MIN]);
+	if (ret < 0)
+		return ret;
+
+	da906x_data_to_tm(data, &alrm->time);
+	alrm->time.tm_sec = rtc->alarmSecs;
+	alrm->enabled = !!(data[DATA_YEAR] & DA906X_ALARM_ON);
+
+	/* If there is no ticks left to count down and RTC event is
+	   not processed yet, indicate pending */
+	if (rtc->alarmTicks == 0) {
+		ret = da906x_reg_read(rtc->hw, DA906X_REG_EVENT_A);
+		if (ret < 0)
+			return ret;
+		if (ret & (DA906X_E_ALARM | DA906X_E_TICK))
+			alrm->pending = 1;
+	} else {
+		alrm->pending = 0;
+	}
+
+	return 0;
+}
+
+static int da906x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct da906x_rtc *rtc = dev_get_drvdata(dev);
+	u8 data[CLOCK_DATA_LEN] = { [0 ... (CLOCK_DATA_LEN - 1)] = 0 };
+	struct rtc_time cur_tm;
+	int cmp_val;
+	int ret;
+
+	data[DATA_MIN] = DA906X_ALARM_STATUS_ALARM;
+	data[DATA_MONTH] = DA906X_TICK_TYPE_SEC;
+	if (rtc->tick_wake)
+		data[DATA_MONTH] |= DA906X_TICK_WAKE;
+
+	ret = da906x_rtc_read_time(dev, &cur_tm);
+	if (ret < 0)
+		return ret;
+
+	if (alrm->enabled) {
+		cmp_val = da906x_rtc_test_delay(&alrm->time, &cur_tm);
+		if (cmp_val == DA906X_ALARM_DELAY) {
+			/* Set alarm for longer delay */
+			data[DATA_YEAR] |= DA906X_ALARM_ON;
+		} else if (cmp_val > 0) {
+			/* Count ticks for shorter delay */
+			rtc->alarmTicks = cmp_val - 1;
+			data[DATA_YEAR] |= DA906X_TICK_ON;
+		} else if (cmp_val == 0) {
+			/* Just about time - report event */
+			rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+		}
+	}
+
+	da906x_tm_to_data(&alrm->time, data);
+	rtc->alarmSecs = alrm->time.tm_sec;
+
+	return da906x_block_write(rtc->hw, DA906X_REG_ALARM_MI, ALARM_DATA_LEN,
+				 &data[DATA_MIN]);
+}
+
+static int da906x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct da906x_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_wkalrm alrm;
+	int ret;
+
+	ret = da906x_reg_read(rtc->hw, DATA_YEAR);
+	if (ret < 0)
+		return ret;
+
+	if (enabled) {
+		/* Enable alarm, if it is not enabled already */
+		if (!(ret & (DA906X_ALARM_ON | DA906X_TICK_ON))) {
+			ret = da906x_rtc_read_alarm(dev, &alrm);
+			if (ret < 0)
+				return ret;
+
+			alrm.enabled = 1;
+			ret = da906x_rtc_set_alarm(dev, &alrm);
+		}
+	} else {
+		ret = da906x_reg_clear_bits(rtc->hw, DA906X_REG_ALARM_Y,
+					    DA906X_ALARM_ON);
+	}
+
+	return ret;
+}
+
+/* On alarm interrupt, start to count ticks to enable seconds precision
+   (if alarm seconds != 0). */
+static irqreturn_t da906x_alarm_event(int irq, void *data)
+{
+	struct da906x_rtc *rtc = data;
+
+	if (rtc->alarmSecs) {
+		rtc->alarmTicks = rtc->alarmSecs - 1;
+		da906x_reg_update(rtc->hw, DA906X_REG_ALARM_Y,
+				  DA906X_ALARM_ON | DA906X_TICK_ON,
+				  DA906X_TICK_ON);
+	} else {
+		da906x_reg_clear_bits(rtc->hw, DA906X_REG_ALARM_Y,
+				      DA906X_ALARM_ON);
+		rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* On tick interrupt, count down seconds left to timeout */
+static irqreturn_t da906x_tick_event(int irq, void *data)
+{
+	struct da906x_rtc *rtc = data;
+
+	if (rtc->alarmTicks-- == 0) {
+		da906x_reg_clear_bits(rtc->hw,
+				      DA906X_REG_ALARM_Y, DA906X_TICK_ON);
+		rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_UF);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops da906x_rtc_ops = {
+	.read_time = da906x_rtc_read_time,
+	.set_time = da906x_rtc_set_time,
+	.read_alarm = da906x_rtc_read_alarm,
+	.set_alarm = da906x_rtc_set_alarm,
+	.alarm_irq_enable = da906x_rtc_alarm_irq_enable,
+};
+
+static __devinit int da906x_rtc_probe(struct platform_device *pdev)
+{
+	struct da906x *da906x = dev_get_drvdata(pdev->dev.parent);
+	struct da906x_rtc *rtc;
+	int ret;
+	int alarm_mo;
+
+	/* Enable RTC hardware */
+	ret = da906x_reg_set_bits(da906x, DA906X_REG_CONTROL_E, DA906X_RTC_EN);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to enable RTC.\n");
+		return ret;
+	}
+
+	ret = da906x_reg_set_bits(da906x, DA906X_REG_EN_32K, DA906X_CRYSTAL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to run 32 KHz OSC.\n");
+		return ret;
+	}
+
+	ret = da906x_reg_read(da906x, DA906X_REG_ALARM_MO);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to read RTC register.\n");
+		return ret;
+	}
+	alarm_mo = ret;
+
+	/* Register RTC device */
+	rtc = devm_kzalloc(&pdev->dev, sizeof *rtc, GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, rtc);
+
+	rtc->hw = da906x;
+	rtc->rtc_dev = rtc_device_register(DA906X_DRVNAME_RTC, &pdev->dev,
+					   &da906x_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc->rtc_dev)) {
+		dev_err(&pdev->dev, "Failed to register RTC device: %ld\n",
+			PTR_ERR(rtc->rtc_dev));
+		return PTR_ERR(rtc->rtc_dev);
+	}
+
+	if (alarm_mo & DA906X_TICK_WAKE)
+		rtc->tick_wake = 1;
+
+	/* Register interrupts. Complain on errors but let device
+	   to be registered at least for date/time. */
+	rtc->irq_alarm = platform_get_irq_byname(pdev, "ALARM");
+	ret = request_threaded_irq(rtc->irq_alarm, NULL, da906x_alarm_event,
+				IRQF_TRIGGER_LOW | IRQF_ONESHOT, "ALARM", rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request ALARM IRQ.\n");
+		rtc->irq_alarm = -ENXIO;
+		return 0;
+	}
+
+	rtc->irq_tick = platform_get_irq_byname(pdev, "TICK");
+	ret = request_threaded_irq(rtc->irq_tick, NULL, da906x_tick_event,
+			IRQF_TRIGGER_RISING | IRQF_ONESHOT, "TICK", rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request TICK IRQ.\n");
+		rtc->irq_tick = -ENXIO;
+	}
+
+	return 0;
+}
+
+static int __devexit da906x_rtc_remove(struct platform_device *pdev)
+{
+	struct da906x_rtc *rtc = platform_get_drvdata(pdev);
+
+	if (rtc->irq_alarm >= 0)
+		free_irq(rtc->irq_alarm, rtc);
+
+	if (rtc->irq_tick >= 0)
+		free_irq(rtc->irq_tick, rtc);
+
+	rtc_device_unregister(rtc->rtc_dev);
+	return 0;
+}
+
+static struct platform_driver da906x_rtc_driver = {
+	.probe		= da906x_rtc_probe,
+	.remove		= __devexit_p(da906x_rtc_remove),
+	.driver		= {
+		.name	= DA906X_DRVNAME_RTC,
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(da906x_rtc_driver);
+
+/* Module information */
+MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>");
+MODULE_DESCRIPTION("DA906x RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DA906X_DRVNAME_RTC);
-- 
1.7.0.4


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2012-08-29 13:32 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-11 12:10 [PATCH] regulator: Fix bug in regulator_mode_to_status() core function Krystian Garbaciak
2012-07-11 13:28 ` Mark Brown
2012-07-11 14:18   ` Krystian Garbaciak
2012-07-11 14:26     ` Mark Brown
2012-07-11 15:13     ` Krystian Garbaciak
2012-07-11 17:42       ` Mark Brown
2012-07-12 10:50         ` [PATCH] regulator: Fix a typo " Krystian Garbaciak
2012-07-12 12:53           ` [PATCH] regulator: Add REGULATOR_STATUS_UNDEFINED Krystian Garbaciak
2012-07-12 17:20             ` Mark Brown
2012-07-12 17:18           ` [PATCH] regulator: Fix a typo in regulator_mode_to_status() core function Mark Brown
2012-07-12 17:20           ` Mark Brown
2012-08-24 14:00 [RFC PATCH 3/8] rtc: Add RTC driver for DA906x PMIC Krystian Garbaciak
2012-08-24 14:05 ` [RFC PATCH 4/8] hwmon: Add DA906x hardware monitoring support Krystian Garbaciak
2012-08-24 18:45   ` Guenter Roeck
2012-08-29 13:25     ` [PATCH] regulator: Fix bug in regulator_mode_to_status() core function Krystian Garbaciak

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.