Commit aeedacaeaf9c02dddfeb6af87bef80c96f9050cb

Authored by Saeed Bishara
Committed by Linus Torvalds
1 parent a766ae3ebd

rtc-mv: add support for Alarm

This patch adds the Alarm support, this mode enabled when adding
IORESOURCE_IRQ to the platform device resources.

The patch also enables the wakeup mode, so the wakealarm sysfs file (under
/sys/class/rtc/rtcX/) can be used to configure the alarm clock.

Signed-off-by: Saeed Bishara <saeed@marvell.com>
Signed-off-by: Nicolas Pitre <nico@marvell.com>
Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 154 additions and 3 deletions Side-by-side Diff

drivers/rtc/rtc-mv.c
... ... @@ -27,10 +27,17 @@
27 27 #define RTC_MONTH_OFFS 8
28 28 #define RTC_YEAR_OFFS 16
29 29  
  30 +#define RTC_ALARM_TIME_REG_OFFS 8
  31 +#define RTC_ALARM_DATE_REG_OFFS 0xc
  32 +#define RTC_ALARM_VALID (1 << 7)
30 33  
  34 +#define RTC_ALARM_INTERRUPT_MASK_REG_OFFS 0x10
  35 +#define RTC_ALARM_INTERRUPT_CASUE_REG_OFFS 0x14
  36 +
