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