xref: /openbmc/linux/drivers/rtc/rtc-m48t35.c (revision 9adc8050)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Driver for the SGS-Thomson M48T35 Timekeeper RAM chip
4  *
5  * Copyright (C) 2000 Silicon Graphics, Inc.
6  * Written by Ulf Carlsson (ulfc@engr.sgi.com)
7  *
8  * Copyright (C) 2008 Thomas Bogendoerfer
9  *
10  * Based on code written by Paul Gortmaker.
11  */
12 
13 #include <linux/module.h>
14 #include <linux/rtc.h>
15 #include <linux/slab.h>
16 #include <linux/platform_device.h>
17 #include <linux/bcd.h>
18 #include <linux/io.h>
19 #include <linux/err.h>
20 
21 struct m48t35_rtc {
22 	u8	pad[0x7ff8];    /* starts at 0x7ff8 */
23 	u8	control;
24 	u8	sec;
25 	u8	min;
26 	u8	hour;
27 	u8	day;
28 	u8	date;
29 	u8	month;
30 	u8	year;
31 };
32 
33 #define M48T35_RTC_SET		0x80
34 #define M48T35_RTC_READ		0x40
35 
36 struct m48t35_priv {
37 	struct rtc_device *rtc;
38 	struct m48t35_rtc __iomem *reg;
39 	size_t size;
40 	unsigned long baseaddr;
41 	spinlock_t lock;
42 };
43 
44 static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
45 {
46 	struct m48t35_priv *priv = dev_get_drvdata(dev);
47 	u8 control;
48 
49 	/*
50 	 * Only the values that we read from the RTC are set. We leave
51 	 * tm_wday, tm_yday and tm_isdst untouched. Even though the
52 	 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
53 	 * by the RTC when initially set to a non-zero value.
54 	 */
55 	spin_lock_irq(&priv->lock);
56 	control = readb(&priv->reg->control);
57 	writeb(control | M48T35_RTC_READ, &priv->reg->control);
58 	tm->tm_sec = readb(&priv->reg->sec);
59 	tm->tm_min = readb(&priv->reg->min);
60 	tm->tm_hour = readb(&priv->reg->hour);
61 	tm->tm_mday = readb(&priv->reg->date);
62 	tm->tm_mon = readb(&priv->reg->month);
63 	tm->tm_year = readb(&priv->reg->year);
64 	writeb(control, &priv->reg->control);
65 	spin_unlock_irq(&priv->lock);
66 
67 	tm->tm_sec = bcd2bin(tm->tm_sec);
68 	tm->tm_min = bcd2bin(tm->tm_min);
69 	tm->tm_hour = bcd2bin(tm->tm_hour);
70 	tm->tm_mday = bcd2bin(tm->tm_mday);
71 	tm->tm_mon = bcd2bin(tm->tm_mon);
72 	tm->tm_year = bcd2bin(tm->tm_year);
73 
74 	/*
75 	 * Account for differences between how the RTC uses the values
76 	 * and how they are defined in a struct rtc_time;
77 	 */
78 	tm->tm_year += 70;
79 	if (tm->tm_year <= 69)
80 		tm->tm_year += 100;
81 
82 	tm->tm_mon--;
83 	return 0;
84 }
85 
86 static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
87 {
88 	struct m48t35_priv *priv = dev_get_drvdata(dev);
89 	unsigned char mon, day, hrs, min, sec;
90 	unsigned int yrs;
91 	u8 control;
92 
93 	yrs = tm->tm_year + 1900;
94 	mon = tm->tm_mon + 1;   /* tm_mon starts at zero */
95 	day = tm->tm_mday;
96 	hrs = tm->tm_hour;
97 	min = tm->tm_min;
98 	sec = tm->tm_sec;
99 
100 	if (yrs < 1970)
101 		return -EINVAL;
102 
103 	yrs -= 1970;
104 	if (yrs > 255)    /* They are unsigned */
105 		return -EINVAL;
106 
107 	if (yrs > 169)
108 		return -EINVAL;
109 
110 	if (yrs >= 100)
111 		yrs -= 100;
112 
113 	sec = bin2bcd(sec);
114 	min = bin2bcd(min);
115 	hrs = bin2bcd(hrs);
116 	day = bin2bcd(day);
117 	mon = bin2bcd(mon);
118 	yrs = bin2bcd(yrs);
119 
120 	spin_lock_irq(&priv->lock);
121 	control = readb(&priv->reg->control);
122 	writeb(control | M48T35_RTC_SET, &priv->reg->control);
123 	writeb(yrs, &priv->reg->year);
124 	writeb(mon, &priv->reg->month);
125 	writeb(day, &priv->reg->date);
126 	writeb(hrs, &priv->reg->hour);
127 	writeb(min, &priv->reg->min);
128 	writeb(sec, &priv->reg->sec);
129 	writeb(control, &priv->reg->control);
130 	spin_unlock_irq(&priv->lock);
131 	return 0;
132 }
133 
134 static const struct rtc_class_ops m48t35_ops = {
135 	.read_time	= m48t35_read_time,
136 	.set_time	= m48t35_set_time,
137 };
138 
139 static int m48t35_probe(struct platform_device *pdev)
140 {
141 	struct resource *res;
142 	struct m48t35_priv *priv;
143 
144 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
145 	if (!res)
146 		return -ENODEV;
147 	priv = devm_kzalloc(&pdev->dev, sizeof(struct m48t35_priv), GFP_KERNEL);
148 	if (!priv)
149 		return -ENOMEM;
150 
151 	priv->size = resource_size(res);
152 	/*
153 	 * kludge: remove the #ifndef after ioc3 resource
154 	 * conflicts are resolved
155 	 */
156 #ifndef CONFIG_SGI_IP27
157 	if (!devm_request_mem_region(&pdev->dev, res->start, priv->size,
158 				     pdev->name))
159 		return -EBUSY;
160 #endif
161 	priv->baseaddr = res->start;
162 	priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size);
163 	if (!priv->reg)
164 		return -ENOMEM;
165 
166 	spin_lock_init(&priv->lock);
167 
168 	platform_set_drvdata(pdev, priv);
169 
170 	priv->rtc = devm_rtc_device_register(&pdev->dev, "m48t35",
171 				  &m48t35_ops, THIS_MODULE);
172 	return PTR_ERR_OR_ZERO(priv->rtc);
173 }
174 
175 static struct platform_driver m48t35_platform_driver = {
176 	.driver		= {
177 		.name	= "rtc-m48t35",
178 	},
179 	.probe		= m48t35_probe,
180 };
181 
182 module_platform_driver(m48t35_platform_driver);
183 
184 MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
185 MODULE_DESCRIPTION("M48T35 RTC driver");
186 MODULE_LICENSE("GPL");
187 MODULE_ALIAS("platform:rtc-m48t35");
188