📄 rtc-cmos.c
字号:
spin_lock(&rtc_lock); irqstat = CMOS_READ(RTC_INTR_FLAGS); irqstat &= (CMOS_READ(RTC_CONTROL) & RTC_IRQMASK) | RTC_IRQF; spin_unlock(&rtc_lock); if (is_intr(irqstat)) { rtc_update_irq(p, 1, irqstat); return IRQ_HANDLED; } else return IRQ_NONE;}#ifdef CONFIG_PNP#define is_pnp() 1#define INITSECTION#else#define is_pnp() 0#define INITSECTION __init#endifstatic int INITSECTIONcmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq){ struct cmos_rtc_board_info *info = dev->platform_data; int retval = 0; unsigned char rtc_control; /* there can be only one ... */ if (cmos_rtc.dev) return -EBUSY; if (!ports) return -ENODEV; /* Claim I/O ports ASAP, minimizing conflict with legacy driver. * * REVISIT non-x86 systems may instead use memory space resources * (needing ioremap etc), not i/o space resources like this ... */ ports = request_region(ports->start, ports->end + 1 - ports->start, driver_name); if (!ports) { dev_dbg(dev, "i/o registers already in use\n"); return -EBUSY; } cmos_rtc.irq = rtc_irq; cmos_rtc.iomem = ports; /* For ACPI systems extension info comes from the FADT. On others, * board specific setup provides it as appropriate. Systems where * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and * some almost-clones) can provide hooks to make that behave. */ if (info) { cmos_rtc.day_alrm = info->rtc_day_alarm; cmos_rtc.mon_alrm = info->rtc_mon_alarm; cmos_rtc.century = info->rtc_century; if (info->wake_on && info->wake_off) { cmos_rtc.wake_on = info->wake_on; cmos_rtc.wake_off = info->wake_off; } } cmos_rtc.rtc = rtc_device_register(driver_name, dev, &cmos_rtc_ops, THIS_MODULE); if (IS_ERR(cmos_rtc.rtc)) { retval = PTR_ERR(cmos_rtc.rtc); goto cleanup0; } cmos_rtc.dev = dev; dev_set_drvdata(dev, &cmos_rtc); rename_region(ports, cmos_rtc.rtc->dev.bus_id); spin_lock_irq(&rtc_lock); /* force periodic irq to CMOS reset default of 1024Hz; * * REVISIT it's been reported that at least one x86_64 ALI mobo * doesn't use 32KHz here ... for portability we might need to * do something about other clock frequencies. */ CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); cmos_rtc.rtc->irq_freq = 1024; /* disable irqs. * * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; * allegedly some older rtcs need that to handle irqs properly */ rtc_control = CMOS_READ(RTC_CONTROL); rtc_control &= ~(RTC_PIE | RTC_AIE | RTC_UIE); CMOS_WRITE(rtc_control, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); spin_unlock_irq(&rtc_lock); /* FIXME teach the alarm code how to handle binary mode; * <asm-generic/rtc.h> doesn't know 12-hour mode either. */ if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) { dev_dbg(dev, "only 24-hr BCD mode supported\n"); retval = -ENXIO; goto cleanup1; } if (is_valid_irq(rtc_irq)) retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED, cmos_rtc.rtc->dev.bus_id, cmos_rtc.rtc); if (retval < 0) { dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); goto cleanup1; } /* REVISIT optionally make 50 or 114 bytes NVRAM available, * like rtc-ds1553, rtc-ds1742 ... this will often include * registers for century, and day/month alarm. */ pr_info("%s: alarms up to one %s%s\n", cmos_rtc.rtc->dev.bus_id, is_valid_irq(rtc_irq) ? (cmos_rtc.mon_alrm ? "year" : (cmos_rtc.day_alrm ? "month" : "day")) : "no", cmos_rtc.century ? ", y3k" : "" ); return 0;cleanup1: cmos_rtc.dev = NULL; rtc_device_unregister(cmos_rtc.rtc);cleanup0: release_region(ports->start, ports->end + 1 - ports->start); return retval;}static void cmos_do_shutdown(void){ unsigned char rtc_control; spin_lock_irq(&rtc_lock); rtc_control = CMOS_READ(RTC_CONTROL); rtc_control &= ~(RTC_PIE|RTC_AIE|RTC_UIE); CMOS_WRITE(rtc_control, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); spin_unlock_irq(&rtc_lock);}static void __exit cmos_do_remove(struct device *dev){ struct cmos_rtc *cmos = dev_get_drvdata(dev); struct resource *ports; cmos_do_shutdown(); if (is_valid_irq(cmos->irq)) free_irq(cmos->irq, cmos->rtc); rtc_device_unregister(cmos->rtc); cmos->rtc = NULL; ports = cmos->iomem; release_region(ports->start, ports->end + 1 - ports->start); cmos->iomem = NULL; cmos->dev = NULL; dev_set_drvdata(dev, NULL);}#ifdef CONFIG_PMstatic int cmos_suspend(struct device *dev, pm_message_t mesg){ struct cmos_rtc *cmos = dev_get_drvdata(dev); int do_wake = device_may_wakeup(dev); unsigned char tmp; /* only the alarm might be a wakeup event source */ spin_lock_irq(&rtc_lock); cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL); if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { unsigned char irqstat; if (do_wake) tmp &= ~(RTC_PIE|RTC_UIE); else tmp &= ~(RTC_PIE|RTC_AIE|RTC_UIE); CMOS_WRITE(tmp, RTC_CONTROL); irqstat = CMOS_READ(RTC_INTR_FLAGS); irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF; if (is_intr(irqstat)) rtc_update_irq(cmos->rtc, 1, irqstat); } spin_unlock_irq(&rtc_lock); if (tmp & RTC_AIE) { cmos->enabled_wake = 1; if (cmos->wake_on) cmos->wake_on(dev); else enable_irq_wake(cmos->irq); } pr_debug("%s: suspend%s, ctrl %02x\n", cmos_rtc.rtc->dev.bus_id, (tmp & RTC_AIE) ? ", alarm may wake" : "", tmp); return 0;}static int cmos_resume(struct device *dev){ struct cmos_rtc *cmos = dev_get_drvdata(dev); unsigned char tmp = cmos->suspend_ctrl; /* re-enable any irqs previously active */ if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { if (cmos->enabled_wake) { if (cmos->wake_off) cmos->wake_off(dev); else disable_irq_wake(cmos->irq); cmos->enabled_wake = 0; } spin_lock_irq(&rtc_lock); CMOS_WRITE(tmp, RTC_CONTROL); tmp = CMOS_READ(RTC_INTR_FLAGS); tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF; if (is_intr(tmp)) rtc_update_irq(cmos->rtc, 1, tmp); spin_unlock_irq(&rtc_lock); } pr_debug("%s: resume, ctrl %02x\n", cmos_rtc.rtc->dev.bus_id, cmos->suspend_ctrl); return 0;}#else#define cmos_suspend NULL#define cmos_resume NULL#endif/*----------------------------------------------------------------*//* The "CMOS" RTC normally lives on the platform_bus. On ACPI systems, * the device node will always be created as a PNPACPI device. Plus * pre-ACPI PCs probably list it in the PNPBIOS tables. */#ifdef CONFIG_PNP#include <linux/pnp.h>static int __devinitcmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id){ /* REVISIT paranoia argues for a shutdown notifier, since PNP * drivers can't provide shutdown() methods to disable IRQs. * Or better yet, fix PNP to allow those methods... */ if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0)) /* Some machines contain a PNP entry for the RTC, but * don't define the IRQ. It should always be safe to * hardcode it in these cases */ return cmos_do_probe(&pnp->dev, &pnp->res.port_resource[0], 8); else return cmos_do_probe(&pnp->dev, &pnp->res.port_resource[0], pnp->res.irq_resource[0].start);}static void __exit cmos_pnp_remove(struct pnp_dev *pnp){ cmos_do_remove(&pnp->dev);}#ifdef CONFIG_PMstatic int cmos_pnp_suspend(struct pnp_dev *pnp, pm_message_t mesg){ return cmos_suspend(&pnp->dev, mesg);}static int cmos_pnp_resume(struct pnp_dev *pnp){ return cmos_resume(&pnp->dev);}#else#define cmos_pnp_suspend NULL#define cmos_pnp_resume NULL#endifstatic const struct pnp_device_id rtc_ids[] = { { .id = "PNP0b00", }, { .id = "PNP0b01", }, { .id = "PNP0b02", }, { },};MODULE_DEVICE_TABLE(pnp, rtc_ids);static struct pnp_driver cmos_pnp_driver = { .name = (char *) driver_name, .id_table = rtc_ids, .probe = cmos_pnp_probe, .remove = __exit_p(cmos_pnp_remove), /* flag ensures resume() gets called, and stops syslog spam */ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, .suspend = cmos_pnp_suspend, .resume = cmos_pnp_resume,};static int __init cmos_init(void){ return pnp_register_driver(&cmos_pnp_driver);}module_init(cmos_init);static void __exit cmos_exit(void){ pnp_unregister_driver(&cmos_pnp_driver);}module_exit(cmos_exit);#else /* no PNP *//*----------------------------------------------------------------*//* Platform setup should have set up an RTC device, when PNP is * unavailable ... this could happen even on (older) PCs. */static int __init cmos_platform_probe(struct platform_device *pdev){ return cmos_do_probe(&pdev->dev, platform_get_resource(pdev, IORESOURCE_IO, 0), platform_get_irq(pdev, 0));}static int __exit cmos_platform_remove(struct platform_device *pdev){ cmos_do_remove(&pdev->dev); return 0;}static void cmos_platform_shutdown(struct platform_device *pdev){ cmos_do_shutdown();}static struct platform_driver cmos_platform_driver = { .remove = __exit_p(cmos_platform_remove), .shutdown = cmos_platform_shutdown, .driver = { .name = (char *) driver_name, .suspend = cmos_suspend, .resume = cmos_resume, }};static int __init cmos_init(void){ return platform_driver_probe(&cmos_platform_driver, cmos_platform_probe);}module_init(cmos_init);static void __exit cmos_exit(void){ platform_driver_unregister(&cmos_platform_driver);}module_exit(cmos_exit);#endif /* !PNP */MODULE_AUTHOR("David Brownell");MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -