Commit 4ec8c7f52654cdbb13770d04dc76f9a4615cd4e1
1 parent
4fa030a43d
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
rtc: imx dryice: Add missing clk_prepare
prepare the clock before enabling it. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Showing 1 changed file with 3 additions and 3 deletions Inline Diff
drivers/rtc/rtc-imxdi.c
1 | /* | 1 | /* |
2 | * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. | 2 | * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. |
3 | * Copyright 2010 Orex Computed Radiography | 3 | * Copyright 2010 Orex Computed Radiography |
4 | */ | 4 | */ |
5 | 5 | ||
6 | /* | 6 | /* |
7 | * The code contained herein is licensed under the GNU General Public | 7 | * The code contained herein is licensed under the GNU General Public |
8 | * License. You may obtain a copy of the GNU General Public License | 8 | * License. You may obtain a copy of the GNU General Public License |
9 | * Version 2 or later at the following locations: | 9 | * Version 2 or later at the following locations: |
10 | * | 10 | * |
11 | * http://www.opensource.org/licenses/gpl-license.html | 11 | * http://www.opensource.org/licenses/gpl-license.html |
12 | * http://www.gnu.org/copyleft/gpl.html | 12 | * http://www.gnu.org/copyleft/gpl.html |
13 | */ | 13 | */ |
14 | 14 | ||
15 | /* based on rtc-mc13892.c */ | 15 | /* based on rtc-mc13892.c */ |
16 | 16 | ||
17 | /* | 17 | /* |
18 | * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block | 18 | * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block |
19 | * to implement a Linux RTC. Times and alarms are truncated to seconds. | 19 | * to implement a Linux RTC. Times and alarms are truncated to seconds. |
20 | * Since the RTC framework performs API locking via rtc->ops_lock the | 20 | * Since the RTC framework performs API locking via rtc->ops_lock the |
21 | * only simultaneous accesses we need to deal with is updating DryIce | 21 | * only simultaneous accesses we need to deal with is updating DryIce |
22 | * registers while servicing an alarm. | 22 | * registers while servicing an alarm. |
23 | * | 23 | * |
24 | * Note that reading the DSR (DryIce Status Register) automatically clears | 24 | * Note that reading the DSR (DryIce Status Register) automatically clears |
25 | * the WCF (Write Complete Flag). All DryIce writes are synchronized to the | 25 | * the WCF (Write Complete Flag). All DryIce writes are synchronized to the |
26 | * LP (Low Power) domain and set the WCF upon completion. Writes to the | 26 | * LP (Low Power) domain and set the WCF upon completion. Writes to the |
27 | * DIER (DryIce Interrupt Enable Register) are the only exception. These | 27 | * DIER (DryIce Interrupt Enable Register) are the only exception. These |
28 | * occur at normal bus speeds and do not set WCF. Periodic interrupts are | 28 | * occur at normal bus speeds and do not set WCF. Periodic interrupts are |
29 | * not supported by the hardware. | 29 | * not supported by the hardware. |
30 | */ | 30 | */ |
31 | 31 | ||
32 | #include <linux/io.h> | 32 | #include <linux/io.h> |
33 | #include <linux/clk.h> | 33 | #include <linux/clk.h> |
34 | #include <linux/delay.h> | 34 | #include <linux/delay.h> |
35 | #include <linux/module.h> | 35 | #include <linux/module.h> |
36 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
37 | #include <linux/rtc.h> | 37 | #include <linux/rtc.h> |
38 | #include <linux/sched.h> | 38 | #include <linux/sched.h> |
39 | #include <linux/workqueue.h> | 39 | #include <linux/workqueue.h> |
40 | 40 | ||
41 | /* DryIce Register Definitions */ | 41 | /* DryIce Register Definitions */ |
42 | 42 | ||
43 | #define DTCMR 0x00 /* Time Counter MSB Reg */ | 43 | #define DTCMR 0x00 /* Time Counter MSB Reg */ |
44 | #define DTCLR 0x04 /* Time Counter LSB Reg */ | 44 | #define DTCLR 0x04 /* Time Counter LSB Reg */ |
45 | 45 | ||
46 | #define DCAMR 0x08 /* Clock Alarm MSB Reg */ | 46 | #define DCAMR 0x08 /* Clock Alarm MSB Reg */ |
47 | #define DCALR 0x0c /* Clock Alarm LSB Reg */ | 47 | #define DCALR 0x0c /* Clock Alarm LSB Reg */ |
48 | #define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */ | 48 | #define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */ |
49 | 49 | ||
50 | #define DCR 0x10 /* Control Reg */ | 50 | #define DCR 0x10 /* Control Reg */ |
51 | #define DCR_TCE (1 << 3) /* Time Counter Enable */ | 51 | #define DCR_TCE (1 << 3) /* Time Counter Enable */ |
52 | 52 | ||
53 | #define DSR 0x14 /* Status Reg */ | 53 | #define DSR 0x14 /* Status Reg */ |
54 | #define DSR_WBF (1 << 10) /* Write Busy Flag */ | 54 | #define DSR_WBF (1 << 10) /* Write Busy Flag */ |
55 | #define DSR_WNF (1 << 9) /* Write Next Flag */ | 55 | #define DSR_WNF (1 << 9) /* Write Next Flag */ |
56 | #define DSR_WCF (1 << 8) /* Write Complete Flag */ | 56 | #define DSR_WCF (1 << 8) /* Write Complete Flag */ |
57 | #define DSR_WEF (1 << 7) /* Write Error Flag */ | 57 | #define DSR_WEF (1 << 7) /* Write Error Flag */ |
58 | #define DSR_CAF (1 << 4) /* Clock Alarm Flag */ | 58 | #define DSR_CAF (1 << 4) /* Clock Alarm Flag */ |
59 | #define DSR_NVF (1 << 1) /* Non-Valid Flag */ | 59 | #define DSR_NVF (1 << 1) /* Non-Valid Flag */ |
60 | #define DSR_SVF (1 << 0) /* Security Violation Flag */ | 60 | #define DSR_SVF (1 << 0) /* Security Violation Flag */ |
61 | 61 | ||
62 | #define DIER 0x18 /* Interrupt Enable Reg */ | 62 | #define DIER 0x18 /* Interrupt Enable Reg */ |
63 | #define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */ | 63 | #define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */ |
64 | #define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */ | 64 | #define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */ |
65 | #define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */ | 65 | #define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */ |
66 | #define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */ | 66 | #define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */ |
67 | 67 | ||
68 | /** | 68 | /** |
69 | * struct imxdi_dev - private imxdi rtc data | 69 | * struct imxdi_dev - private imxdi rtc data |
70 | * @pdev: pionter to platform dev | 70 | * @pdev: pionter to platform dev |
71 | * @rtc: pointer to rtc struct | 71 | * @rtc: pointer to rtc struct |
72 | * @ioaddr: IO registers pointer | 72 | * @ioaddr: IO registers pointer |
73 | * @irq: dryice normal interrupt | 73 | * @irq: dryice normal interrupt |
74 | * @clk: input reference clock | 74 | * @clk: input reference clock |
75 | * @dsr: copy of the DSR register | 75 | * @dsr: copy of the DSR register |
76 | * @irq_lock: interrupt enable register (DIER) lock | 76 | * @irq_lock: interrupt enable register (DIER) lock |
77 | * @write_wait: registers write complete queue | 77 | * @write_wait: registers write complete queue |
78 | * @write_mutex: serialize registers write | 78 | * @write_mutex: serialize registers write |
79 | * @work: schedule alarm work | 79 | * @work: schedule alarm work |
80 | */ | 80 | */ |
81 | struct imxdi_dev { | 81 | struct imxdi_dev { |
82 | struct platform_device *pdev; | 82 | struct platform_device *pdev; |
83 | struct rtc_device *rtc; | 83 | struct rtc_device *rtc; |
84 | void __iomem *ioaddr; | 84 | void __iomem *ioaddr; |
85 | int irq; | 85 | int irq; |
86 | struct clk *clk; | 86 | struct clk *clk; |
87 | u32 dsr; | 87 | u32 dsr; |
88 | spinlock_t irq_lock; | 88 | spinlock_t irq_lock; |
89 | wait_queue_head_t write_wait; | 89 | wait_queue_head_t write_wait; |
90 | struct mutex write_mutex; | 90 | struct mutex write_mutex; |
91 | struct work_struct work; | 91 | struct work_struct work; |
92 | }; | 92 | }; |
93 | 93 | ||
94 | /* | 94 | /* |
95 | * enable a dryice interrupt | 95 | * enable a dryice interrupt |
96 | */ | 96 | */ |
97 | static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) | 97 | static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) |
98 | { | 98 | { |
99 | unsigned long flags; | 99 | unsigned long flags; |
100 | 100 | ||
101 | spin_lock_irqsave(&imxdi->irq_lock, flags); | 101 | spin_lock_irqsave(&imxdi->irq_lock, flags); |
102 | __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr, | 102 | __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr, |
103 | imxdi->ioaddr + DIER); | 103 | imxdi->ioaddr + DIER); |
104 | spin_unlock_irqrestore(&imxdi->irq_lock, flags); | 104 | spin_unlock_irqrestore(&imxdi->irq_lock, flags); |
105 | } | 105 | } |
106 | 106 | ||
107 | /* | 107 | /* |
108 | * disable a dryice interrupt | 108 | * disable a dryice interrupt |
109 | */ | 109 | */ |
110 | static void di_int_disable(struct imxdi_dev *imxdi, u32 intr) | 110 | static void di_int_disable(struct imxdi_dev *imxdi, u32 intr) |
111 | { | 111 | { |
112 | unsigned long flags; | 112 | unsigned long flags; |
113 | 113 | ||
114 | spin_lock_irqsave(&imxdi->irq_lock, flags); | 114 | spin_lock_irqsave(&imxdi->irq_lock, flags); |
115 | __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr, | 115 | __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr, |
116 | imxdi->ioaddr + DIER); | 116 | imxdi->ioaddr + DIER); |
117 | spin_unlock_irqrestore(&imxdi->irq_lock, flags); | 117 | spin_unlock_irqrestore(&imxdi->irq_lock, flags); |
118 | } | 118 | } |
119 | 119 | ||
120 | /* | 120 | /* |
121 | * This function attempts to clear the dryice write-error flag. | 121 | * This function attempts to clear the dryice write-error flag. |
122 | * | 122 | * |
123 | * A dryice write error is similar to a bus fault and should not occur in | 123 | * A dryice write error is similar to a bus fault and should not occur in |
124 | * normal operation. Clearing the flag requires another write, so the root | 124 | * normal operation. Clearing the flag requires another write, so the root |
125 | * cause of the problem may need to be fixed before the flag can be cleared. | 125 | * cause of the problem may need to be fixed before the flag can be cleared. |
126 | */ | 126 | */ |
127 | static void clear_write_error(struct imxdi_dev *imxdi) | 127 | static void clear_write_error(struct imxdi_dev *imxdi) |
128 | { | 128 | { |
129 | int cnt; | 129 | int cnt; |
130 | 130 | ||
131 | dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n"); | 131 | dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n"); |
132 | 132 | ||
133 | /* clear the write error flag */ | 133 | /* clear the write error flag */ |
134 | __raw_writel(DSR_WEF, imxdi->ioaddr + DSR); | 134 | __raw_writel(DSR_WEF, imxdi->ioaddr + DSR); |
135 | 135 | ||
136 | /* wait for it to take effect */ | 136 | /* wait for it to take effect */ |
137 | for (cnt = 0; cnt < 1000; cnt++) { | 137 | for (cnt = 0; cnt < 1000; cnt++) { |
138 | if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0) | 138 | if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0) |
139 | return; | 139 | return; |
140 | udelay(10); | 140 | udelay(10); |
141 | } | 141 | } |
142 | dev_err(&imxdi->pdev->dev, | 142 | dev_err(&imxdi->pdev->dev, |
143 | "ERROR: Cannot clear write-error flag!\n"); | 143 | "ERROR: Cannot clear write-error flag!\n"); |
144 | } | 144 | } |
145 | 145 | ||
146 | /* | 146 | /* |
147 | * Write a dryice register and wait until it completes. | 147 | * Write a dryice register and wait until it completes. |
148 | * | 148 | * |
149 | * This function uses interrupts to determine when the | 149 | * This function uses interrupts to determine when the |
150 | * write has completed. | 150 | * write has completed. |
151 | */ | 151 | */ |
152 | static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg) | 152 | static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg) |
153 | { | 153 | { |
154 | int ret; | 154 | int ret; |
155 | int rc = 0; | 155 | int rc = 0; |
156 | 156 | ||
157 | /* serialize register writes */ | 157 | /* serialize register writes */ |
158 | mutex_lock(&imxdi->write_mutex); | 158 | mutex_lock(&imxdi->write_mutex); |
159 | 159 | ||
160 | /* enable the write-complete interrupt */ | 160 | /* enable the write-complete interrupt */ |
161 | di_int_enable(imxdi, DIER_WCIE); | 161 | di_int_enable(imxdi, DIER_WCIE); |
162 | 162 | ||
163 | imxdi->dsr = 0; | 163 | imxdi->dsr = 0; |
164 | 164 | ||
165 | /* do the register write */ | 165 | /* do the register write */ |
166 | __raw_writel(val, imxdi->ioaddr + reg); | 166 | __raw_writel(val, imxdi->ioaddr + reg); |
167 | 167 | ||
168 | /* wait for the write to finish */ | 168 | /* wait for the write to finish */ |
169 | ret = wait_event_interruptible_timeout(imxdi->write_wait, | 169 | ret = wait_event_interruptible_timeout(imxdi->write_wait, |
170 | imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1)); | 170 | imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1)); |
171 | if (ret < 0) { | 171 | if (ret < 0) { |
172 | rc = ret; | 172 | rc = ret; |
173 | goto out; | 173 | goto out; |
174 | } else if (ret == 0) { | 174 | } else if (ret == 0) { |
175 | dev_warn(&imxdi->pdev->dev, | 175 | dev_warn(&imxdi->pdev->dev, |
176 | "Write-wait timeout " | 176 | "Write-wait timeout " |
177 | "val = 0x%08x reg = 0x%08x\n", val, reg); | 177 | "val = 0x%08x reg = 0x%08x\n", val, reg); |
178 | } | 178 | } |
179 | 179 | ||
180 | /* check for write error */ | 180 | /* check for write error */ |
181 | if (imxdi->dsr & DSR_WEF) { | 181 | if (imxdi->dsr & DSR_WEF) { |
182 | clear_write_error(imxdi); | 182 | clear_write_error(imxdi); |
183 | rc = -EIO; | 183 | rc = -EIO; |
184 | } | 184 | } |
185 | 185 | ||
186 | out: | 186 | out: |
187 | mutex_unlock(&imxdi->write_mutex); | 187 | mutex_unlock(&imxdi->write_mutex); |
188 | 188 | ||
189 | return rc; | 189 | return rc; |
190 | } | 190 | } |
191 | 191 | ||
192 | /* | 192 | /* |
193 | * read the seconds portion of the current time from the dryice time counter | 193 | * read the seconds portion of the current time from the dryice time counter |
194 | */ | 194 | */ |
195 | static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm) | 195 | static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm) |
196 | { | 196 | { |
197 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); | 197 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); |
198 | unsigned long now; | 198 | unsigned long now; |
199 | 199 | ||
200 | now = __raw_readl(imxdi->ioaddr + DTCMR); | 200 | now = __raw_readl(imxdi->ioaddr + DTCMR); |
201 | rtc_time_to_tm(now, tm); | 201 | rtc_time_to_tm(now, tm); |
202 | 202 | ||
203 | return 0; | 203 | return 0; |
204 | } | 204 | } |
205 | 205 | ||
206 | /* | 206 | /* |
207 | * set the seconds portion of dryice time counter and clear the | 207 | * set the seconds portion of dryice time counter and clear the |
208 | * fractional part. | 208 | * fractional part. |
209 | */ | 209 | */ |
210 | static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs) | 210 | static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs) |
211 | { | 211 | { |
212 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); | 212 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); |
213 | int rc; | 213 | int rc; |
214 | 214 | ||
215 | /* zero the fractional part first */ | 215 | /* zero the fractional part first */ |
216 | rc = di_write_wait(imxdi, 0, DTCLR); | 216 | rc = di_write_wait(imxdi, 0, DTCLR); |
217 | if (rc == 0) | 217 | if (rc == 0) |
218 | rc = di_write_wait(imxdi, secs, DTCMR); | 218 | rc = di_write_wait(imxdi, secs, DTCMR); |
219 | 219 | ||
220 | return rc; | 220 | return rc; |
221 | } | 221 | } |
222 | 222 | ||
223 | static int dryice_rtc_alarm_irq_enable(struct device *dev, | 223 | static int dryice_rtc_alarm_irq_enable(struct device *dev, |
224 | unsigned int enabled) | 224 | unsigned int enabled) |
225 | { | 225 | { |
226 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); | 226 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); |
227 | 227 | ||
228 | if (enabled) | 228 | if (enabled) |
229 | di_int_enable(imxdi, DIER_CAIE); | 229 | di_int_enable(imxdi, DIER_CAIE); |
230 | else | 230 | else |
231 | di_int_disable(imxdi, DIER_CAIE); | 231 | di_int_disable(imxdi, DIER_CAIE); |
232 | 232 | ||
233 | return 0; | 233 | return 0; |
234 | } | 234 | } |
235 | 235 | ||
236 | /* | 236 | /* |
237 | * read the seconds portion of the alarm register. | 237 | * read the seconds portion of the alarm register. |
238 | * the fractional part of the alarm register is always zero. | 238 | * the fractional part of the alarm register is always zero. |
239 | */ | 239 | */ |
240 | static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | 240 | static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
241 | { | 241 | { |
242 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); | 242 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); |
243 | u32 dcamr; | 243 | u32 dcamr; |
244 | 244 | ||
245 | dcamr = __raw_readl(imxdi->ioaddr + DCAMR); | 245 | dcamr = __raw_readl(imxdi->ioaddr + DCAMR); |
246 | rtc_time_to_tm(dcamr, &alarm->time); | 246 | rtc_time_to_tm(dcamr, &alarm->time); |
247 | 247 | ||
248 | /* alarm is enabled if the interrupt is enabled */ | 248 | /* alarm is enabled if the interrupt is enabled */ |
249 | alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0; | 249 | alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0; |
250 | 250 | ||
251 | /* don't allow the DSR read to mess up DSR_WCF */ | 251 | /* don't allow the DSR read to mess up DSR_WCF */ |
252 | mutex_lock(&imxdi->write_mutex); | 252 | mutex_lock(&imxdi->write_mutex); |
253 | 253 | ||
254 | /* alarm is pending if the alarm flag is set */ | 254 | /* alarm is pending if the alarm flag is set */ |
255 | alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0; | 255 | alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0; |
256 | 256 | ||
257 | mutex_unlock(&imxdi->write_mutex); | 257 | mutex_unlock(&imxdi->write_mutex); |
258 | 258 | ||
259 | return 0; | 259 | return 0; |
260 | } | 260 | } |
261 | 261 | ||
262 | /* | 262 | /* |
263 | * set the seconds portion of dryice alarm register | 263 | * set the seconds portion of dryice alarm register |
264 | */ | 264 | */ |
265 | static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | 265 | static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
266 | { | 266 | { |
267 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); | 267 | struct imxdi_dev *imxdi = dev_get_drvdata(dev); |
268 | unsigned long now; | 268 | unsigned long now; |
269 | unsigned long alarm_time; | 269 | unsigned long alarm_time; |
270 | int rc; | 270 | int rc; |
271 | 271 | ||
272 | rc = rtc_tm_to_time(&alarm->time, &alarm_time); | 272 | rc = rtc_tm_to_time(&alarm->time, &alarm_time); |
273 | if (rc) | 273 | if (rc) |
274 | return rc; | 274 | return rc; |
275 | 275 | ||
276 | /* don't allow setting alarm in the past */ | 276 | /* don't allow setting alarm in the past */ |
277 | now = __raw_readl(imxdi->ioaddr + DTCMR); | 277 | now = __raw_readl(imxdi->ioaddr + DTCMR); |
278 | if (alarm_time < now) | 278 | if (alarm_time < now) |
279 | return -EINVAL; | 279 | return -EINVAL; |
280 | 280 | ||
281 | /* write the new alarm time */ | 281 | /* write the new alarm time */ |
282 | rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR); | 282 | rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR); |
283 | if (rc) | 283 | if (rc) |
284 | return rc; | 284 | return rc; |
285 | 285 | ||
286 | if (alarm->enabled) | 286 | if (alarm->enabled) |
287 | di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */ | 287 | di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */ |
288 | else | 288 | else |
289 | di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */ | 289 | di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */ |
290 | 290 | ||
291 | return 0; | 291 | return 0; |
292 | } | 292 | } |
293 | 293 | ||
294 | static struct rtc_class_ops dryice_rtc_ops = { | 294 | static struct rtc_class_ops dryice_rtc_ops = { |
295 | .read_time = dryice_rtc_read_time, | 295 | .read_time = dryice_rtc_read_time, |
296 | .set_mmss = dryice_rtc_set_mmss, | 296 | .set_mmss = dryice_rtc_set_mmss, |
297 | .alarm_irq_enable = dryice_rtc_alarm_irq_enable, | 297 | .alarm_irq_enable = dryice_rtc_alarm_irq_enable, |
298 | .read_alarm = dryice_rtc_read_alarm, | 298 | .read_alarm = dryice_rtc_read_alarm, |
299 | .set_alarm = dryice_rtc_set_alarm, | 299 | .set_alarm = dryice_rtc_set_alarm, |
300 | }; | 300 | }; |
301 | 301 | ||
302 | /* | 302 | /* |
303 | * dryice "normal" interrupt handler | 303 | * dryice "normal" interrupt handler |
304 | */ | 304 | */ |
305 | static irqreturn_t dryice_norm_irq(int irq, void *dev_id) | 305 | static irqreturn_t dryice_norm_irq(int irq, void *dev_id) |
306 | { | 306 | { |
307 | struct imxdi_dev *imxdi = dev_id; | 307 | struct imxdi_dev *imxdi = dev_id; |
308 | u32 dsr, dier; | 308 | u32 dsr, dier; |
309 | irqreturn_t rc = IRQ_NONE; | 309 | irqreturn_t rc = IRQ_NONE; |
310 | 310 | ||
311 | dier = __raw_readl(imxdi->ioaddr + DIER); | 311 | dier = __raw_readl(imxdi->ioaddr + DIER); |
312 | 312 | ||
313 | /* handle write complete and write error cases */ | 313 | /* handle write complete and write error cases */ |
314 | if ((dier & DIER_WCIE)) { | 314 | if ((dier & DIER_WCIE)) { |
315 | /*If the write wait queue is empty then there is no pending | 315 | /*If the write wait queue is empty then there is no pending |
316 | operations. It means the interrupt is for DryIce -Security. | 316 | operations. It means the interrupt is for DryIce -Security. |
317 | IRQ must be returned as none.*/ | 317 | IRQ must be returned as none.*/ |
318 | if (list_empty_careful(&imxdi->write_wait.task_list)) | 318 | if (list_empty_careful(&imxdi->write_wait.task_list)) |
319 | return rc; | 319 | return rc; |
320 | 320 | ||
321 | /* DSR_WCF clears itself on DSR read */ | 321 | /* DSR_WCF clears itself on DSR read */ |
322 | dsr = __raw_readl(imxdi->ioaddr + DSR); | 322 | dsr = __raw_readl(imxdi->ioaddr + DSR); |
323 | if ((dsr & (DSR_WCF | DSR_WEF))) { | 323 | if ((dsr & (DSR_WCF | DSR_WEF))) { |
324 | /* mask the interrupt */ | 324 | /* mask the interrupt */ |
325 | di_int_disable(imxdi, DIER_WCIE); | 325 | di_int_disable(imxdi, DIER_WCIE); |
326 | 326 | ||
327 | /* save the dsr value for the wait queue */ | 327 | /* save the dsr value for the wait queue */ |
328 | imxdi->dsr |= dsr; | 328 | imxdi->dsr |= dsr; |
329 | 329 | ||
330 | wake_up_interruptible(&imxdi->write_wait); | 330 | wake_up_interruptible(&imxdi->write_wait); |
331 | rc = IRQ_HANDLED; | 331 | rc = IRQ_HANDLED; |
332 | } | 332 | } |
333 | } | 333 | } |
334 | 334 | ||
335 | /* handle the alarm case */ | 335 | /* handle the alarm case */ |
336 | if ((dier & DIER_CAIE)) { | 336 | if ((dier & DIER_CAIE)) { |
337 | /* DSR_WCF clears itself on DSR read */ | 337 | /* DSR_WCF clears itself on DSR read */ |
338 | dsr = __raw_readl(imxdi->ioaddr + DSR); | 338 | dsr = __raw_readl(imxdi->ioaddr + DSR); |
339 | if (dsr & DSR_CAF) { | 339 | if (dsr & DSR_CAF) { |
340 | /* mask the interrupt */ | 340 | /* mask the interrupt */ |
341 | di_int_disable(imxdi, DIER_CAIE); | 341 | di_int_disable(imxdi, DIER_CAIE); |
342 | 342 | ||
343 | /* finish alarm in user context */ | 343 | /* finish alarm in user context */ |
344 | schedule_work(&imxdi->work); | 344 | schedule_work(&imxdi->work); |
345 | rc = IRQ_HANDLED; | 345 | rc = IRQ_HANDLED; |
346 | } | 346 | } |
347 | } | 347 | } |
348 | return rc; | 348 | return rc; |
349 | } | 349 | } |
350 | 350 | ||
351 | /* | 351 | /* |
352 | * post the alarm event from user context so it can sleep | 352 | * post the alarm event from user context so it can sleep |
353 | * on the write completion. | 353 | * on the write completion. |
354 | */ | 354 | */ |
355 | static void dryice_work(struct work_struct *work) | 355 | static void dryice_work(struct work_struct *work) |
356 | { | 356 | { |
357 | struct imxdi_dev *imxdi = container_of(work, | 357 | struct imxdi_dev *imxdi = container_of(work, |
358 | struct imxdi_dev, work); | 358 | struct imxdi_dev, work); |
359 | 359 | ||
360 | /* dismiss the interrupt (ignore error) */ | 360 | /* dismiss the interrupt (ignore error) */ |
361 | di_write_wait(imxdi, DSR_CAF, DSR); | 361 | di_write_wait(imxdi, DSR_CAF, DSR); |
362 | 362 | ||
363 | /* pass the alarm event to the rtc framework. */ | 363 | /* pass the alarm event to the rtc framework. */ |
364 | rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF); | 364 | rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF); |
365 | } | 365 | } |
366 | 366 | ||
367 | /* | 367 | /* |
368 | * probe for dryice rtc device | 368 | * probe for dryice rtc device |
369 | */ | 369 | */ |
370 | static int dryice_rtc_probe(struct platform_device *pdev) | 370 | static int dryice_rtc_probe(struct platform_device *pdev) |
371 | { | 371 | { |
372 | struct resource *res; | 372 | struct resource *res; |
373 | struct imxdi_dev *imxdi; | 373 | struct imxdi_dev *imxdi; |
374 | int rc; | 374 | int rc; |
375 | 375 | ||
376 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 376 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
377 | if (!res) | 377 | if (!res) |
378 | return -ENODEV; | 378 | return -ENODEV; |
379 | 379 | ||
380 | imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL); | 380 | imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL); |
381 | if (!imxdi) | 381 | if (!imxdi) |
382 | return -ENOMEM; | 382 | return -ENOMEM; |
383 | 383 | ||
384 | imxdi->pdev = pdev; | 384 | imxdi->pdev = pdev; |
385 | 385 | ||
386 | if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), | 386 | if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), |
387 | pdev->name)) | 387 | pdev->name)) |
388 | return -EBUSY; | 388 | return -EBUSY; |
389 | 389 | ||
390 | imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start, | 390 | imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start, |
391 | resource_size(res)); | 391 | resource_size(res)); |
392 | if (imxdi->ioaddr == NULL) | 392 | if (imxdi->ioaddr == NULL) |
393 | return -ENOMEM; | 393 | return -ENOMEM; |
394 | 394 | ||
395 | imxdi->irq = platform_get_irq(pdev, 0); | 395 | imxdi->irq = platform_get_irq(pdev, 0); |
396 | if (imxdi->irq < 0) | 396 | if (imxdi->irq < 0) |
397 | return imxdi->irq; | 397 | return imxdi->irq; |
398 | 398 | ||
399 | init_waitqueue_head(&imxdi->write_wait); | 399 | init_waitqueue_head(&imxdi->write_wait); |
400 | 400 | ||
401 | INIT_WORK(&imxdi->work, dryice_work); | 401 | INIT_WORK(&imxdi->work, dryice_work); |
402 | 402 | ||
403 | mutex_init(&imxdi->write_mutex); | 403 | mutex_init(&imxdi->write_mutex); |
404 | 404 | ||
405 | imxdi->clk = clk_get(&pdev->dev, NULL); | 405 | imxdi->clk = clk_get(&pdev->dev, NULL); |
406 | if (IS_ERR(imxdi->clk)) | 406 | if (IS_ERR(imxdi->clk)) |
407 | return PTR_ERR(imxdi->clk); | 407 | return PTR_ERR(imxdi->clk); |
408 | clk_enable(imxdi->clk); | 408 | clk_prepare_enable(imxdi->clk); |
409 | 409 | ||
410 | /* | 410 | /* |
411 | * Initialize dryice hardware | 411 | * Initialize dryice hardware |
412 | */ | 412 | */ |
413 | 413 | ||
414 | /* mask all interrupts */ | 414 | /* mask all interrupts */ |
415 | __raw_writel(0, imxdi->ioaddr + DIER); | 415 | __raw_writel(0, imxdi->ioaddr + DIER); |
416 | 416 | ||
417 | rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, | 417 | rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, |
418 | IRQF_SHARED, pdev->name, imxdi); | 418 | IRQF_SHARED, pdev->name, imxdi); |
419 | if (rc) { | 419 | if (rc) { |
420 | dev_warn(&pdev->dev, "interrupt not available.\n"); | 420 | dev_warn(&pdev->dev, "interrupt not available.\n"); |
421 | goto err; | 421 | goto err; |
422 | } | 422 | } |
423 | 423 | ||
424 | /* put dryice into valid state */ | 424 | /* put dryice into valid state */ |
425 | if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) { | 425 | if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) { |
426 | rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR); | 426 | rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR); |
427 | if (rc) | 427 | if (rc) |
428 | goto err; | 428 | goto err; |
429 | } | 429 | } |
430 | 430 | ||
431 | /* initialize alarm */ | 431 | /* initialize alarm */ |
432 | rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR); | 432 | rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR); |
433 | if (rc) | 433 | if (rc) |
434 | goto err; | 434 | goto err; |
435 | rc = di_write_wait(imxdi, 0, DCALR); | 435 | rc = di_write_wait(imxdi, 0, DCALR); |
436 | if (rc) | 436 | if (rc) |
437 | goto err; | 437 | goto err; |
438 | 438 | ||
439 | /* clear alarm flag */ | 439 | /* clear alarm flag */ |
440 | if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) { | 440 | if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) { |
441 | rc = di_write_wait(imxdi, DSR_CAF, DSR); | 441 | rc = di_write_wait(imxdi, DSR_CAF, DSR); |
442 | if (rc) | 442 | if (rc) |
443 | goto err; | 443 | goto err; |
444 | } | 444 | } |
445 | 445 | ||
446 | /* the timer won't count if it has never been written to */ | 446 | /* the timer won't count if it has never been written to */ |
447 | if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) { | 447 | if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) { |
448 | rc = di_write_wait(imxdi, 0, DTCMR); | 448 | rc = di_write_wait(imxdi, 0, DTCMR); |
449 | if (rc) | 449 | if (rc) |
450 | goto err; | 450 | goto err; |
451 | } | 451 | } |
452 | 452 | ||
453 | /* start keeping time */ | 453 | /* start keeping time */ |
454 | if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) { | 454 | if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) { |
455 | rc = di_write_wait(imxdi, | 455 | rc = di_write_wait(imxdi, |
456 | __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE, | 456 | __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE, |
457 | DCR); | 457 | DCR); |
458 | if (rc) | 458 | if (rc) |
459 | goto err; | 459 | goto err; |
460 | } | 460 | } |
461 | 461 | ||
462 | platform_set_drvdata(pdev, imxdi); | 462 | platform_set_drvdata(pdev, imxdi); |
463 | imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev, | 463 | imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev, |
464 | &dryice_rtc_ops, THIS_MODULE); | 464 | &dryice_rtc_ops, THIS_MODULE); |
465 | if (IS_ERR(imxdi->rtc)) { | 465 | if (IS_ERR(imxdi->rtc)) { |
466 | rc = PTR_ERR(imxdi->rtc); | 466 | rc = PTR_ERR(imxdi->rtc); |
467 | goto err; | 467 | goto err; |
468 | } | 468 | } |
469 | 469 | ||
470 | return 0; | 470 | return 0; |
471 | 471 | ||
472 | err: | 472 | err: |
473 | clk_disable(imxdi->clk); | 473 | clk_disable_unprepare(imxdi->clk); |
474 | clk_put(imxdi->clk); | 474 | clk_put(imxdi->clk); |
475 | 475 | ||
476 | return rc; | 476 | return rc; |
477 | } | 477 | } |
478 | 478 | ||
479 | static int __devexit dryice_rtc_remove(struct platform_device *pdev) | 479 | static int __devexit dryice_rtc_remove(struct platform_device *pdev) |
480 | { | 480 | { |
481 | struct imxdi_dev *imxdi = platform_get_drvdata(pdev); | 481 | struct imxdi_dev *imxdi = platform_get_drvdata(pdev); |
482 | 482 | ||
483 | flush_work(&imxdi->work); | 483 | flush_work(&imxdi->work); |
484 | 484 | ||
485 | /* mask all interrupts */ | 485 | /* mask all interrupts */ |
486 | __raw_writel(0, imxdi->ioaddr + DIER); | 486 | __raw_writel(0, imxdi->ioaddr + DIER); |
487 | 487 | ||
488 | rtc_device_unregister(imxdi->rtc); | 488 | rtc_device_unregister(imxdi->rtc); |
489 | 489 | ||
490 | clk_disable(imxdi->clk); | 490 | clk_disable_unprepare(imxdi->clk); |
491 | clk_put(imxdi->clk); | 491 | clk_put(imxdi->clk); |
492 | 492 | ||
493 | return 0; | 493 | return 0; |
494 | } | 494 | } |
495 | 495 | ||
496 | static struct platform_driver dryice_rtc_driver = { | 496 | static struct platform_driver dryice_rtc_driver = { |
497 | .driver = { | 497 | .driver = { |
498 | .name = "imxdi_rtc", | 498 | .name = "imxdi_rtc", |
499 | .owner = THIS_MODULE, | 499 | .owner = THIS_MODULE, |
500 | }, | 500 | }, |
501 | .remove = __devexit_p(dryice_rtc_remove), | 501 | .remove = __devexit_p(dryice_rtc_remove), |
502 | }; | 502 | }; |
503 | 503 | ||
504 | static int __init dryice_rtc_init(void) | 504 | static int __init dryice_rtc_init(void) |
505 | { | 505 | { |
506 | return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe); | 506 | return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe); |
507 | } | 507 | } |
508 | 508 | ||
509 | static void __exit dryice_rtc_exit(void) | 509 | static void __exit dryice_rtc_exit(void) |
510 | { | 510 | { |
511 | platform_driver_unregister(&dryice_rtc_driver); | 511 | platform_driver_unregister(&dryice_rtc_driver); |
512 | } | 512 | } |
513 | 513 | ||
514 | module_init(dryice_rtc_init); | 514 | module_init(dryice_rtc_init); |
515 | module_exit(dryice_rtc_exit); | 515 | module_exit(dryice_rtc_exit); |
516 | 516 | ||
517 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | 517 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); |
518 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); | 518 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); |
519 | MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)"); | 519 | MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)"); |
520 | MODULE_LICENSE("GPL"); | 520 | MODULE_LICENSE("GPL"); |
521 | 521 |