1ed14dc0aSMiaoqing Pan /*
2ed14dc0aSMiaoqing Pan * Copyright (c) 2015 Qualcomm Atheros, Inc.
3ed14dc0aSMiaoqing Pan *
4ed14dc0aSMiaoqing Pan * Permission to use, copy, modify, and/or distribute this software for any
5ed14dc0aSMiaoqing Pan * purpose with or without fee is hereby granted, provided that the above
6ed14dc0aSMiaoqing Pan * copyright notice and this permission notice appear in all copies.
7ed14dc0aSMiaoqing Pan *
8ed14dc0aSMiaoqing Pan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9ed14dc0aSMiaoqing Pan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10ed14dc0aSMiaoqing Pan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11ed14dc0aSMiaoqing Pan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12ed14dc0aSMiaoqing Pan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13ed14dc0aSMiaoqing Pan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14ed14dc0aSMiaoqing Pan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15ed14dc0aSMiaoqing Pan */
16ed14dc0aSMiaoqing Pan
17ed14dc0aSMiaoqing Pan #include <linux/hw_random.h>
18ed14dc0aSMiaoqing Pan #include <linux/kthread.h>
19ed14dc0aSMiaoqing Pan
20ed14dc0aSMiaoqing Pan #include "ath9k.h"
21ed14dc0aSMiaoqing Pan #include "hw.h"
22ed14dc0aSMiaoqing Pan #include "ar9003_phy.h"
23ed14dc0aSMiaoqing Pan
ath9k_rng_data_read(struct ath_softc * sc,u32 * buf,u32 buf_size)24ed14dc0aSMiaoqing Pan static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size)
25ed14dc0aSMiaoqing Pan {
26ed14dc0aSMiaoqing Pan int i, j;
27ed14dc0aSMiaoqing Pan u32 v1, v2, rng_last = sc->rng_last;
28ed14dc0aSMiaoqing Pan struct ath_hw *ah = sc->sc_ah;
29ed14dc0aSMiaoqing Pan
30ed14dc0aSMiaoqing Pan ath9k_ps_wakeup(sc);
31ed14dc0aSMiaoqing Pan
32*b3a663f0SWenli Looi REG_RMW_FIELD(ah, AR_PHY_TEST(ah), AR_PHY_TEST_BBB_OBS_SEL, 1);
33*b3a663f0SWenli Looi REG_CLR_BIT(ah, AR_PHY_TEST(ah), AR_PHY_TEST_RX_OBS_SEL_BIT5);
34*b3a663f0SWenli Looi REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS(ah), AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
35ed14dc0aSMiaoqing Pan
36ed14dc0aSMiaoqing Pan for (i = 0, j = 0; i < buf_size; i++) {
37ed14dc0aSMiaoqing Pan v1 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
38ed14dc0aSMiaoqing Pan v2 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
39ed14dc0aSMiaoqing Pan
40ed14dc0aSMiaoqing Pan /* wait for data ready */
41ed14dc0aSMiaoqing Pan if (v1 && v2 && rng_last != v1 && v1 != v2 && v1 != 0xffff &&
42ed14dc0aSMiaoqing Pan v2 != 0xffff)
43ed14dc0aSMiaoqing Pan buf[j++] = (v1 << 16) | v2;
44ed14dc0aSMiaoqing Pan
45ed14dc0aSMiaoqing Pan rng_last = v2;
46ed14dc0aSMiaoqing Pan }
47ed14dc0aSMiaoqing Pan
48ed14dc0aSMiaoqing Pan ath9k_ps_restore(sc);
49ed14dc0aSMiaoqing Pan
50ed14dc0aSMiaoqing Pan sc->rng_last = rng_last;
51ed14dc0aSMiaoqing Pan
52ed14dc0aSMiaoqing Pan return j << 2;
53ed14dc0aSMiaoqing Pan }
54ed14dc0aSMiaoqing Pan
ath9k_rng_delay_get(u32 fail_stats)55a2cb3d5fSMiaoqing Pan static u32 ath9k_rng_delay_get(u32 fail_stats)
56a2cb3d5fSMiaoqing Pan {
57a2cb3d5fSMiaoqing Pan u32 delay;
58a2cb3d5fSMiaoqing Pan
59a2cb3d5fSMiaoqing Pan if (fail_stats < 100)
60a2cb3d5fSMiaoqing Pan delay = 10;
61a2cb3d5fSMiaoqing Pan else if (fail_stats < 105)
62a2cb3d5fSMiaoqing Pan delay = 1000;
63a2cb3d5fSMiaoqing Pan else
64a2cb3d5fSMiaoqing Pan delay = 10000;
65a2cb3d5fSMiaoqing Pan
66a2cb3d5fSMiaoqing Pan return delay;
67a2cb3d5fSMiaoqing Pan }
68a2cb3d5fSMiaoqing Pan
ath9k_rng_read(struct hwrng * rng,void * buf,size_t max,bool wait)69fcd09c90SJason A. Donenfeld static int ath9k_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
70ed14dc0aSMiaoqing Pan {
71fcd09c90SJason A. Donenfeld struct ath_softc *sc = container_of(rng, struct ath_softc, rng_ops);
72fcd09c90SJason A. Donenfeld u32 fail_stats = 0, word;
73fcd09c90SJason A. Donenfeld int bytes_read = 0;
74ed14dc0aSMiaoqing Pan
75fcd09c90SJason A. Donenfeld for (;;) {
76fcd09c90SJason A. Donenfeld if (max & ~3UL)
77fcd09c90SJason A. Donenfeld bytes_read = ath9k_rng_data_read(sc, buf, max >> 2);
78fcd09c90SJason A. Donenfeld if ((max & 3UL) && ath9k_rng_data_read(sc, &word, 1)) {
79fcd09c90SJason A. Donenfeld memcpy(buf + bytes_read, &word, max & 3UL);
80fcd09c90SJason A. Donenfeld bytes_read += max & 3UL;
81fcd09c90SJason A. Donenfeld memzero_explicit(&word, sizeof(word));
82fcd09c90SJason A. Donenfeld }
83fcd09c90SJason A. Donenfeld if (!wait || !max || likely(bytes_read) || fail_stats > 110)
84fcd09c90SJason A. Donenfeld break;
85ed14dc0aSMiaoqing Pan
8636cb6494SJason A. Donenfeld if (hwrng_msleep(rng, ath9k_rng_delay_get(++fail_stats)))
8736cb6494SJason A. Donenfeld break;
88ed14dc0aSMiaoqing Pan }
89ed14dc0aSMiaoqing Pan
90fcd09c90SJason A. Donenfeld if (wait && !bytes_read && max)
91fcd09c90SJason A. Donenfeld bytes_read = -EIO;
92fcd09c90SJason A. Donenfeld return bytes_read;
93ed14dc0aSMiaoqing Pan }
94ed14dc0aSMiaoqing Pan
ath9k_rng_start(struct ath_softc * sc)95ed14dc0aSMiaoqing Pan void ath9k_rng_start(struct ath_softc *sc)
96ed14dc0aSMiaoqing Pan {
97fcd09c90SJason A. Donenfeld static atomic_t serial = ATOMIC_INIT(0);
98ed14dc0aSMiaoqing Pan struct ath_hw *ah = sc->sc_ah;
99ed14dc0aSMiaoqing Pan
100fcd09c90SJason A. Donenfeld if (sc->rng_ops.read)
101ed14dc0aSMiaoqing Pan return;
102ed14dc0aSMiaoqing Pan
103ed14dc0aSMiaoqing Pan if (!AR_SREV_9300_20_OR_LATER(ah))
104ed14dc0aSMiaoqing Pan return;
105ed14dc0aSMiaoqing Pan
106fcd09c90SJason A. Donenfeld snprintf(sc->rng_name, sizeof(sc->rng_name), "ath9k_%u",
107fcd09c90SJason A. Donenfeld (atomic_inc_return(&serial) - 1) & U16_MAX);
108fcd09c90SJason A. Donenfeld sc->rng_ops.name = sc->rng_name;
109fcd09c90SJason A. Donenfeld sc->rng_ops.read = ath9k_rng_read;
110fcd09c90SJason A. Donenfeld sc->rng_ops.quality = 320;
111fcd09c90SJason A. Donenfeld
112fcd09c90SJason A. Donenfeld if (devm_hwrng_register(sc->dev, &sc->rng_ops))
113fcd09c90SJason A. Donenfeld sc->rng_ops.read = NULL;
114ed14dc0aSMiaoqing Pan }
115ed14dc0aSMiaoqing Pan
ath9k_rng_stop(struct ath_softc * sc)116ed14dc0aSMiaoqing Pan void ath9k_rng_stop(struct ath_softc *sc)
117ed14dc0aSMiaoqing Pan {
118fcd09c90SJason A. Donenfeld if (sc->rng_ops.read) {
119fcd09c90SJason A. Donenfeld devm_hwrng_unregister(sc->dev, &sc->rng_ops);
120fcd09c90SJason A. Donenfeld sc->rng_ops.read = NULL;
12107246c11SMiaoqing Pan }
122ed14dc0aSMiaoqing Pan }
123