linux4.1.36 2440 启用 RTC 支持
阅读原文时间:2023年07月11日阅读:1

/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");

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章