linux/drivers/net/wireless/ath/ath9k/rng.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015 Qualcomm Atheros, Inc.
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include <linux/hw_random.h>
  18#include <linux/kthread.h>
  19
  20#include "ath9k.h"
  21#include "hw.h"
  22#include "ar9003_phy.h"
  23
  24#define ATH9K_RNG_BUF_SIZE      320
  25#define ATH9K_RNG_ENTROPY(x)    (((x) * 8 * 10) >> 5) /* quality: 10/32 */
  26
  27static DECLARE_WAIT_QUEUE_HEAD(rng_queue);
  28
  29static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size)
  30{
  31        int i, j;
  32        u32  v1, v2, rng_last = sc->rng_last;
  33        struct ath_hw *ah = sc->sc_ah;
  34
  35        ath9k_ps_wakeup(sc);
  36
  37        REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
  38        REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
  39        REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
  40
  41        for (i = 0, j = 0; i < buf_size; i++) {
  42                v1 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
  43                v2 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
  44
  45                /* wait for data ready */
  46                if (v1 && v2 && rng_last != v1 && v1 != v2 && v1 != 0xffff &&
  47                    v2 != 0xffff)
  48                        buf[j++] = (v1 << 16) | v2;
  49
  50                rng_last = v2;
  51        }
  52
  53        ath9k_ps_restore(sc);
  54
  55        sc->rng_last = rng_last;
  56
  57        return j << 2;
  58}
  59
  60static u32 ath9k_rng_delay_get(u32 fail_stats)
  61{
  62        u32 delay;
  63
  64        if (fail_stats < 100)
  65                delay = 10;
  66        else if (fail_stats < 105)
  67                delay = 1000;
  68        else
  69                delay = 10000;
  70
  71        return delay;
  72}
  73
  74static int ath9k_rng_kthread(void *data)
  75{
  76        int bytes_read;
  77        struct ath_softc *sc = data;
  78        u32 *rng_buf;
  79        u32 delay, fail_stats = 0;
  80
  81        rng_buf = kmalloc_array(ATH9K_RNG_BUF_SIZE, sizeof(u32), GFP_KERNEL);
  82        if (!rng_buf)
  83                goto out;
  84
  85        while (!kthread_should_stop()) {
  86                bytes_read = ath9k_rng_data_read(sc, rng_buf,
  87                                                 ATH9K_RNG_BUF_SIZE);
  88                if (unlikely(!bytes_read)) {
  89                        delay = ath9k_rng_delay_get(++fail_stats);
  90                        wait_event_interruptible_timeout(rng_queue,
  91                                                         kthread_should_stop(),
  92                                                         msecs_to_jiffies(delay));
  93                        continue;
  94                }
  95
  96                fail_stats = 0;
  97
  98                /* sleep until entropy bits under write_wakeup_threshold */
  99                add_hwgenerator_randomness((void *)rng_buf, bytes_read,
 100                                           ATH9K_RNG_ENTROPY(bytes_read));
 101        }
 102
 103        kfree(rng_buf);
 104out:
 105        sc->rng_task = NULL;
 106
 107        return 0;
 108}
 109
 110void ath9k_rng_start(struct ath_softc *sc)
 111{
 112        struct ath_hw *ah = sc->sc_ah;
 113
 114        if (sc->rng_task)
 115                return;
 116
 117        if (!AR_SREV_9300_20_OR_LATER(ah))
 118                return;
 119
 120        sc->rng_task = kthread_run(ath9k_rng_kthread, sc, "ath9k-hwrng");
 121        if (IS_ERR(sc->rng_task))
 122                sc->rng_task = NULL;
 123}
 124
 125void ath9k_rng_stop(struct ath_softc *sc)
 126{
 127        if (sc->rng_task) {
 128                kthread_stop(sc->rng_task);
 129                sc->rng_task = NULL;
 130        }
 131}
 132