xref: /OK3568_Linux_fs/kernel/drivers/char/hw_random/intel-rng.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * RNG driver for Intel RNGs
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright 2005 (c) MontaVista Software, Inc.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * with the majority of the code coming from:
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
9*4882a593Smuzhiyun  * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * derived from
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Hardware driver for the AMD 768 Random Number Generator (RNG)
14*4882a593Smuzhiyun  * (c) Copyright 2001 Red Hat Inc
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * derived from
17*4882a593Smuzhiyun  *
18*4882a593Smuzhiyun  * Hardware driver for Intel i810 Random Number Generator (RNG)
19*4882a593Smuzhiyun  * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
20*4882a593Smuzhiyun  * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * This file is licensed under  the terms of the GNU General Public
23*4882a593Smuzhiyun  * License version 2. This program is licensed "as is" without any
24*4882a593Smuzhiyun  * warranty of any kind, whether express or implied.
25*4882a593Smuzhiyun  */
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include <linux/hw_random.h>
28*4882a593Smuzhiyun #include <linux/kernel.h>
29*4882a593Smuzhiyun #include <linux/module.h>
30*4882a593Smuzhiyun #include <linux/pci.h>
31*4882a593Smuzhiyun #include <linux/stop_machine.h>
32*4882a593Smuzhiyun #include <linux/delay.h>
33*4882a593Smuzhiyun #include <linux/slab.h>
34*4882a593Smuzhiyun #include <asm/io.h>
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define PFX	KBUILD_MODNAME ": "
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun /*
40*4882a593Smuzhiyun  * RNG registers
41*4882a593Smuzhiyun  */
42*4882a593Smuzhiyun #define INTEL_RNG_HW_STATUS			0
43*4882a593Smuzhiyun #define         INTEL_RNG_PRESENT		0x40
44*4882a593Smuzhiyun #define         INTEL_RNG_ENABLED		0x01
45*4882a593Smuzhiyun #define INTEL_RNG_STATUS			1
46*4882a593Smuzhiyun #define         INTEL_RNG_DATA_PRESENT		0x01
47*4882a593Smuzhiyun #define INTEL_RNG_DATA				2
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun /*
50*4882a593Smuzhiyun  * Magic address at which Intel PCI bridges locate the RNG
51*4882a593Smuzhiyun  */
52*4882a593Smuzhiyun #define INTEL_RNG_ADDR				0xFFBC015F
53*4882a593Smuzhiyun #define INTEL_RNG_ADDR_LEN			3
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun /*
56*4882a593Smuzhiyun  * LPC bridge PCI config space registers
57*4882a593Smuzhiyun  */
58*4882a593Smuzhiyun #define FWH_DEC_EN1_REG_OLD			0xe3
59*4882a593Smuzhiyun #define FWH_DEC_EN1_REG_NEW			0xd9 /* high byte of 16-bit register */
60*4882a593Smuzhiyun #define FWH_F8_EN_MASK				0x80
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #define BIOS_CNTL_REG_OLD			0x4e
63*4882a593Smuzhiyun #define BIOS_CNTL_REG_NEW			0xdc
64*4882a593Smuzhiyun #define BIOS_CNTL_WRITE_ENABLE_MASK		0x01
65*4882a593Smuzhiyun #define BIOS_CNTL_LOCK_ENABLE_MASK		0x02
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun /*
68*4882a593Smuzhiyun  * Magic address at which Intel Firmware Hubs get accessed
69*4882a593Smuzhiyun  */
70*4882a593Smuzhiyun #define INTEL_FWH_ADDR				0xffff0000
71*4882a593Smuzhiyun #define INTEL_FWH_ADDR_LEN			2
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun /*
74*4882a593Smuzhiyun  * Intel Firmware Hub command codes (write to any address inside the device)
75*4882a593Smuzhiyun  */
76*4882a593Smuzhiyun #define INTEL_FWH_RESET_CMD			0xff /* aka READ_ARRAY */
77*4882a593Smuzhiyun #define INTEL_FWH_READ_ID_CMD			0x90
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /*
80*4882a593Smuzhiyun  * Intel Firmware Hub Read ID command result addresses
81*4882a593Smuzhiyun  */
82*4882a593Smuzhiyun #define INTEL_FWH_MANUFACTURER_CODE_ADDRESS	0x000000
83*4882a593Smuzhiyun #define INTEL_FWH_DEVICE_CODE_ADDRESS		0x000001
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun /*
86*4882a593Smuzhiyun  * Intel Firmware Hub Read ID command result values
87*4882a593Smuzhiyun  */
88*4882a593Smuzhiyun #define INTEL_FWH_MANUFACTURER_CODE		0x89
89*4882a593Smuzhiyun #define INTEL_FWH_DEVICE_CODE_8M		0xac
90*4882a593Smuzhiyun #define INTEL_FWH_DEVICE_CODE_4M		0xad
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun /*
93*4882a593Smuzhiyun  * Data for PCI driver interface
94*4882a593Smuzhiyun  *
95*4882a593Smuzhiyun  * This data only exists for exporting the supported
96*4882a593Smuzhiyun  * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
97*4882a593Smuzhiyun  * register a pci_driver, because someone else might one day
98*4882a593Smuzhiyun  * want to register another driver on the same PCI id.
99*4882a593Smuzhiyun  */
100*4882a593Smuzhiyun static const struct pci_device_id pci_tbl[] = {
101*4882a593Smuzhiyun /* AA
102*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2418) }, */
103*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2410) }, /* AA */
104*4882a593Smuzhiyun /* AB
105*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2428) }, */
106*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2420) }, /* AB */
107*4882a593Smuzhiyun /* ??
108*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2430) }, */
109*4882a593Smuzhiyun /* BAM, CAM, DBM, FBM, GxM
110*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2448) }, */
111*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x244c) }, /* BAM */
112*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x248c) }, /* CAM */
113*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x24cc) }, /* DBM */
114*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2641) }, /* FBM */
115*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x27b9) }, /* GxM */
116*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x27bd) }, /* GxM DH */
117*4882a593Smuzhiyun /* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx
118*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x244e) }, */
119*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2440) }, /* BA */
120*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2480) }, /* CA */
121*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x24c0) }, /* DB */
122*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x24d0) }, /* Ex */
123*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x25a1) }, /* 6300 */
124*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2640) }, /* Fx */
125*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2670) }, /* 631x/632x */
126*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2671) }, /* 631x/632x */
127*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2672) }, /* 631x/632x */
128*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2673) }, /* 631x/632x */
129*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2674) }, /* 631x/632x */
130*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2675) }, /* 631x/632x */
131*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2676) }, /* 631x/632x */
132*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2677) }, /* 631x/632x */
133*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2678) }, /* 631x/632x */
134*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2679) }, /* 631x/632x */
135*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x267a) }, /* 631x/632x */
136*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x267b) }, /* 631x/632x */
137*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x267c) }, /* 631x/632x */
138*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x267d) }, /* 631x/632x */
139*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x267e) }, /* 631x/632x */
140*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x267f) }, /* 631x/632x */
141*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x27b8) }, /* Gx */
142*4882a593Smuzhiyun /* E
143*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x245e) }, */
144*4882a593Smuzhiyun 	{ PCI_DEVICE(0x8086, 0x2450) }, /* E  */
145*4882a593Smuzhiyun 	{ 0, },	/* terminate list */
146*4882a593Smuzhiyun };
147*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, pci_tbl);
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun static __initdata int no_fwh_detect;
150*4882a593Smuzhiyun module_param(no_fwh_detect, int, 0);
151*4882a593Smuzhiyun MODULE_PARM_DESC(no_fwh_detect, "Skip FWH detection:\n"
152*4882a593Smuzhiyun                                 " positive value - skip if FWH space locked read-only\n"
153*4882a593Smuzhiyun                                 " negative value - skip always");
154*4882a593Smuzhiyun 
hwstatus_get(void __iomem * mem)155*4882a593Smuzhiyun static inline u8 hwstatus_get(void __iomem *mem)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	return readb(mem + INTEL_RNG_HW_STATUS);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
hwstatus_set(void __iomem * mem,u8 hw_status)160*4882a593Smuzhiyun static inline u8 hwstatus_set(void __iomem *mem,
161*4882a593Smuzhiyun 			      u8 hw_status)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	writeb(hw_status, mem + INTEL_RNG_HW_STATUS);
164*4882a593Smuzhiyun 	return hwstatus_get(mem);
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun 
intel_rng_data_present(struct hwrng * rng,int wait)167*4882a593Smuzhiyun static int intel_rng_data_present(struct hwrng *rng, int wait)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun 	void __iomem *mem = (void __iomem *)rng->priv;
170*4882a593Smuzhiyun 	int data, i;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	for (i = 0; i < 20; i++) {
173*4882a593Smuzhiyun 		data = !!(readb(mem + INTEL_RNG_STATUS) &
174*4882a593Smuzhiyun 			  INTEL_RNG_DATA_PRESENT);
175*4882a593Smuzhiyun 		if (data || !wait)
176*4882a593Smuzhiyun 			break;
177*4882a593Smuzhiyun 		udelay(10);
178*4882a593Smuzhiyun 	}
179*4882a593Smuzhiyun 	return data;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
intel_rng_data_read(struct hwrng * rng,u32 * data)182*4882a593Smuzhiyun static int intel_rng_data_read(struct hwrng *rng, u32 *data)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	void __iomem *mem = (void __iomem *)rng->priv;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	*data = readb(mem + INTEL_RNG_DATA);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	return 1;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun 
intel_rng_init(struct hwrng * rng)191*4882a593Smuzhiyun static int intel_rng_init(struct hwrng *rng)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun 	void __iomem *mem = (void __iomem *)rng->priv;
194*4882a593Smuzhiyun 	u8 hw_status;
195*4882a593Smuzhiyun 	int err = -EIO;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	hw_status = hwstatus_get(mem);
198*4882a593Smuzhiyun 	/* turn RNG h/w on, if it's off */
199*4882a593Smuzhiyun 	if ((hw_status & INTEL_RNG_ENABLED) == 0)
200*4882a593Smuzhiyun 		hw_status = hwstatus_set(mem, hw_status | INTEL_RNG_ENABLED);
201*4882a593Smuzhiyun 	if ((hw_status & INTEL_RNG_ENABLED) == 0) {
202*4882a593Smuzhiyun 		pr_err(PFX "cannot enable RNG, aborting\n");
203*4882a593Smuzhiyun 		goto out;
204*4882a593Smuzhiyun 	}
205*4882a593Smuzhiyun 	err = 0;
206*4882a593Smuzhiyun out:
207*4882a593Smuzhiyun 	return err;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
intel_rng_cleanup(struct hwrng * rng)210*4882a593Smuzhiyun static void intel_rng_cleanup(struct hwrng *rng)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	void __iomem *mem = (void __iomem *)rng->priv;
213*4882a593Smuzhiyun 	u8 hw_status;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	hw_status = hwstatus_get(mem);
216*4882a593Smuzhiyun 	if (hw_status & INTEL_RNG_ENABLED)
217*4882a593Smuzhiyun 		hwstatus_set(mem, hw_status & ~INTEL_RNG_ENABLED);
218*4882a593Smuzhiyun 	else
219*4882a593Smuzhiyun 		pr_warn(PFX "unusual: RNG already disabled\n");
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun static struct hwrng intel_rng = {
224*4882a593Smuzhiyun 	.name		= "intel",
225*4882a593Smuzhiyun 	.init		= intel_rng_init,
226*4882a593Smuzhiyun 	.cleanup	= intel_rng_cleanup,
227*4882a593Smuzhiyun 	.data_present	= intel_rng_data_present,
228*4882a593Smuzhiyun 	.data_read	= intel_rng_data_read,
229*4882a593Smuzhiyun };
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun struct intel_rng_hw {
232*4882a593Smuzhiyun 	struct pci_dev *dev;
233*4882a593Smuzhiyun 	void __iomem *mem;
234*4882a593Smuzhiyun 	u8 bios_cntl_off;
235*4882a593Smuzhiyun 	u8 bios_cntl_val;
236*4882a593Smuzhiyun 	u8 fwh_dec_en1_off;
237*4882a593Smuzhiyun 	u8 fwh_dec_en1_val;
238*4882a593Smuzhiyun };
239*4882a593Smuzhiyun 
intel_rng_hw_init(void * _intel_rng_hw)240*4882a593Smuzhiyun static int __init intel_rng_hw_init(void *_intel_rng_hw)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	struct intel_rng_hw *intel_rng_hw = _intel_rng_hw;
243*4882a593Smuzhiyun 	u8 mfc, dvc;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	/* interrupts disabled in stop_machine call */
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))
248*4882a593Smuzhiyun 		pci_write_config_byte(intel_rng_hw->dev,
249*4882a593Smuzhiyun 		                      intel_rng_hw->fwh_dec_en1_off,
250*4882a593Smuzhiyun 		                      intel_rng_hw->fwh_dec_en1_val |
251*4882a593Smuzhiyun 				      FWH_F8_EN_MASK);
252*4882a593Smuzhiyun 	if (!(intel_rng_hw->bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK))
253*4882a593Smuzhiyun 		pci_write_config_byte(intel_rng_hw->dev,
254*4882a593Smuzhiyun 		                      intel_rng_hw->bios_cntl_off,
255*4882a593Smuzhiyun 		                      intel_rng_hw->bios_cntl_val |
256*4882a593Smuzhiyun 				      BIOS_CNTL_WRITE_ENABLE_MASK);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);
259*4882a593Smuzhiyun 	writeb(INTEL_FWH_READ_ID_CMD, intel_rng_hw->mem);
260*4882a593Smuzhiyun 	mfc = readb(intel_rng_hw->mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);
261*4882a593Smuzhiyun 	dvc = readb(intel_rng_hw->mem + INTEL_FWH_DEVICE_CODE_ADDRESS);
262*4882a593Smuzhiyun 	writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	if (!(intel_rng_hw->bios_cntl_val &
265*4882a593Smuzhiyun 	      (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
266*4882a593Smuzhiyun 		pci_write_config_byte(intel_rng_hw->dev,
267*4882a593Smuzhiyun 				      intel_rng_hw->bios_cntl_off,
268*4882a593Smuzhiyun 				      intel_rng_hw->bios_cntl_val);
269*4882a593Smuzhiyun 	if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))
270*4882a593Smuzhiyun 		pci_write_config_byte(intel_rng_hw->dev,
271*4882a593Smuzhiyun 				      intel_rng_hw->fwh_dec_en1_off,
272*4882a593Smuzhiyun 				      intel_rng_hw->fwh_dec_en1_val);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	if (mfc != INTEL_FWH_MANUFACTURER_CODE ||
275*4882a593Smuzhiyun 	    (dvc != INTEL_FWH_DEVICE_CODE_8M &&
276*4882a593Smuzhiyun 	     dvc != INTEL_FWH_DEVICE_CODE_4M)) {
277*4882a593Smuzhiyun 		pr_notice(PFX "FWH not detected\n");
278*4882a593Smuzhiyun 		return -ENODEV;
279*4882a593Smuzhiyun 	}
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	return 0;
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun 
intel_init_hw_struct(struct intel_rng_hw * intel_rng_hw,struct pci_dev * dev)284*4882a593Smuzhiyun static int __init intel_init_hw_struct(struct intel_rng_hw *intel_rng_hw,
285*4882a593Smuzhiyun 					struct pci_dev *dev)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun 	intel_rng_hw->bios_cntl_val = 0xff;
288*4882a593Smuzhiyun 	intel_rng_hw->fwh_dec_en1_val = 0xff;
289*4882a593Smuzhiyun 	intel_rng_hw->dev = dev;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	/* Check for Intel 82802 */
292*4882a593Smuzhiyun 	if (dev->device < 0x2640) {
293*4882a593Smuzhiyun 		intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;
294*4882a593Smuzhiyun 		intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_OLD;
295*4882a593Smuzhiyun 	} else {
296*4882a593Smuzhiyun 		intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;
297*4882a593Smuzhiyun 		intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_NEW;
298*4882a593Smuzhiyun 	}
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	pci_read_config_byte(dev, intel_rng_hw->fwh_dec_en1_off,
301*4882a593Smuzhiyun 			     &intel_rng_hw->fwh_dec_en1_val);
302*4882a593Smuzhiyun 	pci_read_config_byte(dev, intel_rng_hw->bios_cntl_off,
303*4882a593Smuzhiyun 			     &intel_rng_hw->bios_cntl_val);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	if ((intel_rng_hw->bios_cntl_val &
306*4882a593Smuzhiyun 	     (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))
307*4882a593Smuzhiyun 	    == BIOS_CNTL_LOCK_ENABLE_MASK) {
308*4882a593Smuzhiyun 		static __initdata /*const*/ char warning[] =
309*4882a593Smuzhiyun PFX "Firmware space is locked read-only. If you can't or\n"
310*4882a593Smuzhiyun PFX "don't want to disable this in firmware setup, and if\n"
311*4882a593Smuzhiyun PFX "you are certain that your system has a functional\n"
312*4882a593Smuzhiyun PFX "RNG, try using the 'no_fwh_detect' option.\n";
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 		if (no_fwh_detect)
315*4882a593Smuzhiyun 			return -ENODEV;
316*4882a593Smuzhiyun 		pr_warn("%s", warning);
317*4882a593Smuzhiyun 		return -EBUSY;
318*4882a593Smuzhiyun 	}
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	intel_rng_hw->mem = ioremap(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);
321*4882a593Smuzhiyun 	if (intel_rng_hw->mem == NULL)
322*4882a593Smuzhiyun 		return -EBUSY;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	return 0;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 
mod_init(void)328*4882a593Smuzhiyun static int __init mod_init(void)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun 	int err = -ENODEV;
331*4882a593Smuzhiyun 	int i;
332*4882a593Smuzhiyun 	struct pci_dev *dev = NULL;
333*4882a593Smuzhiyun 	void __iomem *mem;
334*4882a593Smuzhiyun 	u8 hw_status;
335*4882a593Smuzhiyun 	struct intel_rng_hw *intel_rng_hw;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	for (i = 0; !dev && pci_tbl[i].vendor; ++i)
338*4882a593Smuzhiyun 		dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device,
339*4882a593Smuzhiyun 				     NULL);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	if (!dev)
342*4882a593Smuzhiyun 		goto out; /* Device not found. */
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	if (no_fwh_detect < 0) {
345*4882a593Smuzhiyun 		pci_dev_put(dev);
346*4882a593Smuzhiyun 		goto fwh_done;
347*4882a593Smuzhiyun 	}
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	intel_rng_hw = kmalloc(sizeof(*intel_rng_hw), GFP_KERNEL);
350*4882a593Smuzhiyun 	if (!intel_rng_hw) {
351*4882a593Smuzhiyun 		pci_dev_put(dev);
352*4882a593Smuzhiyun 		goto out;
353*4882a593Smuzhiyun 	}
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	err = intel_init_hw_struct(intel_rng_hw, dev);
356*4882a593Smuzhiyun 	if (err) {
357*4882a593Smuzhiyun 		pci_dev_put(dev);
358*4882a593Smuzhiyun 		kfree(intel_rng_hw);
359*4882a593Smuzhiyun 		if (err == -ENODEV)
360*4882a593Smuzhiyun 			goto fwh_done;
361*4882a593Smuzhiyun 		goto out;
362*4882a593Smuzhiyun 	}
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	/*
365*4882a593Smuzhiyun 	 * Since the BIOS code/data is going to disappear from its normal
366*4882a593Smuzhiyun 	 * location with the Read ID command, all activity on the system
367*4882a593Smuzhiyun 	 * must be stopped until the state is back to normal.
368*4882a593Smuzhiyun 	 *
369*4882a593Smuzhiyun 	 * Use stop_machine because IPIs can be blocked by disabling
370*4882a593Smuzhiyun 	 * interrupts.
371*4882a593Smuzhiyun 	 */
372*4882a593Smuzhiyun 	err = stop_machine(intel_rng_hw_init, intel_rng_hw, NULL);
373*4882a593Smuzhiyun 	pci_dev_put(dev);
374*4882a593Smuzhiyun 	iounmap(intel_rng_hw->mem);
375*4882a593Smuzhiyun 	kfree(intel_rng_hw);
376*4882a593Smuzhiyun 	if (err)
377*4882a593Smuzhiyun 		goto out;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun fwh_done:
380*4882a593Smuzhiyun 	err = -ENOMEM;
381*4882a593Smuzhiyun 	mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);
382*4882a593Smuzhiyun 	if (!mem)
383*4882a593Smuzhiyun 		goto out;
384*4882a593Smuzhiyun 	intel_rng.priv = (unsigned long)mem;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	/* Check for Random Number Generator */
387*4882a593Smuzhiyun 	err = -ENODEV;
388*4882a593Smuzhiyun 	hw_status = hwstatus_get(mem);
389*4882a593Smuzhiyun 	if ((hw_status & INTEL_RNG_PRESENT) == 0) {
390*4882a593Smuzhiyun 		iounmap(mem);
391*4882a593Smuzhiyun 		goto out;
392*4882a593Smuzhiyun 	}
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	pr_info("Intel 82802 RNG detected\n");
395*4882a593Smuzhiyun 	err = hwrng_register(&intel_rng);
396*4882a593Smuzhiyun 	if (err) {
397*4882a593Smuzhiyun 		pr_err(PFX "RNG registering failed (%d)\n",
398*4882a593Smuzhiyun 		       err);
399*4882a593Smuzhiyun 		iounmap(mem);
400*4882a593Smuzhiyun 	}
401*4882a593Smuzhiyun out:
402*4882a593Smuzhiyun 	return err;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun 
mod_exit(void)406*4882a593Smuzhiyun static void __exit mod_exit(void)
407*4882a593Smuzhiyun {
408*4882a593Smuzhiyun 	void __iomem *mem = (void __iomem *)intel_rng.priv;
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	hwrng_unregister(&intel_rng);
411*4882a593Smuzhiyun 	iounmap(mem);
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun module_init(mod_init);
415*4882a593Smuzhiyun module_exit(mod_exit);
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun MODULE_DESCRIPTION("H/W RNG driver for Intel chipsets");
418*4882a593Smuzhiyun MODULE_LICENSE("GPL");
419