Chinaunix首页 | 论坛 | 博客
  • 博客访问: 685216
  • 博文数量: 207
  • 博客积分: 1743
  • 博客等级: 上尉
  • 技术积分: 2044
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-20 14:36
文章分类

全部博文(207)

文章存档

2016年(24)

2015年(10)

2014年(50)

2013年(45)

2012年(78)

分类: LINUX

2012-10-17 09:18:38

/*

 * linux/drivers/serial/tcc-rtc.c

 *

 * Author:

 * Created: Feb 10, 2009

 * Description: RTC driver for Telechips TCC Series

 *

 * Copyright (C) 2009 Telechips 

 *

 * 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.

 *

 * You should have received a copy of the GNU General Public License

 * along with this program; if not, see the file COPYING, or write

 * to the Free Software Foundation, Inc.,

 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

 */





#include

#include

#include

#include

#include

#include

#include

#include

#include

#include



#include

#include

#include

#include

#include

#include



#include

#include "tcc/tca_alarm.h"





//#define printk printk

#define DRV_NAME "tcc-rtc"



#define RTCCON      0x00

#define INTCON      0x04

#define RTCALM      0x08

#define ALMSEC      0x0C

#define ALMMIN      0x10

#define ALMHOUR     0x14

#define ALMDATE     0x18

#define ALMDAY      0x1C

#define ALMMON      0x20

#define ALMYEAR     0x24

#define BCDSEC      0x28

#define BCDMIN      0x2C

#define BCDHOUR     0x30

#define BCDDATE     0x34

#define BCDDAY      0x38

#define BCDMON      0x3C

#define BCDYEAR     0x40

#define RTCIM       0x44

#define RTCPEND     0x48



#if 0

#pragma pack(push, 4)

struct tcc_rtc_regs {

    volatile unsigned long RTCCON, INTCON, RTCALM,

             ALMSEC, ALMMIN, ALMHOUR, ALMDATE, ALMDAY, ALMMON, ALMYEAR,

             BCDSEC, BCDMIN, BCDHOUR, BCDDATE, BCDDAY, BCDMON, BCDYEAR, 

             RTCIM, RTCPEND;

};

#pragma pack(pop)



volatile struct tcc_rtc_regs *rtc_regs;

#endif



static void __iomem *rtc_base;

static void __iomem *pic_base;

static int tcc_rtc_alarmno = NO_IRQ;





/* IRQ Handlers */



static irqreturn_t tcc_rtc_alarmirq(int irq, void *id)

{

   printk("[IRQ OK]--------------------------------------------------------------\n");



    tca_alarm_setint((unsigned int)rtc_base);



rtc_update_irq(id, 1, RTC_AF | RTC_IRQF);



return IRQ_HANDLED;

}



/* Update control registers */

static void tcc_rtc_setaie(int to)

{

unsigned int tmp;



printk("%s: aie=%d\n", __func__, to);

    tcc_writel( tcc_readl(rtc_base + RTCCON) | Hw1, rtc_base + RTCCON);

    tcc_writel( tcc_readl(rtc_base + INTCON) | Hw0, rtc_base + INTCON);


tmp = tcc_readl(rtc_base + RTCALM) & ~Hw7;



if (to){

tmp |= Hw7;

    tca_alarm_setpmwkup((unsigned int)rtc_base, (unsigned int)pic_base);

}

tcc_writel(tmp, rtc_base + RTCALM);



    //tcc_writel( tcc_readl(rtc_base + INTCON) & ~Hw0, rtc_base + INTCON);

    tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw1, rtc_base + RTCCON);

}





/* Time read/write */

static int tcc_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)

{

    rtctime pTime;





    local_irq_disable();

    tca_rtc_gettime((unsigned int)rtc_base, &pTime);



    rtc_tm->tm_sec  = pTime.wSecond;

    rtc_tm->tm_min  = pTime.wMinute;

    rtc_tm->tm_hour = pTime.wHour;

    rtc_tm->tm_mday = pTime.wDay;

    rtc_tm->tm_mon  = pTime.wMonth - 1;

    rtc_tm->tm_year = pTime.wYear - 1900;



    printk("read time %02d.%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);



    local_irq_enable();



return 0;

}



static int tcc_rtc_settime(struct device *dev, struct rtc_time *tm)

{

    rtctime pTime;



printk("set time %02d.%02d.%02d %02d/%02d/%02d\n",

tm->tm_year, tm->tm_mon, tm->tm_mday,

tm->tm_hour, tm->tm_min, tm->tm_sec);



    local_irq_disable();



    pTime.wSecond       = tm->tm_sec;

    pTime.wMinute       = tm->tm_min;

    pTime.wHour         = tm->tm_hour;

    pTime.wDay          = tm->tm_mday;

    pTime.wMonth        = tm->tm_mon + 1;

    pTime.wYear         = tm->tm_year + 1900;



    tca_rtc_settime((unsigned int)rtc_base, &pTime);



    local_irq_enable();



return 0;

}



static int tcc_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)

