Blame view

drivers/rtc/rtc-m48t35.c 4.38 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
2
3
4
5
6
7
8
9
10
  /*
   * Driver for the SGS-Thomson M48T35 Timekeeper RAM chip
   *
   * Copyright (C) 2000 Silicon Graphics, Inc.
   * Written by Ulf Carlsson (ulfc@engr.sgi.com)
   *
   * Copyright (C) 2008 Thomas Bogendoerfer
   *
   * Based on code written by Paul Gortmaker.
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
11
12
13
14
   */
  
  #include <linux/module.h>
  #include <linux/rtc.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
16
17
  #include <linux/platform_device.h>
  #include <linux/bcd.h>
d7a6119f4   Geert Uytterhoeven   rtc: rtc-ds1286 a...
18
  #include <linux/io.h>
8ff7b7d90   Sachin Kamat   drivers/rtc/rtc-m...
19
  #include <linux/err.h>
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
20

d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
21
22
  struct m48t35_rtc {
  	u8	pad[0x7ff8];    /* starts at 0x7ff8 */
10cf8300e   Thomas Bogendoerfer   MIPS: SGI-IP27: f...
23
24
25
26
27
28
29
30
31
32
  #ifdef CONFIG_SGI_IP27
  	u8	hour;
  	u8	min;
  	u8	sec;
  	u8	control;
  	u8	year;
  	u8	month;
  	u8	date;
  	u8	day;
  #else
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
33
34
35
36
37
38
39
40
  	u8	control;
  	u8	sec;
  	u8	min;
  	u8	hour;
  	u8	day;
  	u8	date;
  	u8	month;
  	u8	year;
10cf8300e   Thomas Bogendoerfer   MIPS: SGI-IP27: f...
41
  #endif
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
42
43
44
45
46
47
48
49
50
51
52
53
54
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
85
86
87
88
89
90
91
92
93
  };
  
  #define M48T35_RTC_SET		0x80
  #define M48T35_RTC_READ		0x40
  
  struct m48t35_priv {
  	struct rtc_device *rtc;
  	struct m48t35_rtc __iomem *reg;
  	size_t size;
  	unsigned long baseaddr;
  	spinlock_t lock;
  };
  
  static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
  {
  	struct m48t35_priv *priv = dev_get_drvdata(dev);
  	u8 control;
  
  	/*
  	 * Only the values that we read from the RTC are set. We leave
  	 * tm_wday, tm_yday and tm_isdst untouched. Even though the
  	 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
  	 * by the RTC when initially set to a non-zero value.
  	 */
  	spin_lock_irq(&priv->lock);
  	control = readb(&priv->reg->control);
  	writeb(control | M48T35_RTC_READ, &priv->reg->control);
  	tm->tm_sec = readb(&priv->reg->sec);
  	tm->tm_min = readb(&priv->reg->min);
  	tm->tm_hour = readb(&priv->reg->hour);
  	tm->tm_mday = readb(&priv->reg->date);
  	tm->tm_mon = readb(&priv->reg->month);
  	tm->tm_year = readb(&priv->reg->year);
  	writeb(control, &priv->reg->control);
  	spin_unlock_irq(&priv->lock);
  
  	tm->tm_sec = bcd2bin(tm->tm_sec);
  	tm->tm_min = bcd2bin(tm->tm_min);
  	tm->tm_hour = bcd2bin(tm->tm_hour);
  	tm->tm_mday = bcd2bin(tm->tm_mday);
  	tm->tm_mon = bcd2bin(tm->tm_mon);
  	tm->tm_year = bcd2bin(tm->tm_year);
  
  	/*
  	 * Account for differences between how the RTC uses the values
  	 * and how they are defined in a struct rtc_time;
  	 */
  	tm->tm_year += 70;
  	if (tm->tm_year <= 69)
  		tm->tm_year += 100;
  
  	tm->tm_mon--;
22652ba72   Alexandre Belloni   rtc: stop validat...
94
  	return 0;
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  }
  
  static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
  {
  	struct m48t35_priv *priv = dev_get_drvdata(dev);
  	unsigned char mon, day, hrs, min, sec;
  	unsigned int yrs;
  	u8 control;
  
  	yrs = tm->tm_year + 1900;
  	mon = tm->tm_mon + 1;   /* tm_mon starts at zero */
  	day = tm->tm_mday;
  	hrs = tm->tm_hour;
  	min = tm->tm_min;
  	sec = tm->tm_sec;
  
  	if (yrs < 1970)
  		return -EINVAL;
  
  	yrs -= 1970;
  	if (yrs > 255)    /* They are unsigned */
  		return -EINVAL;
  
  	if (yrs > 169)
  		return -EINVAL;
  
  	if (yrs >= 100)
  		yrs -= 100;
  
  	sec = bin2bcd(sec);
  	min = bin2bcd(min);
  	hrs = bin2bcd(hrs);
  	day = bin2bcd(day);
  	mon = bin2bcd(mon);
  	yrs = bin2bcd(yrs);
  
  	spin_lock_irq(&priv->lock);
  	control = readb(&priv->reg->control);
  	writeb(control | M48T35_RTC_SET, &priv->reg->control);
  	writeb(yrs, &priv->reg->year);
  	writeb(mon, &priv->reg->month);
  	writeb(day, &priv->reg->date);
  	writeb(hrs, &priv->reg->hour);
  	writeb(min, &priv->reg->min);
  	writeb(sec, &priv->reg->sec);
  	writeb(control, &priv->reg->control);
  	spin_unlock_irq(&priv->lock);
  	return 0;
  }
  
  static const struct rtc_class_ops m48t35_ops = {
  	.read_time	= m48t35_read_time,
  	.set_time	= m48t35_set_time,
  };
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
149
  static int m48t35_probe(struct platform_device *pdev)
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
150
  {
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
151
152
  	struct resource *res;
  	struct m48t35_priv *priv;
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
153
154
155
156
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!res)
  		return -ENODEV;
