Blame view

drivers/rtc/rtc-pcf2123.c 12.3 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
7f3923a18   Chris Verges   rtc: Philips PCF2...
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * An SPI driver for the Philips PCF2123 RTC
   * Copyright 2009 Cyber Switching, Inc.
   *
   * Author: Chris Verges <chrisv@cyberswitching.com>
   * Maintainers: http://www.cyberswitching.com
   *
   * based on the RS5C348 driver in this same directory.
   *
   * Thanks to Christian Pellegrin <chripell@fsfe.org> for
   * the sysfs contributions to this driver.
   *
7f3923a18   Chris Verges   rtc: Philips PCF2...
14
15
16
17
   * Please note that the CS is active high, so platform data
   * should look something like:
   *
   * static struct spi_board_info ek_spi_devices[] = {
369015fbd   Sachin Kamat   drivers/rtc/rtc-p...
18
19
20
21
22
   *	...
   *	{
   *		.modalias		= "rtc-pcf2123",
   *		.chip_select		= 1,
   *		.controller_data	= (void *)AT91_PIN_PA10,
7f3923a18   Chris Verges   rtc: Philips PCF2...
23
24
25
26
27
28
   *		.max_speed_hz		= 1000 * 1000,
   *		.mode			= SPI_CS_HIGH,
   *		.bus_num		= 0,
   *	},
   *	...
   *};
7f3923a18   Chris Verges   rtc: Philips PCF2...
29
30
31
32
33
34
35
36
   */
  
  #include <linux/bcd.h>
  #include <linux/delay.h>
  #include <linux/device.h>
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
3fc70077e   Joshua Clayton   drivers/rtc/rtc-p...
37
  #include <linux/of.h>
7f3923a18   Chris Verges   rtc: Philips PCF2...
38
  #include <linux/string.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
39
  #include <linux/slab.h>
7f3923a18   Chris Verges   rtc: Philips PCF2...
40
41
  #include <linux/rtc.h>
  #include <linux/spi/spi.h>
2113852b2   Paul Gortmaker   rtc: Add module.h...
42
  #include <linux/module.h>
790d03393   Dylan Howey   rtc: pcf2123: por...
43
  #include <linux/regmap.h>
7f3923a18   Chris Verges   rtc: Philips PCF2...
44

245cb74be   Joshua Clayton   rtc: pcf2123: def...
45
  /* REGISTERS */
7f3923a18   Chris Verges   rtc: Philips PCF2...
46
47
48
49
50
51
52
53
54
  #define PCF2123_REG_CTRL1	(0x00)	/* Control Register 1 */
  #define PCF2123_REG_CTRL2	(0x01)	/* Control Register 2 */
  #define PCF2123_REG_SC		(0x02)	/* datetime */
  #define PCF2123_REG_MN		(0x03)
  #define PCF2123_REG_HR		(0x04)
  #define PCF2123_REG_DM		(0x05)
  #define PCF2123_REG_DW		(0x06)
  #define PCF2123_REG_MO		(0x07)
  #define PCF2123_REG_YR		(0x08)