{

struct rtc_time *alm_tm = &alrm->time;

unsigned int alm_en, alm_pnd;



    rtctime pTime;



    printk("%s\n", __func__);



    local_irq_disable();



    tcc_writel( tcc_readl(rtc_base + RTCCON) | Hw1, rtc_base + RTCCON);

    tcc_writel( tcc_readl(rtc_base + INTCON) | Hw0, rtc_base + INTCON);



alm_en = tcc_readl(rtc_base + RTCALM);

alm_pnd = tcc_readl(rtc_base + RTCPEND);



alrm->enabled = (alm_en & Hw7) ? 1 : 0;

alrm->pending = (alm_pnd & Hw0) ? 1 : 0;



    printk(" alrm->enabled = %d, alm_en = %d\n", alrm->enabled, alm_en);



    tcc_writel( tcc_readl(rtc_base + INTCON) & ~Hw0, rtc_base + INTCON);

    tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw1, rtc_base + RTCCON);



    tca_alarm_gettime((unsigned int)rtc_base, &pTime);



    alm_tm->tm_sec  = pTime.wSecond;

    alm_tm->tm_min  = pTime.wMinute;

    alm_tm->tm_hour = pTime.wHour;

    alm_tm->tm_mday = pTime.wDay ;

    alm_tm->tm_mon  = pTime.wMonth - 1;

    alm_tm->tm_year = pTime.wYear - 1900;



printk("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\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);



    local_irq_enable();



return 0;

}



static int tcc_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)

{

    rtctime pTime;

struct rtc_time *tm = &alrm->time;

    printk("%s\n", __func__);



    local_irq_disable();



    alrm->enabled = 1;



    


pTime.wSecond   = tm->tm_sec;

    pTime.wMinute   = tm->tm_min;

    pTime.wHour     = tm->tm_hour;

    pTime.wDay      = tm->tm_mday;

    pTime.wMonth    = tm->tm_mon + 1;

    pTime.wYear     = tm->tm_year + 1900;



printk("set alarm %02d.%02d.%02d %02d/%02d/%02d\n",

pTime.wSecond, pTime.wMinute, pTime.wHour,

         pTime.wDay, pTime.wMonth, pTime.wYear);



    tca_alarm_settime((unsigned int)rtc_base, &pTime);

#if 0

    //tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw0, rtc_base + RTCCON);

    tcc_writel( tcc_readl(rtc_base + RTCCON) | Hw1, rtc_base + RTCCON);

    tcc_writel( tcc_readl(rtc_base + INTCON) | Hw0, rtc_base + INTCON);

    //tcc_writel( tcc_readl(rtc_base + INTCON) & ~Hw15, rtc_base + INTCON);


if (alrm->enabled) {

   tcc_writel(tcc_readl(rtc_base + RTCALM)| Hw7, rtc_base + RTCALM);

enable_irq_wake(tcc_rtc_alarmno);

    } else {

   tcc_writel(tcc_readl(rtc_base + RTCALM)& ~Hw7, rtc_base + RTCALM);

disable_irq_wake(tcc_rtc_alarmno);

    }



    tcc_writel( tcc_readl(rtc_base + INTCON) & ~Hw0, rtc_base + INTCON);

    tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw1, rtc_base + RTCCON);

#endif

    local_irq_enable();



return 0;

}



static int tcc_rtc_proc(struct device *dev, struct seq_file *seq)

{

return 0;

}





static int tcc_rtc_ioctl(struct device *dev,

             unsigned int cmd, unsigned long arg)

{

    unsigned int ret = -ENOIOCTLCMD;



    switch (cmd) {

    case RTC_AIE_OFF:

        tcc_rtc_setaie(0);

        ret = 0;

        break;

    case RTC_AIE_ON:

tcc_rtc_setaie(1);

        ret = 0;

        break;

    case RTC_PIE_OFF:

        break;

    case RTC_PIE_ON:

        break;

    case RTC_IRQP_READ:

        break;

    case RTC_IRQP_SET:

        break;

    case RTC_UIE_ON:

        break;

    case RTC_UIE_OFF:

        break;

        ret = -EINVAL;

    }



    return ret;

}





