zoukankan      html  css  js  c++  java
  • linux4.1.36 2440 启用 RTC 支持

    /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 <linux/module.h>
    #include <linux/fs.h>
    #include <linux/string.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    #include <linux/interrupt.h>
    #include <linux/rtc.h>
    #include <linux/bcd.h>
    #include <linux/clk.h>
    #include <linux/log2.h>
    #include <linux/slab.h>
    #include <linux/of.h>
    #include <linux/uaccess.h>
    #include <linux/io.h>
    
    #include <asm/irq.h>
    #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
    ", __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 0;
    }
    
    /* 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 0;
    }
    
    /* 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 = 0;
    
        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 == 0 && !have_retried) {
            have_retried = 1;
            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 += 100;
    
        dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d
    ",
             1900 + 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 -= 1;
    
        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 - 100;
    
        dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d
    ",
             1900 + 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 < 0 || year >= 100) {
            dev_err(dev, "rtc only supports 100 years
    ");
            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 + 1), info->base + S3C2410_RTCMON);
        writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR);
    
        s3c_rtc_disable_clk(info);
    
        return 0;
    }
    
    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) ? 1 : 0;
    
        dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d
    ",
             alm_en,
             1900 + 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 = -1;
    
        if (alm_en & S3C2410_RTCALM_MINEN)
            alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
        else
            alm_tm->tm_min = -1;
    
        if (alm_en & S3C2410_RTCALM_HOUREN)
            alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
        else
            alm_tm->tm_hour = -1;
    
        if (alm_en & S3C2410_RTCALM_DAYEN)
            alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
        else
            alm_tm->tm_mday = -1;
    
        if (alm_en & S3C2410_RTCALM_MONEN) {
            alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
            alm_tm->tm_mon -= 1;
        } else {
            alm_tm->tm_mon = -1;
        }
    
        if (alm_en & S3C2410_RTCALM_YEAREN)
            alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
        else
            alm_tm->tm_year = -1;
    
        return 0;
    }
    
    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
    ",
             alrm->enabled,
             1900 + tm->tm_year, tm->tm_mon + 1, 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 < 60 && tm->tm_sec >= 0) {
            alrm_en |= S3C2410_RTCALM_SECEN;
            writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC);
        }
    
        if (tm->tm_min < 60 && tm->tm_min >= 0) {
            alrm_en |= S3C2410_RTCALM_MINEN;
            writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN);
        }
    
        if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
            alrm_en |= S3C2410_RTCALM_HOUREN;
            writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
        }
    
        dev_dbg(dev, "setting S3C2410_RTCALM to %08x
    ", alrm_en);
    
        writeb(alrm_en, info->base + S3C2410_RTCALM);
    
        s3c_rtc_disable_clk(info);
    
        s3c_rtc_setaie(dev, alrm->enabled);
    
        return 0;
    }
    
    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 0;
    }
    
    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) == 0) {
            dev_info(info->dev, "rtc disabled, re-enabling
    ");
    
            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
    ");
    
            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
    ");
    
            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, 0);
    
        clk_unprepare(info->rtc_clk);
        info->rtc_clk = NULL;
    
        return 0;
    }
    
    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, 1);
        if (info->irq_tick < 0) {
            dev_err(&pdev->dev, "no irq for rtc tick
    ");
            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
    ");
            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, 0);
        if (info->irq_alarm < 0) {
            dev_err(&pdev->dev, "no irq for alarm
    ");
            return info->irq_alarm;
        }
    
        dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d
    ",
             info->irq_tick, info->irq_alarm);
    
        /* get the memory region */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        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
    ");
            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
    ");
                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
    ",
             readw(info->base + S3C2410_RTCCON));
    
        device_init_wakeup(&pdev->dev, 1);
    
        /* Check RTC Time */
        if (s3c_rtc_gettime(&pdev->dev, &rtc_tm)) {
            rtc_tm.tm_year    = 100;
            rtc_tm.tm_mon    = 0;
            rtc_tm.tm_mday    = 1;
            rtc_tm.tm_hour    = 0;
            rtc_tm.tm_min    = 0;
            rtc_tm.tm_sec    = 0;
    
            s3c_rtc_settime(&pdev->dev, &rtc_tm);
    
            dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it
    ");
        }
    
        /* 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
    ");
            ret = PTR_ERR(info->rtc);
            goto err_nortc;
        }
    
        ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
                  0,  "s3c2410-rtc alarm", info);
        if (ret) {
            dev_err(&pdev->dev, "IRQ%d error %d
    ", info->irq_alarm, ret);
            goto err_nortc;
        }
    
        ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
                  0,  "s3c2410-rtc tick", info);
        if (ret) {
            dev_err(&pdev->dev, "IRQ%d error %d
    ", info->irq_tick, ret);
            goto err_nortc;
        }
    
        if (info->data->select_tick_clk)
            info->data->select_tick_clk(info);
    
        s3c_rtc_setfreq(info, 1);
    
        s3c_rtc_disable_clk(info);
    
        return 0;
    
     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) == 0)
                info->wake_en = true;
            else
                dev_err(dev, "enable_irq_wake failed
    ");
        }
    
        return 0;
    }
    
    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 0;
    }
    #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, 1, RTC_AF | RTC_IRQF);
    }
    
    static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
    {
        rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
        writeb(mask, info->base + S3C2410_INTP);
    }
    
    static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq)
    {
        unsigned int tmp = 0;
        int val;
    
        tmp = readb(info->base + S3C2410_TICNT);
        tmp &= S3C2410_TICNT_ENABLE;
    
        val = (info->rtc->max_user_freq / freq) - 1;
        tmp |= val;
    
        writel(tmp, info->base + S3C2410_TICNT);
    }
    
    static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq)
    {
        unsigned int tmp = 0;
        int val;
    
        tmp = readb(info->base + S3C2410_TICNT);
        tmp &= S3C2410_TICNT_ENABLE;
    
        val = (info->rtc->max_user_freq / freq) - 1;
    
        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 = 0;
        int val;
    
        tmp = readb(info->base + S3C2410_TICNT);
        tmp &= S3C2410_TICNT_ENABLE;
    
        val = (info->rtc->max_user_freq / freq) - 1;
    
        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) - 1;
        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	: %s
    ", 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	: %s
    ", 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        = 128,
        .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        = 32768,
        .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        = 32768,
        .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        = 32768,
        .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        = 32768,
        .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");
  • 相关阅读:
    HDU 1874 畅通project续 最短路径入门(dijkstra)
    怎样推断 ios设备的类型(iphone,ipod,ipad)
    双向数据绑定---AngularJS的基本原理学习
    常见的CPU訪问引起的内存保护问题为什么仅仅用event_122上报
    最简单的基于FFmpeg的视频编码器-更新版(YUV编码为HEVC(H.265))
    【Java项目实战】——DRP之HTML总结
    myql5.7.7优化配置參数
    2014年七月最佳jQuery插件荟萃
    敏捷DoD完毕定义的多种形态
    c#网络通信框架networkcomms内核解析之三 消息同步调用
  • 原文地址:https://www.cnblogs.com/ningci/p/6567584.html
Copyright © 2011-2022 走看看