/* * set the seconds portion of dryice time counter and clear the * fractional part. */ static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs) { struct imxdi_dev *imxdi = dev_get_drvdata(dev); int rc; /* zero the fractional part first */ rc = di_write_wait(imxdi, 0, DTCLR); if (rc == 0) rc = di_write_wait(imxdi, secs, DTCMR); return rc; }
/* * set the seconds portion of dryice alarm register */ static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct imxdi_dev *imxdi = dev_get_drvdata(dev); unsigned long now; unsigned long alarm_time; int rc; rc = rtc_tm_to_time(&alarm->time, &alarm_time); if (rc) return rc; /* don't allow setting alarm in the past */ now = __raw_readl(imxdi->ioaddr + DTCMR); if (alarm_time < now) return -EINVAL; /* write the new alarm time */ rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR); if (rc) return rc; if (alarm->enabled) di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */ else di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */ return 0; }
/* * post the alarm event from user context so it can sleep * on the write completion. */ static void dryice_work(struct work_struct *work) { struct imxdi_dev *imxdi = container_of(work, struct imxdi_dev, work); /* dismiss the interrupt (ignore error) */ di_write_wait(imxdi, DSR_CAF, DSR); /* pass the alarm event to the rtc framework. */ rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF); }
static void dryice_work(struct work_struct *work) { struct imxdi_dev *imxdi = container_of(work, struct imxdi_dev, work); di_write_wait(imxdi, DSR_CAF, DSR); rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF); }
/* * probe for dryice rtc device */ static int dryice_rtc_probe(struct platform_device *pdev) { struct resource *res; struct imxdi_dev *imxdi; int rc; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL); if (!imxdi) return -ENOMEM; imxdi->pdev = pdev; if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), pdev->name)) return -EBUSY; imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (imxdi->ioaddr == NULL) return -ENOMEM; spin_lock_init(&imxdi->irq_lock); imxdi->irq = platform_get_irq(pdev, 0); if (imxdi->irq < 0) return imxdi->irq; init_waitqueue_head(&imxdi->write_wait); INIT_WORK(&imxdi->work, dryice_work); mutex_init(&imxdi->write_mutex); imxdi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(imxdi->clk)) return PTR_ERR(imxdi->clk); clk_prepare_enable(imxdi->clk); /* * Initialize dryice hardware */ /* mask all interrupts */ __raw_writel(0, imxdi->ioaddr + DIER); rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, IRQF_SHARED, pdev->name, imxdi); if (rc) { dev_warn(&pdev->dev, "interrupt not available.\n"); goto err; } /* put dryice into valid state */ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) { rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR); if (rc) goto err; } /* initialize alarm */ rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR); if (rc) goto err; rc = di_write_wait(imxdi, 0, DCALR); if (rc) goto err; /* clear alarm flag */ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) { rc = di_write_wait(imxdi, DSR_CAF, DSR); if (rc) goto err; } /* the timer won't count if it has never been written to */ if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) { rc = di_write_wait(imxdi, 0, DTCMR); if (rc) goto err; } /* start keeping time */ if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) { rc = di_write_wait(imxdi, __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE, DCR); if (rc) goto err; } platform_set_drvdata(pdev, imxdi); imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev, &dryice_rtc_ops, THIS_MODULE); if (IS_ERR(imxdi->rtc)) { rc = PTR_ERR(imxdi->rtc); goto err; } return 0; err: clk_disable_unprepare(imxdi->clk); return rc; }