245cb74be   Joshua Clayton   rtc: pcf2123: def...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  #define PCF2123_REG_ALRM_MN	(0x09)	/* Alarm Registers */
  #define PCF2123_REG_ALRM_HR	(0x0a)
  #define PCF2123_REG_ALRM_DM	(0x0b)
  #define PCF2123_REG_ALRM_DW	(0x0c)
  #define PCF2123_REG_OFFSET	(0x0d)	/* Clock Rate Offset Register */
  #define PCF2123_REG_TMR_CLKOUT	(0x0e)	/* Timer Registers */
  #define PCF2123_REG_CTDWN_TMR	(0x0f)
  
  /* PCF2123_REG_CTRL1 BITS */
  #define CTRL1_CLEAR		(0)	/* Clear */
  #define CTRL1_CORR_INT		BIT(1)	/* Correction irq enable */
  #define CTRL1_12_HOUR		BIT(2)	/* 12 hour time */
  #define CTRL1_SW_RESET	(BIT(3) | BIT(4) | BIT(6))	/* Software reset */
  #define CTRL1_STOP		BIT(5)	/* Stop the clock */
  #define CTRL1_EXT_TEST		BIT(7)	/* External clock test mode */
  
  /* PCF2123_REG_CTRL2 BITS */
  #define CTRL2_TIE		BIT(0)	/* Countdown timer irq enable */
  #define CTRL2_AIE		BIT(1)	/* Alarm irq enable */
  #define CTRL2_TF		BIT(2)	/* Countdown timer flag */
  #define CTRL2_AF		BIT(3)	/* Alarm flag */
  #define CTRL2_TI_TP		BIT(4)	/* Irq pin generates pulse */
  #define CTRL2_MSF		BIT(5)	/* Minute or second irq flag */
  #define CTRL2_SI		BIT(6)	/* Second irq enable */
  #define CTRL2_MI		BIT(7)	/* Minute irq enable */
  
  /* PCF2123_REG_SC BITS */
  #define OSC_HAS_STOPPED		BIT(7)	/* Clock has been stopped */
  
  /* PCF2123_REG_ALRM_XX BITS */
5bdf40dab   Alexandre Belloni   rtc: pcf2123: don...
85
  #define ALRM_DISABLE		BIT(7)	/* MN, HR, DM, or DW alarm matching */
245cb74be   Joshua Clayton   rtc: pcf2123: def...
86
87
88
89
90
91
92
93
94
  
  /* PCF2123_REG_TMR_CLKOUT BITS */
  #define CD_TMR_4096KHZ		(0)	/* 4096 KHz countdown timer */
  #define CD_TMR_64HZ		(1)	/* 64 Hz countdown timer */
  #define CD_TMR_1HZ		(2)	/* 1 Hz countdown timer */
  #define CD_TMR_60th_HZ		(3)	/* 60th Hz countdown timer */
  #define CD_TMR_TE		BIT(3)	/* Countdown timer enable */
  
  /* PCF2123_REG_OFFSET BITS */
82df3e045   Martin Kepplinger   rtc: pcf2123: use...
95
  #define OFFSET_SIGN_BIT		6	/* 2's complement sign bit */
245cb74be   Joshua Clayton   rtc: pcf2123: def...
96
  #define OFFSET_COARSE		BIT(7)	/* Coarse mode offset */
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
97
  #define OFFSET_STEP		(2170)	/* Offset step in parts per billion */
790d03393   Dylan Howey   rtc: pcf2123: por...
98
  #define OFFSET_MASK		GENMASK(6, 0)	/* Offset value */
245cb74be   Joshua Clayton   rtc: pcf2123: def...
99
100
101
102
  
  /* READ/WRITE ADDRESS BITS */
  #define PCF2123_WRITE		BIT(4)
  #define PCF2123_READ		(BIT(4) | BIT(7))
7f3923a18   Chris Verges   rtc: Philips PCF2...
103

7f3923a18   Chris Verges   rtc: Philips PCF2...
104
105
  
  static struct spi_driver pcf2123_driver;
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
106
  struct pcf2123_data {
7f3923a18   Chris Verges   rtc: Philips PCF2...
107
  	struct rtc_device *rtc;
790d03393   Dylan Howey   rtc: pcf2123: por...
108
  	struct regmap *map;
7f3923a18   Chris Verges   rtc: Philips PCF2...
109
  };
790d03393   Dylan Howey   rtc: pcf2123: por...
110
111
112
113
114
115
116
  static const struct regmap_config pcf2123_regmap_config = {
  	.reg_bits = 8,
  	.val_bits = 8,
  	.read_flag_mask = PCF2123_READ,
  	.write_flag_mask = PCF2123_WRITE,
  	.max_register = PCF2123_REG_CTDWN_TMR,
  };
7f3923a18   Chris Verges   rtc: Philips PCF2...
117

bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
118
  static int pcf2123_read_offset(struct device *dev, long *offset)
83ab7dad0   Christophe JAILLET   rtc: pcf2123: Add...
119
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
120
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
790d03393   Dylan Howey   rtc: pcf2123: por...
121
122
  	int ret, val;
  	unsigned int reg;
7f3923a18   Chris Verges   rtc: Philips PCF2...
123

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
124
  	ret = regmap_read(pcf2123->map, PCF2123_REG_OFFSET, &reg);
4c5591c1e   Jingoo Han   drivers/rtc/rtc-p...
125
126
  	if (ret)
  		return ret;
790d03393   Dylan Howey   rtc: pcf2123: por...
127
  	val = sign_extend32((reg & OFFSET_MASK), OFFSET_SIGN_BIT);
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
128
129
  
  	if (reg & OFFSET_COARSE)
790d03393   Dylan Howey   rtc: pcf2123: por...
130
  		val *= 2;
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
131

790d03393   Dylan Howey   rtc: pcf2123: por...
132
  	*offset = ((long)val) * OFFSET_STEP;
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  
  	return 0;
  }
  
  /*
   * The offset register is a 7 bit signed value with a coarse bit in bit 7.
   * The main difference between the two is normal offset adjusts the first
   * second of n minutes every other hour, with 61, 62 and 63 being shoved
   * into the 60th minute.
   * The coarse adjustment does the same, but every hour.
   * the two overlap, with every even normal offset value corresponding
   * to a coarse offset. Based on this algorithm, it seems that despite the
   * name, coarse offset is a better fit for overlapping values.
   */
  static int pcf2123_set_offset(struct device *dev, long offset)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
149
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
150
151
152
153
154
155
156
  	s8 reg;
  
  	if (offset > OFFSET_STEP * 127)
  		reg = 127;
  	else if (offset < OFFSET_STEP * -128)
  		reg = -128;
  	else
fedc459a3   Alexandre Belloni   rtc: pcf2123: fix...
157
  		reg = DIV_ROUND_CLOSEST(offset, OFFSET_STEP);
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
158
159
160
161
162
163
164
165
166
167
  
  	/* choose fine offset only for odd values in the normal range */
  	if (reg & 1 && reg <= 63 && reg >= -64) {
  		/* Normal offset. Clear the coarse bit */
  		reg &= ~OFFSET_COARSE;
  	} else {
  		/* Coarse offset. Divide by 2 and set the coarse bit */
  		reg >>= 1;
  		reg |= OFFSET_COARSE;
  	}
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
168
  	return regmap_write(pcf2123->map, PCF2123_REG_OFFSET, (unsigned int)reg);
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
169
  }
7f3923a18   Chris Verges   rtc: Philips PCF2...
170
171
  static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
172
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
66c056d6a   Joshua Clayton   rtc: pcf2123: cle...
173
  	u8 rxbuf[7];
7f3923a18   Chris Verges   rtc: Philips PCF2...
174
  	int ret;
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
175
  	ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_SC, rxbuf,
790d03393   Dylan Howey   rtc: pcf2123: por...
176
177
  				sizeof(rxbuf));
  	if (ret)
7f3923a18   Chris Verges   rtc: Philips PCF2...
178
  		return ret;
7f3923a18   Chris Verges   rtc: Philips PCF2...
179

f07fa9242   Joshua Clayton   rtc: pcf2123: avo...
180
181
182
183
184
  	if (rxbuf[0] & OSC_HAS_STOPPED) {
  		dev_info(dev, "clock was stopped. Time is not valid
  ");
  		return -EINVAL;
  	}
7f3923a18   Chris Verges   rtc: Philips PCF2...
185
186
187
188
189
190
  	tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F);
  	tm->tm_min = bcd2bin(rxbuf[1] & 0x7F);
  	tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */
  	tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F);
  	tm->tm_wday = rxbuf[4] & 0x07;
  	tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */
