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