Blame view

drivers/rtc/rtc-m48t35.c 4.6 KB
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * 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.
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the
   * Free Software Foundation; either version 2 of the License, or (at your
   * option) any later version.
   */
  
  #include <linux/module.h>
  #include <linux/rtc.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
20
21
  #include <linux/platform_device.h>
  #include <linux/bcd.h>
d7a6119f4   Geert Uytterhoeven   rtc: rtc-ds1286 a...
22
  #include <linux/io.h>
8ff7b7d90   Sachin Kamat   drivers/rtc/rtc-m...
23
  #include <linux/err.h>
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
24

d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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
94
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
  struct m48t35_rtc {
  	u8	pad[0x7ff8];    /* starts at 0x7ff8 */
  	u8	control;
  	u8	sec;
  	u8	min;
  	u8	hour;
  	u8	day;
  	u8	date;
  	u8	month;
  	u8	year;
  };
  
  #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--;
  	return rtc_valid_tm(tm);
  }
  
  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...
142
  static int m48t35_probe(struct platform_device *pdev)
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
143
  {
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
144
145
  	struct resource *res;
  	struct m48t35_priv *priv;
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
146
147
148
149
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!res)
  		return -ENODEV;
4f58cd9b4   Sachin Kamat   drivers/rtc/rtc-m...
150
  	priv = devm_kzalloc(&pdev->dev, sizeof(struct m48t35_priv), GFP_KERNEL);
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
151
152
  	if (!priv)
  		return -ENOMEM;
28f65c11f   Joe Perches   treewide: Convert...
153
  	priv->size = resource_size(res);
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
154
155
156
157
158
  	/*
  	 * kludge: remove the #ifndef after ioc3 resource
  	 * conflicts are resolved
  	 */
  #ifndef CONFIG_SGI_IP27
4f58cd9b4   Sachin Kamat   drivers/rtc/rtc-m...
159
160
161
  	if (!devm_request_mem_region(&pdev->dev, res->start, priv->size,
  				     pdev->name))
  		return -EBUSY;
d1dbd82e2   Thomas Bogendoerfer   RTC: M48T35: new ...
162
163
  #endif
  	priv->baseaddr = res->start;
4f58cd9b4   Sachin Kamat   drivers/rtc/rtc-m...
164
165
166
  	priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size);
  	if (!priv->reg)
  		return -ENOMEM;
b74d2caa6   Alessandro Zummo   rtc: fix driver d...
167

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