summaryrefslogtreecommitdiff
path: root/src/common/power.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/power.h')
-rw-r--r--src/common/power.h51
1 files changed, 51 insertions, 0 deletions
diff --git a/src/common/power.h b/src/common/power.h
index 279a26e..ac1f03d 100644
--- a/src/common/power.h
+++ b/src/common/power.h
@@ -23,6 +23,8 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
* }}} */
+#include <util/delay_basic.h>
+#include "io.h"
/** Initialise power reduction. */
void
@@ -33,4 +35,53 @@ power_init (void);
void
power_sleep (void);
+/** Own version of clock_prescale_set which does not touch I bit. */
+static inline void __attribute__((__always_inline__))
+power_clock_prescale_set (uint8_t x)
+{
+ uint8_t tmp = _BV (CLKPCE);
+ __asm__ __volatile__ (
+ "out %1, %0" "\n\t"
+ "out %1, %2" "\n\t"
+ : /* no outputs */
+ : "d" (tmp),
+ "I" (_SFR_IO_ADDR (CLKPR)),
+ "d" (x)
+ : "r0");
+}
+
+#define CLOCK_COEF (1 << (AC_POWER_CLKPS_LOW - AC_POWER_CLKPS_INITIAL))
+/** Wait for a given duration, trying to reduce clock speed. */
+static inline void __attribute__((always_inline))
+power_delay_ms (double ms)
+{
+ double slow_ticks = (AC_FREQ / 1e3 / CLOCK_COEF) * ms;
+ if (slow_ticks > 3.0)
+ {
+ /* Can use slow clock, round down as some extra instructions will be
+ * executed using slow clock. */
+ if (slow_ticks / 3.0 <= 256)
+ {
+ uint8_t loops = (uint8_t) (slow_ticks / 3.0);
+ power_clock_prescale_set (AC_POWER_CLKPS_LOW);
+ _delay_loop_1 (loops);
+ power_clock_prescale_set (AC_POWER_CLKPS_INITIAL);
+ }
+ else
+ {
+ uint16_t loops = (uint16_t) (slow_ticks / 4.0);
+ power_clock_prescale_set (AC_POWER_CLKPS_LOW);
+ _delay_loop_2 (loops);
+ power_clock_prescale_set (AC_POWER_CLKPS_INITIAL);
+ }
+ /* Do not support huge delay. */
+ }
+ else
+ {
+ /* Here, the delay is small, use a short loop. */
+ uint8_t loops = (uint8_t) (ms * AC_FREQ / 3e3);
+ _delay_loop_1 (loops);
+ }
+}
+
#endif /* power_h */