⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtc-cmos.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	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 + -