4f58cd9b4   Sachin Kamat   drivers/rtc/rtc-m...
157
  	priv = devm_kzalloc(&pdev->dev, sizeof(struct m48t35_priv), GFP_KERNEL);
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
158
159
  	if (!priv)
  		return -ENOMEM;
28f65c11f   Joe Perches   treewide: Convert...
160
  	priv->size = resource_size(res);
4f58cd9b4   Sachin Kamat   drivers/rtc/rtc-m...
161
162
163
  	if (!devm_request_mem_region(&pdev->dev, res->start, priv->size,
  				     pdev->name))
  		return -EBUSY;
eac1c3fc5   Thomas Bogendoerfer   rtc: m48t35: remo...
164

d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
165
  	priv->baseaddr = res->start;
4f58cd9b4   Sachin Kamat   drivers/rtc/rtc-m...
166
167
168
  	priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size);
  	if (!priv->reg)
  		return -ENOMEM;
b74d2caa6   Alessandro Zummo   rtc: fix driver d...
169

d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
170
  	spin_lock_init(&priv->lock);
b74d2caa6   Alessandro Zummo   rtc: fix driver d...
171
172
  
  	platform_set_drvdata(pdev, priv);
4f58cd9b4   Sachin Kamat   drivers/rtc/rtc-m...
173
  	priv->rtc = devm_rtc_device_register(&pdev->dev, "m48t35",
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
174
  				  &m48t35_ops, THIS_MODULE);
dac30a984   Sachin Kamat   drivers/rtc: Repl...
175
  	return PTR_ERR_OR_ZERO(priv->rtc);
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
176
  }
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
177
178
179
  static struct platform_driver m48t35_platform_driver = {
  	.driver		= {
  		.name	= "rtc-m48t35",
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
180
181
  	},
  	.probe		= m48t35_probe,
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
182
  };
0c4eae665   Axel Lin   rtc: convert driv...
183
  module_platform_driver(m48t35_platform_driver);
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
184
185
186
187
  
  MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
  MODULE_DESCRIPTION("M48T35 RTC driver");
  MODULE_LICENSE("GPL");
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
188
  MODULE_ALIAS("platform:rtc-m48t35");