Core: - Coding style and whitespace fixes (interface, Makefile and Kconfig) - New rtc_tm_sub() helper - New CONFIG_RTC_SYSTOHC_DEVICE option - Removed rtc_set_mmss() New drivers: - Mediatek MT6397 - Cortina Gemini Drivers: - Year 2106 fixes for isl1208, pcf8563 and sunxi - update author email for at32ap700x and efi - ds1307: alarm fix - efi: use correct EFI 'epoch' - hym8563: make irq optional - imxdi: cleanups and better handling of the security/tamper monitoring - snvs: fix wakealarm - Compilation fixes or warning removal for gemini, mt6397, palmas, pfc8563 - Trivial cleanups for ab8500, ds1216, ds1286, ds1672, ep93xx, hid-sensor-time, max6900, max8998, max77686, max77802, mc13xxx, mv, mxc, s3c, spear, v3020 - Kconfig fixes for stmp3xxx and xgene -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJVi7VHAAoJEKbNnwlvZCyzv+YP/1sMAsrusvx3cUEmhJurNk5H +vPUdogbr8df9voLkHqTaK1B5+LrL5EXYu+y/6VOwaLIiYv7P9k/eOeZ44fraidc H7xnDQ7cOGzE5LpjPg8+o56rh3rGDF1UdZBwbFTgfv4gmZi+GAf+2MEUam+dVVtF K9+7Xu77A6EgW+h2uHix8GVtflHdRk/Vxjxl0uOR9BM9O883N5dYYZ7sjenfG65D dctAEo+U1xUCw3GCIQ63DvD0joThQk7IB9xJmK/lE5nIv4tbGp5PV8VSNkjKxbFz 1wa9SR1dhBdjbJ6ZeHh8N5EJ0d5MrssW1blsb7/06MHTu0KYgCndU5EEDOE2pVqD GJooYa6RsNqz/KElJ7/0z4T+DJxW7BWaZYRIwv4jpuA4/Y74O5FDw0ULOqxhB8zd kVgfWb0feNQikaZio8gOyzIBkw3CBZBu+f6HMJGaeYWSzTNsDLgA8ee7mUawMP31 ljOxOuPMiTGUbAczK9URo/acZYWNyCOQdm85FxCuwCuPe48m7CyTU6wWBX0Vwj5q y3sHH5stcykCesneNj+IJ8v/knqo9d6M43CbCbwBeo0DJskuXF6jjQ9QLBNZP9U0 7qQa7isL3j3J7T5EouJIBQH2yy4SFiffVOZOPbJ8l+iRUPZUMdOEs2VkQs8BKIZ+ znu/Ov941h8/HbyI6rcl =mugF -----END PGP SIGNATURE----- Merge tag 'rtc-v4.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "Core: - Coding style and whitespace fixes (interface, Makefile and Kconfig) - New rtc_tm_sub() helper - New CONFIG_RTC_SYSTOHC_DEVICE option - Removed rtc_set_mmss() New drivers: - Mediatek MT6397 - Cortina Gemini Drivers: - Year 2106 fixes for isl1208, pcf8563 and sunxi - update author email for at32ap700x and efi - ds1307: alarm fix - efi: use correct EFI 'epoch' - hym8563: make irq optional - imxdi: cleanups and better handling of the security/tamper monitoring - snvs: fix wakealarm - Compilation fixes or warning removal for gemini, mt6397, palmas, pfc8563 - Trivial cleanups for ab8500, ds1216, ds1286, ds1672, ep93xx, hid-sensor-time, max6900, max8998, max77686, max77802, mc13xxx, mv, mxc, s3c, spear, v3020 - Kconfig fixes for stmp3xxx and xgene" * tag 'rtc-v4.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (48 commits) rtc: remove useless I2C dependencies rtc: whitespace fixes rtc: Properly sort Makefile MAINTAINERS: Add RTC subsystem repository rtc: pfc8563: fix uninitialized variable warning rtc: ds1307: Enable the mcp794xx alarm after programming time rtc: hym8563: make the irq optional rtc: gemini: fix cocci warnings rtc: mv: correct 24 hour error message rtc: mv: use BIT() rtc: efi: use correct EFI 'epoch' rtc: interface: Remove rtc_set_mmss() sparc: time: Replace update_persistent_clock() with CONFIG_RTC_SYSTOHC rtc: NTP: Add CONFIG_RTC_SYSTOHC_DEVICE for NTP synchronization rtc: sunxi: Replace deprecated rtc_tm_to_time() rtc: isl1208: Replace deprecated rtc_tm_to_time() rtc: Introduce rtc_tm_sub() helper function rtc: pcf8563: Replace deprecated rtc_time_to_tm() and rtc_tm_to_time() rtc: palmas: Initialise bb_charging flag before using it rtc: simplify use of devm_ioremap_resource ...tirimbino
commit
c13c810063
@ -0,0 +1,175 @@ |
||||
/*
|
||||
* Gemini OnChip RTC |
||||
* |
||||
* Copyright (C) 2009 Janos Laube <janos.dev@gmail.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. |
||||
* |
||||
* 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. |
||||
* |
||||
* Original code for older kernel 2.6.15 are from Stormlinksemi |
||||
* first update from Janos Laube for > 2.6.29 kernels |
||||
* |
||||
* checkpatch fixes and usage of rtc-lib code |
||||
* Hans Ulli Kroll <ulli.kroll@googlemail.com> |
||||
*/ |
||||
|
||||
#include <linux/rtc.h> |
||||
#include <linux/io.h> |
||||
#include <linux/slab.h> |
||||
#include <linux/platform_device.h> |
||||
#include <linux/kernel.h> |
||||
#include <linux/module.h> |
||||
|
||||
#define DRV_NAME "rtc-gemini" |
||||
#define DRV_VERSION "0.2" |
||||
|
||||
MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>"); |
||||
MODULE_DESCRIPTION("RTC driver for Gemini SoC"); |
||||
MODULE_LICENSE("GPL"); |
||||
MODULE_ALIAS("platform:" DRV_NAME); |
||||
|
||||
struct gemini_rtc { |
||||
struct rtc_device *rtc_dev; |
||||
void __iomem *rtc_base; |
||||
int rtc_irq; |
||||
}; |
||||
|
||||
enum gemini_rtc_offsets { |
||||
GEMINI_RTC_SECOND = 0x00, |
||||
GEMINI_RTC_MINUTE = 0x04, |
||||
GEMINI_RTC_HOUR = 0x08, |
||||
GEMINI_RTC_DAYS = 0x0C, |
||||
GEMINI_RTC_ALARM_SECOND = 0x10, |
||||
GEMINI_RTC_ALARM_MINUTE = 0x14, |
||||
GEMINI_RTC_ALARM_HOUR = 0x18, |
||||
GEMINI_RTC_RECORD = 0x1C, |
||||
GEMINI_RTC_CR = 0x20 |
||||
}; |
||||
|
||||
static irqreturn_t gemini_rtc_interrupt(int irq, void *dev) |
||||
{ |
||||
return IRQ_HANDLED; |
||||
} |
||||
|
||||
/*
|
||||
* Looks like the RTC in the Gemini SoC is (totaly) broken |
||||
* We can't read/write directly the time from RTC registers. |
||||
* We must do some "offset" calculation to get the real time |
||||
* |
||||
* This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does |
||||
* the same thing, without the rtc-lib.c calls. |
||||
*/ |
||||
|
||||
static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm) |
||||
{ |
||||
struct gemini_rtc *rtc = dev_get_drvdata(dev); |
||||
|
||||
unsigned int days, hour, min, sec; |
||||
unsigned long offset, time; |
||||
|
||||
sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); |
||||
min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); |
||||
hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); |
||||
days = readl(rtc->rtc_base + GEMINI_RTC_DAYS); |
||||
offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD); |
||||
|
||||
time = offset + days * 86400 + hour * 3600 + min * 60 + sec; |
||||
|
||||
rtc_time_to_tm(time, tm); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm) |
||||
{ |
||||
struct gemini_rtc *rtc = dev_get_drvdata(dev); |
||||
unsigned int sec, min, hour, day; |
||||
unsigned long offset, time; |
||||
|
||||
if (tm->tm_year >= 2148) /* EPOCH Year + 179 */ |
||||
return -EINVAL; |
||||
|
||||
rtc_tm_to_time(tm, &time); |
||||
|
||||
sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); |
||||
min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); |
||||
hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); |
||||
day = readl(rtc->rtc_base + GEMINI_RTC_DAYS); |
||||
|
||||
offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); |
||||
|
||||
writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD); |
||||
writel(0x01, rtc->rtc_base + GEMINI_RTC_CR); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static struct rtc_class_ops gemini_rtc_ops = { |
||||
.read_time = gemini_rtc_read_time, |
||||
.set_time = gemini_rtc_set_time, |
||||
}; |
||||
|
||||
static int gemini_rtc_probe(struct platform_device *pdev) |
||||
{ |
||||
struct gemini_rtc *rtc; |
||||
struct device *dev = &pdev->dev; |
||||
struct resource *res; |
||||
int ret; |
||||
|
||||
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); |
||||
if (unlikely(!rtc)) |
||||
return -ENOMEM; |
||||
platform_set_drvdata(pdev, rtc); |
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
||||
if (!res) |
||||
return -ENODEV; |
||||
|
||||
rtc->rtc_irq = res->start; |
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
||||
if (!res) |
||||
return -ENODEV; |
||||
|
||||
rtc->rtc_base = devm_ioremap(dev, res->start, |
||||
resource_size(res)); |
||||
|
||||
ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt, |
||||
IRQF_SHARED, pdev->name, dev); |
||||
if (unlikely(ret)) |
||||
return ret; |
||||
|
||||
rtc->rtc_dev = rtc_device_register(pdev->name, dev, |
||||
&gemini_rtc_ops, THIS_MODULE); |
||||
if (likely(IS_ERR(rtc->rtc_dev))) |
||||
return PTR_ERR(rtc->rtc_dev); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int gemini_rtc_remove(struct platform_device *pdev) |
||||
{ |
||||
struct gemini_rtc *rtc = platform_get_drvdata(pdev); |
||||
|
||||
rtc_device_unregister(rtc->rtc_dev); |
||||
platform_set_drvdata(pdev, NULL); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static struct platform_driver gemini_rtc_driver = { |
||||
.driver = { |
||||
.name = DRV_NAME, |
||||
}, |
||||
.probe = gemini_rtc_probe, |
||||
.remove = gemini_rtc_remove, |
||||
}; |
||||
|
||||
module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe); |
@ -0,0 +1,395 @@ |
||||
/*
|
||||
* Copyright (c) 2014-2015 MediaTek Inc. |
||||
* Author: Tianping.Fang <tianping.fang@mediatek.com> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
*/ |
||||
|
||||
#include <linux/delay.h> |
||||
#include <linux/init.h> |
||||
#include <linux/module.h> |
||||
#include <linux/regmap.h> |
||||
#include <linux/rtc.h> |
||||
#include <linux/irqdomain.h> |
||||
#include <linux/platform_device.h> |
||||
#include <linux/of_address.h> |
||||
#include <linux/of_irq.h> |
||||
#include <linux/io.h> |
||||
#include <linux/mfd/mt6397/core.h> |
||||
|
||||
#define RTC_BBPU 0x0000 |
||||
#define RTC_BBPU_CBUSY BIT(6) |
||||
|
||||
#define RTC_WRTGR 0x003c |
||||
|
||||
#define RTC_IRQ_STA 0x0002 |
||||
#define RTC_IRQ_STA_AL BIT(0) |
||||
#define RTC_IRQ_STA_LP BIT(3) |
||||
|
||||
#define RTC_IRQ_EN 0x0004 |
||||
#define RTC_IRQ_EN_AL BIT(0) |
||||
#define RTC_IRQ_EN_ONESHOT BIT(2) |
||||
#define RTC_IRQ_EN_LP BIT(3) |
||||
#define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL) |
||||
|
||||
#define RTC_AL_MASK 0x0008 |
||||
#define RTC_AL_MASK_DOW BIT(4) |
||||
|
||||
#define RTC_TC_SEC 0x000a |
||||
/* Min, Hour, Dom... register offset to RTC_TC_SEC */ |
||||
#define RTC_OFFSET_SEC 0 |
||||
#define RTC_OFFSET_MIN 1 |
||||
#define RTC_OFFSET_HOUR 2 |
||||
#define RTC_OFFSET_DOM 3 |
||||
#define RTC_OFFSET_DOW 4 |
||||
#define RTC_OFFSET_MTH 5 |
||||
#define RTC_OFFSET_YEAR 6 |
||||
#define RTC_OFFSET_COUNT 7 |
||||
|
||||
#define RTC_AL_SEC 0x0018 |
||||
|
||||
#define RTC_PDN2 0x002e |
||||
#define RTC_PDN2_PWRON_ALARM BIT(4) |
||||
|
||||
#define RTC_MIN_YEAR 1968 |
||||
#define RTC_BASE_YEAR 1900 |
||||
#define RTC_NUM_YEARS 128 |
||||
#define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR) |
||||
|
||||
struct mt6397_rtc { |
||||
struct device *dev; |
||||
struct rtc_device *rtc_dev; |
||||
struct mutex lock; |
||||
struct regmap *regmap; |
||||
int irq; |
||||
u32 addr_base; |
||||
}; |
||||
|
||||
static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc) |
||||
{ |
||||
unsigned long timeout = jiffies + HZ; |
||||
int ret; |
||||
u32 data; |
||||
|
||||
ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_WRTGR, 1); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
while (1) { |
||||
ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_BBPU, |
||||
&data); |
||||
if (ret < 0) |
||||
break; |
||||
if (!(data & RTC_BBPU_CBUSY)) |
||||
break; |
||||
if (time_after(jiffies, timeout)) { |
||||
ret = -ETIMEDOUT; |
||||
break; |
||||
} |
||||
cpu_relax(); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static irqreturn_t mtk_rtc_irq_handler_thread(int irq, void *data) |
||||
{ |
||||
struct mt6397_rtc *rtc = data; |
||||
u32 irqsta, irqen; |
||||
int ret; |
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_STA, &irqsta); |
||||
if ((ret >= 0) && (irqsta & RTC_IRQ_STA_AL)) { |
||||
rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); |
||||
irqen = irqsta & ~RTC_IRQ_EN_AL; |
||||
mutex_lock(&rtc->lock); |
||||
if (regmap_write(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, |
||||
irqen) < 0) |
||||
mtk_rtc_write_trigger(rtc); |
||||
mutex_unlock(&rtc->lock); |
||||
|
||||
return IRQ_HANDLED; |
||||
} |
||||
|
||||
return IRQ_NONE; |
||||
} |
||||
|
||||
static int __mtk_rtc_read_time(struct mt6397_rtc *rtc, |
||||
struct rtc_time *tm, int *sec) |
||||
{ |
||||
int ret; |
||||
u16 data[RTC_OFFSET_COUNT]; |
||||
|
||||
mutex_lock(&rtc->lock); |
||||
ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, |
||||
data, RTC_OFFSET_COUNT); |
||||
if (ret < 0) |
||||
goto exit; |
||||
|
||||
tm->tm_sec = data[RTC_OFFSET_SEC]; |
||||
tm->tm_min = data[RTC_OFFSET_MIN]; |
||||
tm->tm_hour = data[RTC_OFFSET_HOUR]; |
||||
tm->tm_mday = data[RTC_OFFSET_DOM]; |
||||
tm->tm_mon = data[RTC_OFFSET_MTH]; |
||||
tm->tm_year = data[RTC_OFFSET_YEAR]; |
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, sec); |
||||
exit: |
||||
mutex_unlock(&rtc->lock); |
||||
return ret; |
||||
} |
||||
|
||||
static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm) |
||||
{ |
||||
time64_t time; |
||||
struct mt6397_rtc *rtc = dev_get_drvdata(dev); |
||||
int days, sec, ret; |
||||
|
||||
do { |
||||
ret = __mtk_rtc_read_time(rtc, tm, &sec); |
||||
if (ret < 0) |
||||
goto exit; |
||||
} while (sec < tm->tm_sec); |
||||
|
||||
/* HW register use 7 bits to store year data, minus
|
||||
* RTC_MIN_YEAR_OFFSET before write year data to register, and plus |
||||
* RTC_MIN_YEAR_OFFSET back after read year from register |
||||
*/ |
||||
tm->tm_year += RTC_MIN_YEAR_OFFSET; |
||||
|
||||
/* HW register start mon from one, but tm_mon start from zero. */ |
||||
tm->tm_mon--; |
||||
time = rtc_tm_to_time64(tm); |
||||
|
||||
/* rtc_tm_to_time64 covert Gregorian date to seconds since
|
||||
* 01-01-1970 00:00:00, and this date is Thursday. |
||||
*/ |
||||
days = div_s64(time, 86400); |
||||
tm->tm_wday = (days + 4) % 7; |
||||
|
||||
exit: |
||||
return ret; |
||||
} |
||||
|
||||
static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm) |
||||
{ |
||||
struct mt6397_rtc *rtc = dev_get_drvdata(dev); |
||||
int ret; |
||||
u16 data[RTC_OFFSET_COUNT]; |
||||
|
||||
tm->tm_year -= RTC_MIN_YEAR_OFFSET; |
||||
tm->tm_mon++; |
||||
|
||||
data[RTC_OFFSET_SEC] = tm->tm_sec; |
||||
data[RTC_OFFSET_MIN] = tm->tm_min; |
||||
data[RTC_OFFSET_HOUR] = tm->tm_hour; |
||||
data[RTC_OFFSET_DOM] = tm->tm_mday; |
||||
data[RTC_OFFSET_MTH] = tm->tm_mon; |
||||
data[RTC_OFFSET_YEAR] = tm->tm_year; |
||||
|
||||
mutex_lock(&rtc->lock); |
||||
ret = regmap_bulk_write(rtc->regmap, rtc->addr_base + RTC_TC_SEC, |
||||
data, RTC_OFFSET_COUNT); |
||||
if (ret < 0) |
||||
goto exit; |
||||
|
||||
/* Time register write to hardware after call trigger function */ |
||||
ret = mtk_rtc_write_trigger(rtc); |
||||
|
||||
exit: |
||||
mutex_unlock(&rtc->lock); |
||||
return ret; |
||||
} |
||||
|
||||
static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
||||
{ |
||||
struct rtc_time *tm = &alm->time; |
||||
struct mt6397_rtc *rtc = dev_get_drvdata(dev); |
||||
u32 irqen, pdn2; |
||||
int ret; |
||||
u16 data[RTC_OFFSET_COUNT]; |
||||
|
||||
mutex_lock(&rtc->lock); |
||||
ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, &irqen); |
||||
if (ret < 0) |
||||
goto err_exit; |
||||
ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_PDN2, &pdn2); |
||||
if (ret < 0) |
||||
goto err_exit; |
||||
|
||||
ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC, |
||||
data, RTC_OFFSET_COUNT); |
||||
if (ret < 0) |
||||
goto err_exit; |
||||
|
||||
alm->enabled = !!(irqen & RTC_IRQ_EN_AL); |
||||
alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM); |
||||
mutex_unlock(&rtc->lock); |
||||
|
||||
tm->tm_sec = data[RTC_OFFSET_SEC]; |
||||
tm->tm_min = data[RTC_OFFSET_MIN]; |
||||
tm->tm_hour = data[RTC_OFFSET_HOUR]; |
||||
tm->tm_mday = data[RTC_OFFSET_DOM]; |
||||
tm->tm_mon = data[RTC_OFFSET_MTH]; |
||||
tm->tm_year = data[RTC_OFFSET_YEAR]; |
||||
|
||||
tm->tm_year += RTC_MIN_YEAR_OFFSET; |
||||
tm->tm_mon--; |
||||
|
||||
return 0; |
||||
err_exit: |
||||
mutex_unlock(&rtc->lock); |
||||
return ret; |
||||
} |
||||
|
||||
static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) |
||||
{ |
||||
struct rtc_time *tm = &alm->time; |
||||
struct mt6397_rtc *rtc = dev_get_drvdata(dev); |
||||
int ret; |
||||
u16 data[RTC_OFFSET_COUNT]; |
||||
|
||||
tm->tm_year -= RTC_MIN_YEAR_OFFSET; |
||||
tm->tm_mon++; |
||||
|
||||
data[RTC_OFFSET_SEC] = tm->tm_sec; |
||||
data[RTC_OFFSET_MIN] = tm->tm_min; |
||||
data[RTC_OFFSET_HOUR] = tm->tm_hour; |
||||
data[RTC_OFFSET_DOM] = tm->tm_mday; |
||||
data[RTC_OFFSET_MTH] = tm->tm_mon; |
||||
data[RTC_OFFSET_YEAR] = tm->tm_year; |
||||
|
||||
mutex_lock(&rtc->lock); |
||||
if (alm->enabled) { |
||||
ret = regmap_bulk_write(rtc->regmap, |
||||
rtc->addr_base + RTC_AL_SEC, |
||||
data, RTC_OFFSET_COUNT); |
||||
if (ret < 0) |
||||
goto exit; |
||||
ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_AL_MASK, |
||||
RTC_AL_MASK_DOW); |
||||
if (ret < 0) |
||||
goto exit; |
||||
ret = regmap_update_bits(rtc->regmap, |
||||
rtc->addr_base + RTC_IRQ_EN, |
||||
RTC_IRQ_EN_ONESHOT_AL, |
||||
RTC_IRQ_EN_ONESHOT_AL); |
||||
if (ret < 0) |
||||
goto exit; |
||||
} else { |
||||
ret = regmap_update_bits(rtc->regmap, |
||||
rtc->addr_base + RTC_IRQ_EN, |
||||
RTC_IRQ_EN_ONESHOT_AL, 0); |
||||
if (ret < 0) |
||||
goto exit; |
||||
} |
||||
|
||||
/* All alarm time register write to hardware after calling
|
||||
* mtk_rtc_write_trigger. This can avoid race condition if alarm |
||||
* occur happen during writing alarm time register. |
||||
*/ |
||||
ret = mtk_rtc_write_trigger(rtc); |
||||
exit: |
||||
mutex_unlock(&rtc->lock); |
||||
return ret; |
||||
} |
||||
|
||||
static struct rtc_class_ops mtk_rtc_ops = { |
||||
.read_time = mtk_rtc_read_time, |
||||
.set_time = mtk_rtc_set_time, |
||||
.read_alarm = mtk_rtc_read_alarm, |
||||
.set_alarm = mtk_rtc_set_alarm, |
||||
}; |
||||
|
||||
static int mtk_rtc_probe(struct platform_device *pdev) |
||||
{ |
||||
struct resource *res; |
||||
struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); |
||||
struct mt6397_rtc *rtc; |
||||
int ret; |
||||
|
||||
rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL); |
||||
if (!rtc) |
||||
return -ENOMEM; |
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
||||
rtc->addr_base = res->start; |
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
||||
rtc->irq = irq_create_mapping(mt6397_chip->irq_domain, res->start); |
||||
if (rtc->irq <= 0) |
||||
return -EINVAL; |
||||
|
||||
rtc->regmap = mt6397_chip->regmap; |
||||
rtc->dev = &pdev->dev; |
||||
mutex_init(&rtc->lock); |
||||
|
||||
platform_set_drvdata(pdev, rtc); |
||||
|
||||
ret = request_threaded_irq(rtc->irq, NULL, |
||||
mtk_rtc_irq_handler_thread, |
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, |
||||
"mt6397-rtc", rtc); |
||||
if (ret) { |
||||
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", |
||||
rtc->irq, ret); |
||||
goto out_dispose_irq; |
||||
} |
||||
|
||||
rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev, |
||||
&mtk_rtc_ops, THIS_MODULE); |
||||
if (IS_ERR(rtc->rtc_dev)) { |
||||
dev_err(&pdev->dev, "register rtc device failed\n"); |
||||
ret = PTR_ERR(rtc->rtc_dev); |
||||
goto out_free_irq; |
||||
} |
||||
|
||||
device_init_wakeup(&pdev->dev, 1); |
||||
|
||||
return 0; |
||||
|
||||
out_free_irq: |
||||
free_irq(rtc->irq, rtc->rtc_dev); |
||||
out_dispose_irq: |
||||
irq_dispose_mapping(rtc->irq); |
||||
return ret; |
||||
} |
||||
|
||||
static int mtk_rtc_remove(struct platform_device *pdev) |
||||
{ |
||||
struct mt6397_rtc *rtc = platform_get_drvdata(pdev); |
||||
|
||||
rtc_device_unregister(rtc->rtc_dev); |
||||
free_irq(rtc->irq, rtc->rtc_dev); |
||||
irq_dispose_mapping(rtc->irq); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct of_device_id mt6397_rtc_of_match[] = { |
||||
{ .compatible = "mediatek,mt6397-rtc", }, |
||||
{ } |
||||
}; |
||||
|
||||
static struct platform_driver mtk_rtc_driver = { |
||||
.driver = { |
||||
.name = "mt6397-rtc", |
||||
.of_match_table = mt6397_rtc_of_match, |
||||
}, |
||||
.probe = mtk_rtc_probe, |
||||
.remove = mtk_rtc_remove, |
||||
}; |
||||
|
||||
module_platform_driver(mtk_rtc_driver); |
||||
|
||||
MODULE_LICENSE("GPL v2"); |
||||
MODULE_AUTHOR("Tianping Fang <tianping.fang@mediatek.com>"); |
||||
MODULE_DESCRIPTION("RTC Driver for MediaTek MT6397 PMIC"); |
||||
MODULE_ALIAS("platform:mt6397-rtc"); |
Loading…
Reference in new issue