d5b626e13   Alexandre Belloni   rtc: pcf2123: let...
191
  	tm->tm_year = bcd2bin(rxbuf[6]) + 100;
7f3923a18   Chris Verges   rtc: Philips PCF2...
192

c33850bbc   Dylan Howey   rtc: pcf2123: use...
193
194
  	dev_dbg(dev, "%s: tm is %ptR
  ", __func__, tm);
7f3923a18   Chris Verges   rtc: Philips PCF2...
195

22652ba72   Alexandre Belloni   rtc: stop validat...
196
  	return 0;
7f3923a18   Chris Verges   rtc: Philips PCF2...
197
198
199
200
  }
  
  static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
201
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
790d03393   Dylan Howey   rtc: pcf2123: por...
202
  	u8 txbuf[7];
7f3923a18   Chris Verges   rtc: Philips PCF2...
203
  	int ret;
c33850bbc   Dylan Howey   rtc: pcf2123: use...
204
205
  	dev_dbg(dev, "%s: tm is %ptR
  ", __func__, tm);
7f3923a18   Chris Verges   rtc: Philips PCF2...
206
207
  
  	/* Stop the counter first */
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
208
  	ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP);
790d03393   Dylan Howey   rtc: pcf2123: por...
209
  	if (ret)
7f3923a18   Chris Verges   rtc: Philips PCF2...
210
  		return ret;
7f3923a18   Chris Verges   rtc: Philips PCF2...
211
212
  
  	/* Set the new time */
790d03393   Dylan Howey   rtc: pcf2123: por...
213
214
215
216
217
218
  	txbuf[0] = bin2bcd(tm->tm_sec & 0x7F);
  	txbuf[1] = bin2bcd(tm->tm_min & 0x7F);
  	txbuf[2] = bin2bcd(tm->tm_hour & 0x3F);
  	txbuf[3] = bin2bcd(tm->tm_mday & 0x3F);
  	txbuf[4] = tm->tm_wday & 0x07;
  	txbuf[5] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */
d5b626e13   Alexandre Belloni   rtc: pcf2123: let...
219
  	txbuf[6] = bin2bcd(tm->tm_year - 100);
790d03393   Dylan Howey   rtc: pcf2123: por...
220

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
221
  	ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_SC, txbuf,
790d03393   Dylan Howey   rtc: pcf2123: por...
222
223
  				sizeof(txbuf));
  	if (ret)
7f3923a18   Chris Verges   rtc: Philips PCF2...
224
  		return ret;
7f3923a18   Chris Verges   rtc: Philips PCF2...
225
226
  
  	/* Start the counter */
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
227
  	ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR);
790d03393   Dylan Howey   rtc: pcf2123: por...
228
  	if (ret)
7f3923a18   Chris Verges   rtc: Philips PCF2...
229
  		return ret;
7f3923a18   Chris Verges   rtc: Philips PCF2...
230
231
232
  
  	return 0;
  }
577f64820   Alexandre Belloni   rtc: pcf2123: imp...
233
234
  static int pcf2123_rtc_alarm_irq_enable(struct device *dev, unsigned int en)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
235
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
577f64820   Alexandre Belloni   rtc: pcf2123: imp...
236

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
237
  	return regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE,
577f64820   Alexandre Belloni   rtc: pcf2123: imp...
238
239
  				  en ? CTRL2_AIE : 0);
  }
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
240
241
  static int pcf2123_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
242
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
243
244
245
  	u8 rxbuf[4];
  	int ret;
  	unsigned int val = 0;
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
246
  	ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_ALRM_MN, rxbuf,
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
247
248
249
250
251
252
253
254
255
256
257
  				sizeof(rxbuf));
  	if (ret)
  		return ret;
  
  	alm->time.tm_min = bcd2bin(rxbuf[0] & 0x7F);
  	alm->time.tm_hour = bcd2bin(rxbuf[1] & 0x3F);
  	alm->time.tm_mday = bcd2bin(rxbuf[2] & 0x3F);
  	alm->time.tm_wday = bcd2bin(rxbuf[3] & 0x07);
  
  	dev_dbg(dev, "%s: alm is %ptR
  ", __func__, &alm->time);
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
258
  	ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
259
260
261
262
263
264
265
266
267
268
  	if (ret)
  		return ret;
  
  	alm->enabled = !!(val & CTRL2_AIE);
  
  	return 0;
  }
  
  static int pcf2123_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
