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