static const struct rtc_class_ops tcc_rtcops = {

.read_time = tcc_rtc_gettime,

.set_time = tcc_rtc_settime,

.read_alarm = tcc_rtc_getalarm,

.set_alarm = tcc_rtc_setalarm,

.proc        = tcc_rtc_proc,

    .ioctl      = tcc_rtc_ioctl,

};



#if 0

static void tcc_rtc_enable(struct platform_device *pdev, int en)

{

if (rtc_base == NULL)

return;





    tca_alarm_setint((unsigned int)rtc_base);

}

#endif



static int tcc_rtc_remove(struct platform_device *dev)

{

struct rtc_device *rtc = platform_get_drvdata(dev);



platform_set_drvdata(dev, NULL);

rtc_device_unregister(rtc);



tcc_rtc_setaie(0);



    free_irq(tcc_rtc_alarmno, rtc);



return 0;

}





static int tcc_rtc_probe(struct platform_device *pdev)

{

struct rtc_device *rtc;

int ret;

    volatile PIOBUSCFG pIOBUSCFG = (volatile PIOBUSCFG)tcc_p2v(HwIOBUSCFG_BASE);

    int valid_time = 1;



#if defined(CONFIG_MACH_TCC8900)

    // BUS Enable

    pIOBUSCFG->HCLKEN0 |= Hw26;

#endif



rtc_base = (void __iomem *)tcc_p2v(HwRTC_BASE);

pic_base = (void __iomem *)tcc_p2v(HwPIC_BASE);



if (rtc_base == NULL) {

        printk("failed ioremap()\n"); 

        return -ENOMEM;

}



    

    // SW Reset

    if(valid_time)

    {

#if defined(CONFIG_MACH_TCC8900)

        pIOBUSCFG->HRSTEN0 &= ~Hw26;

        pIOBUSCFG->HRSTEN0 |= Hw26;

#endif

        // tcc_rtc_init((unsigned int)rtc_base);

        // RTC Initialization 

        tcc_writel( Hw1, rtc_base + RTCCON);

        tcc_writel( Hw0, rtc_base + INTCON);

        tcc_writel( tcc_readl(rtc_base + RTCCON) | Hw0, rtc_base + RTCCON);

        //tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw15, rtc_base + RTCCON);

        tcc_writel( tcc_readl(rtc_base + INTCON) & ~Hw15, rtc_base + INTCON);

        tcc_writel( tcc_readl(rtc_base + RTCCON) & ~(Hw13 | Hw12 | Hw10 | Hw9 | Hw8), rtc_base + INTCON);

        tcc_writel( tcc_readl(rtc_base + INTCON) | Hw12, rtc_base + INTCON);

        //tcc_writel( tcc_readl(rtc_base + RTCCON) | Hw15, rtc_base + RTCCON);

        tcc_writel( tcc_readl(rtc_base + INTCON) | Hw15, rtc_base + INTCON);



        tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw0, rtc_base + RTCCON);

        tcc_writel( tcc_readl(rtc_base + RTCIM) & ~(Hw3 | Hw0), rtc_base + RTCIM);

        tcc_writel( tcc_readl(rtc_base + RTCIM) | Hw2, rtc_base + RTCIM);



        tcc_writel( tcc_readl(rtc_base + RTCALM) & ~(Hw7 - Hw0), rtc_base + RTCALM);

        tcc_writel( tcc_readl(rtc_base + INTCON) & ~Hw0, rtc_base + INTCON);

        tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw1, rtc_base + RTCCON);



    }else {

        // ADD  PMWKUP RTC Normal mode}

    }



//tcc_rtc_enable(pdev, 1);

    //tca_alarm_setint((unsigned int)rtc_base);

//tca_alarm_clrpmwkup((unsigned int)rtc_base, (unsigned int)pic_base);

/* find the IRQs */

tcc_rtc_alarmno = platform_get_irq(pdev, 0);

    printk("tcc_rtc: alarm irq %d\n", tcc_rtc_alarmno);



if (tcc_rtc_alarmno < 0) {

dev_err(&pdev->dev, "no irq for alarm\n");

return -ENOENT;

}



/* register RTC and exit */

rtc = rtc_device_register(pdev->name, &pdev->dev, &tcc_rtcops, THIS_MODULE);



