diff options
author | Frank Li <Frank.Li@freescale.com> | 2015-05-26 12:25:57 -0400 |
---|---|---|
committer | Shawn Guo <shawnguo@kernel.org> | 2015-07-14 22:15:10 -0400 |
commit | d482893b1caa5345b066c9721bab6636059e2a61 (patch) | |
tree | 252e51f49da87cdae6416c1414e3a32367cc985b | |
parent | 20c305f66077d2e646b23336d4404261dc283cf9 (diff) |
rtc: snvs: use syscon to access register
snvs included rtc, on/off key, power-off module
change to syscon to access register
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
-rw-r--r-- | drivers/rtc/rtc-snvs.c | 132 |
1 files changed, 67 insertions, 65 deletions
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index d87a85cefb66..950c5d0b6dca 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c | |||
@@ -18,6 +18,10 @@ | |||
18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
19 | #include <linux/rtc.h> | 19 | #include <linux/rtc.h> |
20 | #include <linux/clk.h> | 20 | #include <linux/clk.h> |
21 | #include <linux/mfd/syscon.h> | ||
22 | #include <linux/regmap.h> | ||
23 | |||
24 | #define SNVS_LPREGISTER_OFFSET 0x34 | ||
21 | 25 | ||
22 | /* These register offsets are relative to LP (Low Power) range */ | 26 | /* These register offsets are relative to LP (Low Power) range */ |
23 | #define SNVS_LPCR 0x04 | 27 | #define SNVS_LPCR 0x04 |
@@ -37,31 +41,36 @@ | |||
37 | 41 | ||
38 | struct snvs_rtc_data { | 42 | struct snvs_rtc_data { |
39 | struct rtc_device *rtc; | 43 | struct rtc_device *rtc; |
40 | void __iomem *ioaddr; | 44 | struct regmap *regmap; |
45 | int offset; | ||
41 | int irq; | 46 | int irq; |
42 | spinlock_t lock; | ||
43 | struct clk *clk; | 47 | struct clk *clk; |
44 | }; | 48 | }; |
45 | 49 | ||
46 | static u32 rtc_read_lp_counter(void __iomem *ioaddr) | 50 | static u32 rtc_read_lp_counter(struct snvs_rtc_data *data) |
47 | { | 51 | { |
48 | u64 read1, read2; | 52 | u64 read1, read2; |
53 | u32 val; | ||
49 | 54 | ||
50 | do { | 55 | do { |
51 | read1 = readl(ioaddr + SNVS_LPSRTCMR); | 56 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val); |
57 | read1 = val; | ||
52 | read1 <<= 32; | 58 | read1 <<= 32; |
53 | read1 |= readl(ioaddr + SNVS_LPSRTCLR); | 59 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val); |
60 | read1 |= val; | ||
54 | 61 | ||
55 | read2 = readl(ioaddr + SNVS_LPSRTCMR); | 62 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val); |
63 | read2 = val; | ||
56 | read2 <<= 32; | 64 | read2 <<= 32; |
57 | read2 |= readl(ioaddr + SNVS_LPSRTCLR); | 65 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val); |
66 | read2 |= val; | ||
58 | } while (read1 != read2); | 67 | } while (read1 != read2); |
59 | 68 | ||
60 | /* Convert 47-bit counter to 32-bit raw second count */ | 69 | /* Convert 47-bit counter to 32-bit raw second count */ |
61 | return (u32) (read1 >> CNTR_TO_SECS_SH); | 70 | return (u32) (read1 >> CNTR_TO_SECS_SH); |
62 | } | 71 | } |
63 | 72 | ||
64 | static void rtc_write_sync_lp(void __iomem *ioaddr) | 73 | static void rtc_write_sync_lp(struct snvs_rtc_data *data) |
65 | { | 74 | { |
66 | u32 count1, count2, count3; | 75 | u32 count1, count2, count3; |
67 | int i; | 76 | int i; |
@@ -69,15 +78,15 @@ static void rtc_write_sync_lp(void __iomem *ioaddr) | |||
69 | /* Wait for 3 CKIL cycles */ | 78 | /* Wait for 3 CKIL cycles */ |
70 | for (i = 0; i < 3; i++) { | 79 | for (i = 0; i < 3; i++) { |
71 | do { | 80 | do { |
72 | count1 = readl(ioaddr + SNVS_LPSRTCLR); | 81 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); |
73 | count2 = readl(ioaddr + SNVS_LPSRTCLR); | 82 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2); |
74 | } while (count1 != count2); | 83 | } while (count1 != count2); |
75 | 84 | ||
76 | /* Now wait until counter value changes */ | 85 | /* Now wait until counter value changes */ |
77 | do { | 86 | do { |
78 | do { | 87 | do { |
79 | count2 = readl(ioaddr + SNVS_LPSRTCLR); | 88 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2); |
80 | count3 = readl(ioaddr + SNVS_LPSRTCLR); | 89 | regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count3); |
81 | } while (count2 != count3); | 90 | } while (count2 != count3); |
82 | } while (count3 == count1); | 91 | } while (count3 == count1); |
83 | } | 92 | } |
@@ -85,23 +94,14 @@ static void rtc_write_sync_lp(void __iomem *ioaddr) | |||
85 | 94 | ||
86 | static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) | 95 | static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) |
87 | { | 96 | { |
88 | unsigned long flags; | ||
89 | int timeout = 1000; | 97 | int timeout = 1000; |
90 | u32 lpcr; | 98 | u32 lpcr; |
91 | 99 | ||
92 | spin_lock_irqsave(&data->lock, flags); | 100 | regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV, |
93 | 101 | enable ? SNVS_LPCR_SRTC_ENV : 0); | |
94 | lpcr = readl(data->ioaddr + SNVS_LPCR); | ||
95 | if (enable) | ||
96 | lpcr |= SNVS_LPCR_SRTC_ENV; | ||
97 | else | ||
98 | lpcr &= ~SNVS_LPCR_SRTC_ENV; | ||
99 | writel(lpcr, data->ioaddr + SNVS_LPCR); | ||
100 | |||
101 | spin_unlock_irqrestore(&data->lock, flags); | ||
102 | 102 | ||
103 | while (--timeout) { | 103 | while (--timeout) { |
104 | lpcr = readl(data->ioaddr + SNVS_LPCR); | 104 | regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr); |
105 | 105 | ||
106 | if (enable) { | 106 | if (enable) { |
107 | if (lpcr & SNVS_LPCR_SRTC_ENV) | 107 | if (lpcr & SNVS_LPCR_SRTC_ENV) |
@@ -121,7 +121,7 @@ static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) | |||
121 | static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm) | 121 | static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm) |
122 | { | 122 | { |
123 | struct snvs_rtc_data *data = dev_get_drvdata(dev); | 123 | struct snvs_rtc_data *data = dev_get_drvdata(dev); |
124 | unsigned long time = rtc_read_lp_counter(data->ioaddr); | 124 | unsigned long time = rtc_read_lp_counter(data); |
125 | 125 | ||
126 | rtc_time_to_tm(time, tm); | 126 | rtc_time_to_tm(time, tm); |
127 | 127 | ||
@@ -139,8 +139,8 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
139 | snvs_rtc_enable(data, false); | 139 | snvs_rtc_enable(data, false); |
140 | 140 | ||
141 | /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ | 141 | /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ |
142 | writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR); | 142 | regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH); |
143 | writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR); | 143 | regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH)); |
144 | 144 | ||
145 | /* Enable RTC again */ | 145 | /* Enable RTC again */ |
146 | snvs_rtc_enable(data, true); | 146 | snvs_rtc_enable(data, true); |
@@ -153,10 +153,10 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
153 | struct snvs_rtc_data *data = dev_get_drvdata(dev); | 153 | struct snvs_rtc_data *data = dev_get_drvdata(dev); |
154 | u32 lptar, lpsr; | 154 | u32 lptar, lpsr; |
155 | 155 | ||
156 | lptar = readl(data->ioaddr + SNVS_LPTAR); | 156 | regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar); |
157 | rtc_time_to_tm(lptar, &alrm->time); | 157 | rtc_time_to_tm(lptar, &alrm->time); |
158 | 158 | ||
159 | lpsr = readl(data->ioaddr + SNVS_LPSR); | 159 | regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); |
160 | alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0; | 160 | alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0; |
161 | 161 | ||
162 | return 0; | 162 | return 0; |
@@ -165,21 +165,12 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
165 | static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) | 165 | static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) |
166 | { | 166 | { |
167 | struct snvs_rtc_data *data = dev_get_drvdata(dev); | 167 | struct snvs_rtc_data *data = dev_get_drvdata(dev); |
168 | u32 lpcr; | ||
169 | unsigned long flags; | ||
170 | |||
171 | spin_lock_irqsave(&data->lock, flags); | ||
172 | 168 | ||
173 | lpcr = readl(data->ioaddr + SNVS_LPCR); | 169 | regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, |
174 | if (enable) | 170 | (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN), |
175 | lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN); | 171 | enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0); |
176 | else | ||
177 | lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN); | ||
178 | writel(lpcr, data->ioaddr + SNVS_LPCR); | ||
179 | 172 | ||
180 | spin_unlock_irqrestore(&data->lock, flags); | 173 | rtc_write_sync_lp(data); |
181 | |||
182 | rtc_write_sync_lp(data->ioaddr); | ||
183 | 174 | ||
184 | return 0; | 175 | return 0; |
185 | } | 176 | } |
@@ -189,24 +180,14 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
189 | struct snvs_rtc_data *data = dev_get_drvdata(dev); | 180 | struct snvs_rtc_data *data = dev_get_drvdata(dev); |
190 | struct rtc_time *alrm_tm = &alrm->time; | 181 | struct rtc_time *alrm_tm = &alrm->time; |
191 | unsigned long time; | 182 | unsigned long time; |
192 | unsigned long flags; | ||
193 | u32 lpcr; | ||
194 | 183 | ||
195 | rtc_tm_to_time(alrm_tm, &time); | 184 | rtc_tm_to_time(alrm_tm, &time); |
196 | 185 | ||
197 | spin_lock_irqsave(&data->lock, flags); | 186 | regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0); |
198 | 187 | regmap_write(data->regmap, data->offset + SNVS_LPTAR, time); | |
199 | /* Have to clear LPTA_EN before programming new alarm time in LPTAR */ | ||
200 | lpcr = readl(data->ioaddr + SNVS_LPCR); | ||
201 | lpcr &= ~SNVS_LPCR_LPTA_EN; | ||
202 | writel(lpcr, data->ioaddr + SNVS_LPCR); | ||
203 | |||
204 | spin_unlock_irqrestore(&data->lock, flags); | ||
205 | |||
206 | writel(time, data->ioaddr + SNVS_LPTAR); | ||
207 | 188 | ||
208 | /* Clear alarm interrupt status bit */ | 189 | /* Clear alarm interrupt status bit */ |
209 | writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR); | 190 | regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA); |
210 | 191 | ||
211 | return snvs_rtc_alarm_irq_enable(dev, alrm->enabled); | 192 | return snvs_rtc_alarm_irq_enable(dev, alrm->enabled); |
212 | } | 193 | } |
@@ -226,7 +207,7 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) | |||
226 | u32 lpsr; | 207 | u32 lpsr; |
227 | u32 events = 0; | 208 | u32 events = 0; |
228 | 209 | ||
229 | lpsr = readl(data->ioaddr + SNVS_LPSR); | 210 | regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); |
230 | 211 | ||
231 | if (lpsr & SNVS_LPSR_LPTA) { | 212 | if (lpsr & SNVS_LPSR_LPTA) { |
232 | events |= (RTC_AF | RTC_IRQF); | 213 | events |= (RTC_AF | RTC_IRQF); |
@@ -238,25 +219,48 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) | |||
238 | } | 219 | } |
239 | 220 | ||
240 | /* clear interrupt status */ | 221 | /* clear interrupt status */ |
241 | writel(lpsr, data->ioaddr + SNVS_LPSR); | 222 | regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr); |
242 | 223 | ||
243 | return events ? IRQ_HANDLED : IRQ_NONE; | 224 | return events ? IRQ_HANDLED : IRQ_NONE; |
244 | } | 225 | } |
245 | 226 | ||
227 | static const struct regmap_config snvs_rtc_config = { | ||
228 | .reg_bits = 32, | ||
229 | .val_bits = 32, | ||
230 | .reg_stride = 4, | ||
231 | }; | ||
232 | |||
246 | static int snvs_rtc_probe(struct platform_device *pdev) | 233 | static int snvs_rtc_probe(struct platform_device *pdev) |
247 | { | 234 | { |
248 | struct snvs_rtc_data *data; | 235 | struct snvs_rtc_data *data; |
249 | struct resource *res; | 236 | struct resource *res; |
250 | int ret; | 237 | int ret; |
238 | void __iomem *mmio; | ||
251 | 239 | ||
252 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | 240 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
253 | if (!data) | 241 | if (!data) |
254 | return -ENOMEM; | 242 | return -ENOMEM; |
255 | 243 | ||
256 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 244 | data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap"); |
257 | data->ioaddr = devm_ioremap_resource(&pdev->dev, res); | 245 | |
258 | if (IS_ERR(data->ioaddr)) | 246 | if (IS_ERR(data->regmap)) { |
259 | return PTR_ERR(data->ioaddr); | 247 | dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n"); |
248 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
249 | |||
250 | mmio = devm_ioremap_resource(&pdev->dev, res); | ||
251 | if (IS_ERR(mmio)) | ||
252 | return PTR_ERR(mmio); | ||
253 | |||
254 | data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config); | ||
255 | } else { | ||
256 | data->offset = SNVS_LPREGISTER_OFFSET; | ||
257 | of_property_read_u32(pdev->dev.of_node, "offset", &data->offset); | ||
258 | } | ||
259 | |||
260 | if (!data->regmap) { | ||
261 | dev_err(&pdev->dev, "Can't find snvs syscon\n"); | ||
262 | return -ENODEV; | ||
263 | } | ||
260 | 264 | ||
261 | data->irq = platform_get_irq(pdev, 0); | 265 | data->irq = platform_get_irq(pdev, 0); |
262 | if (data->irq < 0) | 266 | if (data->irq < 0) |
@@ -276,13 +280,11 @@ static int snvs_rtc_probe(struct platform_device *pdev) | |||
276 | 280 | ||
277 | platform_set_drvdata(pdev, data); | 281 | platform_set_drvdata(pdev, data); |
278 | 282 | ||
279 | spin_lock_init(&data->lock); | ||
280 | |||
281 | /* Initialize glitch detect */ | 283 | /* Initialize glitch detect */ |
282 | writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR); | 284 | regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT); |
283 | 285 | ||
284 | /* Clear interrupt status */ | 286 | /* Clear interrupt status */ |
285 | writel(0xffffffff, data->ioaddr + SNVS_LPSR); | 287 | regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff); |
286 | 288 | ||
287 | /* Enable RTC */ | 289 | /* Enable RTC */ |
288 | snvs_rtc_enable(data, true); | 290 | snvs_rtc_enable(data, true); |