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