xref: /linux/drivers/net/wireless/ath/ath9k/rng.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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