1 /* 2 * Real-time clock support. 3 * 4 * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/hw_mmio_register_block.h> 23 #include "rtc-defs.h" 24 #include "rtc-generic.h" 25 26 27 28 // Peripheral abstraction. 29 30 Rtc_chip::Rtc_chip(l4_addr_t addr, Cpm_chip *cpm) 31 : _cpm(cpm) 32 { 33 _regs = new Hw::Mmio_register_block<32>(addr); 34 35 // Reset time regulation, in case it is completely scrambled. 36 // NOTE: Using 32768 cycles for a 1Hz signal. 37 38 set_regulator(32767, 0); 39 } 40 41 enum Clock_identifiers 42 Rtc_chip::get_clock() 43 { 44 return Clock_rtc; 45 } 46 47 uint32_t 48 Rtc_chip::read_checked(unsigned reg) 49 { 50 uint32_t last, current; 51 52 wait(); 53 last = _regs[reg]; 54 55 while (1) 56 { 57 wait(); 58 current = _regs[reg]; 59 60 if (current == last) 61 return current; 62 else 63 last = current; 64 } 65 } 66 67 void 68 Rtc_chip::wait() 69 { 70 while (!(_regs[Rtc_control] & Control_write_ready)); 71 } 72 73 void 74 Rtc_chip::write_enable() 75 { 76 wait(); 77 _regs[Hibernate_write_enable_pattern] = Write_enable_pattern; 78 79 while (!(_regs[Hibernate_write_enable_pattern] & Write_enable_status)); 80 81 wait(); 82 } 83 84 void 85 Rtc_chip::disable() 86 { 87 write_enable(); 88 _regs[Rtc_control] = _regs[Rtc_control] & ~Control_rtc_enable; 89 } 90 91 void 92 Rtc_chip::enable() 93 { 94 write_enable(); 95 _regs[Rtc_control] = _regs[Rtc_control] | Control_rtc_enable; 96 } 97 98 void 99 Rtc_chip::alarm_disable() 100 { 101 write_enable(); 102 _regs[Rtc_control] = _regs[Rtc_control] & ~Control_alarm_enable; 103 } 104 105 void 106 Rtc_chip::alarm_enable() 107 { 108 write_enable(); 109 _regs[Rtc_control] = (_regs[Rtc_control] & ~Control_alarm) | Control_alarm_enable; 110 } 111 112 void 113 Rtc_chip::wakeup_alarm_disable() 114 { 115 write_enable(); 116 _regs[Hibernate_wakeup_control] = _regs[Hibernate_wakeup_control] & ~Rtc_alarm_wakeup_enable; 117 } 118 119 void 120 Rtc_chip::wakeup_alarm_enable() 121 { 122 write_enable(); 123 _regs[Hibernate_wakeup_control] = _regs[Hibernate_wakeup_control] | Rtc_alarm_wakeup_enable; 124 } 125 126 uint32_t 127 Rtc_chip::get_seconds() 128 { 129 return read_checked(Rtc_seconds); 130 } 131 132 void 133 Rtc_chip::set_seconds(uint32_t seconds) 134 { 135 write_enable(); 136 _regs[Rtc_seconds] = seconds; 137 } 138 139 uint32_t 140 Rtc_chip::get_alarm_seconds() 141 { 142 return read_checked(Rtc_alarm_seconds); 143 } 144 145 void 146 Rtc_chip::set_alarm_seconds(uint32_t seconds) 147 { 148 write_enable(); 149 _regs[Rtc_alarm_seconds] = seconds; 150 } 151 152 void 153 Rtc_chip::set_regulator(uint32_t base, uint32_t adjustment) 154 { 155 base = base ? base - 1 : 0; 156 adjustment = adjustment ? adjustment - 1 : 0; 157 158 if (base > Regulator_1Hz_cycle_count_limit) 159 base = Regulator_1Hz_cycle_count_limit; 160 161 if (adjustment > Regulator_adjust_count_limit) 162 adjustment = Regulator_adjust_count_limit; 163 164 write_enable(); 165 _regs[Rtc_regulator] = (base << Regulator_1Hz_cycle_count_shift) | 166 (adjustment << Regulator_adjust_count_shift); 167 } 168 169 // Device-specific method. 170 171 void 172 Rtc_chip::_pre_power_down() 173 { 174 } 175 176 void 177 Rtc_chip::_power_down() 178 { 179 _pre_power_down(); 180 181 write_enable(); 182 _regs[Hibernate_control] = _regs[Hibernate_control] | Hibernate_power_down; 183 } 184 185 void 186 Rtc_chip::hibernate() 187 { 188 alarm_enable(); 189 wakeup_alarm_enable(); 190 _power_down(); 191 } 192 193 void 194 Rtc_chip::power_down() 195 { 196 wakeup_alarm_disable(); 197 _power_down(); 198 }