diff options
author | H Hartley Sweeten <hsweeten@visionengravers.com> | 2017-02-10 13:11:56 -0500 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2017-02-11 19:07:38 -0500 |
commit | 8057c86d43a6579421c97b00c6df1ab0bc5e51a0 (patch) | |
tree | fd58603905dbd9d5b5addda56f8db36cb4ca8fc8 /drivers/rtc | |
parent | 68b54f477fae4f50a4010a1b5019bd185d452fa7 (diff) |
rtc: m48t86: allow driver to manage its resources
Allow this driver to, optionally, manage it's own resources and do the
read/write operations if the platform does not provide them.
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-m48t86.c | 153 |
1 files changed, 102 insertions, 51 deletions
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 30648ea9e8e0..4dcdbd2a2408 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
19 | #include <linux/platform_data/rtc-m48t86.h> | 19 | #include <linux/platform_data/rtc-m48t86.h> |
20 | #include <linux/bcd.h> | 20 | #include <linux/bcd.h> |
21 | #include <linux/io.h> | ||
21 | 22 | ||
22 | #define M48T86_SEC 0x00 | 23 | #define M48T86_SEC 0x00 |
23 | #define M48T86_SECALRM 0x01 | 24 | #define M48T86_SECALRM 0x01 |
@@ -38,39 +39,72 @@ | |||
38 | #define M48T86_D 0x0d | 39 | #define M48T86_D 0x0d |
39 | #define M48T86_D_VRT BIT(7) | 40 | #define M48T86_D_VRT BIT(7) |
40 | 41 | ||
42 | struct m48t86_rtc_info { | ||
43 | void __iomem *index_reg; | ||
44 | void __iomem *data_reg; | ||
45 | struct rtc_device *rtc; | ||
46 | struct m48t86_ops *ops; | ||
47 | }; | ||
48 | |||
49 | static unsigned char m48t86_readb(struct device *dev, unsigned long addr) | ||
50 | { | ||
51 | struct m48t86_rtc_info *info = dev_get_drvdata(dev); | ||
52 | unsigned char value; | ||
53 | |||
54 | if (info->ops) { | ||
55 | value = info->ops->readbyte(addr); | ||
56 | } else { | ||
57 | writeb(addr, info->index_reg); | ||
58 | value = readb(info->data_reg); | ||
59 | } | ||
60 | return value; | ||
61 | } | ||
62 | |||
63 | static void m48t86_writeb(struct device *dev, | ||
64 | unsigned char value, unsigned long addr) | ||
65 | { | ||
66 | struct m48t86_rtc_info *info = dev_get_drvdata(dev); | ||
67 | |||
68 | if (info->ops) { | ||
69 | info->ops->writebyte(value, addr); | ||
70 | } else { | ||
71 | writeb(addr, info->index_reg); | ||
72 | writeb(value, info->data_reg); | ||
73 | } | ||
74 | } | ||
75 | |||
41 | static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) | 76 | static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) |
42 | { | 77 | { |
43 | unsigned char reg; | 78 | unsigned char reg; |
44 | struct platform_device *pdev = to_platform_device(dev); | ||
45 | struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); | ||
46 | 79 | ||
47 | reg = ops->readbyte(M48T86_B); | 80 | reg = m48t86_readb(dev, M48T86_B); |
48 | 81 | ||
49 | if (reg & M48T86_B_DM) { | 82 | if (reg & M48T86_B_DM) { |
50 | /* data (binary) mode */ | 83 | /* data (binary) mode */ |
51 | tm->tm_sec = ops->readbyte(M48T86_SEC); | 84 | tm->tm_sec = m48t86_readb(dev, M48T86_SEC); |
52 | tm->tm_min = ops->readbyte(M48T86_MIN); | 85 | tm->tm_min = m48t86_readb(dev, M48T86_MIN); |
53 | tm->tm_hour = ops->readbyte(M48T86_HOUR) & 0x3f; | 86 | tm->tm_hour = m48t86_readb(dev, M48T86_HOUR) & 0x3f; |
54 | tm->tm_mday = ops->readbyte(M48T86_DOM); | 87 | tm->tm_mday = m48t86_readb(dev, M48T86_DOM); |
55 | /* tm_mon is 0-11 */ | 88 | /* tm_mon is 0-11 */ |
56 | tm->tm_mon = ops->readbyte(M48T86_MONTH) - 1; | 89 | tm->tm_mon = m48t86_readb(dev, M48T86_MONTH) - 1; |
57 | tm->tm_year = ops->readbyte(M48T86_YEAR) + 100; | 90 | tm->tm_year = m48t86_readb(dev, M48T86_YEAR) + 100; |
58 | tm->tm_wday = ops->readbyte(M48T86_DOW); | 91 | tm->tm_wday = m48t86_readb(dev, M48T86_DOW); |
59 | } else { | 92 | } else { |
60 | /* bcd mode */ | 93 | /* bcd mode */ |
61 | tm->tm_sec = bcd2bin(ops->readbyte(M48T86_SEC)); | 94 | tm->tm_sec = bcd2bin(m48t86_readb(dev, M48T86_SEC)); |
62 | tm->tm_min = bcd2bin(ops->readbyte(M48T86_MIN)); | 95 | tm->tm_min = bcd2bin(m48t86_readb(dev, M48T86_MIN)); |
63 | tm->tm_hour = bcd2bin(ops->readbyte(M48T86_HOUR) & 0x3f); | 96 | tm->tm_hour = bcd2bin(m48t86_readb(dev, M48T86_HOUR) & |
64 | tm->tm_mday = bcd2bin(ops->readbyte(M48T86_DOM)); | 97 | 0x3f); |
98 | tm->tm_mday = bcd2bin(m48t86_readb(dev, M48T86_DOM)); | ||
65 | /* tm_mon is 0-11 */ | 99 | /* tm_mon is 0-11 */ |
66 | tm->tm_mon = bcd2bin(ops->readbyte(M48T86_MONTH)) - 1; | 100 | tm->tm_mon = bcd2bin(m48t86_readb(dev, M48T86_MONTH)) - 1; |
67 | tm->tm_year = bcd2bin(ops->readbyte(M48T86_YEAR)) + 100; | 101 | tm->tm_year = bcd2bin(m48t86_readb(dev, M48T86_YEAR)) + 100; |
68 | tm->tm_wday = bcd2bin(ops->readbyte(M48T86_DOW)); | 102 | tm->tm_wday = bcd2bin(m48t86_readb(dev, M48T86_DOW)); |
69 | } | 103 | } |
70 | 104 | ||
71 | /* correct the hour if the clock is in 12h mode */ | 105 | /* correct the hour if the clock is in 12h mode */ |
72 | if (!(reg & M48T86_B_H24)) | 106 | if (!(reg & M48T86_B_H24)) |
73 | if (ops->readbyte(M48T86_HOUR) & 0x80) | 107 | if (m48t86_readb(dev, M48T86_HOUR) & 0x80) |
74 | tm->tm_hour += 12; | 108 | tm->tm_hour += 12; |
75 | 109 | ||
76 | return rtc_valid_tm(tm); | 110 | return rtc_valid_tm(tm); |
@@ -79,38 +113,36 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
79 | static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) | 113 | static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) |
80 | { | 114 | { |
81 | unsigned char reg; | 115 | unsigned char reg; |
82 | struct platform_device *pdev = to_platform_device(dev); | ||
83 | struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); | ||
84 | 116 | ||
85 | reg = ops->readbyte(M48T86_B); | 117 | reg = m48t86_readb(dev, M48T86_B); |
86 | 118 | ||
87 | /* update flag and 24h mode */ | 119 | /* update flag and 24h mode */ |
88 | reg |= M48T86_B_SET | M48T86_B_H24; | 120 | reg |= M48T86_B_SET | M48T86_B_H24; |
89 | ops->writebyte(reg, M48T86_B); | 121 | m48t86_writeb(dev, reg, M48T86_B); |
90 | 122 | ||
91 | if (reg & M48T86_B_DM) { | 123 | if (reg & M48T86_B_DM) { |
92 | /* data (binary) mode */ | 124 | /* data (binary) mode */ |
93 | ops->writebyte(tm->tm_sec, M48T86_SEC); | 125 | m48t86_writeb(dev, tm->tm_sec, M48T86_SEC); |
94 | ops->writebyte(tm->tm_min, M48T86_MIN); | 126 | m48t86_writeb(dev, tm->tm_min, M48T86_MIN); |
95 | ops->writebyte(tm->tm_hour, M48T86_HOUR); | 127 | m48t86_writeb(dev, tm->tm_hour, M48T86_HOUR); |
96 | ops->writebyte(tm->tm_mday, M48T86_DOM); | 128 | m48t86_writeb(dev, tm->tm_mday, M48T86_DOM); |
97 | ops->writebyte(tm->tm_mon + 1, M48T86_MONTH); | 129 | m48t86_writeb(dev, tm->tm_mon + 1, M48T86_MONTH); |
98 | ops->writebyte(tm->tm_year % 100, M48T86_YEAR); | 130 | m48t86_writeb(dev, tm->tm_year % 100, M48T86_YEAR); |
99 | ops->writebyte(tm->tm_wday, M48T86_DOW); | 131 | m48t86_writeb(dev, tm->tm_wday, M48T86_DOW); |
100 | } else { | 132 | } else { |
101 | /* bcd mode */ | 133 | /* bcd mode */ |
102 | ops->writebyte(bin2bcd(tm->tm_sec), M48T86_SEC); | 134 | m48t86_writeb(dev, bin2bcd(tm->tm_sec), M48T86_SEC); |
103 | ops->writebyte(bin2bcd(tm->tm_min), M48T86_MIN); | 135 | m48t86_writeb(dev, bin2bcd(tm->tm_min), M48T86_MIN); |
104 | ops->writebyte(bin2bcd(tm->tm_hour), M48T86_HOUR); | 136 | m48t86_writeb(dev, bin2bcd(tm->tm_hour), M48T86_HOUR); |
105 | ops->writebyte(bin2bcd(tm->tm_mday), M48T86_DOM); | 137 | m48t86_writeb(dev, bin2bcd(tm->tm_mday), M48T86_DOM); |
106 | ops->writebyte(bin2bcd(tm->tm_mon + 1), M48T86_MONTH); | 138 | m48t86_writeb(dev, bin2bcd(tm->tm_mon + 1), M48T86_MONTH); |
107 | ops->writebyte(bin2bcd(tm->tm_year % 100), M48T86_YEAR); | 139 | m48t86_writeb(dev, bin2bcd(tm->tm_year % 100), M48T86_YEAR); |
108 | ops->writebyte(bin2bcd(tm->tm_wday), M48T86_DOW); | 140 | m48t86_writeb(dev, bin2bcd(tm->tm_wday), M48T86_DOW); |
109 | } | 141 | } |
110 | 142 | ||
111 | /* update ended */ | 143 | /* update ended */ |
112 | reg &= ~M48T86_B_SET; | 144 | reg &= ~M48T86_B_SET; |
113 | ops->writebyte(reg, M48T86_B); | 145 | m48t86_writeb(dev, reg, M48T86_B); |
114 | 146 | ||
115 | return 0; | 147 | return 0; |
116 | } | 148 | } |
@@ -118,15 +150,13 @@ static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
118 | static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) | 150 | static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) |
119 | { | 151 | { |
120 | unsigned char reg; | 152 | unsigned char reg; |
121 | struct platform_device *pdev = to_platform_device(dev); | ||
122 | struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); | ||
123 | 153 | ||
124 | reg = ops->readbyte(M48T86_B); | 154 | reg = m48t86_readb(dev, M48T86_B); |
125 | 155 | ||
126 | seq_printf(seq, "mode\t\t: %s\n", | 156 | seq_printf(seq, "mode\t\t: %s\n", |
127 | (reg & M48T86_B_DM) ? "binary" : "bcd"); | 157 | (reg & M48T86_B_DM) ? "binary" : "bcd"); |
128 | 158 | ||
129 | reg = ops->readbyte(M48T86_D); | 159 | reg = m48t86_readb(dev, M48T86_D); |
130 | 160 | ||
131 | seq_printf(seq, "battery\t\t: %s\n", | 161 | seq_printf(seq, "battery\t\t: %s\n", |
132 | (reg & M48T86_D_VRT) ? "ok" : "exhausted"); | 162 | (reg & M48T86_D_VRT) ? "ok" : "exhausted"); |
@@ -140,23 +170,44 @@ static const struct rtc_class_ops m48t86_rtc_ops = { | |||
140 | .proc = m48t86_rtc_proc, | 170 | .proc = m48t86_rtc_proc, |
141 | }; | 171 | }; |
142 | 172 | ||
143 | static int m48t86_rtc_probe(struct platform_device *dev) | 173 | static int m48t86_rtc_probe(struct platform_device *pdev) |
144 | { | 174 | { |
175 | struct m48t86_rtc_info *info; | ||
145 | unsigned char reg; | 176 | unsigned char reg; |
146 | struct m48t86_ops *ops = dev_get_platdata(&dev->dev); | ||
147 | struct rtc_device *rtc; | ||
148 | 177 | ||
149 | rtc = devm_rtc_device_register(&dev->dev, "m48t86", | 178 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
150 | &m48t86_rtc_ops, THIS_MODULE); | 179 | if (!info) |
180 | return -ENOMEM; | ||
181 | |||
182 | info->ops = dev_get_platdata(&pdev->dev); | ||
183 | if (!info->ops) { | ||
184 | struct resource *res; | ||
185 | |||
186 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
187 | if (!res) | ||
188 | return -ENODEV; | ||
189 | info->index_reg = devm_ioremap_resource(&pdev->dev, res); | ||
190 | if (IS_ERR(info->index_reg)) | ||
191 | return PTR_ERR(info->index_reg); | ||
192 | |||
193 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
194 | if (!res) | ||
195 | return -ENODEV; | ||
196 | info->data_reg = devm_ioremap_resource(&pdev->dev, res); | ||
197 | if (IS_ERR(info->data_reg)) | ||
198 | return PTR_ERR(info->data_reg); | ||
199 | } | ||
151 | 200 | ||
152 | if (IS_ERR(rtc)) | 201 | dev_set_drvdata(&pdev->dev, info); |
153 | return PTR_ERR(rtc); | ||
154 | 202 | ||
155 | platform_set_drvdata(dev, rtc); | 203 | info->rtc = devm_rtc_device_register(&pdev->dev, "m48t86", |
204 | &m48t86_rtc_ops, THIS_MODULE); | ||
205 | if (IS_ERR(info->rtc)) | ||
206 | return PTR_ERR(info->rtc); | ||
156 | 207 | ||
157 | /* read battery status */ | 208 | /* read battery status */ |
158 | reg = ops->readbyte(M48T86_D); | 209 | reg = m48t86_readb(&pdev->dev, M48T86_D); |
159 | dev_info(&dev->dev, "battery %s\n", | 210 | dev_info(&pdev->dev, "battery %s\n", |
160 | (reg & M48T86_D_VRT) ? "ok" : "exhausted"); | 211 | (reg & M48T86_D_VRT) ? "ok" : "exhausted"); |
161 | 212 | ||
162 | return 0; | 213 | return 0; |