31 37 struct rtc_plat_data {
32 38 struct rtc_device *rtc;
33 39 void __iomem *ioaddr;
  40 + int irq;
34 41 };
35 42  
36 43 static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm)
37 44  
... ... @@ -84,12 +91,134 @@
84 91 return rtc_valid_tm(tm);
85 92 }
86 93  
  94 +static int mv_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
  95 +{
  96 + struct rtc_plat_data *pdata = dev_get_drvdata(dev);
  97 + void __iomem *ioaddr = pdata->ioaddr;
  98 + u32 rtc_time, rtc_date;
  99 + unsigned int year, month, day, hour, minute, second, wday;
  100 +
  101 + rtc_time = readl(ioaddr + RTC_ALARM_TIME_REG_OFFS);
  102 + rtc_date = readl(ioaddr + RTC_ALARM_DATE_REG_OFFS);
  103 +
  104 + second = rtc_time & 0x7f;
  105 + minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
  106 + hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */
  107 + wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
  108 +
  109 + day = rtc_date & 0x3f;
  110 + month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f;
  111 + year = (rtc_date >> RTC_YEAR_OFFS) & 0xff;
  112 +
  113 + alm->time.tm_sec = bcd2bin(second);
  114 + alm->time.tm_min = bcd2bin(minute);
  115 + alm->time.tm_hour = bcd2bin(hour);
  116 + alm->time.tm_mday = bcd2bin(day);
  117 + alm->time.tm_wday = bcd2bin(wday);
  118 + alm->time.tm_mon = bcd2bin(month) - 1;
  119 + /* hw counts from year 2000, but tm_year is relative to 1900 */
  120 + alm->time.tm_year = bcd2bin(year) + 100;
  121 +
  122 + if (rtc_valid_tm(&alm->time) < 0) {
  123 + dev_err(dev, "retrieved alarm date/time is not valid.\n");
  124 + rtc_time_to_tm(0, &alm->time);
  125 + }
  126 +
  127 + alm->enabled = !!readl(ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
  128 + return 0;
  129 +}
  130 +
  131 +static int mv_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
  132 +{
  133 + struct rtc_plat_data *pdata = dev_get_drvdata(dev);
  134 + void __iomem *ioaddr = pdata->ioaddr;
  135 + u32 rtc_reg = 0;
  136 +
  137 + if (alm->time.tm_sec >= 0)
  138 + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_sec))
  139 + << RTC_SECONDS_OFFS;
  140 + if (alm->time.tm_min >= 0)
  141 + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_min))
  142 + << RTC_MINUTES_OFFS;
  143 + if (alm->time.tm_hour >= 0)
  144 + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_hour))
  145 + << RTC_HOURS_OFFS;
  146 +
  147 + writel(rtc_reg, ioaddr + RTC_ALARM_TIME_REG_OFFS);
  148 +
  149 + if (alm->time.tm_mday >= 0)
  150 + rtc_reg = (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mday))
  151 + << RTC_MDAY_OFFS;
  152 + else
  153 + rtc_reg = 0;
  154 +
  155 + if (alm->time.tm_mon >= 0)
  156 + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mon + 1))
  157 + << RTC_MONTH_OFFS;
  158 +
  159 + if (alm->time.tm_year >= 0)
  160 + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_year % 100))
  161 + << RTC_YEAR_OFFS;
  162 +
  163 + writel(rtc_reg, ioaddr + RTC_ALARM_DATE_REG_OFFS);
  164 + writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
  165 + writel(alm->enabled ? 1 : 0,
  166 + ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
  167 +
  168 + return 0;
  169 +}
  170 +
  171 +static int mv_rtc_ioctl(struct device *dev, unsigned int cmd,
  172 + unsigned long arg)
  173 +{
  174 + struct platform_device *pdev = to_platform_device(dev);
  175 + struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  176 + void __iomem *ioaddr = pdata->ioaddr;
  177 +
  178 + if (pdata->irq < 0)
  179 + return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
  180 + switch (cmd) {
  181 + case RTC_AIE_OFF:
  182 + writel(0, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
  183 + break;
  184 + case RTC_AIE_ON:
  185 + writel(1, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
  186 + break;
  187 + default:
  188 + return -ENOIOCTLCMD;
  189 + }
  190 + return 0;
  191 +}
  192 +
  193 +static irqreturn_t mv_rtc_interrupt(int irq, void *data)
  194 +{
  195 + struct rtc_plat_data *pdata = data;
  196 + void __iomem *ioaddr = pdata->ioaddr;
  197 +
  198 + /* alarm irq? */
  199 + if (!readl(ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS))
  200 + return IRQ_NONE;
  201 +
  202 + /* clear interrupt */
  203 + writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
  204 + rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
  205 + return IRQ_HANDLED;
  206 +}
  207 +
87 208 static const struct rtc_class_ops mv_rtc_ops = {
88 209 .read_time = mv_rtc_read_time,
89 210 .set_time = mv_rtc_set_time,
90 211 };
91 212  
92   -static int __init mv_rtc_probe(struct platform_device *pdev)
  213 +static const struct rtc_class_ops mv_rtc_alarm_ops = {
  214 + .read_time = mv_rtc_read_time,
  215 + .set_time = mv_rtc_set_time,
  216 + .read_alarm = mv_rtc_read_alarm,
  217 + .set_alarm = mv_rtc_set_alarm,
  218 + .ioctl = mv_rtc_ioctl,
  219 +};
  220 +
  221 +static int __devinit mv_rtc_probe(struct platform_device *pdev)
93 222 {
94 223 struct resource *res;
95 224 struct rtc_plat_data *pdata;
96 225  
97 226  
98 227  
... ... @@ -130,18 +259,40 @@
130 259 }
131 260 }
132 261  
  262 + pdata->irq = platform_get_irq(pdev, 0);
  263 +
133 264 platform_set_drvdata(pdev, pdata);
134   - pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
135   - &mv_rtc_ops, THIS_MODULE);
  265 +
  266 + if (pdata->irq >= 0) {
  267 + device_init_wakeup(&pdev->dev, 1);
  268 + pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
  269 + &mv_rtc_alarm_ops,
  270 + THIS_MODULE);
  271 + } else
  272 + pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
  273 + &mv_rtc_ops, THIS_MODULE);
136 274 if (IS_ERR(pdata->rtc))
137 275 return PTR_ERR(pdata->rtc);
138 276  
  277 + if (pdata->irq >= 0) {
  278 + writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
  279 + if (devm_request_irq(&pdev->dev, pdata->irq, mv_rtc_interrupt,
  280 + IRQF_DISABLED | IRQF_SHARED,
  281 + pdev->name, pdata) < 0) {
  282 + dev_warn(&pdev->dev, "interrupt not available.\n");
  283 + pdata->irq = -1;
  284 + }
  285 + }
  286 +
139 287 return 0;
140 288 }
141 289  
142 290 static int __exit mv_rtc_remove(struct platform_device *pdev)
143 291 {
144 292 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  293 +
  294 + if (pdata->irq >= 0)
  295 + device_init_wakeup(&pdev->dev, 0);
145 296  
146 297 rtc_device_unregister(pdata->rtc);
147 298 return 0;