Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: driver for Conexant Digicolor CX92755 on-chip RTC

Add driver for the RTC hardware block on the Conexant CX92755 SoC, from
the Digicolor series of SoCs. Tested on the Equinox evaluation board for
the CX92755 chip.

[akpm@linux-foundation.org: build command arrays at compile-time]
Signed-off-by: Baruch Siach <baruch@tkos.co.il>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Baruch Siach and committed by
Linus Torvalds
ba172208 71b800b6

+238
+10
drivers/rtc/Kconfig
··· 1111 1111 This driver can also be built as a module. If so, the module 1112 1112 will be called rtc-davinci. 1113 1113 1114 + config RTC_DRV_DIGICOLOR 1115 + tristate "Conexant Digicolor RTC" 1116 + depends on ARCH_DIGICOLOR 1117 + help 1118 + If you say yes here you get support for the RTC on Conexant 1119 + Digicolor platforms. This currently includes the CX92755 SoC. 1120 + 1121 + This driver can also be built as a module. If so, the module 1122 + will be called rtc-digicolor. 1123 + 1114 1124 config RTC_DRV_IMXDI 1115 1125 tristate "Freescale IMX DryIce Real Time Clock" 1116 1126 depends on ARCH_MXC
+1
drivers/rtc/Makefile
··· 40 40 obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o 41 41 obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o 42 42 obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o 43 + obj-$(CONFIG_RTC_DRV_DIGICOLOR) += rtc-digicolor.o 43 44 obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o 44 45 obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o 45 46 obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
+227
drivers/rtc/rtc-digicolor.c
··· 1 + /* 2 + * Real Time Clock driver for Conexant Digicolor 3 + * 4 + * Copyright (C) 2015 Paradox Innovation Ltd. 5 + * 6 + * Author: Baruch Siach <baruch@tkos.co.il> 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms of the GNU General Public License as published by the 10 + * Free Software Foundation; either version 2 of the License, or (at your 11 + * option) any later version. 12 + */ 13 + 14 + #include <linux/io.h> 15 + #include <linux/iopoll.h> 16 + #include <linux/delay.h> 17 + #include <linux/module.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/rtc.h> 20 + #include <linux/of.h> 21 + 22 + #define DC_RTC_CONTROL 0x0 23 + #define DC_RTC_TIME 0x8 24 + #define DC_RTC_REFERENCE 0xc 25 + #define DC_RTC_ALARM 0x10 26 + #define DC_RTC_INTFLAG_CLEAR 0x14 27 + #define DC_RTC_INTENABLE 0x16 28 + 29 + #define DC_RTC_CMD_MASK 0xf 30 + #define DC_RTC_GO_BUSY BIT(7) 31 + 32 + #define CMD_NOP 0 33 + #define CMD_RESET 1 34 + #define CMD_WRITE 3 35 + #define CMD_READ 4 36 + 37 + #define CMD_DELAY_US (10*1000) 38 + #define CMD_TIMEOUT_US (500*CMD_DELAY_US) 39 + 40 + struct dc_rtc { 41 + struct rtc_device *rtc_dev; 42 + void __iomem *regs; 43 + }; 44 + 45 + static int dc_rtc_cmds(struct dc_rtc *rtc, const u8 *cmds, int len) 46 + { 47 + u8 val; 48 + int i, ret; 49 + 50 + for (i = 0; i < len; i++) { 51 + writeb_relaxed((cmds[i] & DC_RTC_CMD_MASK) | DC_RTC_GO_BUSY, 52 + rtc->regs + DC_RTC_CONTROL); 53 + ret = readb_relaxed_poll_timeout( 54 + rtc->regs + DC_RTC_CONTROL, val, 55 + !(val & DC_RTC_GO_BUSY), CMD_DELAY_US, CMD_TIMEOUT_US); 56 + if (ret < 0) 57 + return ret; 58 + } 59 + 60 + return 0; 61 + } 62 + 63 + static int dc_rtc_read(struct dc_rtc *rtc, unsigned long *val) 64 + { 65 + static const u8 read_cmds[] = {CMD_READ, CMD_NOP}; 66 + u32 reference, time1, time2; 67 + int ret; 68 + 69 + ret = dc_rtc_cmds(rtc, read_cmds, ARRAY_SIZE(read_cmds)); 70 + if (ret < 0) 71 + return ret; 72 + 73 + reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE); 74 + time1 = readl_relaxed(rtc->regs + DC_RTC_TIME); 75 + /* Read twice to ensure consistency */ 76 + while (1) { 77 + time2 = readl_relaxed(rtc->regs + DC_RTC_TIME); 78 + if (time1 == time2) 79 + break; 80 + time1 = time2; 81 + } 82 + 83 + *val = reference + time1; 84 + return 0; 85 + } 86 + 87 + static int dc_rtc_write(struct dc_rtc *rtc, u32 val) 88 + { 89 + static const u8 write_cmds[] = {CMD_WRITE, CMD_NOP, CMD_RESET, CMD_NOP}; 90 + 91 + writel_relaxed(val, rtc->regs + DC_RTC_REFERENCE); 92 + return dc_rtc_cmds(rtc, write_cmds, ARRAY_SIZE(write_cmds)); 93 + } 94 + 95 + static int dc_rtc_read_time(struct device *dev, struct rtc_time *tm) 96 + { 97 + struct dc_rtc *rtc = dev_get_drvdata(dev); 98 + unsigned long now; 99 + int ret; 100 + 101 + ret = dc_rtc_read(rtc, &now); 102 + if (ret < 0) 103 + return ret; 104 + rtc_time64_to_tm(now, tm); 105 + 106 + return 0; 107 + } 108 + 109 + static int dc_rtc_set_mmss(struct device *dev, unsigned long secs) 110 + { 111 + struct dc_rtc *rtc = dev_get_drvdata(dev); 112 + 113 + return dc_rtc_write(rtc, secs); 114 + } 115 + 116 + static int dc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) 117 + { 118 + struct dc_rtc *rtc = dev_get_drvdata(dev); 119 + u32 alarm_reg, reference; 120 + unsigned long now; 121 + int ret; 122 + 123 + alarm_reg = readl_relaxed(rtc->regs + DC_RTC_ALARM); 124 + reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE); 125 + rtc_time64_to_tm(reference + alarm_reg, &alarm->time); 126 + 127 + ret = dc_rtc_read(rtc, &now); 128 + if (ret < 0) 129 + return ret; 130 + 131 + alarm->pending = alarm_reg + reference > now; 132 + alarm->enabled = readl_relaxed(rtc->regs + DC_RTC_INTENABLE); 133 + 134 + return 0; 135 + } 136 + 137 + static int dc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) 138 + { 139 + struct dc_rtc *rtc = dev_get_drvdata(dev); 140 + time64_t alarm_time; 141 + u32 reference; 142 + 143 + alarm_time = rtc_tm_to_time64(&alarm->time); 144 + 145 + reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE); 146 + writel_relaxed(alarm_time - reference, rtc->regs + DC_RTC_ALARM); 147 + 148 + writeb_relaxed(!!alarm->enabled, rtc->regs + DC_RTC_INTENABLE); 149 + 150 + return 0; 151 + } 152 + 153 + static int dc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 154 + { 155 + struct dc_rtc *rtc = dev_get_drvdata(dev); 156 + 157 + writeb_relaxed(!!enabled, rtc->regs + DC_RTC_INTENABLE); 158 + 159 + return 0; 160 + } 161 + 162 + static struct rtc_class_ops dc_rtc_ops = { 163 + .read_time = dc_rtc_read_time, 164 + .set_mmss = dc_rtc_set_mmss, 165 + .read_alarm = dc_rtc_read_alarm, 166 + .set_alarm = dc_rtc_set_alarm, 167 + .alarm_irq_enable = dc_rtc_alarm_irq_enable, 168 + }; 169 + 170 + static irqreturn_t dc_rtc_irq(int irq, void *dev_id) 171 + { 172 + struct dc_rtc *rtc = dev_id; 173 + 174 + writeb_relaxed(1, rtc->regs + DC_RTC_INTFLAG_CLEAR); 175 + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); 176 + 177 + return IRQ_HANDLED; 178 + } 179 + 180 + static int __init dc_rtc_probe(struct platform_device *pdev) 181 + { 182 + struct resource *res; 183 + struct dc_rtc *rtc; 184 + int irq, ret; 185 + 186 + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 187 + if (!rtc) 188 + return -ENOMEM; 189 + 190 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 191 + rtc->regs = devm_ioremap_resource(&pdev->dev, res); 192 + if (IS_ERR(rtc->regs)) 193 + return PTR_ERR(rtc->regs); 194 + 195 + irq = platform_get_irq(pdev, 0); 196 + if (irq < 0) 197 + return irq; 198 + ret = devm_request_irq(&pdev->dev, irq, dc_rtc_irq, 0, pdev->name, rtc); 199 + if (ret < 0) 200 + return ret; 201 + 202 + platform_set_drvdata(pdev, rtc); 203 + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, 204 + &dc_rtc_ops, THIS_MODULE); 205 + if (IS_ERR(rtc->rtc_dev)) 206 + return PTR_ERR(rtc->rtc_dev); 207 + 208 + return 0; 209 + } 210 + 211 + static const struct of_device_id dc_dt_ids[] = { 212 + { .compatible = "cnxt,cx92755-rtc" }, 213 + { /* sentinel */ } 214 + }; 215 + MODULE_DEVICE_TABLE(of, dc_dt_ids); 216 + 217 + static struct platform_driver dc_rtc_driver = { 218 + .driver = { 219 + .name = "digicolor_rtc", 220 + .of_match_table = of_match_ptr(dc_dt_ids), 221 + }, 222 + }; 223 + module_platform_driver_probe(dc_rtc_driver, dc_rtc_probe); 224 + 225 + MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); 226 + MODULE_DESCRIPTION("Conexant Digicolor Realtime Clock Driver (RTC)"); 227 + MODULE_LICENSE("GPL");