Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2000, 2001 Broadcom Corporation | |
3 | * | |
4 | * Copyright (C) 2002 MontaVista Software Inc. | |
5 | * Author: jsun@mvista.com or jsun@junsun.net | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. | |
11 | * | |
12 | */ | |
13 | #include <linux/bcd.h> | |
14 | #include <linux/types.h> | |
15 | #include <linux/time.h> | |
16 | ||
17 | #include <asm/time.h> | |
18 | #include <asm/addrspace.h> | |
19 | #include <asm/io.h> | |
20 | ||
21 | #include <asm/sibyte/sb1250.h> | |
22 | #include <asm/sibyte/sb1250_regs.h> | |
23 | #include <asm/sibyte/sb1250_smbus.h> | |
24 | ||
25 | ||
26 | /* M41T81 definitions */ | |
27 | ||
28 | /* | |
29 | * Register bits | |
30 | */ | |
31 | ||
32 | #define M41T81REG_SC_ST 0x80 /* stop bit */ | |
33 | #define M41T81REG_HR_CB 0x40 /* century bit */ | |
34 | #define M41T81REG_HR_CEB 0x80 /* century enable bit */ | |
35 | #define M41T81REG_CTL_S 0x20 /* sign bit */ | |
36 | #define M41T81REG_CTL_FT 0x40 /* frequency test bit */ | |
37 | #define M41T81REG_CTL_OUT 0x80 /* output level */ | |
38 | #define M41T81REG_WD_RB0 0x01 /* watchdog resolution bit 0 */ | |
39 | #define M41T81REG_WD_RB1 0x02 /* watchdog resolution bit 1 */ | |
40 | #define M41T81REG_WD_BMB0 0x04 /* watchdog multiplier bit 0 */ | |
41 | #define M41T81REG_WD_BMB1 0x08 /* watchdog multiplier bit 1 */ | |
42 | #define M41T81REG_WD_BMB2 0x10 /* watchdog multiplier bit 2 */ | |
43 | #define M41T81REG_WD_BMB3 0x20 /* watchdog multiplier bit 3 */ | |
44 | #define M41T81REG_WD_BMB4 0x40 /* watchdog multiplier bit 4 */ | |
45 | #define M41T81REG_AMO_ABE 0x20 /* alarm in "battery back-up mode" enable bit */ | |
46 | #define M41T81REG_AMO_SQWE 0x40 /* square wave enable */ | |
47 | #define M41T81REG_AMO_AFE 0x80 /* alarm flag enable flag */ | |
48 | #define M41T81REG_ADT_RPT5 0x40 /* alarm repeat mode bit 5 */ | |
49 | #define M41T81REG_ADT_RPT4 0x80 /* alarm repeat mode bit 4 */ | |
50 | #define M41T81REG_AHR_RPT3 0x80 /* alarm repeat mode bit 3 */ | |
51 | #define M41T81REG_AHR_HT 0x40 /* halt update bit */ | |
52 | #define M41T81REG_AMN_RPT2 0x80 /* alarm repeat mode bit 2 */ | |
53 | #define M41T81REG_ASC_RPT1 0x80 /* alarm repeat mode bit 1 */ | |
54 | #define M41T81REG_FLG_AF 0x40 /* alarm flag (read only) */ | |
55 | #define M41T81REG_FLG_WDF 0x80 /* watchdog flag (read only) */ | |
56 | #define M41T81REG_SQW_RS0 0x10 /* sqw frequency bit 0 */ | |
57 | #define M41T81REG_SQW_RS1 0x20 /* sqw frequency bit 1 */ | |
58 | #define M41T81REG_SQW_RS2 0x40 /* sqw frequency bit 2 */ | |
59 | #define M41T81REG_SQW_RS3 0x80 /* sqw frequency bit 3 */ | |
60 | ||
61 | ||
62 | /* | |
63 | * Register numbers | |
64 | */ | |
65 | ||
66 | #define M41T81REG_TSC 0x00 /* tenths/hundredths of second */ | |
67 | #define M41T81REG_SC 0x01 /* seconds */ | |
68 | #define M41T81REG_MN 0x02 /* minute */ | |
69 | #define M41T81REG_HR 0x03 /* hour/century */ | |
70 | #define M41T81REG_DY 0x04 /* day of week */ | |
71 | #define M41T81REG_DT 0x05 /* date of month */ | |
72 | #define M41T81REG_MO 0x06 /* month */ | |
73 | #define M41T81REG_YR 0x07 /* year */ | |
74 | #define M41T81REG_CTL 0x08 /* control */ | |
75 | #define M41T81REG_WD 0x09 /* watchdog */ | |
76 | #define M41T81REG_AMO 0x0A /* alarm: month */ | |
77 | #define M41T81REG_ADT 0x0B /* alarm: date */ | |
78 | #define M41T81REG_AHR 0x0C /* alarm: hour */ | |
79 | #define M41T81REG_AMN 0x0D /* alarm: minute */ | |
80 | #define M41T81REG_ASC 0x0E /* alarm: second */ | |
81 | #define M41T81REG_FLG 0x0F /* flags */ | |
82 | #define M41T81REG_SQW 0x13 /* square wave register */ | |
83 | ||
84 | #define M41T81_CCR_ADDRESS 0x68 | |
65bda1a9 MR |
85 | |
86 | #define SMB_CSR(reg) IOADDR(A_SMB_REGISTER(1, reg)) | |
1da177e4 LT |
87 | |
88 | static int m41t81_read(uint8_t addr) | |
89 | { | |
65bda1a9 | 90 | while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY) |
1da177e4 LT |
91 | ; |
92 | ||
65bda1a9 MR |
93 | __raw_writeq(addr & 0xff, SMB_CSR(R_SMB_CMD)); |
94 | __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_WR1BYTE, | |
95 | SMB_CSR(R_SMB_START)); | |
1da177e4 | 96 | |
65bda1a9 | 97 | while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY) |
1da177e4 LT |
98 | ; |
99 | ||
65bda1a9 MR |
100 | __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_RD1BYTE, |
101 | SMB_CSR(R_SMB_START)); | |
1da177e4 | 102 | |
65bda1a9 | 103 | while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY) |
1da177e4 LT |
104 | ; |
105 | ||
65bda1a9 | 106 | if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) { |
1da177e4 | 107 | /* Clear error bit by writing a 1 */ |
65bda1a9 | 108 | __raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS)); |
1da177e4 LT |
109 | return -1; |
110 | } | |
111 | ||
635c9907 | 112 | return __raw_readq(SMB_CSR(R_SMB_DATA)) & 0xff; |
1da177e4 LT |
113 | } |
114 | ||
115 | static int m41t81_write(uint8_t addr, int b) | |
116 | { | |
65bda1a9 | 117 | while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY) |
1da177e4 LT |
118 | ; |
119 | ||
65bda1a9 MR |
120 | __raw_writeq(addr & 0xff, SMB_CSR(R_SMB_CMD)); |
121 | __raw_writeq(b & 0xff, SMB_CSR(R_SMB_DATA)); | |
122 | __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_WR2BYTE, | |
123 | SMB_CSR(R_SMB_START)); | |
1da177e4 | 124 | |
65bda1a9 | 125 | while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY) |
1da177e4 LT |
126 | ; |
127 | ||
65bda1a9 | 128 | if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) { |
1da177e4 | 129 | /* Clear error bit by writing a 1 */ |
65bda1a9 | 130 | __raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS)); |
1da177e4 | 131 | return -1; |
42a3b4f2 | 132 | } |
1da177e4 LT |
133 | |
134 | /* read the same byte again to make sure it is written */ | |
65bda1a9 MR |
135 | __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_RD1BYTE, |
136 | SMB_CSR(R_SMB_START)); | |
1da177e4 | 137 | |
65bda1a9 | 138 | while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY) |
1da177e4 | 139 | ; |
42a3b4f2 | 140 | |
1da177e4 LT |
141 | return 0; |
142 | } | |
143 | ||
144 | int m41t81_set_time(unsigned long t) | |
145 | { | |
146 | struct rtc_time tm; | |
53c2df2f | 147 | unsigned long flags; |
1da177e4 | 148 | |
90b02340 RB |
149 | /* Note we don't care about the century */ |
150 | rtc_time_to_tm(t, &tm); | |
1da177e4 LT |
151 | |
152 | /* | |
153 | * Note the write order matters as it ensures the correctness. | |
42a3b4f2 | 154 | * When we write sec, 10th sec is clear. It is reasonable to |
1da177e4 LT |
155 | * believe we should finish writing min within a second. |
156 | */ | |
157 | ||
53c2df2f | 158 | spin_lock_irqsave(&rtc_lock, flags); |
02112dbc | 159 | tm.tm_sec = bin2bcd(tm.tm_sec); |
1da177e4 | 160 | m41t81_write(M41T81REG_SC, tm.tm_sec); |
42a3b4f2 | 161 | |
02112dbc | 162 | tm.tm_min = bin2bcd(tm.tm_min); |
1da177e4 LT |
163 | m41t81_write(M41T81REG_MN, tm.tm_min); |
164 | ||
02112dbc | 165 | tm.tm_hour = bin2bcd(tm.tm_hour); |
1da177e4 LT |
166 | tm.tm_hour = (tm.tm_hour & 0x3f) | (m41t81_read(M41T81REG_HR) & 0xc0); |
167 | m41t81_write(M41T81REG_HR, tm.tm_hour); | |
168 | ||
169 | /* tm_wday starts from 0 to 6 */ | |
170 | if (tm.tm_wday == 0) tm.tm_wday = 7; | |
02112dbc | 171 | tm.tm_wday = bin2bcd(tm.tm_wday); |
1da177e4 LT |
172 | m41t81_write(M41T81REG_DY, tm.tm_wday); |
173 | ||
02112dbc | 174 | tm.tm_mday = bin2bcd(tm.tm_mday); |
1da177e4 LT |
175 | m41t81_write(M41T81REG_DT, tm.tm_mday); |
176 | ||
177 | /* tm_mon starts from 0, *ick* */ | |
178 | tm.tm_mon ++; | |
02112dbc | 179 | tm.tm_mon = bin2bcd(tm.tm_mon); |
1da177e4 LT |
180 | m41t81_write(M41T81REG_MO, tm.tm_mon); |
181 | ||
182 | /* we don't do century, everything is beyond 2000 */ | |
183 | tm.tm_year %= 100; | |
02112dbc | 184 | tm.tm_year = bin2bcd(tm.tm_year); |
1da177e4 | 185 | m41t81_write(M41T81REG_YR, tm.tm_year); |
53c2df2f | 186 | spin_unlock_irqrestore(&rtc_lock, flags); |
1da177e4 LT |
187 | |
188 | return 0; | |
189 | } | |
190 | ||
191 | unsigned long m41t81_get_time(void) | |
192 | { | |
193 | unsigned int year, mon, day, hour, min, sec; | |
53c2df2f | 194 | unsigned long flags; |
1da177e4 | 195 | |
42a3b4f2 | 196 | /* |
1da177e4 LT |
197 | * min is valid if two reads of sec are the same. |
198 | */ | |
199 | for (;;) { | |
53c2df2f | 200 | spin_lock_irqsave(&rtc_lock, flags); |
1da177e4 LT |
201 | sec = m41t81_read(M41T81REG_SC); |
202 | min = m41t81_read(M41T81REG_MN); | |
203 | if (sec == m41t81_read(M41T81REG_SC)) break; | |
53c2df2f | 204 | spin_unlock_irqrestore(&rtc_lock, flags); |
1da177e4 LT |
205 | } |
206 | hour = m41t81_read(M41T81REG_HR) & 0x3f; | |
207 | day = m41t81_read(M41T81REG_DT); | |
208 | mon = m41t81_read(M41T81REG_MO); | |
209 | year = m41t81_read(M41T81REG_YR); | |
53c2df2f | 210 | spin_unlock_irqrestore(&rtc_lock, flags); |
1da177e4 | 211 | |
02112dbc AB |
212 | sec = bcd2bin(sec); |
213 | min = bcd2bin(min); | |
214 | hour = bcd2bin(hour); | |
215 | day = bcd2bin(day); | |
216 | mon = bcd2bin(mon); | |
217 | year = bcd2bin(year); | |
1da177e4 LT |
218 | |
219 | year += 2000; | |
220 | ||
221 | return mktime(year, mon, day, hour, min, sec); | |
222 | } | |
223 | ||
224 | int m41t81_probe(void) | |
225 | { | |
226 | unsigned int tmp; | |
227 | ||
228 | /* enable chip if it is not enabled yet */ | |
229 | tmp = m41t81_read(M41T81REG_SC); | |
230 | m41t81_write(M41T81REG_SC, tmp & 0x7f); | |
231 | ||
635c9907 | 232 | return m41t81_read(M41T81REG_SC) != -1; |
1da177e4 | 233 | } |