269
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
270
271
272
273
274
  	u8 txbuf[4];
  	int ret;
  
  	dev_dbg(dev, "%s: alm is %ptR
  ", __func__, &alm->time);
d0ce6ef71   Alexandre Belloni   rtc; pcf2123: fix...
275
  	/* Disable alarm interrupt */
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
276
  	ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, 0);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
277
  	if (ret)
7f3923a18   Chris Verges   rtc: Philips PCF2...
278
  		return ret;
7f3923a18   Chris Verges   rtc: Philips PCF2...
279

d0ce6ef71   Alexandre Belloni   rtc; pcf2123: fix...
280
  	/* Ensure alarm flag is clear */
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
281
  	ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
282
283
284
285
286
287
288
  	if (ret)
  		return ret;
  
  	/* Set new alarm */
  	txbuf[0] = bin2bcd(alm->time.tm_min & 0x7F);
  	txbuf[1] = bin2bcd(alm->time.tm_hour & 0x3F);
  	txbuf[2] = bin2bcd(alm->time.tm_mday & 0x3F);
5bdf40dab   Alexandre Belloni   rtc: pcf2123: don...
289
  	txbuf[3] = ALRM_DISABLE;
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
290

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
291
  	ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_ALRM_MN, txbuf,
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
292
293
294
  				sizeof(txbuf));
  	if (ret)
  		return ret;
577f64820   Alexandre Belloni   rtc: pcf2123: imp...
295
  	return pcf2123_rtc_alarm_irq_enable(dev, alm->enabled);
7f3923a18   Chris Verges   rtc: Philips PCF2...
296
  }
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
297
298
  static irqreturn_t pcf2123_rtc_irq(int irq, void *dev)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
299
300
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
  	struct mutex *lock = &pcf2123->rtc->ops_lock;
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
301
302
303
304
  	unsigned int val = 0;
  	int ret = IRQ_NONE;
  
  	mutex_lock(lock);
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
305
  	regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
306
307
308
309
310
311
  
  	/* Alarm? */
  	if (val & CTRL2_AF) {
  		ret = IRQ_HANDLED;
  
  		/* Clear alarm flag */
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
312
  		regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
313

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
314
  		rtc_update_irq(pcf2123->rtc, 1, RTC_IRQF | RTC_AF);
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
315
316
317
318
319
320
  	}
  
  	mutex_unlock(lock);
  
  	return ret;
  }
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
321
322
  static int pcf2123_reset(struct device *dev)
  {
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
323
  	struct pcf2123_data *pcf2123 = dev_get_drvdata(dev);
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
324
  	int ret;
790d03393   Dylan Howey   rtc: pcf2123: por...
325
  	unsigned int val = 0;
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
326

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
327
  	ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_SW_RESET);
790d03393   Dylan Howey   rtc: pcf2123: por...
328
  	if (ret)
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
329
330
331
332
333
  		return ret;
  
  	/* Stop the counter */
  	dev_dbg(dev, "stopping RTC
  ");
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
334
  	ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP);
790d03393   Dylan Howey   rtc: pcf2123: por...
335
  	if (ret)
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
336
337
338
339
340
  		return ret;
  
  	/* See if the counter was actually stopped */
  	dev_dbg(dev, "checking for presence of RTC
  ");
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
341
  	ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL1, &val);
790d03393   Dylan Howey   rtc: pcf2123: por...
342
  	if (ret)
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
343
  		return ret;
790d03393   Dylan Howey   rtc: pcf2123: por...
344
345
346
  	dev_dbg(dev, "received data from RTC (0x%08X)
  ", val);
  	if (!(val & CTRL1_STOP))
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
347
348
349
  		return -ENODEV;
  
  	/* Start the counter */
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
350
  	ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR);
