/drivers/rtc/rtc-s3c.c
822行
static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.driver = {
.name = "s3c-rtc",
.pm = &s3c_rtc_pm_ops,
.of_match_table = of_match_ptr(s3c_rtc_dt_match),
},
};
module_platform_driver(s3c_rtc_driver);
/arch/arm/plat-samsung/devs.c
836行
static struct resource s3c_rtc_resource[] = {
[0] = DEFINE_RES_MEM(S3C24XX_PA_RTC, SZ_256),
[1] = DEFINE_RES_IRQ(IRQ_RTC),
[2] = DEFINE_RES_IRQ(IRQ_TICK),
};
struct platform_device s3c_device_rtc = {
.name = "s3c-rtc", //修改此处 和 s3c_rtc_driver 中的 .name 相同
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};
修改 mach-smdk2440.c 中添加设备
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&dm9000_device_eth,
&s3c_device_rtc, //添加此项
};
make uImage 使用新内核 启动
错误信息
Unable to handle kernel NULL pointer dereference at virtual address 000000c0
pgd = c0004000
[000000c0] *pgd=00000000
Internal error: Oops: 5 [#1] ARM
Modules linked in:
CPU: 0 PID: 1 Comm: swapper Not tainted 4.1.36 #73
Hardware name: SMDK2440
task: c381faa0 ti: c3822000 task.ti: c3822000
PC is at s3c_rtc_probe+0x4c/0x3f0
LR is at s3c_rtc_probe+0x38/0x3f0
经过 printk 大法以后定位到 s3c_rtc_get_data() 出现空指针问题
static const struct of_device_id s3c_rtc_dt_match[];
static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
{
const struct of_device_id *match;
match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
return (struct s3c_rtc_data *)match->data;
}
改为
static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
{
const struct of_device_id *match;
match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
if(NULL != match)
{
return (struct s3c_rtc_data *)match->data;
}
return NULL;
}
改了以后,启动不报空指针错误了。但是还是不能用rtc 。
要自己实现这么一个函数
添加一个函数声明
static struct s3c_rtc_data *get_s3c_2410_data(void);
在最下面添加实现
static struct s3c_rtc_data *get_s3c_2410_data(void)
{
return (struct s3c_rtc_data *)&s3c2410_rtc_data;
}
rtc-s3c.c
/* drivers/rtc/rtc-s3c.c
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* Copyright (c) 2004,2006 Simtec Electronics
* Ben Dooks, ben@simtec.co.uk
* http://armlinux.simtec.co.uk/
*
* 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.
*
* S3C2410/S3C2440/S3C24XX Internal RTC Driver
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "rtc-s3c.h"
struct s3c_rtc {
struct device *dev;
struct rtc_device *rtc;
void \_\_iomem \*base;
struct clk \*rtc\_clk;
struct clk \*rtc\_src\_clk;
bool clk\_disabled;
struct s3c\_rtc\_data \*data;
int irq\_alarm;
int irq\_tick;
spinlock\_t pie\_lock;
spinlock\_t alarm\_clk\_lock;
int ticnt\_save, ticnt\_en\_save;
bool wake\_en;
};
struct s3c_rtc_data {
int max_user_freq;
bool needs_src_clk;
void (\*irq\_handler) (struct s3c\_rtc \*info, int mask);
void (\*set\_freq) (struct s3c\_rtc \*info, int freq);
void (\*enable\_tick) (struct s3c\_rtc \*info, struct seq\_file \*seq);
void (\*select\_tick\_clk) (struct s3c\_rtc \*info);
void (\*save\_tick\_cnt) (struct s3c\_rtc \*info);
void (\*restore\_tick\_cnt) (struct s3c\_rtc \*info);
void (\*enable) (struct s3c\_rtc \*info);
void (\*disable) (struct s3c\_rtc \*info);
};
static struct s3c_rtc_data *get_s3c_2410_data(void);
static void s3c_rtc_enable_clk(struct s3c_rtc *info)
{
unsigned long irq_flags;
spin\_lock\_irqsave(&info->alarm\_clk\_lock, irq\_flags);
if (info->clk\_disabled) {
clk\_enable(info->rtc\_clk);
if (info->data->needs\_src\_clk)
clk\_enable(info->rtc\_src\_clk);
info->clk\_disabled = false;
}
spin\_unlock\_irqrestore(&info->alarm\_clk\_lock, irq\_flags);
}
static void s3c_rtc_disable_clk(struct s3c_rtc *info)
{
unsigned long irq_flags;
spin\_lock\_irqsave(&info->alarm\_clk\_lock, irq\_flags);
if (!info->clk\_disabled) {
if (info->data->needs\_src\_clk)
clk\_disable(info->rtc\_src\_clk);
clk\_disable(info->rtc\_clk);
info->clk\_disabled = true;
}
spin\_unlock\_irqrestore(&info->alarm\_clk\_lock, irq\_flags);
}
/* IRQ Handlers */
static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
{
struct s3c_rtc *info = (struct s3c_rtc *)id;
if (info->data->irq\_handler)
info->data->irq\_handler(info, S3C2410\_INTP\_TIC);
return IRQ\_HANDLED;
}
static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
{
struct s3c_rtc *info = (struct s3c_rtc *)id;
if (info->data->irq\_handler)
info->data->irq\_handler(info, S3C2410\_INTP\_ALM);
return IRQ\_HANDLED;
}
/* Update control registers */
static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int tmp;
dev\_dbg(info->dev, "%s: aie=%d\\n", \_\_func\_\_, enabled);
s3c\_rtc\_enable\_clk(info);
tmp = readb(info->base + S3C2410\_RTCALM) & ~S3C2410\_RTCALM\_ALMEN;
if (enabled)
tmp |= S3C2410\_RTCALM\_ALMEN;
writeb(tmp, info->base + S3C2410\_RTCALM);
s3c\_rtc\_disable\_clk(info);
if (enabled)
s3c\_rtc\_enable\_clk(info);
else
s3c\_rtc\_disable\_clk(info);
return ;
}
/* Set RTC frequency */
static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
{
if (!is_power_of_2(freq))
return -EINVAL;
s3c\_rtc\_enable\_clk(info);
spin\_lock\_irq(&info->pie\_lock);
if (info->data->set\_freq)
info->data->set\_freq(info, freq);
spin\_unlock\_irq(&info->pie\_lock);
s3c\_rtc\_disable\_clk(info);
return ;
}
/* Time read/write */
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int have_retried = ;
s3c\_rtc\_enable\_clk(info);
retry_get_time:
rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON);
rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR);
rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC);
/\* the only way to work out whether the system was mid-update
\* when we read it is to check the second counter, and if it
\* is zero, then we re-try the entire read
\*/
if (rtc\_tm->tm\_sec == && !have\_retried) {
have\_retried = ;
goto retry\_get\_time;
}
rtc\_tm->tm\_sec = bcd2bin(rtc\_tm->tm\_sec);
rtc\_tm->tm\_min = bcd2bin(rtc\_tm->tm\_min);
rtc\_tm->tm\_hour = bcd2bin(rtc\_tm->tm\_hour);
rtc\_tm->tm\_mday = bcd2bin(rtc\_tm->tm\_mday);
rtc\_tm->tm\_mon = bcd2bin(rtc\_tm->tm\_mon);
rtc\_tm->tm\_year = bcd2bin(rtc\_tm->tm\_year);
s3c\_rtc\_disable\_clk(info);
rtc\_tm->tm\_year += ;
dev\_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\\n",
+ rtc\_tm->tm\_year, rtc\_tm->tm\_mon, rtc\_tm->tm\_mday,
rtc\_tm->tm\_hour, rtc\_tm->tm\_min, rtc\_tm->tm\_sec);
rtc\_tm->tm\_mon -= ;
return rtc\_valid\_tm(rtc\_tm);
}
static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
int year = tm->tm_year - ;
dev\_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\\n",
+ tm->tm\_year, tm->tm\_mon, tm->tm\_mday,
tm->tm\_hour, tm->tm\_min, tm->tm\_sec);
/\* we get around y2k by simply not supporting it \*/
if (year < || year >= ) {
dev\_err(dev, "rtc only supports 100 years\\n");
return -EINVAL;
}
s3c\_rtc\_enable\_clk(info);
writeb(bin2bcd(tm->tm\_sec), info->base + S3C2410\_RTCSEC);
writeb(bin2bcd(tm->tm\_min), info->base + S3C2410\_RTCMIN);
writeb(bin2bcd(tm->tm\_hour), info->base + S3C2410\_RTCHOUR);
writeb(bin2bcd(tm->tm\_mday), info->base + S3C2410\_RTCDATE);
writeb(bin2bcd(tm->tm\_mon + ), info->base + S3C2410\_RTCMON);
writeb(bin2bcd(year), info->base + S3C2410\_RTCYEAR);
s3c\_rtc\_disable\_clk(info);
return ;
}
static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *alm_tm = &alrm->time;
unsigned int alm_en;
s3c\_rtc\_enable\_clk(info);
alm\_tm->tm\_sec = readb(info->base + S3C2410\_ALMSEC);
alm\_tm->tm\_min = readb(info->base + S3C2410\_ALMMIN);
alm\_tm->tm\_hour = readb(info->base + S3C2410\_ALMHOUR);
alm\_tm->tm\_mon = readb(info->base + S3C2410\_ALMMON);
alm\_tm->tm\_mday = readb(info->base + S3C2410\_ALMDATE);
alm\_tm->tm\_year = readb(info->base + S3C2410\_ALMYEAR);
alm\_en = readb(info->base + S3C2410\_RTCALM);
s3c\_rtc\_disable\_clk(info);
alrm->enabled = (alm\_en & S3C2410\_RTCALM\_ALMEN) ? : ;
dev\_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\\n",
alm\_en,
+ alm\_tm->tm\_year, alm\_tm->tm\_mon, alm\_tm->tm\_mday,
alm\_tm->tm\_hour, alm\_tm->tm\_min, alm\_tm->tm\_sec);
/\* decode the alarm enable field \*/
if (alm\_en & S3C2410\_RTCALM\_SECEN)
alm\_tm->tm\_sec = bcd2bin(alm\_tm->tm\_sec);
else
alm\_tm->tm\_sec = -;
if (alm\_en & S3C2410\_RTCALM\_MINEN)
alm\_tm->tm\_min = bcd2bin(alm\_tm->tm\_min);
else
alm\_tm->tm\_min = -;
if (alm\_en & S3C2410\_RTCALM\_HOUREN)
alm\_tm->tm\_hour = bcd2bin(alm\_tm->tm\_hour);
else
alm\_tm->tm\_hour = -;
if (alm\_en & S3C2410\_RTCALM\_DAYEN)
alm\_tm->tm\_mday = bcd2bin(alm\_tm->tm\_mday);
else
alm\_tm->tm\_mday = -;
if (alm\_en & S3C2410\_RTCALM\_MONEN) {
alm\_tm->tm\_mon = bcd2bin(alm\_tm->tm\_mon);
alm\_tm->tm\_mon -= ;
} else {
alm\_tm->tm\_mon = -;
}
if (alm\_en & S3C2410\_RTCALM\_YEAREN)
alm\_tm->tm\_year = bcd2bin(alm\_tm->tm\_year);
else
alm\_tm->tm\_year = -;
return ;
}
static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *tm = &alrm->time;
unsigned int alrm_en;
dev\_dbg(dev, "s3c\_rtc\_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\\n",
alrm->enabled,
+ tm->tm\_year, tm->tm\_mon + , tm->tm\_mday,
tm->tm\_hour, tm->tm\_min, tm->tm\_sec);
s3c\_rtc\_enable\_clk(info);
alrm\_en = readb(info->base + S3C2410\_RTCALM) & S3C2410\_RTCALM\_ALMEN;
writeb(0x00, info->base + S3C2410\_RTCALM);
if (tm->tm\_sec < && tm->tm\_sec >= ) {
alrm\_en |= S3C2410\_RTCALM\_SECEN;
writeb(bin2bcd(tm->tm\_sec), info->base + S3C2410\_ALMSEC);
}
if (tm->tm\_min < && tm->tm\_min >= ) {
alrm\_en |= S3C2410\_RTCALM\_MINEN;
writeb(bin2bcd(tm->tm\_min), info->base + S3C2410\_ALMMIN);
}
if (tm->tm\_hour < && tm->tm\_hour >= ) {
alrm\_en |= S3C2410\_RTCALM\_HOUREN;
writeb(bin2bcd(tm->tm\_hour), info->base + S3C2410\_ALMHOUR);
}
dev\_dbg(dev, "setting S3C2410\_RTCALM to %08x\\n", alrm\_en);
writeb(alrm\_en, info->base + S3C2410\_RTCALM);
s3c\_rtc\_disable\_clk(info);
s3c\_rtc\_setaie(dev, alrm->enabled);
return ;
}
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
s3c\_rtc\_enable\_clk(info);
if (info->data->enable\_tick)
info->data->enable\_tick(info, seq);
s3c\_rtc\_disable\_clk(info);
return ;
}
static const struct rtc_class_ops s3c_rtcops = {
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.proc = s3c_rtc_proc,
.alarm_irq_enable = s3c_rtc_setaie,
};
static void s3c24xx_rtc_enable(struct s3c_rtc *info)
{
unsigned int con, tmp;
con = readw(info->base + S3C2410\_RTCCON);
/\* re-enable the device, and check it is ok \*/
if ((con & S3C2410\_RTCCON\_RTCEN) == ) {
dev\_info(info->dev, "rtc disabled, re-enabling\\n");
tmp = readw(info->base + S3C2410\_RTCCON);
writew(tmp | S3C2410\_RTCCON\_RTCEN,
info->base + S3C2410\_RTCCON);
}
if (con & S3C2410\_RTCCON\_CNTSEL) {
dev\_info(info->dev, "removing RTCCON\_CNTSEL\\n");
tmp = readw(info->base + S3C2410\_RTCCON);
writew(tmp & ~S3C2410\_RTCCON\_CNTSEL,
info->base + S3C2410\_RTCCON);
}
if (con & S3C2410\_RTCCON\_CLKRST) {
dev\_info(info->dev, "removing RTCCON\_CLKRST\\n");
tmp = readw(info->base + S3C2410\_RTCCON);
writew(tmp & ~S3C2410\_RTCCON\_CLKRST,
info->base + S3C2410\_RTCCON);
}
}
static void s3c24xx_rtc_disable(struct s3c_rtc *info)
{
unsigned int con;
con = readw(info->base + S3C2410\_RTCCON);
con &= ~S3C2410\_RTCCON\_RTCEN;
writew(con, info->base + S3C2410\_RTCCON);
con = readb(info->base + S3C2410\_TICNT);
con &= ~S3C2410\_TICNT\_ENABLE;
writeb(con, info->base + S3C2410\_TICNT);
}
static void s3c6410_rtc_disable(struct s3c_rtc *info)
{
unsigned int con;
con = readw(info->base + S3C2410\_RTCCON);
con &= ~S3C64XX\_RTCCON\_TICEN;
con &= ~S3C2410\_RTCCON\_RTCEN;
writew(con, info->base + S3C2410\_RTCCON);
}
static int s3c_rtc_remove(struct platform_device *pdev)
{
struct s3c_rtc *info = platform_get_drvdata(pdev);
s3c\_rtc\_setaie(info->dev, );
clk\_unprepare(info->rtc\_clk);
info->rtc\_clk = NULL;
return ;
}
static const struct of_device_id s3c_rtc_dt_match[];
static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
{
const struct of_device_id *match;
match = of\_match\_node(s3c\_rtc\_dt\_match, pdev->dev.of\_node);
if(NULL != match)
{
return (struct s3c\_rtc\_data \*)match->data;
}
return NULL;
}
static int s3c_rtc_probe(struct platform_device *pdev)
{
struct s3c_rtc *info = NULL;
struct rtc_time rtc_tm;
struct resource *res;
int ret;
info = devm\_kzalloc(&pdev->dev, sizeof(\*info), GFP\_KERNEL);
if (!info)
return -ENOMEM;
/\* find the IRQs \*/
info->irq\_tick = platform\_get\_irq(pdev, );
if (info->irq\_tick < ) {
dev\_err(&pdev->dev, "no irq for rtc tick\\n");
return info->irq\_tick;
}
info->dev = &pdev->dev;
//改为自己实现的函数
//info->data = s3c\_rtc\_get\_data(pdev);
info->data = get\_s3c\_2410\_data();
if (!info->data) {
dev\_err(&pdev->dev, "failed getting s3c\_rtc\_data\\n");
return -EINVAL;
}
spin\_lock\_init(&info->pie\_lock);
spin\_lock\_init(&info->alarm\_clk\_lock);
platform\_set\_drvdata(pdev, info);
info->irq\_alarm = platform\_get\_irq(pdev, );
if (info->irq\_alarm < ) {
dev\_err(&pdev->dev, "no irq for alarm\\n");
return info->irq\_alarm;
}
dev\_dbg(&pdev->dev, "s3c2410\_rtc: tick irq %d, alarm irq %d\\n",
info->irq\_tick, info->irq\_alarm);
/\* get the memory region \*/
res = platform\_get\_resource(pdev, IORESOURCE\_MEM, );
info->base = devm\_ioremap\_resource(&pdev->dev, res);
if (IS\_ERR(info->base))
return PTR\_ERR(info->base);
info->rtc\_clk = devm\_clk\_get(&pdev->dev, "rtc");
if (IS\_ERR(info->rtc\_clk)) {
dev\_err(&pdev->dev, "failed to find rtc clock\\n");
return PTR\_ERR(info->rtc\_clk);
}
clk\_prepare\_enable(info->rtc\_clk);
if (info->data->needs\_src\_clk) {
info->rtc\_src\_clk = devm\_clk\_get(&pdev->dev, "rtc\_src");
if (IS\_ERR(info->rtc\_src\_clk)) {
dev\_err(&pdev->dev,
"failed to find rtc source clock\\n");
return PTR\_ERR(info->rtc\_src\_clk);
}
clk\_prepare\_enable(info->rtc\_src\_clk);
}
/\* check to see if everything is setup correctly \*/
if (info->data->enable)
info->data->enable(info);
dev\_dbg(&pdev->dev, "s3c2410\_rtc: RTCCON=%02x\\n",
readw(info->base + S3C2410\_RTCCON));
device\_init\_wakeup(&pdev->dev, );
/\* Check RTC Time \*/
if (s3c\_rtc\_gettime(&pdev->dev, &rtc\_tm)) {
rtc\_tm.tm\_year = ;
rtc\_tm.tm\_mon = ;
rtc\_tm.tm\_mday = ;
rtc\_tm.tm\_hour = ;
rtc\_tm.tm\_min = ;
rtc\_tm.tm\_sec = ;
s3c\_rtc\_settime(&pdev->dev, &rtc\_tm);
dev\_warn(&pdev->dev, "warning: invalid RTC value so initializing it\\n");
}
/\* register RTC and exit \*/
info->rtc = devm\_rtc\_device\_register(&pdev->dev, "s3c", &s3c\_rtcops,
THIS\_MODULE);
if (IS\_ERR(info->rtc)) {
dev\_err(&pdev->dev, "cannot attach rtc\\n");
ret = PTR\_ERR(info->rtc);
goto err\_nortc;
}
ret = devm\_request\_irq(&pdev->dev, info->irq\_alarm, s3c\_rtc\_alarmirq,
, "s3c2410-rtc alarm", info);
if (ret) {
dev\_err(&pdev->dev, "IRQ%d error %d\\n", info->irq\_alarm, ret);
goto err\_nortc;
}
ret = devm\_request\_irq(&pdev->dev, info->irq\_tick, s3c\_rtc\_tickirq,
, "s3c2410-rtc tick", info);
if (ret) {
dev\_err(&pdev->dev, "IRQ%d error %d\\n", info->irq\_tick, ret);
goto err\_nortc;
}
if (info->data->select\_tick\_clk)
info->data->select\_tick\_clk(info);
s3c\_rtc\_setfreq(info, );
s3c\_rtc\_disable\_clk(info);
return ;
err_nortc:
if (info->data->disable)
info->data->disable(info);
if (info->data->needs\_src\_clk)
clk\_disable\_unprepare(info->rtc\_src\_clk);
clk\_disable\_unprepare(info->rtc\_clk);
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int s3c_rtc_suspend(struct device *dev)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
s3c\_rtc\_enable\_clk(info);
/\* save TICNT for anyone using periodic interrupts \*/
if (info->data->save\_tick\_cnt)
info->data->save\_tick\_cnt(info);
if (info->data->disable)
info->data->disable(info);
if (device\_may\_wakeup(dev) && !info->wake\_en) {
if (enable\_irq\_wake(info->irq\_alarm) == )
info->wake\_en = true;
else
dev\_err(dev, "enable\_irq\_wake failed\\n");
}
return ;
}
static int s3c_rtc_resume(struct device *dev)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
if (info->data->enable)
info->data->enable(info);
if (info->data->restore\_tick\_cnt)
info->data->restore\_tick\_cnt(info);
s3c\_rtc\_disable\_clk(info);
if (device\_may\_wakeup(dev) && info->wake\_en) {
disable\_irq\_wake(info->irq\_alarm);
info->wake\_en = false;
}
return ;
}
#endif
static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
{
rtc_update_irq(info->rtc, , RTC_AF | RTC_IRQF);
}
static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
{
rtc_update_irq(info->rtc, , RTC_AF | RTC_IRQF);
writeb(mask, info->base + S3C2410_INTP);
}
static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq)
{
unsigned int tmp = ;
int val;
tmp = readb(info->base + S3C2410\_TICNT);
tmp &= S3C2410\_TICNT\_ENABLE;
val = (info->rtc->max\_user\_freq / freq) - ;
tmp |= val;
writel(tmp, info->base + S3C2410\_TICNT);
}
static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq)
{
unsigned int tmp = ;
int val;
tmp = readb(info->base + S3C2410\_TICNT);
tmp &= S3C2410\_TICNT\_ENABLE;
val = (info->rtc->max\_user\_freq / freq) - ;
tmp |= S3C2443\_TICNT\_PART(val);
writel(S3C2443\_TICNT1\_PART(val), info->base + S3C2443\_TICNT1);
writel(S3C2416\_TICNT2\_PART(val), info->base + S3C2416\_TICNT2);
writel(tmp, info->base + S3C2410\_TICNT);
}
static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq)
{
unsigned int tmp = ;
int val;
tmp = readb(info->base + S3C2410\_TICNT);
tmp &= S3C2410\_TICNT\_ENABLE;
val = (info->rtc->max\_user\_freq / freq) - ;
tmp |= S3C2443\_TICNT\_PART(val);
writel(S3C2443\_TICNT1\_PART(val), info->base + S3C2443\_TICNT1);
writel(tmp, info->base + S3C2410\_TICNT);
}
static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq)
{
int val;
val = (info->rtc->max\_user\_freq / freq) - ;
writel(val, info->base + S3C2410\_TICNT);
}
static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
{
unsigned int ticnt;
ticnt = readb(info->base + S3C2410\_TICNT);
ticnt &= S3C2410\_TICNT\_ENABLE;
seq\_printf(seq, "periodic\_IRQ\\t: %s\\n", ticnt ? "yes" : "no");
}
static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info)
{
unsigned int con;
con = readw(info->base + S3C2410\_RTCCON);
con |= S3C2443\_RTCCON\_TICSEL;
writew(con, info->base + S3C2410\_RTCCON);
}
static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
{
unsigned int ticnt;
ticnt = readw(info->base + S3C2410\_RTCCON);
ticnt &= S3C64XX\_RTCCON\_TICEN;
seq\_printf(seq, "periodic\_IRQ\\t: %s\\n", ticnt ? "yes" : "no");
}
static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info)
{
info->ticnt_save = readb(info->base + S3C2410_TICNT);
}
static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info)
{
writeb(info->ticnt_save, info->base + S3C2410_TICNT);
}
static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info)
{
info->ticnt_en_save = readw(info->base + S3C2410_RTCCON);
info->ticnt_en_save &= S3C64XX_RTCCON_TICEN;
info->ticnt_save = readl(info->base + S3C2410_TICNT);
}
static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
{
unsigned int con;
writel(info->ticnt\_save, info->base + S3C2410\_TICNT);
if (info->ticnt\_en\_save) {
con = readw(info->base + S3C2410\_RTCCON);
writew(con | info->ticnt\_en\_save,
info->base + S3C2410\_RTCCON);
}
}
static struct s3c_rtc_data const s3c2410_rtc_data = {
.max_user_freq = ,
.irq_handler = s3c24xx_rtc_irq,
.set_freq = s3c2410_rtc_setfreq,
.enable_tick = s3c24xx_rtc_enable_tick,
.save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
.restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static struct s3c_rtc_data const s3c2416_rtc_data = {
.max_user_freq = ,
.irq_handler = s3c24xx_rtc_irq,
.set_freq = s3c2416_rtc_setfreq,
.enable_tick = s3c24xx_rtc_enable_tick,
.select_tick_clk = s3c2416_rtc_select_tick_clk,
.save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
.restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static struct s3c_rtc_data const s3c2443_rtc_data = {
.max_user_freq = ,
.irq_handler = s3c24xx_rtc_irq,
.set_freq = s3c2443_rtc_setfreq,
.enable_tick = s3c24xx_rtc_enable_tick,
.select_tick_clk = s3c2416_rtc_select_tick_clk,
.save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
.restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static struct s3c_rtc_data const s3c6410_rtc_data = {
.max_user_freq = ,
.needs_src_clk = true,
.irq_handler = s3c6410_rtc_irq,
.set_freq = s3c6410_rtc_setfreq,
.enable_tick = s3c6410_rtc_enable_tick,
.save_tick_cnt = s3c6410_rtc_save_tick_cnt,
.restore_tick_cnt = s3c6410_rtc_restore_tick_cnt,
.enable = s3c24xx_rtc_enable,
.disable = s3c6410_rtc_disable,
};
static struct s3c_rtc_data const exynos3250_rtc_data = {
.max_user_freq = ,
.needs_src_clk = true,
.irq_handler = s3c6410_rtc_irq,
.set_freq = s3c6410_rtc_setfreq,
.enable_tick = s3c6410_rtc_enable_tick,
.save_tick_cnt = s3c6410_rtc_save_tick_cnt,
.restore_tick_cnt = s3c6410_rtc_restore_tick_cnt,
.enable = s3c24xx_rtc_enable,
.disable = s3c6410_rtc_disable,
};
static struct s3c_rtc_data *get_s3c_2410_data(void)
{
return (struct s3c_rtc_data *)&s3c2410_rtc_data;
}
static const struct of_device_id s3c_rtc_dt_match[] = {
{
.compatible = "samsung,s3c2410-rtc",
.data = (void *)&s3c2410_rtc_data,
}, {
.compatible = "samsung,s3c2416-rtc",
.data = (void *)&s3c2416_rtc_data,
}, {
.compatible = "samsung,s3c2443-rtc",
.data = (void *)&s3c2443_rtc_data,
}, {
.compatible = "samsung,s3c6410-rtc",
.data = (void *)&s3c6410_rtc_data,
}, {
.compatible = "samsung,exynos3250-rtc",
.data = (void *)&exynos3250_rtc_data,
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.driver = {
.name = "s3c-rtc",
.pm = &s3c_rtc_pm_ops,
.of_match_table = of_match_ptr(s3c_rtc_dt_match),
},
};
module_platform_driver(s3c_rtc_driver);
MODULE_DESCRIPTION("Samsung S3C RTC Driver");
MODULE_AUTHOR("Ben Dooks ben@simtec.co.uk");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c2410-rtc");
手机扫一扫
移动阅读更方便
你可能感兴趣的文章