altos: Add power management to TRNG driver
[fw/altos] / src / drivers / ao_trng_send.c
index 34a8a981bb866406e76a690b355c6e7548be5764..63366d3ef53baad6f4b9f0f2a107aeea0c76a79b 100644 (file)
 #include <ao_adc_fast.h>
 #include <ao_crc.h>
 #include <ao_trng_send.h>
+#include <ao_exti.h>
+#include <ao_power.h>
+
+static uint8_t trng_running;
+
+static void
+ao_trng_send_raw(uint16_t *buf)
+{
+       uint16_t        i;
+       uint16_t        t;
+       uint16_t        *rnd = (uint16_t *) ao_adc_ring;
+
+       t = ao_adc_get(AO_USB_IN_SIZE>>1);      /* one 16-bit value per two output bytes */
+       for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
+               *buf++ = rnd[t];
+               t = (t + 1) & (AO_ADC_RING_SIZE - 1);
+       }
+}
+
+static void
+ao_trng_send_cooked(uint16_t *buf)
+{
+       uint16_t        i;
+       uint16_t        t;
+       uint32_t        *rnd = (uint32_t *) ao_adc_ring;
+
+       t = ao_adc_get(AO_USB_IN_SIZE) >> 1;    /* one 16-bit value per output byte */
+       for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
+               *buf++ = ao_crc_in_32_out_16(rnd[t]);
+               t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
+       }
+}
+
+static inline int
+ao_send_raw(void)
+{
+       return !ao_gpio_get(AO_RAW_PORT, AO_RAW_BIT, AO_RAW_PIN);
+}
 
 static void
 ao_trng_send(void)
 {
        static uint16_t *buffer[2];
-       int             usb_buf_id;
-       uint16_t        i;
-       uint16_t        *buf;
-       uint32_t        *rnd;
+       int     usb_buf_id;
 
        if (!buffer[0]) {
                buffer[0] = ao_usb_alloc();
@@ -41,13 +76,24 @@ ao_trng_send(void)
        ao_crc_reset();
 
        for (;;) {
-               ao_led_on(AO_LED_TRNG_ACTIVE);
-               rnd = (uint32_t *) ao_adc_get(AO_USB_IN_SIZE);  /* one 16-bit value per output byte */
-               buf = buffer[usb_buf_id];
-               for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++)
-                       *buf++ = ao_crc_in_32_out_16(*rnd++);
+               if (!trng_running) {
+                       /* Delay long enough for the HV power supply
+                        * to stabilize so that the first bits we read
+                        * aren't of poor quality
+                        */
+                       ao_delay(AO_MS_TO_TICKS(10));
+                       trng_running = TRUE;
+               }
+               if (ao_send_raw()) {
+                       ao_led_on(AO_LED_TRNG_RAW);
+                       ao_trng_send_raw(buffer[usb_buf_id]);
+                       ao_led_off(AO_LED_TRNG_RAW);
+               } else {
+                       ao_led_on(AO_LED_TRNG_COOKED);
+                       ao_trng_send_cooked(buffer[usb_buf_id]);
+                       ao_led_off(AO_LED_TRNG_COOKED);
+               }
                ao_adc_ack(AO_USB_IN_SIZE);
-               ao_led_off(AO_LED_TRNG_ACTIVE);
                ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
                usb_buf_id = 1-usb_buf_id;
        }
@@ -55,8 +101,39 @@ ao_trng_send(void)
 
 static struct ao_task ao_trng_send_task;
 
+#if AO_POWER_MANAGEMENT
+
+static void ao_trng_suspend(void *arg)
+{
+       (void) arg;
+#ifdef AO_TRNG_ENABLE_PORT
+       ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 0);
+#endif
+       trng_running = FALSE;
+}
+
+static void ao_trng_resume(void *arg)
+{
+       (void) arg;
+#ifdef AO_TRNG_ENABLE_PORT
+       ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1);
+#endif
+}
+
+static struct ao_power ao_trng_power = {
+       .suspend = ao_trng_suspend,
+       .resume = ao_trng_resume
+};
+
+#endif
+
 void
 ao_trng_send_init(void)
 {
+#ifdef AO_TRNG_ENABLE_PORT
+       ao_enable_output(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1);
+       ao_power_register(&ao_trng_power);
+#endif
+       ao_enable_input(AO_RAW_PORT, AO_RAW_BIT, AO_EXTI_MODE_PULL_UP);
        ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send");
 }