790d03393   Dylan Howey   rtc: pcf2123: por...
351
  	if (ret)
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
352
353
354
355
  		return ret;
  
  	return 0;
  }
7f3923a18   Chris Verges   rtc: Philips PCF2...
356
357
358
  static const struct rtc_class_ops pcf2123_rtc_ops = {
  	.read_time	= pcf2123_rtc_read_time,
  	.set_time	= pcf2123_rtc_set_time,
bae2f6470   Joshua Clayton   rtc: pcf2123: imp...
359
360
  	.read_offset	= pcf2123_read_offset,
  	.set_offset	= pcf2123_set_offset,
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
361
362
  	.read_alarm	= pcf2123_rtc_read_alarm,
  	.set_alarm	= pcf2123_rtc_set_alarm,
577f64820   Alexandre Belloni   rtc: pcf2123: imp...
363
  	.alarm_irq_enable = pcf2123_rtc_alarm_irq_enable,
7f3923a18   Chris Verges   rtc: Philips PCF2...
364
  };
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
365
  static int pcf2123_probe(struct spi_device *spi)
7f3923a18   Chris Verges   rtc: Philips PCF2...
366
367
  {
  	struct rtc_device *rtc;
f07fa9242   Joshua Clayton   rtc: pcf2123: avo...
368
  	struct rtc_time tm;
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
369
  	struct pcf2123_data *pcf2123;
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
370
  	int ret = 0;
7f3923a18   Chris Verges   rtc: Philips PCF2...
371

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
372
  	pcf2123 = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_data),
dd48ccc49   Jingoo Han   rtc: rtc-pcf2123:...
373
  				GFP_KERNEL);
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
374
  	if (!pcf2123)
7f3923a18   Chris Verges   rtc: Philips PCF2...
375
  		return -ENOMEM;
d3bad6026   Alexandre Belloni   rtc: pcf2123: sto...
376

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
377
  	dev_set_drvdata(&spi->dev, pcf2123);
7f3923a18   Chris Verges   rtc: Philips PCF2...
378

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
379
  	pcf2123->map = devm_regmap_init_spi(spi, &pcf2123_regmap_config);