if (IS_ERR(rtc)) {

dev_err(&pdev->dev, "cannot attach rtc\n");

ret = PTR_ERR(rtc);

goto err_nortc;

}



platform_set_drvdata(pdev, rtc);



#if 0

    if(request_irq(tcc_rtc_alarmno, tcc_rtc_alarmirq, 

                   IRQF_DISABLED, DRV_NAME, rtc))

    {

        printk("%s: RTC timer interrupt IRQ%d already claimed\n",

                 pdev->name, tcc_rtc_alarmno);

        return 0;

    }

PPIC    pPIC = (PPIC)(unsigned int)pic_base;

pPIC->CLR1 = Hw11;

pPIC->MSTS1 &= ~Hw11;

pPIC->SEL1 &= ~Hw11;

    tca_alarm_setpmwkup((unsigned int)rtc_base, (unsigned int)pic_base);

device_init_wakeup(&pdev->dev, 1);



#endif

return 0;



 err_nortc:

//tcc_rtc_enable(pdev, 0);

    rtc_device_unregister(rtc);

    return ret;



}

#ifdef CONFIG_PM



/* RTC Power management control */



static int tcc_rtc_suspend(struct platform_device *pdev, pm_message_t state)

{

    if (device_may_wakeup(&pdev->dev))

        enable_irq_wake(tcc_rtc_alarmno);

return 0;

}



static int tcc_rtc_resume(struct platform_device *pdev)

{

if (device_may_wakeup(&pdev->dev))

disable_irq_wake(tcc_rtc_alarmno);



{

#if 0

volatile PIOBUSCFG pIOBUSCFG = (volatile PIOBUSCFG)tcc_p2v(HwIOBUSCFG_BASE);



pIOBUSCFG->HCLKEN0 |= Hw26; // BUS Enable

pIOBUSCFG->HRSTEN0 &= ~Hw26; // SW Reset

pIOBUSCFG->HRSTEN0 |= Hw26;



tcc_writel( Hw1, rtc_base + RTCCON);

tcc_writel( Hw0, rtc_base + INTCON);

tcc_writel( tcc_readl(rtc_base + RTCCON) | Hw0, rtc_base + RTCCON);

tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw15, rtc_base + RTCCON);

tcc_writel( tcc_readl(rtc_base + RTCCON) & ~(Hw13 | Hw12 | Hw10 | Hw9 | Hw8), rtc_base + INTCON);

tcc_writel( tcc_readl(rtc_base + INTCON) | Hw12, rtc_base + INTCON);

tcc_writel( tcc_readl(rtc_base + RTCCON) | Hw15, rtc_base + RTCCON);



tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw0, rtc_base + RTCCON);

tcc_writel( tcc_readl(rtc_base + RTCIM) & ~(Hw3 | Hw0), rtc_base + RTCIM);

tcc_writel( tcc_readl(rtc_base + RTCIM) | Hw2, rtc_base + RTCIM);



tcc_writel( tcc_readl(rtc_base + RTCALM) & ~(Hw7 - Hw0), rtc_base + RTCALM);

tcc_writel( tcc_readl(rtc_base + INTCON) & ~Hw0, rtc_base + INTCON);

tcc_writel( tcc_readl(rtc_base + RTCCON) & ~Hw1, rtc_base + RTCCON);

#endif

}





return 0;

}

#else

#define tcc_rtc_suspend NULL

#define tcc_rtc_resume  NULL

#endif



static struct platform_driver tcc_rtc_driver = {

.probe = tcc_rtc_probe,

.remove = tcc_rtc_remove,

.suspend = tcc_rtc_suspend,

.resume = tcc_rtc_resume,

.driver = {

.name = DRV_NAME,

.owner = THIS_MODULE,

},

};



static char __initdata banner[] = "TCC RTC, (c) 2009, Telechips \n";



static int __init tcc_rtc_init(void)

{

printk(banner);

return platform_driver_register(&tcc_rtc_driver);

}



static void __exit tcc_rtc_exit(void)

{

platform_driver_unregister(&tcc_rtc_driver);

}



module_init(tcc_rtc_init);

module_exit(tcc_rtc_exit);



MODULE_AUTHOR("linux ");

MODULE_DESCRIPTION("Telechips RTC Driver");

MODULE_LICENSE("GPL");
阅读(1047) | 评论(0) | 转发(0) |
0

上一篇:const 小计

下一篇:H.246 MPEG4 区别

给主人留下些什么吧!~~