9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
380
  	if (IS_ERR(pcf2123->map)) {
790d03393   Dylan Howey   rtc: pcf2123: por...
381
382
  		dev_err(&spi->dev, "regmap init failed.
  ");
9a5aeaad7   Alexandre Belloni   rtc: pcf2123: rem...
383
  		return PTR_ERR(pcf2123->map);
790d03393   Dylan Howey   rtc: pcf2123: por...
384
  	}
f07fa9242   Joshua Clayton   rtc: pcf2123: avo...
385
  	ret = pcf2123_rtc_read_time(&spi->dev, &tm);
1e094b946   Joshua Clayton   rtc: pcf2123: ref...
386
  	if (ret < 0) {
f07fa9242   Joshua Clayton   rtc: pcf2123: avo...
387
388
389
390
  		ret = pcf2123_reset(&spi->dev);
  		if (ret < 0) {
  			dev_err(&spi->dev, "chip not found
  ");
9a5aeaad7   Alexandre Belloni   rtc: pcf2123: rem...
391
  			return ret;
f07fa9242   Joshua Clayton   rtc: pcf2123: avo...
392
  		}
7f3923a18   Chris Verges   rtc: Philips PCF2...
393
  	}
7f3923a18   Chris Verges   rtc: Philips PCF2...
394
395
396
  	dev_info(&spi->dev, "spiclk %u KHz.
  ",
  			(spi->max_speed_hz + 500) / 1000);
7f3923a18   Chris Verges   rtc: Philips PCF2...
397
  	/* Finalize the initialization */
935a7f459   Alexandre Belloni   rtc: pcf2123: con...
398
399
  	rtc = devm_rtc_allocate_device(&spi->dev);
  	if (IS_ERR(rtc))
9a5aeaad7   Alexandre Belloni   rtc: pcf2123: rem...
400
  		return PTR_ERR(rtc);
7f3923a18   Chris Verges   rtc: Philips PCF2...
401

9126a2b16   Alexandre Belloni   rtc: pcf2123: ren...
402
  	pcf2123->rtc = rtc;
7f3923a18   Chris Verges   rtc: Philips PCF2...
403

e32e60a2d   Dylan Howey   rtc: pcf2123: add...
404
405
406
407
408
409
410
411
412
413
414
  	/* Register alarm irq */
  	if (spi->irq > 0) {
  		ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
  				pcf2123_rtc_irq,
  				IRQF_TRIGGER_LOW | IRQF_ONESHOT,
  				pcf2123_driver.driver.name, &spi->dev);
  		if (!ret)
  			device_init_wakeup(&spi->dev, true);
  		else
  			dev_err(&spi->dev, "could not request irq.
  ");
7f3923a18   Chris Verges   rtc: Philips PCF2...
415
  	}
e32e60a2d   Dylan Howey   rtc: pcf2123: add...
416
417
418
419
  	/* The PCF2123's alarm only has minute accuracy. Must add timer
  	 * support to this driver to generate interrupts more than once
  	 * per minute.
  	 */
935a7f459   Alexandre Belloni   rtc: pcf2123: con...
420
421
  	rtc->uie_unsupported = 1;
  	rtc->ops = &pcf2123_rtc_ops;
d5b626e13   Alexandre Belloni   rtc: pcf2123: let...
422
423
424
  	rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
  	rtc->range_max = RTC_TIMESTAMP_END_2099;
  	rtc->set_start_time = true;
935a7f459   Alexandre Belloni   rtc: pcf2123: con...
425
426
427
428
  
  	ret = rtc_register_device(rtc);
  	if (ret)
  		return ret;
f3d2570a1   Chris Verges   rtc-philips-pcf21...
429

7f3923a18   Chris Verges   rtc: Philips PCF2...
430
  	return 0;
7f3923a18   Chris Verges   rtc: Philips PCF2...
431
  }
3fc70077e   Joshua Clayton   drivers/rtc/rtc-p...
432
433
  #ifdef CONFIG_OF
  static const struct of_device_id pcf2123_dt_ids[] = {
cb36cf803   Alexandre Belloni   rtc: pcf2123: add...
434
  	{ .compatible = "nxp,pcf2123", },
3c3d71013   Alexandre Belloni   rtc: pcf2123: Add...
435
  	{ .compatible = "microcrystal,rv2123", },
cb36cf803   Alexandre Belloni   rtc: pcf2123: add...
436
437
  	/* Deprecated, do not use */
  	{ .compatible = "nxp,rtc-pcf2123", },
3fc70077e   Joshua Clayton   drivers/rtc/rtc-p...
438
439
440
441
  	{ /* sentinel */ }
  };
  MODULE_DEVICE_TABLE(of, pcf2123_dt_ids);
  #endif
7f3923a18   Chris Verges   rtc: Philips PCF2...
442
443
444
  static struct spi_driver pcf2123_driver = {
  	.driver	= {
  			.name	= "rtc-pcf2123",
3fc70077e   Joshua Clayton   drivers/rtc/rtc-p...
445
  			.of_match_table = of_match_ptr(pcf2123_dt_ids),
7f3923a18   Chris Verges   rtc: Philips PCF2...
446
447
  	},
  	.probe	= pcf2123_probe,
7f3923a18   Chris Verges   rtc: Philips PCF2...
448
  };
109e94184   Axel Lin   rtc: convert rtc ...
449
  module_spi_driver(pcf2123_driver);
7f3923a18   Chris Verges   rtc: Philips PCF2...
450
451
452
453
  
  MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>");
  MODULE_DESCRIPTION("NXP PCF2123 RTC driver");
  MODULE_LICENSE("GPL");